Crudnick is a framework for creating an admin backend CMS with React and Tapioca.
- Authentication
- Login
- Log out
- Request password reset
- Reset password
- List/add/edit/delete users
- Index pages
- Search
- Sort
- Add/edit pages
- Delete records
Warning: This package is still a work-in-progress. Use at your own risk.
In the terminal, run the following commands:
npx create-react-app my-app
cd my-app
npm install --save https://github.com/jlbelanger/crudnickCreate a new file .env containing the following (but replace the values):
VITE_API_URL=https://example.local/api
VITE_FRONTEND_URL=https://example.local
VITE_COOKIE_PREFIX=example
VITE_TITLE="Example"
Update index.html:
<!-- Replace: -->
<title>React App</title>
<!-- With: -->
<title>%VITE_TITLE%</title>Create a subfolder in the src folder named Pages and a subfolder in Pages named Users. (So in all, you should have my-app/src/Pages/Users.)
Create four new files in the Users subfolder:
my-app/src/Pages/Users/Add.jsxmy-app/src/Pages/Users/Edit.jsxmy-app/src/Pages/Users/Form.jsxmy-app/src/Pages/Users/Index.jsx
Add the following to src/Pages/Users/Add.jsx:
import { AddForm } from '@jlbelanger/crudnick';
import Form from './Form';
import React from 'react';
export default function Add() {
return <AddForm apiPath="users" component={Form} path="users" singular="user" />;
}Add the following to src/Pages/Users/Edit.jsx:
import { EditForm } from '@jlbelanger/crudnick';
import Form from './Form';
import React from 'react';
import { useParams } from 'react-router';
export default function Edit() {
const { id } = useParams();
return <EditForm apiPath="users" component={Form} name="username" path="users" singular="user" url={`users/${id}`} />;
}Add the following to src/Pages/Users/Form.jsx:
import { Field } from '@jlbelanger/formosa';
import PropTypes from 'prop-types';
import React from 'react';
export default function Form({ formType }) {
return (
<div className="formosa-horizontal">
<Field autoComplete="off" label="Username" name="username" required />
<Field autoComplete="off" label="Email" name="email" type="email" required />
<Field autoComplete="off" label="Password" name="password" type="password" required={formType === 'add'} />
</div>
);
}
Form.propTypes = {
formType: PropTypes.string.isRequired,
};Add the following to src/Pages/Users/Index.jsx:
import { IndexTable } from '@jlbelanger/crudnick';
import React from 'react';
export default function Index() {
return (
<IndexTable
columns={[
{
key: 'username',
label: 'Username',
link: true,
},
{
key: 'email',
label: 'Email',
},
]}
defaultOptions={{
sortKey: 'username',
sortDir: 'asc',
}}
path="users"
title="Users"
url="users?sort=username&fields[users]=username,email"
/>
);
}Create a new file src/Routes.js containing the following:
import { ForgotPassword, Login, NotFound, PrivateRoute, ResetPassword } from '@jlbelanger/crudnick';
import { createBrowserRouter } from 'react-router';
import Layout from './Layout';
import UserAdd from './Pages/Users/Add';
import UserEdit from './Pages/Users/Edit';
import UserIndex from './Pages/Users/Index';
export default createBrowserRouter([
{
path: '/',
Component: Layout,
children: [
{
index: true,
Component: Login,
},
{
path: 'forgot-password',
Component: ForgotPassword,
},
{
path: 'reset-password/:token',
Component: ResetPassword,
},
{
path: '',
Component: PrivateRoute,
children: [
{
path: 'users',
children: [
{ index: true, Component: UserIndex },
{ path: 'add', Component: UserAdd },
{ path: ':id', Component: UserEdit },
],
},
],
},
{
path: '*',
Component: NotFound,
},
],
},
]);Create a new file src/Layout.jsx containing the following:
import { Layout as CrudnickLayout } from '@jlbelanger/crudnick';
import { Outlet } from 'react-router';
import React from 'react';
export default function MyLayout() {
const nav = [{ label: 'Users', path: '/users' }];
return (
<CrudnickLayout nav={nav}>
<Outlet />
</CrudnickLayout>
);
}Replace the contents of src/index.jsx with the following:
import '@jlbelanger/crudnick/dist/index.css';
import { createRoot } from 'react-dom/client';
import { CrudnickConfig } from '@jlbelanger/crudnick';
import { FormosaConfig } from '@jlbelanger/formosa';
import { RouterProvider } from 'react-router';
import Routes from './Routes';
import { StrictMode } from 'react';
CrudnickConfig.init({
basePath: import.meta.env.VITE_BASE_PATH,
cookiePrefix: import.meta.env.VITE_COOKIE_PREFIX,
frontendUrl: import.meta.env.VITE_FRONTEND_URL,
siteTitle: import.meta.env.VITE_TITLE,
});
FormosaConfig.init({
apiPrefix: import.meta.env.VITE_API_URL,
});
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<RouterProvider router={Routes} />
</StrictMode>,
);In the terminal, still in the my-app folder, run the following command:
npm start- Corrieography: React app and Laravel API
- Jenny's Wardrobe: React app and Laravel API
# Clone the repo
git clone https://github.com/jlbelanger/crudnick.git
cd crudnick
# Install dependencies
npm installnpm startnpm run lint