[
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# production\n/build\n\n# misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2021 Vaniya\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://m6v3l9.github.io/react-material-admin/\" rel=\"noopener\" target=\"_blank\"><img width=\"120\" src=\"https://m6v3l9.github.io/react-material-admin/logo.svg\" alt=\"React Material Admin logo\"></a></p>\n</p>\n\n<h1 align=\"center\">React Material Admin</h1>\n<p align=\"center\">\n<b>react-material-admin</b> is a free and open-source admin  application including many real-world examples. It is based on React and Material-UI.\n</p>\n\n[![react-material-admin-demo](https://cdn.dribbble.com/users/6538082/screenshots/15805144/media/5687464c7190019afb748863ac6957d3.png?compress=1&resize=1200x900)](https://m6v3l9.github.io/react-material-admin/)\n\n## Getting Started\n\n```\n# Install dependencies\nyarn install\n\n# Run the app\nyarn start\n```\n\nThis will automatically open [http://localhost:3000](http://localhost:3000).\n\n## Features\n\n```\n- Admin\n  - Home\n  - Dashboard/Charts\n  - FAQ\n  - Help Center\n  - Profile Activity\n  - Profile Information\n  - Profile Password\n- Auth\n  - Forgot Password\n  - Forgot Password Submit\n  - Login\n  - Register\n- Calendar App\n- Core\n  - Forbidden\n  - Not Found\n  - Under Constructions\n- Landing\n- User Management\n```\n\n## Technologies\n\n| Package               | Description                                    | Docs                                                                            |\n| --------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------- |\n| Analytics             | Google Analytics                               | [Docs](https://analytics.google.com/analytics/web/react-ga)                     |\n| Bundle Size Analyzer  | Source map explorer                            | [Docs](https://create-react-app.dev/docs/analyzing-the-bundle-size)             |\n| Charts                | Recharts                                       | [Docs](https://recharts.org/)                                                   |\n| CI                    | Github CI                                      | [Docs]()                                                                        |\n| Code Splitting        | Route-based code splitting (included in React) | [Docs](https://reactjs.org/docs/code-splitting.html#route-based-code-splitting) |\n| Components            | Material-UI                                    | [Docs](https://material-ui.com/)                                                |\n| Data Fetching         | React Query Toolkit                            | [Docs](https://react-query.tanstack.com/)                                       |\n| Deployment            | Github Pages                                   | [Docs](https://create-react-app.dev/docs/deployment#github-pages)               |\n| Environment Variables | Dotenv (included in Create React App)          | [Docs](https://create-react-app.dev/docs/adding-custom-environment-variables)   |\n| Error Monitoring      | Sentry                                         | [Docs](https://docs.sentry.io/platforms/javascript/guides/react/)               |\n| Form                  | Formik                                         | [Docs](https://formik.org/)                                                     |\n| I18N                  | react-i18next                                  | [Docs](https://react.i18next.com/)                                              |\n| Routing               | React Router                                   | [Docs](https://reactrouter.com/)                                                |\n| Theming (+ dark mode) | Material-UI                                    | [Docs](https://material-ui.com/customization/theming/)                          |\n| Toolchain             | Create React App                               | [Docs](https://create-react-app.dev/)                                           |\n| TypeScript            | TypeScript                                     | [Docs](https://create-react-app.dev/docs/adding-typescript/)                    |\n| Validation            | Yup                                            | [Docs](https://github.com/jquense/yup)                                          |\n\n## Coming Soon\n\n| Package      | Description                                 | Docs                            |\n| ------------ | ------------------------------------------- | ------------------------------- |\n| Drag & Drop  | Add Projects page with Drag & Drop features |                                 |\n| E2E Testing  | Cypress                                     | [Docs](https://www.cypress.io/) |\n| Unit Testing | Jest                                        | [Docs](https://jestjs.io/)      |\n\n## License\n\nThis project is licensed under the terms of the\n[MIT license](/LICENSE).\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-material-admin\",\n  \"version\": \"0.1.0-alpha\",\n  \"description\": \"Free and open-source admin application made with React\",\n  \"author\": \"m6v3l9 <m6v3l9@gmail.com>\",\n  \"homepage\": \"https://m6v3l9.github.io/react-material-admin\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"@emotion/react\": \"^11.1.5\",\n    \"@emotion/styled\": \"^11.1.5\",\n    \"@fullcalendar/daygrid\": \"5.7.0\",\n    \"@fullcalendar/react\": \"5.7.0\",\n    \"@material-ui/core\": \"^5.0.0-alpha.35\",\n    \"@material-ui/icons\": \"^5.0.0-alpha.35\",\n    \"@material-ui/lab\": \"5.0.0-alpha.35\",\n    \"@sentry/react\": \"^6.4.1\",\n    \"@testing-library/jest-dom\": \"^5.11.4\",\n    \"@testing-library/react\": \"^11.1.0\",\n    \"@testing-library/user-event\": \"^12.1.10\",\n    \"@types/jest\": \"^26.0.15\",\n    \"@types/node\": \"^12.0.0\",\n    \"@types/react\": \"^17.0.0\",\n    \"@types/react-dom\": \"^17.0.0\",\n    \"axios\": \"^0.21.1\",\n    \"axios-mock-adapter\": \"^1.19.0\",\n    \"date-fns\": \"^2.19.0\",\n    \"env-cmd\": \"^10.1.0\",\n    \"formik\": \"^2.2.6\",\n    \"gh-pages\": \"^3.1.0\",\n    \"history\": \"^5.0.0\",\n    \"i18next\": \"^20.1.0\",\n    \"i18next-browser-languagedetector\": \"^6.1.0\",\n    \"i18next-xhr-backend\": \"^3.2.2\",\n    \"react\": \"^17.0.2\",\n    \"react-dom\": \"^17.0.2\",\n    \"react-error-boundary\": \"^3.1.3\",\n    \"react-i18next\": \"^11.8.11\",\n    \"react-query\": \"^3.16.0\",\n    \"react-router\": \"6.0.0-beta.0\",\n    \"react-router-dom\": \"6.0.0-beta.0\",\n    \"react-scripts\": \"4.0.3\",\n    \"recharts\": \"^2.0.9\",\n    \"source-map-explorer\": \"^2.5.2\",\n    \"typescript\": \"^4.1.2\",\n    \"web-vitals\": \"^1.0.1\",\n    \"yup\": \"^0.32.9\"\n  },\n  \"scripts\": {\n    \"analyze\": \"source-map-explorer 'build/static/js/*.js'\",\n    \"build\": \"react-scripts build\",\n    \"build:staging\": \"env-cmd -f .env.staging npm run build\",\n    \"build:production\": \"env-cmd -f .env.production npm run build\",\n    \"predeploy\": \"yarn run build:production\",\n    \"deploy\": \"gh-pages -d build\",\n    \"start\": \"react-scripts start\",\n    \"test\": \"react-scripts test\"\n  },\n  \"eslintConfig\": {\n    \"extends\": [\n      \"react-app\",\n      \"react-app/jest\"\n    ]\n  },\n  \"browserslist\": {\n    \"production\": [\n      \">0.2%\",\n      \"not dead\",\n      \"not op_mini all\"\n    ],\n    \"development\": [\n      \"last 1 chrome version\",\n      \"last 1 firefox version\",\n      \"last 1 safari version\"\n    ]\n  }\n}\n"
  },
  {
    "path": "public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>Single Page Apps for GitHub Pages</title>\n    <script type=\"text/javascript\">\n      // Single Page Apps for GitHub Pages\n      // MIT License\n      // https://github.com/rafgraph/spa-github-pages\n      // This script takes the current url and converts the path and query\n      // string into just a query string, and then redirects the browser\n      // to the new url with only a query string and hash fragment,\n      // e.g. https://www.foo.tld/one/two?a=b&c=d#qwe, becomes\n      // https://www.foo.tld/?/one/two&a=b~and~c=d#qwe\n      // Note: this 404.html file must be at least 512 bytes for it to work\n      // with Internet Explorer (it is currently > 512 bytes)\n\n      // If you're creating a Project Pages site and NOT using a custom domain,\n      // then set pathSegmentsToKeep to 1 (enterprise users may need to set it to > 1).\n      // This way the code will only replace the route part of the path, and not\n      // the real directory in which the app resides, for example:\n      // https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes\n      // https://username.github.io/repo-name/?/one/two&a=b~and~c=d#qwe\n      // Otherwise, leave pathSegmentsToKeep as 0.\n      var pathSegmentsToKeep = 1;\n\n      var l = window.location;\n      l.replace(\n        l.protocol +\n          \"//\" +\n          l.hostname +\n          (l.port ? \":\" + l.port : \"\") +\n          l.pathname\n            .split(\"/\")\n            .slice(0, 1 + pathSegmentsToKeep)\n            .join(\"/\") +\n          \"/?/\" +\n          l.pathname\n            .slice(1)\n            .split(\"/\")\n            .slice(pathSegmentsToKeep)\n            .join(\"/\")\n            .replace(/&/g, \"~and~\") +\n          (l.search ? \"&\" + l.search.slice(1).replace(/&/g, \"~and~\") : \"\") +\n          l.hash\n      );\n    </script>\n  </head>\n  <body></body>\n</html>\n"
  },
  {
    "path": "public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <link rel=\"icon\" href=\"%PUBLIC_URL%/favicon.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <meta name=\"theme-color\" content=\"#000000\" />\n    <meta name=\"description\" content=\"React Admin dashboard made with React\" />\n    <link rel=\"apple-touch-icon\" href=\"%PUBLIC_URL%/logo192.png\" />\n    <!--\n      manifest.json provides metadata used when your web app is installed on a\n      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/\n    -->\n    <link rel=\"manifest\" href=\"%PUBLIC_URL%/manifest.json\" />\n    <!--\n      Notice the use of %PUBLIC_URL% in the tags above.\n      It will be replaced with the URL of the `public` folder during the build.\n      Only files inside the `public` folder can be referenced from the HTML.\n\n      Unlike \"/favicon.ico\" or \"favicon.ico\", \"%PUBLIC_URL%/favicon.ico\" will\n      work correctly both with client-side routing and a non-root public URL.\n      Learn how to configure a non-root public URL by running `npm run build`.\n    -->\n    <base href=\"%PUBLIC_URL%/\" />\n    <title>%REACT_APP_NAME%</title>\n\n    <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" />\n    <link\n      href=\"https://fonts.googleapis.com/css2?family=Nunito:wght@400;700;800&display=swap\"\n      rel=\"stylesheet\"\n    />\n\n    <!-- Global site tag (gtag.js) - Google Analytics -->\n    <script\n      async\n      src=\"https://www.googletagmanager.com/gtag/js?id=%REACT_APP_GA_TRACKING_ID%\"\n    ></script>\n    <script>\n      window.dataLayer = window.dataLayer || [];\n      function gtag() {\n        dataLayer.push(arguments);\n      }\n      gtag(\"js\", new Date());\n\n      gtag(\"config\", \"%REACT_APP_GA_TRACKING_ID%\");\n    </script>\n\n    <!-- Start Single Page Apps for GitHub Pages -->\n    <script type=\"text/javascript\">\n      // Single Page Apps for GitHub Pages\n      // MIT License\n      // https://github.com/rafgraph/spa-github-pages\n      // This script checks to see if a redirect is present in the query string,\n      // converts it back into the correct url and adds it to the\n      // browser's history using window.history.replaceState(...),\n      // which won't cause the browser to attempt to load the new url.\n      // When the single page app is loaded further down in this file,\n      // the correct url will be waiting in the browser's history for\n      // the single page app to route accordingly.\n      (function (l) {\n        if (l.search[1] === \"/\") {\n          var decoded = l.search\n            .slice(1)\n            .split(\"&\")\n            .map(function (s) {\n              return s.replace(/~and~/g, \"&\");\n            })\n            .join(\"?\");\n          window.history.replaceState(\n            null,\n            null,\n            l.pathname.slice(0, -1) + decoded + l.hash\n          );\n        }\n      })(window.location);\n    </script>\n    <!-- End Single Page Apps for GitHub Pages -->\n  </head>\n  <body>\n    <noscript>You need to enable JavaScript to run this app.</noscript>\n    <div id=\"root\"></div>\n    <!--\n      This HTML file is a template.\n      If you open it directly in the browser, you will see an empty page.\n\n      You can add webfonts, meta tags, or analytics to this file.\n      The build step will place the bundled scripts into the <body> tag.\n\n      To begin the development, run `npm start` or `yarn start`.\n      To create a production bundle, use `npm run build` or `yarn build`.\n    -->\n  </body>\n</html>\n"
  },
  {
    "path": "public/locales/en/translation.json",
    "content": "{\n  \"admin\": {\n    \"drawer\": {\n      \"menu\": {\n        \"calendar\": \"Calendar\",\n        \"dashboard\": \"Dashboard\",\n        \"help\": \"Help\",\n        \"home\": \"Home\",\n        \"projects\": \"Projects\",\n        \"settings\": \"Settings\",\n        \"userManagement\": \"Users\"\n      }\n    },\n    \"header\": {\n      \"notifications\": {\n        \"empty\": {\n          \"title\": \"Your notification list is empty\"\n        },\n        \"seeAll\": \"See all notifications\"\n      }\n    },\n    \"home\": {\n      \"achievement\": {\n        \"action\": \"View Profile\",\n        \"description\": \"You have completed {{progress}}% of your profile. Your current progress is great.\",\n        \"title\": \"Congratulations {{name}}\"\n      },\n      \"followers\": {\n        \"units\": {\n          \"likes\": \"Likes\",\n          \"love\": \"Love\",\n          \"smiles\": \"Smiles\"\n        }\n      },\n      \"meeting\": {\n        \"title\": \"Meetings\"\n      },\n      \"targets\": {\n        \"income\": \"Income\",\n        \"followers\": \"Followers\",\n        \"title\": \"Targets\",\n        \"views\": \"Views\"\n      },\n      \"views\": {\n        \"action\": \"View Dashboard\",\n        \"unit\": \"Views\"\n      },\n      \"welcome\": {\n        \"message\": \"This page is designed to give some important information about the application. Let's make someting together!\",\n        \"subTitle\": \"Welcome back!\",\n        \"title\": \"Hi {{name}},\"\n      }\n    }\n  },\n  \"auth\": {\n    \"forgotPassword\": {\n      \"form\": {\n        \"action\": \"Send Confirmation\",\n        \"back\": \"Back to login\",\n        \"email\": {\n          \"label\": \"E-mail Address\"\n        }\n      },\n      \"notifications\": {\n        \"success\": \"Email has been sent!\"\n      },\n      \"subTitle\": \"To get a validation code, enter the e-mail address associated to your account.\",\n      \"title\": \"Get validation code\"\n    },\n    \"forgotPasswordSubmit\": {\n      \"form\": {\n        \"action\": \"Reset Password\",\n        \"back\": \"Back to login\",\n        \"code\": {\n          \"label\": \"Code\"\n        },\n        \"confirmPassword\": {\n          \"label\": \"Confirm Password\"\n        },\n        \"newPassword\": {\n          \"label\": \"New Password\"\n        }\n      },\n      \"notifications\": {\n        \"success\": \"Your password has been changed!\"\n      },\n      \"subTitle\": \"Enter the code you received by mail and choose a new password.\",\n      \"title\": \"Change your password\"\n    },\n    \"login\": {\n      \"forgotPasswordLink\": \"Forgot password?\",\n      \"form\": {\n        \"email\": {\n          \"label\": \"E-mail Address\"\n        },\n        \"password\": {\n          \"label\": \"Password\"\n        }\n      },\n      \"newAccountLink\": \"Don't have an account? Sign Up!\",\n      \"submit\": \"Sign in\",\n      \"title\": \"Sign in\"\n    },\n    \"register\": {\n      \"back\": \"Back to login\",\n      \"form\": {\n        \"email\": {\n          \"label\": \"E-mail Address\"\n        },\n        \"firstName\": {\n          \"label\": \"First Name\"\n        },\n        \"gender\": {\n          \"label\": \"Gender\",\n          \"options\": {\n            \"f\": \"F\",\n            \"m\": \"M\",\n            \"n\": \"NC\"\n          }\n        },\n        \"lastName\": {\n          \"label\": \"Last Name\"\n        }\n      },\n      \"notifications\": {\n        \"success\": \"Your account has been created successfully!\"\n      },\n      \"submit\": \"Register\",\n      \"title\": \"Register\"\n    }\n  },\n  \"calendar\": {\n    \"confirmations\": {\n      \"delete\": \"Are you sure you want to delete this event?\"\n    },\n    \"form\": {\n      \"color\": {\n        \"label\": \"Color\"\n      },\n      \"description\": {\n        \"label\": \"Description\"\n      },\n      \"end\": {\n        \"label\": \"End Date\"\n      },\n      \"start\": {\n        \"label\": \"Start Date\"\n      },\n      \"title\": {\n        \"label\": \"Title\"\n      }\n    },\n    \"modal\": {\n      \"add\": {\n        \"action\": \"Add\",\n        \"title\": \"Add Event\"\n      },\n      \"edit\": {\n        \"action\": \"Edit\",\n        \"title\": \"Edit Event\"\n      }\n    },\n    \"notifications\": {\n      \"addSuccess\": \"{{event}} has been added!\",\n      \"deleteSuccess\": \"Event has been deleted!\",\n      \"updateSuccess\": \"{{event}} has been updated!\"\n    },\n    \"title\": \"Calendar\"\n  },\n  \"common\": {\n    \"backHome\": \"Back to Home\",\n    \"cancel\": \"Cancel\",\n    \"confirm\": \"Confirm\",\n    \"confirmation\": \"Confirmation\",\n    \"delete\": \"Delete\",\n    \"edit\": \"Edit\",\n    \"errors\": {\n      \"forbidden\": {\n        \"subTitle\": \"You don't have access to view this page\"\n      },\n      \"notFound\": {\n        \"subTitle\": \"Sorry, we couldn’t find the page you’re looking for. Perhaps you’ve mistyped the URL? Be sure to check your spelling.\",\n        \"title\": \"Oops!\"\n      },\n      \"unexpected\": {\n        \"subTitle\": \"Something went wrong! If the problem persists, contact us!\",\n        \"title\": \"Oops!\"\n      },\n      \"underConstructions\": {\n        \"subTitle\": \"We are actively working on this page.\",\n        \"title\": \"Under constructions!\"\n      }\n    },\n    \"reset\": \"Reset\",\n    \"retry\": \"Try Again\",\n    \"selected\": \"Selected\",\n    \"snackbar\": {\n      \"error\": \"Error\",\n      \"success\": \"Success\"\n    },\n    \"today\": \"Today\",\n    \"update\": \"Update\",\n    \"validations\": {\n      \"email\": \"Invalid email address\",\n      \"max\": \"Must be {{size}} characters or less\",\n      \"min\": \"Must be {{size}} characters or more\",\n      \"passwordMatch\": \"Password does not match\",\n      \"required\": \"Required\"\n    }\n  },\n  \"dashboard\": {\n    \"activity\": {\n      \"title\": \"Activity\"\n    },\n    \"budget\": {\n      \"legend\": {\n        \"unit\": \"Budget ($K)\"\n      },\n      \"title\": \"Budget\"\n    },\n    \"orderProgress\": {\n      \"title\": \"Orders\"\n    },\n    \"overview\": {\n      \"orders\": \"Orders\",\n      \"sales\": \"Sales\",\n      \"users\": \"Users\",\n      \"visits\": \"Visits\"\n    },\n    \"progress\": {\n      \"title\": \"Progress\"\n    },\n    \"salesByAge\": {\n      \"title\": \"Sales by Age\"\n    },\n    \"salesByCategory\": {\n      \"legend\": {\n        \"books\": \"Books\",\n        \"movies\": \"Movies & TV\",\n        \"software\": \"Software\"\n      },\n      \"title\": \"Sales by Category\"\n    },\n    \"salesHistory\": {\n      \"title\": \"Sales History\",\n      \"unit\": \"$ today\"\n    },\n    \"salesProgress\": {\n      \"title\": \"Sales\"\n    },\n    \"teams\": {\n      \"columns\": {\n        \"progress\": \"Progress\",\n        \"team\": \"Team\",\n        \"value\": \"Value\"\n      },\n      \"title\": \"Teams Progress\"\n    },\n    \"users\": {\n      \"title\": \"Recent Users\"\n    },\n    \"visitProgress\": {\n      \"title\": \"Visits\"\n    },\n    \"title\": \"Dashboard\"\n  },\n  \"faq\": {\n    \"noAnswerLink\": \"Can't find it here? Check out our Help Center.\",\n    \"questions\": {\n      \"title1\": \"What is React Admin?\",\n      \"answer1\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.\",\n      \"title2\": \"How does it work?\",\n      \"answer2\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.\",\n      \"title3\": \"What are the features available?\",\n      \"answer3\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.\",\n      \"title4\": \"Who is maintaining the project?\",\n      \"answer4\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.\",\n      \"title5\": \"What is the license of the project?\",\n      \"answer5\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.\",\n      \"title6\": \"How can I contribute to the project?\",\n      \"answer6\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.\"\n    },\n    \"title\": \"Frequently Asked Questions\"\n  },\n  \"help\": {\n    \"menu\": {\n      \"contact\": \"Contact\",\n      \"faq\": \"FAQ\",\n      \"guide\": \"Guide\",\n      \"support\": \"Support\"\n    },\n    \"title\": \"Help Center\"\n  },\n  \"landing\": {\n    \"cta\": {\n      \"main\": \"Sign in\",\n      \"mainAuth\": \"Continue as {{name}}\",\n      \"secondary\": \"Source code\"\n    },\n    \"features\": {\n      \"more\": \"More on Github\",\n      \"title\": \"Main Features\"\n    },\n    \"title\": \"Free and open-source admin application made with React and Material-UI\"\n  },\n  \"notifications\": {\n    \"newComment\": \"<bold>{{user}}</bold> has written a new comment on your profile\",\n    \"unreadMessages\": \"You have <bold>{{quantity}}</bold> unread messages\"\n  },\n  \"profile\": {\n    \"activity\": {\n      \"empty\": \"Logs of your activity will show up here!\",\n      \"logs\": {\n        \"eventAdded\": \"<bold>You</bold> added a new event: <bold>{{resource}}</bold>\",\n        \"eventUpdated\": \"<bold>You</bold> updated event: <bold>{{resource}}</bold>\",\n        \"userAdded\": \"<bold>You</bold> added a new user: <bold>{{resource}}</bold>\",\n        \"userDeleted\": \"<bold>You</bold> deleted user: <bold>{{resource}}</bold>\",\n        \"userUpdated\": \"<bold>You</bold> updated user: <bold>{{resource}}</bold>\"\n      }\n    },\n    \"completion\": {\n      \"title\": \"Profile Completion\"\n    },\n    \"info\": {\n      \"form\": {\n        \"email\": {\n          \"label\": \"Email Address\"\n        },\n        \"firstName\": {\n          \"label\": \"First Name\"\n        },\n        \"gender\": {\n          \"label\": \"Gender\",\n          \"options\": {\n            \"f\": \"Female\",\n            \"m\": \"Male\",\n            \"n\": \"NC\"\n          }\n        },\n        \"lastName\": {\n          \"label\": \"Last Name\"\n        }\n      },\n      \"title\": \"Update profile\"\n    },\n    \"menu\": {\n      \"activity\": \"Activity\",\n      \"info\": \"Information\",\n      \"password\": \"Password\"\n    },\n    \"notifications\": {\n      \"informationUpdated\": \"Information updated!\",\n      \"passwordChanged\": \"Password changed!\"\n    },\n    \"password\": {\n      \"form\": {\n        \"current\": {\n          \"label\": \"Current Password\"\n        },\n        \"confirm\": {\n          \"label\": \"Confirm Password\"\n        },\n        \"new\": {\n          \"label\": \"New Password\"\n        }\n      },\n      \"title\": \"Change your password\"\n    }\n  },\n  \"settings\": {\n    \"drawer\": {\n      \"direction\": {\n        \"label\": \"Direction\",\n        \"options\": {\n          \"ltr\": \"LTR\",\n          \"rtl\": \"RTL\"\n        }\n      },\n      \"language\": {\n        \"label\": \"Language\",\n        \"options\": {\n          \"en\": \"English\",\n          \"fr\": \"Français\"\n        }\n      },\n      \"mode\": {\n        \"label\": \"Mode\",\n        \"options\": {\n          \"dark\": \"Dark\",\n          \"light\": \"Light\"\n        }\n      },\n      \"sidebar\": {\n        \"label\": \"Sidebar\",\n        \"options\": {\n          \"collapsed\": \"Collapsed\",\n          \"full\": \"Full\"\n        }\n      },\n      \"title\": \"Settings\"\n    }\n  },\n  \"userManagement\": {\n    \"confirmations\": {\n      \"delete\": \"Are you sure you want to delete this user?\"\n    },\n    \"form\": {\n      \"disabled\": {\n        \"label\": \"Disabled\"\n      },\n      \"email\": {\n        \"label\": \"Email Address\"\n      },\n      \"firstName\": {\n        \"label\": \"First Name\"\n      },\n      \"gender\": {\n        \"label\": \"Gender\",\n        \"options\": {\n          \"f\": \"Female\",\n          \"m\": \"Male\",\n          \"n\": \"NC\"\n        }\n      },\n      \"lastName\": {\n        \"label\": \"Last Name\"\n      },\n      \"role\": {\n        \"label\": \"Role\"\n      }\n    },\n    \"modal\": {\n      \"add\": {\n        \"action\": \"Add\",\n        \"title\": \"Add User\"\n      },\n      \"edit\": {\n        \"action\": \"Edit\",\n        \"title\": \"Edit User\"\n      }\n    },\n    \"notifications\": {\n      \"addSuccess\": \"{{user}} has been added!\",\n      \"deleteSuccess\": \"User(s) have been deleted!\",\n      \"updateSuccess\": \"{{user}} has been updated!\"\n    },\n    \"table\": {\n      \"headers\": {\n        \"actions\": \"Actions\",\n        \"gender\": \"Gender\",\n        \"role\": \"Role\",\n        \"status\": \"Status\",\n        \"user\": \"User\"\n      }\n    },\n    \"toolbar\": {\n      \"title\": \"Users\"\n    }\n  }\n}\n"
  },
  {
    "path": "public/locales/fr/translation.json",
    "content": "{\n  \"admin\": {\n    \"drawer\": {\n      \"menu\": {\n        \"calendar\": \"Calendrier\",\n        \"dashboard\": \"Dashboard\",\n        \"help\": \"Aide\",\n        \"home\": \"Home\",\n        \"projects\": \"Projets\",\n        \"settings\": \"Paramètres\",\n        \"userManagement\": \"Utilisateurs\"\n      }\n    },\n    \"header\": {\n      \"notifications\": {\n        \"empty\": {\n          \"title\": \"Votre liste de notifications est vide\"\n        },\n        \"seeAll\": \"Voir toutes les notifications\"\n      }\n    },\n    \"home\": {\n      \"achievement\": {\n        \"action\": \"Voir mon profil\",\n        \"description\": \"Vous avez complété {{progress}}% de votre profile. Vos progrès sont fantastiques.\",\n        \"title\": \"Félicitations {{name}}\"\n      },\n      \"followers\": {\n        \"units\": {\n          \"likes\": \"Likes\",\n          \"love\": \"Love\",\n          \"smiles\": \"Smiles\"\n        }\n      },\n      \"meeting\": {\n        \"title\": \"Réunions\"\n      },\n      \"targets\": {\n        \"income\": \"Revenus\",\n        \"followers\": \"Followers\",\n        \"title\": \"Objectifs\",\n        \"views\": \"Vues\"\n      },\n      \"views\": {\n        \"action\": \"Voir le dashboard\",\n        \"unit\": \"Vues\"\n      },\n      \"welcome\": {\n        \"message\": \"Cette page a été conçue pour afficher les informations importantes de l'application\",\n        \"subTitle\": \"Heureux de vous revoir!\",\n        \"title\": \"Bonjour {{name}},\"\n      }\n    }\n  },\n  \"auth\": {\n    \"forgotPassword\": {\n      \"form\": {\n        \"action\": \"Envoyer le code\",\n        \"back\": \"Retourner sur la page de login\",\n        \"email\": {\n          \"label\": \"Adresse E-mail\"\n        }\n      },\n      \"notifications\": {\n        \"success\": \"Un e-mail a été envoyé!\"\n      },\n      \"subTitle\": \"Pour obtenir un code de validation, entrez d'abord l'adresse e-mail que vous avez ajoutée à votre compte.\",\n      \"title\": \"Obtenir un code de validation\"\n    },\n    \"forgotPasswordSubmit\": {\n      \"form\": {\n        \"action\": \"Réinitialiser le mot de passe\",\n        \"back\": \"Retourner sur la page de login\",\n        \"code\": {\n          \"label\": \"Code\"\n        },\n        \"confirmPassword\": {\n          \"label\": \"Confirmer le mot de passe\"\n        },\n        \"newPassword\": {\n          \"label\": \"Nouveau mot de passe\"\n        }\n      },\n      \"notifications\": {\n        \"success\": \"Votre mot de passe a été modifié!\"\n      },\n      \"subTitle\": \"Entrez le code reçu via e-mail et choisissez votre nouveau mot de passe.\",\n      \"title\": \"Changer le mot de passe\"\n    },\n    \"login\": {\n      \"forgotPasswordLink\": \"Mot de passe oublié?\",\n      \"form\": {\n        \"email\": {\n          \"label\": \"Adresse E-mail\"\n        },\n        \"password\": {\n          \"label\": \"Mot de passe\"\n        }\n      },\n      \"newAccountLink\": \"Vous n'avez pas encore de compte? Enregistrez-vous!\",\n      \"submit\": \"Se connecter\",\n      \"title\": \"Se connecter\"\n    },\n    \"register\": {\n      \"back\": \"Retourner sur la page de login\",\n      \"form\": {\n        \"email\": {\n          \"label\": \"Adresse E-mail\"\n        },\n        \"firstName\": {\n          \"label\": \"Prénom\"\n        },\n        \"gender\": {\n          \"label\": \"Genre\",\n          \"options\": {\n            \"f\": \"F\",\n            \"m\": \"M\",\n            \"n\": \"NC\"\n          }\n        },\n        \"lastName\": {\n          \"label\": \"Nom\"\n        }\n      },\n      \"notifications\": {\n        \"success\": \"Votre compte a été créé avec succès!\"\n      },\n      \"submit\": \"S'enregistrer\",\n      \"title\": \"S'enregistrer\"\n    }\n  },\n  \"calendar\": {\n    \"confirmations\": {\n      \"delete\": \"Etes-vous certain de vouloir supprimer cet évènement?\"\n    },\n    \"form\": {\n      \"color\": {\n        \"label\": \"Couleur\"\n      },\n      \"description\": {\n        \"label\": \"Description\"\n      },\n      \"end\": {\n        \"label\": \"Date de fin\"\n      },\n      \"start\": {\n        \"label\": \"Date de début\"\n      },\n      \"title\": {\n        \"label\": \"Titre\"\n      }\n    },\n    \"modal\": {\n      \"add\": {\n        \"action\": \"Ajouter\",\n        \"title\": \"Ajouter un évènement\"\n      },\n      \"edit\": {\n        \"action\": \"Modifier\",\n        \"title\": \"Modifier l'évènement\"\n      }\n    },\n    \"notifications\": {\n      \"addSuccess\": \"{{event}} a été ajouté!\",\n      \"deleteSuccess\": \"L'évènement a été supprimé!\",\n      \"updateSuccess\": \"{{event}} a été modifié!\"\n    },\n    \"title\": \"Calendrier\"\n  },\n  \"common\": {\n    \"backHome\": \"Retourner sur la page d'accueil\",\n    \"cancel\": \"Annuler\",\n    \"confirm\": \"Confirmer\",\n    \"confirmation\": \"Confirmation\",\n    \"delete\": \"Supprimer\",\n    \"edit\": \"Editer\",\n    \"errors\": {\n      \"forbidden\": {\n        \"subTitle\": \"Vous n'avez pas les accès suffisant pour accéder à cette page\"\n      },\n      \"notFound\": {\n        \"subTitle\": \"Désolé, nous n'avons pas pu trouver la page que vous cherchez. L'URL peut être incorrect.\",\n        \"title\": \"Oups!\"\n      },\n      \"unexpected\": {\n        \"subTitle\": \"Une erreur s'est produite! Si le problème persiste, contactez-nous!\",\n        \"title\": \"Oups!\"\n      },\n      \"underConstructions\": {\n        \"subTitle\": \"Nous travaillons activement sur cette page\",\n        \"title\": \"Chantier en cours!\"\n      }\n    },\n    \"reset\": \"Reset\",\n    \"retry\": \"Réessayer\",\n    \"selected\": \"sélectionné(s)\",\n    \"snackbar\": {\n      \"error\": \"Erreur\",\n      \"success\": \"Succès\"\n    },\n    \"today\": \"Aujourd'hui\",\n    \"update\": \"Modifier\",\n    \"validations\": {\n      \"email\": \"Adresse e-mail invalide\",\n      \"max\": \"Maximum {{size}} caractères autorisés\",\n      \"min\": \"Minimum {{size}} caractères autorisés\",\n      \"passwordMatch\": \"Les mots de passe ne correspondent pas\",\n      \"required\": \"Requis\"\n    }\n  },\n  \"dashboard\": {\n    \"activity\": {\n      \"title\": \"Activité\"\n    },\n    \"budget\": {\n      \"legend\": {\n        \"unit\": \"Budget ($K)\"\n      },\n      \"title\": \"Budget\"\n    },\n    \"orderProgress\": {\n      \"title\": \"Commandes\"\n    },\n    \"overview\": {\n      \"orders\": \"Commandes\",\n      \"sales\": \"Ventes\",\n      \"users\": \"Utilisateurs\",\n      \"visits\": \"Visites\"\n    },\n    \"progress\": {\n      \"title\": \"Progrès\"\n    },\n    \"salesByAge\": {\n      \"title\": \"Ventes par tranche d'âge\"\n    },\n    \"salesByCategory\": {\n      \"legend\": {\n        \"books\": \"Livres\",\n        \"movies\": \"Films et TV\",\n        \"software\": \"Software\"\n      },\n      \"title\": \"Ventes par catégorie\"\n    },\n    \"salesHistory\": {\n      \"title\": \"Historique des ventes\",\n      \"unit\": \"$ aujourd'hui\"\n    },\n    \"salesProgress\": {\n      \"title\": \"Ventes\"\n    },\n    \"teams\": {\n      \"columns\": {\n        \"progress\": \"Progrès\",\n        \"team\": \"Equipe\",\n        \"value\": \"Valeur\"\n      },\n      \"title\": \"Progrès des équipes\"\n    },\n    \"users\": {\n      \"title\": \"Utilisateurs récents\"\n    },\n    \"visitProgress\": {\n      \"title\": \"Visites\"\n    },\n    \"title\": \"Dashboard\"\n  },\n  \"faq\": {\n    \"noAnswerLink\": \"Vous ne trouvez pas la réponse à votre question? Retournez à la page d'aide.\",\n    \"questions\": {\n      \"title1\": \"Qu'est ce que React Admin?\",\n      \"answer1\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.\",\n      \"title2\": \"Comment fonctionne-t-il?\",\n      \"answer2\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.\",\n      \"title3\": \"Quelles sont les fonctionnalités disponibles?\",\n      \"answer3\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.\",\n      \"title4\": \"Qui maintient le projet?\",\n      \"answer4\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.\",\n      \"title5\": \"Quelle est la licence utilisée?\",\n      \"answer5\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.\",\n      \"title6\": \"Comment puis-je contribuer au projet?\",\n      \"answer6\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.\"\n    },\n    \"title\": \"Questions Fréquemment Posées\"\n  },\n  \"help\": {\n    \"menu\": {\n      \"contact\": \"Contact\",\n      \"faq\": \"FAQ\",\n      \"guide\": \"Guide\",\n      \"support\": \"Support\"\n    },\n    \"title\": \"Centre d'aide\"\n  },\n  \"landing\": {\n    \"cta\": {\n      \"main\": \"Se connecter\",\n      \"mainAuth\": \"Continuer en tant que {{name}}\",\n      \"secondary\": \"Code source\"\n    },\n    \"features\": {\n      \"more\": \"Plus sur Github\",\n      \"title\": \"Principales fonctionnalités\"\n    },\n    \"title\": \"Open-source dashboard application fait avec React\"\n  },\n  \"notifications\": {\n    \"newComment\": \"<bold>{{user}}</bold> a écrit un commentaire sur votre profil\",\n    \"unreadMessages\": \"Vous avez <bold>{{quantity}}</bold> messages non-lus\"\n  },\n  \"profile\": {\n    \"activity\": {\n      \"empty\": \"Les logs de votre activité seront affichés ici!\",\n      \"logs\": {\n        \"eventAdded\": \"<bold>Vous</bold> avez ajouté un nouvel évènement: <bold>{{resource}}</bold>\",\n        \"eventUpdated\": \"<bold>Vous</bold> avez modifié l'évènement: <bold>{{resource}}</bold>\",\n        \"userAdded\": \"<bold>Vous</bold> avez ajouté un nouvel utilisateur: <bold>{{resource}}</bold>\",\n        \"userDeleted\": \"<bold>Vous</bold> avez supprimé l'utilisateur: <bold>{{resource}}</bold>\",\n        \"userUpdated\": \"<bold>Vous</bold> avez modifié l'utilisateur: <bold>{{resource}}</bold>\"\n      }\n    },\n    \"completion\": {\n      \"title\": \"Avancée du profil\"\n    },\n    \"info\": {\n      \"form\": {\n        \"email\": {\n          \"label\": \"Adresse Email\"\n        },\n        \"firstName\": {\n          \"label\": \"Prénom\"\n        },\n        \"gender\": {\n          \"label\": \"Genre\",\n          \"options\": {\n            \"f\": \"Femme\",\n            \"m\": \"Homme\",\n            \"n\": \"NC\"\n          }\n        },\n        \"lastName\": {\n          \"label\": \"Nom\"\n        }\n      },\n      \"title\": \"Modifier le profil\"\n    },\n    \"menu\": {\n      \"activity\": \"Activité\",\n      \"info\": \"Information\",\n      \"password\": \"Mot de passe\"\n    },\n    \"notifications\": {\n      \"informationUpdated\": \"Information modifiée!\",\n      \"passwordChanged\": \"Mot de passe modifié!\"\n    },\n    \"password\": {\n      \"form\": {\n        \"current\": {\n          \"label\": \"Mot de passe actuel\"\n        },\n        \"confirm\": {\n          \"label\": \"Confirmer le mot de passe\"\n        },\n        \"new\": {\n          \"label\": \"Nouveau mot de passe\"\n        }\n      },\n      \"title\": \"Changer votre mot de passe\"\n    }\n  },\n  \"settings\": {\n    \"drawer\": {\n      \"direction\": {\n        \"label\": \"Direction\",\n        \"options\": {\n          \"ltr\": \"LTR\",\n          \"rtl\": \"RTL\"\n        }\n      },\n      \"language\": {\n        \"label\": \"Langage\",\n        \"options\": {\n          \"en\": \"English\",\n          \"fr\": \"Français\"\n        }\n      },\n      \"mode\": {\n        \"label\": \"Mode\",\n        \"options\": {\n          \"dark\": \"Foncé\",\n          \"light\": \"Clair\"\n        }\n      },\n      \"sidebar\": {\n        \"label\": \"Barre latérale\",\n        \"options\": {\n          \"collapsed\": \"Réduite\",\n          \"full\": \"Complète\"\n        }\n      },\n      \"title\": \"Paramètres\"\n    }\n  },\n  \"userManagement\": {\n    \"confirmations\": {\n      \"delete\": \"Etes-vous certain de vouloir supprimer cet utilisateur?\"\n    },\n    \"form\": {\n      \"disabled\": {\n        \"label\": \"Désactivé\"\n      },\n      \"email\": {\n        \"label\": \"Addresse E-mail\"\n      },\n      \"firstName\": {\n        \"label\": \"Prénom\"\n      },\n      \"gender\": {\n        \"label\": \"Genre\",\n        \"options\": {\n          \"f\": \"Femme\",\n          \"m\": \"Homme\",\n          \"n\": \"NC\"\n        }\n      },\n      \"lastName\": {\n        \"label\": \"Nom\"\n      },\n      \"role\": {\n        \"label\": \"Rôle\"\n      }\n    },\n    \"modal\": {\n      \"add\": {\n        \"action\": \"Ajouter\",\n        \"title\": \"Ajouter un utilisateur\"\n      },\n      \"edit\": {\n        \"action\": \"Modifier\",\n        \"title\": \"Modifier l'utilisateur\"\n      }\n    },\n    \"notifications\": {\n      \"addSuccess\": \"{{user}} a été ajouté!\",\n      \"deleteSuccess\": \"L(es) utilisateur(s) ont été supprimés!\",\n      \"updateSuccess\": \"{{user}} a été modifié!\"\n    },\n    \"table\": {\n      \"headers\": {\n        \"actions\": \"Actions\",\n        \"gender\": \"Genre\",\n        \"role\": \"Rôle\",\n        \"status\": \"Statut\",\n        \"user\": \"Utilisateur\"\n      }\n    },\n    \"toolbar\": {\n      \"title\": \"Utilisateurs\"\n    }\n  }\n}\n"
  },
  {
    "path": "public/manifest.json",
    "content": "{\n  \"short_name\": \"React Admin\",\n  \"name\": \"React Material Admin\",\n  \"icons\": [\n    {\n      \"src\": \"favicon.ico\",\n      \"sizes\": \"64x64 32x32 24x24 16x16\",\n      \"type\": \"image/x-icon\"\n    },\n    {\n      \"src\": \"logo192.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"192x192\"\n    },\n    {\n      \"src\": \"logo512.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"512x512\"\n    }\n  ],\n  \"start_url\": \".\",\n  \"display\": \"standalone\",\n  \"theme_color\": \"#000000\",\n  \"background_color\": \"#ffffff\"\n}\n"
  },
  {
    "path": "public/robots.txt",
    "content": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\nDisallow:\n"
  },
  {
    "path": "src/App.test.tsx",
    "content": "import { render, screen } from \"@testing-library/react\";\nimport React from \"react\";\nimport App from \"./App\";\n\ntest(\"renders learn react link\", () => {\n  render(<App />);\n  const linkElement = screen.getByText(/learn react/i);\n  expect(linkElement).toBeInTheDocument();\n});\n"
  },
  {
    "path": "src/App.tsx",
    "content": "import * as Sentry from \"@sentry/react\";\nimport React from \"react\";\nimport { QueryClient, QueryClientProvider } from \"react-query\";\nimport { ReactQueryDevtools } from \"react-query/devtools\";\nimport AppRoutes from \"./AppRoutes\";\nimport AuthProvider from \"./auth/contexts/AuthProvider\";\nimport Loader from \"./core/components/Loader\";\nimport QueryWrapper from \"./core/components/QueryWrapper\";\nimport SettingsProvider from \"./core/contexts/SettingsProvider\";\nimport SnackbarProvider from \"./core/contexts/SnackbarProvider\";\nimport usePageTracking from \"./core/hooks/usePageTracking\";\n\nif (process.env.NODE_ENV === \"production\") {\n  Sentry.init({\n    dsn: process.env.REACT_APP_SENTRY_DSN,\n  });\n}\n\n// Create a client\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      refetchOnWindowFocus: false,\n      retry: 0,\n      suspense: true,\n    },\n  },\n});\n\nfunction App() {\n  usePageTracking();\n\n  return (\n    <React.Suspense fallback={<Loader />}>\n      <Sentry.ErrorBoundary fallback={\"An error has occurred\"}>\n        <QueryClientProvider client={queryClient}>\n          <SettingsProvider>\n            <QueryWrapper>\n              <SnackbarProvider>\n                <AuthProvider>\n                  <AppRoutes />\n                </AuthProvider>\n              </SnackbarProvider>\n            </QueryWrapper>\n          </SettingsProvider>\n          <ReactQueryDevtools initialIsOpen />\n        </QueryClientProvider>\n      </Sentry.ErrorBoundary>\n    </React.Suspense>\n  );\n}\n\nexport default App;\n"
  },
  {
    "path": "src/AppRoutes.tsx",
    "content": "import { lazy } from \"react\";\nimport { Navigate, Route, Routes } from \"react-router-dom\";\nimport PrivateRoute from \"./core/components/PrivateRoute\";\n\n// Admin\nconst Admin = lazy(() => import(\"./admin/pages/Admin\"));\nconst Dashboard = lazy(() => import(\"./admin/pages/Dashboard\"));\nconst Faq = lazy(() => import(\"./admin/pages/Faq\"));\nconst HelpCenter = lazy(() => import(\"./admin/pages/HelpCenter\"));\nconst Home = lazy(() => import(\"./admin/pages/Home\"));\nconst Profile = lazy(() => import(\"./admin/pages/Profile\"));\nconst ProfileActivity = lazy(() => import(\"./admin/pages/ProfileActivity\"));\nconst ProfileInformation = lazy(\n  () => import(\"./admin/pages/ProfileInformation\")\n);\nconst ProfilePassword = lazy(() => import(\"./admin/pages/ProfilePassword\"));\n\n// Auth\nconst ForgotPassword = lazy(() => import(\"./auth/pages/ForgotPassword\"));\nconst ForgotPasswordSubmit = lazy(\n  () => import(\"./auth/pages/ForgotPasswordSubmit\")\n);\nconst Login = lazy(() => import(\"./auth/pages/Login\"));\nconst Register = lazy(() => import(\"./auth/pages/Register\"));\n\n// Calendar\nconst CalendarApp = lazy(() => import(\"./calendar/pages/CalendarApp\"));\n\n// Core\nconst Forbidden = lazy(() => import(\"./core/pages/Forbidden\"));\nconst NotFound = lazy(() => import(\"./core/pages/NotFound\"));\nconst UnderConstructions = lazy(\n  () => import(\"./core/pages/UnderConstructions\")\n);\n\n// Landing\nconst Landing = lazy(() => import(\"./landing/pages/Landing\"));\n\n// Users\nconst UserManagement = lazy(() => import(\"./users/pages/UserManagement\"));\n\nconst AppRoutes = () => {\n  return (\n    <Routes basename={process.env.PUBLIC_URL}>\n      <Route path=\"/\" element={<Landing />} />\n      <PrivateRoute path=\"admin\" element={<Admin />}>\n        <PrivateRoute path=\"/\" element={<Home />} />\n        <PrivateRoute path=\"calendar\" element={<CalendarApp />} />\n        <PrivateRoute path=\"dashboard\" element={<Dashboard />} />\n        <PrivateRoute path=\"faq\" element={<Faq />} />\n        <PrivateRoute path=\"help\" element={<HelpCenter />} />\n        <PrivateRoute path=\"profile\" element={<Profile />}>\n          <PrivateRoute path=\"/\" element={<ProfileActivity />} />\n          <PrivateRoute path=\"information\" element={<ProfileInformation />} />\n          <PrivateRoute path=\"password\" element={<ProfilePassword />} />\n        </PrivateRoute>\n        <PrivateRoute\n          path=\"projects\"\n          element={\n            <Navigate\n              to={`/${process.env.PUBLIC_URL}/under-construction`}\n              replace\n            />\n          }\n        />\n        <PrivateRoute path=\"user-management\" element={<UserManagement />} />\n      </PrivateRoute>\n      <Route path=\"forgot-password\" element={<ForgotPassword />} />\n      <Route path=\"forgot-password-submit\" element={<ForgotPasswordSubmit />} />\n      <Route path=\"login\" element={<Login />} />\n      <Route path=\"register\" element={<Register />} />\n      <Route path=\"under-construction\" element={<UnderConstructions />} />\n      <Route path=\"403\" element={<Forbidden />} />\n      <Route path=\"404\" element={<NotFound />} />\n      <Route\n        path=\"*\"\n        element={<Navigate to={`/${process.env.PUBLIC_URL}/404`} replace />}\n      />\n    </Routes>\n  );\n};\n\nexport default AppRoutes;\n"
  },
  {
    "path": "src/admin/components/AdminAppBar.tsx",
    "content": "import AppBar from \"@material-ui/core/AppBar\";\nimport { drawerCollapsedWidth, drawerWidth } from \"../../core/config/layout\";\nimport { useSettings } from \"../../core/contexts/SettingsProvider\";\n\ntype AdminAppBarProps = {\n  children: React.ReactNode;\n};\n\nconst AdminAppBar = ({ children }: AdminAppBarProps) => {\n  const { collapsed } = useSettings();\n  const width = collapsed ? drawerCollapsedWidth : drawerWidth;\n\n  return (\n    <AppBar\n      color=\"default\"\n      position=\"fixed\"\n      sx={{\n        width: { lg: `calc(100% - ${width}px)` },\n        marginLeft: { lg: width },\n      }}\n    >\n      {children}\n    </AppBar>\n  );\n};\n\nexport default AdminAppBar;\n"
  },
  {
    "path": "src/admin/components/AdminDrawer.tsx",
    "content": "import Avatar from \"@material-ui/core/Avatar\";\nimport Box from \"@material-ui/core/Box\";\nimport Drawer from \"@material-ui/core/Drawer\";\nimport List from \"@material-ui/core/List\";\nimport ListItem from \"@material-ui/core/ListItem\";\nimport ListItemAvatar from \"@material-ui/core/ListItemAvatar\";\nimport ListItemText from \"@material-ui/core/ListItemText\";\nimport AccountTreeIcon from \"@material-ui/icons/AccountTree\";\nimport BarChartIcon from \"@material-ui/icons/BarChart\";\nimport EventIcon from \"@material-ui/icons/Event\";\nimport HelpCenterIcon from \"@material-ui/icons/HelpCenter\";\nimport HomeIcon from \"@material-ui/icons/Home\";\nimport PeopleIcon from \"@material-ui/icons/People\";\nimport PersonIcon from \"@material-ui/icons/Person\";\nimport SettingsIcon from \"@material-ui/icons/Settings\";\nimport { useTranslation } from \"react-i18next\";\nimport { NavLink } from \"react-router-dom\";\nimport { useAuth } from \"../../auth/contexts/AuthProvider\";\nimport Logo from \"../../core/components/Logo\";\nimport { drawerCollapsedWidth, drawerWidth } from \"../../core/config/layout\";\n\ntype AdminDrawerProps = {\n  collapsed: boolean;\n  mobileOpen: boolean;\n  onDrawerToggle: () => void;\n  onSettingsToggle: () => void;\n};\n\nexport const menuItems = [\n  {\n    icon: HomeIcon,\n    key: \"admin.drawer.menu.home\",\n    path: \"/admin\",\n  },\n  {\n    icon: BarChartIcon,\n    key: \"admin.drawer.menu.dashboard\",\n    path: \"/admin/dashboard\",\n  },\n  {\n    icon: PeopleIcon,\n    key: \"admin.drawer.menu.userManagement\",\n    path: \"/admin/user-management\",\n  },\n  {\n    icon: EventIcon,\n    key: \"admin.drawer.menu.calendar\",\n    path: \"/admin/calendar\",\n  },\n  {\n    icon: AccountTreeIcon,\n    key: \"admin.drawer.menu.projects\",\n    path: \"/admin/projects\",\n  },\n  {\n    icon: HelpCenterIcon,\n    key: \"admin.drawer.menu.help\",\n    path: \"/admin/help\",\n  },\n];\n\nconst AdminDrawer = ({\n  collapsed,\n  mobileOpen,\n  onDrawerToggle,\n  onSettingsToggle,\n}: AdminDrawerProps) => {\n  const { userInfo } = useAuth();\n  const { t } = useTranslation();\n\n  const width = collapsed ? drawerCollapsedWidth : drawerWidth;\n\n  const drawer = (\n    <Box sx={{ display: \"flex\", flexDirection: \"column\", minHeight: \"100%\" }}>\n      <Logo sx={{ display: \"flex\", p: 4 }} />\n      <List component=\"nav\" sx={{ px: 2 }}>\n        {menuItems.map((item) => (\n          <ListItem\n            button\n            component={NavLink}\n            key={item.path}\n            activeClassName=\"Mui-selected\"\n            end={true}\n            to={`/${process.env.PUBLIC_URL}${item.path}`}\n          >\n            <ListItemAvatar>\n              <Avatar sx={{ color: \"inherit\", bgcolor: \"transparent\" }}>\n                <item.icon />\n              </Avatar>\n            </ListItemAvatar>\n            <ListItemText\n              primary={t(item.key)}\n              sx={{\n                display: collapsed ? \"none\" : \"block\",\n              }}\n            />\n          </ListItem>\n        ))}\n      </List>\n      <Box sx={{ flexGrow: 1 }} />\n      <List component=\"nav\" sx={{ p: 2 }}>\n        <ListItem\n          button\n          component={NavLink}\n          to={`/${process.env.PUBLIC_URL}/admin/profile`}\n        >\n          <ListItemAvatar>\n            <Avatar>\n              <PersonIcon />\n            </Avatar>\n          </ListItemAvatar>\n          {userInfo && (\n            <ListItemText\n              primary={`${userInfo.firstName} ${userInfo.lastName}`}\n              sx={{\n                display: collapsed ? \"none\" : \"block\",\n              }}\n            />\n          )}\n        </ListItem>\n        <ListItem button onClick={onSettingsToggle}>\n          <ListItemAvatar>\n            <Avatar>\n              <SettingsIcon />\n            </Avatar>\n          </ListItemAvatar>\n          <ListItemText\n            primary={t(\"admin.drawer.menu.settings\")}\n            sx={{\n              display: collapsed ? \"none\" : \"block\",\n            }}\n          />\n        </ListItem>\n      </List>\n    </Box>\n  );\n\n  return (\n    <Box\n      aria-label=\"Admin drawer\"\n      component=\"nav\"\n      sx={{\n        width: { lg: width },\n        flexShrink: { lg: 0 },\n      }}\n    >\n      {/* The implementation can be swapped with js to avoid SEO duplication of links. */}\n      <Drawer\n        variant=\"temporary\"\n        open={mobileOpen}\n        onClose={onDrawerToggle}\n        ModalProps={{\n          keepMounted: true, // Better open performance on mobile.\n        }}\n        sx={{\n          display: { xs: \"block\", lg: \"none\" },\n          \"& .MuiDrawer-paper\": {\n            boxSizing: \"border-box\",\n            width: width,\n          },\n        }}\n      >\n        {drawer}\n      </Drawer>\n      <Drawer\n        variant=\"permanent\"\n        open\n        sx={{\n          display: { xs: \"none\", lg: \"block\" },\n          \"& .MuiDrawer-paper\": {\n            boxSizing: \"border-box\",\n            width: width,\n          },\n        }}\n      >\n        {drawer}\n      </Drawer>\n    </Box>\n  );\n};\n\nexport default AdminDrawer;\n"
  },
  {
    "path": "src/admin/components/AdminToolbar.tsx",
    "content": "import IconButton from \"@material-ui/core/IconButton\";\nimport Toolbar from \"@material-ui/core/Toolbar\";\nimport Typography from \"@material-ui/core/Typography\";\nimport MenuIcon from \"@material-ui/icons/Menu\";\nimport { useSettings } from \"../../core/contexts/SettingsProvider\";\n\ntype AdminToolbarProps = {\n  children?: React.ReactNode;\n  title?: string;\n};\n\nconst AdminToolbar = ({ children, title }: AdminToolbarProps) => {\n  const { toggleDrawer } = useSettings();\n\n  return (\n    <Toolbar sx={{ px: { xs: 3, sm: 6 } }}>\n      <IconButton\n        color=\"inherit\"\n        aria-label=\"open drawer\"\n        edge=\"start\"\n        onClick={toggleDrawer}\n        sx={{\n          display: { lg: \"none\" },\n          marginRight: 2,\n        }}\n      >\n        <MenuIcon />\n      </IconButton>\n      <Typography variant=\"h2\" component=\"h1\" sx={{ flexGrow: 1 }}>\n        {title}\n      </Typography>\n      {children}\n    </Toolbar>\n  );\n};\n\nexport default AdminToolbar;\n"
  },
  {
    "path": "src/admin/components/RecentNotifications.tsx",
    "content": "import Avatar from \"@material-ui/core/Avatar\";\nimport Badge from \"@material-ui/core/Badge\";\nimport Box from \"@material-ui/core/Box\";\nimport Button from \"@material-ui/core/Button\";\nimport IconButton from \"@material-ui/core/IconButton\";\nimport List from \"@material-ui/core/List\";\nimport ListItem from \"@material-ui/core/ListItem\";\nimport ListItemAvatar from \"@material-ui/core/ListItemAvatar\";\nimport ListItemText from \"@material-ui/core/ListItemText\";\nimport Popover from \"@material-ui/core/Popover\";\nimport NotificationsIcon from \"@material-ui/icons/Notifications\";\nimport PersonIcon from \"@material-ui/icons/Person\";\nimport formatDistanceToNow from \"date-fns/formatDistanceToNow\";\nimport { useMemo, useState } from \"react\";\nimport { Trans, useTranslation } from \"react-i18next\";\nimport { NavLink } from \"react-router-dom\";\nimport Empty from \"../../core/components/Empty\";\nimport Loader from \"../../core/components/Loader\";\nimport Result from \"../../core/components/Result\";\nimport { useDateLocale } from \"../../core/hooks/useDateLocale\";\nimport { notificationKeys } from \"../config/notification\";\nimport { useNotifications } from \"../hooks/useNotifications\";\n\nconst RecentNotifications = () => {\n  const locale = useDateLocale();\n  const { t } = useTranslation();\n\n  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);\n\n  const { data, isError, isLoading } = useNotifications();\n\n  const open = Boolean(anchorEl);\n\n  const unreadCount = useMemo(\n    () => data && data.filter((notification) => notification.unread).length,\n    [data]\n  );\n\n  const handleClick = (event: React.MouseEvent<HTMLElement>) => {\n    setAnchorEl(event.currentTarget);\n  };\n\n  const handleClose = () => {\n    setAnchorEl(null);\n  };\n\n  return (\n    <Box>\n      <IconButton\n        id=\"notifications-button\"\n        aria-controls=\"notifications-popover\"\n        aria-haspopup=\"true\"\n        aria-expanded={open ? \"true\" : \"false\"}\n        aria-label=\"show recent notifications\"\n        color=\"inherit\"\n        onClick={handleClick}\n      >\n        <Badge color=\"error\" variant=\"dot\" invisible={!unreadCount}>\n          <NotificationsIcon />\n        </Badge>\n      </IconButton>\n      <Popover\n        id=\"notifications-popover\"\n        open={open}\n        anchorEl={anchorEl}\n        onClose={handleClose}\n        anchorOrigin={{\n          vertical: \"bottom\",\n          horizontal: \"right\",\n        }}\n        transformOrigin={{\n          vertical: \"top\",\n          horizontal: \"right\",\n        }}\n      >\n        <Box sx={{ width: 360 }}>\n          {!isLoading && !isError && data && data.length > 0 && (\n            <List\n              component=\"nav\"\n              aria-label=\"notifications popover\"\n              sx={{ px: 2 }}\n            >\n              {data.map((notification) => (\n                <ListItem\n                  button\n                  component={NavLink}\n                  key={notification.id}\n                  to={\"\"}\n                >\n                  <ListItemAvatar>\n                    <Avatar>\n                      <PersonIcon />\n                    </Avatar>\n                  </ListItemAvatar>\n                  <ListItemText\n                    primary={\n                      <Trans\n                        components={{ bold: <strong /> }}\n                        defaults=\"<bold>{{ user }}</bold> did someting <bold>{{ quantity }}</bold> times\"\n                        i18nKey={notificationKeys[notification.code]}\n                        values={notification.params}\n                      />\n                    }\n                    secondary={formatDistanceToNow(\n                      new Date(notification.createdAt),\n                      { addSuffix: true, locale }\n                    )}\n                  />\n                </ListItem>\n              ))}\n            </List>\n          )}\n          {!isLoading && !isError && (!data || data.length === 0) && (\n            <Empty title={t(\"admin.header.notifications.empty.title\")} />\n          )}\n          {isError && (\n            <Result\n              status=\"error\"\n              subTitle={t(\"common.errors.unexpected.subTitle\")}\n              title={t(\"common.errors.unexpected.title\")}\n            />\n          )}\n          {isLoading && <Loader />}\n          <Box sx={{ px: 2, pb: 2 }}>\n            <Button\n              color=\"secondary\"\n              fullWidth\n              sx={{ bgcolor: \"background.default\" }}\n              variant=\"contained\"\n            >\n              {t(\"admin.header.notifications.seeAll\")}\n            </Button>\n          </Box>\n        </Box>\n      </Popover>\n    </Box>\n  );\n};\n\nexport default RecentNotifications;\n"
  },
  {
    "path": "src/admin/config/activity.ts",
    "content": "export const logKeys: { [key: string]: string } = {\n  eventAdded: \"profile.activity.logs.eventAdded\",\n  eventUpdated: \"profile.activity.logs.eventUpdated\",\n  userAdded: \"profile.activity.logs.eventAdded\",\n  userDeleted: \"profile.activity.logs.userDeleted\",\n  userUpdated: \"profile.activity.logs.userUpdated\",\n};\n"
  },
  {
    "path": "src/admin/config/notification.ts",
    "content": "export const notificationKeys: { [key: string]: string } = {\n  newComment: \"notifications.newComment\",\n  unreadMessages: \"notifications.unreadMessages\",\n};\n"
  },
  {
    "path": "src/admin/hooks/useActivityLogs.ts",
    "content": "import axios from \"axios\";\nimport { useQuery } from \"react-query\";\nimport { ActivityLog } from \"../types/activityLog\";\n\nconst fetchActivityLogs = async (): Promise<ActivityLog[]> => {\n  const { data } = await axios.get(\"/api/activity-logs\");\n  return data;\n};\n\nexport function useActivityLogs() {\n  return useQuery(\"activity-logs\", () => fetchActivityLogs());\n}\n"
  },
  {
    "path": "src/admin/hooks/useNotifications.ts",
    "content": "import axios from \"axios\";\nimport { useQuery } from \"react-query\";\nimport { Notification } from \"../types/notification\";\n\nconst fetchNotifications = async (): Promise<Notification[]> => {\n  const { data } = await axios.get(\"/api/notifications\");\n  return data;\n};\n\nexport function useNotifications() {\n  return useQuery(\"notifications\", () => fetchNotifications(), {\n    suspense: false,\n  });\n}\n"
  },
  {
    "path": "src/admin/hooks/useProfileInfo.ts",
    "content": "import axios from \"axios\";\nimport { useQuery } from \"react-query\";\nimport { ProfileInfo } from \"../types/profileInfo\";\n\nconst fetchProfileInfo = async (): Promise<ProfileInfo> => {\n  const { data } = await axios.get(\"/api/profile-info\");\n  return data;\n};\n\nexport function useProfileInfo() {\n  return useQuery(\"profile-info\", () => fetchProfileInfo());\n}\n"
  },
  {
    "path": "src/admin/hooks/useUpdateProfileInfo.ts",
    "content": "import axios from \"axios\";\nimport { useMutation, useQueryClient } from \"react-query\";\nimport { ProfileInfo } from \"../types/profileInfo\";\n\nconst updateProfileInfo = async (\n  profileInfo: ProfileInfo\n): Promise<ProfileInfo> => {\n  const { data } = await axios.put(\"/api/profile-info\", profileInfo);\n  return data;\n};\n\nexport function useUpdateProfileInfo() {\n  const queryClient = useQueryClient();\n\n  const { isLoading, mutateAsync } = useMutation(updateProfileInfo, {\n    onSuccess: (profileInfo: ProfileInfo) => {\n      queryClient.setQueryData([\"profile-info\"], profileInfo);\n    },\n  });\n\n  return { isUpdating: isLoading, updateProfileInfo: mutateAsync };\n}\n"
  },
  {
    "path": "src/admin/pages/Admin.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Toolbar from \"@material-ui/core/Toolbar\";\nimport { useState } from \"react\";\nimport { Outlet } from \"react-router-dom\";\nimport QueryWrapper from \"../../core/components/QueryWrapper\";\nimport SettingsDrawer from \"../../core/components/SettingsDrawer\";\nimport { useSettings } from \"../../core/contexts/SettingsProvider\";\nimport AdminDrawer from \"../components/AdminDrawer\";\n\nconst AdminLayout = () => {\n  const [settingsOpen, setSettingsOpen] = useState(false);\n\n  const { collapsed, open, toggleDrawer } = useSettings();\n\n  const handleSettingsToggle = () => {\n    setSettingsOpen(!settingsOpen);\n  };\n\n  return (\n    <Box sx={{ display: \"flex\" }}>\n      <AdminDrawer\n        collapsed={collapsed}\n        mobileOpen={open}\n        onDrawerToggle={toggleDrawer}\n        onSettingsToggle={handleSettingsToggle}\n      />\n      <SettingsDrawer\n        onDrawerToggle={handleSettingsToggle}\n        open={settingsOpen}\n      />\n      <Box component=\"main\" sx={{ flexGrow: 1, pb: 3, px: { xs: 3, sm: 6 } }}>\n        <Toolbar />\n        <QueryWrapper>\n          <Outlet />\n        </QueryWrapper>\n      </Box>\n    </Box>\n  );\n};\n\nexport default AdminLayout;\n"
  },
  {
    "path": "src/admin/pages/Dashboard.tsx",
    "content": "import Grid from \"@material-ui/core/Grid\";\nimport AttachMoneyIcon from \"@material-ui/icons/AttachMoney\";\nimport ShoppingBasketIcon from \"@material-ui/icons/ShoppingBasket\";\nimport SupervisorAccountIcon from \"@material-ui/icons/SupervisorAccount\";\nimport React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport AdminAppBar from \"../components/AdminAppBar\";\nimport AdminToolbar from \"../components/AdminToolbar\";\nimport ActivityWidget from \"../widgets/ActivityWidget\";\nimport BudgetWidget from \"../widgets/BudgetWidget\";\nimport CircleProgressWidget from \"../widgets/CircleProgressWidget\";\nimport OverviewWidget from \"../widgets/OverviewWidget\";\nimport ProgressWidget from \"../widgets/ProgressWidget\";\nimport SalesByAgeWidget from \"../widgets/SalesByAgeWidget\";\nimport SalesByCategoryWidget from \"../widgets/SalesByCategoryWidget\";\nimport SalesHistoryWidget from \"../widgets/SalesHistoryWidget\";\nimport TeamProgressWidget from \"../widgets/TeamProgressWidget\";\nimport UsersWidget from \"../widgets/UsersWidget\";\n\nconst overviewItems = [\n  {\n    unit: \"dashboard.overview.visits\",\n    value: \"20 700\",\n  },\n  {\n    unit: \"dashboard.overview.sales\",\n    value: \"$ 1 550\",\n  },\n  {\n    unit: \"dashboard.overview.orders\",\n    value: \"149\",\n  },\n  {\n    unit: \"dashboard.overview.users\",\n    value: \"657\",\n  },\n];\n\nconst Dashboard = () => {\n  const { t } = useTranslation();\n\n  return (\n    <React.Fragment>\n      <AdminAppBar>\n        <AdminToolbar title={t(\"dashboard.title\")} />\n      </AdminAppBar>\n      <Grid container spacing={2}>\n        {overviewItems.map((item, index) => (\n          <Grid key={index} item xs={6} md={3}>\n            <OverviewWidget description={t(item.unit)} title={item.value} />\n          </Grid>\n        ))}\n        <Grid item xs={12} md={8}>\n          <ActivityWidget />\n        </Grid>\n        <Grid item xs={12} md={4}>\n          <BudgetWidget />\n        </Grid>\n        <Grid item xs={12} md={4}>\n          <SalesHistoryWidget value={567} />\n        </Grid>\n        <Grid item xs={12} md={4}>\n          <ProgressWidget\n            avatar={<SupervisorAccountIcon />}\n            mb={2}\n            title={t(\"dashboard.visitProgress.title\")}\n            value={75}\n          />\n          <ProgressWidget\n            avatar={<ShoppingBasketIcon />}\n            mb={2}\n            title={t(\"dashboard.orderProgress.title\")}\n            value={50}\n          />\n          <ProgressWidget\n            avatar={<AttachMoneyIcon />}\n            title={t(\"dashboard.salesProgress.title\")}\n            value={25}\n          />\n        </Grid>\n        <Grid item xs={12} md={4}>\n          <CircleProgressWidget\n            height={204}\n            title={t(\"dashboard.progress.title\")}\n            value={75}\n          />\n        </Grid>\n        <Grid item xs={12} md={4}>\n          <UsersWidget />\n        </Grid>\n        <Grid item xs={12} md={8}>\n          <TeamProgressWidget />\n        </Grid>\n        <Grid item xs={12} md={4}>\n          <SalesByCategoryWidget />\n        </Grid>\n        <Grid item xs={12} md={8}>\n          <SalesByAgeWidget />\n        </Grid>\n      </Grid>\n    </React.Fragment>\n  );\n};\n\nexport default Dashboard;\n"
  },
  {
    "path": "src/admin/pages/Faq.tsx",
    "content": "import Accordion from \"@material-ui/core/Accordion\";\nimport AccordionDetails from \"@material-ui/core/AccordionDetails\";\nimport AccordionSummary from \"@material-ui/core/AccordionSummary\";\nimport Container from \"@material-ui/core/Container\";\nimport Link from \"@material-ui/core/Link\";\nimport Typography from \"@material-ui/core/Typography\";\nimport ExpandMoreIcon from \"@material-ui/icons/ExpandMore\";\nimport React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link as RouterLink } from \"react-router-dom\";\nimport AdminAppBar from \"../components/AdminAppBar\";\nimport AdminToolbar from \"../components/AdminToolbar\";\n\nconst questions = [\n  {\n    title: \"faq.questions.title1\",\n    answer: \"faq.questions.answer1\",\n  },\n  {\n    title: \"faq.questions.title2\",\n    answer: \"faq.questions.answer2\",\n  },\n  {\n    title: \"faq.questions.title3\",\n    answer: \"faq.questions.answer3\",\n  },\n  {\n    title: \"faq.questions.title4\",\n    answer: \"faq.questions.answer4\",\n  },\n  {\n    title: \"faq.questions.title5\",\n    answer: \"faq.questions.answer5\",\n  },\n  {\n    title: \"faq.questions.title6\",\n    answer: \"faq.questions.answer6\",\n  },\n];\n\nconst Faq = () => {\n  const { t } = useTranslation();\n\n  return (\n    <React.Fragment>\n      <AdminAppBar>\n        <AdminToolbar />\n      </AdminAppBar>\n      <Container maxWidth=\"sm\">\n        <Typography align=\"center\" marginBottom={6} variant=\"h2\">\n          {t(\"faq.title\")}\n        </Typography>\n        {questions.map((question, index) => (\n          <Accordion key={index}>\n            <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n              <Typography component=\"p\" variant=\"h6\">\n                {t(question.title)}\n              </Typography>\n            </AccordionSummary>\n            <AccordionDetails>\n              <Typography color=\"text.secondary\">\n                {t(question.answer)}\n              </Typography>\n            </AccordionDetails>\n          </Accordion>\n        ))}\n        <Link\n          component={RouterLink}\n          to={`/${process.env.PUBLIC_URL}/admin/help`}\n          variant=\"body2\"\n        >\n          {t(\"faq.noAnswerLink\")}\n        </Link>\n      </Container>\n    </React.Fragment>\n  );\n};\n\nexport default Faq;\n"
  },
  {
    "path": "src/admin/pages/HelpCenter.tsx",
    "content": "import Avatar from \"@material-ui/core/Avatar\";\nimport Badge from \"@material-ui/core/Badge\";\nimport Card from \"@material-ui/core/Card\";\nimport CardActionArea from \"@material-ui/core/CardActionArea\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport CardHeader from \"@material-ui/core/CardHeader\";\nimport Container from \"@material-ui/core/Container\";\nimport Grid from \"@material-ui/core/Grid\";\nimport Typography from \"@material-ui/core/Typography\";\nimport HelpIcon from \"@material-ui/icons/Help\";\nimport MailIcon from \"@material-ui/icons/Mail\";\nimport SchoolIcon from \"@material-ui/icons/School\";\nimport SupportIcon from \"@material-ui/icons/Support\";\nimport React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link as RouterLink } from \"react-router-dom\";\nimport { ReactComponent as HelpSvg } from \"../../core/assets/help.svg\";\nimport SvgContainer from \"../../core/components/SvgContainer\";\nimport AdminAppBar from \"../components/AdminAppBar\";\nimport AdminToolbar from \"../components/AdminToolbar\";\n\nconst HelpCenter = () => {\n  const { t } = useTranslation();\n\n  return (\n    <React.Fragment>\n      <AdminAppBar>\n        <AdminToolbar title={t(\"help.title\")} />\n      </AdminAppBar>\n      <Container maxWidth=\"xs\" sx={{ mt: 3 }}>\n        <SvgContainer>\n          <HelpSvg />\n        </SvgContainer>\n      </Container>\n      <Grid container spacing={2} sx={{ mt: 3 }}>\n        <Grid item xs={6} lg={3}>\n          <Card>\n            <CardActionArea disabled={true}>\n              <CardHeader\n                avatar={\n                  <Avatar aria-label=\"Guides icon\">\n                    <SchoolIcon />\n                  </Avatar>\n                }\n              />\n              <CardContent>\n                <Badge\n                  badgeContent=\"Coming soon\"\n                  color=\"primary\"\n                  sx={{\n                    \"& .MuiBadge-badge\": {\n                      top: -8,\n                      right: 10,\n                      whiteSpace: \"nowrap\",\n                    },\n                  }}\n                >\n                  <Typography variant=\"h6\" component=\"div\" sx={{ flexGrow: 1 }}>\n                    {t(\"help.menu.guide\")}\n                  </Typography>\n                </Badge>\n              </CardContent>\n            </CardActionArea>\n          </Card>\n        </Grid>\n        <Grid item xs={6} lg={3}>\n          <Card>\n            <CardActionArea\n              component={RouterLink}\n              to={`/${process.env.PUBLIC_URL}/admin/faq`}\n            >\n              <CardHeader\n                avatar={\n                  <Avatar aria-label=\"FAQ icon\">\n                    <HelpIcon />\n                  </Avatar>\n                }\n              />\n              <CardContent>\n                <Typography variant=\"h6\" component=\"div\" sx={{ flexGrow: 1 }}>\n                  {t(\"help.menu.faq\")}\n                </Typography>\n              </CardContent>\n            </CardActionArea>\n          </Card>\n        </Grid>\n        <Grid item xs={6} lg={3}>\n          <Card>\n            <CardActionArea\n              component=\"a\"\n              href={process.env.REACT_APP_SUPPORT_LINK}\n              rel=\"noopener noreferrer\"\n              target=\"_blank\"\n            >\n              <CardHeader\n                avatar={\n                  <Avatar aria-label=\"Support icon\">\n                    <SupportIcon />\n                  </Avatar>\n                }\n              />\n              <CardContent>\n                <Typography variant=\"h6\" component=\"div\" sx={{ flexGrow: 1 }}>\n                  {t(\"help.menu.support\")}\n                </Typography>\n              </CardContent>\n            </CardActionArea>\n          </Card>\n        </Grid>\n        <Grid item xs={6} lg={3}>\n          <Card>\n            <CardActionArea\n              component=\"a\"\n              href={`mailto:${process.env.REACT_APP_CONTACT_MAIL}`}\n            >\n              <CardHeader\n                avatar={\n                  <Avatar aria-label=\"Mail icon\">\n                    <MailIcon />\n                  </Avatar>\n                }\n              />\n              <CardContent>\n                <Typography variant=\"h6\" component=\"div\" sx={{ flexGrow: 1 }}>\n                  {t(\"help.menu.contact\")}\n                </Typography>\n              </CardContent>\n            </CardActionArea>\n          </Card>\n        </Grid>\n      </Grid>\n    </React.Fragment>\n  );\n};\n\nexport default HelpCenter;\n"
  },
  {
    "path": "src/admin/pages/Home.tsx",
    "content": "import Grid from \"@material-ui/core/Grid\";\nimport React from \"react\";\nimport AdminAppBar from \"../components/AdminAppBar\";\nimport AdminToolbar from \"../components/AdminToolbar\";\nimport RecentNotifications from \"../components/RecentNotifications\";\nimport AchievementWidget from \"../widgets/AchievementWidget\";\nimport FollowersWidget from \"../widgets/FollowersWidget\";\nimport MeetingWidgets from \"../widgets/MeetingWidgets\";\nimport PersonalTargetsWidget from \"../widgets/PersonalTargetsWidget\";\nimport ViewsWidget from \"../widgets/ViewsWidget\";\nimport WelcomeWidget from \"../widgets/WelcomeWidget\";\n\nconst Home = () => {\n  return (\n    <React.Fragment>\n      <AdminAppBar>\n        <AdminToolbar>\n          <RecentNotifications />\n        </AdminToolbar>\n      </AdminAppBar>\n      <Grid container spacing={2}>\n        <Grid item xs={12} md={6} lg={4}>\n          <WelcomeWidget />\n          <AchievementWidget />\n        </Grid>\n        <Grid item xs={12} md={6} lg={4}>\n          <FollowersWidget />\n          <ViewsWidget />\n        </Grid>\n        <Grid item xs={12} md={6} lg={4}>\n          <PersonalTargetsWidget />\n          <MeetingWidgets />\n        </Grid>\n      </Grid>\n    </React.Fragment>\n  );\n};\n\nexport default Home;\n"
  },
  {
    "path": "src/admin/pages/Profile.tsx",
    "content": "import Avatar from \"@material-ui/core/Avatar\";\nimport Box from \"@material-ui/core/Box\";\nimport Fab from \"@material-ui/core/Fab\";\nimport Grid from \"@material-ui/core/Grid\";\nimport Tab from \"@material-ui/core/Tab\";\nimport Tabs from \"@material-ui/core/Tabs\";\nimport Typography from \"@material-ui/core/Typography\";\nimport ExitToAppIcon from \"@material-ui/icons/ExitToApp\";\nimport PersonIcon from \"@material-ui/icons/Person\";\nimport React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { NavLink, Outlet } from \"react-router-dom\";\nimport { useAuth } from \"../../auth/contexts/AuthProvider\";\nimport QueryWrapper from \"../../core/components/QueryWrapper\";\nimport { useSnackbar } from \"../../core/contexts/SnackbarProvider\";\nimport AdminAppBar from \"../components/AdminAppBar\";\nimport AdminToolbar from \"../components/AdminToolbar\";\nimport CircleProgressWidget from \"../widgets/CircleProgressWidget\";\n\nconst profileMenuItems = [\n  {\n    key: \"profile.menu.activity\",\n    path: \"\",\n  },\n  {\n    key: \"profile.menu.info\",\n    path: \"./information\",\n  },\n  {\n    key: \"profile.menu.password\",\n    path: \"./password\",\n  },\n];\n\nconst Profile = () => {\n  const { isLoggingOut, logout, userInfo } = useAuth();\n  const snackbar = useSnackbar();\n  const { t } = useTranslation();\n\n  const handleLogout = () => {\n    logout().catch(() =>\n      snackbar.error(t(\"common.errors.unexpected.subTitle\"))\n    );\n  };\n\n  return (\n    <React.Fragment>\n      <AdminAppBar>\n        <AdminToolbar>\n          <Fab\n            aria-label=\"logout\"\n            color=\"secondary\"\n            disabled={isLoggingOut}\n            onClick={handleLogout}\n          >\n            <ExitToAppIcon />\n          </Fab>\n        </AdminToolbar>\n      </AdminAppBar>\n      <Grid container spacing={12}>\n        <Grid item xs={12} md={4} marginTop={3}>\n          <Box\n            sx={{\n              display: \"flex\",\n              flexDirection: \"column\",\n              alignItems: \"center\",\n              textAlign: \"center\",\n              mb: 6,\n            }}\n          >\n            <Avatar\n              sx={{\n                bgcolor: \"background.paper\",\n                mb: 3,\n                height: 160,\n                width: 160,\n              }}\n            >\n              <PersonIcon sx={{ fontSize: 120 }} />\n            </Avatar>\n            <Typography\n              component=\"div\"\n              variant=\"h4\"\n            >{`${userInfo?.firstName} ${userInfo?.lastName}`}</Typography>\n            <Typography variant=\"body2\">{userInfo?.role}</Typography>\n          </Box>\n          <CircleProgressWidget\n            height={244}\n            title={t(\"profile.completion.title\")}\n            value={75}\n          />\n        </Grid>\n        <Grid item xs={12} md={8} marginTop={3}>\n          <Box sx={{ mb: 4 }}>\n            <Tabs aria-label=\"profile nav tabs\" value={false}>\n              {profileMenuItems.map((item) => (\n                <Tab\n                  key={item.key}\n                  activeClassName=\"Mui-selected\"\n                  end={true}\n                  component={NavLink}\n                  label={t(item.key)}\n                  to={item.path}\n                />\n              ))}\n            </Tabs>\n          </Box>\n          <QueryWrapper>\n            <Outlet />\n          </QueryWrapper>\n        </Grid>\n      </Grid>\n    </React.Fragment>\n  );\n};\n\nexport default Profile;\n"
  },
  {
    "path": "src/admin/pages/ProfileActivity.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport Typography from \"@material-ui/core/Typography\";\nimport Timeline from \"@material-ui/lab/Timeline\";\nimport TimelineConnector from \"@material-ui/lab/TimelineConnector\";\nimport TimelineContent from \"@material-ui/lab/TimelineContent\";\nimport TimelineDot from \"@material-ui/lab/TimelineDot\";\nimport TimelineItem from \"@material-ui/lab/TimelineItem\";\nimport TimelineSeparator from \"@material-ui/lab/TimelineSeparator\";\nimport formatDistanceToNow from \"date-fns/formatDistanceToNow\";\nimport { Trans, useTranslation } from \"react-i18next\";\nimport Empty from \"../../core/components/Empty\";\nimport { useDateLocale } from \"../../core/hooks/useDateLocale\";\nimport { logKeys } from \"../config/activity\";\nimport { useActivityLogs } from \"../hooks/useActivityLogs\";\n\nconst ProfileActivity = () => {\n  const locale = useDateLocale();\n  const { t } = useTranslation();\n\n  const { data } = useActivityLogs();\n\n  if (!data || data.length === 0) {\n    return <Empty title={t(\"profile.activity.empty\")} />;\n  }\n\n  return (\n    <Box sx={{ \"& .MuiTimelineItem-root:before\": { content: \"none\" } }}>\n      <Timeline>\n        {data.map((log) => (\n          <TimelineItem key={log.id}>\n            <TimelineSeparator>\n              <TimelineDot color=\"grey\" />\n              <TimelineConnector color=\"grey\" />\n            </TimelineSeparator>\n            <TimelineContent>\n              <Card>\n                <CardContent>\n                  <Trans\n                    components={{ bold: <strong /> }}\n                    defaults=\"<bold>You</bold> modify resource <bold>{{ resouce }}</bold>\"\n                    i18nKey={logKeys[log.code]}\n                    values={log.params}\n                  />\n                  <Typography component=\"div\" marginTop={1} variant=\"caption\">\n                    {formatDistanceToNow(new Date(log.createdAt), {\n                      addSuffix: true,\n                      locale,\n                    })}\n                  </Typography>\n                </CardContent>\n              </Card>\n            </TimelineContent>\n          </TimelineItem>\n        ))}\n      </Timeline>\n    </Box>\n  );\n};\n\nexport default ProfileActivity;\n"
  },
  {
    "path": "src/admin/pages/ProfileInformation.tsx",
    "content": "import Button from \"@material-ui/core/Button\";\nimport Card from \"@material-ui/core/Card\";\nimport CardActions from \"@material-ui/core/CardActions\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport CardHeader from \"@material-ui/core/CardHeader\";\nimport FormControl from \"@material-ui/core/FormControl\";\nimport FormControlLabel from \"@material-ui/core/FormControlLabel\";\nimport FormLabel from \"@material-ui/core/FormLabel\";\nimport Radio from \"@material-ui/core/Radio\";\nimport RadioGroup from \"@material-ui/core/RadioGroup\";\nimport TextField from \"@material-ui/core/TextField\";\nimport LoadingButton from \"@material-ui/lab/LoadingButton\";\nimport { useFormik } from \"formik\";\nimport { useTranslation } from \"react-i18next\";\nimport * as Yup from \"yup\";\nimport { useUpdateProfileInfo } from \"../../admin/hooks/useUpdateProfileInfo\";\nimport { useSnackbar } from \"../../core/contexts/SnackbarProvider\";\nimport { useProfileInfo } from \"../hooks/useProfileInfo\";\nimport { ProfileInfo } from \"../types/profileInfo\";\n\nconst genders = [\n  { label: \"profile.info.form.gender.options.f\", value: \"F\" },\n  { label: \"profile.info.form.gender.options.m\", value: \"M\" },\n  { label: \"profile.info.form.gender.options.n\", value: \"NC\" },\n];\n\nconst ProfileInformation = () => {\n  const snackbar = useSnackbar();\n  const { t } = useTranslation();\n\n  const { data } = useProfileInfo();\n  const { isUpdating, updateProfileInfo } = useUpdateProfileInfo();\n\n  const formik = useFormik({\n    initialValues: {\n      email: data ? data.email : \"\",\n      firstName: data ? data.firstName : \"\",\n      gender: data ? data.gender : undefined,\n      job: data ? data.job : \"\",\n      lastName: data ? data.lastName : \"\",\n    },\n    validationSchema: Yup.object({\n      email: Yup.string()\n        .email(t(\"common.validations.email\"))\n        .required(t(\"common.validations.required\")),\n      firstName: Yup.string()\n        .max(20, t(\"common.validations.max\", { size: 20 }))\n        .required(t(\"common.validations.required\")),\n      lastName: Yup.string()\n        .max(30, t(\"common.validations.max\", { size: 30 }))\n        .required(t(\"common.validations.required\")),\n    }),\n    onSubmit: (values) => handleSubmit(values),\n  });\n\n  const handleSubmit = async (values: Partial<ProfileInfo>) => {\n    updateProfileInfo({ ...values, id: data?.id } as ProfileInfo)\n      .then(() => {\n        snackbar.success(t(\"profile.notifications.informationUpdated\"));\n      })\n      .catch(() => {\n        snackbar.error(t(\"common.errors.unexpected.subTitle\"));\n      });\n  };\n\n  return (\n    <form onSubmit={formik.handleSubmit} noValidate>\n      <Card>\n        <CardHeader title={t(\"profile.info.title\")} />\n        <CardContent>\n          <TextField\n            margin=\"normal\"\n            required\n            fullWidth\n            id=\"lastName\"\n            label={t(\"profile.info.form.lastName.label\")}\n            name=\"lastName\"\n            autoComplete=\"family-name\"\n            autoFocus\n            disabled={isUpdating}\n            value={formik.values.lastName}\n            onChange={formik.handleChange}\n            error={formik.touched.lastName && Boolean(formik.errors.lastName)}\n            helperText={formik.touched.lastName && formik.errors.lastName}\n          />\n          <TextField\n            margin=\"normal\"\n            required\n            fullWidth\n            id=\"firstName\"\n            label={t(\"profile.info.form.firstName.label\")}\n            name=\"firstName\"\n            autoComplete=\"given-name\"\n            disabled={isUpdating}\n            value={formik.values.firstName}\n            onChange={formik.handleChange}\n            error={formik.touched.firstName && Boolean(formik.errors.firstName)}\n            helperText={formik.touched.firstName && formik.errors.firstName}\n          />\n          <FormControl component=\"fieldset\" margin=\"normal\">\n            <FormLabel component=\"legend\">\n              {t(\"profile.info.form.gender.label\")}\n            </FormLabel>\n            <RadioGroup\n              row\n              aria-label=\"gender\"\n              name=\"gender\"\n              value={formik.values.gender}\n              onChange={formik.handleChange}\n            >\n              {genders.map((gender) => (\n                <FormControlLabel\n                  key={gender.value}\n                  value={gender.value}\n                  control={<Radio />}\n                  label={t(gender.label)}\n                />\n              ))}\n            </RadioGroup>\n          </FormControl>\n          <TextField\n            margin=\"normal\"\n            required\n            fullWidth\n            id=\"email\"\n            label={t(\"profile.info.form.email.label\")}\n            name=\"email\"\n            autoComplete=\"email\"\n            disabled={isUpdating}\n            value={formik.values.email}\n            onChange={formik.handleChange}\n            error={formik.touched.email && Boolean(formik.errors.email)}\n            helperText={formik.touched.email && formik.errors.email}\n          />\n        </CardContent>\n        <CardActions>\n          <Button onClick={() => formik.resetForm()}>\n            {t(\"common.reset\")}\n          </Button>\n          <LoadingButton loading={isUpdating} type=\"submit\" variant=\"contained\">\n            {t(\"common.update\")}\n          </LoadingButton>\n        </CardActions>\n      </Card>\n    </form>\n  );\n};\n\nexport default ProfileInformation;\n"
  },
  {
    "path": "src/admin/pages/ProfilePassword.tsx",
    "content": "import Card from \"@material-ui/core/Card\";\nimport CardActions from \"@material-ui/core/CardActions\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport CardHeader from \"@material-ui/core/CardHeader\";\nimport TextField from \"@material-ui/core/TextField\";\nimport LoadingButton from \"@material-ui/lab/LoadingButton\";\nimport { useFormik } from \"formik\";\nimport { useTranslation } from \"react-i18next\";\nimport * as Yup from \"yup\";\nimport { useUpdatePassword } from \"../../auth/hooks/useUpdatePassword\";\nimport { useSnackbar } from \"../../core/contexts/SnackbarProvider\";\n\nconst ProfilePassword = () => {\n  const snackbar = useSnackbar();\n  const { t } = useTranslation();\n\n  const { isUpdating, updatePassword } = useUpdatePassword();\n\n  const formik = useFormik({\n    initialValues: {\n      oldPassword: \"\",\n      newPassword: \"\",\n      confirmPassword: \"\",\n    },\n    validationSchema: Yup.object({\n      oldPassword: Yup.string()\n        .min(8, t(\"common.validations.min\", { size: 8 }))\n        .required(t(\"common.validations.required\")),\n      newPassword: Yup.string()\n        .min(8, t(\"common.validations.min\", { size: 8 }))\n        .required(t(\"common.validations.required\")),\n      confirmPassword: Yup.string()\n        .oneOf([Yup.ref(\"newPassword\")], t(\"common.validations.passwordMatch\"))\n        .required(t(\"common.validations.required\")),\n    }),\n    onSubmit: (values) =>\n      handleUpdatePassword(values.oldPassword, values.newPassword),\n  });\n\n  const handleUpdatePassword = async (\n    oldPassword: string,\n    newPassword: string\n  ) => {\n    updatePassword({ oldPassword, newPassword })\n      .then(() => {\n        formik.resetForm();\n        snackbar.success(t(\"profile.notifications.passwordChanged\"));\n      })\n      .catch(() => {\n        snackbar.error(t(\"common.errors.unexpected.subTitle\"));\n      });\n  };\n\n  return (\n    <form onSubmit={formik.handleSubmit} noValidate>\n      <Card>\n        <CardHeader title={t(\"profile.password.title\")} />\n        <CardContent>\n          <TextField\n            margin=\"normal\"\n            required\n            fullWidth\n            name=\"oldPassword\"\n            label={t(\"profile.password.form.current.label\")}\n            type=\"password\"\n            id=\"oldPassword\"\n            autoComplete=\"current-password\"\n            disabled={isUpdating}\n            value={formik.values.oldPassword}\n            onChange={formik.handleChange}\n            error={\n              formik.touched.oldPassword && Boolean(formik.errors.oldPassword)\n            }\n            helperText={formik.touched.oldPassword && formik.errors.oldPassword}\n          />\n          <TextField\n            margin=\"normal\"\n            required\n            fullWidth\n            name=\"newPassword\"\n            label={t(\"profile.password.form.new.label\")}\n            type=\"password\"\n            id=\"newPassword\"\n            disabled={isUpdating}\n            value={formik.values.newPassword}\n            onChange={formik.handleChange}\n            error={\n              formik.touched.newPassword && Boolean(formik.errors.newPassword)\n            }\n            helperText={formik.touched.newPassword && formik.errors.newPassword}\n          />\n          <TextField\n            margin=\"normal\"\n            required\n            fullWidth\n            name=\"confirmPassword\"\n            label={t(\"profile.password.form.confirm.label\")}\n            type=\"password\"\n            id=\"confirmPassword\"\n            disabled={isUpdating}\n            value={formik.values.confirmPassword}\n            onChange={formik.handleChange}\n            error={\n              formik.touched.confirmPassword &&\n              Boolean(formik.errors.confirmPassword)\n            }\n            helperText={\n              formik.touched.confirmPassword && formik.errors.confirmPassword\n            }\n          />\n        </CardContent>\n        <CardActions>\n          <LoadingButton type=\"submit\" loading={isUpdating} variant=\"contained\">\n            {t(\"common.update\")}\n          </LoadingButton>\n        </CardActions>\n      </Card>\n    </form>\n  );\n};\n\nexport default ProfilePassword;\n"
  },
  {
    "path": "src/admin/types/activityLog.ts",
    "content": "export interface ActivityLog {\n  id: string;\n  actor: string;\n  code: string;\n  createdAt: number;\n  params?: { [key: string]: string };\n}\n"
  },
  {
    "path": "src/admin/types/notification.ts",
    "content": "export interface Notification {\n  id: string;\n  code: string;\n  createdAt: number;\n  params?: {\n    quantity?: string;\n    user?: string;\n  };\n  unread: boolean;\n}\n"
  },
  {
    "path": "src/admin/types/profileInfo.ts",
    "content": "export interface ProfileInfo {\n  id: string;\n  avatar?: string;\n  email: string;\n  firstName: string;\n  gender?: \"F\" | \"M\" | \"NC\";\n  job: string;\n  lastName: string;\n}\n"
  },
  {
    "path": "src/admin/widgets/AchievementWidget.tsx",
    "content": "import Avatar from \"@material-ui/core/Avatar\";\nimport Button from \"@material-ui/core/Button\";\nimport Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport Typography from \"@material-ui/core/Typography\";\nimport StarIcon from \"@material-ui/icons/Star\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link as RouterLink } from \"react-router-dom\";\nimport { useAuth } from \"../../auth/contexts/AuthProvider\";\n\nconst AchievementWidget = () => {\n  const { userInfo } = useAuth();\n  const { t } = useTranslation();\n\n  return (\n    <Card sx={{ bgcolor: \"primary.main\", color: \"primary.contrastText\" }}>\n      <CardContent\n        sx={{\n          display: \"flex\",\n          flexDirection: \"column\",\n          alignItems: \"center\",\n          textAlign: \"center\",\n        }}\n      >\n        <Avatar sx={{ bgcolor: \"secondary.main\", mb: 3 }}>\n          <StarIcon color=\"primary\" />\n        </Avatar>\n        <Typography gutterBottom variant=\"h5\" component=\"div\">\n          {t(\"admin.home.achievement.title\", { name: userInfo?.firstName })}\n        </Typography>\n        <Typography marginBottom={3} variant=\"body2\">\n          {t(\"admin.home.achievement.description\", {\n            progress: userInfo?.progress,\n          })}\n        </Typography>\n        <Button\n          color=\"secondary\"\n          component={RouterLink}\n          to={`/${process.env.PUBLIC_URL}/admin/profile`}\n          variant=\"contained\"\n        >\n          {t(\"admin.home.achievement.action\")}\n        </Button>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default AchievementWidget;\n"
  },
  {
    "path": "src/admin/widgets/ActivityWidget.tsx",
    "content": "import Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport CardHeader from \"@material-ui/core/CardHeader\";\nimport { useTheme } from \"@material-ui/core/styles\";\nimport { useTranslation } from \"react-i18next\";\nimport { Line, LineChart, ResponsiveContainer, Tooltip, XAxis } from \"recharts\";\n\nconst data = [\n  {\n    name: \"Jan\",\n    pv: 2400,\n  },\n  {\n    name: \"Feb\",\n    pv: 1398,\n  },\n  {\n    name: \"Mar\",\n    pv: 9800,\n  },\n  {\n    name: \"Apr\",\n    pv: 3908,\n  },\n  {\n    name: \"May\",\n    pv: 4800,\n  },\n  {\n    name: \"Jun\",\n    pv: 3800,\n  },\n  {\n    name: \"Jul\",\n    pv: 4300,\n  },\n];\n\nconst ActivityWidget = () => {\n  const { t } = useTranslation();\n  const theme = useTheme();\n\n  return (\n    <Card>\n      <CardHeader title={t(\"dashboard.activity.title\")} />\n      <CardContent>\n        <ResponsiveContainer width=\"99%\" height={244}>\n          <LineChart\n            width={500}\n            height={300}\n            data={data}\n            margin={{\n              top: 5,\n              right: 16,\n              left: 16,\n              bottom: 5,\n            }}\n          >\n            <XAxis\n              axisLine={false}\n              tick={{ fill: theme.palette.text.secondary, fontSize: 12 }}\n              tickLine={false}\n              dataKey=\"name\"\n            />\n            <Tooltip\n              contentStyle={{\n                borderRadius: 16,\n                boxShadow: theme.shadows[3],\n                backgroundColor: theme.palette.background.paper,\n                borderColor: theme.palette.background.paper,\n              }}\n            />\n            <Line\n              name=\"Value\"\n              type=\"monotone\"\n              dataKey=\"pv\"\n              stroke={theme.palette.primary.main}\n              strokeWidth={6}\n              activeDot={{ r: 8 }}\n            />\n          </LineChart>\n        </ResponsiveContainer>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default ActivityWidget;\n"
  },
  {
    "path": "src/admin/widgets/BudgetWidget.tsx",
    "content": "import Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport CardHeader from \"@material-ui/core/CardHeader\";\nimport { useTheme } from \"@material-ui/core/styles\";\nimport { useTranslation } from \"react-i18next\";\nimport {\n  PolarAngleAxis,\n  Radar,\n  RadarChart,\n  ResponsiveContainer,\n  Tooltip,\n} from \"recharts\";\n\nconst data = [\n  {\n    subject: \"Marketing\",\n    A: 110,\n  },\n  {\n    subject: \"Research\",\n    A: 98,\n  },\n  {\n    subject: \"Sales\",\n    A: 86,\n  },\n  {\n    subject: \"Ops\",\n    A: 99,\n  },\n  {\n    subject: \"HR\",\n    A: 85,\n  },\n  {\n    subject: \"Dev\",\n    A: 65,\n  },\n];\n\nconst BudgetWidget = () => {\n  const { t } = useTranslation();\n  const theme = useTheme();\n\n  return (\n    <Card>\n      <CardHeader title={t(\"dashboard.budget.title\")} />\n      <CardContent>\n        <ResponsiveContainer width=\"99%\" height={244}>\n          <RadarChart cx=\"50%\" cy=\"50%\" outerRadius=\"80%\" data={data}>\n            <PolarAngleAxis\n              dataKey=\"subject\"\n              tick={{ fill: theme.palette.text.secondary, fontSize: 14 }}\n            />\n            <Radar\n              name={t(\"dashboard.budget.legend.unit\")}\n              dataKey=\"A\"\n              stroke={theme.palette.primary.main}\n              strokeWidth={8}\n              fill={theme.palette.primary.main}\n            />\n            <Tooltip\n              contentStyle={{\n                borderRadius: 16,\n                boxShadow: theme.shadows[3],\n                backgroundColor: theme.palette.background.paper,\n                borderColor: theme.palette.background.paper,\n              }}\n            />\n          </RadarChart>\n        </ResponsiveContainer>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default BudgetWidget;\n"
  },
  {
    "path": "src/admin/widgets/CircleProgressWidget.tsx",
    "content": "import Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport CardHeader from \"@material-ui/core/CardHeader\";\nimport useTheme from \"@material-ui/core/styles/useTheme\";\nimport {\n  PolarAngleAxis,\n  RadialBar,\n  RadialBarChart,\n  ResponsiveContainer,\n} from \"recharts\";\n\ntype CircleProgressWidgetProps = {\n  height?: number;\n  title: string;\n  value: number;\n};\n\nconst CircleProgressWidget = ({\n  height = 120,\n  title,\n  value,\n}: CircleProgressWidgetProps) => {\n  const theme = useTheme();\n\n  return (\n    <Card>\n      <CardHeader title={title} />\n      <CardContent>\n        <ResponsiveContainer width=\"99%\" height={height}>\n          <RadialBarChart\n            innerRadius=\"85%\"\n            outerRadius=\"85%\"\n            barSize={32}\n            data={[{ fill: theme.palette.primary.main, value }]}\n            startAngle={90}\n            endAngle={-270}\n          >\n            <PolarAngleAxis\n              type=\"number\"\n              domain={[0, 100]}\n              dataKey={\"value\"}\n              angleAxisId={0}\n              tick={false}\n            />\n            <RadialBar\n              cornerRadius={16}\n              label={{\n                fill: theme.palette.text.primary,\n                fontSize: theme.typography.h1.fontSize,\n                fontWeight: theme.typography.h1.fontWeight,\n                position: \"center\",\n              }}\n              background={{ fill: theme.palette.background.default }}\n              dataKey=\"value\"\n            />\n          </RadialBarChart>\n        </ResponsiveContainer>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default CircleProgressWidget;\n"
  },
  {
    "path": "src/admin/widgets/FollowersWidget.tsx",
    "content": "import Avatar from \"@material-ui/core/Avatar\";\nimport Box from \"@material-ui/core/Box\";\nimport Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport Typography from \"@material-ui/core/Typography\";\nimport ArrowDropDownIcon from \"@material-ui/icons/ArrowDropDown\";\nimport ArrowDropUpIcon from \"@material-ui/icons/ArrowDropUp\";\nimport ArrowRightIcon from \"@material-ui/icons/ArrowRight\";\nimport EmojiEmotionsIcon from \"@material-ui/icons/EmojiEmotions\";\nimport FavoriteIcon from \"@material-ui/icons/Favorite\";\nimport ThumbUpIcon from \"@material-ui/icons/ThumbUp\";\nimport React from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nconst socials = [\n  {\n    bgcolor: \"primary.main\",\n    icon: <ThumbUpIcon sx={{ color: \"#fff\" }} />,\n    name: \"Likes\",\n    trend: <ArrowDropUpIcon sx={{ color: \"success.main\" }} />,\n    unitKey: \"admin.home.followers.units.likes\",\n    value: \"26,789\",\n  },\n  {\n    bgcolor: \"error.main\",\n    icon: <FavoriteIcon style={{ color: \"#fff\" }} />,\n    name: \"Love\",\n    trend: <ArrowRightIcon sx={{ color: \"action.disabled\" }} />,\n    unitKey: \"admin.home.followers.units.love\",\n    value: \"6,754\",\n  },\n  {\n    bgcolor: \"warning.main\",\n    icon: <EmojiEmotionsIcon style={{ color: \"#fff\" }} />,\n    name: \"Smiles\",\n    trend: <ArrowDropDownIcon sx={{ color: \"error.main\" }} />,\n    unitKey: \"admin.home.followers.units.smiles\",\n    value: \"52,789\",\n  },\n];\n\nconst FollowersWidget = () => {\n  const { t } = useTranslation();\n\n  return (\n    <React.Fragment>\n      {socials.map((social) => (\n        <Card key={social.name} sx={{ mb: 2 }}>\n          <CardContent sx={{ display: \"flex\", alignItems: \"center\" }}>\n            <Avatar\n              aria-label={`${social.name} avatar`}\n              sx={{ bgcolor: social.bgcolor, mr: 2 }}\n            >\n              {social.icon}\n            </Avatar>\n            <Box sx={{ flexGrow: 1 }}>\n              <Typography component=\"div\" variant=\"h6\">\n                {social.value}\n              </Typography>\n              <Typography variant=\"body2\" color=\"textSecondary\" component=\"div\">\n                {t(social.name)}\n              </Typography>\n            </Box>\n            {social.trend}\n          </CardContent>\n        </Card>\n      ))}\n    </React.Fragment>\n  );\n};\n\nexport default FollowersWidget;\n"
  },
  {
    "path": "src/admin/widgets/MeetingWidgets.tsx",
    "content": "import Avatar from \"@material-ui/core/Avatar\";\nimport Box from \"@material-ui/core/Box\";\nimport Button from \"@material-ui/core/Button\";\nimport Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport IconButton from \"@material-ui/core/IconButton\";\nimport Typography from \"@material-ui/core/Typography\";\nimport AddIcon from \"@material-ui/icons/Add\";\nimport ChevronRightIcon from \"@material-ui/icons/ChevronRight\";\nimport React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link as RouterLink } from \"react-router-dom\";\n\nconst meetings = [\n  {\n    id: \"1\",\n    person: \"Emmy Anderson\",\n    date: \"8:00 - 10:00\",\n    image: \"img/portrait-1.jpg\",\n  },\n  {\n    id: \"2\",\n    person: \"Joy McGlynn\",\n    date: \"11:00 - 12:00\",\n    image: \"img/portrait-2.jpg\",\n  },\n  {\n    id: \"3\",\n    person: \"Mara Dach\",\n    date: \"14:00 - 15:00\",\n    image: \"img/portrait-3.jpg\",\n  },\n];\n\nconst MeetingWidgets = () => {\n  const { t } = useTranslation();\n\n  return (\n    <React.Fragment>\n      <Typography component=\"h2\" marginBottom={3} variant=\"h4\">\n        {t(\"admin.home.meeting.title\")}\n      </Typography>\n      {meetings.map((meeting) => (\n        <Card key={meeting.id} sx={{ mb: 2 }}>\n          <CardContent sx={{ display: \"flex\", alignItems: \"center\" }}>\n            <Avatar\n              alt={`${meeting.person} avatar`}\n              src={meeting.image}\n              sx={{ mr: 2 }}\n            />\n            <Box sx={{ flexGrow: 1 }}>\n              <Typography component=\"div\" variant=\"h6\">\n                {meeting.person}\n              </Typography>\n              <Typography variant=\"body2\" color=\"textSecondary\" component=\"div\">\n                {meeting.date}\n              </Typography>\n            </Box>\n            <IconButton\n              aria-label=\"Go to meeting details\"\n              component={RouterLink}\n              to={`/${process.env.PUBLIC_URL}/admin/calendar`}\n            >\n              <ChevronRightIcon />\n            </IconButton>\n          </CardContent>\n        </Card>\n      ))}\n      <Button\n        aria-label=\"Add new meeting\"\n        color=\"secondary\"\n        component={RouterLink}\n        fullWidth\n        to={`/${process.env.PUBLIC_URL}/admin/calendar`}\n        variant=\"contained\"\n      >\n        <AddIcon />\n      </Button>\n    </React.Fragment>\n  );\n};\n\nexport default MeetingWidgets;\n"
  },
  {
    "path": "src/admin/widgets/OverviewWidget.tsx",
    "content": "import Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport Typography from \"@material-ui/core/Typography\";\n\ntype OverviewWidgetProps = {\n  color?: \"primary\" | \"warning\" | \"error\";\n  description: string;\n  title: string;\n};\n\nconst OverviewWidget = ({ description, title }: OverviewWidgetProps) => {\n  return (\n    <Card>\n      <CardContent sx={{ textAlign: \"center\" }}>\n        <Typography gutterBottom component=\"div\" variant=\"h3\">\n          {title}\n        </Typography>\n        <Typography variant=\"body2\" color=\"textSecondary\" component=\"p\">\n          {description}\n        </Typography>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default OverviewWidget;\n"
  },
  {
    "path": "src/admin/widgets/PersonalTargetsWidget.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport CardHeader from \"@material-ui/core/CardHeader\";\nimport LinearProgress from \"@material-ui/core/LinearProgress\";\nimport List from \"@material-ui/core/List\";\nimport ListItem from \"@material-ui/core/ListItem\";\nimport ListItemText from \"@material-ui/core/ListItemText\";\nimport Typography from \"@material-ui/core/Typography\";\nimport { useTranslation } from \"react-i18next\";\n\nconst targets = [\n  { name: \"Views\", nameKey: \"admin.home.targets.views\", value: 75 },\n  { name: \"Followers\", nameKey: \"admin.home.targets.followers\", value: 50 },\n  { name: \"Income\", nameKey: \"admin.home.targets.income\", value: 25 },\n];\n\nconst PersonalTargetsWidget = () => {\n  const { t } = useTranslation();\n\n  return (\n    <Card sx={{ mb: 4 }}>\n      <CardHeader title={t(\"admin.home.targets.title\")} />\n      <CardContent>\n        <List>\n          {targets.map((target) => (\n            <ListItem disableGutters key={target.name}>\n              <ListItemText>\n                <Box sx={{ display: \"flex\", mb: 1 }}>\n                  <Typography component=\"div\" variant=\"h6\">\n                    {t(target.nameKey)}\n                  </Typography>\n                  <Box sx={{ flexGrow: 1 }} />\n                  <Typography component=\"div\" variant=\"h6\">\n                    {`${target.value}%`}\n                  </Typography>\n                </Box>\n                <LinearProgress\n                  aria-label={`${t(target.nameKey)} progress`}\n                  sx={{\n                    color:\n                      target.value >= 75\n                        ? \"primary.main\"\n                        : target.value <= 25\n                        ? \"error.main\"\n                        : \"warning.main\",\n                  }}\n                  color=\"inherit\"\n                  variant=\"determinate\"\n                  value={target.value}\n                />\n              </ListItemText>\n            </ListItem>\n          ))}\n        </List>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default PersonalTargetsWidget;\n"
  },
  {
    "path": "src/admin/widgets/ProgressWidget.tsx",
    "content": "import Avatar from \"@material-ui/core/Avatar\";\nimport Box from \"@material-ui/core/Box\";\nimport Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport LinearProgress from \"@material-ui/core/LinearProgress\";\nimport Typography from \"@material-ui/core/Typography\";\n\ntype ProgressWidgetProps = {\n  avatar: React.ReactNode;\n  mb?: number;\n  title: string;\n  value: number;\n};\n\nconst ProgressWidget = ({\n  avatar,\n  mb = 0,\n  title,\n  value,\n}: ProgressWidgetProps) => {\n  return (\n    <Card sx={{ mb }}>\n      <CardContent sx={{ display: \"flex\", alignItems: \"center\" }}>\n        <Avatar sx={{ mr: 2 }}>{avatar}</Avatar>\n        <Box sx={{ flexGrow: 1 }}>\n          <Box sx={{ display: \"flex\", mb: 1 }}>\n            <Typography component=\"div\" variant=\"h6\">\n              {title}\n            </Typography>\n            <Box sx={{ flexGrow: 1 }} />\n            <Typography component=\"div\" color=\"textSecondary\">\n              {`${value}%`}\n            </Typography>\n          </Box>\n          <LinearProgress\n            aria-label={`${title} progress`}\n            sx={{ height: 8 }}\n            variant=\"determinate\"\n            value={value}\n          />\n        </Box>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default ProgressWidget;\n"
  },
  {
    "path": "src/admin/widgets/SalesByAgeWidget.tsx",
    "content": "import Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport CardHeader from \"@material-ui/core/CardHeader\";\nimport { useTheme } from \"@material-ui/core/styles\";\nimport { useTranslation } from \"react-i18next\";\nimport {\n  Legend,\n  PolarAngleAxis,\n  RadialBar,\n  RadialBarChart,\n  ResponsiveContainer,\n} from \"recharts\";\n\nconst SalesByAgeWidget = () => {\n  const { t } = useTranslation();\n  const theme = useTheme();\n\n  const data = [\n    {\n      name: \"18-39\",\n      uv: 30,\n      fill: theme.palette.text.secondary,\n    },\n    {\n      name: \"40-59\",\n      uv: 45,\n      fill: theme.palette.error.main,\n    },\n    {\n      name: \"60-79\",\n      uv: 60,\n      fill: theme.palette.warning.main,\n    },\n    {\n      name: \"80+\",\n      uv: 75,\n      fill: theme.palette.primary.main,\n    },\n  ];\n\n  return (\n    <Card>\n      <CardHeader title={t(\"dashboard.salesByAge.title\")} />\n      <CardContent>\n        <ResponsiveContainer width=\"99%\" height={244}>\n          <RadialBarChart\n            barGap={1}\n            innerRadius=\"15%\"\n            outerRadius=\"100%\"\n            barSize={16}\n            data={data}\n          >\n            <PolarAngleAxis\n              type=\"number\"\n              domain={[0, 100]}\n              dataKey={\"value\"}\n              angleAxisId={0}\n              tick={false}\n            />\n            <RadialBar\n              background={{ fill: theme.palette.background.default }}\n              cornerRadius={16}\n              label={{ position: \"insideStart\", fill: \"#fff\", fontWeight: 700 }}\n              dataKey=\"uv\"\n            />\n            <Legend\n              align=\"right\"\n              wrapperStyle={{ fontWeight: 700 }}\n              iconSize={16}\n              layout=\"vertical\"\n              verticalAlign=\"middle\"\n            />\n          </RadialBarChart>\n        </ResponsiveContainer>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default SalesByAgeWidget;\n"
  },
  {
    "path": "src/admin/widgets/SalesByCategoryWidget.tsx",
    "content": "import Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport CardHeader from \"@material-ui/core/CardHeader\";\nimport { useTheme } from \"@material-ui/core/styles\";\nimport { useTranslation } from \"react-i18next\";\nimport { Legend, Pie, PieChart, ResponsiveContainer, Tooltip } from \"recharts\";\n\nconst SalesByCategoryWidget = () => {\n  const { t } = useTranslation();\n  const theme = useTheme();\n\n  const data = [\n    {\n      name: t(\"dashboard.salesByCategory.legend.books\"),\n      fill: theme.palette.primary.main,\n      value: 400,\n    },\n    {\n      name: t(\"dashboard.salesByCategory.legend.movies\"),\n      fill: theme.palette.warning.main,\n      value: 300,\n    },\n    {\n      name: t(\"dashboard.salesByCategory.legend.software\"),\n      fill: theme.palette.error.main,\n      value: 300,\n    },\n  ];\n\n  return (\n    <Card>\n      <CardHeader title={t(\"dashboard.salesByCategory.title\")} />\n      <CardContent>\n        <ResponsiveContainer width=\"99%\" height={244}>\n          <PieChart width={244} height={244}>\n            <Pie\n              dataKey=\"value\"\n              isAnimationActive={false}\n              data={data}\n              cx=\"50%\"\n              cy=\"50%\"\n              outerRadius={80}\n              stroke={theme.palette.background.paper}\n              strokeWidth={8}\n            />\n\n            <Tooltip\n              contentStyle={{\n                borderRadius: 16,\n                boxShadow: theme.shadows[3],\n                backgroundColor: theme.palette.background.paper,\n                borderColor: theme.palette.background.paper,\n              }}\n              itemStyle={{\n                color: theme.palette.text.primary,\n              }}\n            />\n            <Legend wrapperStyle={{ fontSize: 14 }} />\n          </PieChart>\n        </ResponsiveContainer>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default SalesByCategoryWidget;\n"
  },
  {
    "path": "src/admin/widgets/SalesHistoryWidget.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport CardHeader from \"@material-ui/core/CardHeader\";\nimport { useTheme } from \"@material-ui/core/styles\";\nimport Typography from \"@material-ui/core/Typography\";\nimport TrendingUpIcon from \"@material-ui/icons/TrendingUp\";\nimport { useTranslation } from \"react-i18next\";\nimport { Bar, BarChart, ResponsiveContainer } from \"recharts\";\n\ntype SalesWidgetProps = {\n  value: number;\n};\n\nconst SalesWidget = ({ value }: SalesWidgetProps) => {\n  const { t } = useTranslation();\n  const theme = useTheme();\n\n  const data = [\n    {\n      name: \"Mon\",\n      uv: 4000,\n    },\n    {\n      name: \"Tue\",\n      uv: 3000,\n    },\n    {\n      name: \"Wed\",\n      uv: 2000,\n    },\n    {\n      name: \"Thu\",\n      uv: 2780,\n    },\n    {\n      name: \"Fri\",\n      uv: 1890,\n    },\n    {\n      name: \"Sat\",\n      uv: 2390,\n    },\n  ];\n\n  return (\n    <Card>\n      <CardHeader title={t(\"dashboard.salesHistory.title\")} />\n      <CardContent>\n        <ResponsiveContainer width=\"99%\" height={124}>\n          <BarChart\n            width={150}\n            height={40}\n            data={data}\n            margin={{\n              right: 0,\n              left: 0,\n            }}\n          >\n            <Bar\n              dataKey=\"uv\"\n              fill={theme.palette.primary.main}\n              radius={[50, 50, 50, 50]}\n            />\n          </BarChart>\n        </ResponsiveContainer>\n        <Box sx={{ display: \"flex\", alignItems: \"center\", mt: 3 }}>\n          <Box sx={{ flexGrow: 1 }}>\n            <Typography variant=\"h2\" component=\"div\" marginBottom={1}>\n              {value}\n            </Typography>\n            <Typography variant=\"body2\" color=\"textSecondary\" component=\"div\">\n              {t(\"dashboard.salesHistory.unit\")}\n            </Typography>\n          </Box>\n          <TrendingUpIcon sx={{ color: \"text.secondary\" }} />\n        </Box>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default SalesWidget;\n"
  },
  {
    "path": "src/admin/widgets/TeamProgressWidget.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport CardHeader from \"@material-ui/core/CardHeader\";\nimport LinearProgress from \"@material-ui/core/LinearProgress\";\nimport Table from \"@material-ui/core/Table\";\nimport TableBody from \"@material-ui/core/TableBody\";\nimport TableCell from \"@material-ui/core/TableCell\";\nimport TableContainer from \"@material-ui/core/TableContainer\";\nimport TableHead from \"@material-ui/core/TableHead\";\nimport TableRow from \"@material-ui/core/TableRow\";\nimport Typography from \"@material-ui/core/Typography\";\nimport { useTranslation } from \"react-i18next\";\n\nconst teams = [\n  {\n    id: \"1\",\n    color: \"primary.main\",\n    name: \"Marketing Team\",\n    progress: 75,\n    value: 122,\n  },\n  {\n    id: \"2\",\n    color: \"warning.main\",\n    name: \"Operations Team\",\n    progress: 50,\n    value: 82,\n  },\n  {\n    id: \"3\",\n    color: \"error.main\",\n    name: \"Sales Team\",\n    progress: 25,\n    value: 39,\n  },\n  {\n    id: \"4\",\n    color: \"text.secondary\",\n    name: \"Research Team\",\n    progress: 10,\n    value: 9,\n  },\n];\n\nconst TeamProgressWidget = () => {\n  const { t } = useTranslation();\n\n  return (\n    <Card>\n      <CardHeader title={t(\"dashboard.teams.title\")} />\n      <CardContent sx={{ px: 2 }}>\n        <TableContainer>\n          <Table\n            aria-label=\"team progress table\"\n            size=\"small\"\n            sx={{\n              \"& td, & th\": {\n                border: 0,\n              },\n            }}\n          >\n            <TableHead>\n              <TableRow>\n                <TableCell>{t(\"dashboard.teams.columns.team\")}</TableCell>\n                <TableCell>{t(\"dashboard.teams.columns.progress\")}</TableCell>\n                <TableCell align=\"center\">\n                  {t(\"dashboard.teams.columns.value\")}\n                </TableCell>\n              </TableRow>\n            </TableHead>\n            <TableBody>\n              {teams.map((team) => (\n                <TableRow key={team.id}>\n                  <TableCell>\n                    <Typography color=\"text.secondary\" component=\"div\">\n                      {team.name}\n                    </Typography>\n                  </TableCell>\n                  <TableCell>\n                    <Box sx={{ display: \"flex\", alignItems: \"center\" }}>\n                      <Box sx={{ width: \"100%\", mr: 3 }}>\n                        <LinearProgress\n                          aria-label={`${team.name} progress`}\n                          color=\"inherit\"\n                          sx={{ color: team.color }}\n                          value={team.progress}\n                          variant=\"determinate\"\n                        />\n                      </Box>\n                      <Box sx={{ minWidth: 35 }}>\n                        <Typography\n                          component=\"span\"\n                          variant=\"h6\"\n                          color={team.color}\n                        >{`${team.progress}%`}</Typography>\n                      </Box>\n                    </Box>\n                  </TableCell>\n                  <TableCell align=\"center\">{team.value}</TableCell>\n                </TableRow>\n              ))}\n            </TableBody>\n          </Table>\n        </TableContainer>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default TeamProgressWidget;\n"
  },
  {
    "path": "src/admin/widgets/UsersWidget.tsx",
    "content": "import Avatar from \"@material-ui/core/Avatar\";\nimport Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport CardHeader from \"@material-ui/core/CardHeader\";\nimport IconButton from \"@material-ui/core/IconButton\";\nimport List from \"@material-ui/core/List\";\nimport ListItem from \"@material-ui/core/ListItem\";\nimport ListItemAvatar from \"@material-ui/core/ListItemAvatar\";\nimport ListItemSecondaryAction from \"@material-ui/core/ListItemSecondaryAction\";\nimport ListItemText from \"@material-ui/core/ListItemText\";\nimport { useTheme } from \"@material-ui/core/styles\";\nimport ChevronRightIcon from \"@material-ui/icons/ChevronRight\";\nimport PersonIcon from \"@material-ui/icons/Person\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link as RouterLink } from \"react-router-dom\";\n\nconst users = [\n  {\n    id: \"1\",\n    firstName: \"Rhys\",\n    gender: \"M\",\n    lastName: \"Arriaga\",\n    role: \"Admin\",\n  },\n  {\n    id: \"2\",\n    firstName: \"Laura\",\n    gender: \"F\",\n    lastName: \"Core\",\n    role: \"Member\",\n  },\n  {\n    id: \"3\",\n    firstName: \"Joshua\",\n    gender: \"M\",\n    lastName: \"Jagger\",\n    role: \"Member\",\n  },\n];\n\nconst UsersWidget = () => {\n  const theme = useTheme();\n  const { t } = useTranslation();\n\n  return (\n    <Card>\n      <CardHeader title={t(\"dashboard.users.title\")} />\n      <CardContent>\n        <List>\n          {users.map((user) => (\n            <ListItem disableGutters key={user.id}>\n              <ListItemAvatar>\n                <Avatar>\n                  <PersonIcon />\n                </Avatar>\n              </ListItemAvatar>\n              <ListItemText\n                primary={`${user.lastName} ${user.firstName}`}\n                primaryTypographyProps={{\n                  fontWeight: theme.typography.fontWeightMedium,\n                }}\n                secondary={user.role}\n              />\n              <ListItemSecondaryAction>\n                <IconButton\n                  aria-label=\"Go to user details\"\n                  component={RouterLink}\n                  edge=\"end\"\n                  to={`/${process.env.PUBLIC_URL}/admin/user-management`}\n                >\n                  <ChevronRightIcon />\n                </IconButton>\n              </ListItemSecondaryAction>\n            </ListItem>\n          ))}\n        </List>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default UsersWidget;\n"
  },
  {
    "path": "src/admin/widgets/ViewsWidget.tsx",
    "content": "import Avatar from \"@material-ui/core/Avatar\";\nimport Box from \"@material-ui/core/Box\";\nimport Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport IconButton from \"@material-ui/core/IconButton\";\nimport useTheme from \"@material-ui/core/styles/useTheme\";\nimport Typography from \"@material-ui/core/Typography\";\nimport ChevronRightIcon from \"@material-ui/icons/ChevronRight\";\nimport DashboardIcon from \"@material-ui/icons/Dashboard\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link as RouterLink } from \"react-router-dom\";\nimport { Area, AreaChart, ResponsiveContainer, Tooltip, XAxis } from \"recharts\";\n\nconst data = [\n  {\n    name: \"Jan\",\n    fb: 2.5,\n  },\n  {\n    name: \"Feb\",\n    fb: 1.4,\n  },\n  {\n    name: \"Mar\",\n    fb: 6,\n  },\n  {\n    name: \"Avr\",\n    fb: 4,\n  },\n];\n\nconst views = \"6.967.431\";\n\nconst ViewsWidget = () => {\n  const theme = useTheme();\n  const { t } = useTranslation();\n\n  return (\n    <Card>\n      <CardContent>\n        <Typography\n          align=\"center\"\n          component=\"div\"\n          marginBottom={2}\n          variant=\"body2\"\n        >\n          {t(\"admin.home.views.unit\")}\n        </Typography>\n        <Typography align=\"center\" component=\"div\" variant=\"h2\">\n          {views}\n        </Typography>\n        <Box sx={{ height: 224 }}>\n          <ResponsiveContainer width=\"100%\" height=\"100%\">\n            <AreaChart\n              width={500}\n              height={400}\n              data={data}\n              margin={{\n                top: 0,\n                right: 0,\n                left: 0,\n                bottom: 0,\n              }}\n            >\n              <XAxis\n                axisLine={false}\n                dataKey=\"name\"\n                interval=\"preserveStartEnd\"\n                tick={{ fill: theme.palette.text.secondary, fontSize: 12 }}\n                tickLine={false}\n              />\n              <Tooltip\n                contentStyle={{\n                  borderRadius: 16,\n                  boxShadow: theme.shadows[3],\n                  backgroundColor: theme.palette.background.paper,\n                  borderColor: theme.palette.background.paper,\n                }}\n              />\n              <Area\n                type=\"monotone\"\n                dataKey=\"fb\"\n                fill={theme.palette.primary.main}\n                fillOpacity={0.3}\n                stroke={theme.palette.primary.main}\n                strokeWidth={6}\n                activeDot={{ r: 8 }}\n              />\n            </AreaChart>\n          </ResponsiveContainer>\n        </Box>\n        <Card sx={{ bgcolor: \"background.default\", mt: 5 }}>\n          <CardContent sx={{ display: \"flex\", alignItems: \"center\" }}>\n            <Avatar sx={{ bgcolor: \"background.paper\", mr: 2 }}>\n              <DashboardIcon />\n            </Avatar>\n            <Box sx={{ flexGrow: 1 }}>\n              <Typography component=\"div\" variant=\"h6\">\n                {t(\"admin.home.views.action\")}\n              </Typography>\n            </Box>\n            <IconButton\n              aria-label=\"Go to dashboard\"\n              component={RouterLink}\n              to={`/${process.env.PUBLIC_URL}/admin/dashboard`}\n            >\n              <ChevronRightIcon />\n            </IconButton>\n          </CardContent>\n        </Card>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default ViewsWidget;\n"
  },
  {
    "path": "src/admin/widgets/WelcomeWidget.tsx",
    "content": "import Card from \"@material-ui/core/Card\";\nimport CardContent from \"@material-ui/core/CardContent\";\nimport Typography from \"@material-ui/core/Typography\";\nimport { useTranslation } from \"react-i18next\";\nimport { useAuth } from \"../../auth/contexts/AuthProvider\";\nimport { ReactComponent as WelcomeSvg } from \"../../core/assets/welcome.svg\";\nimport SvgContainer from \"../../core/components/SvgContainer\";\n\nconst WelcomeWidget = () => {\n  const { userInfo } = useAuth();\n  const { t } = useTranslation();\n\n  return (\n    <Card elevation={0} sx={{ backgroundColor: \"transparent\", mb: 2 }}>\n      <CardContent>\n        <Typography component=\"div\" gutterBottom variant=\"h1\">\n          {t(\"admin.home.welcome.title\", { name: userInfo?.firstName })}\n        </Typography>\n        <Typography\n          component=\"div\"\n          sx={{ fontWeight: 300, mb: 3 }}\n          variant=\"h1\"\n        >\n          {t(\"admin.home.welcome.subTitle\")}\n        </Typography>\n        <Typography\n          color=\"textSecondary\"\n          component=\"p\"\n          gutterBottom\n          marginBottom={2}\n          variant=\"subtitle1\"\n        >\n          {t(\"admin.home.welcome.message\")}\n        </Typography>\n        <SvgContainer>\n          <WelcomeSvg />\n        </SvgContainer>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default WelcomeWidget;\n"
  },
  {
    "path": "src/auth/contexts/AuthProvider.tsx",
    "content": "import React, { createContext, useContext } from \"react\";\nimport { useLocalStorage } from \"../../core/hooks/useLocalStorage\";\nimport { useLogin } from \"../hooks/useLogin\";\nimport { useLogout } from \"../hooks/useLogout\";\nimport { useUserInfo } from \"../hooks/useUserInfo\";\nimport { UserInfo } from \"../types/userInfo\";\n\ninterface AuthContextInterface {\n  hasRole: (roles?: string[]) => {};\n  isLoggingIn: boolean;\n  isLoggingOut: boolean;\n  login: (email: string, password: string) => Promise<any>;\n  logout: () => Promise<any>;\n  userInfo?: UserInfo;\n}\n\nexport const AuthContext = createContext({} as AuthContextInterface);\n\ntype AuthProviderProps = {\n  children?: React.ReactNode;\n};\n\nconst AuthProvider = ({ children }: AuthProviderProps) => {\n  const [authKey, setAuthKey] = useLocalStorage<string>(\"authkey\", \"\");\n\n  const { isLoggingIn, login } = useLogin();\n  const { isLoggingOut, logout } = useLogout();\n  const { data: userInfo } = useUserInfo(authKey);\n\n  const hasRole = (roles?: string[]) => {\n    if (!roles || roles.length === 0) {\n      return true;\n    }\n    if (!userInfo) {\n      return false;\n    }\n    return roles.includes(userInfo.role);\n  };\n\n  const handleLogin = async (email: string, password: string) => {\n    return login({ email, password })\n      .then((key: string) => {\n        setAuthKey(key);\n        return key;\n      })\n      .catch((err) => {\n        throw err;\n      });\n  };\n\n  const handleLogout = async () => {\n    return logout()\n      .then((data) => {\n        setAuthKey(\"\");\n        return data;\n      })\n      .catch((err) => {\n        throw err;\n      });\n  };\n\n  return (\n    <AuthContext.Provider\n      value={{\n        hasRole,\n        isLoggingIn,\n        isLoggingOut,\n        login: handleLogin,\n        logout: handleLogout,\n        userInfo,\n      }}\n    >\n      {children}\n    </AuthContext.Provider>\n  );\n};\n\nexport function useAuth() {\n  return useContext(AuthContext);\n}\n\nexport default AuthProvider;\n"
  },
  {
    "path": "src/auth/hooks/useForgotPassword.ts",
    "content": "import axios from \"axios\";\nimport { useMutation } from \"react-query\";\n\nconst forgotPassword = async ({ email }: { email: string }) => {\n  const { data } = await axios.post(\"/api/forgot-password\", { email });\n  return data;\n};\n\nexport function useForgotPassword() {\n  const { isLoading, mutateAsync } = useMutation(forgotPassword);\n  return { isLoading, forgotPassword: mutateAsync };\n}\n"
  },
  {
    "path": "src/auth/hooks/useForgotPasswordSubmit.ts",
    "content": "import axios from \"axios\";\nimport { useMutation } from \"react-query\";\n\nconst forgotPasswordSubmit = async ({\n  code,\n  newPassword,\n}: {\n  code: string;\n  newPassword: string;\n}) => {\n  const { data } = await axios.post(\"/api/forgot-password-submit\", {\n    code,\n    newPassword,\n  });\n  return data;\n};\n\nexport function useForgotPasswordSubmit() {\n  const { isLoading, mutateAsync } = useMutation(forgotPasswordSubmit);\n  return { isLoading, forgotPasswordSubmit: mutateAsync };\n}\n"
  },
  {
    "path": "src/auth/hooks/useLogin.ts",
    "content": "import axios from \"axios\";\nimport { useMutation } from \"react-query\";\n\nconst login = async ({\n  email,\n  password,\n}: {\n  email: string;\n  password: string;\n}): Promise<string> => {\n  const { data } = await axios.post(\"/api/login\", { email, password });\n  return data;\n};\n\nexport function useLogin() {\n  const { isLoading, mutateAsync } = useMutation(login);\n\n  return { isLoggingIn: isLoading, login: mutateAsync };\n}\n"
  },
  {
    "path": "src/auth/hooks/useLogout.ts",
    "content": "import axios from \"axios\";\nimport { useMutation } from \"react-query\";\n\nconst logout = async (): Promise<string> => {\n  const { data } = await axios.post(\"/api/logout\");\n  return data;\n};\n\nexport function useLogout() {\n  const { isLoading, mutateAsync } = useMutation(logout);\n\n  return { isLoggingOut: isLoading, logout: mutateAsync };\n}\n"
  },
  {
    "path": "src/auth/hooks/useRegister.ts",
    "content": "import axios from \"axios\";\nimport { useMutation } from \"react-query\";\nimport { UserInfo } from \"../types/userInfo\";\n\nconst register = async (userInfo: UserInfo): Promise<UserInfo> => {\n  const { data } = await axios.post(\"/api/register\", userInfo);\n  return data;\n};\n\nexport function useRegister() {\n  const { isLoading, mutateAsync } = useMutation(register);\n  return { isRegistering: isLoading, register: mutateAsync };\n}\n"
  },
  {
    "path": "src/auth/hooks/useUpdatePassword.ts",
    "content": "import axios from \"axios\";\nimport { useMutation } from \"react-query\";\n\nconst updatePassword = async ({\n  oldPassword,\n  newPassword,\n}: {\n  oldPassword: string;\n  newPassword: string;\n}) => {\n  const { data } = await axios.put(\"/api/password\", {\n    oldPassword,\n    newPassword,\n  });\n  return data;\n};\n\nexport function useUpdatePassword() {\n  const { isLoading, mutateAsync } = useMutation(updatePassword);\n  return { isUpdating: isLoading, updatePassword: mutateAsync };\n}\n"
  },
  {
    "path": "src/auth/hooks/useUserInfo.ts",
    "content": "import axios from \"axios\";\nimport { useQuery } from \"react-query\";\nimport { UserInfo } from \"../types/userInfo\";\n\nconst fetchUserInfo = async (key?: string): Promise<UserInfo> => {\n  const { data } = await axios.get(\"/api/user-info\", { params: { key } });\n  return data;\n};\n\nexport function useUserInfo(key?: string) {\n  return useQuery([\"user-info\", key], () => fetchUserInfo(key), {\n    enabled: !!key,\n  });\n}\n"
  },
  {
    "path": "src/auth/pages/ForgotPassword.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Button from \"@material-ui/core/Button\";\nimport TextField from \"@material-ui/core/TextField\";\nimport Typography from \"@material-ui/core/Typography\";\nimport LoadingButton from \"@material-ui/lab/LoadingButton\";\nimport { useFormik } from \"formik\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link as RouterLink, useNavigate } from \"react-router-dom\";\nimport * as Yup from \"yup\";\nimport BoxedLayout from \"../../core/components/BoxedLayout\";\nimport { useSnackbar } from \"../../core/contexts/SnackbarProvider\";\nimport { useForgotPassword } from \"../hooks/useForgotPassword\";\n\nconst ForgotPassword = () => {\n  const navigate = useNavigate();\n  const snackbar = useSnackbar();\n  const { t } = useTranslation();\n\n  const { forgotPassword, isLoading } = useForgotPassword();\n\n  const formik = useFormik({\n    initialValues: {\n      email: \"\",\n    },\n    validationSchema: Yup.object({\n      email: Yup.string()\n        .email(t(\"common.validations.email\"))\n        .required(t(\"common.validations.required\")),\n    }),\n    onSubmit: ({ email }) => handleForgotPassword(email),\n  });\n\n  const handleForgotPassword = async (email: string) => {\n    forgotPassword({ email })\n      .then(() => {\n        snackbar.success(t(\"auth.forgotPassword.notifications.success\"));\n        navigate(`/${process.env.PUBLIC_URL}/forgot-password-submit`);\n      })\n      .catch(() => {\n        snackbar.error(t(\"common.errors.unexpected.subTitle\"));\n      });\n  };\n\n  return (\n    <BoxedLayout>\n      <Typography component=\"h1\" variant=\"h5\">\n        {t(\"auth.forgotPassword.title\")}\n      </Typography>\n      <Typography marginTop={3}>{t(\"auth.forgotPassword.subTitle\")}</Typography>\n      <Box\n        component=\"form\"\n        marginTop={3}\n        noValidate\n        onSubmit={formik.handleSubmit}\n      >\n        <TextField\n          margin=\"normal\"\n          required\n          fullWidth\n          id=\"email\"\n          label={t(\"auth.forgotPassword.form.email.label\")}\n          name=\"email\"\n          autoComplete=\"email\"\n          autoFocus\n          disabled={isLoading}\n          value={formik.values.email}\n          onChange={formik.handleChange}\n          error={formik.touched.email && Boolean(formik.errors.email)}\n          helperText={formik.touched.email && formik.errors.email}\n        />\n        <LoadingButton\n          type=\"submit\"\n          fullWidth\n          variant=\"contained\"\n          color=\"primary\"\n          disabled={isLoading}\n          loading={isLoading}\n          sx={{ mt: 2 }}\n        >\n          {t(\"auth.forgotPassword.form.action\")}\n        </LoadingButton>\n        <Button\n          component={RouterLink}\n          to={`/${process.env.PUBLIC_URL}/login`}\n          color=\"primary\"\n          fullWidth\n          sx={{ mt: 2 }}\n        >\n          {t(\"auth.forgotPassword.form.back\")}\n        </Button>\n      </Box>\n    </BoxedLayout>\n  );\n};\n\nexport default ForgotPassword;\n"
  },
  {
    "path": "src/auth/pages/ForgotPasswordSubmit.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Button from \"@material-ui/core/Button\";\nimport TextField from \"@material-ui/core/TextField\";\nimport Typography from \"@material-ui/core/Typography\";\nimport LoadingButton from \"@material-ui/lab/LoadingButton\";\nimport { useFormik } from \"formik\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link, useNavigate } from \"react-router-dom\";\nimport * as Yup from \"yup\";\nimport BoxedLayout from \"../../core/components/BoxedLayout\";\nimport { useSnackbar } from \"../../core/contexts/SnackbarProvider\";\nimport { useForgotPasswordSubmit } from \"../hooks/useForgotPasswordSubmit\";\n\nconst ForgotPasswordSubmit = () => {\n  const navigate = useNavigate();\n  const snackbar = useSnackbar();\n  const { t } = useTranslation();\n\n  const { forgotPasswordSubmit, isLoading } = useForgotPasswordSubmit();\n\n  const formik = useFormik({\n    initialValues: {\n      code: \"\",\n      newPassword: \"\",\n      confirmPassword: \"\",\n    },\n    validationSchema: Yup.object({\n      code: Yup.string().required(t(\"common.validations.required\")),\n      newPassword: Yup.string().required(t(\"common.validations.required\")),\n      confirmPassword: Yup.string().required(t(\"common.validations.required\")),\n    }),\n    onSubmit: ({ code, newPassword }) =>\n      handleSubmitPassword(code, newPassword),\n  });\n\n  const handleSubmitPassword = async (code: string, newPassword: string) => {\n    forgotPasswordSubmit({ code, newPassword })\n      .then(() => {\n        snackbar.success(t(\"auth.forgotPasswordSubmit.notifications.success\"));\n        navigate(`/${process.env.PUBLIC_URL}/login`);\n      })\n      .catch(() => {\n        snackbar.error(t(\"common.errors.unexpected.subTitle\"));\n      });\n  };\n\n  return (\n    <BoxedLayout>\n      <Typography component=\"h1\" variant=\"h5\">\n        {t(\"auth.forgotPasswordSubmit.title\")}\n      </Typography>\n      <Typography marginTop={3}>\n        {t(\"auth.forgotPasswordSubmit.subTitle\")}\n      </Typography>\n      <Box\n        component=\"form\"\n        marginTop={3}\n        noValidate\n        onSubmit={formik.handleSubmit}\n      >\n        <TextField\n          margin=\"normal\"\n          required\n          fullWidth\n          id=\"code\"\n          label={t(\"auth.forgotPasswordSubmit.form.code.label\")}\n          name=\"code\"\n          autoFocus\n          disabled={isLoading}\n          value={formik.values.code}\n          onChange={formik.handleChange}\n          error={formik.touched.code && Boolean(formik.errors.code)}\n          helperText={formik.touched.code && formik.errors.code}\n        />\n        <TextField\n          margin=\"normal\"\n          required\n          fullWidth\n          name=\"newPassword\"\n          label={t(\"auth.forgotPasswordSubmit.form.newPassword.label\")}\n          type=\"password\"\n          id=\"newPassword\"\n          disabled={isLoading}\n          value={formik.values.newPassword}\n          onChange={formik.handleChange}\n          error={\n            formik.touched.newPassword && Boolean(formik.errors.newPassword)\n          }\n          helperText={formik.touched.newPassword && formik.errors.newPassword}\n        />\n        <TextField\n          margin=\"normal\"\n          required\n          fullWidth\n          name=\"confirmPassword\"\n          label={t(\"auth.forgotPasswordSubmit.form.confirmPassword.label\")}\n          type=\"password\"\n          id=\"confirmPassword\"\n          disabled={isLoading}\n          value={formik.values.confirmPassword}\n          onChange={formik.handleChange}\n          error={\n            formik.touched.confirmPassword &&\n            Boolean(formik.errors.confirmPassword)\n          }\n          helperText={\n            formik.touched.confirmPassword && formik.errors.confirmPassword\n          }\n        />\n        <LoadingButton\n          type=\"submit\"\n          fullWidth\n          variant=\"contained\"\n          color=\"primary\"\n          disabled={isLoading}\n          loading={isLoading}\n          sx={{ mt: 2 }}\n        >\n          {t(\"auth.forgotPasswordSubmit.form.action\")}\n        </LoadingButton>\n        <Button\n          component={Link}\n          to={`/${process.env.PUBLIC_URL}/login`}\n          color=\"primary\"\n          fullWidth\n          sx={{ mt: 2 }}\n        >\n          {t(\"auth.forgotPasswordSubmit.form.back\")}\n        </Button>\n      </Box>\n    </BoxedLayout>\n  );\n};\n\nexport default ForgotPasswordSubmit;\n"
  },
  {
    "path": "src/auth/pages/Login.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Button from \"@material-ui/core/Button\";\nimport Grid from \"@material-ui/core/Grid\";\nimport Link from \"@material-ui/core/Link\";\nimport Paper from \"@material-ui/core/Paper\";\nimport TextField from \"@material-ui/core/TextField\";\nimport Typography from \"@material-ui/core/Typography\";\nimport LoadingButton from \"@material-ui/lab/LoadingButton\";\nimport { useFormik } from \"formik\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link as RouterLink, useNavigate } from \"react-router-dom\";\nimport * as Yup from \"yup\";\nimport BoxedLayout from \"../../core/components/BoxedLayout\";\nimport { useSnackbar } from \"../../core/contexts/SnackbarProvider\";\nimport { useAuth } from \"../contexts/AuthProvider\";\n\nconst Login = () => {\n  const { isLoggingIn, login } = useAuth();\n  const navigate = useNavigate();\n  const snackbar = useSnackbar();\n  const { t } = useTranslation();\n\n  const handleLogin = (email: string, password: string) => {\n    login(email, password)\n      .then(() =>\n        navigate(`/${process.env.PUBLIC_URL}/admin`, { replace: true })\n      )\n      .catch(() => snackbar.error(t(\"common.errors.unexpected.subTitle\")));\n  };\n\n  const formik = useFormik({\n    initialValues: {\n      email: \"demo@example.com\",\n      password: \"guWEK<'r/-47-XG3\",\n    },\n    validationSchema: Yup.object({\n      email: Yup.string()\n        .email(t(\"common.validations.email\"))\n        .required(t(\"common.validations.required\")),\n      password: Yup.string()\n        .min(8, t(\"common.validations.min\", { size: 8 }))\n        .required(t(\"common.validations.required\")),\n    }),\n    onSubmit: (values) => handleLogin(values.email, values.password),\n  });\n\n  return (\n    <Grid container component=\"main\" sx={{ height: \"100vh\" }}>\n      <Grid\n        item\n        xs={false}\n        sm={4}\n        md={7}\n        sx={{\n          backgroundImage: \"url(./img/startup.svg)\",\n          backgroundRepeat: \"no-repeat\",\n          bgcolor: \"background.default\",\n          backgroundSize: \"cover\",\n          backgroundPosition: \"center\",\n        }}\n      />\n      <Grid item xs={12} sm={8} md={5} component={Paper} square>\n        <BoxedLayout>\n          <Typography component=\"h1\" variant=\"h5\">\n            {t(\"auth.login.title\")}\n          </Typography>\n          <Box\n            component=\"form\"\n            marginTop={3}\n            noValidate\n            onSubmit={formik.handleSubmit}\n          >\n            <TextField\n              margin=\"normal\"\n              variant=\"filled\"\n              required\n              fullWidth\n              id=\"email\"\n              label={t(\"auth.login.form.email.label\")}\n              name=\"email\"\n              autoComplete=\"email\"\n              autoFocus\n              disabled={isLoggingIn}\n              value={formik.values.email}\n              onChange={formik.handleChange}\n              error={formik.touched.email && Boolean(formik.errors.email)}\n              helperText={formik.touched.email && formik.errors.email}\n            />\n            <TextField\n              margin=\"normal\"\n              variant=\"filled\"\n              required\n              fullWidth\n              name=\"password\"\n              label={t(\"auth.login.form.password.label\")}\n              type=\"password\"\n              id=\"password\"\n              autoComplete=\"current-password\"\n              disabled={isLoggingIn}\n              value={formik.values.password}\n              onChange={formik.handleChange}\n              error={formik.touched.password && Boolean(formik.errors.password)}\n              helperText={formik.touched.password && formik.errors.password}\n            />\n            <Box sx={{ textAlign: \"right\" }}>\n              <Link\n                component={RouterLink}\n                to={`/${process.env.PUBLIC_URL}/forgot-password`}\n                variant=\"body2\"\n              >\n                {t(\"auth.login.forgotPasswordLink\")}\n              </Link>\n            </Box>\n            <LoadingButton\n              type=\"submit\"\n              fullWidth\n              loading={isLoggingIn}\n              variant=\"contained\"\n              sx={{ mt: 3 }}\n            >\n              {t(\"auth.login.submit\")}\n            </LoadingButton>\n            <Button\n              component={RouterLink}\n              to={`/${process.env.PUBLIC_URL}/register`}\n              color=\"primary\"\n              fullWidth\n              sx={{ mt: 2 }}\n            >\n              {t(\"auth.login.newAccountLink\")}\n            </Button>\n          </Box>\n        </BoxedLayout>\n      </Grid>\n    </Grid>\n  );\n};\n\nexport default Login;\n"
  },
  {
    "path": "src/auth/pages/Register.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Button from \"@material-ui/core/Button\";\nimport FormControl from \"@material-ui/core/FormControl\";\nimport FormControlLabel from \"@material-ui/core/FormControlLabel\";\nimport FormLabel from \"@material-ui/core/FormLabel\";\nimport Radio from \"@material-ui/core/Radio\";\nimport RadioGroup from \"@material-ui/core/RadioGroup\";\nimport TextField from \"@material-ui/core/TextField\";\nimport Typography from \"@material-ui/core/Typography\";\nimport LoadingButton from \"@material-ui/lab/LoadingButton\";\nimport { useFormik } from \"formik\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link, useNavigate } from \"react-router-dom\";\nimport * as Yup from \"yup\";\nimport BoxedLayout from \"../../core/components/BoxedLayout\";\nimport { useSnackbar } from \"../../core/contexts/SnackbarProvider\";\nimport { useRegister } from \"../hooks/useRegister\";\nimport { UserInfo } from \"../types/userInfo\";\n\nconst genders = [\n  { label: \"auth.register.form.gender.options.f\", value: \"F\" },\n  { label: \"auth.register.form.gender.options.m\", value: \"M\" },\n  { label: \"auth.register.form.gender.options.n\", value: \"NC\" },\n];\n\nconst Register = () => {\n  const navigate = useNavigate();\n  const snackbar = useSnackbar();\n  const { t } = useTranslation();\n\n  const { isRegistering, register } = useRegister();\n\n  const formik = useFormik({\n    initialValues: {\n      email: \"\",\n      firstName: \"\",\n      gender: \"F\",\n      lastName: \"\",\n    },\n    validationSchema: Yup.object({\n      email: Yup.string()\n        .email(\"Invalid email address\")\n        .required(t(\"common.validations.required\")),\n      firstName: Yup.string()\n        .max(20, t(\"common.validations.max\", { size: 20 }))\n        .required(t(\"common.validations.required\")),\n      lastName: Yup.string()\n        .max(30, t(\"common.validations.max\", { size: 30 }))\n        .required(t(\"common.validations.required\")),\n    }),\n    onSubmit: (values) => handleRegister(values),\n  });\n\n  const handleRegister = async (values: Partial<UserInfo>) => {\n    register(values as UserInfo)\n      .then(() => {\n        snackbar.success(t(\"auth.register.notifications.success\"));\n        navigate(`/${process.env.PUBLIC_URL}/login`);\n      })\n      .catch(() => {\n        snackbar.error(t(\"common.errors.unexpected.subTitle\"));\n      });\n  };\n\n  return (\n    <BoxedLayout>\n      <Typography component=\"h1\" variant=\"h5\">\n        {t(\"auth.register.title\")}\n      </Typography>\n      <Box\n        component=\"form\"\n        marginTop={3}\n        noValidate\n        onSubmit={formik.handleSubmit}\n      >\n        <TextField\n          margin=\"normal\"\n          required\n          fullWidth\n          id=\"lastName\"\n          label={t(\"auth.register.form.lastName.label\")}\n          name=\"lastName\"\n          autoComplete=\"family-name\"\n          autoFocus\n          disabled={isRegistering}\n          value={formik.values.lastName}\n          onChange={formik.handleChange}\n          error={formik.touched.lastName && Boolean(formik.errors.lastName)}\n          helperText={formik.touched.lastName && formik.errors.lastName}\n        />\n        <TextField\n          margin=\"normal\"\n          required\n          fullWidth\n          id=\"firstName\"\n          label={t(\"auth.register.form.firstName.label\")}\n          name=\"firstName\"\n          autoComplete=\"given-name\"\n          disabled={isRegistering}\n          value={formik.values.firstName}\n          onChange={formik.handleChange}\n          error={formik.touched.firstName && Boolean(formik.errors.firstName)}\n          helperText={formik.touched.firstName && formik.errors.firstName}\n        />\n        <FormControl component=\"fieldset\" margin=\"normal\">\n          <FormLabel component=\"legend\">\n            {t(\"auth.register.form.gender.label\")}\n          </FormLabel>\n          <RadioGroup\n            row\n            aria-label=\"gender\"\n            name=\"gender\"\n            value={formik.values.gender}\n            onChange={formik.handleChange}\n          >\n            {genders.map((gender) => (\n              <FormControlLabel\n                control={<Radio />}\n                key={gender.value}\n                disabled={isRegistering}\n                label={t(gender.label)}\n                value={gender.value}\n              />\n            ))}\n          </RadioGroup>\n        </FormControl>\n        <TextField\n          margin=\"normal\"\n          required\n          fullWidth\n          id=\"email\"\n          label={t(\"auth.register.form.email.label\")}\n          name=\"email\"\n          autoComplete=\"email\"\n          disabled={isRegistering}\n          value={formik.values.email}\n          onChange={formik.handleChange}\n          error={formik.touched.email && Boolean(formik.errors.email)}\n          helperText={formik.touched.email && formik.errors.email}\n        />\n        <LoadingButton\n          type=\"submit\"\n          fullWidth\n          variant=\"contained\"\n          color=\"primary\"\n          disabled={isRegistering}\n          loading={isRegistering}\n          sx={{ mt: 2 }}\n        >\n          {t(\"auth.register.submit\")}\n        </LoadingButton>\n        <Button\n          component={Link}\n          to={`/${process.env.PUBLIC_URL}/login`}\n          color=\"primary\"\n          fullWidth\n          sx={{ mt: 2 }}\n        >\n          {t(\"auth.register.back\")}\n        </Button>\n      </Box>\n    </BoxedLayout>\n  );\n};\n\nexport default Register;\n"
  },
  {
    "path": "src/auth/types/userInfo.ts",
    "content": "export interface UserInfo {\n  id: string;\n  avatar?: string;\n  email: string;\n  firstName: string;\n  job: string;\n  lastName: string;\n  progress: number;\n  role: string;\n}\n"
  },
  {
    "path": "src/calendar/components/Calendar.tsx",
    "content": "import FullCalendar, {\n  CalendarOptions,\n  EventClickArg,\n} from \"@fullcalendar/react\";\nimport dayGridPlugin from \"@fullcalendar/daygrid\";\nimport Box from \"@material-ui/core/Box\";\nimport Button from \"@material-ui/core/Button\";\nimport IconButton from \"@material-ui/core/IconButton\";\nimport {\n  alpha,\n  experimentalStyled as styled,\n  useTheme,\n} from \"@material-ui/core/styles\";\nimport Typography from \"@material-ui/core/Typography\";\nimport ArrowLeftIcon from \"@material-ui/icons/ArrowLeft\";\nimport ArrowRightIcon from \"@material-ui/icons/ArrowRight\";\nimport EventIcon from \"@material-ui/icons/Event\";\nimport React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Event, eventColors } from \"../types/event\";\n\nconst StyledWrapper = styled(\"div\")(\n  ({ theme }) => `\n  .fc-theme-standard .fc-scrollgrid {\n    border-color: ${theme.palette.divider};\n  }\n\n  .fc th {\n    border-right: none;\n    border-left: none;\n    padding: 10px 0;\n  }\n\n  .fc-theme-standard .fc-scrollgrid {\n    border-right: none;\n    border-left: none;\n    border-bottom: none;\n  }\n\n  .fc-theme-standard td, .fc-theme-standard th {\n    border-right: none;\n  }\n\n  .fc-theme-standard td, .fc-theme-standard th {\n    border-color: ${theme.palette.divider};\n  }\n\n  .fc .fc-daygrid-day-number {\n    color: ${theme.palette.text.secondary};\n    font-size: 14px;\n    font-weight: ${theme.typography.fontWeightBold};\n    padding: 12px;\n  }\n\n  .fc .fc-daygrid-day.fc-day-today {\n    background-color: ${alpha(theme.palette.primary.main, 0.1)};\n  }\n`\n);\n\ntype CalendarProps = {\n  events: Event[];\n  onEventClick: (event?: Event) => void;\n} & CalendarOptions;\n\nconst Calendar = ({\n  events,\n  onEventClick,\n  ...calendarProps\n}: CalendarProps) => {\n  const theme = useTheme();\n  const { i18n, t } = useTranslation();\n\n  const [viewTitle, setViewTitle] = useState(\"\");\n  const [calendarRef, setCalendarRef] = useState<FullCalendar | null>(null);\n\n  const onCalendarRefSet = useCallback((ref) => {\n    if (ref !== null) {\n      setCalendarRef(ref);\n    }\n  }, []);\n\n  const handleEventClick = (arg: EventClickArg) => {\n    if (onEventClick) {\n      const event = events.find((e) => e.id === arg.event.id);\n      onEventClick(event);\n    }\n  };\n\n  const handleNext = () => {\n    if (calendarRef) {\n      calendarRef.getApi().next();\n      setViewTitle(calendarRef.getApi().getCurrentData().viewTitle);\n    }\n  };\n\n  const handlePrev = () => {\n    if (calendarRef) {\n      calendarRef.getApi().prev();\n      setViewTitle(calendarRef.getApi().getCurrentData().viewTitle);\n    }\n  };\n\n  const handleToday = () => {\n    if (calendarRef) {\n      calendarRef.getApi().today();\n      setViewTitle(calendarRef.getApi().getCurrentData().viewTitle);\n    }\n  };\n\n  useEffect(() => {\n    if (calendarRef) {\n      setViewTitle(calendarRef.getApi().getCurrentData().viewTitle);\n    }\n  }, [calendarRef, i18n.language]);\n\n  const eventSource = useMemo(() => {\n    return events.map((event: Event) => {\n      if (event.color && eventColors.includes(event.color)) {\n        return { ...event, color: theme.palette[event.color].main };\n      }\n      return event;\n    });\n  }, [events, theme]);\n\n  return (\n    <React.Fragment>\n      {/* Start - Custom Header Bar */}\n      <Box\n        sx={{\n          display: \"flex\",\n          justifyContent: \"space-between\",\n          alignItems: \"center\",\n          px: 3,\n          py: 2,\n        }}\n      >\n        <Typography sx={{ display: \"inline-flex\" }} variant=\"h5\">\n          <EventIcon sx={{ mr: 2 }} />\n          {viewTitle}\n        </Typography>\n        <Box>\n          <IconButton\n            aria-label=\"previous\"\n            component=\"span\"\n            onClick={handlePrev}\n          >\n            <ArrowLeftIcon />\n          </IconButton>\n          <Button onClick={handleToday}>{t(\"common.today\")}</Button>\n          <IconButton\n            aria-label=\"next\"\n            component=\"span\"\n            edge=\"end\"\n            onClick={handleNext}\n          >\n            <ArrowRightIcon />\n          </IconButton>\n        </Box>\n      </Box>\n      {/* End - Custom Header Bar */}\n      <StyledWrapper>\n        <FullCalendar\n          headerToolbar={false}\n          plugins={[dayGridPlugin]}\n          initialView=\"dayGridMonth\"\n          locale={i18n.language}\n          ref={onCalendarRefSet}\n          events={eventSource}\n          eventClick={handleEventClick}\n          eventTimeFormat={{\n            hour: \"numeric\",\n            minute: \"2-digit\",\n            meridiem: false,\n          }}\n          {...calendarProps}\n        />\n      </StyledWrapper>\n    </React.Fragment>\n  );\n};\n\nexport default Calendar;\n"
  },
  {
    "path": "src/calendar/components/EventDialog.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Button from \"@material-ui/core/Button\";\nimport Dialog from \"@material-ui/core/Dialog\";\nimport DialogActions from \"@material-ui/core/DialogActions\";\nimport DialogContent from \"@material-ui/core/DialogContent\";\nimport DialogTitle from \"@material-ui/core/DialogTitle\";\nimport FormControl from \"@material-ui/core/FormControl\";\nimport FormLabel from \"@material-ui/core/FormLabel\";\nimport IconButton from \"@material-ui/core/IconButton\";\nimport Radio from \"@material-ui/core/Radio\";\nimport RadioGroup from \"@material-ui/core/RadioGroup\";\nimport TextField from \"@material-ui/core/TextField\";\nimport DeleteIcon from \"@material-ui/icons/Delete\";\nimport LoadingButton from \"@material-ui/lab/LoadingButton\";\nimport MobileDateTimePicker from \"@material-ui/lab/MobileDateTimePicker\";\nimport { getTime } from \"date-fns\";\nimport { useFormik } from \"formik\";\nimport { useTranslation } from \"react-i18next\";\nimport * as Yup from \"yup\";\nimport { Event, EventColor, eventColors } from \"../types/event\";\n\ntype EventDialogProps = {\n  onAdd: (event: Partial<Event>) => void;\n  onClose: () => void;\n  onDelete: (eventId: string) => void;\n  onUpdate: (event: Event) => void;\n  open: boolean;\n  processing: boolean;\n  event?: Event;\n};\n\ntype EventFormValues = {\n  title: string;\n  description?: string;\n  start: Date;\n  end: Date;\n  color?: EventColor;\n};\n\nconst EventDialog = ({\n  onAdd,\n  onClose,\n  onDelete,\n  onUpdate,\n  open,\n  processing,\n  event,\n}: EventDialogProps) => {\n  const { t } = useTranslation();\n\n  const editMode = Boolean(event && event.id);\n\n  const convertFormValues = (values: EventFormValues): Partial<Event> => {\n    return {\n      ...values,\n      start: getTime(values.start),\n      end: getTime(values.end),\n    };\n  };\n\n  const handleSubmit = (values: EventFormValues) => {\n    const newEvent = convertFormValues(values);\n    if (event && event.id) {\n      onUpdate({ ...newEvent, id: event.id } as Event);\n    } else {\n      onAdd(newEvent);\n    }\n  };\n\n  const formik = useFormik({\n    initialValues: {\n      title: event ? event.title : \"\",\n      description: event ? event.description : undefined,\n      start: event ? new Date(event.start) : new Date(),\n      end: event ? new Date(event.end) : new Date(),\n      color: event ? event.color : \"primary\",\n    },\n    validationSchema: Yup.object({\n      title: Yup.string()\n        .max(30, t(\"common.validations.max\", { size: 30 }))\n        .required(t(\"common.validations.required\")),\n      description: Yup.string().max(\n        100,\n        t(\"common.validations.max\", { size: 100 })\n      ),\n      start: Yup.date().required(t(\"common.validations.required\")),\n      end: Yup.date().required(t(\"common.validations.required\")),\n      color: Yup.string(),\n    }),\n    onSubmit: handleSubmit,\n  });\n\n  return (\n    <Dialog open={open} onClose={onClose} aria-labelledby=\"event-dialog-title\">\n      <form onSubmit={formik.handleSubmit} noValidate>\n        <DialogTitle id=\"event-dialog-title\">\n          {editMode\n            ? t(\"calendar.modal.edit.title\")\n            : t(\"calendar.modal.add.title\")}\n        </DialogTitle>\n        <DialogContent>\n          <TextField\n            margin=\"normal\"\n            required\n            fullWidth\n            id=\"title\"\n            label={t(\"calendar.form.title.label\")}\n            name=\"title\"\n            autoFocus\n            disabled={processing}\n            value={formik.values.title}\n            onChange={formik.handleChange}\n            error={formik.touched.title && Boolean(formik.errors.title)}\n            helperText={formik.touched.title && formik.errors.title}\n          />\n          <TextField\n            margin=\"normal\"\n            fullWidth\n            id=\"description\"\n            label={t(\"calendar.form.description.label\")}\n            name=\"description\"\n            disabled={processing}\n            value={formik.values.description}\n            onChange={formik.handleChange}\n            error={\n              formik.touched.description && Boolean(formik.errors.description)\n            }\n            helperText={formik.touched.description && formik.errors.description}\n          />\n          <MobileDateTimePicker\n            label={t(\"calendar.form.start.label\")}\n            inputFormat=\"dd/MM/yyyy H:mm\"\n            value={formik.values.start}\n            onChange={(date: Date | null) =>\n              formik.setFieldValue(\"start\", date)\n            }\n            renderInput={(params) => (\n              <TextField\n                {...params}\n                id=\"start\"\n                disabled={processing}\n                fullWidth\n                margin=\"normal\"\n                name=\"start\"\n              />\n            )}\n          />\n          <MobileDateTimePicker\n            label={t(\"calendar.form.end.label\")}\n            inputFormat=\"dd/MM/yyyy H:mm\"\n            value={formik.values.end}\n            onChange={(date: Date | null) => formik.setFieldValue(\"end\", date)}\n            renderInput={(params) => (\n              <TextField\n                {...params}\n                id=\"end\"\n                disabled={processing}\n                fullWidth\n                margin=\"normal\"\n                name=\"end\"\n              />\n            )}\n          />\n          <FormControl component=\"fieldset\" margin=\"normal\">\n            <FormLabel component=\"legend\">\n              {t(\"calendar.form.color.label\")}\n            </FormLabel>\n            <RadioGroup\n              row\n              aria-label=\"color\"\n              name=\"color\"\n              value={formik.values.color}\n              onChange={formik.handleChange}\n            >\n              {eventColors.map((color) => (\n                <Radio\n                  key={color}\n                  disabled={processing}\n                  sx={{\n                    color: `${color}.main`,\n                    \"&.Mui-checked\": {\n                      color: `${color}.main`,\n                    },\n                  }}\n                  value={color}\n                />\n              ))}\n            </RadioGroup>\n          </FormControl>\n        </DialogContent>\n        <DialogActions>\n          {event && event.id && (\n            <IconButton\n              aria-label=\"delete event\"\n              onClick={() => onDelete(event.id)}\n              disabled={processing}\n            >\n              <DeleteIcon />\n            </IconButton>\n          )}\n          <Box sx={{ flexGrow: 1 }} />\n          <Button onClick={onClose}>{t(\"common.cancel\")}</Button>\n          <LoadingButton loading={processing} type=\"submit\" variant=\"contained\">\n            {editMode\n              ? t(\"calendar.modal.edit.action\")\n              : t(\"calendar.modal.add.action\")}\n          </LoadingButton>\n        </DialogActions>\n      </form>\n    </Dialog>\n  );\n};\n\nexport default EventDialog;\n"
  },
  {
    "path": "src/calendar/hooks/useAddEvent.ts",
    "content": "import axios from \"axios\";\nimport { useMutation, useQueryClient } from \"react-query\";\nimport { addOne } from \"../../core/utils/crudUtils\";\nimport { Event } from \"../types/event\";\n\nconst addEvent = async (event: Event): Promise<Event> => {\n  const { data } = await axios.post(\"/api/events\", event);\n  return data;\n};\n\nexport function useAddEvent() {\n  const queryClient = useQueryClient();\n\n  const { isLoading, mutateAsync } = useMutation(addEvent, {\n    onSuccess: (event: Event) => {\n      queryClient.setQueryData<Event[]>([\"events\"], (oldEvents) =>\n        addOne(oldEvents, event)\n      );\n    },\n  });\n\n  return { isAdding: isLoading, addEvent: mutateAsync };\n}\n"
  },
  {
    "path": "src/calendar/hooks/useDeleteEvent.ts",
    "content": "import axios from \"axios\";\nimport { useMutation, useQueryClient } from \"react-query\";\nimport { removeOne } from \"../../core/utils/crudUtils\";\nimport { Event } from \"../types/event\";\n\nconst deleteEvent = async (eventId: string): Promise<string> => {\n  const { data } = await axios.delete(\"/api/events\", { data: eventId });\n  return data;\n};\n\nexport function useDeleteEvent() {\n  const queryClient = useQueryClient();\n\n  const { isLoading, mutateAsync } = useMutation(deleteEvent, {\n    onSuccess: (eventId: string) => {\n      queryClient.setQueryData<Event[]>([\"events\"], (oldEvents) =>\n        removeOne(oldEvents, eventId)\n      );\n    },\n  });\n\n  return { isDeleting: isLoading, deleteEvent: mutateAsync };\n}\n"
  },
  {
    "path": "src/calendar/hooks/useEvents.ts",
    "content": "import axios from \"axios\";\nimport { useQuery } from \"react-query\";\nimport { Event } from \"../types/event\";\n\nconst fetchEvents = async (): Promise<Event[]> => {\n  const { data } = await axios.get(\"/api/events\");\n  return data;\n};\n\nexport function useEvents() {\n  return useQuery(\"events\", () => fetchEvents());\n}\n"
  },
  {
    "path": "src/calendar/hooks/useUpdateEvent.ts",
    "content": "import axios from \"axios\";\nimport { useMutation, useQueryClient } from \"react-query\";\nimport { updateOne } from \"../../core/utils/crudUtils\";\nimport { Event } from \"../types/event\";\n\nconst updateEvent = async (event: Event): Promise<Event> => {\n  const { data } = await axios.put(\"/api/events\", event);\n  return data;\n};\n\nexport function useUpdateEvent() {\n  const queryClient = useQueryClient();\n\n  const { isLoading, mutateAsync } = useMutation(updateEvent, {\n    onSuccess: (event: Event) => {\n      queryClient.setQueryData<Event[]>([\"events\"], (oldEvents) =>\n        updateOne(oldEvents, event)\n      );\n    },\n  });\n\n  return { isUpdating: isLoading, updateEvent: mutateAsync };\n}\n"
  },
  {
    "path": "src/calendar/pages/CalendarApp.tsx",
    "content": "import Card from \"@material-ui/core/Card\";\nimport Fab from \"@material-ui/core/Fab\";\nimport AddIcon from \"@material-ui/icons/Add\";\nimport React, { useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport AdminAppBar from \"../../admin/components/AdminAppBar\";\nimport AdminToolbar from \"../../admin/components/AdminToolbar\";\nimport { useAddEvent } from \"../../calendar/hooks/useAddEvent\";\nimport ConfirmDialog from \"../../core/components/ConfirmDialog\";\nimport { useSnackbar } from \"../../core/contexts/SnackbarProvider\";\nimport Calendar from \"../components/Calendar\";\nimport EventDialog from \"../components/EventDialog\";\nimport { useDeleteEvent } from \"../hooks/useDeleteEvent\";\nimport { useEvents } from \"../hooks/useEvents\";\nimport { useUpdateEvent } from \"../hooks/useUpdateEvent\";\nimport { Event } from \"../types/event\";\n\nconst CalendarApp = () => {\n  const snackbar = useSnackbar();\n  const { t } = useTranslation();\n\n  const [eventDeleted, setEventDeleted] =\n    useState<string | undefined>(undefined);\n  const [eventUpdated, setEventUpdated] =\n    useState<Event | undefined>(undefined);\n  const [openConfirmDeleteDialog, setOpenConfirmDeleteDialog] = useState(false);\n  const [openEventDialog, setOpenEventDialog] = useState(false);\n\n  const { addEvent, isAdding } = useAddEvent();\n  const { deleteEvent, isDeleting } = useDeleteEvent();\n  const { data } = useEvents();\n  const { isUpdating, updateEvent } = useUpdateEvent();\n\n  const processing = isAdding || isDeleting || isUpdating;\n\n  const handleAddEvent = async (event: Partial<Event>) => {\n    addEvent(event as Event)\n      .then(() => {\n        snackbar.success(\n          t(\"calendar.notifications.addSuccess\", { event: event.title })\n        );\n        setOpenEventDialog(false);\n      })\n      .catch(() => {\n        snackbar.error(t(\"common.errors.unexpected.subTitle\"));\n      });\n  };\n\n  const handleDeleteEvent = async () => {\n    if (eventDeleted) {\n      deleteEvent(eventDeleted)\n        .then(() => {\n          snackbar.success(t(\"calendar.notifications.deleteSuccess\"));\n          setOpenConfirmDeleteDialog(false);\n          setOpenEventDialog(false);\n        })\n        .catch(() => {\n          snackbar.error(t(\"common.errors.unexpected.subTitle\"));\n        });\n    }\n  };\n\n  const handleUpdateEvent = async (event: Event) => {\n    updateEvent(event)\n      .then(() => {\n        snackbar.success(\n          t(\"calendar.notifications.updateSuccess\", { event: event.title })\n        );\n        setOpenEventDialog(false);\n      })\n      .catch(() => {\n        snackbar.error(t(\"common.errors.unexpected.subTitle\"));\n      });\n  };\n\n  const handleCloseConfirmDeleteDialog = () => {\n    setOpenConfirmDeleteDialog(false);\n  };\n\n  const handleCloseEventDialog = () => {\n    setEventUpdated(undefined);\n    setOpenEventDialog(false);\n  };\n\n  const handleOpenConfirmDeleteDialog = (eventId: string) => {\n    setEventDeleted(eventId);\n    setOpenConfirmDeleteDialog(true);\n  };\n\n  const handleOpenEventDialog = (event?: Event) => {\n    setEventUpdated(event);\n    setOpenEventDialog(true);\n  };\n\n  return (\n    <React.Fragment>\n      <AdminAppBar>\n        <AdminToolbar title={t(\"calendar.title\")}>\n          <Fab\n            aria-label=\"add event\"\n            color=\"primary\"\n            onClick={() => handleOpenEventDialog()}\n            size=\"small\"\n          >\n            <AddIcon />\n          </Fab>\n        </AdminToolbar>\n      </AdminAppBar>\n      <Card>\n        <Calendar\n          contentHeight={720}\n          events={data || []}\n          onEventClick={handleOpenEventDialog}\n        />\n      </Card>\n      <ConfirmDialog\n        description={t(\"calendar.confirmations.delete\")}\n        pending={processing}\n        onClose={handleCloseConfirmDeleteDialog}\n        onConfirm={handleDeleteEvent}\n        open={openConfirmDeleteDialog}\n        title={t(\"common.confirmation\")}\n      />\n      {openEventDialog && (\n        <EventDialog\n          onAdd={handleAddEvent}\n          onClose={handleCloseEventDialog}\n          onDelete={handleOpenConfirmDeleteDialog}\n          onUpdate={handleUpdateEvent}\n          open={openEventDialog}\n          processing={processing}\n          event={eventUpdated}\n        />\n      )}\n    </React.Fragment>\n  );\n};\n\nexport default CalendarApp;\n"
  },
  {
    "path": "src/calendar/types/event.ts",
    "content": "export interface Event {\n  id: string;\n  title: string;\n  description?: string;\n  start: number;\n  end: number;\n  color?: EventColor;\n}\n\nexport const eventColors = [\"primary\", \"warning\", \"error\", \"success\"] as const;\n\nexport type EventColor = typeof eventColors[number];\n"
  },
  {
    "path": "src/core/components/BoxedLayout.tsx",
    "content": "import AppBar from \"@material-ui/core/AppBar\";\nimport Box from \"@material-ui/core/Box\";\nimport Container from \"@material-ui/core/Container\";\nimport GlobalStyles from \"@material-ui/core/GlobalStyles\";\nimport IconButton from \"@material-ui/core/IconButton\";\nimport useTheme from \"@material-ui/core/styles/useTheme\";\nimport Toolbar from \"@material-ui/core/Toolbar\";\nimport SettingsIcon from \"@material-ui/icons/Settings\";\nimport React, { useState } from \"react\";\nimport Logo from \"./Logo\";\nimport SettingsDrawer from \"./SettingsDrawer\";\n\ntype BoxedLayoutProps = {\n  children: React.ReactNode;\n};\n\nconst BoxedLayout = ({ children }: BoxedLayoutProps) => {\n  const theme = useTheme();\n  const [settingsOpen, setSettingsOpen] = useState(false);\n\n  const handleSettingsToggle = () => {\n    setSettingsOpen(!settingsOpen);\n  };\n\n  return (\n    <React.Fragment>\n      <GlobalStyles\n        styles={{ body: { backgroundColor: theme.palette.background.paper } }}\n      />\n      <AppBar color=\"transparent\" position=\"relative\">\n        <Toolbar>\n          <Box sx={{ flexGrow: 1 }} />\n          <IconButton\n            aria-label=\"settings\"\n            component=\"span\"\n            onClick={handleSettingsToggle}\n          >\n            <SettingsIcon />\n          </IconButton>\n        </Toolbar>\n      </AppBar>\n      <Container component=\"main\" maxWidth=\"xs\" sx={{ mt: 6 }}>\n        <Box\n          sx={{\n            display: \"flex\",\n            flexDirection: \"column\",\n            alignItems: \"center\",\n          }}\n        >\n          <Logo sx={{ mb: 2 }} />\n          {children}\n          <Box>\n            <SettingsDrawer\n              onDrawerToggle={handleSettingsToggle}\n              open={settingsOpen}\n            />\n          </Box>\n        </Box>\n      </Container>\n    </React.Fragment>\n  );\n};\n\nexport default BoxedLayout;\n"
  },
  {
    "path": "src/core/components/ConfirmDialog.tsx",
    "content": "import Button from \"@material-ui/core/Button\";\nimport Dialog from \"@material-ui/core/Dialog\";\nimport DialogActions from \"@material-ui/core/DialogActions\";\nimport DialogContent from \"@material-ui/core/DialogContent\";\nimport DialogContentText from \"@material-ui/core/DialogContentText\";\nimport DialogTitle from \"@material-ui/core/DialogTitle\";\nimport LoadingButton from \"@material-ui/lab/LoadingButton\";\nimport { useTranslation } from \"react-i18next\";\nimport { ReactComponent as ConfirmSvg } from \"../assets/confirm.svg\";\nimport SvgContainer from \"./SvgContainer\";\n\ntype ConfirmDialogProps = {\n  description?: string;\n  onClose: () => void;\n  onConfirm: () => void;\n  open: boolean;\n  pending: boolean;\n  title: string;\n};\n\nconst ConfirmDialog = ({\n  description,\n  onClose,\n  onConfirm,\n  open,\n  pending,\n  title,\n}: ConfirmDialogProps) => {\n  const { t } = useTranslation();\n\n  return (\n    <Dialog\n      open={open}\n      onClose={onClose}\n      aria-labelledby=\"confirm-dialog-title\"\n      aria-describedby=\"confirm-dialog-description\"\n    >\n      <DialogContent sx={{ textAlign: \"center\" }}>\n        <SvgContainer>\n          <ConfirmSvg style={{ maxWidth: 280, width: \"100%\" }} />\n        </SvgContainer>\n        <DialogTitle id=\"confirm-dialog-title\" sx={{ pb: 1, pt: 0 }}>\n          {title}\n        </DialogTitle>\n        {description && (\n          <DialogContentText id=\"confirm-dialog-description\">\n            {description}\n          </DialogContentText>\n        )}\n      </DialogContent>\n      <DialogActions>\n        <Button onClick={onClose}>{t(\"common.cancel\")}</Button>\n        <LoadingButton\n          autoFocus\n          onClick={onConfirm}\n          loading={pending}\n          variant=\"contained\"\n        >\n          {t(\"common.confirm\")}\n        </LoadingButton>\n      </DialogActions>\n    </Dialog>\n  );\n};\n\nexport default ConfirmDialog;\n"
  },
  {
    "path": "src/core/components/Empty.tsx",
    "content": "import { ReactComponent as EmptySvg } from \"../assets/empty.svg\";\nimport Result from \"./Result\";\n\ntype EmptyProps = {\n  message?: string;\n  title: string;\n};\n\nconst Empty = ({ message, title }: EmptyProps) => {\n  return <Result image={<EmptySvg />} subTitle={message} title={title} />;\n};\n\nexport default Empty;\n"
  },
  {
    "path": "src/core/components/Footer.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Link from \"@material-ui/core/Link\";\nimport Typography from \"@material-ui/core/Typography\";\nimport { Link as RouterLink } from \"react-router-dom\";\n\nconst Footer = () => {\n  return (\n    <Box sx={{ p: 6 }} component=\"footer\">\n      <Typography variant=\"body2\" color=\"text.secondary\" align=\"center\">\n        {\"© \"}\n        <Link\n          color=\"inherit\"\n          component={RouterLink}\n          to={`/${process.env.PUBLIC_URL}/`}\n        >\n          {process.env.REACT_APP_NAME}\n        </Link>{\" \"}\n        {new Date().getFullYear()}\n        {\".\"}\n      </Typography>\n    </Box>\n  );\n};\n\nexport default Footer;\n"
  },
  {
    "path": "src/core/components/Loader.tsx",
    "content": "import { useTheme } from \"@material-ui/core/styles\";\nimport Logo from \"./Logo\";\n\nconst Loader = () => {\n  const theme = useTheme();\n  return (\n    <Logo\n      size={100}\n      sx={{\n        \"@keyframes pulse\": {\n          \"0%\": {\n            opacity: 1,\n          },\n          \"50%\": {\n            opacity: 0.4,\n          },\n          \"100%\": {\n            opacity: 1,\n          },\n        },\n        animation: \"pulse 1.5s ease-in-out 0.5s infinite\",\n        color: theme.palette.mode === \"light\" ? \"grey.300\" : \"grey.600\",\n        textAlign: \"center\",\n        px: 3,\n        py: 8,\n      }}\n    />\n  );\n};\n\nexport default Loader;\n"
  },
  {
    "path": "src/core/components/Logo.tsx",
    "content": "import Box, { BoxProps } from \"@material-ui/core/Box\";\nimport { ReactComponent as LogoSvg } from \"../assets/logo.svg\";\n\ntype LogoProps = {\n  colored?: boolean;\n  size?: number;\n} & BoxProps;\n\nconst Logo = ({ colored = false, size = 40, ...boxProps }: LogoProps) => {\n  return (\n    <Box {...boxProps}>\n      <LogoSvg height={size} width={size} />\n    </Box>\n  );\n};\n\nexport default Logo;\n"
  },
  {
    "path": "src/core/components/PrivateRoute.tsx",
    "content": "import { Navigate, Route, RouteProps } from \"react-router\";\nimport { useAuth } from \"../../auth/contexts/AuthProvider\";\n\ntype PrivateRouteProps = {\n  roles?: string[];\n} & RouteProps;\n\nconst PrivateRoute = ({\n  children,\n  roles,\n  ...routeProps\n}: PrivateRouteProps) => {\n  const { hasRole, userInfo } = useAuth();\n\n  if (userInfo) {\n    if (!hasRole(roles)) {\n      return <Navigate to={`/${process.env.PUBLIC_URL}/403`} />;\n    }\n    return <Route {...routeProps} />;\n  } else {\n    return <Navigate to={`/${process.env.PUBLIC_URL}/login`} />;\n  }\n};\n\nexport default PrivateRoute;\n"
  },
  {
    "path": "src/core/components/QueryWrapper.tsx",
    "content": "import Button from \"@material-ui/core/Button\";\nimport React from \"react\";\nimport { ErrorBoundary } from \"react-error-boundary\";\nimport { useTranslation } from \"react-i18next\";\nimport { useQueryErrorResetBoundary } from \"react-query\";\nimport Loader from \"./Loader\";\nimport Result from \"./Result\";\n\ntype QueryWrapperProps = {\n  children: React.ReactNode;\n};\n\nconst QueryWrapper = ({ children }: QueryWrapperProps) => {\n  const { reset } = useQueryErrorResetBoundary();\n  const { t } = useTranslation();\n\n  return (\n    <ErrorBoundary\n      onReset={reset}\n      fallbackRender={({ resetErrorBoundary }) => (\n        <Result\n          extra={\n            <Button onClick={() => resetErrorBoundary()} variant=\"contained\">\n              {t(\"common.retry\")}\n            </Button>\n          }\n          status=\"error\"\n          subTitle={t(\"common.errors.unexpected.subTitle\")}\n          title={t(\"common.errors.unexpected.title\")}\n        />\n      )}\n    >\n      <React.Suspense fallback={<Loader />}>{children}</React.Suspense>\n    </ErrorBoundary>\n  );\n};\n\nexport default QueryWrapper;\n"
  },
  {
    "path": "src/core/components/Result.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Container from \"@material-ui/core/Container\";\nimport Typography from \"@material-ui/core/Typography\";\nimport React from \"react\";\nimport { ReactComponent as ErrorSvg } from \"../assets/error.svg\";\nimport { ReactComponent as SuccessSvg } from \"../assets/success.svg\";\nimport SvgContainer from \"./SvgContainer\";\n\ntype ResultImageProps = {\n  customImage?: React.ReactNode;\n  status?: \"error\" | \"success\";\n};\n\nconst ResultImage = ({ customImage, status }: ResultImageProps) => {\n  let image = customImage;\n\n  if (!image) {\n    if (status === \"error\") {\n      image = <ErrorSvg />;\n    } else if (status === \"success\") {\n      image = <SuccessSvg />;\n    }\n  }\n\n  return image ? <Box marginBottom={3}>{image}</Box> : null;\n};\n\ntype ResultProps = {\n  extra?: React.ReactNode;\n  image?: React.ReactNode;\n  maxWidth?: \"xs\" | \"sm\";\n  status?: \"error\" | \"success\";\n  subTitle?: string;\n  title: string;\n};\n\nconst Result = ({\n  extra,\n  image,\n  maxWidth = \"xs\",\n  status,\n  subTitle,\n  title,\n}: ResultProps) => {\n  return (\n    <Container maxWidth={maxWidth}>\n      <Box sx={{ textAlign: \"center\", px: 3, py: 8 }}>\n        <SvgContainer>\n          <ResultImage customImage={image} status={status} />\n        </SvgContainer>\n        <Typography gutterBottom variant=\"h5\">\n          {title}\n        </Typography>\n        {subTitle && <Typography variant=\"body2\">{subTitle}</Typography>}\n        {extra && <Box sx={{ mt: 4, textAlign: \"center\" }}>{extra}</Box>}\n      </Box>\n    </Container>\n  );\n};\n\nexport default Result;\n"
  },
  {
    "path": "src/core/components/SelectToolbar.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Fab from \"@material-ui/core/Fab\";\nimport Toolbar from \"@material-ui/core/Toolbar\";\nimport Tooltip from \"@material-ui/core/Tooltip\";\nimport CloseIcon from \"@material-ui/icons/Close\";\nimport DeleteIcon from \"@material-ui/icons/Delete\";\nimport { useTranslation } from \"react-i18next\";\n\ninterface SelectToolbarProps {\n  onCancel: () => void;\n  onDelete: (userIds: string[]) => void;\n  processing: boolean;\n  selected: string[];\n}\n\nconst SelectToolbar = ({\n  onCancel,\n  onDelete,\n  processing,\n  selected,\n}: SelectToolbarProps) => {\n  const { t } = useTranslation();\n\n  const numSelected = selected.length;\n\n  return (\n    <Toolbar sx={{ ml: 1, px: { xs: 3, sm: 6 } }}>\n      <Fab color=\"secondary\" onClick={onCancel} variant=\"extended\">\n        <CloseIcon sx={{ mr: 1 }} />\n        {numSelected} {t(\"common.selected\")}\n      </Fab>\n      <Box sx={{ flexGrow: 1 }} />\n\n      {numSelected > 0 && (\n        <Tooltip title={t(\"common.delete\") as string}>\n          <Fab\n            color=\"secondary\"\n            disabled={processing}\n            onClick={() => onDelete(selected)}\n          >\n            <DeleteIcon />\n          </Fab>\n        </Tooltip>\n      )}\n    </Toolbar>\n  );\n};\n\nexport default SelectToolbar;\n"
  },
  {
    "path": "src/core/components/SettingsDrawer.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport Drawer from \"@material-ui/core/Drawer\";\nimport FormControl from \"@material-ui/core/FormControl\";\nimport FormControlLabel from \"@material-ui/core/FormControlLabel\";\nimport IconButton from \"@material-ui/core/IconButton\";\nimport Radio from \"@material-ui/core/Radio\";\nimport RadioGroup from \"@material-ui/core/RadioGroup\";\nimport ToggleButton from \"@material-ui/core/ToggleButton\";\nimport ToggleButtonGroup from \"@material-ui/core/ToggleButtonGroup\";\nimport Typography from \"@material-ui/core/Typography\";\nimport CloseIcon from \"@material-ui/icons/Close\";\nimport { useTranslation } from \"react-i18next\";\nimport { drawerWidth } from \"../config/layout\";\nimport { useSettings } from \"../contexts/SettingsProvider\";\n\ntype SettingsDrawerProps = {\n  onDrawerToggle: () => void;\n  open: boolean;\n};\n\nconst SettingsDrawer = ({ onDrawerToggle, open }: SettingsDrawerProps) => {\n  const {\n    changeCollapsed,\n    changeDirection,\n    changeMode,\n    collapsed,\n    direction,\n    mode,\n  } = useSettings();\n  const { i18n, t } = useTranslation();\n\n  const handleDirectionChange = (_: any, direction: \"ltr\" | \"rtl\") => {\n    changeDirection(direction);\n  };\n\n  const handleLanguageChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n    i18n.changeLanguage((event.target as HTMLInputElement).value);\n  };\n\n  const handleModeChange = (_: any, mode: string) => {\n    changeMode(mode);\n  };\n\n  const handleSidebarChange = (_: any, collapsed: boolean) => {\n    changeCollapsed(collapsed);\n  };\n\n  return (\n    <Drawer\n      anchor=\"left\"\n      open={open}\n      onClose={onDrawerToggle}\n      sx={{\n        \"& .MuiDrawer-paper\": {\n          width: drawerWidth,\n        },\n      }}\n      variant=\"temporary\"\n    >\n      <Box\n        sx={{\n          display: \"flex\",\n          justifyContent: \"space-between\",\n          alignItems: \"center\",\n          p: 2,\n        }}\n      >\n        <Typography variant=\"h5\">{t(\"settings.drawer.title\")}</Typography>\n        <IconButton color=\"inherit\" onClick={onDrawerToggle} edge=\"end\">\n          <CloseIcon />\n        </IconButton>\n      </Box>\n      <Box sx={{ pl: 2, pr: 2 }}>\n        <Typography\n          gutterBottom\n          id=\"settings-language\"\n          marginTop={3}\n          variant=\"h6\"\n        >\n          {t(\"settings.drawer.language.label\")}\n        </Typography>\n        <FormControl>\n          <RadioGroup\n            aria-label=\"language\"\n            name=\"language-radio-group\"\n            onChange={handleLanguageChange}\n            value={i18n.language}\n          >\n            <FormControlLabel\n              value=\"en\"\n              control={<Radio />}\n              label={t(\"settings.drawer.language.options.en\")}\n            />\n            <FormControlLabel\n              value=\"fr\"\n              control={<Radio />}\n              label={t(\"settings.drawer.language.options.fr\")}\n            />\n          </RadioGroup>\n        </FormControl>\n        <Typography gutterBottom id=\"settings-mode\" marginTop={3} variant=\"h6\">\n          {t(\"settings.drawer.mode.label\")}\n        </Typography>\n        <ToggleButtonGroup\n          color=\"primary\"\n          value={mode}\n          exclusive\n          fullWidth\n          onChange={handleModeChange}\n        >\n          <ToggleButton value=\"light\">\n            {t(\"settings.drawer.mode.options.light\")}\n          </ToggleButton>\n          <ToggleButton value=\"dark\">\n            {t(\"settings.drawer.mode.options.dark\")}\n          </ToggleButton>\n        </ToggleButtonGroup>\n        <Typography gutterBottom id=\"settings-mode\" marginTop={3} variant=\"h6\">\n          {t(\"settings.drawer.direction.label\")}\n        </Typography>\n        <ToggleButtonGroup\n          color=\"primary\"\n          value={direction}\n          exclusive\n          fullWidth\n          onChange={handleDirectionChange}\n        >\n          <ToggleButton value=\"ltr\">\n            {t(\"settings.drawer.direction.options.ltr\")}\n          </ToggleButton>\n          <ToggleButton value=\"rtl\">\n            {t(\"settings.drawer.direction.options.rtl\")}\n          </ToggleButton>\n        </ToggleButtonGroup>\n        <Typography\n          gutterBottom\n          id=\"settings-sidebar\"\n          marginTop={3}\n          variant=\"h6\"\n        >\n          {t(\"settings.drawer.sidebar.label\")}\n        </Typography>\n        <ToggleButtonGroup\n          color=\"primary\"\n          value={collapsed}\n          exclusive\n          fullWidth\n          onChange={handleSidebarChange}\n        >\n          <ToggleButton value={true}>\n            {t(\"settings.drawer.sidebar.options.collapsed\")}\n          </ToggleButton>\n          <ToggleButton value={false}>\n            {t(\"settings.drawer.sidebar.options.full\")}\n          </ToggleButton>\n        </ToggleButtonGroup>\n      </Box>\n    </Drawer>\n  );\n};\n\nexport default SettingsDrawer;\n"
  },
  {
    "path": "src/core/components/SvgContainer.tsx",
    "content": "import Box from \"@material-ui/core/Box\";\nimport { useTheme } from \"@material-ui/core/styles\";\nimport React from \"react\";\n\ntype SvgContainerProps = {\n  children: React.ReactNode;\n};\n\nconst SvgContainer = ({ children }: SvgContainerProps) => {\n  const theme = useTheme();\n  return (\n    <Box\n      sx={{\n        svg: { height: \"100%\", width: \"100%\" },\n        \".fill-primary\": { fill: theme.palette.primary.light },\n        \".fill-secondary\": { fill: theme.palette.secondary.light },\n        \".fill-error\": { fill: theme.palette.error.light },\n        \".fill-success\": { fill: theme.palette.success.light },\n        \".fill-warning\": { fill: theme.palette.warning.light },\n        \".fill-paper\": { fill: theme.palette.background.paper },\n      }}\n    >\n      {children}\n    </Box>\n  );\n};\n\nexport default SvgContainer;\n"
  },
  {
    "path": "src/core/config/i18n.ts",
    "content": "import i18n from \"i18next\";\nimport LanguageDetector from \"i18next-browser-languagedetector\";\nimport Backend from \"i18next-xhr-backend\";\nimport { initReactI18next } from \"react-i18next\";\n\ni18n\n  .use(Backend)\n  .use(LanguageDetector)\n  .use(initReactI18next) // passes i18n down to react-i18next\n  .init({\n    backend: {\n      loadPath: `${process.env.PUBLIC_URL}/locales/{{lng}}/translation.json`,\n    },\n    fallbackLng: \"en\",\n    interpolation: {\n      escapeValue: false, // not needed for react as it escapes by default\n    },\n    supportedLngs: [\"en\", \"fr\"],\n  });\n"
  },
  {
    "path": "src/core/config/layout.ts",
    "content": "export const drawerCollapsedWidth = 104;\nexport const drawerWidth = 280;\n"
  },
  {
    "path": "src/core/contexts/SettingsProvider.tsx",
    "content": "import { ThemeProvider as MuiThemeProvider } from \"@material-ui/core\";\nimport CssBaseline from \"@material-ui/core/CssBaseline\";\nimport AdapterDateFns from \"@material-ui/lab/AdapterDateFns\";\nimport LocalizationProvider from \"@material-ui/lab/LocalizationProvider\";\nimport React, {\n  createContext,\n  useContext,\n  useEffect,\n  useMemo,\n  useState,\n} from \"react\";\nimport { useLocalStorage } from \"../hooks/useLocalStorage\";\nimport { createTheme } from \"../theme\";\n\ninterface SettingsContextInterface {\n  collapsed: boolean;\n  direction: string;\n  mode: string;\n  open: boolean;\n  changeCollapsed: (collapsed: boolean) => void;\n  changeDirection: (direction: \"ltr\" | \"rtl\") => void;\n  changeMode: (mode: string) => void;\n  toggleDrawer: () => void;\n}\n\nexport const SettingsContext = createContext({} as SettingsContextInterface);\n\ntype SettingsProviderProps = {\n  children: React.ReactNode;\n};\n\nconst SettingsProvider = ({ children }: SettingsProviderProps) => {\n  const [collapsed, setCollapsed] = useLocalStorage(\"sidebarcollapsed\", false);\n  const [direction, setDirection] = useLocalStorage(\"direction\", \"ltr\");\n  const [mode, setMode] = useLocalStorage(\"mode\", \"light\");\n  const [open, setOpen] = useState(false);\n\n  useEffect(() => {\n    document.body.dir = direction;\n  }, [direction]);\n\n  const theme = useMemo(\n    () => createTheme(direction as \"ltr\" | \"rtl\", mode as \"dark\" | \"light\"),\n    [direction, mode]\n  );\n\n  const changeCollapsed = (collapsed: boolean) => {\n    if (typeof collapsed === \"boolean\") {\n      setCollapsed(collapsed);\n    }\n  };\n\n  const changeDirection = (direction: \"ltr\" | \"rtl\") => {\n    if (direction) {\n      setDirection(direction);\n    }\n  };\n\n  const changeMode = (mode: string) => {\n    if (mode) {\n      setMode(mode);\n    }\n  };\n\n  const toggleDrawer = () => {\n    setOpen(!open);\n  };\n\n  return (\n    <SettingsContext.Provider\n      value={{\n        collapsed,\n        direction,\n        mode,\n        open,\n        changeCollapsed,\n        changeDirection,\n        changeMode,\n        toggleDrawer,\n      }}\n    >\n      <MuiThemeProvider theme={theme}>\n        <LocalizationProvider dateAdapter={AdapterDateFns}>\n          <CssBaseline />\n          {children}\n        </LocalizationProvider>\n      </MuiThemeProvider>\n    </SettingsContext.Provider>\n  );\n};\n\nexport function useSettings() {\n  return useContext(SettingsContext);\n}\n\nexport default SettingsProvider;\n"
  },
  {
    "path": "src/core/contexts/SnackbarProvider.tsx",
    "content": "import Alert, { Color } from \"@material-ui/core/Alert\";\nimport AlertTitle from \"@material-ui/core/AlertTitle\";\nimport Snackbar from \"@material-ui/core/Snackbar\";\nimport React, { createContext, useContext, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\ninterface SnackbarContextInterface {\n  error: (newMessage: string) => void;\n  success: (newMessage: string) => void;\n}\n\nexport const SnackbarContext = createContext({} as SnackbarContextInterface);\n\ntype SnackbarProviderProps = {\n  children: React.ReactNode;\n};\n\nconst SnackbarProvider = ({ children }: SnackbarProviderProps) => {\n  const { t } = useTranslation();\n  const [open, setOpen] = useState(false);\n  const [message, setMessage] = useState(\"\");\n  const [title, setTitle] = useState(\"\");\n  const [severity, setSeverity] = useState<Color | undefined>(undefined);\n\n  const handleClose = (\n    event: React.SyntheticEvent | React.MouseEvent,\n    reason?: string\n  ) => {\n    if (reason === \"clickaway\") {\n      return;\n    }\n\n    setOpen(false);\n  };\n\n  const error = (newMessage: string) => {\n    setTitle(t(\"common.snackbar.error\"));\n    setMessage(newMessage);\n    setSeverity(\"error\");\n    setOpen(true);\n  };\n\n  const success = (newMessage: string) => {\n    setTitle(t(\"common.snackbar.success\"));\n    setMessage(newMessage);\n    setSeverity(\"success\");\n    setOpen(true);\n  };\n\n  return (\n    <SnackbarContext.Provider value={{ error, success }}>\n      {children}\n      <Snackbar\n        key={message}\n        anchorOrigin={{\n          vertical: \"bottom\",\n          horizontal: \"right\",\n        }}\n        open={open}\n        autoHideDuration={6000}\n        onClose={handleClose}\n      >\n        <Alert onClose={handleClose} severity={severity}>\n          <AlertTitle>{title}</AlertTitle>\n          {message}\n        </Alert>\n      </Snackbar>\n    </SnackbarContext.Provider>\n  );\n};\n\nexport function useSnackbar() {\n  return useContext(SnackbarContext);\n}\n\nexport default SnackbarProvider;\n"
  },
  {
    "path": "src/core/hooks/useDateLocale.ts",
    "content": "import { Locale } from \"date-fns\";\nimport en from \"date-fns/locale/en-US\";\nimport fr from \"date-fns/locale/fr\";\nimport { useEffect, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\nconst locales: { [key: string]: Locale } = { en, fr };\n\nexport function useDateLocale(): Locale | undefined {\n  const [locale, setLocale] = useState<Locale | undefined>(undefined);\n  const { i18n } = useTranslation();\n\n  useEffect(() => {\n    setLocale(locales[i18n.language]);\n  }, [i18n.language]);\n\n  return locale;\n}\n"
  },
  {
    "path": "src/core/hooks/useLocalStorage.ts",
    "content": "// https://usehooks.com/useLocalStorage/\n\nimport { useState } from \"react\";\n\nexport function useLocalStorage<T>(\n  key: string,\n  initialValue: T\n): [T, (value: T) => void] {\n  // State to store our value\n  // Pass initial state function to useState so logic is only executed once\n  const [storedValue, setStoredValue] = useState<T>(() => {\n    try {\n      // Get from local storage by key\n      const item = window.localStorage.getItem(key);\n      // Parse stored json or if none return initialValue\n      return item ? JSON.parse(item) : initialValue;\n    } catch (error) {\n      // If error also return initialValue\n      console.log(error);\n      return initialValue;\n    }\n  });\n  // Return a wrapped version of useState's setter function that ...\n  // ... persists the new value to localStorage.\n  const setValue = (value: T) => {\n    try {\n      // Allow value to be a function so we have same API as useState\n      const valueToStore =\n        value instanceof Function ? value(storedValue) : value;\n      // Save state\n      setStoredValue(valueToStore);\n      // Save to local storage\n      window.localStorage.setItem(key, JSON.stringify(valueToStore));\n    } catch (error) {\n      // A more advanced implementation would handle the error case\n      console.log(error);\n    }\n  };\n  return [storedValue, setValue];\n}\n"
  },
  {
    "path": "src/core/hooks/usePageTracking.ts",
    "content": "import { useEffect, useState } from \"react\";\nimport { useLocation } from \"react-router-dom\";\n\nconst usePageTracking = () => {\n  const location = useLocation();\n  const [initialized, setInitialized] = useState(false);\n\n  useEffect(() => {\n    const trackingId = process.env.REACT_APP_GA_TRACKING_ID;\n    if (trackingId) {\n      setInitialized(true);\n    }\n  }, []);\n\n  useEffect(() => {\n    if (initialized) {\n      (window as any).gtag(\"send\", \"page_view\", {\n        page_location: window.location.href,\n        page_path: window.location.pathname,\n      });\n    }\n  }, [initialized, location]);\n};\n\nexport default usePageTracking;\n"
  },
  {
    "path": "src/core/pages/Forbidden.tsx",
    "content": "import Button from \"@material-ui/core/Button\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link as RouterLink } from \"react-router-dom\";\nimport { ReactComponent as ForbiddenSvg } from \"../assets/403.svg\";\nimport Result from \"../components/Result\";\n\nconst Forbidden = () => {\n  const { t } = useTranslation();\n\n  return (\n    <Result\n      extra={\n        <Button\n          color=\"secondary\"\n          component={RouterLink}\n          to={`/${process.env.PUBLIC_URL}/admin`}\n          variant=\"contained\"\n        >\n          {t(\"common.backHome\")}\n        </Button>\n      }\n      image={<ForbiddenSvg />}\n      maxWidth=\"sm\"\n      subTitle={t(\"common.errors.forbidden.subTitle\")}\n      title={t(\"common.errors.unexpected.title\")}\n    />\n  );\n};\n\nexport default Forbidden;\n"
  },
  {
    "path": "src/core/pages/NotFound.tsx",
    "content": "import Button from \"@material-ui/core/Button\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link as RouterLink } from \"react-router-dom\";\nimport Result from \"../../core/components/Result\";\nimport { ReactComponent as NotFoundSvg } from \"../assets/404.svg\";\n\nconst NotFound = () => {\n  const { t } = useTranslation();\n\n  return (\n    <Result\n      extra={\n        <Button\n          color=\"secondary\"\n          component={RouterLink}\n          to={`/${process.env.PUBLIC_URL}/admin`}\n          variant=\"contained\"\n        >\n          {t(\"common.backHome\")}\n        </Button>\n      }\n      image={<NotFoundSvg />}\n      maxWidth=\"sm\"\n      subTitle={t(\"common.errors.notFound.subTitle\")}\n      title={t(\"common.errors.notFound.title\")}\n    />\n  );\n};\n\nexport default NotFound;\n"
  },
  {
    "path": "src/core/pages/UnderConstructions.tsx",
    "content": "import Button from \"@material-ui/core/Button\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link as RouterLink } from \"react-router-dom\";\nimport Result from \"../../core/components/Result\";\nimport { ReactComponent as ConstructionsSvg } from \"../assets/constructions.svg\";\n\nconst UnderConstructions = () => {\n  const { t } = useTranslation();\n\n  return (\n    <Result\n      extra={\n        <Button\n          color=\"secondary\"\n          component={RouterLink}\n          to={`/${process.env.PUBLIC_URL}/admin`}\n          variant=\"contained\"\n        >\n          {t(\"common.backHome\")}\n        </Button>\n      }\n      image={<ConstructionsSvg />}\n      maxWidth=\"sm\"\n      subTitle={t(\"common.errors.underConstructions.subTitle\")}\n      title={t(\"common.errors.underConstructions.title\")}\n    />\n  );\n};\n\nexport default UnderConstructions;\n"
  },
  {
    "path": "src/core/theme/components.tsx",
    "content": "import { Theme } from \"@material-ui/core\";\nimport CheckCircle from \"@material-ui/icons/CheckCircle\";\nimport RadioButtonUnchecked from \"@material-ui/icons/RadioButtonUnchecked\";\nimport RemoveCircle from \"@material-ui/icons/RemoveCircle\";\n\nexport const createThemeComponents = (theme: Theme) => ({\n  MuiAccordion: {\n    styleOverrides: {\n      root: {\n        borderRadius: theme.shape.borderRadius,\n        marginBottom: theme.spacing(3),\n        \"&.Mui-expanded:last-of-type\": {\n          marginBottom: theme.spacing(3),\n        },\n        \"&:before\": {\n          content: \"none\",\n        },\n      },\n    },\n  },\n  MuiAccordionDetails: {\n    styleOverrides: {\n      root: {\n        padding: theme.spacing(1, 3, 3),\n      },\n    },\n  },\n  MuiAccordionSummary: {\n    styleOverrides: {\n      root: {\n        padding: theme.spacing(3),\n      },\n      content: {\n        margin: 0,\n      },\n    },\n  },\n  MuiAppBar: {\n    defaultProps: {\n      elevation: 0,\n    },\n    styleOverrides: {\n      root: {\n        \"&.MuiAppBar-colorDefault\": {\n          backgroundColor: theme.palette.background.default,\n          color: theme.palette.text.primary,\n        },\n      },\n    },\n  },\n  MuiAvatar: {\n    styleOverrides: {\n      root: {\n        color: \"inherit\",\n        backgroundColor: theme.palette.background.default,\n      },\n    },\n  },\n  MuiButton: {\n    defaultProps: {\n      disableElevation: true,\n    },\n    styleOverrides: {\n      root: {\n        padding: \"16px 24px\",\n        textTransform: \"none\" as any,\n      },\n      label: {\n        fontWeight: theme.typography.fontWeightMedium,\n      },\n      text: {\n        padding: \"16px 16px\",\n      },\n    },\n  },\n  MuiButtonBase: {\n    defaultProps: {\n      disableRipple: true, // No more ripple, on the whole application\n    },\n  },\n  MuiCardActions: {\n    styleOverrides: {\n      root: {\n        justifyContent: \"flex-end\",\n        padding: \"0 24px 24px 24px\",\n      },\n    },\n  },\n  MuiCardContent: {\n    styleOverrides: {\n      root: {\n        padding: theme.spacing(3),\n      },\n    },\n  },\n  MuiCardHeader: {\n    styleOverrides: {\n      root: {\n        padding: \"24px 24px 0 24px\",\n      },\n    },\n  },\n  MuiCheckbox: {\n    defaultProps: {\n      checkedIcon: <CheckCircle />,\n      indeterminateIcon: <RemoveCircle />,\n      icon: <RadioButtonUnchecked />,\n    },\n  },\n  MuiChip: {\n    styleOverrides: {\n      label: {\n        fontWeight: theme.typography.fontWeightMedium,\n      },\n    },\n  },\n  MuiDialogActions: {\n    styleOverrides: {\n      root: {\n        padding: 24,\n      },\n    },\n  },\n  MuiDialogTitle: {\n    styleOverrides: {\n      root: {\n        padding: 24,\n        \"& .MuiTypography-root\": {\n          fontSize: \"1.25rem\",\n        },\n      },\n    },\n  },\n  MuiDrawer: {\n    styleOverrides: {\n      paper: {\n        border: \"none; !important\",\n      },\n    },\n  },\n  MuiFab: {\n    styleOverrides: {\n      root: {\n        boxShadow: \"none\",\n        lineHeight: \"inherit\",\n        textTransform: \"none\" as any,\n        \"&.MuiFab-secondary\": {\n          color: theme.palette.text.primary,\n        },\n      },\n    },\n  },\n  MuiFilledInput: {\n    defaultProps: {\n      disableUnderline: true,\n    },\n    styleOverrides: {\n      root: {\n        borderRadius: theme.shape.borderRadius,\n      },\n    },\n  },\n  MuiInternalClock: {\n    styleOverrides: {\n      clock: {\n        backgroundColor: theme.palette.background.default,\n      },\n    },\n  },\n  MuiInternalDateTimePickerTabs: {\n    styleOverrides: {\n      tabs: {\n        backgroundColor: theme.palette.background.default,\n        \"& MuiTabs-indicator\": {\n          height: 0,\n        },\n      },\n    },\n  },\n  MuiLinearProgress: {\n    styleOverrides: {\n      root: {\n        borderRadius: 16,\n        height: 12,\n      },\n    },\n  },\n  MuiList: {\n    defaultProps: {\n      disablePadding: true,\n    },\n  },\n  MuiListItem: {\n    styleOverrides: {\n      root: {\n        borderRadius: 16,\n        paddingTop: 12,\n        paddingBottom: 12,\n        \"&.Mui-selected\": {\n          backgroundColor: theme.palette.background.default,\n          color: theme.palette.text.primary,\n        },\n      },\n    },\n  },\n  MuiListItemIcon: {\n    styleOverrides: {\n      root: {\n        minWidth: 40,\n      },\n    },\n  },\n  MuiMenu: {\n    styleOverrides: {\n      list: {\n        paddingRight: 8,\n        paddingLeft: 8,\n      },\n    },\n  },\n  MuiMenuItem: {\n    styleOverrides: {\n      root: {\n        paddingTop: 12,\n        paddingBottom: 12,\n      },\n    },\n  },\n  MuiOutlinedInput: {\n    styleOverrides: {\n      input: {\n        \"&:-webkit-autofill\": {\n          WebkitBoxShadow: `0 0 0 30px ${theme.palette.background.paper} inset`,\n        },\n      },\n    },\n  },\n  MuiPaper: {\n    defaultProps: {\n      elevation: 0,\n    },\n    styleOverrides: {\n      root: {\n        backgroundImage: \"none\",\n      },\n    },\n  },\n  MuiRadio: {\n    defaultProps: {\n      color: \"primary\" as \"primary\",\n    },\n  },\n  MuiTab: {\n    styleOverrides: {\n      root: {\n        borderRadius: \"50rem\",\n        padding: \"10px 16px\",\n        maxWidth: \"initial !important\",\n        minHeight: \"initial !important\",\n        minWidth: \"initial !important\",\n        textTransform: \"none\" as any,\n        \"&.Mui-selected\": {\n          backgroundColor: theme.palette.background.paper,\n          color: theme.palette.text.primary,\n        },\n      },\n    },\n  },\n  MuiTableCell: {\n    styleOverrides: {\n      root: {\n        borderBottom: `1px solid ${theme.palette.divider}`,\n        padding: \"24px 16px\",\n      },\n      sizeSmall: {\n        padding: \"12px 16px\",\n      },\n    },\n  },\n  MuiTimeline: {\n    styleOverrides: {\n      root: {\n        padding: \"0 0 0 16px\",\n      },\n    },\n  },\n  MuiTimelineContent: {\n    styleOverrides: {\n      root: {\n        padding: \"12px 16px\",\n      },\n    },\n  },\n  MuiToggleButton: {\n    styleOverrides: {\n      root: {\n        color: theme.palette.text.secondary,\n        borderRadius: \"12px !important\",\n        border: \"none\",\n        textTransform: \"none\" as any,\n        \"&.Mui-selected\": {\n          backgroundColor: theme.palette.background.paper,\n          color: theme.palette.text.primary,\n        },\n      },\n    },\n  },\n  MuiToggleButtonGroup: {\n    styleOverrides: {\n      root: {\n        backgroundColor: theme.palette.background.default,\n        padding: 5,\n      },\n    },\n  },\n});\n"
  },
  {
    "path": "src/core/theme/index.ts",
    "content": "import { createTheme as createMuiTheme } from \"@material-ui/core\";\nimport { createThemeComponents } from \"./components\";\nimport mixins from \"./mixins\";\nimport { darkPalette, lightPalette } from \"./palette\";\nimport shape from \"./shape\";\nimport transitions from \"./transitions\";\nimport typography from \"./typography\";\n\nexport const createTheme = (\n  direction: \"ltr\" | \"rtl\",\n  mode: \"dark\" | \"light\"\n) => {\n  const palette = mode === \"dark\" ? darkPalette : lightPalette;\n\n  // Create base theme\n  const baseTheme = createMuiTheme({\n    direction,\n    mixins,\n    palette,\n    shape,\n    transitions,\n    typography,\n  });\n\n  // Inject base theme to be used in components\n  return createMuiTheme(\n    {\n      components: createThemeComponents(baseTheme),\n    },\n    baseTheme\n  );\n};\n"
  },
  {
    "path": "src/core/theme/mixins.ts",
    "content": "const mixins = {\n  toolbar: {\n    minHeight: 80,\n    \"@media (min-width:600px)\": {\n      minHeight: 104,\n    },\n    \"@media (min-width:0px) and (orientation: landscape)\": {\n      minHeight: 80,\n    },\n  },\n};\n\nexport default mixins;\n"
  },
  {
    "path": "src/core/theme/palette.ts",
    "content": "import { PaletteMode } from \"@material-ui/core\";\n\nconst palette = {\n  grey: {\n    \"50\": \"#ECEFF1\",\n    \"100\": \"#CFD8DC\",\n    \"200\": \"#B0BEC5\",\n    \"300\": \"#90A4AE\",\n    \"400\": \"#78909C\",\n    \"500\": \"#607D8B\",\n    \"600\": \"#546E7A\",\n    \"700\": \"#455A64\",\n    \"800\": \"#37474F\",\n    \"900\": \"#263238\",\n  },\n};\n\nexport const darkPalette = {\n  ...palette,\n  contrastThreshold: 4.5,\n  mode: \"dark\" as PaletteMode,\n  error: {\n    main: \"#FF8A65\",\n  },\n  info: {\n    main: \"#4FC3F7\",\n  },\n  primary: {\n    main: \"#64B5F6\",\n    contrastText: palette.grey[900],\n  },\n  secondary: {\n    main: palette.grey[900],\n  },\n  success: {\n    main: \"#81C784\",\n  },\n  warning: {\n    main: \"#FFD54F\",\n  },\n  text: {\n    primary: palette.grey[100],\n    secondary: palette.grey[300],\n    disabled: palette.grey[600],\n  },\n  divider: palette.grey[700],\n  background: {\n    paper: palette.grey[900],\n    default: palette.grey[800],\n  },\n  action: {\n    selectedOpacity: 0,\n    selected: palette.grey[800],\n  },\n};\n\nexport const lightPalette = {\n  ...palette,\n  contrastThreshold: 3,\n  mode: \"light\" as PaletteMode,\n  error: {\n    main: \"#FF3D00\",\n  },\n  info: {\n    main: \"#00B0FF\",\n  },\n  primary: {\n    main: \"#2962FF\",\n    contrastText: \"#FFF\",\n  },\n  secondary: {\n    main: \"#FFF\",\n  },\n  success: {\n    main: \"#00E676\",\n  },\n  warning: {\n    main: \"#FFC400\",\n  },\n  text: {\n    primary: palette.grey[700],\n    secondary: palette.grey[500],\n    disabled: palette.grey[300],\n  },\n  divider: palette.grey[100],\n  background: {\n    paper: \"#FFF\",\n    default: palette.grey[50],\n  },\n  action: {\n    selectedOpacity: 0,\n    selected: palette.grey[50],\n  },\n};\n"
  },
  {
    "path": "src/core/theme/shape.ts",
    "content": "const shape = {\n  borderRadius: 16,\n};\n\nexport default shape;\n"
  },
  {
    "path": "src/core/theme/transitions.ts",
    "content": "const transitions = {\n  duration: {\n    shortest: 75,\n    shorter: 100,\n    short: 125,\n    standard: 150,\n    complex: 175,\n    enteringScreen: 115,\n    leavingScreen: 95,\n  },\n};\n\nexport default transitions;\n"
  },
  {
    "path": "src/core/theme/typography.ts",
    "content": "const typography = {\n  fontFamily: \"Nunito, sans-serif\",\n  fontWeightMedium: 700,\n  fontWeightBold: 800,\n  h1: {\n    fontWeight: 800,\n    fontSize: \"2rem\",\n    letterSpacing: 0,\n  },\n  h2: {\n    fontWeight: 800,\n    fontSize: \"1.5rem\",\n    letterSpacing: 0,\n  },\n  h3: {\n    fontWeight: 800,\n    fontSize: \"1.375rem\",\n    letterSpacing: 0,\n  },\n  h4: {\n    fontWeight: 800,\n    fontSize: \"1.25rem\",\n    letterSpacing: 0,\n  },\n  h5: {\n    fontWeight: 800,\n    fontSize: \"1.125rem\",\n    letterSpacing: 0,\n  },\n  h6: {\n    fontWeight: 700,\n    fontSize: \"1rem\",\n    letterSpacing: 0,\n  },\n  subtitle1: {\n    letterSpacing: 0,\n  },\n  subtitle2: {\n    letterSpacing: 0,\n  },\n  body1: {\n    letterSpacing: 0,\n  },\n  body2: {\n    letterSpacing: 0,\n  },\n  button: {\n    letterSpacing: 0,\n  },\n  caption: {\n    letterSpacing: 0,\n  },\n  overline: {\n    letterSpacing: 0,\n  },\n};\n\nexport default typography;\n"
  },
  {
    "path": "src/core/utils/crudUtils.ts",
    "content": "export function addOne<T>(items: T[] = [], newItem: T) {\n  return [...items, newItem];\n}\n\nexport function removeOne<T extends { id: string }>(\n  items: T[] = [],\n  itemId: string\n) {\n  return items.filter((item) => item.id !== itemId);\n}\n\nexport function removeMany<T extends { id: string }>(\n  items: T[] = [],\n  itemIds: string[]\n) {\n  return items.filter((item) => !itemIds.includes(item.id));\n}\n\nexport function updateOne<T extends { id: string }>(\n  items: T[] = [],\n  updatedItem: T\n) {\n  return items.map((item) => (item.id === updatedItem.id ? updatedItem : item));\n}\n"
  },
  {
    "path": "src/core/utils/selectUtils.ts",
    "content": "export const selectAll = (list: any, key = \"id\") =>\n  list.map((item: any) => item[key]);\n\nexport const selectOne = (selected: any, id: string) => {\n  const selectedIndex = selected.indexOf(id);\n  let newSelected: string[] = [];\n\n  if (selectedIndex === -1) {\n    newSelected = newSelected.concat(selected, id);\n  } else if (selectedIndex === 0) {\n    newSelected = newSelected.concat(selected.slice(1));\n  } else if (selectedIndex === selected.length - 1) {\n    newSelected = newSelected.concat(selected.slice(0, -1));\n  } else if (selectedIndex > 0) {\n    newSelected = newSelected.concat(\n      selected.slice(0, selectedIndex),\n      selected.slice(selectedIndex + 1)\n    );\n  }\n\n  return newSelected;\n};\n"
  },
  {
    "path": "src/index.tsx",
    "content": "import React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport { BrowserRouter } from \"react-router-dom\";\nimport App from \"./App\";\nimport \"./core/config/i18n\";\nimport \"./mocks/server\";\nimport reportWebVitals from \"./reportWebVitals\";\n\nReactDOM.render(\n  <React.StrictMode>\n    <BrowserRouter>\n      <App />\n    </BrowserRouter>\n  </React.StrictMode>,\n  document.getElementById(\"root\")\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n"
  },
  {
    "path": "src/landing/components/LandingLayout.tsx",
    "content": "import AppBar from \"@material-ui/core/AppBar\";\nimport IconButton from \"@material-ui/core/IconButton\";\nimport Paper from \"@material-ui/core/Paper\";\nimport Toolbar from \"@material-ui/core/Toolbar\";\nimport Typography from \"@material-ui/core/Typography\";\nimport SettingsIcon from \"@material-ui/icons/Settings\";\nimport React, { useState } from \"react\";\nimport Footer from \"../../core/components/Footer\";\nimport Logo from \"../../core/components/Logo\";\nimport SettingsDrawer from \"../../core/components/SettingsDrawer\";\n\ntype LandingLayoutProps = {\n  children: React.ReactNode;\n};\n\nconst LandingLayout = ({ children }: LandingLayoutProps) => {\n  const [settingsOpen, setSettingsOpen] = useState(false);\n\n  const handleSettingsToggle = () => {\n    setSettingsOpen(!settingsOpen);\n  };\n\n  return (\n    <Paper square>\n      <AppBar color=\"transparent\" position=\"relative\">\n        <Toolbar>\n          <Logo size={24} sx={{ mr: 2 }} />\n          <Typography variant=\"h6\" color=\"inherit\" noWrap sx={{ flexGrow: 1 }}>\n            {process.env.REACT_APP_NAME}\n          </Typography>\n          <IconButton\n            color=\"default\"\n            aria-label=\"settings\"\n            component=\"span\"\n            onClick={handleSettingsToggle}\n          >\n            <SettingsIcon />\n          </IconButton>\n          <SettingsDrawer\n            onDrawerToggle={handleSettingsToggle}\n            open={settingsOpen}\n          />\n        </Toolbar>\n      </AppBar>\n      <main>{children}</main>\n      <Footer />\n    </Paper>\n  );\n};\n\nexport default LandingLayout;\n"
  },
  {
    "path": "src/landing/pages/Landing.tsx",
    "content": "import Avatar from \"@material-ui/core/Avatar\";\nimport Box from \"@material-ui/core/Box\";\nimport Button from \"@material-ui/core/Button\";\nimport Container from \"@material-ui/core/Container\";\nimport List from \"@material-ui/core/List\";\nimport ListItem from \"@material-ui/core/ListItem\";\nimport ListItemAvatar from \"@material-ui/core/ListItemAvatar\";\nimport ListItemText from \"@material-ui/core/ListItemText\";\nimport Stack from \"@material-ui/core/Stack\";\nimport { useTheme } from \"@material-ui/core/styles\";\nimport Typography from \"@material-ui/core/Typography\";\nimport StarIcon from \"@material-ui/icons/Star\";\nimport { useTranslation } from \"react-i18next\";\nimport { Link as RouterLink } from \"react-router-dom\";\nimport { useAuth } from \"../../auth/contexts/AuthProvider\";\nimport LandingLayout from \"../components/LandingLayout\";\n\nconst features = [\n  { name: \"Bootstraped with Create React App\" },\n  { name: \"Components & Themes built on top of Material-UI\" },\n  { name: \"Data Fetching with React Query\" },\n  { name: \"Written in TypeScript\" },\n  { name: \"Real-world examples\" },\n  { name: \"Best Practices\" },\n  { name: \"MIT License\" },\n];\n\nconst Landing = () => {\n  const { userInfo } = useAuth();\n  const theme = useTheme();\n  const { t } = useTranslation();\n\n  return (\n    <LandingLayout>\n      <main>\n        <Box\n          sx={{\n            py: 6,\n          }}\n        >\n          <Container maxWidth=\"sm\">\n            <Typography\n              variant=\"h1\"\n              align=\"center\"\n              color=\"text.primary\"\n              marginBottom={4}\n            >\n              {t(\"landing.title\")}\n            </Typography>\n            <Stack\n              sx={{ pt: 3 }}\n              direction=\"row\"\n              spacing={2}\n              justifyContent=\"center\"\n            >\n              <Button\n                component=\"a\"\n                href={process.env.REACT_APP_SOURCE_LINK}\n                rel=\"noopener noreferrer\"\n                target=\"_blank\"\n                variant=\"outlined\"\n              >\n                {t(\"landing.cta.secondary\")}\n              </Button>\n              {userInfo ? (\n                <Button\n                  component={RouterLink}\n                  to={`/${process.env.PUBLIC_URL}/admin`}\n                  variant=\"contained\"\n                >\n                  {t(\"landing.cta.mainAuth\", { name: userInfo.firstName })}\n                </Button>\n              ) : (\n                <Button\n                  component={RouterLink}\n                  to={`/${process.env.PUBLIC_URL}/login`}\n                  variant=\"contained\"\n                >\n                  {t(\"landing.cta.main\")}\n                </Button>\n              )}\n            </Stack>\n          </Container>\n        </Box>\n        <Container sx={{ py: 6 }} maxWidth=\"md\">\n          <img\n            alt=\"Application demo\"\n            src={`img/template-${theme.palette.mode}.png`}\n            style={{\n              borderRadius: 24,\n              borderStyle: \"solid\",\n              borderWidth: 4,\n              borderColor: theme.palette.background.default,\n              width: \"100%\",\n            }}\n          />\n        </Container>\n        <Container sx={{ py: 8 }} maxWidth=\"md\">\n          <Stack alignItems=\"center\">\n            <Typography\n              variant=\"h2\"\n              align=\"center\"\n              color=\"text.primary\"\n              gutterBottom\n            >\n              {t(\"landing.features.title\")}\n            </Typography>\n            <List sx={{ pt: 3 }}>\n              {features.map((feature, index) => (\n                <ListItem key={index}>\n                  <ListItemAvatar>\n                    <Avatar>\n                      <StarIcon />\n                    </Avatar>\n                  </ListItemAvatar>\n                  <ListItemText primary={feature.name} />\n                </ListItem>\n              ))}\n            </List>\n            <Button\n              component=\"a\"\n              href={process.env.REACT_APP_SOURCE_LINK}\n              rel=\"noopener noreferrer\"\n              target=\"_blank\"\n              sx={{ mt: 3 }}\n              variant=\"outlined\"\n            >\n              {t(\"landing.features.more\")}\n            </Button>\n          </Stack>\n        </Container>\n      </main>\n    </LandingLayout>\n  );\n};\n\nexport default Landing;\n"
  },
  {
    "path": "src/mocks/activityLogs.json",
    "content": "[\n  {\n    \"id\": \"1\",\n    \"actor\": \"You\",\n    \"code\": \"eventAdded\",\n    \"createdAt\": 1617868226000,\n    \"params\": {\n      \"resource\": \"React Summit\"\n    }\n  },\n  {\n    \"id\": \"2\",\n    \"actor\": \"John Smith\",\n    \"code\": \"eventUpdated\",\n    \"createdAt\": 1617868226000,\n    \"params\": {\n      \"resource\": \"React Summit\"\n    }\n  },\n  {\n    \"id\": \"3\",\n    \"actor\": \"John Smith\",\n    \"code\": \"userDeleted\",\n    \"createdAt\": 1617868226000,\n    \"params\": {\n      \"resource\": \"John Smith\"\n    }\n  },\n  {\n    \"id\": \"4\",\n    \"actor\": \"John Smith\",\n    \"code\": \"userUpdated\",\n    \"createdAt\": 1617868226000,\n    \"params\": {\n      \"resource\": \"John Smith\"\n    }\n  },\n  {\n    \"id\": \"5\",\n    \"actor\": \"John Smith\",\n    \"code\": \"userAdded\",\n    \"createdAt\": 1617868226000,\n    \"params\": {\n      \"resource\": \"John Smith\"\n    }\n  }\n]\n"
  },
  {
    "path": "src/mocks/events.json",
    "content": "[\n  {\n    \"id\": \"1\",\n    \"title\": \"React Summit\",\n    \"description\": \"Lorem ipsum dolor sid amet\",\n    \"start\": 1519211809934,\n    \"end\": 1519211809934,\n    \"color\": \"primary\"\n  }\n]\n"
  },
  {
    "path": "src/mocks/notifications.json",
    "content": "[\n  {\n    \"id\": \"1\",\n    \"code\": \"unreadMessages\",\n    \"createdAt\": 1617868226000,\n    \"params\": {\n      \"quantity\": \"5\"\n    },\n    \"unread\": true\n  },\n  {\n    \"id\": \"2\",\n    \"code\": \"newComment\",\n    \"createdAt\": 1617868226000,\n    \"params\": {\n      \"user\": \"John Smith\"\n    },\n    \"unread\": false\n  }\n]\n"
  },
  {
    "path": "src/mocks/profileInfo.json",
    "content": "{\n  \"id\": \"1\",\n  \"avatar\": \"\",\n  \"email\": \"john@smith.com\",\n  \"firstName\": \"John\",\n  \"job\": \"Founder\",\n  \"lastName\": \"Smith\"\n}\n"
  },
  {
    "path": "src/mocks/server.ts",
    "content": "import axios from \"axios\";\nimport MockAdapter from \"axios-mock-adapter\";\nimport activityLogs from \"./activityLogs.json\";\nimport events from \"./events.json\";\nimport notifications from \"./notifications.json\";\nimport profileInfo from \"./profileInfo.json\";\nimport userInfo from \"./userInfo.json\";\nimport users from \"./users.json\";\n\nconst now = Date.now();\n\nfunction generateId() {\n  return (Math.floor(Math.random() * 10000) + 1).toString();\n}\n\n// This sets the mock adapter on the default instance\nlet mock = new MockAdapter(axios, { delayResponse: 2000 });\n\n// Activity\nmock.onGet(\"/api/activity-logs\").reply(200, activityLogs);\n\n// Auth\nmock.onPut(\"/api/password\").reply(({ data }) => [200, data]);\nmock.onPost(\"/api/forgot-password\").reply(200);\nmock.onPost(\"/api/forgot-password-submit\").reply(200);\nmock.onPost(\"/api/login\").reply(200, \"AUTHKEY123\");\nmock.onPost(\"/api/logout\").reply(200);\nmock.onPost(\"/api/register\").reply(201);\nmock\n  .onGet(\"/api/user-info\", { params: { key: \"AUTHKEY123\" } })\n  .reply(200, userInfo);\n\n// Events\nmock.onDelete(\"/api/events\").reply(({ data }) => [200, data]);\nmock.onGet(\"/api/events\").reply(\n  200,\n  events.map((e) => ({ ...e, start: now, end: now }))\n);\nmock\n  .onPost(\"/api/events\")\n  .reply(({ data }) => [201, { ...JSON.parse(data), id: generateId() }]);\nmock.onPut(\"/api/events\").reply(({ data }) => [200, data]);\n\n// Notifications\nmock.onGet(\"/api/notifications\").reply(200, notifications);\n\n// Profile\nmock.onGet(\"/api/profile-info\").reply(200, profileInfo);\nmock.onPut(\"/api/profile-info\").reply(({ data }) => [200, data]);\n\n// Users\nmock.onDelete(\"/api/users\").reply(({ data }) => [200, data]);\nmock.onGet(\"/api/users\").reply(200, users);\nmock\n  .onPost(\"/api/users\")\n  .reply(({ data }) => [201, { ...JSON.parse(data), id: generateId() }]);\nmock.onPut(\"/api/users\").reply(({ data }) => [200, data]);\n"
  },
  {
    "path": "src/mocks/userInfo.json",
    "content": "{\n  \"id\": \"1\",\n  \"avatar\": \"\",\n  \"email\": \"john@smith.com\",\n  \"firstName\": \"John\",\n  \"job\": \"Founder\",\n  \"lastName\": \"Smith\",\n  \"progress\": 75,\n  \"role\": \"Admin\"\n}\n"
  },
  {
    "path": "src/mocks/users.json",
    "content": "[\n  {\n    \"id\": \"1\",\n    \"avatar\": \"\",\n    \"disabled\": false,\n    \"email\": \"rhys@arriaga.com\",\n    \"firstName\": \"Rhys\",\n    \"gender\": \"M\",\n    \"lastName\": \"Arriaga\",\n    \"role\": \"Admin\"\n  },\n  {\n    \"id\": \"2\",\n    \"avatar\": \"\",\n    \"disabled\": false,\n    \"email\": \"laura@core.com\",\n    \"firstName\": \"Laura\",\n    \"gender\": \"F\",\n    \"lastName\": \"Core\",\n    \"role\": \"Member\"\n  },\n  {\n    \"id\": \"3\",\n    \"avatar\": \"\",\n    \"disabled\": false,\n    \"email\": \"joshua@jagger.com\",\n    \"firstName\": \"Joshua\",\n    \"gender\": \"M\",\n    \"lastName\": \"Jagger\",\n    \"role\": \"Member\"\n  },\n  {\n    \"id\": \"4\",\n    \"avatar\": \"\",\n    \"disabled\": true,\n    \"email\": \"jason@jimenez.com\",\n    \"firstName\": \"Jason\",\n    \"gender\": \"M\",\n    \"lastName\": \"Jimenez\",\n    \"role\": \"Member\"\n  },\n  {\n    \"id\": \"5\",\n    \"avatar\": \"\",\n    \"disabled\": true,\n    \"email\": \"rhonda@mcguire.com\",\n    \"firstName\": \"Rhonda\",\n    \"gender\": \"F\",\n    \"lastName\": \"Mcguire\",\n    \"role\": \"Member\"\n  },\n  {\n    \"id\": \"6\",\n    \"avatar\": \"\",\n    \"disabled\": false,\n    \"email\": \"aaron@moreno.com\",\n    \"firstName\": \"Aaron\",\n    \"gender\": \"M\",\n    \"lastName\": \"Moreno\",\n    \"role\": \"Member\"\n  },\n  {\n    \"id\": \"7\",\n    \"avatar\": \"\",\n    \"disabled\": false,\n    \"email\": \"carley@murray.com\",\n    \"firstName\": \"Carley\",\n    \"gender\": \"F\",\n    \"lastName\": \"Murray\",\n    \"role\": \"Member\"\n  },\n  {\n    \"id\": \"8\",\n    \"avatar\": \"\",\n    \"disabled\": false,\n    \"email\": \"cherise@owen.com\",\n    \"firstName\": \"Cherise\",\n    \"gender\": \"F\",\n    \"lastName\": \"Owen\",\n    \"role\": \"Member\"\n  },\n  {\n    \"id\": \"9\",\n    \"avatar\": \"\",\n    \"disabled\": false,\n    \"email\": \"nathan@romero.com\",\n    \"firstName\": \"Nathan\",\n    \"gender\": \"M\",\n    \"lastName\": \"Romero\",\n    \"role\": \"Member\"\n  },\n  {\n    \"id\": \"10\",\n    \"avatar\": \"\",\n    \"disabled\": false,\n    \"email\": \"olivia@spence.com\",\n    \"firstName\": \"Olivia\",\n    \"gender\": \"F\",\n    \"lastName\": \"Spence\",\n    \"role\": \"Member\"\n  },\n  {\n    \"id\": \"11\",\n    \"avatar\": \"\",\n    \"disabled\": false,\n    \"email\": \"elisha@wade.com\",\n    \"firstName\": \"Elisha\",\n    \"gender\": \"F\",\n    \"lastName\": \"Wade\",\n    \"role\": \"Member\"\n  }\n]\n"
  },
  {
    "path": "src/react-app-env.d.ts",
    "content": "/// <reference types=\"react-scripts\" />\n"
  },
  {
    "path": "src/reportWebVitals.ts",
    "content": "import { ReportHandler } from 'web-vitals';\n\nconst reportWebVitals = (onPerfEntry?: ReportHandler) => {\n  if (onPerfEntry && onPerfEntry instanceof Function) {\n    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n      getCLS(onPerfEntry);\n      getFID(onPerfEntry);\n      getFCP(onPerfEntry);\n      getLCP(onPerfEntry);\n      getTTFB(onPerfEntry);\n    });\n  }\n};\n\nexport default reportWebVitals;\n"
  },
  {
    "path": "src/setupTests.ts",
    "content": "// jest-dom adds custom jest matchers for asserting on DOM nodes.\n// allows you to do things like:\n// expect(element).toHaveTextContent(/react/i)\n// learn more: https://github.com/testing-library/jest-dom\nimport '@testing-library/jest-dom';\n"
  },
  {
    "path": "src/users/components/UserDialog.tsx",
    "content": "import Button from \"@material-ui/core/Button\";\nimport Checkbox from \"@material-ui/core/Checkbox\";\nimport Dialog from \"@material-ui/core/Dialog\";\nimport DialogActions from \"@material-ui/core/DialogActions\";\nimport DialogContent from \"@material-ui/core/DialogContent\";\nimport DialogTitle from \"@material-ui/core/DialogTitle\";\nimport FormControl from \"@material-ui/core/FormControl\";\nimport FormControlLabel from \"@material-ui/core/FormControlLabel\";\nimport FormLabel from \"@material-ui/core/FormLabel\";\nimport MenuItem from \"@material-ui/core/MenuItem\";\nimport Radio from \"@material-ui/core/Radio\";\nimport RadioGroup from \"@material-ui/core/RadioGroup\";\nimport TextField from \"@material-ui/core/TextField\";\nimport LoadingButton from \"@material-ui/lab/LoadingButton\";\nimport { useFormik } from \"formik\";\nimport { useTranslation } from \"react-i18next\";\nimport * as Yup from \"yup\";\nimport { User } from \"../types/user\";\n\nconst genders = [\n  { label: \"userManagement.form.gender.options.f\", value: \"F\" },\n  { label: \"userManagement.form.gender.options.m\", value: \"M\" },\n  { label: \"userManagement.form.gender.options.n\", value: \"NC\" },\n];\nconst roles = [\"Admin\", \"Member\"];\n\ntype UserDialogProps = {\n  onAdd: (user: Partial<User>) => void;\n  onClose: () => void;\n  onUpdate: (user: User) => void;\n  open: boolean;\n  processing: boolean;\n  user?: User;\n};\n\nconst UserDialog = ({\n  onAdd,\n  onClose,\n  onUpdate,\n  open,\n  processing,\n  user,\n}: UserDialogProps) => {\n  const { t } = useTranslation();\n\n  const editMode = Boolean(user && user.id);\n\n  const handleSubmit = (values: Partial<User>) => {\n    if (user && user.id) {\n      onUpdate({ ...values, id: user.id } as User);\n    } else {\n      onAdd(values);\n    }\n  };\n\n  const formik = useFormik({\n    initialValues: {\n      disabled: user ? user.disabled : false,\n      email: user ? user.email : \"\",\n      firstName: user ? user.firstName : \"\",\n      gender: user ? user.gender : \"F\",\n      lastName: user ? user.lastName : \"\",\n      role: user ? user.role : \"\",\n    },\n    validationSchema: Yup.object({\n      email: Yup.string()\n        .email(t(\"common.validations.email\"))\n        .required(t(\"common.validations.required\")),\n      firstName: Yup.string()\n        .max(20, t(\"common.validations.max\", { size: 20 }))\n        .required(t(\"common.validations.required\")),\n      lastName: Yup.string()\n        .max(30, t(\"common.validations.max\", { size: 30 }))\n        .required(t(\"common.validations.required\")),\n      role: Yup.string().required(t(\"common.validations.required\")),\n    }),\n    onSubmit: handleSubmit,\n  });\n\n  return (\n    <Dialog open={open} onClose={onClose} aria-labelledby=\"user-dialog-title\">\n      <form onSubmit={formik.handleSubmit} noValidate>\n        <DialogTitle id=\"user-dialog-title\">\n          {editMode\n            ? t(\"userManagement.modal.edit.title\")\n            : t(\"userManagement.modal.add.title\")}\n        </DialogTitle>\n        <DialogContent>\n          <TextField\n            margin=\"normal\"\n            required\n            fullWidth\n            id=\"lastName\"\n            label={t(\"userManagement.form.lastName.label\")}\n            name=\"lastName\"\n            autoComplete=\"family-name\"\n            autoFocus\n            disabled={processing}\n            value={formik.values.lastName}\n            onChange={formik.handleChange}\n            error={formik.touched.lastName && Boolean(formik.errors.lastName)}\n            helperText={formik.touched.lastName && formik.errors.lastName}\n          />\n          <TextField\n            margin=\"normal\"\n            required\n            fullWidth\n            id=\"firstName\"\n            label={t(\"userManagement.form.firstName.label\")}\n            name=\"firstName\"\n            autoComplete=\"given-name\"\n            disabled={processing}\n            value={formik.values.firstName}\n            onChange={formik.handleChange}\n            error={formik.touched.firstName && Boolean(formik.errors.firstName)}\n            helperText={formik.touched.firstName && formik.errors.firstName}\n          />\n          <FormControl component=\"fieldset\" margin=\"normal\">\n            <FormLabel component=\"legend\">\n              {t(\"userManagement.form.gender.label\")}\n            </FormLabel>\n            <RadioGroup\n              row\n              aria-label=\"gender\"\n              name=\"gender\"\n              value={formik.values.gender}\n              onChange={formik.handleChange}\n            >\n              {genders.map((gender) => (\n                <FormControlLabel\n                  key={gender.value}\n                  disabled={processing}\n                  value={gender.value}\n                  control={<Radio />}\n                  label={t(gender.label)}\n                />\n              ))}\n            </RadioGroup>\n          </FormControl>\n          <TextField\n            margin=\"normal\"\n            required\n            fullWidth\n            id=\"email\"\n            label={t(\"userManagement.form.email.label\")}\n            name=\"email\"\n            autoComplete=\"email\"\n            disabled={processing}\n            value={formik.values.email}\n            onChange={formik.handleChange}\n            error={formik.touched.email && Boolean(formik.errors.email)}\n            helperText={formik.touched.email && formik.errors.email}\n          />\n          <TextField\n            margin=\"normal\"\n            required\n            id=\"role\"\n            disabled={processing}\n            fullWidth\n            select\n            label={t(\"userManagement.form.role.label\")}\n            name=\"role\"\n            value={formik.values.role}\n            onChange={formik.handleChange}\n            error={formik.touched.role && Boolean(formik.errors.role)}\n            helperText={formik.touched.role && formik.errors.role}\n          >\n            {roles.map((role) => (\n              <MenuItem key={role} value={role}>\n                {role}\n              </MenuItem>\n            ))}\n          </TextField>\n          <FormControl component=\"fieldset\" margin=\"normal\">\n            <FormControlLabel\n              name=\"disabled\"\n              disabled={processing}\n              onChange={formik.handleChange}\n              checked={formik.values.disabled}\n              control={<Checkbox />}\n              label={t(\"userManagement.form.disabled.label\")}\n            />\n          </FormControl>\n        </DialogContent>\n        <DialogActions>\n          <Button onClick={onClose}>{t(\"common.cancel\")}</Button>\n          <LoadingButton loading={processing} type=\"submit\" variant=\"contained\">\n            {editMode\n              ? t(\"userManagement.modal.edit.action\")\n              : t(\"userManagement.modal.add.action\")}\n          </LoadingButton>\n        </DialogActions>\n      </form>\n    </Dialog>\n  );\n};\n\nexport default UserDialog;\n"
  },
  {
    "path": "src/users/components/UserTable.tsx",
    "content": "import Avatar from \"@material-ui/core/Avatar\";\nimport Box from \"@material-ui/core/Box\";\nimport Checkbox from \"@material-ui/core/Checkbox\";\nimport Chip from \"@material-ui/core/Chip\";\nimport IconButton from \"@material-ui/core/IconButton\";\nimport ListItemIcon from \"@material-ui/core/ListItemIcon\";\nimport Menu from \"@material-ui/core/Menu\";\nimport MenuItem from \"@material-ui/core/MenuItem\";\nimport Table from \"@material-ui/core/Table\";\nimport TableBody from \"@material-ui/core/TableBody\";\nimport TableCell from \"@material-ui/core/TableCell\";\nimport TableContainer from \"@material-ui/core/TableContainer\";\nimport TableHead from \"@material-ui/core/TableHead\";\nimport TablePagination from \"@material-ui/core/TablePagination\";\nimport TableRow from \"@material-ui/core/TableRow\";\nimport Typography from \"@material-ui/core/Typography\";\nimport DeleteIcon from \"@material-ui/icons/Delete\";\nimport EditIcon from \"@material-ui/icons/Edit\";\nimport MoreVertIcon from \"@material-ui/icons/MoreVert\";\nimport PersonIcon from \"@material-ui/icons/Person\";\nimport React, { useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport Empty from \"../../core/components/Empty\";\nimport * as selectUtils from \"../../core/utils/selectUtils\";\nimport { User } from \"../types/user\";\n\ninterface HeadCell {\n  id: string;\n  label: string;\n  align: \"center\" | \"left\" | \"right\";\n}\n\nconst headCells: HeadCell[] = [\n  {\n    id: \"user\",\n    align: \"left\",\n    label: \"userManagement.table.headers.user\",\n  },\n  {\n    id: \"gender\",\n    align: \"center\",\n    label: \"userManagement.table.headers.gender\",\n  },\n  {\n    id: \"role\",\n    align: \"center\",\n    label: \"userManagement.table.headers.role\",\n  },\n  {\n    id: \"status\",\n    align: \"center\",\n    label: \"userManagement.table.headers.status\",\n  },\n];\n\ninterface EnhancedTableProps {\n  numSelected: number;\n  onSelectAllClick: (event: React.ChangeEvent<HTMLInputElement>) => void;\n  rowCount: number;\n}\n\nfunction EnhancedTableHead({\n  onSelectAllClick,\n  numSelected,\n  rowCount,\n}: EnhancedTableProps) {\n  const { t } = useTranslation();\n\n  return (\n    <TableHead>\n      <TableRow sx={{ \"& th\": { border: 0 } }}>\n        <TableCell sx={{ py: 0 }}>\n          <Checkbox\n            color=\"primary\"\n            indeterminate={numSelected > 0 && numSelected < rowCount}\n            checked={rowCount > 0 && numSelected === rowCount}\n            onChange={onSelectAllClick}\n            inputProps={{\n              \"aria-label\": \"select all users\",\n            }}\n          />\n        </TableCell>\n        {headCells.map((headCell) => (\n          <TableCell key={headCell.id} align={headCell.align} sx={{ py: 0 }}>\n            {t(headCell.label)}\n          </TableCell>\n        ))}\n        <TableCell align=\"right\" sx={{ py: 0 }}>\n          {t(\"userManagement.table.headers.actions\")}\n        </TableCell>\n      </TableRow>\n    </TableHead>\n  );\n}\n\ntype UserRowProps = {\n  index: number;\n  onCheck: (id: string) => void;\n  onDelete: (userIds: string[]) => void;\n  onEdit: (user: User) => void;\n  processing: boolean;\n  selected: boolean;\n  user: User;\n};\n\nconst UserRow = ({\n  index,\n  onCheck,\n  onDelete,\n  onEdit,\n  processing,\n  selected,\n  user,\n}: UserRowProps) => {\n  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);\n  const { t } = useTranslation();\n\n  const labelId = `enhanced-table-checkbox-${index}`;\n  const openActions = Boolean(anchorEl);\n\n  const handleOpenActions = (event: React.MouseEvent<HTMLButtonElement>) => {\n    setAnchorEl(event.currentTarget);\n  };\n\n  const handleCloseActions = () => {\n    setAnchorEl(null);\n  };\n\n  const handleDelete = () => {\n    handleCloseActions();\n    onDelete([user.id]);\n  };\n\n  const handleEdit = () => {\n    handleCloseActions();\n    onEdit(user);\n  };\n\n  return (\n    <TableRow\n      aria-checked={selected}\n      tabIndex={-1}\n      key={user.id}\n      selected={selected}\n      sx={{ \"& td\": { bgcolor: \"background.paper\", border: 0 } }}\n    >\n      <TableCell\n        padding=\"checkbox\"\n        sx={{ borderTopLeftRadius: \"1rem\", borderBottomLeftRadius: \"1rem\" }}\n      >\n        <Checkbox\n          color=\"primary\"\n          checked={selected}\n          inputProps={{\n            \"aria-labelledby\": labelId,\n          }}\n          onClick={() => onCheck(user.id)}\n        />\n      </TableCell>\n      <TableCell>\n        <Box sx={{ display: \"flex\", alignItems: \"center\" }}>\n          <Avatar sx={{ mr: 3 }}>\n            <PersonIcon />\n          </Avatar>\n          <Box>\n            <Typography component=\"div\" variant=\"h6\">\n              {`${user.lastName} ${user.firstName}`}\n            </Typography>\n            <Typography color=\"textSecondary\" variant=\"body2\">\n              {user.email}\n            </Typography>\n          </Box>\n        </Box>\n      </TableCell>\n      <TableCell align=\"center\">{user.gender}</TableCell>\n      <TableCell align=\"center\">{user.role}</TableCell>\n      <TableCell align=\"center\">\n        {user.disabled ? (\n          <Chip label=\"Disabled\" />\n        ) : (\n          <Chip color=\"primary\" label=\"Active\" />\n        )}\n      </TableCell>\n      <TableCell\n        align=\"right\"\n        sx={{ borderTopRightRadius: \"1rem\", borderBottomRightRadius: \"1rem\" }}\n      >\n        <IconButton\n          id=\"user-row-menu-button\"\n          aria-label=\"user actions\"\n          aria-controls=\"user-row-menu\"\n          aria-haspopup=\"true\"\n          aria-expanded={openActions ? \"true\" : \"false\"}\n          disabled={processing}\n          onClick={handleOpenActions}\n        >\n          <MoreVertIcon />\n        </IconButton>\n        <Menu\n          id=\"user-row-menu\"\n          anchorEl={anchorEl}\n          aria-labelledby=\"user-row-menu-button\"\n          open={openActions}\n          onClose={handleCloseActions}\n          anchorOrigin={{\n            vertical: \"top\",\n            horizontal: \"right\",\n          }}\n          transformOrigin={{\n            vertical: \"top\",\n            horizontal: \"right\",\n          }}\n        >\n          <MenuItem onClick={handleEdit}>\n            <ListItemIcon>\n              <EditIcon />\n            </ListItemIcon>{\" \"}\n            {t(\"common.edit\")}\n          </MenuItem>\n          <MenuItem onClick={handleDelete}>\n            <ListItemIcon>\n              <DeleteIcon />\n            </ListItemIcon>{\" \"}\n            {t(\"common.delete\")}\n          </MenuItem>\n        </Menu>\n      </TableCell>\n    </TableRow>\n  );\n};\n\ntype UserTableProps = {\n  processing: boolean;\n  onDelete: (userIds: string[]) => void;\n  onEdit: (user: User) => void;\n  onSelectedChange: (selected: string[]) => void;\n  selected: string[];\n  users?: User[];\n};\n\nconst UserTable = ({\n  onDelete,\n  onEdit,\n  onSelectedChange,\n  processing,\n  selected,\n  users = [],\n}: UserTableProps) => {\n  const [page, setPage] = useState(0);\n  const [rowsPerPage, setRowsPerPage] = useState(5);\n\n  const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>) => {\n    if (event.target.checked) {\n      const newSelecteds = selectUtils.selectAll(users);\n      onSelectedChange(newSelecteds);\n      return;\n    }\n    onSelectedChange([]);\n  };\n\n  const handleClick = (id: string) => {\n    let newSelected: string[] = selectUtils.selectOne(selected, id);\n    onSelectedChange(newSelected);\n  };\n\n  const handleChangePage = (event: unknown, newPage: number) => {\n    setPage(newPage);\n  };\n\n  const handleChangeRowsPerPage = (\n    event: React.ChangeEvent<HTMLInputElement>\n  ) => {\n    setRowsPerPage(parseInt(event.target.value, 10));\n    setPage(0);\n  };\n\n  const isSelected = (id: string) => selected.indexOf(id) !== -1;\n\n  if (users.length === 0) {\n    return <Empty title=\"No user yet\" />;\n  }\n\n  return (\n    <React.Fragment>\n      <TableContainer>\n        <Table\n          aria-labelledby=\"tableTitle\"\n          sx={{\n            minWidth: 600,\n            borderCollapse: \"separate\",\n            borderSpacing: \"0 1rem\",\n          }}\n        >\n          <EnhancedTableHead\n            numSelected={selected.length}\n            onSelectAllClick={handleSelectAllClick}\n            rowCount={users.length}\n          />\n          <TableBody>\n            {users\n              .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)\n              .map((user, index) => (\n                <UserRow\n                  index={index}\n                  key={user.id}\n                  onCheck={handleClick}\n                  onDelete={onDelete}\n                  onEdit={onEdit}\n                  processing={processing}\n                  selected={isSelected(user.id)}\n                  user={user}\n                />\n              ))}\n          </TableBody>\n        </Table>\n      </TableContainer>\n      <TablePagination\n        rowsPerPageOptions={[5, 10, 25]}\n        component=\"div\"\n        count={users.length}\n        rowsPerPage={rowsPerPage}\n        page={page}\n        onPageChange={handleChangePage}\n        onRowsPerPageChange={handleChangeRowsPerPage}\n      />\n    </React.Fragment>\n  );\n};\n\nexport default UserTable;\n"
  },
  {
    "path": "src/users/hooks/useAddUser.ts",
    "content": "import axios from \"axios\";\nimport { useMutation, useQueryClient } from \"react-query\";\nimport { addOne } from \"../../core/utils/crudUtils\";\nimport { User } from \"../types/user\";\n\nconst addUser = async (user: User): Promise<User> => {\n  const { data } = await axios.post(\"/api/users\", user);\n  return data;\n};\n\nexport function useAddUser() {\n  const queryClient = useQueryClient();\n\n  const { isLoading, mutateAsync } = useMutation(addUser, {\n    onSuccess: (user: User) => {\n      queryClient.setQueryData<User[]>([\"users\"], (oldUsers) =>\n        addOne(oldUsers, user)\n      );\n    },\n  });\n\n  return { isAdding: isLoading, addUser: mutateAsync };\n}\n"
  },
  {
    "path": "src/users/hooks/useDeleteUsers.ts",
    "content": "import axios from \"axios\";\nimport { useMutation, useQueryClient } from \"react-query\";\nimport { removeMany } from \"../../core/utils/crudUtils\";\nimport { User } from \"../types/user\";\n\nconst deleteUsers = async (userIds: string[]): Promise<string[]> => {\n  const { data } = await axios.delete(\"/api/users\", { data: userIds });\n  return data;\n};\n\nexport function useDeleteUsers() {\n  const queryClient = useQueryClient();\n\n  const { isLoading, mutateAsync } = useMutation(deleteUsers, {\n    onSuccess: (userIds: string[]) => {\n      queryClient.setQueryData<User[]>([\"users\"], (oldUsers) =>\n        removeMany(oldUsers, userIds)\n      );\n    },\n  });\n\n  return { isDeleting: isLoading, deleteUsers: mutateAsync };\n}\n"
  },
  {
    "path": "src/users/hooks/useUpdateUser.ts",
    "content": "import axios from \"axios\";\nimport { useMutation, useQueryClient } from \"react-query\";\nimport { updateOne } from \"../../core/utils/crudUtils\";\nimport { User } from \"../types/user\";\n\nconst updateUser = async (user: User): Promise<User> => {\n  const { data } = await axios.put(\"/api/users\", user);\n  return data;\n};\n\nexport function useUpdateUser() {\n  const queryClient = useQueryClient();\n\n  const { isLoading, mutateAsync } = useMutation(updateUser, {\n    onSuccess: (user: User) => {\n      queryClient.setQueryData<User[]>([\"users\"], (oldUsers) =>\n        updateOne(oldUsers, user)\n      );\n    },\n  });\n\n  return { isUpdating: isLoading, updateUser: mutateAsync };\n}\n"
  },
  {
    "path": "src/users/hooks/useUsers.ts",
    "content": "import axios from \"axios\";\nimport { useQuery } from \"react-query\";\nimport { User } from \"../types/user\";\n\nconst fetchUsers = async (): Promise<User[]> => {\n  const { data } = await axios.get(\"/api/users\");\n  return data;\n};\n\nexport function useUsers() {\n  return useQuery(\"users\", () => fetchUsers());\n}\n"
  },
  {
    "path": "src/users/pages/UserManagement.tsx",
    "content": "import Fab from \"@material-ui/core/Fab\";\nimport AddIcon from \"@material-ui/icons/Add\";\nimport React, { useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport AdminAppBar from \"../../admin/components/AdminAppBar\";\nimport AdminToolbar from \"../../admin/components/AdminToolbar\";\nimport ConfirmDialog from \"../../core/components/ConfirmDialog\";\nimport SelectToolbar from \"../../core/components/SelectToolbar\";\nimport { useSnackbar } from \"../../core/contexts/SnackbarProvider\";\nimport UserDialog from \"../components/UserDialog\";\nimport UserTable from \"../components/UserTable\";\nimport { useAddUser } from \"../hooks/useAddUser\";\nimport { useDeleteUsers } from \"../hooks/useDeleteUsers\";\nimport { useUpdateUser } from \"../hooks/useUpdateUser\";\nimport { useUsers } from \"../hooks/useUsers\";\nimport { User } from \"../types/user\";\n\nconst UserManagement = () => {\n  const snackbar = useSnackbar();\n  const { t } = useTranslation();\n\n  const [openConfirmDeleteDialog, setOpenConfirmDeleteDialog] = useState(false);\n  const [openUserDialog, setOpenUserDialog] = useState(false);\n  const [selected, setSelected] = useState<string[]>([]);\n  const [userDeleted, setUserDeleted] = useState<string[]>([]);\n  const [userUpdated, setUserUpdated] = useState<User | undefined>(undefined);\n\n  const { addUser, isAdding } = useAddUser();\n  const { deleteUsers, isDeleting } = useDeleteUsers();\n  const { isUpdating, updateUser } = useUpdateUser();\n  const { data } = useUsers();\n\n  const processing = isAdding || isDeleting || isUpdating;\n\n  const handleAddUser = async (user: Partial<User>) => {\n    addUser(user as User)\n      .then(() => {\n        snackbar.success(\n          t(\"userManagement.notifications.addSuccess\", {\n            user: `${user.firstName} ${user.lastName}`,\n          })\n        );\n        setOpenUserDialog(false);\n      })\n      .catch(() => {\n        snackbar.error(t(\"common.errors.unexpected.subTitle\"));\n      });\n  };\n\n  const handleDeleteUsers = async () => {\n    deleteUsers(userDeleted)\n      .then(() => {\n        snackbar.success(t(\"userManagement.notifications.deleteSuccess\"));\n        setSelected([]);\n        setUserDeleted([]);\n        setOpenConfirmDeleteDialog(false);\n      })\n      .catch(() => {\n        snackbar.error(t(\"common.errors.unexpected.subTitle\"));\n      });\n  };\n\n  const handleUpdateUser = async (user: User) => {\n    updateUser(user)\n      .then(() => {\n        snackbar.success(\n          t(\"userManagement.notifications.updateSuccess\", {\n            user: `${user.firstName} ${user.lastName}`,\n          })\n        );\n        setOpenUserDialog(false);\n      })\n      .catch(() => {\n        snackbar.error(t(\"common.errors.unexpected.subTitle\"));\n      });\n  };\n\n  const handleCancelSelected = () => {\n    setSelected([]);\n  };\n\n  const handleCloseConfirmDeleteDialog = () => {\n    setOpenConfirmDeleteDialog(false);\n  };\n\n  const handleCloseUserDialog = () => {\n    setUserUpdated(undefined);\n    setOpenUserDialog(false);\n  };\n\n  const handleOpenConfirmDeleteDialog = (userIds: string[]) => {\n    setUserDeleted(userIds);\n    setOpenConfirmDeleteDialog(true);\n  };\n\n  const handleOpenUserDialog = (user?: User) => {\n    setUserUpdated(user);\n    setOpenUserDialog(true);\n  };\n\n  const handleSelectedChange = (newSelected: string[]) => {\n    setSelected(newSelected);\n  };\n\n  return (\n    <React.Fragment>\n      <AdminAppBar>\n        {!selected.length ? (\n          <AdminToolbar title={t(\"userManagement.toolbar.title\")}>\n            <Fab\n              aria-label=\"logout\"\n              color=\"primary\"\n              disabled={processing}\n              onClick={() => handleOpenUserDialog()}\n              size=\"small\"\n            >\n              <AddIcon />\n            </Fab>\n          </AdminToolbar>\n        ) : (\n          <SelectToolbar\n            processing={processing}\n            onCancel={handleCancelSelected}\n            onDelete={handleOpenConfirmDeleteDialog}\n            selected={selected}\n          />\n        )}\n      </AdminAppBar>\n      <UserTable\n        processing={processing}\n        onDelete={handleOpenConfirmDeleteDialog}\n        onEdit={handleOpenUserDialog}\n        onSelectedChange={handleSelectedChange}\n        selected={selected}\n        users={data}\n      />\n      <ConfirmDialog\n        description={t(\"userManagement.confirmations.delete\")}\n        pending={processing}\n        onClose={handleCloseConfirmDeleteDialog}\n        onConfirm={handleDeleteUsers}\n        open={openConfirmDeleteDialog}\n        title={t(\"common.confirmation\")}\n      />\n      {openUserDialog && (\n        <UserDialog\n          onAdd={handleAddUser}\n          onClose={handleCloseUserDialog}\n          onUpdate={handleUpdateUser}\n          open={openUserDialog}\n          processing={processing}\n          user={userUpdated}\n        />\n      )}\n    </React.Fragment>\n  );\n};\n\nexport default UserManagement;\n"
  },
  {
    "path": "src/users/types/user.ts",
    "content": "export interface User {\n  id: string;\n  avatar?: string;\n  disabled: boolean;\n  email: string;\n  firstName: string;\n  gender?: \"F\" | \"M\" | \"NC\";\n  lastName: string;\n  role: string;\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\n      \"dom\",\n      \"dom.iterable\",\n      \"esnext\"\n    ],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\"\n  },\n  \"include\": [\n    \"src\"\n  ]\n}\n"
  }
]