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
246 lines (212 loc) • 10.4 kB
JSX
import { useEffect, useState } from 'react';
import {LeumasBaseStyle} from "../../styles/baseStyles"
import {excludedSchemas} from "./ExcludedSchemas"
import {schemaToCardMapping} from "./SchemaMapping"
import { getAllPublicItems, getItemsByOwner } from '../UniversalCrud/UniversalCrudHelpers';
import useAuthUser from 'react-auth-kit/hooks/useAuthUser';
// import AceEditorWrapper from '../../../components/Editors/ACE/AceEditor';
import MultiPageForm from '../../Forms/MultiPageForm';
import ErrorBoundary from '../../errorBoundary';
import { FaSave, FaTrash, FaEdit } from 'react-icons/fa';
import { fetchSchemas } from './Helpers/fetchSchemas';
function SchemaSelector({ onSchemaSelect , setMode , itemToParent , onCrudSelect , passToParentAgain , hideCreateButton = false}) {
const [schemas, setSchemas] = useState([]);
const [selectedSchema, setSelectedSchema] = useState('');
const auth = useAuthUser()
const [fetchedItems, setFetchedItems] = useState([]);
const [selectedItem, setSelectedItem] = useState(null);
const [CardComponent, setCardComponent] = useState(null);
const [cardSide , setCardSide] = useState(true)
useEffect(() => {
fetchSchemas(
(fetchedSchemas) => {
setSchemas(fetchedSchemas);
console.log(fetchedSchemas);
},
(error) => console.error("Error fetching schemas:", error),
import.meta.env.VITE_REACT_APP_LEUMAS_API_ENDPOINT,
excludedSchemas
);
}, []);
const passToParent = (data) => {
console.log("Schema Selector receiving from MultiPage form" , data)
if(passToParentAgain){
passToParentAgain(data)
}
// Now we pass this into the parent for SchemaSelector
}
const handleSchemaChange = async (e) => {
const schema = e.target.value || e;
console.log("Searching for schema:", schema, "\nSelected CRUD Type:", selectedCrudType);
try {
let items = [];
// Determine action based on selected CRUD type
switch (selectedCrudType) {
case "owned":
case "edit":
case "delete":
case "histories":
// Fetch items owned by the user
items = await getItemsByOwner(`${schema}`, auth?.id, "LeumasAPI", auth?.token);
break;
case "public":
// Fetch public items (assuming getPublicItems is a function you have)
items = await getAllPublicItems(`${schema}` , "LeumasAPI" , auth?.token);
break;
case "create":
// For creating new items, use the form component from schemaToCardMapping
console.log("Creating an item")
break;
default:
console.log("Invalid CRUD type selected");
}
console.log("Fetched items:", items);
setSelectedSchema(schema);
setSelectedItem(null);
setFetchedItems(items); // Set the fetched items
onSchemaSelect(schema, schemaToCardMapping[schema], items);
} catch (error) {
console.error("Error fetching items:", error);
}
};
const handleItemChange = (e) => {
const itemId = e.target.value;
const foundItem = fetchedItems.find(item => item._id === itemId); // Ensure this matches the item ID key
if (foundItem) {
setSelectedItem(foundItem); // Set the selected item
}
if(itemToParent){
itemToParent(foundItem)
}
};
const [selectedCrudType , setSelectedCrudType] = useState("owned")
const handleCrudChange = (e) => {
const crudType = e.target.value;
console.log("Crud Type Changed" , crudType);
setSelectedCrudType(crudType);
setFetchedItems([])
handleSchemaChange(selectedSchema)
if(onCrudSelect){
onCrudSelect(crudType)
}
}
return (
<div className='w-full'>
<div className='w-full flex items-center justify-start'>
<select
className={`${LeumasBaseStyle} w-full`}
value={selectedSchema}
onChange={handleSchemaChange}
>
<option className={`bg-blue-400 text-white`} value="" disabled>Select a schema</option>
{schemas.map(schema => (
<option className={`bg-blue-400 text-white`} key={schema} value={schema}>
{schema}
</option>
))}
</select>
<select value={selectedCrudType} onChange={handleCrudChange} className={`${LeumasBaseStyle}`}>
{/* Owned means to getAllByOwner */}
<option className='bg-blue-400 text-white' value={"owned"}>Owned</option>
{/* Public Means we want to get all Public for our Model */}
<option className='bg-blue-400 text-white' value={"public"}>Public</option>
{/* */}
<option className='bg-blue-400 text-white' value="create">Create</option>
<option className='bg-blue-400 text-white' value={"edit"}>Edit</option>
<option className='bg-blue-400 text-white' value={"delete"}>Delete</option>
<option className='bg-blue-400 text-white' value={"histories"}>Histories</option>
</select>
</div>
{/* The selectedCrudType we have will determine the content below that gets rendered */}
{(selectedCrudType === "owned" || selectedCrudType === "public" || selectedCrudType === "histories" || selectedCrudType === "edit" || selectedCrudType === "delete") &&
<>
{/* if viewing owned, we need to be able to see the fetchedItems in the select, and the card component below, this is also the same for public,
*/}
{/* <p className='flex items-center justify-start gap-2'>
{selectedSchema }{` (s)`} {fetchedItems.length}
</p> */}
{/* Item select */}
{selectedSchema && (
<select className={`${LeumasBaseStyle} w-full mt-4`} onChange={handleItemChange}>
<option className={`bg-blue-400 text-white`} value="" disabled>Select an item</option>
{fetchedItems.map((item, index) => (
<option className={`bg-blue-400 text-white`} key={item._id || index} value={item._id}>
{item.name || item?.title || `Unitled ${selectedSchema} ${index} `} {/* Adjust the property if needed */}
</option>
))}
</select>
)}
{/* Render CardComponent only if selectedItem is not null */}
{selectedItem && (
<div className={`border-2 w-full max-h-[300px] overflow-auto`} onClick={() => setCardSide(!cardSide)}>
{cardSide && CardComponent ? (
<>
<CardComponent model={selectedItem} />
<div className="flex justify-around items-center mt-2">
<button onClick={(e) => {
e.stopPropagation(); // Prevent event bubbling
setCardSide(!cardSide);
}} className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-2 rounded text-xs">
Show JSON
</button>
{/* Additional Utility Buttons Here */}
</div>
</>
) : (
<div className='h-full min-h-[300px]'> {/* Remove max-height and set full height */}
{/* <div className='max-h-[250px] overflow-y-scroll'>
<AceEditorWrapper
mode="json"
content={JSON.stringify(selectedItem, null, 2)}
readOnly={true}
// Set Ace Editor height to 90%
/>
</div> */}
<div className="flex justify-around items-center mt-2 h-[10%] overflow-x-scroll text-[8px] relative top-0"> {/* Utility bar with 10% height and overflow-x-scroll */}
<button onClick={() => setCardSide(!cardSide)} className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-2 rounded">
Show Card
</button>
{/* Additional utility buttons with icons and text */}
<button className="bg-green-500 hover:bg-green-700 text-white font-bold py-1 px-2 rounded flex items-center">
<FaSave className="mr-1"/> Save
</button>
<button className="bg-yellow-500 hover:bg-yellow-700 text-white font-bold py-1 px-2 rounded flex items-center">
<FaEdit className="mr-1"/> Edit
</button>
<button className="bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-2 rounded flex items-center">
<FaTrash className="mr-1"/> Delete
</button>
</div>
</div>
)}
</div>
)}
</>
}
{(selectedCrudType === "create") && selectedSchema && schemaToCardMapping[selectedSchema] && (
<>
{schemaToCardMapping[selectedSchema].tool === null ? (
<ErrorBoundary>
<MultiPageForm
hideCreateButton={hideCreateButton}
passToParent={passToParent}
pages={schemaToCardMapping[selectedSchema].form}
/>
</ErrorBoundary>
) : (
<div>
<p>Sorry, we cannot build complex schema types with a form.</p>
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={() => setMode(schemaToCardMapping[selectedSchema].tool)}
>
Open Tool
</button>
</div>
)}
</>
)}
</div>
);
}
export default SchemaSelector;