├── .gitignore ├── LICENSE ├── README.md ├── babel.config.cjs.json ├── babel.config.esm.json ├── dist ├── cjs │ ├── assets │ │ └── images │ │ │ ├── google.svg │ │ │ └── microsoft.svg │ ├── components │ │ ├── Auth.js │ │ ├── Fireact.js │ │ ├── SetPageTitle.js │ │ ├── auth │ │ │ ├── ActionPages.js │ │ │ ├── ResetPassword.js │ │ │ ├── SignIn.js │ │ │ └── SignUp.js │ │ ├── menus │ │ │ ├── MainMenu.js │ │ │ └── UserMenu.js │ │ ├── templates │ │ │ ├── AppTemplate.js │ │ │ └── PublicTemplate.js │ │ └── user │ │ │ ├── UserDelete.js │ │ │ ├── UserProfile.js │ │ │ ├── UserUpdateEmail.js │ │ │ ├── UserUpdateName.js │ │ │ └── UserUpdatePassword.js │ ├── index.js │ └── pathnames.json └── esm │ ├── assets │ └── images │ │ ├── google.svg │ │ └── microsoft.svg │ ├── components │ ├── Auth.js │ ├── Fireact.js │ ├── SetPageTitle.js │ ├── auth │ │ ├── ActionPages.js │ │ ├── ResetPassword.js │ │ ├── SignIn.js │ │ └── SignUp.js │ ├── menus │ │ ├── MainMenu.js │ │ └── UserMenu.js │ ├── templates │ │ ├── AppTemplate.js │ │ └── PublicTemplate.js │ └── user │ │ ├── UserDelete.js │ │ ├── UserProfile.js │ │ ├── UserUpdateEmail.js │ │ ├── UserUpdateName.js │ │ └── UserUpdatePassword.js │ ├── index.js │ └── pathnames.json ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.css ├── App.js ├── App.test.js ├── authMethods.json ├── index.css ├── index.js ├── lib ├── assets │ └── images │ │ ├── google.svg │ │ └── microsoft.svg ├── components │ ├── Auth.js │ ├── Fireact.js │ ├── SetPageTitle.js │ ├── auth │ │ ├── ActionPages.js │ │ ├── ResetPassword.js │ │ ├── SignIn.js │ │ └── SignUp.js │ ├── menus │ │ ├── MainMenu.js │ │ └── UserMenu.js │ ├── templates │ │ ├── AppTemplate.js │ │ └── PublicTemplate.js │ └── user │ │ ├── UserDelete.js │ │ ├── UserProfile.js │ │ ├── UserUpdateEmail.js │ │ ├── UserUpdateName.js │ │ └── UserUpdatePassword.js ├── index.js └── pathnames.json ├── reportWebVitals.js └── setupTests.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | .env.* 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | firebase.json 26 | src/firebaseConfig.json 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright 2022 Hey Data Pty Ltd 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## What is @fireactjs/core? 2 | 3 | fireactjs-core is the core package for building web applications with Firebase and Reactjs in a simple and fast approach. Its key features include: 4 | 5 | - Built-in Firebase authentication features for users to sign up and sign in 6 | - Built-in user profile features for users to change email, and password and delete user accounts 7 | - Template base design for easy customization 8 | - Component base architecture that supports full customization 9 | - Easy to extend additional features 10 | 11 | ## Live demo 12 | 13 | To experience the package, go to [https://demo.fireactjs.com](https://demo.fireactjs.com) 14 | 15 | ## Documentation 16 | 17 | For documentation of the package, go to [https://fireactjs.com/docs/core-package/](https://fireactjs.com/docs/core-package/) 18 | 19 | ## Installation 20 | 21 | To install the fireactjs-core components, create your Reactjs project first, and then run `npm I @fireactjs/core` to install the components. 22 | 23 | ```jsx 24 | npx create-react-app my-app 25 | cd my-app 26 | npm i @fireactjs/core react-router-dom firebase @mui/material @mui/icons-material @emotion/react @emotion/styled 27 | ``` 28 | 29 | For details on how to create a Reactjs application, please see [https://reactjs.org/docs/create-a-new-react-app.html](https://reactjs.org/docs/create-a-new-react-app.html) 30 | 31 | ## Setup your Firebase project 32 | 33 | As @fireactjs/core is built on Firebase and Reactjs, you will need to have a Firebase project. Go to [the Firebase website](https://firebase.google.com/) and create a project. 34 | 35 | ### Create a web app 36 | 37 | In your Firebase Project settings → General, click the “Add app” button to create a new web app. You will the instructions on install the firebase npm package and a JSON configuration named `firebaseConfig` which you will need to configure your @fireactjs application. 38 | 39 | Create a file called `firebaseConfig.json` in the `/src` folder and copy the `firebaseConfig` JSON to the file similar to the format below. 40 | 41 | ```json 42 | { 43 | "apiKey": "...", 44 | "authDomain": "...", 45 | "projectId": "...", 46 | "storageBucket": "...", 47 | "messagingSenderId": "...", 48 | "appId": "..." 49 | } 50 | ``` 51 | 52 | ### Enable authentication methods 53 | 54 | After you create your Firebase project, go to the project console and enable the authentication methods you plan to use for your web application. @fireactjs/core supports the following authentication methods: 55 | 56 | - Email and password 57 | - Google 58 | - Facebook 59 | - Microsoft 60 | - Twitter 61 | - Github 62 | - Apple 63 | 64 | Some of the authentication methods require you to register your web application in the authentication platforms (e.g. Facebook). Please make sure you complete the necessary steps to enable the authentication methods. 65 | 66 | Create a file called `authMethods.json` in the `/src` folder and copy the following JSON to the file, then set the authentication methods that you enabled to `true` otherwise to `false`. 67 | 68 | ```json 69 | { 70 | "google": true, 71 | "facebook": true, 72 | "microsoft": true, 73 | "apple": true, 74 | "twitter": true, 75 | "github": true 76 | } 77 | ``` 78 | 79 | ### Initialize your Firebase project 80 | 81 | Run `firebase login` to sign in to your Firebase account and then run `firebase init` to initialize your Firebase project locally. 82 | 83 | ### Update Firestore rules 84 | 85 | Update your `firebase.rules` with the code below. 86 | 87 | ``` 88 | rules_version = '2'; 89 | service cloud.firestore { 90 | match /databases/{database}/documents { 91 | match /{document=**} { 92 | allow read, write: if false; 93 | } 94 | match /users/{userId} { 95 | allow read, update, create: if request.auth.uid == userId; 96 | } 97 | } 98 | } 99 | ``` 100 | 101 | ## Modify App.js 102 | 103 | Replace the code in your `src/App.js` with the code below. You can copy the code from [https://github.com/fireactjs/core-demo/blob/main/src/App.js](https://github.com/fireactjs/core-demo/blob/main/src/App.js) 104 | 105 | ```jsx 106 | import './App.css'; 107 | import firebaseConfig from "./firebaseConfig.json"; 108 | import { pathnames, ActionPages, AppTemplate, AuthProvider, AuthRoutes, FireactProvider, MainMenu, PublicTemplate, ResetPassword, SignIn, SignUp, UserMenu, UserProfile, UserUpdateEmail, UserUpdateName, UserUpdatePassword, UserDelete } from '@fireactjs/core'; 109 | import { BrowserRouter, Routes } from "react-router-dom"; 110 | import { Route } from "react-router-dom"; 111 | import LocalFireDepartmentIcon from '@mui/icons-material/LocalFireDepartment'; 112 | import { CircularProgress, Box } from '@mui/material'; 113 | import authMethods from "./authMethods.json"; 114 | 115 | const Logo = ({size, color}) => { 116 | const logoColor = color || 'warning'; 117 | return ( 118 | 119 | ); 120 | } 121 | 122 | const Loader = ({size}) => { 123 | let cpSize = "35px"; 124 | switch(size){ 125 | case "small": 126 | cpSize = "30px"; 127 | break; 128 | case "medium": 129 | cpSize = "35px"; 130 | break; 131 | case "large": 132 | cpSize = "45px"; 133 | break; 134 | default: 135 | cpSize = "35px"; 136 | break; 137 | } 138 | return ( 139 | 140 | 141 |
142 | 143 |
144 |
145 | ); 146 | } 147 | 148 | function App() { 149 | 150 | const config = { 151 | firebaseConfig: firebaseConfig, 152 | brand: "FIREACTJS", 153 | pathnames: pathnames, 154 | authProviders: authMethods 155 | } 156 | 157 | return ( 158 | 159 | 160 | 161 | 162 | } />} > 163 | } toolBarMenu={} drawerMenu={} />}> 164 | } /> 165 | } /> 166 | } /> 167 | } /> 168 | } /> 169 | } /> 170 | 171 | 172 | }> 173 | } 176 | /> 177 | } /> 178 | } 181 | /> 182 | } /> 183 | } 186 | /> 187 | } /> 188 | } 191 | /> 192 | } /> 193 | 194 | 195 | 196 | 197 | 198 | ) 199 | } 200 | 201 | export default App; 202 | 203 | ``` 204 | 205 | Replace `Brand` and `Logo` to customise the logo and the brand of your web application. 206 | 207 | For further customisation, please read the documentation. 208 | 209 | ## Update Firebase template action URL (Optional) 210 | 211 | Go to your Firebase project authentication → Templates, then click on the Edit Template button, and then click on “Customize action URL”. Use `https://www.yourdomain.com/auth-action` as the custom action URL and replace `www.yourdomain.com` with your actual application domain. 212 | 213 | ## Run your app locally 214 | 215 | By now, your app is ready for the first run locally. Use the command `npm start` to start the app. 216 | 217 | ## Deploy to Firebase 218 | 219 | After testing locally, your app is ready to be deployed to Firebase hosting. 220 | 221 | ### Build 222 | 223 | Run `npm run build` to build your app 224 | 225 | ### Deploy 226 | 227 | Run `firebase init` to initialize your project with Firebase and then run `firebase deploy` to deploy your app to Firebase. If you see a blank screen in your production URL, make sure you set the `build` as the folder in your Firebase settings. -------------------------------------------------------------------------------- /babel.config.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/env", 5 | { 6 | "targets": { 7 | "edge": "17", 8 | "firefox": "60", 9 | "chrome": "67", 10 | "safari": "11.1" 11 | }, 12 | "modules": "cjs", 13 | "useBuiltIns": "usage", 14 | "corejs": "3.6.5" 15 | } 16 | ], 17 | "@babel/preset-react" 18 | ] 19 | } -------------------------------------------------------------------------------- /babel.config.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/env", 5 | { 6 | "targets": { 7 | "edge": "17", 8 | "firefox": "60", 9 | "chrome": "67", 10 | "safari": "11.1" 11 | }, 12 | "modules": false, 13 | "useBuiltIns": "usage", 14 | "corejs": "3.6.5" 15 | } 16 | ], 17 | "@babel/preset-react" 18 | ] 19 | } -------------------------------------------------------------------------------- /dist/cjs/assets/images/google.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | google_buttn 6 | Created with Sketch. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /dist/cjs/assets/images/microsoft.svg: -------------------------------------------------------------------------------- 1 | MS-SymbolLockup 2 | -------------------------------------------------------------------------------- /dist/cjs/components/Auth.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("core-js/modules/es.symbol.description.js"); 4 | require("core-js/modules/es.weak-map.js"); 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports.AuthRoutes = exports.AuthProvider = exports.AuthContext = void 0; 9 | require("core-js/modules/web.dom-collections.iterator.js"); 10 | require("core-js/modules/es.regexp.exec.js"); 11 | require("core-js/modules/es.string.search.js"); 12 | var _react = _interopRequireWildcard(require("react")); 13 | var _app = require("firebase/app"); 14 | var _auth = require("firebase/auth"); 15 | var _reactRouterDom = require("react-router-dom"); 16 | var _material = require("@mui/material"); 17 | var _firestore = require("firebase/firestore"); 18 | var _Fireact = require("./Fireact"); 19 | var _functions = require("firebase/functions"); 20 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 21 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } 22 | function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } 23 | function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } 24 | function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 25 | function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); } 26 | function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } 27 | const AuthContext = exports.AuthContext = /*#__PURE__*/_react.default.createContext(); 28 | const AuthProvider = _ref => { 29 | let { 30 | children 31 | } = _ref; 32 | // authorized user state 33 | const [authUser, setAuthUser] = (0, _react.useState)({ 34 | user: null, 35 | data: {}, 36 | checked: false 37 | }); 38 | const { 39 | config 40 | } = (0, _react.useContext)(_Fireact.FireactContext); 41 | const [firebaseApp, setFirebaseApp] = (0, _react.useState)(null); 42 | const [authInstance, setAuthInstance] = (0, _react.useState)(null); 43 | const [firestoreInstance, setFirestoreInstance] = (0, _react.useState)(null); 44 | const [functionsInstance, setFunctionsInstance] = (0, _react.useState)(null); 45 | (0, _react.useEffect)(() => { 46 | const app = (0, _app.initializeApp)(config.firebaseConfig); 47 | const auth = (0, _auth.getAuth)(app); 48 | const firestore = (0, _firestore.getFirestore)(app); 49 | const functions = (0, _functions.getFunctions)(app); 50 | setFirebaseApp(app); 51 | setAuthInstance(auth); 52 | setFirestoreInstance(firestore); 53 | setFunctionsInstance(functions); 54 | (0, _auth.onAuthStateChanged)(auth, user => { 55 | if (user !== null) { 56 | user.getIdToken().then(token => { 57 | const userDoc = (0, _firestore.doc)(firestore, 'users', user.uid); 58 | setAuthUser(prevState => _objectSpread(_objectSpread({}, prevState), {}, { 59 | user: user, 60 | checked: true 61 | })); 62 | (0, _firestore.setDoc)(userDoc, { 63 | displayName: user.displayName, 64 | photoURL: user.photoURL, 65 | email: user.email 66 | }, { 67 | merge: true 68 | }); 69 | }); 70 | } else { 71 | setAuthUser(prevState => _objectSpread(_objectSpread({}, prevState), {}, { 72 | user: null, 73 | checked: true 74 | })); 75 | } 76 | }); 77 | }, [config.firebaseConfig]); 78 | return /*#__PURE__*/_react.default.createElement(AuthContext.Provider, { 79 | value: { 80 | authUser, 81 | setAuthUser, 82 | firebaseApp, 83 | authInstance, 84 | firestoreInstance, 85 | functionsInstance 86 | } 87 | }, children); 88 | }; 89 | exports.AuthProvider = AuthProvider; 90 | const AuthRoutes = _ref2 => { 91 | let { 92 | loader 93 | } = _ref2; 94 | const { 95 | authUser 96 | } = (0, _react.useContext)(AuthContext); 97 | const { 98 | config 99 | } = (0, _react.useContext)(_Fireact.FireactContext); 100 | const signInPath = config.pathnames.SignIn; 101 | if (authUser.checked) { 102 | if (authUser.user !== null) { 103 | return /*#__PURE__*/_react.default.createElement(_reactRouterDom.Outlet, null); 104 | } else { 105 | return /*#__PURE__*/_react.default.createElement(_reactRouterDom.Navigate, { 106 | to: signInPath + "?re=" + document.location.pathname + document.location.search + document.location.hash 107 | }); 108 | } 109 | } else { 110 | return /*#__PURE__*/_react.default.createElement(_material.Box, { 111 | mt: 10 112 | }, /*#__PURE__*/_react.default.createElement(_material.Container, { 113 | maxWidth: "sm" 114 | }, /*#__PURE__*/_react.default.createElement(_material.Box, { 115 | component: "span", 116 | m: 5, 117 | textAlign: "center" 118 | }, loader))); 119 | } 120 | }; 121 | exports.AuthRoutes = AuthRoutes; -------------------------------------------------------------------------------- /dist/cjs/components/Fireact.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.FireactProvider = exports.FireactContext = void 0; 7 | var _react = _interopRequireDefault(require("react")); 8 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 9 | const FireactContext = exports.FireactContext = /*#__PURE__*/_react.default.createContext(); 10 | const FireactProvider = _ref => { 11 | let { 12 | config, 13 | children 14 | } = _ref; 15 | return /*#__PURE__*/_react.default.createElement(FireactContext.Provider, { 16 | value: { 17 | config 18 | } 19 | }, children); 20 | }; 21 | exports.FireactProvider = FireactProvider; -------------------------------------------------------------------------------- /dist/cjs/components/SetPageTitle.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("core-js/modules/es.weak-map.js"); 4 | require("core-js/modules/web.dom-collections.iterator.js"); 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports.SetPageTitle = void 0; 9 | var _react = _interopRequireWildcard(require("react")); 10 | var _Auth = require("./Auth"); 11 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 12 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } 13 | const SetPageTitle = _ref => { 14 | let { 15 | title 16 | } = _ref; 17 | const { 18 | brand 19 | } = (0, _react.useContext)(_Auth.AuthContext); 20 | (0, _react.useEffect)(() => { 21 | document.title = brand ? "".concat(title, " - ").concat(brand) : title; 22 | }, [title, brand]); 23 | return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null); 24 | }; 25 | exports.SetPageTitle = SetPageTitle; -------------------------------------------------------------------------------- /dist/cjs/components/auth/ActionPages.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("core-js/modules/es.weak-map.js"); 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.ActionPages = void 0; 8 | require("core-js/modules/web.dom-collections.iterator.js"); 9 | require("core-js/modules/web.url.js"); 10 | require("core-js/modules/web.url-search-params.js"); 11 | require("core-js/modules/es.regexp.exec.js"); 12 | require("core-js/modules/es.regexp.test.js"); 13 | var _react = _interopRequireWildcard(require("react")); 14 | var _material = require("@mui/material"); 15 | var _SetPageTitle = require("../SetPageTitle"); 16 | var _auth = require("firebase/auth"); 17 | var _reactRouterDom = require("react-router-dom"); 18 | var _Fireact = require("../Fireact"); 19 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 20 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } 21 | const ActionPages = _ref => { 22 | let { 23 | logo 24 | } = _ref; 25 | let title = "Unknown Action"; 26 | const params = new URL(document.location).searchParams; 27 | const mode = params.get('mode'); 28 | const actionCode = params.get('oobCode'); 29 | // const apiKey = params.get('apiKey'); 30 | // const continueUrl = params.get('continueUrl'); 31 | // const lang = params.get('lang') || 'en'; 32 | 33 | switch (mode) { 34 | case 'resetPassword': 35 | title = "Reset Password"; 36 | break; 37 | case 'recoverEmail': 38 | title = 'Recover Email'; 39 | break; 40 | case 'verifyEmail': 41 | title = 'Verfiy Email'; 42 | break; 43 | default: 44 | break; 45 | } 46 | return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_material.Box, null, /*#__PURE__*/_react.default.createElement(_SetPageTitle.SetPageTitle, { 47 | title: title 48 | }), logo, /*#__PURE__*/_react.default.createElement(_material.Typography, { 49 | component: "h1", 50 | variant: "h5" 51 | }, title)), /*#__PURE__*/_react.default.createElement(_material.Box, null, /*#__PURE__*/_react.default.createElement(_material.Stack, { 52 | spacing: 2, 53 | mt: 2 54 | }, (mode === 'verifyEmail' || mode === 'recoverEmail') && /*#__PURE__*/_react.default.createElement(HandleAction, { 55 | actionCode: actionCode, 56 | mode: mode 57 | }), mode === 'resetPassword' && /*#__PURE__*/_react.default.createElement(ResetPassword, { 58 | actionCode: actionCode 59 | })))); 60 | }; 61 | exports.ActionPages = ActionPages; 62 | const ResetPassword = _ref2 => { 63 | let { 64 | actionCode 65 | } = _ref2; 66 | const [processing, setProcessing] = (0, _react.useState)(false); 67 | const [error, setError] = (0, _react.useState)(null); 68 | const [stage, setStage] = (0, _react.useState)('verifying'); 69 | const { 70 | config 71 | } = (0, _react.useContext)(_Fireact.FireactContext); 72 | const [newPassword, setNewPassword] = (0, _react.useState)(''); 73 | const [confirmPassword, setConfirmPassword] = (0, _react.useState)(''); 74 | (0, _react.useEffect)(() => { 75 | const auth = (0, _auth.getAuth)(); 76 | (0, _auth.verifyPasswordResetCode)(auth, actionCode).then(() => { 77 | setStage('form'); 78 | }).catch(error => { 79 | setStage(''); 80 | setError(error.message); 81 | }); 82 | }, [actionCode]); 83 | return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, error && /*#__PURE__*/_react.default.createElement(_material.Alert, { 84 | severity: "error" 85 | }, error), stage === 'verifying' && /*#__PURE__*/_react.default.createElement(_material.Typography, null, "Please wait while verifying your request..."), stage === 'form' && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_material.TextField, { 86 | required: true, 87 | fullWidth: true, 88 | name: "newPassword", 89 | label: "New Password", 90 | type: "password", 91 | autoComplete: "new-password", 92 | margin: "normal", 93 | onChange: e => setNewPassword(e.target.value) 94 | }), /*#__PURE__*/_react.default.createElement(_material.TextField, { 95 | required: true, 96 | fullWidth: true, 97 | name: "confirmPassword", 98 | label: "Confirm Password", 99 | type: "password", 100 | autoComplete: "", 101 | margin: "normal", 102 | onChange: e => setConfirmPassword(e.target.value) 103 | }), /*#__PURE__*/_react.default.createElement(_material.Button, { 104 | type: "button", 105 | fullWidth: true, 106 | variant: "contained", 107 | size: "large", 108 | disabled: processing, 109 | onClick: () => { 110 | setProcessing(true); 111 | setError(null); 112 | const passwordNumericRegex = /\d+/; 113 | const passwordUppercaseRegex = /[A-Z]+/; 114 | const passwordLowercaseRegex = /[a-z]+/; 115 | const passwordSpecialRegex = /[ !"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]+/; 116 | if (!passwordNumericRegex.test(String(newPassword)) || !passwordUppercaseRegex.test(String(newPassword)) || !passwordLowercaseRegex.test(String(newPassword)) || !passwordSpecialRegex.test(String(newPassword)) || newPassword.length < 8) { 117 | setError('The password must contain at least 8 characters with letters (both uppercase and lowercase), numbers, and symbols.'); 118 | setProcessing(false); 119 | } else if (newPassword !== confirmPassword) { 120 | setError('Confirm password does not match with new password.'); 121 | setProcessing(false); 122 | } else { 123 | const auth = (0, _auth.getAuth)(); 124 | (0, _auth.confirmPasswordReset)(auth, actionCode, newPassword).then(() => { 125 | setStage('success'); 126 | }).catch(error => { 127 | setError(error.message); 128 | setProcessing(false); 129 | }); 130 | } 131 | } 132 | }, "Reset Password")), stage === 'success' && /*#__PURE__*/_react.default.createElement(_material.Alert, { 133 | severity: "success" 134 | }, "Your email is verified. Please ", /*#__PURE__*/_react.default.createElement(_reactRouterDom.NavLink, { 135 | to: config.pathnames.SignIn 136 | }, "sign in"), " with your new password.")); 137 | }; 138 | const HandleAction = _ref3 => { 139 | let { 140 | mode, 141 | actionCode 142 | } = _ref3; 143 | const [processing, setProcessing] = (0, _react.useState)(false); 144 | const [error, setError] = (0, _react.useState)(null); 145 | const [success, setSuccess] = (0, _react.useState)(false); 146 | const { 147 | config 148 | } = (0, _react.useContext)(_Fireact.FireactContext); 149 | let processingMessage = 'Please wait'; 150 | let successMessage = 'Done'; 151 | switch (mode) { 152 | case 'verifyEmail': 153 | processingMessage = 'Please wait while verifying your email...'; 154 | successMessage = /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, "Your email is verified. Please ", /*#__PURE__*/_react.default.createElement(_reactRouterDom.NavLink, { 155 | to: config.pathnames.SignIn 156 | }, "sign in"), " again."); 157 | break; 158 | case 'recoverEmail': 159 | processingMessage = 'Please wait while resotring your email...'; 160 | successMessage = /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, "Your email is restored. Please ", /*#__PURE__*/_react.default.createElement(_reactRouterDom.NavLink, { 161 | to: config.pathnames.ResetPassword 162 | }, "reset password"), " to ensure your account is secured."); 163 | break; 164 | default: 165 | break; 166 | } 167 | (0, _react.useEffect)(() => { 168 | setProcessing(true); 169 | setError(null); 170 | setSuccess(false); 171 | const auth = (0, _auth.getAuth)(); 172 | (0, _auth.applyActionCode)(auth, actionCode).then(() => { 173 | setSuccess(true); 174 | setProcessing(false); 175 | }).catch(error => { 176 | setError(error.message); 177 | setProcessing(false); 178 | }); 179 | }, [actionCode]); 180 | return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, processing ? /*#__PURE__*/_react.default.createElement(_material.Typography, null, processingMessage) : success ? /*#__PURE__*/_react.default.createElement(_material.Alert, { 181 | severity: "success" 182 | }, successMessage) : error ? /*#__PURE__*/_react.default.createElement(_material.Alert, { 183 | severity: "error" 184 | }, error) : /*#__PURE__*/_react.default.createElement(_material.Alert, { 185 | severity: "error" 186 | }, "Something went wrong")); 187 | }; -------------------------------------------------------------------------------- /dist/cjs/components/auth/ResetPassword.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("core-js/modules/es.weak-map.js"); 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.ResetPassword = void 0; 8 | require("core-js/modules/web.dom-collections.iterator.js"); 9 | var _react = _interopRequireWildcard(require("react")); 10 | var _material = require("@mui/material"); 11 | var _reactRouterDom = require("react-router-dom"); 12 | var _auth = require("firebase/auth"); 13 | var _SetPageTitle = require("../SetPageTitle"); 14 | var _Fireact = require("../Fireact"); 15 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 16 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } 17 | const ResetPassword = _ref => { 18 | let { 19 | logo 20 | } = _ref; 21 | const { 22 | config 23 | } = (0, _react.useContext)(_Fireact.FireactContext); 24 | const pathnames = config.pathnames; 25 | const signInUrl = pathnames.SignIn; 26 | const title = "Reset Password"; 27 | const [error, setError] = (0, _react.useState)(null); 28 | const [success, setSuccess] = (0, _react.useState)(false); 29 | const [processing, setProcessing] = (0, _react.useState)(false); 30 | const [email, setEmail] = (0, _react.useState)(""); 31 | return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_material.Box, null, /*#__PURE__*/_react.default.createElement(_SetPageTitle.SetPageTitle, { 32 | title: title 33 | }), logo, /*#__PURE__*/_react.default.createElement(_material.Typography, { 34 | component: "h1", 35 | variant: "h5" 36 | }, title)), /*#__PURE__*/_react.default.createElement(_material.Box, null, /*#__PURE__*/_react.default.createElement(_material.Stack, { 37 | spacing: 2, 38 | mt: 2 39 | }, error !== null && /*#__PURE__*/_react.default.createElement(_material.Alert, { 40 | severity: "error" 41 | }, error), success && /*#__PURE__*/_react.default.createElement(_material.Alert, { 42 | severity: "success" 43 | }, "A password reset email has been sent to the email address."), /*#__PURE__*/_react.default.createElement(_material.TextField, { 44 | required: true, 45 | fullWidth: true, 46 | name: "email", 47 | label: "Email", 48 | type: "email", 49 | autoComplete: "email", 50 | margin: "normal", 51 | onChange: e => setEmail(e.target.value) 52 | }), /*#__PURE__*/_react.default.createElement(_material.Button, { 53 | type: "button", 54 | fullWidth: true, 55 | variant: "contained", 56 | size: "large", 57 | disabled: processing, 58 | onClick: () => { 59 | setProcessing(true); 60 | setSuccess(false); 61 | setError(null); 62 | const auth = (0, _auth.getAuth)(); 63 | (0, _auth.sendPasswordResetEmail)(auth, email).then(() => { 64 | setProcessing(false); 65 | setSuccess(true); 66 | }).catch(error => { 67 | switch (error.code) { 68 | case "auth/invalid-email": 69 | setError('The email address is badly formatted.'); 70 | break; 71 | case "auth/missing-email": 72 | setError('The email address is missing.'); 73 | break; 74 | case "auth/user-not-found": 75 | setError('There is no user record corresponding to this identifier. The user may have been deleted.'); 76 | break; 77 | case "auth/too-many-requests": 78 | setError('We have blocked all requests from this device due to unusual activity. Try again later.'); 79 | break; 80 | default: 81 | setError(error.message); 82 | break; 83 | } 84 | setProcessing(false); 85 | }); 86 | } 87 | }, "Reset Password"), signInUrl && /*#__PURE__*/_react.default.createElement(_material.Grid, { 88 | container: true 89 | }, signInUrl && /*#__PURE__*/_react.default.createElement(_material.Grid, { 90 | item: true, 91 | xs: true, 92 | textAlign: "left" 93 | }, /*#__PURE__*/_react.default.createElement(_material.Link, { 94 | to: signInUrl, 95 | component: _reactRouterDom.Link 96 | }, "Sign in with an existing account")))))); 97 | }; 98 | exports.ResetPassword = ResetPassword; -------------------------------------------------------------------------------- /dist/cjs/components/auth/SignUp.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("core-js/modules/es.weak-map.js"); 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.SignUp = void 0; 8 | require("core-js/modules/web.dom-collections.iterator.js"); 9 | require("core-js/modules/es.string.trim.js"); 10 | require("core-js/modules/es.regexp.exec.js"); 11 | require("core-js/modules/es.regexp.test.js"); 12 | var _react = _interopRequireWildcard(require("react")); 13 | var _material = require("@mui/material"); 14 | var _auth = require("firebase/auth"); 15 | var _reactRouterDom = require("react-router-dom"); 16 | var _SetPageTitle = require("../SetPageTitle"); 17 | var _Fireact = require("../Fireact"); 18 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 19 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } 20 | const SignUp = _ref => { 21 | let { 22 | logo, 23 | successUrl 24 | } = _ref; 25 | const { 26 | config 27 | } = (0, _react.useContext)(_Fireact.FireactContext); 28 | const pathnames = config.pathnames; 29 | const title = "Sign Up"; 30 | const signInUrl = pathnames.SignIn; 31 | const resetPasswordUrl = pathnames.ResetPassword; 32 | const re = successUrl || "/"; // redirect successUrl or homepage after sign in 33 | 34 | const [error, setError] = (0, _react.useState)(null); 35 | const [processing, setProcessing] = (0, _react.useState)(false); 36 | const [email, setEmail] = (0, _react.useState)(""); 37 | const [fullname, setFullname] = (0, _react.useState)(""); 38 | const [password, setPassword] = (0, _react.useState)(""); 39 | return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_material.Box, null, /*#__PURE__*/_react.default.createElement(_SetPageTitle.SetPageTitle, { 40 | title: title 41 | }), logo, /*#__PURE__*/_react.default.createElement(_material.Typography, { 42 | component: "h1", 43 | variant: "h5" 44 | }, title)), /*#__PURE__*/_react.default.createElement(_material.Box, null, /*#__PURE__*/_react.default.createElement(_material.Stack, { 45 | spacing: 2, 46 | mt: 2 47 | }, error !== null && /*#__PURE__*/_react.default.createElement(_material.Alert, { 48 | severity: "error" 49 | }, error), /*#__PURE__*/_react.default.createElement(_material.TextField, { 50 | required: true, 51 | fullWidth: true, 52 | name: "email", 53 | label: "Email", 54 | type: "email", 55 | autoComplete: "email", 56 | margin: "normal", 57 | onChange: e => setEmail(e.target.value) 58 | }), /*#__PURE__*/_react.default.createElement(_material.TextField, { 59 | required: true, 60 | fullWidth: true, 61 | name: "fullname", 62 | label: "Full Name", 63 | autoComplete: "name", 64 | type: "text", 65 | margin: "normal", 66 | onChange: e => setFullname(e.target.value) 67 | }), /*#__PURE__*/_react.default.createElement(_material.TextField, { 68 | required: true, 69 | fullWidth: true, 70 | name: "password", 71 | label: "New Password", 72 | autoComplete: "new-password", 73 | type: "password", 74 | margin: "normal", 75 | onChange: e => setPassword(e.target.value) 76 | }), /*#__PURE__*/_react.default.createElement(_material.Button, { 77 | type: "button", 78 | fullWidth: true, 79 | variant: "contained", 80 | size: "large", 81 | disabled: processing, 82 | onClick: () => { 83 | setProcessing(true); 84 | setError(null); 85 | const passwordNumericRegex = /\d+/; 86 | const passwordUppercaseRegex = /[A-Z]+/; 87 | const passwordLowercaseRegex = /[a-z]+/; 88 | const passwordSpecialRegex = /[ !"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]+/; 89 | if (fullname.trim() === "") { 90 | setError('Your full name is required.'); 91 | setProcessing(false); 92 | } else if (!passwordNumericRegex.test(String(password)) || !passwordUppercaseRegex.test(String(password)) || !passwordLowercaseRegex.test(String(password)) || !passwordSpecialRegex.test(String(password)) || password.length < 8) { 93 | setError('The password must contain at least 8 characters with letters (both uppercase and lowercase), numbers, and symbols.'); 94 | setProcessing(false); 95 | } else { 96 | const auth = (0, _auth.getAuth)(); 97 | (0, _auth.createUserWithEmailAndPassword)(auth, email, password).then(result => { 98 | return (0, _auth.updateProfile)(result.user, { 99 | displayName: fullname 100 | }); 101 | }).then(() => { 102 | document.location.href = re.substr(0, 1) === '/' && re.substr(1, 1) !== '/' ? re : '/'; 103 | }).catch(error => { 104 | switch (error.code) { 105 | case "auth/invalid-email": 106 | setError('The email address is badly formatted.'); 107 | break; 108 | case "auth/missing-email": 109 | setError('The email address is missing.'); 110 | break; 111 | case "auth/email-already-in-use": 112 | setError('The email address is already in use by another account.'); 113 | break; 114 | default: 115 | setError(error.message); 116 | break; 117 | } 118 | setProcessing(false); 119 | }); 120 | } 121 | } 122 | }, "Sign Up"), (signInUrl || resetPasswordUrl) && /*#__PURE__*/_react.default.createElement(_material.Grid, { 123 | container: true 124 | }, signInUrl && /*#__PURE__*/_react.default.createElement(_material.Grid, { 125 | item: true, 126 | xs: true, 127 | textAlign: "left" 128 | }, /*#__PURE__*/_react.default.createElement(_material.Link, { 129 | to: signInUrl, 130 | component: _reactRouterDom.Link 131 | }, "Sign in with an existing account")), resetPasswordUrl && /*#__PURE__*/_react.default.createElement(_material.Grid, { 132 | item: true, 133 | textAlign: "left" 134 | }, /*#__PURE__*/_react.default.createElement(_material.Link, { 135 | to: resetPasswordUrl, 136 | component: _reactRouterDom.Link 137 | }, "Reset password")))))); 138 | }; 139 | exports.SignUp = SignUp; -------------------------------------------------------------------------------- /dist/cjs/components/menus/MainMenu.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("core-js/modules/es.weak-map.js"); 4 | require("core-js/modules/web.dom-collections.iterator.js"); 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | exports.MainMenu = void 0; 9 | var _material = require("@mui/material"); 10 | var _react = _interopRequireWildcard(require("react")); 11 | var _reactRouterDom = require("react-router-dom"); 12 | var _Home = _interopRequireDefault(require("@mui/icons-material/Home")); 13 | var _AccountBox = _interopRequireDefault(require("@mui/icons-material/AccountBox")); 14 | var _Fireact = require("../Fireact"); 15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 16 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 17 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } 18 | const MainMenu = _ref => { 19 | let { 20 | customItems 21 | } = _ref; 22 | const { 23 | config 24 | } = (0, _react.useContext)(_Fireact.FireactContext); 25 | const pathnames = config.pathnames; 26 | const profileUrl = pathnames.UserProfile; 27 | return /*#__PURE__*/_react.default.createElement(_material.List, { 28 | component: "nav" 29 | }, /*#__PURE__*/_react.default.createElement(_reactRouterDom.NavLink, { 30 | to: "/", 31 | style: { 32 | textDecoration: 'none' 33 | }, 34 | key: "home" 35 | }, /*#__PURE__*/_react.default.createElement(_material.ListItemButton, null, /*#__PURE__*/_react.default.createElement(_material.ListItemIcon, null, /*#__PURE__*/_react.default.createElement(_Home.default, null)), /*#__PURE__*/_react.default.createElement(_material.ListItemText, { 36 | primary: /*#__PURE__*/_react.default.createElement(_material.Typography, { 37 | color: "textPrimary" 38 | }, "Home") 39 | }))), customItems, profileUrl && [/*#__PURE__*/_react.default.createElement(_material.Divider, { 40 | key: "profile-divider" 41 | }), /*#__PURE__*/_react.default.createElement(_reactRouterDom.NavLink, { 42 | to: profileUrl, 43 | style: { 44 | textDecoration: 'none' 45 | }, 46 | key: "profile" 47 | }, /*#__PURE__*/_react.default.createElement(_material.ListItemButton, null, /*#__PURE__*/_react.default.createElement(_material.ListItemIcon, null, /*#__PURE__*/_react.default.createElement(_AccountBox.default, null)), /*#__PURE__*/_react.default.createElement(_material.ListItemText, { 48 | primary: /*#__PURE__*/_react.default.createElement(_material.Typography, { 49 | color: "textPrimary" 50 | }, "My Profile") 51 | })))]); 52 | }; 53 | exports.MainMenu = MainMenu; -------------------------------------------------------------------------------- /dist/cjs/components/menus/UserMenu.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("core-js/modules/es.weak-map.js"); 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.UserMenu = void 0; 8 | require("core-js/modules/web.dom-collections.iterator.js"); 9 | var _material = require("@mui/material"); 10 | var _react = _interopRequireWildcard(require("react")); 11 | var _Auth = require("../Auth"); 12 | var _AccountBox = _interopRequireDefault(require("@mui/icons-material/AccountBox")); 13 | var _ExitToApp = _interopRequireDefault(require("@mui/icons-material/ExitToApp")); 14 | var _auth = require("firebase/auth"); 15 | var _reactRouterDom = require("react-router-dom"); 16 | var _Fireact = require("../Fireact"); 17 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 18 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 19 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } 20 | const UserMenu = _ref => { 21 | let { 22 | customItems 23 | } = _ref; 24 | const { 25 | config 26 | } = (0, _react.useContext)(_Fireact.FireactContext); 27 | const pathnames = config.pathnames; 28 | const profileUrl = pathnames.UserProfile; 29 | const [anchorEl, setAnchorEl] = (0, _react.useState)(null); 30 | const open = Boolean(anchorEl); 31 | const handleMenu = event => { 32 | setAnchorEl(event.currentTarget); 33 | }; 34 | const handleClose = () => { 35 | setAnchorEl(null); 36 | }; 37 | const navigate = (0, _reactRouterDom.useNavigate)(); 38 | return /*#__PURE__*/_react.default.createElement(_Auth.AuthContext.Consumer, null, context => /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_material.IconButton, { 39 | "ria-label": "account of current user", 40 | "aria-controls": "menu-appbar", 41 | onClick: handleMenu, 42 | "aria-haspopup": "true" 43 | }, /*#__PURE__*/_react.default.createElement(_material.Avatar, { 44 | alt: context.authUser.user.displayName, 45 | src: context.authUser.user.photoURL ? context.authUser.user.photoURL : "https://ui-avatars.com/api/?name=" + encodeURI(context.authUser.user.displayName) + "&background=007bff&size=64&color=f8f9fc" 46 | })), /*#__PURE__*/_react.default.createElement(_material.Menu, { 47 | id: "menu-appbar", 48 | anchorEl: anchorEl, 49 | anchorOrigin: { 50 | vertical: 'bottom', 51 | horizontal: 'right' 52 | }, 53 | keepMounted: true, 54 | transformOrigin: { 55 | vertical: 'top', 56 | horizontal: 'right' 57 | }, 58 | open: open, 59 | onClose: handleClose 60 | }, profileUrl && [/*#__PURE__*/_react.default.createElement(_material.MenuItem, { 61 | key: "profile-menu-item", 62 | onClick: e => { 63 | e.preventDefault(); 64 | handleClose(); 65 | navigate(profileUrl); 66 | } 67 | }, /*#__PURE__*/_react.default.createElement(_AccountBox.default, { 68 | sx: { 69 | marginRight: "10px" 70 | } 71 | }), " Profile"), /*#__PURE__*/_react.default.createElement(_material.Divider, { 72 | key: "profile-menu-divider" 73 | })], customItems, /*#__PURE__*/_react.default.createElement(_material.MenuItem, { 74 | onClick: e => { 75 | e.preventDefault(); 76 | handleClose(); 77 | const auth = (0, _auth.getAuth)(); 78 | (0, _auth.signOut)(auth).then(() => { 79 | document.location.href = "/"; 80 | }); 81 | } 82 | }, /*#__PURE__*/_react.default.createElement(_ExitToApp.default, { 83 | sx: { 84 | marginRight: "10px" 85 | } 86 | }), " Sign Out")))); 87 | }; 88 | exports.UserMenu = UserMenu; -------------------------------------------------------------------------------- /dist/cjs/components/templates/AppTemplate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("core-js/modules/es.weak-map.js"); 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.AppTemplate = void 0; 8 | require("core-js/modules/web.dom-collections.iterator.js"); 9 | require("core-js/modules/es.symbol.description.js"); 10 | var _react = _interopRequireWildcard(require("react")); 11 | var _styles = require("@mui/material/styles"); 12 | var _material = require("@mui/material"); 13 | var _Menu = _interopRequireDefault(require("@mui/icons-material/Menu")); 14 | var _ChevronLeft = _interopRequireDefault(require("@mui/icons-material/ChevronLeft")); 15 | var _ChevronRight = _interopRequireDefault(require("@mui/icons-material/ChevronRight")); 16 | var _reactRouterDom = require("react-router-dom"); 17 | var _Fireact = require("../Fireact"); 18 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 19 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 20 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } 21 | function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } 22 | function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } 23 | function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 24 | function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); } 25 | function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } 26 | const drawerWidth = 240; 27 | const openedMixin = theme => ({ 28 | width: drawerWidth, 29 | transition: theme.transitions.create('width', { 30 | easing: theme.transitions.easing.sharp, 31 | duration: theme.transitions.duration.enteringScreen 32 | }), 33 | overflowX: 'hidden' 34 | }); 35 | const closedMixin = theme => ({ 36 | transition: theme.transitions.create('width', { 37 | easing: theme.transitions.easing.sharp, 38 | duration: theme.transitions.duration.leavingScreen 39 | }), 40 | overflowX: 'hidden', 41 | width: "calc(".concat(theme.spacing(7), " + 1px)"), 42 | [theme.breakpoints.up('sm')]: { 43 | width: "calc(".concat(theme.spacing(9), " + 1px)") 44 | } 45 | }); 46 | const DrawerHeader = (0, _styles.styled)('div')(_ref => { 47 | let { 48 | theme 49 | } = _ref; 50 | return _objectSpread({ 51 | display: 'flex', 52 | alignItems: 'center', 53 | justifyContent: 'flex-end', 54 | padding: theme.spacing(0, 1) 55 | }, theme.mixins.toolbar); 56 | }); 57 | const AppBar = (0, _styles.styled)(_material.AppBar, { 58 | shouldForwardProp: prop => prop !== 'open' 59 | })(_ref2 => { 60 | let { 61 | theme, 62 | open 63 | } = _ref2; 64 | return _objectSpread({ 65 | zIndex: theme.zIndex.drawer + 1, 66 | transition: theme.transitions.create(['width', 'margin'], { 67 | easing: theme.transitions.easing.sharp, 68 | duration: theme.transitions.duration.leavingScreen 69 | }) 70 | }, open && { 71 | marginLeft: drawerWidth, 72 | width: "calc(100% - ".concat(drawerWidth, "px)"), 73 | transition: theme.transitions.create(['width', 'margin'], { 74 | easing: theme.transitions.easing.sharp, 75 | duration: theme.transitions.duration.enteringScreen 76 | }) 77 | }); 78 | }); 79 | const Drawer = (0, _styles.styled)(_material.Drawer, { 80 | shouldForwardProp: prop => prop !== 'open' 81 | })(_ref3 => { 82 | let { 83 | theme, 84 | open 85 | } = _ref3; 86 | return _objectSpread(_objectSpread({ 87 | width: drawerWidth, 88 | flexShrink: 0, 89 | whiteSpace: 'nowrap', 90 | boxSizing: 'border-box' 91 | }, open && _objectSpread(_objectSpread({}, openedMixin(theme)), {}, { 92 | '& .MuiDrawer-paper': openedMixin(theme) 93 | })), !open && _objectSpread(_objectSpread({}, closedMixin(theme)), {}, { 94 | '& .MuiDrawer-paper': closedMixin(theme) 95 | })); 96 | }); 97 | const AppTemplate = _ref4 => { 98 | let { 99 | logo, 100 | drawerMenu, 101 | toolbarChildren, 102 | toolBarMenu 103 | } = _ref4; 104 | const theme = (0, _styles.useTheme)(); 105 | const [open, setOpen] = (0, _react.useState)(true); 106 | const handleDrawerOpen = () => { 107 | setOpen(true); 108 | }; 109 | const handleDrawerClose = () => { 110 | setOpen(false); 111 | }; 112 | const { 113 | config 114 | } = (0, _react.useContext)(_Fireact.FireactContext); 115 | const brand = config.brand; 116 | const navigate = (0, _reactRouterDom.useNavigate)(); 117 | return /*#__PURE__*/_react.default.createElement(_material.Box, { 118 | sx: { 119 | display: "flex" 120 | } 121 | }, /*#__PURE__*/_react.default.createElement(_material.CssBaseline, null), /*#__PURE__*/_react.default.createElement(AppBar, { 122 | position: "fixed", 123 | open: open 124 | }, /*#__PURE__*/_react.default.createElement(_material.Toolbar, null, /*#__PURE__*/_react.default.createElement(_material.IconButton, { 125 | color: "inherit", 126 | "aria-label": "open drawer", 127 | onClick: handleDrawerOpen, 128 | edge: "start", 129 | sx: _objectSpread({ 130 | marginRight: '36px' 131 | }, open && { 132 | display: 'none' 133 | }) 134 | }, /*#__PURE__*/_react.default.createElement(_Menu.default, null)), toolbarChildren, /*#__PURE__*/_react.default.createElement("div", { 135 | style: { 136 | marginLeft: "auto", 137 | marginRight: "0px" 138 | } 139 | }, toolBarMenu))), /*#__PURE__*/_react.default.createElement(Drawer, { 140 | variant: "permanent", 141 | open: open 142 | }, /*#__PURE__*/_react.default.createElement(DrawerHeader, null, open && /*#__PURE__*/_react.default.createElement("div", { 143 | style: { 144 | marginLeft: '0px', 145 | marginRight: 'auto', 146 | display: 'inline-flex', 147 | alignItems: 'center', 148 | flexWrap: 'wrap' 149 | } 150 | }, /*#__PURE__*/_react.default.createElement(_material.Button, { 151 | style: { 152 | color: "#000000" 153 | }, 154 | onClick: () => navigate("/") 155 | }, /*#__PURE__*/_react.default.createElement("div", { 156 | style: { 157 | display: 'inline-flex', 158 | paddingRight: '20px' 159 | } 160 | }, logo), /*#__PURE__*/_react.default.createElement(_material.Typography, { 161 | variant: "h6" 162 | }, brand))), /*#__PURE__*/_react.default.createElement(_material.IconButton, { 163 | onClick: handleDrawerClose 164 | }, theme.direction === 'rtl' ? /*#__PURE__*/_react.default.createElement(_ChevronRight.default, null) : /*#__PURE__*/_react.default.createElement(_ChevronLeft.default, null))), /*#__PURE__*/_react.default.createElement(_material.Divider, null), drawerMenu, /*#__PURE__*/_react.default.createElement(_material.Divider, null)), /*#__PURE__*/_react.default.createElement(_material.Box, { 165 | component: "main", 166 | sx: { 167 | flexGrow: 1, 168 | backgroundColor: theme => theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[900], 169 | height: '100vh', 170 | overflow: 'auto' 171 | } 172 | }, /*#__PURE__*/_react.default.createElement(DrawerHeader, null), /*#__PURE__*/_react.default.createElement("div", { 173 | style: { 174 | position: 'relative' 175 | } 176 | }, /*#__PURE__*/_react.default.createElement(_material.Box, { 177 | mt: 5, 178 | ml: 3, 179 | mr: 3, 180 | mb: 3 181 | }, /*#__PURE__*/_react.default.createElement(_reactRouterDom.Outlet, null))))); 182 | }; 183 | exports.AppTemplate = AppTemplate; -------------------------------------------------------------------------------- /dist/cjs/components/templates/PublicTemplate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.PublicTemplate = void 0; 7 | var _react = _interopRequireDefault(require("react")); 8 | var _material = require("@mui/material"); 9 | var _reactRouterDom = require("react-router-dom"); 10 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 11 | const PublicTemplate = () => { 12 | return /*#__PURE__*/_react.default.createElement(_material.Box, { 13 | mt: 10 14 | }, /*#__PURE__*/_react.default.createElement(_material.Container, { 15 | maxWidth: "sm" 16 | }, /*#__PURE__*/_react.default.createElement(_material.Box, { 17 | component: "span", 18 | m: 5, 19 | textAlign: "center" 20 | }, /*#__PURE__*/_react.default.createElement(_reactRouterDom.Outlet, null)))); 21 | }; 22 | exports.PublicTemplate = PublicTemplate; -------------------------------------------------------------------------------- /dist/cjs/components/user/UserDelete.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("core-js/modules/es.weak-map.js"); 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.UserDelete = void 0; 8 | require("core-js/modules/web.dom-collections.iterator.js"); 9 | var _material = require("@mui/material"); 10 | var _react = _interopRequireWildcard(require("react")); 11 | var _reactRouterDom = require("react-router-dom"); 12 | var _SetPageTitle = require("../SetPageTitle"); 13 | var _auth = require("firebase/auth"); 14 | var _Fireact = require("../Fireact"); 15 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 16 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } 17 | const UserDelete = () => { 18 | const [email, setEmail] = (0, _react.useState)(""); 19 | const [error, setError] = (0, _react.useState)(null); 20 | const [processing, setProcessing] = (0, _react.useState)(false); 21 | const title = "Delete Account"; 22 | const navigate = (0, _reactRouterDom.useNavigate)(); 23 | const auth = (0, _auth.getAuth)(); 24 | const { 25 | config 26 | } = (0, _react.useContext)(_Fireact.FireactContext); 27 | const pathnames = config.pathnames; 28 | return /*#__PURE__*/_react.default.createElement(_material.Container, { 29 | maxWidth: "md" 30 | }, /*#__PURE__*/_react.default.createElement(_SetPageTitle.SetPageTitle, { 31 | title: title 32 | }), /*#__PURE__*/_react.default.createElement(_material.Paper, null, /*#__PURE__*/_react.default.createElement(_material.Box, { 33 | p: 2 34 | }, /*#__PURE__*/_react.default.createElement(_material.Typography, { 35 | component: "h1", 36 | variant: "h4", 37 | align: "center" 38 | }, title)), error !== null && /*#__PURE__*/_react.default.createElement(_material.Box, { 39 | p: 2 40 | }, /*#__PURE__*/_react.default.createElement(_material.Alert, { 41 | severity: "error" 42 | }, error)), /*#__PURE__*/_react.default.createElement(_material.Box, { 43 | p: 2 44 | }, /*#__PURE__*/_react.default.createElement(_material.Typography, null, "Please confirm your email address to delete your user account."), /*#__PURE__*/_react.default.createElement(_material.TextField, { 45 | required: true, 46 | fullWidth: true, 47 | name: "email", 48 | label: "Email Address", 49 | type: "email", 50 | autoComplete: "email", 51 | margin: "normal", 52 | onChange: e => setEmail(e.target.value) 53 | })), /*#__PURE__*/_react.default.createElement(_material.Box, { 54 | p: 2 55 | }, /*#__PURE__*/_react.default.createElement(_material.Grid, { 56 | container: true 57 | }, /*#__PURE__*/_react.default.createElement(_material.Grid, { 58 | item: true, 59 | xs: true 60 | }, /*#__PURE__*/_react.default.createElement(_material.Button, { 61 | type: "button", 62 | color: "secondary", 63 | variant: "outlined", 64 | disabled: processing, 65 | onClick: () => { 66 | navigate(pathnames.UserProfile); 67 | } 68 | }, "Back")), /*#__PURE__*/_react.default.createElement(_material.Grid, { 69 | item: true 70 | }, /*#__PURE__*/_react.default.createElement(_material.Button, { 71 | type: "button", 72 | color: "error", 73 | variant: "contained", 74 | disabled: processing, 75 | onClick: () => { 76 | setProcessing(true); 77 | setError(null); 78 | if (auth.currentUser.email !== email) { 79 | setError("The email address does not match with your email address."); 80 | setProcessing(false); 81 | } else { 82 | (0, _auth.deleteUser)(auth.currentUser).then(() => { 83 | // refresh page 84 | document.location.href = "/"; 85 | }).catch(error => { 86 | switch (error.code) { 87 | case "auth/requires-recent-login": 88 | setError("This operation is sensitive and requires recent authentication. Log in again before retrying this request."); 89 | break; 90 | default: 91 | setError(error.message); 92 | break; 93 | } 94 | setProcessing(false); 95 | }); 96 | } 97 | } 98 | }, "Delete User Account")))))); 99 | }; 100 | exports.UserDelete = UserDelete; -------------------------------------------------------------------------------- /dist/cjs/components/user/UserUpdateEmail.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("core-js/modules/es.weak-map.js"); 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.UserUpdateEmail = void 0; 8 | require("core-js/modules/web.dom-collections.iterator.js"); 9 | var _material = require("@mui/material"); 10 | var _react = _interopRequireWildcard(require("react")); 11 | var _reactRouterDom = require("react-router-dom"); 12 | var _SetPageTitle = require("../SetPageTitle"); 13 | var _auth = require("firebase/auth"); 14 | var _Fireact = require("../Fireact"); 15 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 16 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } 17 | const UserUpdateEmail = () => { 18 | const [email, setEmail] = (0, _react.useState)(""); 19 | const [error, setError] = (0, _react.useState)(null); 20 | const [success, setSuccess] = (0, _react.useState)(false); 21 | const [processing, setProcessing] = (0, _react.useState)(false); 22 | const title = "Change Email"; 23 | const navigate = (0, _reactRouterDom.useNavigate)(); 24 | const auth = (0, _auth.getAuth)(); 25 | const { 26 | config 27 | } = (0, _react.useContext)(_Fireact.FireactContext); 28 | const pathnames = config.pathnames; 29 | return /*#__PURE__*/_react.default.createElement(_material.Container, { 30 | maxWidth: "md" 31 | }, /*#__PURE__*/_react.default.createElement(_SetPageTitle.SetPageTitle, { 32 | title: title 33 | }), /*#__PURE__*/_react.default.createElement(_material.Paper, null, /*#__PURE__*/_react.default.createElement(_material.Box, { 34 | p: 2 35 | }, /*#__PURE__*/_react.default.createElement(_material.Typography, { 36 | component: "h1", 37 | variant: "h4", 38 | align: "center" 39 | }, title)), error !== null && /*#__PURE__*/_react.default.createElement(_material.Box, { 40 | p: 2 41 | }, /*#__PURE__*/_react.default.createElement(_material.Alert, { 42 | severity: "error" 43 | }, error)), success && /*#__PURE__*/_react.default.createElement(_material.Box, { 44 | p: 2 45 | }, /*#__PURE__*/_react.default.createElement(_material.Alert, { 46 | severity: "success" 47 | }, "Your email address has been updated successfully.")), /*#__PURE__*/_react.default.createElement(_material.Box, { 48 | p: 2 49 | }, /*#__PURE__*/_react.default.createElement(_material.TextField, { 50 | required: true, 51 | fullWidth: true, 52 | name: "email", 53 | label: "New Email Address", 54 | type: "email", 55 | autoComplete: "email", 56 | margin: "normal", 57 | onChange: e => setEmail(e.target.value) 58 | })), /*#__PURE__*/_react.default.createElement(_material.Box, { 59 | p: 2 60 | }, /*#__PURE__*/_react.default.createElement(_material.Grid, { 61 | container: true 62 | }, /*#__PURE__*/_react.default.createElement(_material.Grid, { 63 | item: true, 64 | xs: true 65 | }, /*#__PURE__*/_react.default.createElement(_material.Button, { 66 | type: "button", 67 | color: "secondary", 68 | variant: "outlined", 69 | disabled: processing, 70 | onClick: () => { 71 | navigate(pathnames.UserProfile); 72 | } 73 | }, "Back")), /*#__PURE__*/_react.default.createElement(_material.Grid, { 74 | item: true 75 | }, /*#__PURE__*/_react.default.createElement(_material.Button, { 76 | type: "button", 77 | variant: "contained", 78 | disabled: processing, 79 | onClick: () => { 80 | setProcessing(true); 81 | setSuccess(false); 82 | setError(null); 83 | (0, _auth.updateEmail)(auth.currentUser, email).then(() => { 84 | setSuccess(true); 85 | setProcessing(false); 86 | }).catch(error => { 87 | switch (error.code) { 88 | case "auth/requires-recent-login": 89 | setError("This operation is sensitive and requires recent authentication. Log in again before retrying this request."); 90 | break; 91 | case "auth/email-already-in-use": 92 | setError("The email address is already in use by another account."); 93 | break; 94 | default: 95 | setError(error.message); 96 | break; 97 | } 98 | setProcessing(false); 99 | }); 100 | } 101 | }, "Save")))))); 102 | }; 103 | exports.UserUpdateEmail = UserUpdateEmail; -------------------------------------------------------------------------------- /dist/cjs/components/user/UserUpdateName.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("core-js/modules/es.weak-map.js"); 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.UserUpdateName = void 0; 8 | require("core-js/modules/web.dom-collections.iterator.js"); 9 | require("core-js/modules/es.string.trim.js"); 10 | var _material = require("@mui/material"); 11 | var _react = _interopRequireWildcard(require("react")); 12 | var _reactRouterDom = require("react-router-dom"); 13 | var _SetPageTitle = require("../SetPageTitle"); 14 | var _auth = require("firebase/auth"); 15 | var _Fireact = require("../Fireact"); 16 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 17 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } 18 | const UserUpdateName = () => { 19 | const [fullname, setFullname] = (0, _react.useState)(""); 20 | const [error, setError] = (0, _react.useState)(null); 21 | const [success, setSuccess] = (0, _react.useState)(false); 22 | const [processing, setProcessing] = (0, _react.useState)(false); 23 | const title = "Change Name"; 24 | const navigate = (0, _reactRouterDom.useNavigate)(); 25 | const auth = (0, _auth.getAuth)(); 26 | const { 27 | config 28 | } = (0, _react.useContext)(_Fireact.FireactContext); 29 | const pathnames = config.pathnames; 30 | return /*#__PURE__*/_react.default.createElement(_material.Container, { 31 | maxWidth: "md" 32 | }, /*#__PURE__*/_react.default.createElement(_SetPageTitle.SetPageTitle, { 33 | title: title 34 | }), /*#__PURE__*/_react.default.createElement(_material.Paper, null, /*#__PURE__*/_react.default.createElement(_material.Box, { 35 | p: 2 36 | }, /*#__PURE__*/_react.default.createElement(_material.Typography, { 37 | component: "h1", 38 | variant: "h4", 39 | align: "center" 40 | }, title)), error !== null && /*#__PURE__*/_react.default.createElement(_material.Box, { 41 | p: 2 42 | }, /*#__PURE__*/_react.default.createElement(_material.Alert, { 43 | severity: "error" 44 | }, error)), success && /*#__PURE__*/_react.default.createElement(_material.Box, { 45 | p: 2 46 | }, /*#__PURE__*/_react.default.createElement(_material.Alert, { 47 | severity: "success" 48 | }, "Your name has been updated successfully.")), /*#__PURE__*/_react.default.createElement(_material.Box, { 49 | p: 2 50 | }, /*#__PURE__*/_react.default.createElement(_material.TextField, { 51 | required: true, 52 | fullWidth: true, 53 | name: "fullname", 54 | label: "Full Name", 55 | autoComplete: "name", 56 | type: "text", 57 | margin: "normal", 58 | onChange: e => setFullname(e.target.value) 59 | })), /*#__PURE__*/_react.default.createElement(_material.Box, { 60 | p: 2 61 | }, /*#__PURE__*/_react.default.createElement(_material.Grid, { 62 | container: true 63 | }, /*#__PURE__*/_react.default.createElement(_material.Grid, { 64 | item: true, 65 | xs: true 66 | }, /*#__PURE__*/_react.default.createElement(_material.Button, { 67 | type: "button", 68 | color: "secondary", 69 | variant: "outlined", 70 | disabled: processing, 71 | onClick: () => { 72 | navigate(pathnames.UserProfile); 73 | } 74 | }, "Back")), /*#__PURE__*/_react.default.createElement(_material.Grid, { 75 | item: true 76 | }, /*#__PURE__*/_react.default.createElement(_material.Button, { 77 | type: "button", 78 | variant: "contained", 79 | disabled: processing, 80 | onClick: () => { 81 | setProcessing(true); 82 | setSuccess(false); 83 | setError(null); 84 | if (fullname.trim() === "") { 85 | setError('Your full name is required.'); 86 | setProcessing(false); 87 | } else { 88 | (0, _auth.updateProfile)(auth.currentUser, { 89 | displayName: fullname 90 | }).then(() => { 91 | setSuccess(true); 92 | setProcessing(false); 93 | }).catch(error => { 94 | switch (error.code) { 95 | case "auth/requires-recent-login": 96 | setError("This operation is sensitive and requires recent authentication. Log in again before retrying this request."); 97 | break; 98 | default: 99 | setError(error.message); 100 | break; 101 | } 102 | setProcessing(false); 103 | }); 104 | } 105 | } 106 | }, "Save")))))); 107 | }; 108 | exports.UserUpdateName = UserUpdateName; -------------------------------------------------------------------------------- /dist/cjs/components/user/UserUpdatePassword.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("core-js/modules/es.weak-map.js"); 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.UserUpdatePassword = void 0; 8 | require("core-js/modules/web.dom-collections.iterator.js"); 9 | require("core-js/modules/es.regexp.exec.js"); 10 | require("core-js/modules/es.regexp.test.js"); 11 | var _material = require("@mui/material"); 12 | var _react = _interopRequireWildcard(require("react")); 13 | var _reactRouterDom = require("react-router-dom"); 14 | var _SetPageTitle = require("../SetPageTitle"); 15 | var _auth = require("firebase/auth"); 16 | var _Fireact = require("../Fireact"); 17 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 18 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } 19 | const UserUpdatePassword = () => { 20 | const [password, setPassword] = (0, _react.useState)(""); 21 | const [error, setError] = (0, _react.useState)(null); 22 | const [success, setSuccess] = (0, _react.useState)(false); 23 | const [processing, setProcessing] = (0, _react.useState)(false); 24 | const title = "Change Password"; 25 | const navigate = (0, _reactRouterDom.useNavigate)(); 26 | const auth = (0, _auth.getAuth)(); 27 | const { 28 | config 29 | } = (0, _react.useContext)(_Fireact.FireactContext); 30 | const pathnames = config.pathnames; 31 | return /*#__PURE__*/_react.default.createElement(_material.Container, { 32 | maxWidth: "md" 33 | }, /*#__PURE__*/_react.default.createElement(_SetPageTitle.SetPageTitle, { 34 | title: title 35 | }), /*#__PURE__*/_react.default.createElement(_material.Paper, null, /*#__PURE__*/_react.default.createElement(_material.Box, { 36 | p: 2 37 | }, /*#__PURE__*/_react.default.createElement(_material.Typography, { 38 | component: "h1", 39 | variant: "h4", 40 | align: "center" 41 | }, title)), error !== null && /*#__PURE__*/_react.default.createElement(_material.Box, { 42 | p: 2 43 | }, /*#__PURE__*/_react.default.createElement(_material.Alert, { 44 | severity: "error" 45 | }, error)), success && /*#__PURE__*/_react.default.createElement(_material.Box, { 46 | p: 2 47 | }, /*#__PURE__*/_react.default.createElement(_material.Alert, { 48 | severity: "success" 49 | }, "Your password has been updated successfully.")), /*#__PURE__*/_react.default.createElement(_material.Box, { 50 | p: 2 51 | }, /*#__PURE__*/_react.default.createElement(_material.TextField, { 52 | required: true, 53 | fullWidth: true, 54 | name: "password", 55 | label: "New Password", 56 | autoComplete: "new-password", 57 | type: "password", 58 | margin: "normal", 59 | onChange: e => setPassword(e.target.value) 60 | })), /*#__PURE__*/_react.default.createElement(_material.Box, { 61 | p: 2 62 | }, /*#__PURE__*/_react.default.createElement(_material.Grid, { 63 | container: true 64 | }, /*#__PURE__*/_react.default.createElement(_material.Grid, { 65 | item: true, 66 | xs: true 67 | }, /*#__PURE__*/_react.default.createElement(_material.Button, { 68 | type: "button", 69 | color: "secondary", 70 | variant: "outlined", 71 | disabled: processing, 72 | onClick: () => { 73 | navigate(pathnames.UserProfile); 74 | } 75 | }, "Back")), /*#__PURE__*/_react.default.createElement(_material.Grid, { 76 | item: true 77 | }, /*#__PURE__*/_react.default.createElement(_material.Button, { 78 | type: "button", 79 | variant: "contained", 80 | disabled: processing, 81 | onClick: () => { 82 | const passwordNumericRegex = /\d+/; 83 | const passwordUppercaseRegex = /[A-Z]+/; 84 | const passwordLowercaseRegex = /[a-z]+/; 85 | const passwordSpecialRegex = /[ !"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]+/; 86 | setProcessing(true); 87 | setSuccess(false); 88 | setError(null); 89 | if (!passwordNumericRegex.test(String(password)) || !passwordUppercaseRegex.test(String(password)) || !passwordLowercaseRegex.test(String(password)) || !passwordSpecialRegex.test(String(password)) || password.length < 8) { 90 | setError('The password must contain at least 8 characters with letters (both uppercase and lowercase), numbers, and symbols.'); 91 | setProcessing(false); 92 | } else { 93 | (0, _auth.updatePassword)(auth.currentUser, password).then(() => { 94 | setSuccess(true); 95 | setProcessing(false); 96 | }).catch(error => { 97 | switch (error.code) { 98 | case "auth/requires-recent-login": 99 | setError("This operation is sensitive and requires recent authentication. Log in again before retrying this request."); 100 | break; 101 | default: 102 | setError(error.message); 103 | break; 104 | } 105 | setProcessing(false); 106 | }); 107 | } 108 | } 109 | }, "Save")))))); 110 | }; 111 | exports.UserUpdatePassword = UserUpdatePassword; -------------------------------------------------------------------------------- /dist/cjs/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | Object.defineProperty(exports, "ActionPages", { 7 | enumerable: true, 8 | get: function get() { 9 | return _ActionPages.ActionPages; 10 | } 11 | }); 12 | Object.defineProperty(exports, "AppTemplate", { 13 | enumerable: true, 14 | get: function get() { 15 | return _AppTemplate.AppTemplate; 16 | } 17 | }); 18 | Object.defineProperty(exports, "AuthContext", { 19 | enumerable: true, 20 | get: function get() { 21 | return _Auth.AuthContext; 22 | } 23 | }); 24 | Object.defineProperty(exports, "AuthProvider", { 25 | enumerable: true, 26 | get: function get() { 27 | return _Auth.AuthProvider; 28 | } 29 | }); 30 | Object.defineProperty(exports, "AuthRoutes", { 31 | enumerable: true, 32 | get: function get() { 33 | return _Auth.AuthRoutes; 34 | } 35 | }); 36 | Object.defineProperty(exports, "FireactContext", { 37 | enumerable: true, 38 | get: function get() { 39 | return _Fireact.FireactContext; 40 | } 41 | }); 42 | Object.defineProperty(exports, "FireactProvider", { 43 | enumerable: true, 44 | get: function get() { 45 | return _Fireact.FireactProvider; 46 | } 47 | }); 48 | Object.defineProperty(exports, "MainMenu", { 49 | enumerable: true, 50 | get: function get() { 51 | return _MainMenu.MainMenu; 52 | } 53 | }); 54 | Object.defineProperty(exports, "PublicTemplate", { 55 | enumerable: true, 56 | get: function get() { 57 | return _PublicTemplate.PublicTemplate; 58 | } 59 | }); 60 | Object.defineProperty(exports, "ResetPassword", { 61 | enumerable: true, 62 | get: function get() { 63 | return _ResetPassword.ResetPassword; 64 | } 65 | }); 66 | Object.defineProperty(exports, "SetPageTitle", { 67 | enumerable: true, 68 | get: function get() { 69 | return _SetPageTitle.SetPageTitle; 70 | } 71 | }); 72 | Object.defineProperty(exports, "SignIn", { 73 | enumerable: true, 74 | get: function get() { 75 | return _SignIn.SignIn; 76 | } 77 | }); 78 | Object.defineProperty(exports, "SignUp", { 79 | enumerable: true, 80 | get: function get() { 81 | return _SignUp.SignUp; 82 | } 83 | }); 84 | Object.defineProperty(exports, "UserDelete", { 85 | enumerable: true, 86 | get: function get() { 87 | return _UserDelete.UserDelete; 88 | } 89 | }); 90 | Object.defineProperty(exports, "UserMenu", { 91 | enumerable: true, 92 | get: function get() { 93 | return _UserMenu.UserMenu; 94 | } 95 | }); 96 | Object.defineProperty(exports, "UserProfile", { 97 | enumerable: true, 98 | get: function get() { 99 | return _UserProfile.UserProfile; 100 | } 101 | }); 102 | Object.defineProperty(exports, "UserUpdateEmail", { 103 | enumerable: true, 104 | get: function get() { 105 | return _UserUpdateEmail.UserUpdateEmail; 106 | } 107 | }); 108 | Object.defineProperty(exports, "UserUpdateName", { 109 | enumerable: true, 110 | get: function get() { 111 | return _UserUpdateName.UserUpdateName; 112 | } 113 | }); 114 | Object.defineProperty(exports, "UserUpdatePassword", { 115 | enumerable: true, 116 | get: function get() { 117 | return _UserUpdatePassword.UserUpdatePassword; 118 | } 119 | }); 120 | Object.defineProperty(exports, "pathnames", { 121 | enumerable: true, 122 | get: function get() { 123 | return _pathnames.default; 124 | } 125 | }); 126 | var _Auth = require("./components/Auth"); 127 | var _UserDelete = require("./components/user/UserDelete"); 128 | var _PublicTemplate = require("./components/templates/PublicTemplate"); 129 | var _SignIn = require("./components/auth/SignIn"); 130 | var _SignUp = require("./components/auth/SignUp"); 131 | var _ResetPassword = require("./components/auth/ResetPassword"); 132 | var _AppTemplate = require("./components/templates/AppTemplate"); 133 | var _UserMenu = require("./components/menus/UserMenu"); 134 | var _MainMenu = require("./components/menus/MainMenu"); 135 | var _UserProfile = require("./components/user/UserProfile"); 136 | var _UserUpdateEmail = require("./components/user/UserUpdateEmail"); 137 | var _UserUpdateName = require("./components/user/UserUpdateName"); 138 | var _UserUpdatePassword = require("./components/user/UserUpdatePassword"); 139 | var _SetPageTitle = require("./components/SetPageTitle"); 140 | var _Fireact = require("./components/Fireact"); 141 | var _ActionPages = require("./components/auth/ActionPages"); 142 | var _pathnames = _interopRequireDefault(require("./pathnames.json")); 143 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -------------------------------------------------------------------------------- /dist/cjs/pathnames.json: -------------------------------------------------------------------------------- 1 | { 2 | "ActionPages": "/auth-action", 3 | "ResetPassword": "/reset-password", 4 | "SignIn": "/sign-in", 5 | "SignUp": "/sign-up", 6 | "UserDelete": "/user/delete", 7 | "UserProfile": "/user", 8 | "UserUpdateEmail": "/user/update-email", 9 | "UserUpdateName": "/user/update-name", 10 | "UserUpdatePassword": "/user/update-password" 11 | } -------------------------------------------------------------------------------- /dist/esm/assets/images/google.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | google_buttn 6 | Created with Sketch. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /dist/esm/assets/images/microsoft.svg: -------------------------------------------------------------------------------- 1 | MS-SymbolLockup 2 | -------------------------------------------------------------------------------- /dist/esm/components/Auth.js: -------------------------------------------------------------------------------- 1 | function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } 2 | function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } 3 | function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 4 | function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); } 5 | function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } 6 | import "core-js/modules/web.dom-collections.iterator.js"; 7 | import "core-js/modules/es.regexp.exec.js"; 8 | import "core-js/modules/es.string.search.js"; 9 | import "core-js/modules/es.symbol.description.js"; 10 | import React, { useEffect, useState, useContext } from "react"; 11 | import { initializeApp } from 'firebase/app'; 12 | import { getAuth, onAuthStateChanged } from 'firebase/auth'; 13 | import { Navigate, Outlet } from "react-router-dom"; 14 | import { Box, Container } from "@mui/material"; 15 | import { doc, getFirestore, setDoc } from "firebase/firestore"; 16 | import { FireactContext } from "./Fireact"; 17 | import { getFunctions } from "firebase/functions"; 18 | export const AuthContext = /*#__PURE__*/React.createContext(); 19 | export const AuthProvider = _ref => { 20 | let { 21 | children 22 | } = _ref; 23 | // authorized user state 24 | const [authUser, setAuthUser] = useState({ 25 | user: null, 26 | data: {}, 27 | checked: false 28 | }); 29 | const { 30 | config 31 | } = useContext(FireactContext); 32 | const [firebaseApp, setFirebaseApp] = useState(null); 33 | const [authInstance, setAuthInstance] = useState(null); 34 | const [firestoreInstance, setFirestoreInstance] = useState(null); 35 | const [functionsInstance, setFunctionsInstance] = useState(null); 36 | useEffect(() => { 37 | const app = initializeApp(config.firebaseConfig); 38 | const auth = getAuth(app); 39 | const firestore = getFirestore(app); 40 | const functions = getFunctions(app); 41 | setFirebaseApp(app); 42 | setAuthInstance(auth); 43 | setFirestoreInstance(firestore); 44 | setFunctionsInstance(functions); 45 | onAuthStateChanged(auth, user => { 46 | if (user !== null) { 47 | user.getIdToken().then(token => { 48 | const userDoc = doc(firestore, 'users', user.uid); 49 | setAuthUser(prevState => _objectSpread(_objectSpread({}, prevState), {}, { 50 | user: user, 51 | checked: true 52 | })); 53 | setDoc(userDoc, { 54 | displayName: user.displayName, 55 | photoURL: user.photoURL, 56 | email: user.email 57 | }, { 58 | merge: true 59 | }); 60 | }); 61 | } else { 62 | setAuthUser(prevState => _objectSpread(_objectSpread({}, prevState), {}, { 63 | user: null, 64 | checked: true 65 | })); 66 | } 67 | }); 68 | }, [config.firebaseConfig]); 69 | return /*#__PURE__*/React.createElement(AuthContext.Provider, { 70 | value: { 71 | authUser, 72 | setAuthUser, 73 | firebaseApp, 74 | authInstance, 75 | firestoreInstance, 76 | functionsInstance 77 | } 78 | }, children); 79 | }; 80 | export const AuthRoutes = _ref2 => { 81 | let { 82 | loader 83 | } = _ref2; 84 | const { 85 | authUser 86 | } = useContext(AuthContext); 87 | const { 88 | config 89 | } = useContext(FireactContext); 90 | const signInPath = config.pathnames.SignIn; 91 | if (authUser.checked) { 92 | if (authUser.user !== null) { 93 | return /*#__PURE__*/React.createElement(Outlet, null); 94 | } else { 95 | return /*#__PURE__*/React.createElement(Navigate, { 96 | to: signInPath + "?re=" + document.location.pathname + document.location.search + document.location.hash 97 | }); 98 | } 99 | } else { 100 | return /*#__PURE__*/React.createElement(Box, { 101 | mt: 10 102 | }, /*#__PURE__*/React.createElement(Container, { 103 | maxWidth: "sm" 104 | }, /*#__PURE__*/React.createElement(Box, { 105 | component: "span", 106 | m: 5, 107 | textAlign: "center" 108 | }, loader))); 109 | } 110 | }; -------------------------------------------------------------------------------- /dist/esm/components/Fireact.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | export const FireactContext = /*#__PURE__*/React.createContext(); 3 | export const FireactProvider = _ref => { 4 | let { 5 | config, 6 | children 7 | } = _ref; 8 | return /*#__PURE__*/React.createElement(FireactContext.Provider, { 9 | value: { 10 | config 11 | } 12 | }, children); 13 | }; -------------------------------------------------------------------------------- /dist/esm/components/SetPageTitle.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect } from "react"; 2 | import { AuthContext } from "./Auth"; 3 | export const SetPageTitle = _ref => { 4 | let { 5 | title 6 | } = _ref; 7 | const { 8 | brand 9 | } = useContext(AuthContext); 10 | useEffect(() => { 11 | document.title = brand ? "".concat(title, " - ").concat(brand) : title; 12 | }, [title, brand]); 13 | return /*#__PURE__*/React.createElement(React.Fragment, null); 14 | }; -------------------------------------------------------------------------------- /dist/esm/components/auth/ActionPages.js: -------------------------------------------------------------------------------- 1 | import "core-js/modules/web.dom-collections.iterator.js"; 2 | import "core-js/modules/web.url.js"; 3 | import "core-js/modules/web.url-search-params.js"; 4 | import "core-js/modules/es.regexp.exec.js"; 5 | import "core-js/modules/es.regexp.test.js"; 6 | import React, { useContext, useEffect, useState } from "react"; 7 | import { Alert, Box, Stack, Typography, TextField, Button } from "@mui/material"; 8 | import { SetPageTitle } from "../SetPageTitle"; 9 | import { getAuth, applyActionCode, verifyPasswordResetCode, confirmPasswordReset } from "firebase/auth"; 10 | import { NavLink } from "react-router-dom"; 11 | import { FireactContext } from "../Fireact"; 12 | export const ActionPages = _ref => { 13 | let { 14 | logo 15 | } = _ref; 16 | let title = "Unknown Action"; 17 | const params = new URL(document.location).searchParams; 18 | const mode = params.get('mode'); 19 | const actionCode = params.get('oobCode'); 20 | // const apiKey = params.get('apiKey'); 21 | // const continueUrl = params.get('continueUrl'); 22 | // const lang = params.get('lang') || 'en'; 23 | 24 | switch (mode) { 25 | case 'resetPassword': 26 | title = "Reset Password"; 27 | break; 28 | case 'recoverEmail': 29 | title = 'Recover Email'; 30 | break; 31 | case 'verifyEmail': 32 | title = 'Verfiy Email'; 33 | break; 34 | default: 35 | break; 36 | } 37 | return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(SetPageTitle, { 38 | title: title 39 | }), logo, /*#__PURE__*/React.createElement(Typography, { 40 | component: "h1", 41 | variant: "h5" 42 | }, title)), /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Stack, { 43 | spacing: 2, 44 | mt: 2 45 | }, (mode === 'verifyEmail' || mode === 'recoverEmail') && /*#__PURE__*/React.createElement(HandleAction, { 46 | actionCode: actionCode, 47 | mode: mode 48 | }), mode === 'resetPassword' && /*#__PURE__*/React.createElement(ResetPassword, { 49 | actionCode: actionCode 50 | })))); 51 | }; 52 | const ResetPassword = _ref2 => { 53 | let { 54 | actionCode 55 | } = _ref2; 56 | const [processing, setProcessing] = useState(false); 57 | const [error, setError] = useState(null); 58 | const [stage, setStage] = useState('verifying'); 59 | const { 60 | config 61 | } = useContext(FireactContext); 62 | const [newPassword, setNewPassword] = useState(''); 63 | const [confirmPassword, setConfirmPassword] = useState(''); 64 | useEffect(() => { 65 | const auth = getAuth(); 66 | verifyPasswordResetCode(auth, actionCode).then(() => { 67 | setStage('form'); 68 | }).catch(error => { 69 | setStage(''); 70 | setError(error.message); 71 | }); 72 | }, [actionCode]); 73 | return /*#__PURE__*/React.createElement(React.Fragment, null, error && /*#__PURE__*/React.createElement(Alert, { 74 | severity: "error" 75 | }, error), stage === 'verifying' && /*#__PURE__*/React.createElement(Typography, null, "Please wait while verifying your request..."), stage === 'form' && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(TextField, { 76 | required: true, 77 | fullWidth: true, 78 | name: "newPassword", 79 | label: "New Password", 80 | type: "password", 81 | autoComplete: "new-password", 82 | margin: "normal", 83 | onChange: e => setNewPassword(e.target.value) 84 | }), /*#__PURE__*/React.createElement(TextField, { 85 | required: true, 86 | fullWidth: true, 87 | name: "confirmPassword", 88 | label: "Confirm Password", 89 | type: "password", 90 | autoComplete: "", 91 | margin: "normal", 92 | onChange: e => setConfirmPassword(e.target.value) 93 | }), /*#__PURE__*/React.createElement(Button, { 94 | type: "button", 95 | fullWidth: true, 96 | variant: "contained", 97 | size: "large", 98 | disabled: processing, 99 | onClick: () => { 100 | setProcessing(true); 101 | setError(null); 102 | const passwordNumericRegex = /\d+/; 103 | const passwordUppercaseRegex = /[A-Z]+/; 104 | const passwordLowercaseRegex = /[a-z]+/; 105 | const passwordSpecialRegex = /[ !"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]+/; 106 | if (!passwordNumericRegex.test(String(newPassword)) || !passwordUppercaseRegex.test(String(newPassword)) || !passwordLowercaseRegex.test(String(newPassword)) || !passwordSpecialRegex.test(String(newPassword)) || newPassword.length < 8) { 107 | setError('The password must contain at least 8 characters with letters (both uppercase and lowercase), numbers, and symbols.'); 108 | setProcessing(false); 109 | } else if (newPassword !== confirmPassword) { 110 | setError('Confirm password does not match with new password.'); 111 | setProcessing(false); 112 | } else { 113 | const auth = getAuth(); 114 | confirmPasswordReset(auth, actionCode, newPassword).then(() => { 115 | setStage('success'); 116 | }).catch(error => { 117 | setError(error.message); 118 | setProcessing(false); 119 | }); 120 | } 121 | } 122 | }, "Reset Password")), stage === 'success' && /*#__PURE__*/React.createElement(Alert, { 123 | severity: "success" 124 | }, "Your email is verified. Please ", /*#__PURE__*/React.createElement(NavLink, { 125 | to: config.pathnames.SignIn 126 | }, "sign in"), " with your new password.")); 127 | }; 128 | const HandleAction = _ref3 => { 129 | let { 130 | mode, 131 | actionCode 132 | } = _ref3; 133 | const [processing, setProcessing] = useState(false); 134 | const [error, setError] = useState(null); 135 | const [success, setSuccess] = useState(false); 136 | const { 137 | config 138 | } = useContext(FireactContext); 139 | let processingMessage = 'Please wait'; 140 | let successMessage = 'Done'; 141 | switch (mode) { 142 | case 'verifyEmail': 143 | processingMessage = 'Please wait while verifying your email...'; 144 | successMessage = /*#__PURE__*/React.createElement(React.Fragment, null, "Your email is verified. Please ", /*#__PURE__*/React.createElement(NavLink, { 145 | to: config.pathnames.SignIn 146 | }, "sign in"), " again."); 147 | break; 148 | case 'recoverEmail': 149 | processingMessage = 'Please wait while resotring your email...'; 150 | successMessage = /*#__PURE__*/React.createElement(React.Fragment, null, "Your email is restored. Please ", /*#__PURE__*/React.createElement(NavLink, { 151 | to: config.pathnames.ResetPassword 152 | }, "reset password"), " to ensure your account is secured."); 153 | break; 154 | default: 155 | break; 156 | } 157 | useEffect(() => { 158 | setProcessing(true); 159 | setError(null); 160 | setSuccess(false); 161 | const auth = getAuth(); 162 | applyActionCode(auth, actionCode).then(() => { 163 | setSuccess(true); 164 | setProcessing(false); 165 | }).catch(error => { 166 | setError(error.message); 167 | setProcessing(false); 168 | }); 169 | }, [actionCode]); 170 | return /*#__PURE__*/React.createElement(React.Fragment, null, processing ? /*#__PURE__*/React.createElement(Typography, null, processingMessage) : success ? /*#__PURE__*/React.createElement(Alert, { 171 | severity: "success" 172 | }, successMessage) : error ? /*#__PURE__*/React.createElement(Alert, { 173 | severity: "error" 174 | }, error) : /*#__PURE__*/React.createElement(Alert, { 175 | severity: "error" 176 | }, "Something went wrong")); 177 | }; -------------------------------------------------------------------------------- /dist/esm/components/auth/ResetPassword.js: -------------------------------------------------------------------------------- 1 | import "core-js/modules/web.dom-collections.iterator.js"; 2 | import React, { useContext, useState } from "react"; 3 | import { Alert, Button, Box, Stack, TextField, Typography, Grid, Link } from "@mui/material"; 4 | import { Link as RouterLink } from "react-router-dom"; 5 | import { getAuth, sendPasswordResetEmail } from "firebase/auth"; 6 | import { SetPageTitle } from "../SetPageTitle"; 7 | import { FireactContext } from "../Fireact"; 8 | export const ResetPassword = _ref => { 9 | let { 10 | logo 11 | } = _ref; 12 | const { 13 | config 14 | } = useContext(FireactContext); 15 | const pathnames = config.pathnames; 16 | const signInUrl = pathnames.SignIn; 17 | const title = "Reset Password"; 18 | const [error, setError] = useState(null); 19 | const [success, setSuccess] = useState(false); 20 | const [processing, setProcessing] = useState(false); 21 | const [email, setEmail] = useState(""); 22 | return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(SetPageTitle, { 23 | title: title 24 | }), logo, /*#__PURE__*/React.createElement(Typography, { 25 | component: "h1", 26 | variant: "h5" 27 | }, title)), /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Stack, { 28 | spacing: 2, 29 | mt: 2 30 | }, error !== null && /*#__PURE__*/React.createElement(Alert, { 31 | severity: "error" 32 | }, error), success && /*#__PURE__*/React.createElement(Alert, { 33 | severity: "success" 34 | }, "A password reset email has been sent to the email address."), /*#__PURE__*/React.createElement(TextField, { 35 | required: true, 36 | fullWidth: true, 37 | name: "email", 38 | label: "Email", 39 | type: "email", 40 | autoComplete: "email", 41 | margin: "normal", 42 | onChange: e => setEmail(e.target.value) 43 | }), /*#__PURE__*/React.createElement(Button, { 44 | type: "button", 45 | fullWidth: true, 46 | variant: "contained", 47 | size: "large", 48 | disabled: processing, 49 | onClick: () => { 50 | setProcessing(true); 51 | setSuccess(false); 52 | setError(null); 53 | const auth = getAuth(); 54 | sendPasswordResetEmail(auth, email).then(() => { 55 | setProcessing(false); 56 | setSuccess(true); 57 | }).catch(error => { 58 | switch (error.code) { 59 | case "auth/invalid-email": 60 | setError('The email address is badly formatted.'); 61 | break; 62 | case "auth/missing-email": 63 | setError('The email address is missing.'); 64 | break; 65 | case "auth/user-not-found": 66 | setError('There is no user record corresponding to this identifier. The user may have been deleted.'); 67 | break; 68 | case "auth/too-many-requests": 69 | setError('We have blocked all requests from this device due to unusual activity. Try again later.'); 70 | break; 71 | default: 72 | setError(error.message); 73 | break; 74 | } 75 | setProcessing(false); 76 | }); 77 | } 78 | }, "Reset Password"), signInUrl && /*#__PURE__*/React.createElement(Grid, { 79 | container: true 80 | }, signInUrl && /*#__PURE__*/React.createElement(Grid, { 81 | item: true, 82 | xs: true, 83 | textAlign: "left" 84 | }, /*#__PURE__*/React.createElement(Link, { 85 | to: signInUrl, 86 | component: RouterLink 87 | }, "Sign in with an existing account")))))); 88 | }; -------------------------------------------------------------------------------- /dist/esm/components/auth/SignUp.js: -------------------------------------------------------------------------------- 1 | import "core-js/modules/web.dom-collections.iterator.js"; 2 | import "core-js/modules/es.string.trim.js"; 3 | import "core-js/modules/es.regexp.exec.js"; 4 | import "core-js/modules/es.regexp.test.js"; 5 | import React, { useContext, useState } from "react"; 6 | import { Alert, Button, Box, Stack, TextField, Typography, Grid, Link } from "@mui/material"; 7 | import { getAuth, createUserWithEmailAndPassword, updateProfile } from "firebase/auth"; 8 | import { Link as RouterLink } from "react-router-dom"; 9 | import { SetPageTitle } from "../SetPageTitle"; 10 | import { FireactContext } from "../Fireact"; 11 | export const SignUp = _ref => { 12 | let { 13 | logo, 14 | successUrl 15 | } = _ref; 16 | const { 17 | config 18 | } = useContext(FireactContext); 19 | const pathnames = config.pathnames; 20 | const title = "Sign Up"; 21 | const signInUrl = pathnames.SignIn; 22 | const resetPasswordUrl = pathnames.ResetPassword; 23 | const re = successUrl || "/"; // redirect successUrl or homepage after sign in 24 | 25 | const [error, setError] = useState(null); 26 | const [processing, setProcessing] = useState(false); 27 | const [email, setEmail] = useState(""); 28 | const [fullname, setFullname] = useState(""); 29 | const [password, setPassword] = useState(""); 30 | return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(SetPageTitle, { 31 | title: title 32 | }), logo, /*#__PURE__*/React.createElement(Typography, { 33 | component: "h1", 34 | variant: "h5" 35 | }, title)), /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Stack, { 36 | spacing: 2, 37 | mt: 2 38 | }, error !== null && /*#__PURE__*/React.createElement(Alert, { 39 | severity: "error" 40 | }, error), /*#__PURE__*/React.createElement(TextField, { 41 | required: true, 42 | fullWidth: true, 43 | name: "email", 44 | label: "Email", 45 | type: "email", 46 | autoComplete: "email", 47 | margin: "normal", 48 | onChange: e => setEmail(e.target.value) 49 | }), /*#__PURE__*/React.createElement(TextField, { 50 | required: true, 51 | fullWidth: true, 52 | name: "fullname", 53 | label: "Full Name", 54 | autoComplete: "name", 55 | type: "text", 56 | margin: "normal", 57 | onChange: e => setFullname(e.target.value) 58 | }), /*#__PURE__*/React.createElement(TextField, { 59 | required: true, 60 | fullWidth: true, 61 | name: "password", 62 | label: "New Password", 63 | autoComplete: "new-password", 64 | type: "password", 65 | margin: "normal", 66 | onChange: e => setPassword(e.target.value) 67 | }), /*#__PURE__*/React.createElement(Button, { 68 | type: "button", 69 | fullWidth: true, 70 | variant: "contained", 71 | size: "large", 72 | disabled: processing, 73 | onClick: () => { 74 | setProcessing(true); 75 | setError(null); 76 | const passwordNumericRegex = /\d+/; 77 | const passwordUppercaseRegex = /[A-Z]+/; 78 | const passwordLowercaseRegex = /[a-z]+/; 79 | const passwordSpecialRegex = /[ !"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]+/; 80 | if (fullname.trim() === "") { 81 | setError('Your full name is required.'); 82 | setProcessing(false); 83 | } else if (!passwordNumericRegex.test(String(password)) || !passwordUppercaseRegex.test(String(password)) || !passwordLowercaseRegex.test(String(password)) || !passwordSpecialRegex.test(String(password)) || password.length < 8) { 84 | setError('The password must contain at least 8 characters with letters (both uppercase and lowercase), numbers, and symbols.'); 85 | setProcessing(false); 86 | } else { 87 | const auth = getAuth(); 88 | createUserWithEmailAndPassword(auth, email, password).then(result => { 89 | return updateProfile(result.user, { 90 | displayName: fullname 91 | }); 92 | }).then(() => { 93 | document.location.href = re.substr(0, 1) === '/' && re.substr(1, 1) !== '/' ? re : '/'; 94 | }).catch(error => { 95 | switch (error.code) { 96 | case "auth/invalid-email": 97 | setError('The email address is badly formatted.'); 98 | break; 99 | case "auth/missing-email": 100 | setError('The email address is missing.'); 101 | break; 102 | case "auth/email-already-in-use": 103 | setError('The email address is already in use by another account.'); 104 | break; 105 | default: 106 | setError(error.message); 107 | break; 108 | } 109 | setProcessing(false); 110 | }); 111 | } 112 | } 113 | }, "Sign Up"), (signInUrl || resetPasswordUrl) && /*#__PURE__*/React.createElement(Grid, { 114 | container: true 115 | }, signInUrl && /*#__PURE__*/React.createElement(Grid, { 116 | item: true, 117 | xs: true, 118 | textAlign: "left" 119 | }, /*#__PURE__*/React.createElement(Link, { 120 | to: signInUrl, 121 | component: RouterLink 122 | }, "Sign in with an existing account")), resetPasswordUrl && /*#__PURE__*/React.createElement(Grid, { 123 | item: true, 124 | textAlign: "left" 125 | }, /*#__PURE__*/React.createElement(Link, { 126 | to: resetPasswordUrl, 127 | component: RouterLink 128 | }, "Reset password")))))); 129 | }; -------------------------------------------------------------------------------- /dist/esm/components/menus/MainMenu.js: -------------------------------------------------------------------------------- 1 | import { Divider, List, ListItemButton, ListItemIcon, ListItemText, Typography } from "@mui/material"; 2 | import React, { useContext } from "react"; 3 | import { NavLink } from "react-router-dom"; 4 | import HomeIcon from '@mui/icons-material/Home'; 5 | import AccountBoxIcon from '@mui/icons-material/AccountBox'; 6 | import { FireactContext } from "../Fireact"; 7 | export const MainMenu = _ref => { 8 | let { 9 | customItems 10 | } = _ref; 11 | const { 12 | config 13 | } = useContext(FireactContext); 14 | const pathnames = config.pathnames; 15 | const profileUrl = pathnames.UserProfile; 16 | return /*#__PURE__*/React.createElement(List, { 17 | component: "nav" 18 | }, /*#__PURE__*/React.createElement(NavLink, { 19 | to: "/", 20 | style: { 21 | textDecoration: 'none' 22 | }, 23 | key: "home" 24 | }, /*#__PURE__*/React.createElement(ListItemButton, null, /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(HomeIcon, null)), /*#__PURE__*/React.createElement(ListItemText, { 25 | primary: /*#__PURE__*/React.createElement(Typography, { 26 | color: "textPrimary" 27 | }, "Home") 28 | }))), customItems, profileUrl && [/*#__PURE__*/React.createElement(Divider, { 29 | key: "profile-divider" 30 | }), /*#__PURE__*/React.createElement(NavLink, { 31 | to: profileUrl, 32 | style: { 33 | textDecoration: 'none' 34 | }, 35 | key: "profile" 36 | }, /*#__PURE__*/React.createElement(ListItemButton, null, /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(AccountBoxIcon, null)), /*#__PURE__*/React.createElement(ListItemText, { 37 | primary: /*#__PURE__*/React.createElement(Typography, { 38 | color: "textPrimary" 39 | }, "My Profile") 40 | })))]); 41 | }; -------------------------------------------------------------------------------- /dist/esm/components/menus/UserMenu.js: -------------------------------------------------------------------------------- 1 | import "core-js/modules/web.dom-collections.iterator.js"; 2 | import { Avatar, Divider, IconButton, Menu, MenuItem } from "@mui/material"; 3 | import React, { useContext, useState } from "react"; 4 | import { AuthContext } from "../Auth"; 5 | import AccountBoxIcon from '@mui/icons-material/AccountBox'; 6 | import ExitToAppIcon from '@mui/icons-material/ExitToApp'; 7 | import { getAuth, signOut } from "firebase/auth"; 8 | import { useNavigate } from "react-router-dom"; 9 | import { FireactContext } from "../Fireact"; 10 | export const UserMenu = _ref => { 11 | let { 12 | customItems 13 | } = _ref; 14 | const { 15 | config 16 | } = useContext(FireactContext); 17 | const pathnames = config.pathnames; 18 | const profileUrl = pathnames.UserProfile; 19 | const [anchorEl, setAnchorEl] = useState(null); 20 | const open = Boolean(anchorEl); 21 | const handleMenu = event => { 22 | setAnchorEl(event.currentTarget); 23 | }; 24 | const handleClose = () => { 25 | setAnchorEl(null); 26 | }; 27 | const navigate = useNavigate(); 28 | return /*#__PURE__*/React.createElement(AuthContext.Consumer, null, context => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(IconButton, { 29 | "ria-label": "account of current user", 30 | "aria-controls": "menu-appbar", 31 | onClick: handleMenu, 32 | "aria-haspopup": "true" 33 | }, /*#__PURE__*/React.createElement(Avatar, { 34 | alt: context.authUser.user.displayName, 35 | src: context.authUser.user.photoURL ? context.authUser.user.photoURL : "https://ui-avatars.com/api/?name=" + encodeURI(context.authUser.user.displayName) + "&background=007bff&size=64&color=f8f9fc" 36 | })), /*#__PURE__*/React.createElement(Menu, { 37 | id: "menu-appbar", 38 | anchorEl: anchorEl, 39 | anchorOrigin: { 40 | vertical: 'bottom', 41 | horizontal: 'right' 42 | }, 43 | keepMounted: true, 44 | transformOrigin: { 45 | vertical: 'top', 46 | horizontal: 'right' 47 | }, 48 | open: open, 49 | onClose: handleClose 50 | }, profileUrl && [/*#__PURE__*/React.createElement(MenuItem, { 51 | key: "profile-menu-item", 52 | onClick: e => { 53 | e.preventDefault(); 54 | handleClose(); 55 | navigate(profileUrl); 56 | } 57 | }, /*#__PURE__*/React.createElement(AccountBoxIcon, { 58 | sx: { 59 | marginRight: "10px" 60 | } 61 | }), " Profile"), /*#__PURE__*/React.createElement(Divider, { 62 | key: "profile-menu-divider" 63 | })], customItems, /*#__PURE__*/React.createElement(MenuItem, { 64 | onClick: e => { 65 | e.preventDefault(); 66 | handleClose(); 67 | const auth = getAuth(); 68 | signOut(auth).then(() => { 69 | document.location.href = "/"; 70 | }); 71 | } 72 | }, /*#__PURE__*/React.createElement(ExitToAppIcon, { 73 | sx: { 74 | marginRight: "10px" 75 | } 76 | }), " Sign Out")))); 77 | }; -------------------------------------------------------------------------------- /dist/esm/components/templates/AppTemplate.js: -------------------------------------------------------------------------------- 1 | import "core-js/modules/web.dom-collections.iterator.js"; 2 | import "core-js/modules/es.symbol.description.js"; 3 | function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } 4 | function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } 5 | function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 6 | function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); } 7 | function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } 8 | import React, { useContext, useState } from "react"; 9 | import { styled, useTheme } from '@mui/material/styles'; 10 | import { AppBar as MuiAppBar, Box, CssBaseline, Drawer as MuiDrawer, IconButton, Toolbar, Divider, Typography, Button } from "@mui/material"; 11 | import MenuIcon from '@mui/icons-material/Menu'; 12 | import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; 13 | import ChevronRightIcon from '@mui/icons-material/ChevronRight'; 14 | import { Outlet, useNavigate } from "react-router-dom"; 15 | import { FireactContext } from "../Fireact"; 16 | const drawerWidth = 240; 17 | const openedMixin = theme => ({ 18 | width: drawerWidth, 19 | transition: theme.transitions.create('width', { 20 | easing: theme.transitions.easing.sharp, 21 | duration: theme.transitions.duration.enteringScreen 22 | }), 23 | overflowX: 'hidden' 24 | }); 25 | const closedMixin = theme => ({ 26 | transition: theme.transitions.create('width', { 27 | easing: theme.transitions.easing.sharp, 28 | duration: theme.transitions.duration.leavingScreen 29 | }), 30 | overflowX: 'hidden', 31 | width: "calc(".concat(theme.spacing(7), " + 1px)"), 32 | [theme.breakpoints.up('sm')]: { 33 | width: "calc(".concat(theme.spacing(9), " + 1px)") 34 | } 35 | }); 36 | const DrawerHeader = styled('div')(_ref => { 37 | let { 38 | theme 39 | } = _ref; 40 | return _objectSpread({ 41 | display: 'flex', 42 | alignItems: 'center', 43 | justifyContent: 'flex-end', 44 | padding: theme.spacing(0, 1) 45 | }, theme.mixins.toolbar); 46 | }); 47 | const AppBar = styled(MuiAppBar, { 48 | shouldForwardProp: prop => prop !== 'open' 49 | })(_ref2 => { 50 | let { 51 | theme, 52 | open 53 | } = _ref2; 54 | return _objectSpread({ 55 | zIndex: theme.zIndex.drawer + 1, 56 | transition: theme.transitions.create(['width', 'margin'], { 57 | easing: theme.transitions.easing.sharp, 58 | duration: theme.transitions.duration.leavingScreen 59 | }) 60 | }, open && { 61 | marginLeft: drawerWidth, 62 | width: "calc(100% - ".concat(drawerWidth, "px)"), 63 | transition: theme.transitions.create(['width', 'margin'], { 64 | easing: theme.transitions.easing.sharp, 65 | duration: theme.transitions.duration.enteringScreen 66 | }) 67 | }); 68 | }); 69 | const Drawer = styled(MuiDrawer, { 70 | shouldForwardProp: prop => prop !== 'open' 71 | })(_ref3 => { 72 | let { 73 | theme, 74 | open 75 | } = _ref3; 76 | return _objectSpread(_objectSpread({ 77 | width: drawerWidth, 78 | flexShrink: 0, 79 | whiteSpace: 'nowrap', 80 | boxSizing: 'border-box' 81 | }, open && _objectSpread(_objectSpread({}, openedMixin(theme)), {}, { 82 | '& .MuiDrawer-paper': openedMixin(theme) 83 | })), !open && _objectSpread(_objectSpread({}, closedMixin(theme)), {}, { 84 | '& .MuiDrawer-paper': closedMixin(theme) 85 | })); 86 | }); 87 | export const AppTemplate = _ref4 => { 88 | let { 89 | logo, 90 | drawerMenu, 91 | toolbarChildren, 92 | toolBarMenu 93 | } = _ref4; 94 | const theme = useTheme(); 95 | const [open, setOpen] = useState(true); 96 | const handleDrawerOpen = () => { 97 | setOpen(true); 98 | }; 99 | const handleDrawerClose = () => { 100 | setOpen(false); 101 | }; 102 | const { 103 | config 104 | } = useContext(FireactContext); 105 | const brand = config.brand; 106 | const navigate = useNavigate(); 107 | return /*#__PURE__*/React.createElement(Box, { 108 | sx: { 109 | display: "flex" 110 | } 111 | }, /*#__PURE__*/React.createElement(CssBaseline, null), /*#__PURE__*/React.createElement(AppBar, { 112 | position: "fixed", 113 | open: open 114 | }, /*#__PURE__*/React.createElement(Toolbar, null, /*#__PURE__*/React.createElement(IconButton, { 115 | color: "inherit", 116 | "aria-label": "open drawer", 117 | onClick: handleDrawerOpen, 118 | edge: "start", 119 | sx: _objectSpread({ 120 | marginRight: '36px' 121 | }, open && { 122 | display: 'none' 123 | }) 124 | }, /*#__PURE__*/React.createElement(MenuIcon, null)), toolbarChildren, /*#__PURE__*/React.createElement("div", { 125 | style: { 126 | marginLeft: "auto", 127 | marginRight: "0px" 128 | } 129 | }, toolBarMenu))), /*#__PURE__*/React.createElement(Drawer, { 130 | variant: "permanent", 131 | open: open 132 | }, /*#__PURE__*/React.createElement(DrawerHeader, null, open && /*#__PURE__*/React.createElement("div", { 133 | style: { 134 | marginLeft: '0px', 135 | marginRight: 'auto', 136 | display: 'inline-flex', 137 | alignItems: 'center', 138 | flexWrap: 'wrap' 139 | } 140 | }, /*#__PURE__*/React.createElement(Button, { 141 | style: { 142 | color: "#000000" 143 | }, 144 | onClick: () => navigate("/") 145 | }, /*#__PURE__*/React.createElement("div", { 146 | style: { 147 | display: 'inline-flex', 148 | paddingRight: '20px' 149 | } 150 | }, logo), /*#__PURE__*/React.createElement(Typography, { 151 | variant: "h6" 152 | }, brand))), /*#__PURE__*/React.createElement(IconButton, { 153 | onClick: handleDrawerClose 154 | }, theme.direction === 'rtl' ? /*#__PURE__*/React.createElement(ChevronRightIcon, null) : /*#__PURE__*/React.createElement(ChevronLeftIcon, null))), /*#__PURE__*/React.createElement(Divider, null), drawerMenu, /*#__PURE__*/React.createElement(Divider, null)), /*#__PURE__*/React.createElement(Box, { 155 | component: "main", 156 | sx: { 157 | flexGrow: 1, 158 | backgroundColor: theme => theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[900], 159 | height: '100vh', 160 | overflow: 'auto' 161 | } 162 | }, /*#__PURE__*/React.createElement(DrawerHeader, null), /*#__PURE__*/React.createElement("div", { 163 | style: { 164 | position: 'relative' 165 | } 166 | }, /*#__PURE__*/React.createElement(Box, { 167 | mt: 5, 168 | ml: 3, 169 | mr: 3, 170 | mb: 3 171 | }, /*#__PURE__*/React.createElement(Outlet, null))))); 172 | }; -------------------------------------------------------------------------------- /dist/esm/components/templates/PublicTemplate.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box, Container } from "@mui/material"; 3 | import { Outlet } from "react-router-dom"; 4 | export const PublicTemplate = () => { 5 | return /*#__PURE__*/React.createElement(Box, { 6 | mt: 10 7 | }, /*#__PURE__*/React.createElement(Container, { 8 | maxWidth: "sm" 9 | }, /*#__PURE__*/React.createElement(Box, { 10 | component: "span", 11 | m: 5, 12 | textAlign: "center" 13 | }, /*#__PURE__*/React.createElement(Outlet, null)))); 14 | }; -------------------------------------------------------------------------------- /dist/esm/components/user/UserDelete.js: -------------------------------------------------------------------------------- 1 | import "core-js/modules/web.dom-collections.iterator.js"; 2 | import { Alert, Box, Button, Container, Grid, Paper, TextField, Typography } from "@mui/material"; 3 | import React, { useContext, useState } from "react"; 4 | import { useNavigate } from "react-router-dom"; 5 | import { SetPageTitle } from "../SetPageTitle"; 6 | import { deleteUser, getAuth } from "firebase/auth"; 7 | import { FireactContext } from "../Fireact"; 8 | export const UserDelete = () => { 9 | const [email, setEmail] = useState(""); 10 | const [error, setError] = useState(null); 11 | const [processing, setProcessing] = useState(false); 12 | const title = "Delete Account"; 13 | const navigate = useNavigate(); 14 | const auth = getAuth(); 15 | const { 16 | config 17 | } = useContext(FireactContext); 18 | const pathnames = config.pathnames; 19 | return /*#__PURE__*/React.createElement(Container, { 20 | maxWidth: "md" 21 | }, /*#__PURE__*/React.createElement(SetPageTitle, { 22 | title: title 23 | }), /*#__PURE__*/React.createElement(Paper, null, /*#__PURE__*/React.createElement(Box, { 24 | p: 2 25 | }, /*#__PURE__*/React.createElement(Typography, { 26 | component: "h1", 27 | variant: "h4", 28 | align: "center" 29 | }, title)), error !== null && /*#__PURE__*/React.createElement(Box, { 30 | p: 2 31 | }, /*#__PURE__*/React.createElement(Alert, { 32 | severity: "error" 33 | }, error)), /*#__PURE__*/React.createElement(Box, { 34 | p: 2 35 | }, /*#__PURE__*/React.createElement(Typography, null, "Please confirm your email address to delete your user account."), /*#__PURE__*/React.createElement(TextField, { 36 | required: true, 37 | fullWidth: true, 38 | name: "email", 39 | label: "Email Address", 40 | type: "email", 41 | autoComplete: "email", 42 | margin: "normal", 43 | onChange: e => setEmail(e.target.value) 44 | })), /*#__PURE__*/React.createElement(Box, { 45 | p: 2 46 | }, /*#__PURE__*/React.createElement(Grid, { 47 | container: true 48 | }, /*#__PURE__*/React.createElement(Grid, { 49 | item: true, 50 | xs: true 51 | }, /*#__PURE__*/React.createElement(Button, { 52 | type: "button", 53 | color: "secondary", 54 | variant: "outlined", 55 | disabled: processing, 56 | onClick: () => { 57 | navigate(pathnames.UserProfile); 58 | } 59 | }, "Back")), /*#__PURE__*/React.createElement(Grid, { 60 | item: true 61 | }, /*#__PURE__*/React.createElement(Button, { 62 | type: "button", 63 | color: "error", 64 | variant: "contained", 65 | disabled: processing, 66 | onClick: () => { 67 | setProcessing(true); 68 | setError(null); 69 | if (auth.currentUser.email !== email) { 70 | setError("The email address does not match with your email address."); 71 | setProcessing(false); 72 | } else { 73 | deleteUser(auth.currentUser).then(() => { 74 | // refresh page 75 | document.location.href = "/"; 76 | }).catch(error => { 77 | switch (error.code) { 78 | case "auth/requires-recent-login": 79 | setError("This operation is sensitive and requires recent authentication. Log in again before retrying this request."); 80 | break; 81 | default: 82 | setError(error.message); 83 | break; 84 | } 85 | setProcessing(false); 86 | }); 87 | } 88 | } 89 | }, "Delete User Account")))))); 90 | }; -------------------------------------------------------------------------------- /dist/esm/components/user/UserProfile.js: -------------------------------------------------------------------------------- 1 | import "core-js/modules/web.dom-collections.iterator.js"; 2 | import { Alert, Grid, List, ListItem, Box, Avatar, Typography, Divider, Paper, Container } from "@mui/material"; 3 | import React, { useContext, useState } from "react"; 4 | import { AuthContext } from "../Auth"; 5 | import EditIcon from '@mui/icons-material/Edit'; 6 | import SendIcon from '@mui/icons-material/Send'; 7 | import VerifiedUserIcon from '@mui/icons-material/VerifiedUser'; 8 | import { useNavigate } from "react-router-dom"; 9 | import { SetPageTitle } from "../SetPageTitle"; 10 | import { getAuth, sendEmailVerification } from "firebase/auth"; 11 | import { FireactContext } from "../Fireact"; 12 | export const UserProfile = () => { 13 | const navigate = useNavigate(); 14 | const auth = getAuth(); 15 | const [sendVerification, setSendVerification] = useState({ 16 | 'success': false, 17 | 'error': null 18 | }); 19 | const { 20 | config 21 | } = useContext(FireactContext); 22 | const pathnames = config.pathnames; 23 | return /*#__PURE__*/React.createElement(AuthContext.Consumer, null, context => /*#__PURE__*/React.createElement(Container, { 24 | maxWidth: "md" 25 | }, /*#__PURE__*/React.createElement(SetPageTitle, { 26 | title: "User Profile" 27 | }), /*#__PURE__*/React.createElement(Paper, null, sendVerification.error !== null && /*#__PURE__*/React.createElement(Box, { 28 | p: 2 29 | }, /*#__PURE__*/React.createElement(Alert, { 30 | severity: "error" 31 | }, sendVerification.error)), sendVerification.success && /*#__PURE__*/React.createElement(Box, { 32 | p: 2 33 | }, /*#__PURE__*/React.createElement(Alert, { 34 | severity: "success" 35 | }, "Please check your email inbox to verify the email address. Refresh this page after you verified your email address.")), /*#__PURE__*/React.createElement(List, { 36 | component: "nav" 37 | }, /*#__PURE__*/React.createElement(ListItem, null, /*#__PURE__*/React.createElement(Grid, { 38 | container: true, 39 | spacing: 1 40 | }, /*#__PURE__*/React.createElement(Grid, { 41 | item: true, 42 | xs: true 43 | }, /*#__PURE__*/React.createElement(Box, { 44 | p: 1 45 | }, /*#__PURE__*/React.createElement("strong", null, "AVATAR"), /*#__PURE__*/React.createElement("br", null), /*#__PURE__*/React.createElement(Typography, { 46 | color: "textSecondary" 47 | }, "Update via social login")), /*#__PURE__*/React.createElement(Box, { 48 | p: 1 49 | })), /*#__PURE__*/React.createElement(Grid, { 50 | item: true, 51 | sx: { 52 | flexDirection: "column", 53 | display: "flex", 54 | justifyContent: "center" 55 | } 56 | }, /*#__PURE__*/React.createElement(Avatar, { 57 | alt: context.authUser.user.displayName, 58 | src: context.authUser.user.photoURL ? context.authUser.user.photoURL : "https://ui-avatars.com/api/?name=" + encodeURI(context.authUser.user.displayName) + "&background=007bff&size=64&color=f8f9fc", 59 | style: { 60 | height: '64px', 61 | width: '64px' 62 | } 63 | })))), /*#__PURE__*/React.createElement(Divider, null), /*#__PURE__*/React.createElement(ListItem, { 64 | button: true, 65 | onClick: () => { 66 | navigate(pathnames.UserUpdateName); 67 | } 68 | }, /*#__PURE__*/React.createElement(Grid, { 69 | container: true, 70 | spacing: 1 71 | }, /*#__PURE__*/React.createElement(Grid, { 72 | item: true, 73 | xs: true 74 | }, /*#__PURE__*/React.createElement(Box, { 75 | p: 1 76 | }, /*#__PURE__*/React.createElement("strong", null, "NAME"), /*#__PURE__*/React.createElement("br", null), context.authUser.user.displayName)), /*#__PURE__*/React.createElement(Grid, { 77 | item: true, 78 | sx: { 79 | flexDirection: "column", 80 | display: "flex", 81 | justifyContent: "center" 82 | } 83 | }, /*#__PURE__*/React.createElement(EditIcon, null)))), /*#__PURE__*/React.createElement(Divider, null), /*#__PURE__*/React.createElement(ListItem, { 84 | button: true, 85 | onClick: () => { 86 | navigate(pathnames.UserUpdateEmail); 87 | } 88 | }, /*#__PURE__*/React.createElement(Grid, { 89 | container: true, 90 | spacing: 1 91 | }, /*#__PURE__*/React.createElement(Grid, { 92 | item: true, 93 | xs: true 94 | }, /*#__PURE__*/React.createElement(Box, { 95 | p: 1 96 | }, /*#__PURE__*/React.createElement("strong", null, "EMAIL"), /*#__PURE__*/React.createElement("br", null), context.authUser.user.email)), /*#__PURE__*/React.createElement(Grid, { 97 | item: true, 98 | sx: { 99 | flexDirection: "column", 100 | display: "flex", 101 | justifyContent: "center" 102 | } 103 | }, /*#__PURE__*/React.createElement(EditIcon, null)))), /*#__PURE__*/React.createElement(Divider, null), /*#__PURE__*/React.createElement(ListItem, { 104 | button: true, 105 | onClick: () => { 106 | if (!context.authUser.user.emailVerified) { 107 | setSendVerification({ 108 | 'success': false, 109 | 'error': null 110 | }); 111 | sendEmailVerification(auth.currentUser).then(() => { 112 | setSendVerification({ 113 | 'success': true, 114 | 'error': null 115 | }); 116 | }).catch(error => { 117 | switch (error.code) { 118 | case "auth/too-many-requests": 119 | setSendVerification({ 120 | 'success': false, 121 | 'error': "We have blocked all requests from this device due to unusual activity. Try again later." 122 | }); 123 | break; 124 | default: 125 | setSendVerification({ 126 | 'success': false, 127 | 'error': error.message 128 | }); 129 | break; 130 | } 131 | }); 132 | } 133 | } 134 | }, /*#__PURE__*/React.createElement(Grid, { 135 | container: true, 136 | spacing: 1 137 | }, /*#__PURE__*/React.createElement(Grid, { 138 | item: true, 139 | xs: true 140 | }, /*#__PURE__*/React.createElement(Box, { 141 | p: 1 142 | }, /*#__PURE__*/React.createElement("strong", null, "EMAIL VERIFIED"), /*#__PURE__*/React.createElement("br", null), context.authUser.user.emailVerified ? " Verified" : "Unverified email")), /*#__PURE__*/React.createElement(Grid, { 143 | item: true, 144 | sx: { 145 | flexDirection: "column", 146 | display: "flex", 147 | justifyContent: "center" 148 | } 149 | }, context.authUser.user.emailVerified ? /*#__PURE__*/React.createElement(VerifiedUserIcon, null) : /*#__PURE__*/React.createElement(SendIcon, null)))), /*#__PURE__*/React.createElement(Divider, null), /*#__PURE__*/React.createElement(ListItem, { 150 | button: true, 151 | onClick: () => { 152 | navigate(pathnames.UserUpdatePassword); 153 | } 154 | }, /*#__PURE__*/React.createElement(Grid, { 155 | container: true, 156 | spacing: 1 157 | }, /*#__PURE__*/React.createElement(Grid, { 158 | item: true, 159 | xs: true 160 | }, /*#__PURE__*/React.createElement(Box, { 161 | p: 1 162 | }, /*#__PURE__*/React.createElement("strong", null, "PASSWORD"), /*#__PURE__*/React.createElement("br", null), "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022")), /*#__PURE__*/React.createElement(Grid, { 163 | item: true, 164 | sx: { 165 | flexDirection: "column", 166 | display: "flex", 167 | justifyContent: "center" 168 | } 169 | }, /*#__PURE__*/React.createElement(EditIcon, null)))), /*#__PURE__*/React.createElement(Divider, null), /*#__PURE__*/React.createElement(ListItem, { 170 | button: true, 171 | onClick: () => { 172 | navigate(pathnames.UserDelete); 173 | } 174 | }, /*#__PURE__*/React.createElement(Grid, { 175 | container: true, 176 | spacing: 1 177 | }, /*#__PURE__*/React.createElement(Grid, { 178 | container: true, 179 | item: true, 180 | xs: 12, 181 | md: 4 182 | }, /*#__PURE__*/React.createElement(Box, { 183 | p: 1 184 | }, /*#__PURE__*/React.createElement(Typography, { 185 | color: "error" 186 | }, /*#__PURE__*/React.createElement("strong", null, "DELETE ACCOUNT")))))))))); 187 | }; -------------------------------------------------------------------------------- /dist/esm/components/user/UserUpdateEmail.js: -------------------------------------------------------------------------------- 1 | import "core-js/modules/web.dom-collections.iterator.js"; 2 | import { Alert, Box, Button, Container, Grid, Paper, TextField, Typography } from "@mui/material"; 3 | import React, { useContext, useState } from "react"; 4 | import { useNavigate } from "react-router-dom"; 5 | import { SetPageTitle } from "../SetPageTitle"; 6 | import { getAuth, updateEmail } from "firebase/auth"; 7 | import { FireactContext } from "../Fireact"; 8 | export const UserUpdateEmail = () => { 9 | const [email, setEmail] = useState(""); 10 | const [error, setError] = useState(null); 11 | const [success, setSuccess] = useState(false); 12 | const [processing, setProcessing] = useState(false); 13 | const title = "Change Email"; 14 | const navigate = useNavigate(); 15 | const auth = getAuth(); 16 | const { 17 | config 18 | } = useContext(FireactContext); 19 | const pathnames = config.pathnames; 20 | return /*#__PURE__*/React.createElement(Container, { 21 | maxWidth: "md" 22 | }, /*#__PURE__*/React.createElement(SetPageTitle, { 23 | title: title 24 | }), /*#__PURE__*/React.createElement(Paper, null, /*#__PURE__*/React.createElement(Box, { 25 | p: 2 26 | }, /*#__PURE__*/React.createElement(Typography, { 27 | component: "h1", 28 | variant: "h4", 29 | align: "center" 30 | }, title)), error !== null && /*#__PURE__*/React.createElement(Box, { 31 | p: 2 32 | }, /*#__PURE__*/React.createElement(Alert, { 33 | severity: "error" 34 | }, error)), success && /*#__PURE__*/React.createElement(Box, { 35 | p: 2 36 | }, /*#__PURE__*/React.createElement(Alert, { 37 | severity: "success" 38 | }, "Your email address has been updated successfully.")), /*#__PURE__*/React.createElement(Box, { 39 | p: 2 40 | }, /*#__PURE__*/React.createElement(TextField, { 41 | required: true, 42 | fullWidth: true, 43 | name: "email", 44 | label: "New Email Address", 45 | type: "email", 46 | autoComplete: "email", 47 | margin: "normal", 48 | onChange: e => setEmail(e.target.value) 49 | })), /*#__PURE__*/React.createElement(Box, { 50 | p: 2 51 | }, /*#__PURE__*/React.createElement(Grid, { 52 | container: true 53 | }, /*#__PURE__*/React.createElement(Grid, { 54 | item: true, 55 | xs: true 56 | }, /*#__PURE__*/React.createElement(Button, { 57 | type: "button", 58 | color: "secondary", 59 | variant: "outlined", 60 | disabled: processing, 61 | onClick: () => { 62 | navigate(pathnames.UserProfile); 63 | } 64 | }, "Back")), /*#__PURE__*/React.createElement(Grid, { 65 | item: true 66 | }, /*#__PURE__*/React.createElement(Button, { 67 | type: "button", 68 | variant: "contained", 69 | disabled: processing, 70 | onClick: () => { 71 | setProcessing(true); 72 | setSuccess(false); 73 | setError(null); 74 | updateEmail(auth.currentUser, email).then(() => { 75 | setSuccess(true); 76 | setProcessing(false); 77 | }).catch(error => { 78 | switch (error.code) { 79 | case "auth/requires-recent-login": 80 | setError("This operation is sensitive and requires recent authentication. Log in again before retrying this request."); 81 | break; 82 | case "auth/email-already-in-use": 83 | setError("The email address is already in use by another account."); 84 | break; 85 | default: 86 | setError(error.message); 87 | break; 88 | } 89 | setProcessing(false); 90 | }); 91 | } 92 | }, "Save")))))); 93 | }; -------------------------------------------------------------------------------- /dist/esm/components/user/UserUpdateName.js: -------------------------------------------------------------------------------- 1 | import "core-js/modules/web.dom-collections.iterator.js"; 2 | import "core-js/modules/es.string.trim.js"; 3 | import { Alert, Box, Button, Container, Grid, Paper, TextField, Typography } from "@mui/material"; 4 | import React, { useContext, useState } from "react"; 5 | import { useNavigate } from "react-router-dom"; 6 | import { SetPageTitle } from "../SetPageTitle"; 7 | import { getAuth, updateProfile } from "firebase/auth"; 8 | import { FireactContext } from "../Fireact"; 9 | export const UserUpdateName = () => { 10 | const [fullname, setFullname] = useState(""); 11 | const [error, setError] = useState(null); 12 | const [success, setSuccess] = useState(false); 13 | const [processing, setProcessing] = useState(false); 14 | const title = "Change Name"; 15 | const navigate = useNavigate(); 16 | const auth = getAuth(); 17 | const { 18 | config 19 | } = useContext(FireactContext); 20 | const pathnames = config.pathnames; 21 | return /*#__PURE__*/React.createElement(Container, { 22 | maxWidth: "md" 23 | }, /*#__PURE__*/React.createElement(SetPageTitle, { 24 | title: title 25 | }), /*#__PURE__*/React.createElement(Paper, null, /*#__PURE__*/React.createElement(Box, { 26 | p: 2 27 | }, /*#__PURE__*/React.createElement(Typography, { 28 | component: "h1", 29 | variant: "h4", 30 | align: "center" 31 | }, title)), error !== null && /*#__PURE__*/React.createElement(Box, { 32 | p: 2 33 | }, /*#__PURE__*/React.createElement(Alert, { 34 | severity: "error" 35 | }, error)), success && /*#__PURE__*/React.createElement(Box, { 36 | p: 2 37 | }, /*#__PURE__*/React.createElement(Alert, { 38 | severity: "success" 39 | }, "Your name has been updated successfully.")), /*#__PURE__*/React.createElement(Box, { 40 | p: 2 41 | }, /*#__PURE__*/React.createElement(TextField, { 42 | required: true, 43 | fullWidth: true, 44 | name: "fullname", 45 | label: "Full Name", 46 | autoComplete: "name", 47 | type: "text", 48 | margin: "normal", 49 | onChange: e => setFullname(e.target.value) 50 | })), /*#__PURE__*/React.createElement(Box, { 51 | p: 2 52 | }, /*#__PURE__*/React.createElement(Grid, { 53 | container: true 54 | }, /*#__PURE__*/React.createElement(Grid, { 55 | item: true, 56 | xs: true 57 | }, /*#__PURE__*/React.createElement(Button, { 58 | type: "button", 59 | color: "secondary", 60 | variant: "outlined", 61 | disabled: processing, 62 | onClick: () => { 63 | navigate(pathnames.UserProfile); 64 | } 65 | }, "Back")), /*#__PURE__*/React.createElement(Grid, { 66 | item: true 67 | }, /*#__PURE__*/React.createElement(Button, { 68 | type: "button", 69 | variant: "contained", 70 | disabled: processing, 71 | onClick: () => { 72 | setProcessing(true); 73 | setSuccess(false); 74 | setError(null); 75 | if (fullname.trim() === "") { 76 | setError('Your full name is required.'); 77 | setProcessing(false); 78 | } else { 79 | updateProfile(auth.currentUser, { 80 | displayName: fullname 81 | }).then(() => { 82 | setSuccess(true); 83 | setProcessing(false); 84 | }).catch(error => { 85 | switch (error.code) { 86 | case "auth/requires-recent-login": 87 | setError("This operation is sensitive and requires recent authentication. Log in again before retrying this request."); 88 | break; 89 | default: 90 | setError(error.message); 91 | break; 92 | } 93 | setProcessing(false); 94 | }); 95 | } 96 | } 97 | }, "Save")))))); 98 | }; -------------------------------------------------------------------------------- /dist/esm/components/user/UserUpdatePassword.js: -------------------------------------------------------------------------------- 1 | import "core-js/modules/web.dom-collections.iterator.js"; 2 | import "core-js/modules/es.regexp.exec.js"; 3 | import "core-js/modules/es.regexp.test.js"; 4 | import { Alert, Box, Button, Container, Grid, Paper, TextField, Typography } from "@mui/material"; 5 | import React, { useContext, useState } from "react"; 6 | import { useNavigate } from "react-router-dom"; 7 | import { SetPageTitle } from "../SetPageTitle"; 8 | import { getAuth, updatePassword } from "firebase/auth"; 9 | import { FireactContext } from "../Fireact"; 10 | export const UserUpdatePassword = () => { 11 | const [password, setPassword] = useState(""); 12 | const [error, setError] = useState(null); 13 | const [success, setSuccess] = useState(false); 14 | const [processing, setProcessing] = useState(false); 15 | const title = "Change Password"; 16 | const navigate = useNavigate(); 17 | const auth = getAuth(); 18 | const { 19 | config 20 | } = useContext(FireactContext); 21 | const pathnames = config.pathnames; 22 | return /*#__PURE__*/React.createElement(Container, { 23 | maxWidth: "md" 24 | }, /*#__PURE__*/React.createElement(SetPageTitle, { 25 | title: title 26 | }), /*#__PURE__*/React.createElement(Paper, null, /*#__PURE__*/React.createElement(Box, { 27 | p: 2 28 | }, /*#__PURE__*/React.createElement(Typography, { 29 | component: "h1", 30 | variant: "h4", 31 | align: "center" 32 | }, title)), error !== null && /*#__PURE__*/React.createElement(Box, { 33 | p: 2 34 | }, /*#__PURE__*/React.createElement(Alert, { 35 | severity: "error" 36 | }, error)), success && /*#__PURE__*/React.createElement(Box, { 37 | p: 2 38 | }, /*#__PURE__*/React.createElement(Alert, { 39 | severity: "success" 40 | }, "Your password has been updated successfully.")), /*#__PURE__*/React.createElement(Box, { 41 | p: 2 42 | }, /*#__PURE__*/React.createElement(TextField, { 43 | required: true, 44 | fullWidth: true, 45 | name: "password", 46 | label: "New Password", 47 | autoComplete: "new-password", 48 | type: "password", 49 | margin: "normal", 50 | onChange: e => setPassword(e.target.value) 51 | })), /*#__PURE__*/React.createElement(Box, { 52 | p: 2 53 | }, /*#__PURE__*/React.createElement(Grid, { 54 | container: true 55 | }, /*#__PURE__*/React.createElement(Grid, { 56 | item: true, 57 | xs: true 58 | }, /*#__PURE__*/React.createElement(Button, { 59 | type: "button", 60 | color: "secondary", 61 | variant: "outlined", 62 | disabled: processing, 63 | onClick: () => { 64 | navigate(pathnames.UserProfile); 65 | } 66 | }, "Back")), /*#__PURE__*/React.createElement(Grid, { 67 | item: true 68 | }, /*#__PURE__*/React.createElement(Button, { 69 | type: "button", 70 | variant: "contained", 71 | disabled: processing, 72 | onClick: () => { 73 | const passwordNumericRegex = /\d+/; 74 | const passwordUppercaseRegex = /[A-Z]+/; 75 | const passwordLowercaseRegex = /[a-z]+/; 76 | const passwordSpecialRegex = /[ !"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]+/; 77 | setProcessing(true); 78 | setSuccess(false); 79 | setError(null); 80 | if (!passwordNumericRegex.test(String(password)) || !passwordUppercaseRegex.test(String(password)) || !passwordLowercaseRegex.test(String(password)) || !passwordSpecialRegex.test(String(password)) || password.length < 8) { 81 | setError('The password must contain at least 8 characters with letters (both uppercase and lowercase), numbers, and symbols.'); 82 | setProcessing(false); 83 | } else { 84 | updatePassword(auth.currentUser, password).then(() => { 85 | setSuccess(true); 86 | setProcessing(false); 87 | }).catch(error => { 88 | switch (error.code) { 89 | case "auth/requires-recent-login": 90 | setError("This operation is sensitive and requires recent authentication. Log in again before retrying this request."); 91 | break; 92 | default: 93 | setError(error.message); 94 | break; 95 | } 96 | setProcessing(false); 97 | }); 98 | } 99 | } 100 | }, "Save")))))); 101 | }; -------------------------------------------------------------------------------- /dist/esm/index.js: -------------------------------------------------------------------------------- 1 | import { AuthContext, AuthProvider, AuthRoutes } from "./components/Auth"; 2 | import { UserDelete } from "./components/user/UserDelete"; 3 | import { PublicTemplate } from "./components/templates/PublicTemplate"; 4 | import { SignIn } from "./components/auth/SignIn"; 5 | import { SignUp } from "./components/auth/SignUp"; 6 | import { ResetPassword } from "./components/auth/ResetPassword"; 7 | import { AppTemplate } from "./components/templates/AppTemplate"; 8 | import { UserMenu } from "./components/menus/UserMenu"; 9 | import { MainMenu } from "./components/menus/MainMenu"; 10 | import { UserProfile } from "./components/user/UserProfile"; 11 | import { UserUpdateEmail } from "./components/user/UserUpdateEmail"; 12 | import { UserUpdateName } from "./components/user/UserUpdateName"; 13 | import { UserUpdatePassword } from "./components/user/UserUpdatePassword"; 14 | import { SetPageTitle } from "./components/SetPageTitle"; 15 | import { FireactContext, FireactProvider } from "./components/Fireact"; 16 | import { ActionPages } from './components/auth/ActionPages'; 17 | import pathnames from "./pathnames.json"; 18 | export { pathnames, ActionPages, AppTemplate, AuthContext, AuthProvider, AuthRoutes, FireactContext, FireactProvider, MainMenu, PublicTemplate, ResetPassword, SetPageTitle, SignIn, SignUp, UserDelete, UserMenu, UserProfile, UserUpdateEmail, UserUpdateName, UserUpdatePassword }; -------------------------------------------------------------------------------- /dist/esm/pathnames.json: -------------------------------------------------------------------------------- 1 | { 2 | "ActionPages": "/auth-action", 3 | "ResetPassword": "/reset-password", 4 | "SignIn": "/sign-in", 5 | "SignUp": "/sign-up", 6 | "UserDelete": "/user/delete", 7 | "UserProfile": "/user", 8 | "UserUpdateEmail": "/user/update-email", 9 | "UserUpdateName": "/user/update-name", 10 | "UserUpdatePassword": "/user/update-password" 11 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fireactjs/core", 3 | "version": "1.1.2", 4 | "private": false, 5 | "author": "Chaoming Li", 6 | "keywords": [ 7 | "react", 8 | "components", 9 | "ui", 10 | "firebase", 11 | "authentication", 12 | "auth" 13 | ], 14 | "main": "dist/cjs/index.js", 15 | "module": "dist/esm/index.js", 16 | "files": [ 17 | "dist", 18 | "README.md", 19 | "LICENSE" 20 | ], 21 | "license": "ISC", 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/fireactjs/core.git" 25 | }, 26 | "dependencies": { 27 | "@babel/polyfill": "^7.12.1" 28 | }, 29 | "peerDependencies": { 30 | "@emotion/react": "^11.11.3", 31 | "@emotion/styled": "^11.11.0", 32 | "@mui/icons-material": "^5.15.3", 33 | "@mui/material": "^5.15.3", 34 | "firebase": "^10.7.1", 35 | "react": "^18.2.0", 36 | "react-dom": "^18.2.0", 37 | "react-router-dom": "^6.21.1", 38 | "react-scripts": "5.0.1", 39 | "web-vitals": "^2.1.4" 40 | }, 41 | "scripts": { 42 | "start": "PUBLIC_URL=/ react-scripts start", 43 | "build": "rm -rf dist && NODE_ENV=production babel src/lib --out-dir dist/esm --copy-files --config-file ./babel.config.esm.json && NODE_ENV=production babel src/lib --out-dir dist/cjs --copy-files --config-file ./babel.config.cjs.json", 44 | "test": "react-scripts test", 45 | "eject": "react-scripts eject" 46 | }, 47 | "eslintConfig": { 48 | "extends": [ 49 | "react-app", 50 | "react-app/jest" 51 | ] 52 | }, 53 | "browserslist": { 54 | "production": [ 55 | ">0.2%", 56 | "not dead", 57 | "not op_mini all" 58 | ], 59 | "development": [ 60 | "last 1 chrome version", 61 | "last 1 firefox version", 62 | "last 1 safari version" 63 | ] 64 | }, 65 | "devDependencies": { 66 | "@babel/cli": "^7.23.4", 67 | "@babel/core": "^7.23.7", 68 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", 69 | "@babel/preset-env": "^7.23.7", 70 | "@testing-library/jest-dom": "^5.17.0", 71 | "@testing-library/react": "^13.4.0", 72 | "@testing-library/user-event": "^13.5.0", 73 | "core-js": "^3.35.0", 74 | "webpack": "^5.89.0", 75 | "webpack-cli": "^5.1.4" 76 | }, 77 | "description": "fireactjs-core is the core package for building web applications with Firebase and Reactjs in a simple and fast approach. Its key features include:", 78 | "bugs": { 79 | "url": "https://github.com/fireactjs/core/issues" 80 | }, 81 | "homepage": "https://github.com/fireactjs/core#readme" 82 | } 83 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireactjs/core/b1dc41bd474f75a425f786ec51837b2fdb901ed8/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireactjs/core/b1dc41bd474f75a425f786ec51837b2fdb901ed8/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireactjs/core/b1dc41bd474f75a425f786ec51837b2fdb901ed8/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Fireact", 3 | "name": "Fireact Web Application", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import firebaseConfig from "./firebaseConfig.json"; 3 | import { pathnames, ActionPages, AppTemplate, AuthProvider, AuthRoutes, FireactProvider, MainMenu, PublicTemplate, ResetPassword, SignIn, SignUp, UserMenu, UserProfile, UserUpdateEmail, UserUpdateName, UserUpdatePassword, UserDelete } from './lib'; 4 | import { BrowserRouter, Routes } from "react-router-dom"; 5 | import { Route } from "react-router-dom"; 6 | import LocalFireDepartmentIcon from '@mui/icons-material/LocalFireDepartment'; 7 | import { CircularProgress, Box } from '@mui/material'; 8 | import authMethods from "./authMethods.json"; 9 | 10 | const Logo = ({size, color}) => { 11 | const logoColor = color || 'warning'; 12 | return ( 13 | 14 | ); 15 | } 16 | 17 | const Loader = ({size}) => { 18 | let cpSize = "35px"; 19 | switch(size){ 20 | case "small": 21 | cpSize = "30px"; 22 | break; 23 | case "medium": 24 | cpSize = "35px"; 25 | break; 26 | case "large": 27 | cpSize = "45px"; 28 | break; 29 | default: 30 | cpSize = "35px"; 31 | break; 32 | } 33 | return ( 34 | 35 | 36 |
37 | 38 |
39 |
40 | ); 41 | } 42 | 43 | function App() { 44 | 45 | const config = { 46 | firebaseConfig: firebaseConfig, 47 | brand: "FIREACTJS", 48 | pathnames: pathnames, 49 | authProviders: authMethods 50 | } 51 | 52 | return ( 53 | 54 | 55 | 56 | 57 | } />} > 58 | } toolBarMenu={} drawerMenu={} />}> 59 | } /> 60 | } /> 61 | } /> 62 | } /> 63 | } /> 64 | } /> 65 | 66 | 67 | }> 68 | } 71 | /> 72 | } /> 73 | } 76 | /> 77 | } /> 78 | } 81 | /> 82 | } /> 83 | } 86 | /> 87 | } /> 88 | 89 | 90 | 91 | 92 | 93 | ) 94 | } 95 | 96 | export default App; 97 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/authMethods.json: -------------------------------------------------------------------------------- 1 | { 2 | "google": true, 3 | "facebook": true, 4 | "microsoft": true, 5 | "apple": true, 6 | "twitter": true, 7 | "github": true 8 | } -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /src/lib/assets/images/google.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | google_buttn 6 | Created with Sketch. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/lib/assets/images/microsoft.svg: -------------------------------------------------------------------------------- 1 | MS-SymbolLockup 2 | -------------------------------------------------------------------------------- /src/lib/components/Auth.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useContext } from "react"; 2 | import { initializeApp } from 'firebase/app'; 3 | import { getAuth, onAuthStateChanged } from 'firebase/auth'; 4 | import { Navigate, Outlet } from "react-router-dom"; 5 | import { Box, Container } from "@mui/material"; 6 | import { doc, getFirestore, setDoc } from "firebase/firestore"; 7 | import { FireactContext } from "./Fireact"; 8 | import { getFunctions } from "firebase/functions"; 9 | 10 | export const AuthContext = React.createContext(); 11 | 12 | export const AuthProvider = ({children}) => { 13 | 14 | // authorized user state 15 | const [authUser, setAuthUser] = useState( 16 | { 17 | user: null, 18 | data: {}, 19 | checked: false 20 | } 21 | ); 22 | 23 | const { config } = useContext(FireactContext); 24 | const [firebaseApp, setFirebaseApp] = useState(null); 25 | const [authInstance, setAuthInstance] = useState(null); 26 | const [firestoreInstance, setFirestoreInstance] = useState(null); 27 | const [functionsInstance, setFunctionsInstance] = useState(null); 28 | 29 | useEffect(() => { 30 | const app = initializeApp(config.firebaseConfig); 31 | const auth = getAuth(app); 32 | const firestore = getFirestore(app); 33 | const functions = getFunctions(app); 34 | 35 | setFirebaseApp(app); 36 | setAuthInstance(auth); 37 | setFirestoreInstance(firestore); 38 | setFunctionsInstance(functions); 39 | 40 | onAuthStateChanged(auth, (user) => { 41 | if(user !== null){ 42 | user.getIdToken().then(token => { 43 | const userDoc = doc(firestore, 'users', user.uid); 44 | setAuthUser(prevState => ({ 45 | ...prevState, 46 | user: user, 47 | checked: true 48 | })); 49 | setDoc(userDoc, { 50 | displayName: user.displayName, 51 | photoURL: user.photoURL, 52 | email: user.email 53 | },{merge: true}); 54 | }); 55 | }else{ 56 | setAuthUser(prevState => ({ 57 | ...prevState, 58 | user: null, 59 | checked: true 60 | })); 61 | } 62 | }); 63 | },[config.firebaseConfig]); 64 | 65 | return ( 66 | 69 | {children} 70 | 71 | ) 72 | } 73 | 74 | export const AuthRoutes = ({ loader }) => { 75 | const { authUser } = useContext(AuthContext); 76 | const { config } = useContext(FireactContext); 77 | const signInPath = config.pathnames.SignIn; 78 | 79 | if(authUser.checked){ 80 | if(authUser.user !== null){ 81 | return 82 | }else{ 83 | return 84 | } 85 | }else{ 86 | return ( 87 | 88 | 89 | 90 | {loader} 91 | 92 | 93 | 94 | ) 95 | } 96 | } -------------------------------------------------------------------------------- /src/lib/components/Fireact.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const FireactContext = React.createContext(); 4 | 5 | export const FireactProvider = ({config, children}) => { 6 | return ( 7 | 8 | {children} 9 | 10 | ) 11 | } -------------------------------------------------------------------------------- /src/lib/components/SetPageTitle.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect } from "react" 2 | import { AuthContext } from "./Auth" 3 | 4 | export const SetPageTitle = ({title}) => { 5 | const { brand } = useContext(AuthContext); 6 | useEffect(() => { 7 | document.title = brand?(`${title} - ${brand}`):(title); 8 | }, [title, brand]); 9 | return (<>); 10 | } -------------------------------------------------------------------------------- /src/lib/components/auth/ActionPages.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect, useState } from "react"; 2 | import {Alert, Box, Stack, Typography, TextField, Button } from "@mui/material"; 3 | import { SetPageTitle } from "../SetPageTitle"; 4 | import { getAuth, applyActionCode, verifyPasswordResetCode, confirmPasswordReset } from "firebase/auth"; 5 | import { NavLink } from "react-router-dom"; 6 | import { FireactContext } from "../Fireact"; 7 | 8 | export const ActionPages = ({logo}) => { 9 | let title = "Unknown Action"; 10 | const params = (new URL(document.location)).searchParams; 11 | 12 | const mode = params.get('mode'); 13 | const actionCode = params.get('oobCode'); 14 | // const apiKey = params.get('apiKey'); 15 | // const continueUrl = params.get('continueUrl'); 16 | // const lang = params.get('lang') || 'en'; 17 | 18 | switch(mode){ 19 | case 'resetPassword': 20 | title = "Reset Password"; 21 | break; 22 | case 'recoverEmail': 23 | title = 'Recover Email'; 24 | break; 25 | case 'verifyEmail': 26 | title = 'Verfiy Email'; 27 | break; 28 | default: 29 | break; 30 | } 31 | 32 | return ( 33 | <> 34 | 35 | 36 | {logo} 37 | {title} 38 | 39 | 40 | 41 | {(mode === 'verifyEmail' || mode === 'recoverEmail') && } 42 | {mode === 'resetPassword' && 43 | 44 | } 45 | 46 | 47 | 48 | ) 49 | } 50 | 51 | const ResetPassword = ({actionCode}) => { 52 | const [processing, setProcessing] = useState(false); 53 | const [error, setError] = useState(null); 54 | const [stage, setStage] = useState('verifying'); 55 | 56 | const { config } = useContext(FireactContext); 57 | 58 | const [newPassword, setNewPassword] = useState(''); 59 | const [confirmPassword, setConfirmPassword] = useState(''); 60 | 61 | useEffect(() => { 62 | const auth = getAuth(); 63 | verifyPasswordResetCode(auth, actionCode).then(() => { 64 | setStage('form'); 65 | }).catch(error => { 66 | setStage(''); 67 | setError(error.message); 68 | }) 69 | },[actionCode]); 70 | 71 | return ( 72 | <> 73 | {error && {error}} 74 | {stage === 'verifying' && 75 | Please wait while verifying your request... 76 | } 77 | {stage === 'form' && <> 78 | setNewPassword(e.target.value)} /> 79 | setConfirmPassword(e.target.value)} /> 80 | 106 | } 107 | {stage === 'success' && 108 | Your email is verified. Please sign in with your new password. 109 | } 110 | 111 | ) 112 | 113 | } 114 | 115 | const HandleAction = ({mode, actionCode}) => { 116 | const [processing, setProcessing] = useState(false); 117 | const [error, setError] = useState(null); 118 | const [success, setSuccess] = useState(false); 119 | 120 | const { config } = useContext(FireactContext); 121 | 122 | let processingMessage = 'Please wait'; 123 | let successMessage = 'Done'; 124 | 125 | switch(mode){ 126 | case 'verifyEmail': 127 | processingMessage = 'Please wait while verifying your email...'; 128 | successMessage = <>Your email is verified. Please sign in again. 129 | break; 130 | case 'recoverEmail': 131 | processingMessage = 'Please wait while resotring your email...'; 132 | successMessage = <>Your email is restored. Please reset password to ensure your account is secured. 133 | break; 134 | default: 135 | break; 136 | } 137 | 138 | useEffect(() => { 139 | setProcessing(true); 140 | setError(null); 141 | setSuccess(false); 142 | const auth = getAuth(); 143 | applyActionCode(auth, actionCode).then(() => { 144 | setSuccess(true); 145 | setProcessing(false); 146 | }).catch(error => { 147 | setError(error.message); 148 | setProcessing(false); 149 | }) 150 | 151 | },[actionCode]); 152 | 153 | return ( 154 | <> 155 | {processing?( 156 | {processingMessage} 157 | ):( 158 | success?( 159 | {successMessage} 160 | ):( 161 | error?( 162 | {error} 163 | ):( 164 | Something went wrong 165 | ) 166 | ) 167 | )} 168 | 169 | ) 170 | } -------------------------------------------------------------------------------- /src/lib/components/auth/ResetPassword.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState } from "react"; 2 | import { Alert, Button, Box, Stack, TextField, Typography, Grid, Link } from "@mui/material"; 3 | import { Link as RouterLink } from "react-router-dom"; 4 | import { getAuth, sendPasswordResetEmail } from "firebase/auth"; 5 | import { SetPageTitle } from "../SetPageTitle"; 6 | import { FireactContext } from "../Fireact"; 7 | 8 | export const ResetPassword = ({logo}) => { 9 | const { config } = useContext(FireactContext); 10 | const pathnames = config.pathnames; 11 | 12 | const signInUrl = pathnames.SignIn; 13 | const title = "Reset Password"; 14 | 15 | const [error, setError] = useState(null); 16 | const [success, setSuccess] = useState(false); 17 | const [processing, setProcessing] = useState(false); 18 | const [email, setEmail] = useState(""); 19 | 20 | return ( 21 | <> 22 | 23 | 24 | {logo} 25 | {title} 26 | 27 | 28 | 29 | {error !== null && 30 | {error} 31 | } 32 | {success && 33 | A password reset email has been sent to the email address. 34 | } 35 | setEmail(e.target.value)} /> 36 | 65 | {(signInUrl && 66 | 67 | {signInUrl && 68 | 69 | Sign in with an existing account 70 | 71 | } 72 | 73 | )} 74 | 75 | 76 | 77 | ); 78 | } -------------------------------------------------------------------------------- /src/lib/components/auth/SignUp.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState } from "react"; 2 | import { Alert, Button, Box, Stack, TextField, Typography, Grid, Link } from "@mui/material"; 3 | import { getAuth, createUserWithEmailAndPassword, updateProfile } from "firebase/auth"; 4 | import { Link as RouterLink } from "react-router-dom"; 5 | import { SetPageTitle } from "../SetPageTitle"; 6 | import { FireactContext } from "../Fireact"; 7 | 8 | export const SignUp = ({logo, successUrl}) => { 9 | const { config } = useContext(FireactContext); 10 | const pathnames = config.pathnames; 11 | 12 | const title = "Sign Up"; 13 | const signInUrl = pathnames.SignIn; 14 | const resetPasswordUrl = pathnames.ResetPassword; 15 | 16 | const re = successUrl || "/"; // redirect successUrl or homepage after sign in 17 | 18 | const [error, setError] = useState(null); 19 | const [processing, setProcessing] = useState(false); 20 | const [email, setEmail] = useState(""); 21 | const [fullname, setFullname] = useState(""); 22 | const [password, setPassword] = useState(""); 23 | 24 | return ( 25 | <> 26 | 27 | 28 | {logo} 29 | {title} 30 | 31 | 32 | 33 | {error !== null && 34 | {error} 35 | } 36 | setEmail(e.target.value)} /> 37 | setFullname(e.target.value)} /> 38 | setPassword(e.target.value)} /> 39 | 80 | {(signInUrl || resetPasswordUrl) && 81 | 82 | {signInUrl && 83 | 84 | Sign in with an existing account 85 | 86 | } 87 | {resetPasswordUrl && 88 | 89 | Reset password 90 | 91 | } 92 | 93 | } 94 | 95 | 96 | 97 | ); 98 | } -------------------------------------------------------------------------------- /src/lib/components/menus/MainMenu.js: -------------------------------------------------------------------------------- 1 | import { Divider, List, ListItemButton, ListItemIcon, ListItemText, Typography } from "@mui/material"; 2 | import React, { useContext } from "react"; 3 | import { NavLink } from "react-router-dom"; 4 | import HomeIcon from '@mui/icons-material/Home'; 5 | import AccountBoxIcon from '@mui/icons-material/AccountBox'; 6 | import { FireactContext } from "../Fireact"; 7 | 8 | export const MainMenu = ({customItems}) => { 9 | const { config } = useContext(FireactContext); 10 | const pathnames = config.pathnames; 11 | const profileUrl = pathnames.UserProfile; 12 | return ( 13 | 14 | 15 | 16 | 17 | Home} /> 18 | 19 | 20 | {customItems} 21 | {profileUrl && [ 22 | , 23 | 24 | 25 | 26 | My Profile} /> 27 | 28 | 29 | ]} 30 | 31 | ) 32 | } -------------------------------------------------------------------------------- /src/lib/components/menus/UserMenu.js: -------------------------------------------------------------------------------- 1 | import { Avatar, Divider, IconButton, Menu, MenuItem } from "@mui/material"; 2 | import React, { useContext, useState } from "react"; 3 | import { AuthContext } from "../Auth"; 4 | import AccountBoxIcon from '@mui/icons-material/AccountBox'; 5 | import ExitToAppIcon from '@mui/icons-material/ExitToApp'; 6 | import { getAuth, signOut } from "firebase/auth"; 7 | import { useNavigate } from "react-router-dom"; 8 | import { FireactContext } from "../Fireact"; 9 | 10 | 11 | export const UserMenu = ({customItems}) => { 12 | 13 | const { config } = useContext(FireactContext); 14 | const pathnames = config.pathnames; 15 | 16 | const profileUrl = pathnames.UserProfile; 17 | const [anchorEl, setAnchorEl] = useState(null); 18 | const open = Boolean(anchorEl); 19 | const handleMenu = (event) => { 20 | setAnchorEl(event.currentTarget); 21 | }; 22 | const handleClose = () => { 23 | setAnchorEl(null); 24 | }; 25 | 26 | const navigate = useNavigate(); 27 | 28 | return ( 29 | 30 | {(context) => ( 31 | <> 32 | 33 | 34 | 35 | 50 | {profileUrl && 51 | [ 52 | { 53 | e.preventDefault(); 54 | handleClose(); 55 | navigate(profileUrl); 56 | }}> 57 | Profile 58 | , 59 | 60 | ] 61 | } 62 | {customItems} 63 | { 64 | e.preventDefault(); 65 | handleClose(); 66 | const auth = getAuth(); 67 | signOut(auth).then(() => { 68 | document.location.href = "/"; 69 | }); 70 | }}> 71 | Sign Out 72 | 73 | 74 | 75 | )} 76 | 77 | ) 78 | } -------------------------------------------------------------------------------- /src/lib/components/templates/AppTemplate.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState } from "react"; 2 | import { styled, useTheme } from '@mui/material/styles'; 3 | import { AppBar as MuiAppBar, Box, CssBaseline, Drawer as MuiDrawer, IconButton, Toolbar, Divider, Typography, Button } from "@mui/material"; 4 | import MenuIcon from '@mui/icons-material/Menu'; 5 | import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; 6 | import ChevronRightIcon from '@mui/icons-material/ChevronRight'; 7 | import { Outlet, useNavigate } from "react-router-dom"; 8 | import { FireactContext } from "../Fireact"; 9 | 10 | const drawerWidth = 240; 11 | 12 | const openedMixin = (theme) => ({ 13 | width: drawerWidth, 14 | transition: theme.transitions.create('width', { 15 | easing: theme.transitions.easing.sharp, 16 | duration: theme.transitions.duration.enteringScreen, 17 | }), 18 | overflowX: 'hidden', 19 | }); 20 | 21 | const closedMixin = (theme) => ({ 22 | transition: theme.transitions.create('width', { 23 | easing: theme.transitions.easing.sharp, 24 | duration: theme.transitions.duration.leavingScreen, 25 | }), 26 | overflowX: 'hidden', 27 | width: `calc(${theme.spacing(7)} + 1px)`, 28 | [theme.breakpoints.up('sm')]: { 29 | width: `calc(${theme.spacing(9)} + 1px)`, 30 | }, 31 | }); 32 | 33 | const DrawerHeader = styled('div')(({ theme }) => ({ 34 | display: 'flex', 35 | alignItems: 'center', 36 | justifyContent: 'flex-end', 37 | padding: theme.spacing(0, 1), 38 | // necessary for content to be below app bar 39 | ...theme.mixins.toolbar, 40 | })); 41 | 42 | const AppBar = styled(MuiAppBar, { 43 | shouldForwardProp: (prop) => prop !== 'open', 44 | })(({ theme, open }) => ({ 45 | zIndex: theme.zIndex.drawer + 1, 46 | transition: theme.transitions.create(['width', 'margin'], { 47 | easing: theme.transitions.easing.sharp, 48 | duration: theme.transitions.duration.leavingScreen, 49 | }), 50 | ...(open && { 51 | marginLeft: drawerWidth, 52 | width: `calc(100% - ${drawerWidth}px)`, 53 | transition: theme.transitions.create(['width', 'margin'], { 54 | easing: theme.transitions.easing.sharp, 55 | duration: theme.transitions.duration.enteringScreen, 56 | }), 57 | }), 58 | })); 59 | 60 | const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })( 61 | ({ theme, open }) => ({ 62 | width: drawerWidth, 63 | flexShrink: 0, 64 | whiteSpace: 'nowrap', 65 | boxSizing: 'border-box', 66 | ...(open && { 67 | ...openedMixin(theme), 68 | '& .MuiDrawer-paper': openedMixin(theme), 69 | }), 70 | ...(!open && { 71 | ...closedMixin(theme), 72 | '& .MuiDrawer-paper': closedMixin(theme), 73 | }), 74 | }), 75 | ); 76 | 77 | export const AppTemplate = ({logo, drawerMenu, toolbarChildren, toolBarMenu}) => { 78 | const theme = useTheme(); 79 | const [open, setOpen] = useState(true); 80 | 81 | const handleDrawerOpen = () => { 82 | setOpen(true); 83 | }; 84 | 85 | const handleDrawerClose = () => { 86 | setOpen(false); 87 | }; 88 | 89 | const {config} = useContext(FireactContext); 90 | const brand = config.brand; 91 | const navigate = useNavigate(); 92 | 93 | return ( 94 | 95 | 96 | 97 | 98 | 107 | 108 | 109 | {toolbarChildren} 110 |
{toolBarMenu}
114 |
115 |
116 | 117 | 118 | {open &&
119 | 125 |
} 126 | 127 | {theme.direction === 'rtl' ? : } 128 | 129 |
130 | 131 | {drawerMenu} 132 | 133 |
134 | 137 | theme.palette.mode === 'light' 138 | ? theme.palette.grey[100] 139 | : theme.palette.grey[900], 140 | height: '100vh', 141 | overflow: 'auto' 142 | }}> 143 | 144 |
145 | 146 | 147 | 148 |
149 |
150 |
151 | ) 152 | } -------------------------------------------------------------------------------- /src/lib/components/templates/PublicTemplate.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Box, Container } from "@mui/material"; 3 | import { Outlet } from "react-router-dom"; 4 | 5 | export const PublicTemplate = () => { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ) 15 | } -------------------------------------------------------------------------------- /src/lib/components/user/UserDelete.js: -------------------------------------------------------------------------------- 1 | import { Alert, Box, Button, Container, Grid, Paper, TextField, Typography } from "@mui/material"; 2 | import React, { useContext, useState } from "react"; 3 | import { useNavigate } from "react-router-dom"; 4 | import { SetPageTitle } from "../SetPageTitle"; 5 | import { deleteUser, getAuth } from "firebase/auth"; 6 | import { FireactContext } from "../Fireact"; 7 | 8 | export const UserDelete = () => { 9 | const [email, setEmail] = useState(""); 10 | const [error, setError] = useState(null); 11 | const [processing, setProcessing] = useState(false); 12 | const title = "Delete Account"; 13 | const navigate = useNavigate(); 14 | 15 | const auth = getAuth(); 16 | 17 | const { config } = useContext(FireactContext); 18 | const pathnames = config.pathnames; 19 | 20 | return ( 21 | 22 | 23 | 24 | 25 | {title} 26 | 27 | {error !== null && 28 | 29 | {error} 30 | 31 | } 32 | 33 | Please confirm your email address to delete your user account. 34 | setEmail(e.target.value)} /> 35 | 36 | 37 | 38 | 39 | 42 | 43 | 44 | 67 | 68 | 69 | 70 | 71 | 72 | ) 73 | } -------------------------------------------------------------------------------- /src/lib/components/user/UserProfile.js: -------------------------------------------------------------------------------- 1 | import { Alert, Grid, List, ListItem, Box, Avatar, Typography, Divider, Paper, Container } from "@mui/material"; 2 | import React, { useContext, useState } from "react"; 3 | import { AuthContext } from "../Auth"; 4 | import EditIcon from '@mui/icons-material/Edit'; 5 | import SendIcon from '@mui/icons-material/Send'; 6 | import VerifiedUserIcon from '@mui/icons-material/VerifiedUser'; 7 | import { useNavigate } from "react-router-dom"; 8 | import { SetPageTitle } from "../SetPageTitle"; 9 | import { getAuth, sendEmailVerification } from "firebase/auth"; 10 | import { FireactContext } from "../Fireact"; 11 | 12 | export const UserProfile = () => { 13 | const navigate = useNavigate(); 14 | const auth = getAuth(); 15 | const [sendVerification, setSendVerification] = useState({ 16 | 'success': false, 17 | 'error': null 18 | }) 19 | 20 | const { config } = useContext(FireactContext); 21 | const pathnames = config.pathnames; 22 | 23 | return ( 24 | 25 | {context => ( 26 | 27 | 28 | 29 | {sendVerification.error !== null && 30 | 31 | {sendVerification.error} 32 | 33 | } 34 | {sendVerification.success && 35 | 36 | Please check your email inbox to verify the email address. Refresh this page after you verified your email address. 37 | 38 | } 39 | 40 | 41 | 42 | 43 | AVATAR
Update via social login
44 | 45 |
46 | 47 | 48 | 49 |
50 |
51 | 52 | { 53 | navigate(pathnames.UserUpdateName); 54 | }}> 55 | 56 | 57 | NAME
{context.authUser.user.displayName}
58 |
59 | 60 | 61 | 62 |
63 |
64 | 65 | { 66 | navigate(pathnames.UserUpdateEmail); 67 | }}> 68 | 69 | 70 | EMAIL
{context.authUser.user.email}
71 |
72 | 73 | 74 | 75 |
76 |
77 | 78 | { 79 | if(!context.authUser.user.emailVerified){ 80 | setSendVerification({ 81 | 'success': false, 82 | 'error': null 83 | }); 84 | sendEmailVerification(auth.currentUser).then(() => { 85 | setSendVerification({ 86 | 'success': true, 87 | 'error': null 88 | }); 89 | }).catch(error => { 90 | switch(error.code){ 91 | case "auth/too-many-requests": 92 | setSendVerification({ 93 | 'success': false, 94 | 'error': "We have blocked all requests from this device due to unusual activity. Try again later." 95 | }); 96 | break; 97 | default: 98 | setSendVerification({ 99 | 'success': false, 100 | 'error': error.message 101 | }); 102 | break; 103 | } 104 | }); 105 | } 106 | }}> 107 | 108 | 109 | EMAIL VERIFIED
{(context.authUser.user.emailVerified?" Verified":"Unverified email")}
110 |
111 | 112 | {context.authUser.user.emailVerified?():()} 113 | 114 |
115 |
116 | 117 | { 118 | navigate(pathnames.UserUpdatePassword); 119 | }}> 120 | 121 | 122 | PASSWORD
••••••••
123 |
124 | 125 | 126 | 127 |
128 |
129 | 130 | { 131 | navigate(pathnames.UserDelete); 132 | }}> 133 | 134 | 135 | DELETE ACCOUNT 136 | 137 | 138 | 139 |
140 |
141 |
142 | )} 143 |
144 | ) 145 | } -------------------------------------------------------------------------------- /src/lib/components/user/UserUpdateEmail.js: -------------------------------------------------------------------------------- 1 | import { Alert, Box, Button, Container, Grid, Paper, TextField, Typography } from "@mui/material"; 2 | import React, { useContext, useState } from "react"; 3 | import { useNavigate } from "react-router-dom"; 4 | import { SetPageTitle } from "../SetPageTitle"; 5 | import { getAuth, updateEmail } from "firebase/auth"; 6 | import { FireactContext } from "../Fireact"; 7 | 8 | export const UserUpdateEmail = () => { 9 | const [email, setEmail] = useState(""); 10 | const [error, setError] = useState(null); 11 | const [success, setSuccess] = useState(false); 12 | const [processing, setProcessing] = useState(false); 13 | const title = "Change Email"; 14 | const navigate = useNavigate(); 15 | 16 | const auth = getAuth(); 17 | 18 | const { config } = useContext(FireactContext) 19 | const pathnames = config.pathnames; 20 | 21 | return ( 22 | 23 | 24 | 25 | 26 | {title} 27 | 28 | {error !== null && 29 | 30 | {error} 31 | 32 | } 33 | {success && 34 | 35 | Your email address has been updated successfully. 36 | 37 | } 38 | 39 | setEmail(e.target.value)} /> 40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 | 71 | 72 | 73 | 74 | 75 | 76 | ) 77 | } -------------------------------------------------------------------------------- /src/lib/components/user/UserUpdateName.js: -------------------------------------------------------------------------------- 1 | import { Alert, Box, Button, Container, Grid, Paper, TextField, Typography } from "@mui/material"; 2 | import React, { useContext, useState } from "react"; 3 | import { useNavigate } from "react-router-dom"; 4 | import { SetPageTitle } from "../SetPageTitle"; 5 | import { getAuth, updateProfile } from "firebase/auth"; 6 | import { FireactContext } from "../Fireact"; 7 | 8 | export const UserUpdateName = () => { 9 | const [fullname, setFullname] = useState(""); 10 | const [error, setError] = useState(null); 11 | const [success, setSuccess] = useState(false); 12 | const [processing, setProcessing] = useState(false); 13 | const title = "Change Name"; 14 | const navigate = useNavigate(); 15 | 16 | const auth = getAuth(); 17 | 18 | const { config } = useContext(FireactContext); 19 | const pathnames = config.pathnames; 20 | 21 | return ( 22 | 23 | 24 | 25 | 26 | {title} 27 | 28 | {error !== null && 29 | 30 | {error} 31 | 32 | } 33 | {success && 34 | 35 | Your name has been updated successfully. 36 | 37 | } 38 | 39 | setFullname(e.target.value)} /> 40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 | 73 | 74 | 75 | 76 | 77 | 78 | ) 79 | } -------------------------------------------------------------------------------- /src/lib/components/user/UserUpdatePassword.js: -------------------------------------------------------------------------------- 1 | import { Alert, Box, Button, Container, Grid, Paper, TextField, Typography } from "@mui/material"; 2 | import React, { useContext, useState } from "react"; 3 | import { useNavigate } from "react-router-dom"; 4 | import { SetPageTitle } from "../SetPageTitle"; 5 | import { getAuth, updatePassword } from "firebase/auth"; 6 | import { FireactContext } from "../Fireact"; 7 | 8 | export const UserUpdatePassword = () => { 9 | const [password, setPassword] = useState(""); 10 | const [error, setError] = useState(null); 11 | const [success, setSuccess] = useState(false); 12 | const [processing, setProcessing] = useState(false); 13 | const title = "Change Password"; 14 | const navigate = useNavigate(); 15 | 16 | const auth = getAuth(); 17 | 18 | const { config } = useContext(FireactContext); 19 | const pathnames = config.pathnames; 20 | 21 | return ( 22 | 23 | 24 | 25 | 26 | {title} 27 | 28 | {error !== null && 29 | 30 | {error} 31 | 32 | } 33 | {success && 34 | 35 | Your password has been updated successfully. 36 | 37 | } 38 | 39 | setPassword(e.target.value)} /> 40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 | 80 | 81 | 82 | 83 | 84 | 85 | ) 86 | } -------------------------------------------------------------------------------- /src/lib/index.js: -------------------------------------------------------------------------------- 1 | import { AuthContext, AuthProvider, AuthRoutes } from "./components/Auth"; 2 | import { UserDelete } from "./components/user/UserDelete"; 3 | import { PublicTemplate } from "./components/templates/PublicTemplate"; 4 | import { SignIn } from "./components/auth/SignIn"; 5 | import { SignUp } from "./components/auth/SignUp"; 6 | import { ResetPassword } from "./components/auth/ResetPassword"; 7 | import { AppTemplate } from "./components/templates/AppTemplate"; 8 | import { UserMenu } from "./components/menus/UserMenu"; 9 | import { MainMenu } from "./components/menus/MainMenu"; 10 | import { UserProfile } from "./components/user/UserProfile"; 11 | import { UserUpdateEmail } from "./components/user/UserUpdateEmail"; 12 | import { UserUpdateName } from "./components/user/UserUpdateName"; 13 | import { UserUpdatePassword } from "./components/user/UserUpdatePassword"; 14 | import { SetPageTitle } from "./components/SetPageTitle"; 15 | import { FireactContext, FireactProvider } from "./components/Fireact"; 16 | import { ActionPages } from './components/auth/ActionPages'; 17 | 18 | import pathnames from "./pathnames.json"; 19 | 20 | export { 21 | pathnames, 22 | ActionPages, 23 | AppTemplate, 24 | AuthContext, 25 | AuthProvider, 26 | AuthRoutes, 27 | FireactContext, 28 | FireactProvider, 29 | MainMenu, 30 | PublicTemplate, 31 | ResetPassword, 32 | SetPageTitle, 33 | SignIn, 34 | SignUp, 35 | UserDelete, 36 | UserMenu, 37 | UserProfile, 38 | UserUpdateEmail, 39 | UserUpdateName, 40 | UserUpdatePassword 41 | } -------------------------------------------------------------------------------- /src/lib/pathnames.json: -------------------------------------------------------------------------------- 1 | { 2 | "ActionPages": "/auth-action", 3 | "ResetPassword": "/reset-password", 4 | "SignIn": "/sign-in", 5 | "SignUp": "/sign-up", 6 | "UserDelete": "/user/delete", 7 | "UserProfile": "/user", 8 | "UserUpdateEmail": "/user/update-email", 9 | "UserUpdateName": "/user/update-name", 10 | "UserUpdatePassword": "/user/update-password" 11 | } -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | --------------------------------------------------------------------------------