1.0.10 • Published 4 years ago
react-simple-crud-ui v1.0.10
One example worths thoudsands line of explanations.
App.tsx
import React, { useReducer, useState } from 'react';
import Axios from 'axios';
import './App.css';
import CountryForm from './CountryForm';
import LanguageForm from './LanguageForm';
import 'react-toastify/dist/ReactToastify.css';
import { ToastContainer, toast } from 'react-toastify';
import RSCU from 'react-simple-crud-ui';
import { CrudSettingModel, AuthSettingModel } from 'react-simple-crud-ui/dist/CRUD/Model';
function App() {
const AuthenticationLayer = RSCU.components.AuthenticationLayer;
const CrudStateModel = RSCU.models.CrudStateModel;
const CRUD = RSCU.components.CRUD;
const [appState, dispatchApp] = useReducer(RSCU.reducers.appReducer, new RSCU.models.AppStateModel());
const reducer = {
appState,
dispatchApp
}
const countrySetting = {
initialCrudState: new CrudStateModel(10, 'Country'),
Form: CountryForm,
ListGenerator: (crudState, dispatchCrud, mode) => {
return (
<div>
<div className="row bold border-bottom">
<div className="col-4">
Country Name
</div>
<div className="col-4">
Code
</div>
<div className="col-4">
Year
</div>
</div>
<div className="row bold border-bottom">
<div className="col-4">
<input className="form-control" placeholder="Filter By Name" defaultValue={crudState.filter.value} onChange={(e) => dispatchCrud({ type: 'FILTER', payload: { value: e.target.value } })} />
</div>
<div className="col-4">
</div>
<div className="col-4">
</div>
</div>
{
crudState.allDataPagination.map(i => {
const key = new Date().getTime() + '' + i.id;
return (
<div key={key} className="row data-record" title="Click to edit"
onClick={() => dispatchCrud({ type: 'CHANGE_MODE', payload: mode.Edit, editId: i.id })}>
<div className="col-4 grid-left ellipsis">
{i.name}
</div>
<div className="col-4 grid-center ellipsis">
{i.code}
</div>
<div className="col-4 grid-right ellipsis">
{i.year}
</div>
</div>
)
})
}
</div>
)
},
filterFn: (item: any, filterObject: any) => {
return item.name.toLowerCase().includes(filterObject.value);
},
loadAllData: (appReducer, crudReducer, mounted: boolean) => {
const { appState, dispatchApp } = appReducer;
const { crudState, dispatchCrud } = crudReducer;
Axios.post("https://some-graphql-api-endpoint", {
query: `query {
countries {
id
code
name
year
flagUrl
createdDate
updatedDate
userId
}
}`
}, {
headers: {
"token": appState.account.token
}
}).then(axiosData => {
const data = axiosData.data;
if (data.data !== undefined && data.data !== null) {
if (!!mounted) {
dispatchCrud({ type: 'GET_ALL_SUCCESS', payload: data.data.countries })
if (crudState.filter !== '') {
dispatchCrud({ type: 'FILTER', payload: crudState.filter })
}
} else {
console.log('list is unmounted ', mounted);
}
} else {
if (!!mounted) {
dispatchCrud({ type: 'GET_ALL_FAIL', payload: JSON.stringify(data) })
} else {
console.log('list is unmounted ', mounted);
console.log(data);
}
}
}).catch(
err => {
console.log('list is unmounted ', mounted);
console.log(err);
if (!!mounted) {
dispatchCrud({ type: 'GET_ALL_FAIL', payload: JSON.stringify(err) });
}
localStorage.removeItem('account');
dispatchApp({ type: 'LOGOUT' });
}
);
},
addCrud: (appReducer, crudReducer, data) => {
// eslint-disable-next-line
const { appState, dispatchApp } = appReducer;
// eslint-disable-next-line
const { crudState, dispatchCrud } = crudReducer;
dispatchCrud({ type: 'START' });
Axios.post("https://some-graphql-api-endpoint", {
query: `mutation createCountry($data: CountryModel!) {
createCountry(data: $data) {
id
code
name
year
flagUrl
createdDate
updatedDate
userId
}
}
`,
variables: {
"data": { ...data, year: parseInt(data.year) }
}
}, {
headers: {
"token": appState.account.token
}
}).then(i => {
const data = i.data;
// console.log(data);
if (data.data !== undefined && data.data !== null) {
// const createCountry = data.data.createCountry;
// console.log(createCountry);
toast.success("Country is created!");
} else {
console.log(data);
toast.warning("Create failed!");
}
dispatchCrud({ type: 'END' });
}).catch(i => {
console.log(i);
dispatchCrud({ type: 'END' });
toast.error("Country can not be created! Something goes wrong here!");
})
},
loadOneCrud: (appReducer, crudReducer, setThisCrud) => {
// eslint-disable-next-line
const { appState, dispatchApp } = appReducer;
// eslint-disable-next-line
const { crudState, dispatchCrud } = crudReducer;
dispatchCrud({ type: 'START' });
Axios.post("https://some-graphql-api-endpoint", {
query: `query country($id: ID!) {
country(id: $id) {
id
code
name
year
flagUrl
createdDate
updatedDate
userId
}
}`,
variables: {
"id": crudState.editId
}
}, {
headers: {
"token": appState.account.token
}
}).then(i => {
const data = i.data;
// console.log(data);
if (data.data !== null && data.data !== undefined) {
const country = data.data.country;
// console.log(country);
setThisCrud(country);
dispatchCrud({ type: 'END' });
} else {
console.log('error ', data);
dispatchCrud({ type: 'END' });
}
}).catch(i => {
console.log(i);
dispatchCrud({ type: 'END' });
})
},
editCrud: (appReducer, crudReducer, data) => {
// eslint-disable-next-line
const { appState, dispatchApp } = appReducer;
// eslint-disable-next-line
const { crudState, dispatchCrud } = crudReducer;
dispatchCrud({ type: 'START' });
Axios.post("https://some-graphql-api-endpoint", {
query: `mutation updateCountry($data: CountryModel!) {
updateCountry(data: $data) {
id
code
name
year
flagUrl
createdDate
updatedDate
userId
}
}
`,
variables: {
"data": { ...data, year: parseInt(data.year) }
}
}, {
headers: {
"token": appState.account.token
}
}).then(i => {
const data = i.data;
// console.log(data);
if (data.data !== undefined && data.data !== null) {
// const updateCountry = data.data.updateCountry;
// console.log(updateCountry);
toast.success("Country is updated!");
} else {
console.log(data);
toast.warning("Update failed");
}
dispatchCrud({ type: 'END' });
}).catch(i => {
console.log(i);
dispatchCrud({ type: 'END' });
toast.error("Country can not be updated! Something goes wrong here!");
})
},
deleteCrud: (appReducer, crudReducer, data) => {
// eslint-disable-next-line
const { appState, dispatchApp } = appReducer;
// eslint-disable-next-line
const { crudState, dispatchCrud } = crudReducer;
dispatchCrud({ type: 'START' });
Axios.post("https://some-graphql-api-endpoint", {
query: `mutation deleteCountry($data: CountryModel!) {
deleteCountry(data: $data) {
id
code
name
year
flagUrl
}
}
`,
variables: {
"data": {
"id": data.id
}
}
}, {
headers: {
"token": appState.account.token
}
}).then(i => {
const data = i.data;
// console.log(data);
if (data.data !== undefined && data.data !== null) {
// const deleteCountry = data.data.deleteCountry;
// console.log(deleteCountry);
toast.success("Country is deleted!");
} else {
console.log(data);
toast.warning("Delete failed");
}
dispatchCrud({ type: 'END' });
}).catch(i => {
console.log(i);
dispatchCrud({ type: 'END' });
toast.error("Country can not be deleted! Something goes wrong here!");
})
}
} as CrudSettingModel;
const languageSetting: CrudSettingModel = {
initialCrudState: new CrudStateModel(10, 'Language'),
Form: LanguageForm,
ListGenerator: (crudState, dispatchCrud, MODE) => {
return (
<div>
<div className="row bold border-bottom">
<div className="col-4">
Name
</div>
<div className="col-4">
Native Name
</div>
<div className="col-4">
Code
</div>
</div>
<div className="row bold border-bottom">
<div className="col-4">
<input className="form-control" placeholder="Filter By Name" defaultValue={crudState.filter.value} onChange={(e) => dispatchCrud({ type: 'FILTER', payload: { value: e.target.value } })} />
</div>
<div className="col-4">
</div>
<div className="col-4">
</div>
</div>
{
crudState.allDataPagination.map(i => {
const key = new Date().getTime() + '' + i.id;
return (
<div key={key} className="row data-record" title="Click to edit"
onClick={() => dispatchCrud({ type: 'CHANGE_MODE', payload: MODE.Edit, editId: i.id })}>
<div className="col-4 grid-left ellipsis">
{i.name}
</div>
<div className="col-4 grid-center ellipsis">
{i.nativeName}
</div>
<div className="col-4 grid-right ellipsis">
{i.code}
</div>
</div>
)
})
}
</div>
)
},
filterFn: (item, filterObject) => {
return item.name.toLowerCase().includes(filterObject.value);
},
loadAllData: (appReducer, crudReducer, mounted) => {
const { appState, dispatchApp } = appReducer;
const { crudState, dispatchCrud } = crudReducer;
Axios.post("https://some-graphql-api-endpoint", {
query: `query {
languages {
id
code
name
nativeName
createdDate
updatedDate
userId
}
}`
}, {
headers: {
"token": appState.account.token
}
}).then(i => {
const data = i.data;
// console.log(data);
if (data.data !== undefined && data.data !== null) {
if (!!mounted) {
dispatchCrud({ type: 'GET_ALL_SUCCESS', payload: data.data.languages })
if (crudState.filter !== '') {
dispatchCrud({ type: 'FILTER', payload: crudState.filter })
}
} else {
console.log('unmounted');
}
} else {
console.log(data);
if (!!mounted) {
dispatchCrud({ type: 'GET_ALL_FAIL', payload: JSON.stringify(data) })
} else {
console.log('unmounted');
}
}
}
).catch(
i => {
console.log(i);
if (!!mounted) {
dispatchCrud({ type: 'GET_ALL_FAIL', payload: JSON.stringify(i) })
} else {
console.log('unmounted');
}
localStorage.removeItem('account');
dispatchApp({ type: 'LOGOUT' });
}
);
},
addCrud: (appReducer, crudReducer, data) => {
// eslint-disable-next-line
const { appState, dispatchApp } = appReducer;
// eslint-disable-next-line
const { crudState, dispatchCrud } = crudReducer;
dispatchCrud({ type: 'START' });
Axios.post("https://some-graphql-api-endpoint", {
query: `mutation createLanguage($data: LanguageModel!) {
createLanguage(data: $data) {
id
code
name
nativeName
createdDate
updatedDate
userId
}
}`,
variables: {
"data": data
}
}, {
headers: {
"token": appState.account.token
}
}).then(i => {
const data = i.data;
// console.log(data);
if (data.data !== undefined && data.data !== null) {
// const createLanguage = data.data.createLanguage;
// console.log(createLanguage);
toast.success("Language is created!");
} else {
console.log(data);
toast.warning("Create failed!");
}
dispatchCrud({ type: 'END' });
}).catch(i => {
console.log(i);
dispatchCrud({ type: 'END' });
toast.error("Language can not be created! Something goes wrong here!");
})
},
loadOneCrud: (appReducer, crudReducer, setThisCrud) => {
// eslint-disable-next-line
const { appState, dispatchApp } = appReducer;
// eslint-disable-next-line
const { crudState, dispatchCrud } = crudReducer;
dispatchCrud({ type: 'START' });
Axios.post("https://some-graphql-api-endpoint", {
query: `query language($id: ID!) {
language(id: $id) {
id
code
name
nativeName
createdDate
updatedDate
userId
}
}`,
variables: {
"id": crudState.editId
}
}, {
headers: {
"token": appState.account.token
}
}).then(i => {
const data = i.data;
// console.log(data);
if (data.data !== null && data.data !== undefined) {
const language = data.data.language;
// console.log(language);
setThisCrud(language);
dispatchCrud({ type: 'END' });
} else {
console.log('error ', data);
dispatchCrud({ type: 'END' });
}
}).catch(i => {
console.log(i);
dispatchCrud({ type: 'END' });
})
},
editCrud: (appReducer, crudReducer, data) => {
// eslint-disable-next-line
const { appState, dispatchApp } = appReducer;
// eslint-disable-next-line
const { crudState, dispatchCrud } = crudReducer;
dispatchCrud({ type: 'START' });
Axios.post("https://some-graphql-api-endpoint", {
query: `mutation updateLanguage($data: LanguageModel!) {
updateLanguage(data: $data) {
id
code
name
nativeName
createdDate
updatedDate
userId
}
}`,
variables: {
"data": data
}
}, {
headers: {
"token": appState.account.token
}
}).then(i => {
const data = i.data;
// console.log(data);
if (data.data !== undefined && data.data !== null) {
// const updateLanguage = data.data.updateLanguage;
// console.log(updateLanguage);
toast.success("Language is updated!");
} else {
console.log(data);
toast.warning("Update failed");
}
dispatchCrud({ type: 'END' });
}).catch(i => {
console.log(i);
dispatchCrud({ type: 'END' });
toast.error("Language can not be updated! Something goes wrong here!");
})
},
deleteCrud: (appReducer, crudReducer, data) => {
// eslint-disable-next-line
const { appState, dispatchApp } = appReducer;
// eslint-disable-next-line
const { crudState, dispatchCrud } = crudReducer;
dispatchCrud({ type: 'START' });
Axios.post("https://some-graphql-api-endpoint", {
query: `mutation deleteLanguage($data: LanguageModel!) {
deleteLanguage(data: $data) {
id
code
name
nativeName
}
}
`,
variables: {
"data": {
"id": data.id
}
}
}, {
headers: {
"token": appState.account.token
}
}).then(i => {
const data = i.data;
// console.log(data);
if (data.data !== undefined && data.data !== null) {
// const deleteLanguage = data.data.deleteLanguage;
// console.log(deleteLanguage);
toast.success("Language is deleted!");
} else {
console.log(data);
toast.warning("Delete failed");
}
dispatchCrud({ type: 'END' });
}).catch(i => {
console.log(i);
dispatchCrud({ type: 'END' });
toast.error("Language can not be deleted! Something goes wrong here!");
})
}
} as CrudSettingModel;
const MODE = {
COUNTRY: "COUNTRY",
LANGUAGE: "LANGUAGE"
}
const [currentMode, setCurrentMode] = useState(MODE.COUNTRY);
const isCountryMode = currentMode === MODE.COUNTRY;
const isLanguageMode = currentMode === MODE.LANGUAGE;
const authSetting = {
app: {
title: 'Demo Site',
loginTitle: 'Gateway'
},
components: {
CustomComponentGenerator: () => {
if (!!appState.auth) {
return (
<div className="width-100" style={{ marginTop: '20px' }}>
<button type="button" className={'btn ' + (!!isCountryMode ? 'btn-success' : 'btn-dark')} onClick={() => setCurrentMode(MODE.COUNTRY)}>Country</button>
<button type="button" className={'btn ' + (!!isLanguageMode ? 'btn-success' : 'btn-dark')} onClick={() => setCurrentMode(MODE.LANGUAGE)}>Language</button>
<hr></hr>
{!!isCountryMode ? <CRUD setting={countrySetting} reducer={reducer} /> : ''}
{!!isLanguageMode ? <CRUD setting={languageSetting} reducer={reducer} /> : ''}
</div>
)
}
return (
<div>
</div>
);
}
},
login: {
url: "https://some-authenticated-graphql-api-endpoins",
data: (username: string, password: string) => {
return {
query: `
query login($username: String!, $password: String!) {
login(username: $username, password: $password) {
_id
token
username
firstName
lastName
email
role
}
}
`,
variables: {
username: username,
password: password
}
}
},
headers: {}
},
} as AuthSettingModel;
return (
<div className="App container">
<AuthenticationLayer reducer={reducer} setting={authSetting} />
<ToastContainer />
</div>
)
}
export default App;
CountryForm.tsx
import React from 'react';
import { useForm } from 'react-hook-form';
function CountryForm(props: any) {
const { id: inputId, name: inputName, code: inputCode, flagUrl: inputFlagUrl, year: inputYear, updatedDate: inputUpdatedDate } = props.form;
const { onSubmit, onSubmitDeleting } = props;
const { register, handleSubmit, errors } = useForm();
const currentUpdatedDate = () => {
if (inputUpdatedDate === undefined) {
return new Date().toDateString() + ' ' + new Date().toTimeString();
}
return new Date(parseInt(inputUpdatedDate)).toDateString() + ' ' + new Date(parseInt(inputUpdatedDate)).toTimeString()
}
const onFormSubmit = (data: any) => {
// console.log('on form submit, using handle submit from useForm, ', data);
onSubmit(inputId === undefined || inputId === '' ? data : { id: inputId, ...data});
}
const onFormSubmitDeleting = (e: any) => {
e.preventDefault();
onSubmitDeleting({ id: inputId });
}
return (
<form onSubmit={handleSubmit(onFormSubmit)}>
<div className="form-row">
<div className="form-group col-md-6">
<label htmlFor="name">Name</label>
<input type="text" className="form-control" id="name" name="name" defaultValue={inputName} ref={register({ required: true })} />
<p className="error">{errors.name && "Name is required"}</p>
</div>
<div className="form-group col-md-6">
<label htmlFor="code">Code</label>
<input type="text" className="form-control" id="code" name="code" defaultValue={inputCode} ref={register({ required: true })}/>
<p className="error">{errors.code && "Code is required"}</p>
</div>
</div>
<div className="form-group">
<label htmlFor="flagUrl">Flag URL</label>
<input type="text" className="form-control" id="flagUrl" name="flagUrl" placeholder="https://photo.jpg" defaultValue={inputFlagUrl} ref={register({ required: true })} />
<p className="error">{errors.flagUrl && "Flag URL is required"}</p>
</div>
<div className="form-row">
<div className="form-group col-md-6">
<label htmlFor="year">Year</label>
<input type="number" className="form-control" id="year" name="year" defaultValue={inputYear} ref={register({ required: true, min: 1200, max: 2222 })} />
<p className="error">{errors.year?.type === "required" && "Year is required"}</p>
<p className="error">{errors.year?.type === "min" && "Year must be greater than 1200"}</p>
<p className="error">{errors.year?.type === "max" && "Year must be less than 2222"}</p>
</div>
<div className="form-group col-md-6">
<label htmlFor="updatedDate">Latest Update Date</label>
<input type="text" disabled className="form-control" id="updatedDate" readOnly defaultValue={currentUpdatedDate()} />
</div>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
<hr></hr>
{ inputId !== undefined ? <button className="btn btn-danger" onClick={onFormSubmitDeleting}>Delete</button> : '' }
</form>
)
}
export default CountryForm;
LanguageForm.tsx
import React from 'react';
import { useForm } from 'react-hook-form';
function LanguageForm(props: any) {
// console.log(props);
const { id: inputId, name: inputName, code: inputCode, nativeName: inputNativeName, updatedDate: inputUpdatedDate } = props.form;
const { onSubmit, onSubmitDeleting } = props;
const { register, handleSubmit, errors } = useForm();
const currentUpdatedDate = () => {
if (inputUpdatedDate === undefined) {
return new Date().toDateString() + ' ' + new Date().toTimeString();
}
return new Date(parseInt(inputUpdatedDate)).toDateString() + ' ' + new Date(parseInt(inputUpdatedDate)).toTimeString()
}
const onFormSubmit = (data: any) => {
// console.log('on form submit, using handle submit from useForm, ', data);
onSubmit(inputId === undefined || inputId === '' ? data : { id: inputId, ...data});
}
const onFormSubmitDeleting = (e: any) => {
e.preventDefault();
onSubmitDeleting({ id: inputId });
}
return (
<form onSubmit={handleSubmit(onFormSubmit)}>
<div className="form-row">
<div className="form-group col-md-6">
<label htmlFor="name">Name</label>
<input type="text" className="form-control" id="name" name="name" defaultValue={inputName} ref={register({ required: true })} />
<p className="error">{errors.name && "Name is required"}</p>
</div>
<div className="form-group col-md-6">
<label htmlFor="code">Code</label>
<input type="text" className="form-control" id="code" name="code" defaultValue={inputCode} ref={register({ required: true })}/>
<p className="error">{errors.code && "Code is required"}</p>
</div>
</div>
<div className="form-group">
<label htmlFor="nativeName">Native Name</label>
<input type="text" className="form-control" id="nativeName" name="nativeName" placeholder="日本語" defaultValue={inputNativeName} ref={register({ required: true })} />
<p className="error">{errors.nativeName && "Flag URL is required"}</p>
</div>
<div className="form-row">
<div className="form-group col-md-12">
<label htmlFor="updatedDate">Latest Update Date</label>
<input type="text" disabled className="form-control" id="updatedDate" readOnly defaultValue={currentUpdatedDate()} />
</div>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
<hr></hr>
{ inputId !== undefined ? <button className="btn btn-danger" onClick={onFormSubmitDeleting}>Delete</button> : '' }
</form>
)
}
export default LanguageForm;