UNPKG

react-simple-crud-ui

Version:

Written in React with hooks, simple CRUD UI with authentication.

571 lines (545 loc) 27.5 kB
function ___$insertStyle(css) { if (!css) { return; } if (typeof window === 'undefined') { return; } var style = document.createElement('style'); style.setAttribute('type', 'text/css'); style.innerHTML = css; document.head.appendChild(style); return css; } Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var React = require('react'); var React__default = _interopDefault(React); var reactBootstrap = require('react-bootstrap'); var reactConfirmAlert = require('react-confirm-alert'); require('react-confirm-alert/src/react-confirm-alert.css'); var Axios = _interopDefault(require('axios')); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __spreadArrays() { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; } var AuthSettingModel = /** @class */ (function () { function AuthSettingModel(app, components, login) { this.app = app; this.components = components; this.login = login; } return AuthSettingModel; }()); var CrudSettingModel = /** @class */ (function () { function CrudSettingModel(initialCrudState, Form, ListGenerator, filterFn, loadAllData, addCrud, loadOneCrud, editCrud, deleteCrud) { this.initialCrudState = initialCrudState; this.Form = Form; this.ListGenerator = ListGenerator; this.filterFn = filterFn; this.loadAllData = loadAllData; this.addCrud = addCrud; this.loadOneCrud = loadOneCrud; this.editCrud = editCrud; this.deleteCrud = deleteCrud; } return CrudSettingModel; }()); var MODE = { LIST: 'List', ADD: 'Add', EDIT: 'Edit' }; var ModeModel = /** @class */ (function () { function ModeModel() { this.List = MODE.LIST; this.Add = MODE.ADD; this.Edit = MODE.EDIT; } return ModeModel; }()); var FilterModel = /** @class */ (function () { function FilterModel(key, value) { this.key = key; this.value = value; } return FilterModel; }()); var BreadcrumbModel = /** @class */ (function () { function BreadcrumbModel(part1, part2) { this.part1 = part1; this.part2 = part2; } return BreadcrumbModel; }()); var CrudStateModel = /** @class */ (function () { function CrudStateModel(paginationLimit, breadcrumbHome) { this.loading = false; this.error = false; this.errorMessage = ''; this.allData = []; this.allDataFilter = []; this.allDataPagination = []; this.paginationLimit = paginationLimit; this.paginationPage = 1; this.paginationTotalPages = 1; this.mode = MODE.LIST; this.editId = ''; this.breadcrumb = new BreadcrumbModel(MODE.LIST, breadcrumbHome); this.breadcrumbHome = breadcrumbHome; this.filter = new FilterModel('', ''); } return CrudStateModel; }()); var calculatePaginationTotalPages = function (data) { var roundNumber = __spreadArrays(data).length / 10; var isRound = __spreadArrays(data).length % 10 === 0; var calculatedTotalPages = Math.floor(roundNumber); if (!!isRound) { return calculatedTotalPages - 1; } return calculatedTotalPages; }; var GET_ALL_START = 'GET_ALL_START'; var GET_ALL_SUCCESS = 'GET_ALL_SUCCESS'; var GET_ALL_FAIL = 'GET_ALL_FAIL'; var CHANGE_PAGE = 'CHANGE_PAGE'; var FILTER = 'FILTER'; var CHANGE_MODE = 'CHANGE_MODE'; var BREADCRUMB_RETURN_HOME = 'BREADCRUMB_RETURN_HOME'; var START = 'START'; var END = 'END'; var crudReducerGenerator = function (filterFn) { var crudReducer = function (state, action) { var MODE = new ModeModel(); switch (action.type) { case GET_ALL_START: return __assign(__assign({}, state), { loading: true }); case GET_ALL_SUCCESS: var allDataGetAll = action.payload.sort(); return __assign(__assign({}, state), { allData: __spreadArrays(allDataGetAll), allDataFilter: __spreadArrays(allDataGetAll), allDataPagination: __spreadArrays(allDataGetAll).slice(0, state.paginationLimit), paginationTotalPages: calculatePaginationTotalPages(__spreadArrays(allDataGetAll)), loading: false, error: false, errorMessage: '' }); case GET_ALL_FAIL: return __assign(__assign({}, state), { allData: [], error: true, errorMessage: action.payload }); case CHANGE_PAGE: var changePageCountriesPagination = __spreadArrays(state.allDataFilter).slice(state.paginationLimit * action.payload, (state.paginationLimit * action.payload) + state.paginationLimit); return __assign(__assign({}, state), { paginationPage: action.payload, allDataPagination: changePageCountriesPagination }); case FILTER: if (action.payload === undefined) { console.log('Invalid Filter Payload, no value is found!'); return __assign({}, state); } var filtered = action.payload.value === '' ? __spreadArrays(state.allData) : __spreadArrays(state.allData).filter(function (i) { return filterFn(i, action.payload); }); var currentFilterTotalPages = calculatePaginationTotalPages(__spreadArrays(filtered)); var filteredPagination = filtered.slice(0, state.paginationLimit); return __assign(__assign({}, state), { allDataFilter: filtered, allDataPagination: filteredPagination, paginationPage: 1, filter: action.payload, paginationTotalPages: currentFilterTotalPages === 0 ? 1 : currentFilterTotalPages }); case CHANGE_MODE: return __assign(__assign({}, state), { mode: action.payload, editId: action.payload === MODE.Edit ? action.editId : '', breadcrumb: { part1: MODE.List, part2: action.payload } }); case BREADCRUMB_RETURN_HOME: return __assign(__assign({}, state), { mode: MODE.List, breadcrumb: { part1: MODE.List, part2: state.breadcrumbHome, } }); case START: return __assign(__assign({}, state), { loading: true }); case END: return __assign(__assign({}, state), { loading: false }); default: return __assign({}, state); } }; return crudReducer; }; function generateShowRange(totalPages, currentPage) { var showRange = []; for (var i = 1; i <= totalPages; i++) { if (i === currentPage) { showRange.push(i); } else if (i === currentPage - 1) { showRange.push(i); } else if (i === currentPage - 2) { showRange.push(i); } else if (i === currentPage + 1) { showRange.push(i); } else if (i === currentPage + 2) { showRange.push(i); } else { showRange.push(0); } } var newShowRange = []; for (var i = 0; i < showRange.length; i++) { var item = showRange[i]; if (i + 1 === showRange.length) { var willPush1 = totalPages; var willPush2 = totalPages - 1; var willPush3 = totalPages - 2; if (!newShowRange.includes(willPush3)) { newShowRange.push(willPush3); } if (!newShowRange.includes(willPush2)) { newShowRange.push(willPush2); } if (!newShowRange.includes(willPush1)) { newShowRange.push(willPush1); } continue; } if (i === 0) { newShowRange.push(item); continue; } var previousItem = showRange[i - 1]; if (item !== previousItem) { newShowRange.push(item); } } var preFilter = __spreadArrays(newShowRange.filter(function (i) { return i >= 0; })); if ((preFilter.includes(1) || preFilter.includes(2) || preFilter.includes(3)) && preFilter.length <= 4) { return preFilter.filter(function (i) { return i !== 0; }); } return preFilter; } function PaginationComponent(props) { var totalPages = props.totalPages; var currentPage = props.currentPage; var paginationItemComponent = function (pageNumber, active, key) { if (pageNumber === 0) { return React__default.createElement(reactBootstrap.Pagination.Ellipsis, { key: key }); } if (!!active) { return React__default.createElement(reactBootstrap.Pagination.Item, { key: key, active: true }, pageNumber); } else { return React__default.createElement(reactBootstrap.Pagination.Item, { key: key, onClick: function () { return props.onChange(pageNumber); } }, pageNumber); } }; var showRange = generateShowRange(totalPages, currentPage); var handlePageNumber = function (page) { if (page <= 0) { return 1; } if (page >= totalPages) { return totalPages; } return page; }; return (React__default.createElement(reactBootstrap.Pagination, null, React__default.createElement(reactBootstrap.Pagination.First, { onClick: function () { return props.onChange(1); } }), React__default.createElement(reactBootstrap.Pagination.Prev, { onClick: function () { return props.onChange(handlePageNumber(currentPage - 1)); } }), showRange.map(function (i) { var key = new Date().getTime() + i + (1000000 * Math.random()); return (paginationItemComponent(i, currentPage === i, key)); }), React__default.createElement(reactBootstrap.Pagination.Next, { onClick: function () { return props.onChange(handlePageNumber(currentPage + 1)); } }), React__default.createElement(reactBootstrap.Pagination.Last, { onClick: function () { return props.onChange(handlePageNumber(totalPages)); } }))); } function List(props) { var _a = props.crudReducer, crudState = _a.crudState, dispatchCrud = _a.dispatchCrud; var MODE = new ModeModel(); React.useEffect(function () { var mounted = true; dispatchCrud({ type: 'GET_ALL_START' }); props.loadAllData(props.appReducer, props.crudReducer, mounted); return function () { mounted = false; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); //pagination var onPaginationChange = function (page) { dispatchCrud({ type: 'CHANGE_PAGE', payload: page }); }; return (React__default.createElement(React__default.Fragment, null, !crudState.loading ? React__default.createElement("div", { className: "row" }, React__default.createElement("div", { className: "col-10 text-left" }, React__default.createElement("button", { style: { display: 'none' }, type: "button", className: "btn btn-danger" }, "Reload")), React__default.createElement("div", { className: "col-2 text-right" }, React__default.createElement("button", { type: "button", className: "btn btn-info", onClick: function () { return dispatchCrud({ type: 'CHANGE_MODE', payload: MODE.Add }); } }, "+"))) : '', !!crudState.loading ? React__default.createElement("div", { className: "spinner-grow text-danger", role: "status" }, React__default.createElement("span", { className: "sr-only" }, "Loading...")) : '', crudState.allData.length > 0 && !crudState.loading ? (React__default.createElement("div", null, props.ListGenerator(crudState, dispatchCrud, MODE), React__default.createElement("div", { className: "flex-center-all margin-top-20" }, React__default.createElement(PaginationComponent, { currentPage: crudState.paginationPage, totalPages: crudState.paginationTotalPages, onChange: onPaginationChange })))) : '')); } function Add(props) { var dispatchCrud = props.crudReducer.dispatchCrud; var Form = props.Form; var onSubmitAdding = function (data) { props.addCrud(props.appReducer, props.crudReducer, __assign({}, data)); dispatchCrud({ type: 'BREADCRUMB_RETURN_HOME' }); }; return (React__default.createElement("div", null, React__default.createElement("span", null, React__default.createElement("b", null, "Add Country")), React__default.createElement(Form, { form: {}, onSubmit: onSubmitAdding }))); } function Edit(props) { var _a = props.crudReducer, crudState = _a.crudState, dispatchCrud = _a.dispatchCrud; var Form = props.Form; var _b = React.useState(undefined), thisCrud = _b[0], setThisCrud = _b[1]; var loadOneCrudContainer = function () { props.loadOneCrud(props.appReducer, props.crudReducer, setThisCrud); }; var useMountEffect = function (fun) { return React.useEffect(fun, []); }; useMountEffect(loadOneCrudContainer); var onSubmitEditing = function (data) { props.editCrud(props.appReducer, props.crudReducer, __assign({}, data)); dispatchCrud({ type: 'BREADCRUMB_RETURN_HOME' }); }; var onSubmitDeleting = function (data) { reactConfirmAlert.confirmAlert({ customUI: function (_a) { var onClose = _a.onClose; return (React__default.createElement("div", { className: 'custom-delete-confirm-ui' }, React__default.createElement("h1", null, "Are you sure?"), React__default.createElement("p", null, "You want to delete this?"), React__default.createElement("button", { onClick: onClose }, "No"), React__default.createElement("button", { onClick: function () { props.deleteCrud(props.appReducer, props.crudReducer, __assign({}, data)); dispatchCrud({ type: 'BREADCRUMB_RETURN_HOME' }); onClose(); } }, "Yes, Delete it!"))); } }); }; return (React__default.createElement("div", null, React__default.createElement("span", null, React__default.createElement("b", null, "Edit Country")), !crudState.loading && thisCrud !== undefined ? React__default.createElement(Form, { form: thisCrud, onSubmit: onSubmitEditing, onSubmitDeleting: onSubmitDeleting }) : '')); } function BreadcrumbComponent(props) { var _a = props.reducer, bcState = _a.bcState, bcDispatch = _a.bcDispatch; var part1Click = function (value) { bcDispatch({ type: 'BREADCRUMB_RETURN_HOME', payload: value }); }; return (React__default.createElement(reactBootstrap.Breadcrumb, null, React__default.createElement(reactBootstrap.Breadcrumb.Item, { onClick: function () { return part1Click(bcState.breadcrumb.part1); } }, bcState.breadcrumb.part1), React__default.createElement(reactBootstrap.Breadcrumb.Item, { active: true }, bcState.breadcrumb.part2))); } function CrudContainer(props) { var setting = props.setting; var initial = new CrudStateModel(setting.initialCrudState.paginationLimit, setting.initialCrudState.breadcrumbHome); var crudReducer = crudReducerGenerator(setting.filterFn); var _a = React.useReducer(crudReducer, initial), crudState = _a[0], dispatchCrud = _a[1]; var MODE = new ModeModel(); return (React__default.createElement(React__default.Fragment, null, React__default.createElement(BreadcrumbComponent, { reducer: { bcState: crudState, bcDispatch: dispatchCrud } }), crudState.mode === MODE.List ? React__default.createElement(List, { appReducer: props.reducer, crudReducer: { crudState: crudState, dispatchCrud: dispatchCrud }, loadAllData: setting.loadAllData, ListGenerator: setting.ListGenerator }) : '', crudState.mode === MODE.Add ? React__default.createElement(Add, { appReducer: props.reducer, crudReducer: { crudState: crudState, dispatchCrud: dispatchCrud }, addCrud: setting.addCrud, Form: setting.Form }) : '', crudState.mode === MODE.Edit ? React__default.createElement(Edit, { appReducer: props.reducer, crudReducer: { crudState: crudState, dispatchCrud: dispatchCrud }, loadOneCrud: setting.loadOneCrud, editCrud: setting.editCrud, deleteCrud: setting.deleteCrud, Form: setting.Form }) : '', !!crudState.loading ? React__default.createElement("div", { className: "row flex-center-all" }, React__default.createElement("div", { className: "spinner-grow text-danger", role: "status" }, React__default.createElement("span", { className: "sr-only" }, "Loading..."))) : '')); } function Authentication(props) { var _a = props.reducer, appState = _a.appState, dispatchApp = _a.dispatchApp; var setting = props.setting; var _b = React.useState(''), username = _b[0], setUsername = _b[1]; var _c = React.useState(''), password = _c[0], setPassword = _c[1]; // API integration - start var defaultLoginResponseHandler = function (i) { var data = i.data; if (data.data === undefined || data.data === null) { console.log('error'); var errorMessage = data.errors.map(function (j) { var itemMessage = j.message; // const itemExtension = i.extensions.code; // return `${itemExtension}: ${itemMessage}`; return "" + itemMessage; }).join(', '); dispatchApp({ type: 'LOGIN_ERROR', payload: errorMessage }); } else { var accountData = JSON.stringify(data.data.login); localStorage.setItem('account', accountData); dispatchApp({ type: 'LOGIN_SUCCESS', payload: JSON.parse(accountData) }); } }; var loginResponseHandler = setting.login.responseCallback === undefined ? defaultLoginResponseHandler : setting.login.responseCallback; var defaultLoginErrorHandler = function (i) { console.log(i); dispatchApp({ type: 'LOGIN_ERROR', payload: JSON.stringify(i) }); }; var loginErrorHandler = setting.login.errorCallback === undefined ? defaultLoginErrorHandler : setting.login.errorCallback; var login = function (e) { !!e ? e.preventDefault() : console.log('No event is fired!'); dispatchApp({ type: 'START' }); setUsername(''); setPassword(''); Axios.post(setting.login.url, setting.login.data(username, password), { headers: setting.login.headers }).then(loginResponseHandler).catch(loginErrorHandler); }; var defaultLogout = function () { localStorage.removeItem('account'); dispatchApp({ type: 'LOGOUT' }); }; var logout = setting.login.logout === undefined ? defaultLogout : setting.login.logout; // API integration - end // child components - start var loginComponent = setting.components.LoginComponentGenerator === undefined ? function (loginFn, setUsernameFn, setPasswordFn) { return (React__default.createElement("form", { onSubmit: loginFn, className: "margin-top-20" }, React__default.createElement("div", { className: "form-group" }, React__default.createElement("input", { type: "text", className: "form-control", id: "username", placeholder: "Username", value: username, onChange: function (e) { return setUsernameFn(e.target.value); } })), React__default.createElement("div", { className: "form-group" }, React__default.createElement("input", { type: "password", className: "form-control", id: "password", placeholder: "Password", value: password, onChange: function (e) { return setPasswordFn(e.target.value); } })), React__default.createElement("button", { type: "submit", className: "btn btn-primary" }, "Login"))); } : setting.components.LoginComponentGenerator; var errorComponent = setting.components.ErrorComponent === undefined ? function () { return (React__default.createElement("div", { style: { marginTop: '10px' } }, React__default.createElement("span", { style: { color: 'red' } }, appState.errorMessage))); } : setting.components.ErrorComponent; var authHeaderComponent = setting.components.HeaderComponent === undefined ? function () { return (!!appState.auth ? React__default.createElement("div", { className: "width-100" }, React__default.createElement("p", { style: { display: 'none' } }, React__default.createElement("b", null, "Username: "), appState.account.username), React__default.createElement("p", { style: { display: 'none' } }, React__default.createElement("b", null, "Email: "), appState.account.email), React__default.createElement("div", { className: "row" }, React__default.createElement("div", { className: "col-6 text-left" }, React__default.createElement("i", null, "Welcome back, ", appState.account.firstName, " ", appState.account.lastName, "! ", React__default.createElement("span", { style: { display: 'none' } }, "(", appState.account.role, ")"))), React__default.createElement("div", { className: "col-6 text-right" }, logoutComponent))) : ''); } : setting.components.HeaderComponent; var loadingComponent = (!!appState.loading ? React__default.createElement("button", { style: { marginTop: '15px' }, className: "btn btn-primary", type: "button", disabled: true }, React__default.createElement("span", { className: "spinner-grow spinner-grow-sm", role: "status", "aria-hidden": "true" }), "Loading...") : ''); var logoutComponent = (React__default.createElement("button", { type: "button", className: "btn btn-danger", onClick: logout }, "Logout")); // child components - end return (React__default.createElement(React__default.Fragment, null, React__default.createElement("header", { className: "margin-top-20 flex-center-all break-all" }, !!appState.auth ? React__default.createElement("h1", null, setting.app.title) : React__default.createElement("h1", null, setting.app.loginTitle), authHeaderComponent()), React__default.createElement("div", { className: "flex-center-all" }, loadingComponent), !appState.loading ? React__default.createElement("main", { className: "flex-center-all" }, !appState.auth ? loginComponent(login, setUsername, setPassword) : '', setting.components.CustomComponentGenerator(), !!appState.error ? errorComponent() : '') : '')); } var appReducer = function (state, action) { switch (action.type) { case 'START': return __assign(__assign({}, state), { loading: true }); case 'END': return __assign(__assign({}, state), { loading: false }); case 'CHANGE': return __assign(__assign({}, state), action.payload); case 'LOGIN_ERROR': return __assign(__assign({}, state), { error: true, errorMessage: action.payload, loading: false, auth: false, account: {} }); case 'LOGIN_SUCCESS': return __assign(__assign({}, state), { error: false, errorMessage: '', loading: false, auth: true, account: action.payload }); case 'LOGOUT': return __assign(__assign({}, state), { error: false, errorMessage: '', loading: false, auth: false, account: {} }); default: return state; } }; var checkAuth = function () { var cache = localStorage.getItem('account'); if (cache !== null && cache !== undefined) { return true; } return false; }; var getAccountCache = function () { if (checkAuth()) { var account = localStorage.getItem('account'); if (account !== null) { return JSON.parse(account); } return {}; } else { return {}; } }; var AppStateModel = /** @class */ (function () { function AppStateModel(auth, account, loading, error, errorMessage) { if (auth === void 0) { auth = checkAuth(); } if (account === void 0) { account = getAccountCache(); } if (loading === void 0) { loading = false; } if (error === void 0) { error = false; } if (errorMessage === void 0) { errorMessage = false; } this.auth = auth; this.account = account; this.loading = loading; this.error = error; this.errorMessage = errorMessage; } return AppStateModel; }()); var index = { components: { CRUD: CrudContainer, AuthenticationLayer: Authentication }, reducers: { appReducer: appReducer }, models: { AppStateModel: AppStateModel, CrudStateModel: CrudStateModel, FilterModel: FilterModel, CrudSettingModel: CrudSettingModel, AuthSettingModel: AuthSettingModel } }; exports.default = index; //# sourceMappingURL=index.js.map