From 4783bd084298ba64a6a8fa8dabc0e8412e0adf55 Mon Sep 17 00:00:00 2001 From: "lauren.mieczkowski" Date: Tue, 21 Nov 2023 09:26:01 -0700 Subject: [PATCH 001/157] added material ui version 0 --- .env.development | 6 + .env.production | 6 + .env.staging | 6 + .gitignore | 16 +- CODE_OF_CONDUCT.md | 67 - LICENSE | 7 + LICENSE.txt | 13 - README.md | 95 +- components/counter.tsx | 12 - components/icons.tsx | 147 - components/navbar.tsx | 94 - components/primitives.ts | 53 - components/theme-switch.tsx | 77 - config/fonts.ts | 11 - config/site.ts | 25 - eslintrc.json | 3 - layouts/default.tsx | 62 - layouts/head.tsx | 20 - next-env.d.ts | 5 - next.config.js | 4 - package.json | 112 +- pages/_app.tsx | 20 - pages/api/api.ts | 16 - pages/documentation.tsx | 10 - pages/index.tsx | 48 - pages/playground.tsx | 66 - postcss.config.js | 6 - public/404.html | 50 + public/favicon.ico | Bin 11416 -> 23395 bytes public/findr_red_blocky.ico | Bin 0 -> 410598 bytes public/img/portrait-1.jpg | Bin 0 -> 26752 bytes public/img/portrait-2.jpg | Bin 0 -> 22907 bytes public/img/portrait-3.jpg | Bin 0 -> 20292 bytes public/img/startup.svg | 1 + public/img/template-dark.png | Bin 0 -> 57551 bytes public/img/template-light.png | Bin 0 -> 56603 bytes public/index.html | 93 + public/locales/en/translation.json | 431 + public/locales/fr/translation.json | 421 + public/logo.svg | 18 + public/logo192.png | Bin 0 -> 3498 bytes public/logo512.png | Bin 0 -> 10444 bytes public/manifest.json | 25 + public/next.svg | 1 - public/robots.txt | 3 + public/vercel.svg | 1 - src/App.test.tsx | 9 + src/App.tsx | 53 + src/AppRoutes.tsx | 107 + src/admin/components/AdminAppBar.tsx | 27 + src/admin/components/AdminDrawer.tsx | 207 + src/admin/components/AdminToolbar.tsx | 37 + src/admin/components/RecentNotifications.tsx | 140 + src/admin/config/activity.ts | 7 + src/admin/config/notification.ts | 4 + src/admin/hooks/useActivityLogs.ts | 12 + src/admin/hooks/useNotifications.ts | 14 + src/admin/hooks/useProfileInfo.ts | 12 + src/admin/hooks/useUpdateProfileInfo.ts | 22 + src/admin/pages/Admin.tsx | 41 + src/admin/pages/Dashboard.tsx | 105 + src/admin/pages/Faq.tsx | 79 + src/admin/pages/HelpCenter.tsx | 136 + src/admin/pages/Home.tsx | 38 + src/admin/pages/Profile.tsx | 117 + src/admin/pages/ProfileActivity.tsx | 62 + src/admin/pages/ProfileInformation.tsx | 149 + src/admin/pages/ProfilePassword.tsx | 122 + src/admin/types/activityLog.ts | 7 + src/admin/types/notification.ts | 10 + src/admin/types/profileInfo.ts | 9 + src/admin/widgets/AchievementWidget.tsx | 49 + src/admin/widgets/ActivityWidget.tsx | 88 + src/admin/widgets/BudgetWidget.tsx | 77 + src/admin/widgets/CircleProgressWidget.tsx | 63 + src/admin/widgets/DevicesWidget.tsx | 82 + src/admin/widgets/FollowersWidget.tsx | 72 + src/admin/widgets/OverviewWidget.tsx | 26 + src/admin/widgets/PersonalTargetsWidget.tsx | 61 + src/admin/widgets/ProgressWidget.tsx | 47 + src/admin/widgets/SalesByAgeWidget.tsx | 80 + src/admin/widgets/SalesByCategoryWidget.tsx | 66 + src/admin/widgets/SalesHistoryWidget.tsx | 83 + src/admin/widgets/TeamProgressWidget.tsx | 111 + src/admin/widgets/ViewsWidget.tsx | 117 + src/admin/widgets/WelcomeWidget.tsx | 43 + src/auth/contexts/AuthProvider.tsx | 82 + src/auth/hooks/useForgotPassword.ts | 12 + src/auth/hooks/useForgotPasswordSubmit.ts | 21 + src/auth/hooks/useLogin.ts | 19 + src/auth/hooks/useLogout.ts | 13 + src/auth/hooks/useRegister.ts | 13 + src/auth/hooks/useUpdatePassword.ts | 21 + src/auth/hooks/useUserInfo.ts | 14 + src/auth/pages/ForgotPassword.tsx | 96 + src/auth/pages/ForgotPasswordSubmit.tsx | 135 + src/auth/pages/Login.tsx | 122 + src/auth/pages/Register.tsx | 166 + src/auth/types/userInfo.ts | 10 + src/core/assets/403.svg | 1 + src/core/assets/404.svg | 1 + src/core/assets/confirm.svg | 1 + src/core/assets/constructions.svg | 1 + src/core/assets/empty.svg | 1 + src/core/assets/error.svg | 1 + src/core/assets/findr_ash.svg | 76 + src/core/assets/findr_cinder.svg | 76 + src/core/assets/findr_red.svg | 76 + src/core/assets/findr_red_blocky.svg | 15 + src/core/assets/help.svg | 5 + src/core/assets/logo.svg | 11 + src/core/assets/success.svg | 1 + src/core/assets/welcome.svg | 1 + src/core/components/BoxedLayout.tsx | 64 + src/core/components/ConfirmDialog.tsx | 66 + src/core/components/Empty.tsx | 13 + src/core/components/Footer.tsx | 25 + src/core/components/Loader.tsx | 31 + src/core/components/Logo.tsx | 17 + src/core/components/PrivateRoute.tsx | 25 + src/core/components/QueryWrapper.tsx | 38 + src/core/components/Result.tsx | 61 + src/core/components/SelectToolbar.tsx | 49 + src/core/components/SettingsDrawer.tsx | 162 + src/core/components/SvgContainer.tsx | 28 + src/core/config/i18n.ts | 19 + src/core/config/layout.ts | 2 + src/core/contexts/SettingsProvider.tsx | 96 + src/core/contexts/SnackbarProvider.tsx | 76 + src/core/hooks/useDateLocale.ts | 18 + src/core/hooks/useLocalStorage.ts | 40 + src/core/hooks/usePageTracking.ts | 25 + src/core/pages/Forbidden.tsx | 30 + src/core/pages/NotFound.tsx | 30 + src/core/pages/UnderConstructions.tsx | 30 + src/core/theme/components.tsx | 316 + src/core/theme/index.ts | 32 + src/core/theme/mixins.ts | 13 + src/core/theme/palette.ts | 100 + src/core/theme/shape.ts | 5 + src/core/theme/transitions.ts | 13 + src/core/theme/typography.ts | 58 + src/core/utils/crudUtils.ts | 24 + src/core/utils/selectUtils.ts | 22 + src/devices/components/DeviceDialog.tsx | 227 + src/devices/components/DeviceTable.tsx | 330 + src/devices/hooks/useAddDevice.ts | 23 + src/devices/hooks/useDeleteDevices.ts | 23 + src/devices/hooks/useDevices.ts | 12 + src/devices/hooks/useUpdateDevice.ts | 23 + src/devices/pages/DeviceManagement.tsx | 159 + src/devices/types/device.ts | 10 + src/index.tsx | 21 + src/landing/components/LandingLayout.tsx | 51 + src/landing/pages/Landing.tsx | 137 + src/mocks/activityLogs.json | 47 + src/mocks/devices.json | 112 + src/mocks/events.json | 10 + src/mocks/iotRegistration.json | 16 + src/mocks/notifications.json | 20 + src/mocks/profileInfo.json | 8 + src/mocks/server.ts | 57 + src/mocks/userInfo.json | 10 + src/react-app-env.d.ts | 1 + src/reportWebVitals.ts | 15 + src/setupTests.ts | 5 + styles/globals.css | 3 - tailwind.config.js | 15 - tsconfig.json | 33 +- tsconfig.schema.json | 1286 -- types/index.ts | 5 - yarn.lock | 12931 +++++++++++++++++ 172 files changed, 20582 insertions(+), 2169 deletions(-) create mode 100644 .env.development create mode 100644 .env.production create mode 100644 .env.staging delete mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE delete mode 100644 LICENSE.txt delete mode 100644 components/counter.tsx delete mode 100644 components/icons.tsx delete mode 100644 components/navbar.tsx delete mode 100644 components/primitives.ts delete mode 100644 components/theme-switch.tsx delete mode 100644 config/fonts.ts delete mode 100644 config/site.ts delete mode 100644 eslintrc.json delete mode 100644 layouts/default.tsx delete mode 100644 layouts/head.tsx delete mode 100644 next-env.d.ts delete mode 100644 next.config.js delete mode 100644 pages/_app.tsx delete mode 100644 pages/api/api.ts delete mode 100644 pages/documentation.tsx delete mode 100644 pages/index.tsx delete mode 100644 pages/playground.tsx delete mode 100644 postcss.config.js create mode 100644 public/404.html create mode 100644 public/findr_red_blocky.ico create mode 100644 public/img/portrait-1.jpg create mode 100644 public/img/portrait-2.jpg create mode 100644 public/img/portrait-3.jpg create mode 100644 public/img/startup.svg create mode 100644 public/img/template-dark.png create mode 100644 public/img/template-light.png create mode 100644 public/index.html create mode 100644 public/locales/en/translation.json create mode 100644 public/locales/fr/translation.json create mode 100644 public/logo.svg create mode 100644 public/logo192.png create mode 100644 public/logo512.png create mode 100644 public/manifest.json delete mode 100644 public/next.svg create mode 100644 public/robots.txt delete mode 100644 public/vercel.svg create mode 100644 src/App.test.tsx create mode 100644 src/App.tsx create mode 100644 src/AppRoutes.tsx create mode 100644 src/admin/components/AdminAppBar.tsx create mode 100644 src/admin/components/AdminDrawer.tsx create mode 100644 src/admin/components/AdminToolbar.tsx create mode 100644 src/admin/components/RecentNotifications.tsx create mode 100644 src/admin/config/activity.ts create mode 100644 src/admin/config/notification.ts create mode 100644 src/admin/hooks/useActivityLogs.ts create mode 100644 src/admin/hooks/useNotifications.ts create mode 100644 src/admin/hooks/useProfileInfo.ts create mode 100644 src/admin/hooks/useUpdateProfileInfo.ts create mode 100644 src/admin/pages/Admin.tsx create mode 100644 src/admin/pages/Dashboard.tsx create mode 100644 src/admin/pages/Faq.tsx create mode 100644 src/admin/pages/HelpCenter.tsx create mode 100644 src/admin/pages/Home.tsx create mode 100644 src/admin/pages/Profile.tsx create mode 100644 src/admin/pages/ProfileActivity.tsx create mode 100644 src/admin/pages/ProfileInformation.tsx create mode 100644 src/admin/pages/ProfilePassword.tsx create mode 100644 src/admin/types/activityLog.ts create mode 100644 src/admin/types/notification.ts create mode 100644 src/admin/types/profileInfo.ts create mode 100644 src/admin/widgets/AchievementWidget.tsx create mode 100644 src/admin/widgets/ActivityWidget.tsx create mode 100644 src/admin/widgets/BudgetWidget.tsx create mode 100644 src/admin/widgets/CircleProgressWidget.tsx create mode 100644 src/admin/widgets/DevicesWidget.tsx create mode 100644 src/admin/widgets/FollowersWidget.tsx create mode 100644 src/admin/widgets/OverviewWidget.tsx create mode 100644 src/admin/widgets/PersonalTargetsWidget.tsx create mode 100644 src/admin/widgets/ProgressWidget.tsx create mode 100644 src/admin/widgets/SalesByAgeWidget.tsx create mode 100644 src/admin/widgets/SalesByCategoryWidget.tsx create mode 100644 src/admin/widgets/SalesHistoryWidget.tsx create mode 100644 src/admin/widgets/TeamProgressWidget.tsx create mode 100644 src/admin/widgets/ViewsWidget.tsx create mode 100644 src/admin/widgets/WelcomeWidget.tsx create mode 100644 src/auth/contexts/AuthProvider.tsx create mode 100644 src/auth/hooks/useForgotPassword.ts create mode 100644 src/auth/hooks/useForgotPasswordSubmit.ts create mode 100644 src/auth/hooks/useLogin.ts create mode 100644 src/auth/hooks/useLogout.ts create mode 100644 src/auth/hooks/useRegister.ts create mode 100644 src/auth/hooks/useUpdatePassword.ts create mode 100644 src/auth/hooks/useUserInfo.ts create mode 100644 src/auth/pages/ForgotPassword.tsx create mode 100644 src/auth/pages/ForgotPasswordSubmit.tsx create mode 100644 src/auth/pages/Login.tsx create mode 100644 src/auth/pages/Register.tsx create mode 100644 src/auth/types/userInfo.ts create mode 100644 src/core/assets/403.svg create mode 100644 src/core/assets/404.svg create mode 100644 src/core/assets/confirm.svg create mode 100644 src/core/assets/constructions.svg create mode 100644 src/core/assets/empty.svg create mode 100644 src/core/assets/error.svg create mode 100644 src/core/assets/findr_ash.svg create mode 100644 src/core/assets/findr_cinder.svg create mode 100644 src/core/assets/findr_red.svg create mode 100644 src/core/assets/findr_red_blocky.svg create mode 100644 src/core/assets/help.svg create mode 100644 src/core/assets/logo.svg create mode 100644 src/core/assets/success.svg create mode 100644 src/core/assets/welcome.svg create mode 100644 src/core/components/BoxedLayout.tsx create mode 100644 src/core/components/ConfirmDialog.tsx create mode 100644 src/core/components/Empty.tsx create mode 100644 src/core/components/Footer.tsx create mode 100644 src/core/components/Loader.tsx create mode 100644 src/core/components/Logo.tsx create mode 100644 src/core/components/PrivateRoute.tsx create mode 100644 src/core/components/QueryWrapper.tsx create mode 100644 src/core/components/Result.tsx create mode 100644 src/core/components/SelectToolbar.tsx create mode 100644 src/core/components/SettingsDrawer.tsx create mode 100644 src/core/components/SvgContainer.tsx create mode 100644 src/core/config/i18n.ts create mode 100644 src/core/config/layout.ts create mode 100644 src/core/contexts/SettingsProvider.tsx create mode 100644 src/core/contexts/SnackbarProvider.tsx create mode 100644 src/core/hooks/useDateLocale.ts create mode 100644 src/core/hooks/useLocalStorage.ts create mode 100644 src/core/hooks/usePageTracking.ts create mode 100644 src/core/pages/Forbidden.tsx create mode 100644 src/core/pages/NotFound.tsx create mode 100644 src/core/pages/UnderConstructions.tsx create mode 100644 src/core/theme/components.tsx create mode 100644 src/core/theme/index.ts create mode 100644 src/core/theme/mixins.ts create mode 100644 src/core/theme/palette.ts create mode 100644 src/core/theme/shape.ts create mode 100644 src/core/theme/transitions.ts create mode 100644 src/core/theme/typography.ts create mode 100644 src/core/utils/crudUtils.ts create mode 100644 src/core/utils/selectUtils.ts create mode 100644 src/devices/components/DeviceDialog.tsx create mode 100644 src/devices/components/DeviceTable.tsx create mode 100644 src/devices/hooks/useAddDevice.ts create mode 100644 src/devices/hooks/useDeleteDevices.ts create mode 100644 src/devices/hooks/useDevices.ts create mode 100644 src/devices/hooks/useUpdateDevice.ts create mode 100644 src/devices/pages/DeviceManagement.tsx create mode 100644 src/devices/types/device.ts create mode 100644 src/index.tsx create mode 100644 src/landing/components/LandingLayout.tsx create mode 100644 src/landing/pages/Landing.tsx create mode 100644 src/mocks/activityLogs.json create mode 100644 src/mocks/devices.json create mode 100644 src/mocks/events.json create mode 100644 src/mocks/iotRegistration.json create mode 100644 src/mocks/notifications.json create mode 100644 src/mocks/profileInfo.json create mode 100644 src/mocks/server.ts create mode 100644 src/mocks/userInfo.json create mode 100644 src/react-app-env.d.ts create mode 100644 src/reportWebVitals.ts create mode 100644 src/setupTests.ts delete mode 100644 styles/globals.css delete mode 100644 tailwind.config.js delete mode 100644 tsconfig.schema.json delete mode 100644 types/index.ts create mode 100644 yarn.lock diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..ff0a4d4 --- /dev/null +++ b/.env.development @@ -0,0 +1,6 @@ +REACT_APP_NAME=FINDR (Dev) +REACT_APP_CONTACT_MAIL=mss@dish.com +REACT_APP_SOURCE_LINK=https://github.com/DISHDevEx/findr-orchestrator +REACT_APP_SUPPORT_LINK=https://github.com/DISHDevEx/findr +REACT_APP_GA_TRACKING_ID= +REACT_APP_SENTRY_DSN= \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..081a129 --- /dev/null +++ b/.env.production @@ -0,0 +1,6 @@ +REACT_APP_NAME=FINDR +REACT_APP_CONTACT_MAIL=m6v3l9@gmail.com +REACT_APP_SOURCE_LINK=https://github.com/DISHDevEx/findr-orchestrator +REACT_APP_SUPPORT_LINK=https://github.com/DISHDevEx/findr +REACT_APP_GA_TRACKING_ID= +REACT_APP_SENTRY_DSN= \ No newline at end of file diff --git a/.env.staging b/.env.staging new file mode 100644 index 0000000..bec588d --- /dev/null +++ b/.env.staging @@ -0,0 +1,6 @@ +REACT_APP_NAME=FINDR (Staging) +REACT_APP_CONTACT_MAIL=m6v3l9@gmail.com +REACT_APP_SOURCE_LINK=https://github.com/DISHDevEx/findr-orchestrator +REACT_APP_SUPPORT_LINK=https://github.com/DISHDevEx/findr +REACT_APP_GA_TRACKING_ID= +REACT_APP_SENTRY_DSN= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5a9f3c0..bdee10e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,10 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies -package-lock.json -dist/ node_modules +/node_modules /.pnp .pnp.js -.next -.npmrc # testing /coverage @@ -16,18 +13,7 @@ node_modules /build # misc -# Editors -.vscode/ -.idea/ - -# Vagrant -.vagrant/ - -# Mac/OSX .DS_Store - -# Windows -Thumbs.db .env.local .env.development.local .env.test.local diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 0eede75..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,67 +0,0 @@ -Contributor Covenant Code of Conduct - -Our Pledge - -We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. - -Our Standards - -Examples of behavior that contributes to a positive environment for our community include: - -Demonstrating empathy and kindness toward other people -Being respectful of differing opinions, viewpoints, and experiences -Giving and gracefully accepting constructive feedback -Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -Focusing on what is best not just for us as individuals, but for the overall community -Examples of unacceptable behavior include: - -The use of sexualized language or imagery, and sexual attention or advances of any kind -Trolling, insulting or derogatory comments, and personal or political attacks -Public or private harassment -Publishing others’ private information, such as a physical or email address, without their explicit permission -Other conduct which could reasonably be considered inappropriate in a professional setting -Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. - -Scope - -This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. - -Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at devex@dish.com. - -All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. - -Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: - -Correction Community Impact: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. -Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. - -Warning -Community Impact: A violation through a single incident or series of Actions. - -Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. - -Temporary Ban -Community Impact: A serious violation of community standards, including sustained inappropriate behavior. - -Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. - -Violating these terms may lead to a permanent ban. - -Permanent Ban -Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. - -Consequence: A permanent ban from any sort of public interaction within the Community. - -Attribution - -This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html. Community Impact Guidelines were inspired by Mozilla’s code of conduct enforcement ladder. For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8999da7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright 2021 Vaniya + +Permission 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: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE 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. \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 80f654d..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,13 +0,0 @@ -© 2023, DISH Network L.L.C. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/README.md b/README.md index 5c655d4..192fa2d 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,84 @@ -# FINDR-UI -User Interface for FINDR service +

+ React Material Admin logo

+

-## Technologies Used +

React Material Admin

+

+react-material-admin is a free and open-source admin application including many real-world examples. It is based on React and Material-UI. +

-- [Next.js 13](https://nextjs.org/docs/getting-started) -- [NextUI v2](https://nextui.org/) -- [Tailwind CSS](https://tailwindcss.com/) -- [Tailwind Variants](https://tailwind-variants.org) -- [TypeScript](https://www.typescriptlang.org/) -- [Framer Motion](https://www.framer.com/motion/) -- [next-themes](https://github.com/pacocoursey/next-themes) +[![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/) -## How to Use -### Install dependencies +## Getting Started -```bash -npm install ``` +#Clone repo, navigate to root and run: +rm -rf node_modules +rm -rf package-lock.json +yarn config set "strict-ssl" false -g +yarn install +export NODE_OPTIONS=--openssl-legacy-provider +yarn run start -### Run the development server +``` + +This will automatically open [http://localhost:3000](http://localhost:3000). + +## Features + +``` +- Admin + - Home + - Dashboard + - FAQ + - Help Center + - Profile Activity + - Profile Information + - Profile Password +- Auth + - Forgot Password + - Forgot Password Submit + - Login + - Register +- Core + - Forbidden + - Not Found + - Under Constructions +- Landing +- User Management +``` + +## Technologies + +| Package | Description | Docs | +| --------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------- | +| Analytics | Google Analytics | [Docs](https://analytics.google.com/analytics/web/react-ga) | +| Bundle Size Analyzer | Source map explorer | [Docs](https://create-react-app.dev/docs/analyzing-the-bundle-size) | +| Charts | Recharts | [Docs](https://recharts.org/) | +| CI | Github CI | [Docs]() | +| Code Splitting | Route-based code splitting (included in React) | [Docs](https://reactjs.org/docs/code-splitting.html#route-based-code-splitting) | +| Components | Material-UI | [Docs](https://material-ui.com/) | +| Data Fetching | React Query Toolkit | [Docs](https://react-query.tanstack.com/) | +| Deployment | Github Pages | [Docs](https://create-react-app.dev/docs/deployment#github-pages) | +| Environment Variables | Dotenv (included in Create React App) | [Docs](https://create-react-app.dev/docs/adding-custom-environment-variables) | +| Error Monitoring | Sentry | [Docs](https://docs.sentry.io/platforms/javascript/guides/react/) | +| Form | Formik | [Docs](https://formik.org/) | +| I18N | react-i18next | [Docs](https://react.i18next.com/) | +| Routing | React Router | [Docs](https://reactrouter.com/) | +| Theming (+ dark mode) | Material-UI | [Docs](https://material-ui.com/customization/theming/) | +| Toolchain | Create React App | [Docs](https://create-react-app.dev/) | +| TypeScript | TypeScript | [Docs](https://create-react-app.dev/docs/adding-typescript/) | +| Validation | Yup | [Docs](https://github.com/jquense/yup) | + +## Coming Soon + +| Package | Description | Docs | +| ------------ | ------------------------------------------- | ------------------------------- | +| Drag & Drop | Add Projects page with Drag & Drop features | | +| E2E Testing | Cypress | [Docs](https://www.cypress.io/) | +| Unit Testing | Jest | [Docs](https://jestjs.io/) | + +## License -```bash -npm run dev -``` \ No newline at end of file +This project is licensed under the terms of the +[MIT license](/LICENSE). diff --git a/components/counter.tsx b/components/counter.tsx deleted file mode 100644 index a7d9429..0000000 --- a/components/counter.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { useState } from "react"; -import { Button } from "@nextui-org/react"; - -export const Counter = () => { - const [count, setCount] = useState(0); - - return ( - - ); -}; \ No newline at end of file diff --git a/components/icons.tsx b/components/icons.tsx deleted file mode 100644 index 35baf48..0000000 --- a/components/icons.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import * as React from "react"; -import { IconSvgProps } from "../types"; - -export const Logo: React.FC = ({ - size = 36, - width, - height, - ...props -}) => ( - - - -); - -export const GithubIcon: React.FC = ({ - size = 24, - width, - height, - ...props -}) => { - return ( - - - - ); -}; - -export const MoonFilledIcon = ({ - size = 24, - width, - height, - ...props -}: IconSvgProps) => ( - -); - -export const SunFilledIcon = ({ - size = 24, - width, - height, - ...props -}: IconSvgProps) => ( - -); - -export const SearchIcon = (props: IconSvgProps) => ( - -); - -export const NextUILogo: React.FC = (props) => { - const { width, height = 40 } = props; - - return ( - - - - - - ); -}; diff --git a/components/navbar.tsx b/components/navbar.tsx deleted file mode 100644 index 5454889..0000000 --- a/components/navbar.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React from "react"; -import { - Navbar as NextUINavbar, - NavbarContent, - NavbarMenu, - NavbarMenuToggle, - NavbarBrand, - NavbarItem, -} from "@nextui-org/navbar"; -import { Link } from "@nextui-org/link"; -import { Input } from "@nextui-org/input"; -import { link as linkStyles } from "@nextui-org/theme"; -import { siteConfig } from "../config/site"; -import NextLink from "next/link"; -import clsx from "clsx"; - -import { ThemeSwitch } from "./theme-switch"; -import { - GithubIcon, - SearchIcon, - Logo -} from "./icons"; - -export const Navbar = () => { - const searchInput = ( - - } - type="search" - /> - ); - - return ( - - - - - -

FINDR

-
-
-
    - {siteConfig.navItems.map((item) => ( - - - {item.label} - - - ))} -
-
- - - - - - - - - {searchInput} - - - - - - - - - - - - {searchInput} - -
- ); -}; diff --git a/components/primitives.ts b/components/primitives.ts deleted file mode 100644 index 5388287..0000000 --- a/components/primitives.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { tv } from "tailwind-variants"; - -export const title = tv({ - base: "tracking-tight inline font-semibold", - variants: { - color: { - violet: "from-[#FF1CF7] to-[#b249f8]", - yellow: "from-[#FF705B] to-[#FFB457]", - blue: "from-[#5EA2EF] to-[#0072F5]", - cyan: "from-[#00b7fa] to-[#01cfea]", - green: "from-[#6FEE8D] to-[#17c964]", - pink: "from-[#FF72E1] to-[#F54C7A]", - foreground: "dark:from-[#FFFFFF] dark:to-[#4B4B4B]", - }, - size: { - sm: "text-3xl lg:text-4xl", - md: "text-[2.3rem] lg:text-5xl leading-9", - lg: "text-4xl lg:text-6xl", - }, - fullWidth: { - true: "w-full block", - }, - }, - defaultVariants: { - size: "md", - }, - compoundVariants: [ - { - color: [ - "violet", - "yellow", - "blue", - "cyan", - "green", - "pink", - "foreground", - ], - class: "bg-clip-text text-transparent bg-gradient-to-b", - }, - ], -}); - -export const subtitle = tv({ - base: "w-full md:w-1/2 my-2 text-lg lg:text-xl text-default-600 block max-w-full", - variants: { - fullWidth: { - true: "!w-full", - }, - }, - defaultVariants:{ - fullWidth: true - } -}); diff --git a/components/theme-switch.tsx b/components/theme-switch.tsx deleted file mode 100644 index a9fa69e..0000000 --- a/components/theme-switch.tsx +++ /dev/null @@ -1,77 +0,0 @@ -"use client"; - -import { FC } from "react"; -import { VisuallyHidden } from "@react-aria/visually-hidden"; -import { SwitchProps, useSwitch } from "@nextui-org/switch"; -import { useTheme } from "next-themes"; -import {useIsSSR} from "@react-aria/ssr"; -import clsx from "clsx"; - -import { SunFilledIcon, MoonFilledIcon } from "./icons"; - -export interface ThemeSwitchProps { - className?: string; - classNames?: SwitchProps["classNames"]; -} - -export const ThemeSwitch: FC = ({ - className, - classNames, -}) => { - const { theme, setTheme } = useTheme(); - const isSSR = useIsSSR(); - - const onChange = () => { - theme === "light" ? setTheme("dark") : setTheme("light"); - }; - - const { - Component, - slots, - isSelected, - getBaseProps, - getInputProps, - getWrapperProps, - } = useSwitch({ - isSelected: theme === "light", - "aria-label": `Switch to ${theme === "light" ? "dark" : "light"} mode`, - onChange, - }); - - return ( - - - - -
- {!isSelected || isSSR ? : } -
-
- ); -}; diff --git a/config/fonts.ts b/config/fonts.ts deleted file mode 100644 index b4411e2..0000000 --- a/config/fonts.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Fira_Code as FontMono, Inter as FontSans } from "next/font/google" - -export const fontSans = FontSans({ - subsets: ["latin"], - variable: "--font-sans", -}) - -export const fontMono = FontMono({ - subsets: ["latin"], - variable: "--font-mono", -}) diff --git a/config/site.ts b/config/site.ts deleted file mode 100644 index 2f093b1..0000000 --- a/config/site.ts +++ /dev/null @@ -1,25 +0,0 @@ -export type SiteConfig = typeof siteConfig; - -export const siteConfig = { - name: "FINDR - Unified connectivity service", - description: "simple-to-use unified connectivity service to observe, interact, and onboard industry-specific devices across a wide range of network & data protocols", - navItems: [ - { - label: "Home", - href: "/app", - }, - { - label: "Documentation", - href: "/documentation", - }, - { - label: "Product", - href: "/product", - }, - ], - links: { - github: "https://github.com/DISHDevEx/findr", - docs: "https://github.com/DISHDevEx/findr/wiki", - playground: "/playground" - }, -}; diff --git a/eslintrc.json b/eslintrc.json deleted file mode 100644 index bffb357..0000000 --- a/eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/layouts/default.tsx b/layouts/default.tsx deleted file mode 100644 index c49005d..0000000 --- a/layouts/default.tsx +++ /dev/null @@ -1,62 +0,0 @@ -//import "../styles/globals.css"; -import React from "react"; -import { Metadata } from "next"; -import { siteConfig } from "../config/site"; -import { fontSans } from "../config/fonts"; -import { Navbar } from "../components/navbar"; -import { Link } from "@nextui-org/link"; -import clsx from "clsx"; - -export const metadata: Metadata = { - title: { - default: siteConfig.name, - template: `%s - ${siteConfig.name}`, - }, - description: siteConfig.description, - themeColor: [ - { media: "(prefers-color-scheme: light)", color: "white" }, - { media: "(prefers-color-scheme: dark)", color: "black" }, - ], - icons: { - icon: "/favicon.ico", - shortcut: "/favicon-16x16.png", - apple: "/apple-touch-icon.png", - }, -}; - -export default function DefaultLayout({ - children, -}: { - children: React.ReactNode; -}) { - return ( - - - -
- -
- {children} -
-
- - Powered by -

DISH Wireless

- -
-
- - - - ); -} diff --git a/layouts/head.tsx b/layouts/head.tsx deleted file mode 100644 index beb6fff..0000000 --- a/layouts/head.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from "react"; -import NextHead from "next/head"; -import { siteConfig } from "../config/site"; - -export const Head = () => { - return ( - - {siteConfig.name} - - - - - - - ); -}; \ No newline at end of file diff --git a/next-env.d.ts b/next-env.d.ts deleted file mode 100644 index 4f11a03..0000000 --- a/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/next.config.js b/next.config.js deleted file mode 100644 index 767719f..0000000 --- a/next.config.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = {} - -module.exports = nextConfig diff --git a/package.json b/package.json index c31d1b2..2818db2 100644 --- a/package.json +++ b/package.json @@ -1,48 +1,76 @@ { - "name": "next-app-template", - "version": "0.0.1", - "private": true, + "name": "react-material-admin", + "version": "0.1.0-alpha", + "description": "Free and open-source admin application made with React", + "author": "m6v3l9 ", + "homepage": "https://m6v3l9.github.io/react-material-admin", + "license": "MIT", + "dependencies": { + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", + "@material-ui/core": "^5.0.0-alpha.35", + "@material-ui/icons": "^5.0.0-alpha.35", + "@material-ui/lab": "5.0.0-alpha.35", + "@mui/icons-material": "^5.14.16", + "@mui/material": "^5.14.17", + "@sentry/react": "^6.4.1", + "@testing-library/jest-dom": "^5.11.4", + "@testing-library/react": "^11.1.0", + "@testing-library/user-event": "^12.1.10", + "@types/jest": "^26.0.15", + "@types/node": "^12.0.0", + "@types/react": "^17.0.0", + "@types/react-dom": "^17.0.0", + "axios": "^0.21.1", + "axios-mock-adapter": "^1.19.0", + "date-fns": "^2.19.0", + "env-cmd": "^10.1.0", + "formik": "^2.2.6", + "gh-pages": "^3.1.0", + "history": "^5.0.0", + "i18next": "^20.1.0", + "i18next-browser-languagedetector": "^6.1.0", + "i18next-xhr-backend": "^3.2.2", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-error-boundary": "^3.1.3", + "react-i18next": "^11.8.11", + "react-query": "^3.16.0", + "react-router": "6.0.0-beta.0", + "react-router-dom": "6.0.0-beta.0", + "react-scripts": "^4.0.3", + "recharts": "^2.0.9", + "source-map-explorer": "^2.5.2", + "typescript": "^4.1.2", + "web-vitals": "^1.0.1", + "yup": "^0.32.9" + }, "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint" + "analyze": "source-map-explorer 'build/static/js/*.js'", + "build": "react-scripts build", + "build:staging": "env-cmd -f .env.staging npm run build", + "build:production": "env-cmd -f .env.production npm run build", + "predeploy": "yarn run build:production", + "deploy": "gh-pages -d build", + "start": "react-scripts start", + "test": "react-scripts test" }, - "dependencies": { - "@nextui-org/button": "^2.0.21", - "@nextui-org/code": "2.0.7", - "@nextui-org/input": "2.0.10", - "@nextui-org/kbd": "2.0.8", - "@nextui-org/link": "^2.0.9", - "@nextui-org/navbar": "2.0.9", - "@nextui-org/react": "^2.1.13", - "@nextui-org/snippet": "2.0.10", - "@nextui-org/switch": "2.0.9", - "@nextui-org/system": "2.0.5", - "@nextui-org/theme": "^2.0.4", - "@react-aria/ssr": "^3.7.1", - "@react-aria/visually-hidden": "^3.8.3", - "@types/node": "20.4.9", - "@types/react": "18.2.20", - "@types/react-dom": "18.2.7", - "autoprefixer": "10.4.14", - "clsx": "^2.0.0", - "eslint": "8.41.0", - "eslint-config-next": "13.4.4", - "framer-motion": "^10.16.2", - "intl-messageformat": "^10.1.0", - "next": "^13.3.4", - "next-themes": "^0.2.1", - "next-ui": "^0.9.0", - "postcss": "^8.4.31", - "react": "^18.2.0", - "react-dom": "18.2.0", - "react-tools": "^0.13.3", - "tailwind-variants": "^0.1.13", - "tailwindcss": "3.3.3", - "typescript": "^5.0.4" + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] }, - "devDependencies": { - "@types/next": "^9.0.0" + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] } } diff --git a/pages/_app.tsx b/pages/_app.tsx deleted file mode 100644 index 7de5b7a..0000000 --- a/pages/_app.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import "../styles/globals.css"; -import { NextUIProvider } from "@nextui-org/react"; -import { ThemeProvider as NextThemesProvider } from "next-themes"; -import { fontSans, fontMono } from "../config/fonts"; -import type { AppProps } from "next/app"; - -export default function App({ Component, pageProps }: AppProps) { - return ( - - - - - - ); -} - -export const fonts = { - sans: fontSans.style.fontFamily, - mono: fontMono.style.fontFamily, -}; \ No newline at end of file diff --git a/pages/api/api.ts b/pages/api/api.ts deleted file mode 100644 index e5ba4df..0000000 --- a/pages/api/api.ts +++ /dev/null @@ -1,16 +0,0 @@ -export async function fetchGraphQL(query: string) { - const response = await fetch('http://localhost:4000/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ query }), - }); - - if (!response.ok) { - throw new Error('Network response was not ok'); - } - - const { data } = await response.json(); - return data; - } \ No newline at end of file diff --git a/pages/documentation.tsx b/pages/documentation.tsx deleted file mode 100644 index b7ec068..0000000 --- a/pages/documentation.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from "react"; -import { title } from "../components/primitives"; - -export default function DocsPage() { - return ( -
-

How to Use and Contribute

-
- ); -} diff --git a/pages/index.tsx b/pages/index.tsx deleted file mode 100644 index 1bdaf74..0000000 --- a/pages/index.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from "react"; -import NextLink from "next/link"; -import { Link } from "@nextui-org/link"; -import { button as buttonStyles } from "@nextui-org/theme"; -import { siteConfig } from "../config/site"; -import { title, subtitle } from "../components/primitives"; -import { GithubIcon } from "../components/icons"; -import DefaultLayout from "../layouts/default"; - - -export default function IndexPage() { - return ( - -
-
-

Simple-to-use 

-

unified connectivity service 

-
-

- for industry-specific devices, across a wide range of network and data protocols -

-

- onboard, observe and interact -

-
-
- - Playground - - - - GitHub - -
-
-
- ); -} diff --git a/pages/playground.tsx b/pages/playground.tsx deleted file mode 100644 index 3931bd5..0000000 --- a/pages/playground.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React, { useState } from 'react'; -import { title } from '../components/primitives'; -import { fetchGraphQL } from './api/api'; - -export default function PlaygroundPage() { - /** - * useState hook to manage the query and response states of the PlaygroundPage component. - * query: string - the GraphQL query entered by the user. - * setQuery: function - updates the query state. - * response: string - the response received from the GraphQL API. - * setResponse: function - updates the response state. - */ - - const [query, setQuery] = useState(''); - const [response, setResponse] = useState(''); - - /** - * Event handler function that updates the query state when the user types in the query textarea. - * @param event - the change event triggered by the user typing in the query textarea. - */ - const handleQueryChange = (event: React.ChangeEvent) => { - setQuery(event.target.value); - }; - - /** - * Event handler function that updates the response state when the user types in the response textarea. - * @param event - the change event triggered by the user typing in the response textarea. - */ - const handleResponseChange = (event: React.ChangeEvent) => { - setResponse(event.target.value); - }; - - /** - * Event handler function that makes an API call to fetch data from the GraphQL API and updates the response state. - */ - const handleApiCall = async () => { - try { - const data = await fetchGraphQL(query); - setResponse(data); - } catch (error) { - console.error(error); - } - }; - - return ( -
-

Let's Play

- - - -
- ); -} diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index 33ad091..0000000 --- a/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..ddefb08 --- /dev/null +++ b/public/404.html @@ -0,0 +1,50 @@ + + + + + Single Page Apps for GitHub Pages + + + + diff --git a/public/favicon.ico b/public/favicon.ico index 0d2dd347113cb4cd7da335fd5f0a222e59a286ae..ba55c2d92c5af50fdd9a7c846cf43319f23a4df1 100644 GIT binary patch literal 23395 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 diff --git a/public/findr_red_blocky.ico b/public/findr_red_blocky.ico new file mode 100644 index 0000000000000000000000000000000000000000..ab7721f2ab928e5da078c31c9c3039d79d020715 GIT binary patch literal 410598 zcmeFa2b`r4~?VmFClx_aH(*DA>tNwJVGPdbl z`S)F=`mz_cjs5BJwy{6%E>({0E)ATpWYNm5{=ZqdWlwu`$KGd;{eP2}F%~;x-5s^( z^|TM}&|Rv1D5dXRH2AcxQtkD<&%SMnGGgk!&f5R$E>}L*TN?V`o<)_Hx3$Hxmz3&H zrA__mIhCEi_AYw-$z7$&x4TO#FYGE;zR=Y^@X}>Z9s7%Iw|wGn7C&q3&*naUAwD_r&((<;G{@5}|vu96x{l&twr@gif z&+jJ9Kwo+77_jnp7H!+5tG)8WF6(ZuzN@P|aBfd|c&xj;a;H#tdwqN1+qI~+#IagZ zTCrVs`@kE?_esh+j`ZK?E>%Ctx81mYW=Uz_iX|PD|FT{F^_6Q|(Y~d?w>!R}z3mlI zCj3#(H)+RBTu-F_uhEWE55HRb6m)n5cmUP!*($#*U7IFtKR z7`yj#e>uN9_9mxHa zlzH0{`eS<2c9*L=Ep8i|fYIGg9$3`fUOAbx*O2ct@}0o%UfO?ucSr5t&Fh!}lUFj1 z&+s|_2Mk{VtiadGNtQvr`^bMQ_uvX}d&-Ju_Lk~z6XyAR>At5t#8?d6NWKy9?w90! z0J!eDq&)nOz+so}a;>NTZ^pJv>GN0S%9ECqR$f7#tEg`m%2>CGgG2P{XLwm{t@1>4wdP=pOrLn!`dT((KNe7`h zdr4D^W%|wW%D<01VPNIbjzHI;PgQ90@Uv5z-mR+Tu5$e?p?}aP>U_R+FN6N4G_N`ry#P{(h3OSLm>W7nd`pH7)aZXbBk$_n{D+f%OX<~x3CvrzYr(Ssq^YI>j(s8eklW&MRtZNOwYpM4Gz2)j_my~P&v9vV$0_f?buiJVI zo@Sc{c20fq+5??8o&3DJz5eR-&9M;n+qPH#Wl4FUk9yy;qfV( zl21|oIn)oG83;Iq zqLj9)bL=l|bC50ICGdm87@K3#cdznF`uxds-&J0D12o{$C8f&w!U;Lx5apom_LbDN zawYAAPY%3ax+E-wcX#{XOT?$1Qhht%IstZ;OFKU&FQx7`)5q_II^`s(Y%w`OHEd*^roi$|AB&!F|n-#m{B zter^x7pqGEg9qT@`{(Jm^Fe+fOp5vE^1Qt5#wR&fU%hDT>C%Wum#d%d?HK-t{g4^+ zWp|;U(#B)+?aF_j&As(NgT8HE=@7K!h04H_l_lR24wHRv^}T%r-amux{0i8=*|C84 zFPo|yWUtqYqw@s^aQ~m>U(lvg`Z|V}^mJ7B;Qn}Vp*RMqUCN`=@yV2Ss_WwNqkpB$ z)zx0x6*zn#$O7eoXEO&r0}g(7sEBW_h8u zrhGo-uB1Z>^PY~umq)$0c93#@K5hO!lwYpCXS>qaQ}ge=593}Q_+6Bn2W|%bM|lr! z?N&@n{=W|6fsF_G?cHJg;q%&zddl^;F3!!#uFi-55_#~0^aVRf^;G1i8yKgbAv^Tt z%T*>{20xDWp@FNB9WPau%cpT{fXjjE9j|Kt9eTjPHOi>@G}Li$;%EKsBmctlb$!T& zedRUq=av5MARjV({gU4D$ZJwwWS9?Y$BJ!O4g6~d1B0WtK?kMn2l2zcGO$LOC+UjH z?v#I`^tw?F{37Tm=)&@C@~q}}5AD9mzGGv%fx52e`3mVi^4AZQ|AL$kEg|ihq(QeD zc`ka@TZF-@`PnwwJ4T)zQ(azrItIFxNz*&#>LUD!VL7&k-R<4fn7&fX!)qhR( z%BM;9;;VXMKd@77kM^|Mmm)71_<=GRWxZ3G5E}G5=-YejW6D3-RsIPL+E$+3(=qbA zVmj?ny&bi#;(KuAY4SGU;#i^YMcqq1I=)+3lMYATSIq76*cR&IU7ghqaO1sHuVHo^2w86Wr#@N>Q&2*3GuPg$L|a^1FDj{S`|`Ij-Z_@`k9tqio<ZmwazfRoy=)dAmJEH}cla$NC!@mX6_SLe#V3wAm5d+hEbww*#w$hSkAV}{?4FeZca=K%VD zYOn*8R$i&Sop&E|93qVvxDy-J0p_!uuJ)1Vz;8dE%8Y4iyQSypu3gx}L%pTS5w!a= z&|2r-3VyF=eE1DKknuoi!Qsk7z$Rx)R0genKG^M^GW5(OFHKjk-=j~YdRWjS(Zj_( z>f5h(QLT$MT_~*!uvSM$X9u6)g#&MDRyK3#(B6*fyQFE&zBhX(uaG`7zqGlZ-CY`b zC3PI(m`D8-dW^o$_alSuj_#c=C-_%*=lId@Z*_On-n9~{0g?BU`Nt^vhp5rUB1}cG1%}SSVdv28bg4Lb!s?+x)6Y`GoyW6%@&%0vI7t#w1RW3oE60W&Yo zD*AU!?>l8Wbc_#+qw-DdA4Pl3WHQHObwKc%bE_ zft#c`;QB{x@UE9ZtMBIiarpEnmxsbuAz2$vv2GB zo%6J>JiI45#vST1wEbRa(3_dV6Z}oZcF~8!7~d<4@7LEexc#o4_MsqicDIkd1o`7& zbn2ffn{}6lXJijXA3cinO?8!p?g#KYQN2FZll%{@@_1;(F_d+2DrbFNGrwcd3)L5T zI|o9%0&!A1>a%O59o^+Rbgp`SD|zN4Ej065b&04q*M6(5gWpnghR?KGzI1i{(AM#m zC4Zqd19W8KO6s4I&P!8iOBGuA!`{xB$C1Emexcq!UcT`)pWt{1f3LbI0x4a(ihuO^1p0iE`dfNGpI7O&x}!2e#%Irl4(_9k!?~cXply;k2ICiumC3h9 zeNX!$xVDFV1rJYC?@cz|eA;IB!M}z-f%MO9_C5d3_(^rrV1FtH+wH^hDRjeCjs^Vq z+OYlnz}q!pu62xT zukEK;Z+eEe{vhag*b{|6`H1lwI83-3AECWEr7d2SReh&z3LGJC?Luu6&GcWsol*4n zr}DHx(&qEPVe7vPczl}QH)-2#TyKH@e16$eoB6ZGwjj+T$xg3KZR~}xi}vT0gQ<@E zRa+?K9?b8a^j^K1=Ld~PP4DtoX#wqcOa7gr#FaP(!1FSGzu|{nd*G3XOVyu&=ZES0P`+0;RQ~Vo82xwB zJec|{9yw>x8Fz?2xRK{U^N%CVYQG5=Z6(+)?&sZ*@qxv5LhJG2RcnD1V1acI#mfb%?Z_jF?F{V;xLds-{|(%JowA+p)Q2eJ zE8x`kXy3)OTf1>hUroZHgj313Xn#|mfhoQqVt{A^uy}J%=c*ucCSBM$OW8-z=MUOX z`>UPSvZ?o^rR`&Xn{QjQd(xdv`NySi#=MCu>A857$4d|NL4~v-_Nx8~#+o=%=Yes@ z)w(c{_gFqZ`cZ#puniM$YJ4C4+9l#<2~c9#d@cDroJ2b1-VJuPyAiO zcQ;kO>Aq3s&%x90EZcJI8ORHJfLCj?Se{kJpGyCPwe5~Rx^?3>r>n#7`av0kA1HeO zC*q9i@0gAU>mN&a(5w#Q`<60qDQi1$?m^{)fEU4+H}=oG`+@QqB?1@l@Yy_RV+V7qGw7c^TSPs+=Bt+2w)v(e|~wJeB#1 z-N!L-K1JR}oO{?O_J z&C)nmZBM4t-lF{m|Kgiy_XCY_$YLYS&s;g$Id7iK$94*7tNKAx`C*Qf>tCR6(6HLk z_$a;sjENh>C&HSF;P->BG2R!`{$DxnDeq)g%DIc*w)yWozfl`>^e5N%=tcfTF8if8 zlF`yLx=UkQ_mzhKiE+M_^hX)Tr>`W;1Nfo*(tGi(7ayeMhgd#hiemg+^H^2WS*}0t zr~9M+1Nohr(&<}{{+Y^;_1*IAi^5cTBR^G!i*sz?MCq;lgm#8_8sK`GX&nb-G<cl<};w<3f^k=p04ze3Q4aXS%_9fvfo|u<3-}64q1LQFIfMXEj zcbnU@%;%rF1La*19$NS>lkHA0i?I#T8rt!Q{?#bo4?JXF#0z-aiQ1!?a~~$nSjG#) zm+hsS!0%Y$t$b_xU{4Qm9C`obx{Zr#W2!6k->&<0W^a17T7i61eN2D7xB||cVPAwF zZN0X)G`tMC2mjr`BhCk5tW8!ssPO^3UuoOPm(kLdgUpQv4Np$`nYFZe zD&?lWt;e1z%$R!#$1B>}vT^v0eenmJ&zJv3d1By_2y6H-eTp(%^;^h7eamz6Fv$H) zcoP2G&w)2*Hg8pHZ?W6%Dr^I;;5!UHdSV8A)~5)JACzy~F8Xk|@D4N*eI|?tYs8eP z)eY6J^0mjf$I5-muIW8`--{_{MRDCt`rgV_`6l`vE7ut#qr4^_+aGudZLfVmzNvgp zKh}mhS{@4RS1y9bVaH{iiFOm#D>kbee$^|lP2Wg+MKQiKeQV_^{yOfA@mqt=Qm&pW zt%>7MIckT$9c!1rGtT$u@ArxorrZB=t$wYb0R-1wqqGKBF5l{@@eUJ@q4S! z#;rluKjJ(Tr=5S1?pLl1c249t=HYp^Eylk^-GniCoY==5kqee`zeYMLK1^3XxP>34 z_9bMUvy?lhOF#YhY&|J45*eSN9fLard4$+0d6GB|e1m)-&P#W?w;K6Z^&<1wFXqMb zCaX)^Nz^Z@pH`P{C%?{4bq z7$oLk$hC=s&JEs;<<~Ez@|8=M025@`lfkh^)jN|sXN+V@H`l9cDU`W`?P#l^XTghp z9dTTJc_6^v_8ZR;WNPAf#UyQ05)*)El_&Hxt=scKyIt zm#rOxF6^4Uk>&vViM3tP7ZM-<@%m;jSFp7-}-o`OeT$0&Mfk^eEsqb`ucU(7s0Q8 zW_tBabcy$)PqQ|sf71vBve@otSHO~iYJ8QLU?P~aRe#h^IR`af}^wNsp>niL|km|P&`L(y{{Y5tO3r_a`S7VA%TCFNm!uV2aUyVPp?vNhFfumN7uYTosc-o9$DQeMdX zo;M*2HpUAZ_qB7?-Sv54@7Mk^X-v%e=<8&4K&S4(7x7)}c*LPqS7TfMHL@;o81?Op zCk7rr)fa63qWm{mnZ@_`eM;J6_4O9NZOCW&d0vMh?%{v*Z7?T3l5Fyf+uiCzTyMb! zewB9O*|b?65@ZW#>}>KcRGzSAGP7gZM&lX41J*|jhkgF~gTJDBKJOm&2QL2z+<5zJ z+JhZ+EB(OY!))@;XCD2d^yhqizRJw{l=@1eFOnYk>{uZeH}*r2o-_V?F*o+ohcuhE z#8{a6S@_U?(W;y}oBEpN5%%!qbM0sJtJQ)p2Ri{V>*~6!H^YY*bl=<>k7i~0Zl-df z{6sw)-Fi=SjBkMl6JoI^D}O%UON$xv$D;4bb>S=5#yMHJ(|tFvGmq#8&U_dRJ}^}q zTx&%f*%{5!xE_E$@6TGFbM&$6Zw1D`0grx`_F!w4Zzk`i@^?;_pSFBA;5vGax(;m# zew+T%=nHAn>GnJNpxBR)JtrnBC;#3!=m5j^@qKLF6AhW-9Pr4vjsufSVLs;6%cEQ! z=}W$>>Fz1(L;NnYt+ew1){wAA#mr(RwSz#z*QOX<#ya3X^zI<#pWy$=d~eS4>DrS2 zZrqr22|4F`@=Ny3fL_*i5$E&i(miedCh0Cn&r@AvtcP;>F#DMd?Vfyr%vb6gJdXE! zE$vwK@~O)3TQB=bWq2LNKIyuF_haV^?I->h7>!M~ziz$`Vg)|CUForgADVXczt*+q zh4tV0Jn0@9@Hyt;Rq46eb!mC*AGNJ9PGNmSHcuJ*!4IORy&m`ylOFbZehi)E+x_K{ zr+W`kINeAl+m_q3mVHL5?NzP-UY_%(5q$R@yL-{$ga z&`Ix!G4E9zpi7RIeMqng_#EWc8u11-V&n%;fWA0)x5$?{ zhOO3lpTTae<8Qx>;mK^g%q!xip^x%qr=mhw(Rz2DG)W!Ya@|bJt|4I8ezl(~g^koB|VR%oFv5~325cdYE zoKO1S@>>CXfd87%59@W@{3ZA#7)88{`fH3~V-K;+hDRS*-&OmfvaUE`d>65KJ7C*e zhqp6-uFGWNQ=~f?ja+Lx8n{6G<4o7a+2`$_vf?SWiT(tiLV`oIjZ{}t_JkP4^j+Ia z&=tUCaHY$5D)8@>Zisi(YJ>vcO^f(6h7HF1wE^05&PFM2S|H2usG4V ztxbb>C-8eT%~9?vOT$|1$}1>)rDX`)SQh?3;>}X~TDcnQ;<(ZFee(He3$gFxdp764 z&GcUQ8vi8!g73bTGCu@QIGR0&-)tGohYPe})3@IyJvXEQZfS0iep}NDNBTX&^PAon z_U^47!kqdYb7^P4&*e|sYOp6~d&EmSlrhrU(z%p9Szcsv@E&=uH1xlw=_0O&=LZsg zWaD~D%J2LaCgFX1<*tMm>HA!-?xiFh&-nY{_!JLz|An?m)~gol)9#SQu=uU`EUvQ8 z(1(5B*E#x9?XdPM%@6S59(B>(!uqP}3DO09{?bh8oNxel&NRP1WUuq_=Xleu?^FLu z#^MTokM+%Pk16f7=IhO!!7){vf}WeL$r$f_xF!qUalSFo=CNLU?w^`2fBH`UucwVCrsv77_AT)% z`z{?L-?ycG+BEX>An3y7`o}CwJc#|T#{RQLQvE}n5ZP$|$?8k*#qF?W0Us=MH1|zAyW>>x+Z=c4;yn2!A*xZMm zZ`b+WpEMa9zd!A3pX3>M@+HE6Z-ExXH5j#B^$)u)DlcDqeojwm1mAi6RKNM7udHFL zf0MC&H#VhL#q?>t681*s9sK?r;J{kn+IN$ktyy1+@7y2Od5nzGG@gO}on+ib7*k`0 zp|`Gy(C%uV{KfpNpYEvbZTZMRe*&F9hP7ZEKaJI-FP{nPX`rvp1N&p&L%-0Q z8eV z9I>}@VP1BXw#Z+ZZ#vaL|YyvOMTW zL4I3pKFSDd+=N;7{ZV60THz7QjdwCvSz41n5Wqv@IRPe)o&qoi})??)4Ealx8qKlBf@%?xIX3TG!JY8W4Q*n z?HTh8vNvkwe&@G+4)5@b3M0}9>oBikS@lnwKl?V6bQtqY_eXo!m$3TN>C#R2UD+(m zWn|Tq*7dQrKaR=3wPBqHYxb36Y-{@2Kj9Mmav|QWdb9oUC)~6xrZUM})#e}_u)gHK zV*XG@eQdjI?LkUE)fK+*A?Iyg=btM5bld20tt%YAQ}_`Q0u>zvIijRa1sg;%6+E7m4D3eUOAeaW^fk3GK_3v1n!eS&2) z`*U9d%WH*4G%KgsyZl^&mi@^2YrnF-ov6G?``B-~TKApfnBn?Ez^czOdplPBm;Lj7 zHrKHaM&CfRf7SPmKGNZXmJth8Y=>)Z#Xoq*;^McyQstj)Q_?|;-{cY*0w3v6o-+Sbd(Nk}Q~xC?W+1jfUsZKg`o0iX#%BMWr2EMC&V{&Eta810 zkelPzh`-j)n#abvM0thd-Bnr<&a=+h_HDX+%Kk}aH7)c11IpCOKJsbsA^4@Dow;&W zagK}Q(kyWV`RyQOXlOm>&aH~J@=~QTmH(#LKKDVB-XIg)fIoa2ukII6Ot%ov&H=|O z(!T0x$v(|EK$DU$Kl=K`cj1%a$BoX5W$k15N-8({E~B51a6hAUa$&D|;-!$e?y_%v z%p3a7zHXI$n(eJM-~G3b(mv?SJ?ib2&mMO>(VvR_v9EECv%VfVPCW1(a?GAlKdv1h zo%9#xKf3jF@lBZyfWNGIYsiBQ%YD>S-}DLRb<^~IzOTwUp+Brkw(M-~USYqLv(p^+ z>a*|Z_c^ZR&(?al9x}-BsqcPk)=t+z(cgFSyU;lj<^SsUTe00VD|fcndGLg! zcc#6s0P}t zu^I$4VX8ciFrM{_hM9Gu~@xee|c5I>5*8 zD_bYIHPN@AW8*h(%ls0(jgso>pH&f>Xeka`l%Bp@+9RhI2{JJU>6m=h#%RdreiA>sf_Uq!TlX zx0|ZG4gF2KCv?o1lCR99Ez4NjC(c6yem0Zz8+#h#Hd=|j4(Fy3Be}8nYjgYlup#gq zy1yI03;3mdBKA9!hu`x*>?Fj3vrklRZx{Uvo_qFt-t`UMJ#Cu88vDruQBD~8xb{)( zKtD&feqe()a1&@i+$(r!Uu3G5Woc}aRs3in_6Irk@+i{~FGCE5XE&+edzRlO0EYG6 zL}gg?y6+g*tIg9Gmzg%g{Y=37leKI2l~yms{&}VHv_2~3MC_Lzbzeqk)8n3bsn36= zW&a_h16I$&-h6KsS2|?O@J#w0*3S_~7vibP`bylZyn3Cm*A}Iorfn@eXK^@;$*=mR zk>8+K=ev*hOxm!ir_sKwJ|nFFhtF+gA4vBz4C_0}BRgsL0QMDae*yPP^)vN{i1fH=wqpWVXCx~ePhge z`+z^NU+Flilk@JZl-_tf;qMQh^`P&u?S5oO#(S#scRd3w>{UWcS}N=J>54q^BXM3^ z2C=BGnQh-Z+e@3cu-9e=-(ANy%^IQ)`Q3TJ_p9{*YDbu^PR8*q!54##MSJsf={JS% z+7F@i=Vd;o;ibfp&86Ma5$BKVu9yRp#zGOtc8>nZz>hf(XQp-Hk9m96)*C0IS2HbZ zV@Std1ZR3q1+cmWe4LR_a2{@C~~XnNSE+POse7ZlU?msb%d&6xs>Q3n4bi)p5M zb`NvsD!z#+&(EByjE&`+djNv}rzc;+T-xQj1K}Bc?X?r7FVcV3_kAms>-c1EJh^(ueC=^vtYcGq)@SHGcF2_PMW%EeK{zj0IQMppzNxQc zB*eQW+Mm{CK#Oj`7kGE8{Y^ZXdT!Te-fI5e*Yv>uA?^0YBS?XS80X>U*$}Y!}EFm5biw(k#V@LAKlFu8TBqnIlkl17*7Y> z4`OU?3hQdI!?jx9ROzYXldd6?@BE?gWYn-;PS__sbA>~5Sb(p6X| z!upNEn7F6(;9k9phP(F5?M*S??ZdY8t(_w+I)A>)-q^}B27oyb z=+6P31C-hv_Jma*z}C0W_5=9M5sm>e@b5)zMuK6pD>_t&IZd`ruiD|%bs39C^LenN zeV09WCtXjJPdnFp)}((gtn0?kGS{*;=KR=m-lpvV+;`7~u=j^H9{AxGm;cAoJZH=z1ycH=|F>XX%W;jOHJa;1|~tj%^**@gXOc|#!eM&Q5T_q+^uYe-g4+Rf7|-r|1+s=q0@lx?*{I5 z;jSMmtr2kzL|g+9$G{gE`r}0X?_?7=zpti)MnIbo|InMs<-T(sI?Kt7h3mv zcn$j|t$aaBvuRwx>GIH-;&HatD8bfiw%2lXrF}sjI>hnGU?28Zl)e~?IGer_SM=Fs z9jm%p$?N)%v?mU0yq1FZ_pmz_hf!V`U+a|M>zo$e zuG4j&L+`=2FfPuI5R1b8v9oOlYb)O!>V}R?Rfe<;_`aF*TH`shrI9b1-u z{`eEdnyq8qmVIsUryVVQ3w|E<>?rPODGlrEsJ(F^_ofdrAF+S>M}gUyo3f^b7uoH8b*0(^OFM_&mdcsSwTFF>;Ooax=AEAD#yvi7d>$PmZ=mmI^mdHwJr{Y} zecbdTihFsudykzV#9INAR~q{n=SbL7D5T9~%6VJu(2qWmjlnI{eRtsL?ib%V__CS8 z(ET3u6VTp|%rsB4bj~N^$D}prH}7)%l|RJ4xGt;u8+83QIv0T7cl+9Bw`YX=o(RWG z&MAyR+{=djaXyA^?fK3T#v-JNHn_E8?t##({ZqYDPh~E>y;&N{KO))_Dv#6mmVLh6 zi$=mZcKGAI+^qcR-np)Xc?Z5%R>k%DHO_ml_ePnV^UR1TbKa}pi;u1$XAW#n3<&$b zjlOGfi*06H&&p^|0rrsxh=ai7LgyH5s=1%0bHldr{xR`mx;_=ZtgCQipJVPk0FOT~S(;|=)d9kutDK?H?0fN@x;!{|qcDu~JIHkfcvQf* zxCefQk2i<-?r^?e{T?k)DmXXYmRFAaV`8$ED5g)-2Gs_d;YlJzmlv%q!=AC~j2dQWCo3$Yc=>T33m z`fB98uGzQrPFpT*JXQP+@?DtE@k|BqjXt`k)`WfJ!|4u z@2eflclWHk27B=5yE|*!y3cH?vv}3N80DFD>q+=;8-zJ+-Pt|S9B13BT!byB5r1ge z%)S4CuAk(XS#H89$2k4co>8=Om3#XY+mwG+7F8~=PVn#f`EOI*1HZe0#e-9&Sz21P zSUTtWX3}5B&od*yU-!UY8{3=BiPZMcXZK;mE})L;`*D6;VNd?r@!<3xtZUhqSpAcb zlRn+mxpIQtGT$a>)^{9#VCL8cnQ|QN3IF3Bv&4FyW?srXjj{M(+A~*MoAIH*^VUQw z?L!`S91os{*vtEu(jME5o$SBzZJF%;)mx1{*FKu3xPRefd4PlSZFMu=WP5@8Ilvo# zQuScl?w-Kz`2=6u&a-N0FEM1ES5w`WzJ6Js2KSsJQajH+aDPqwh4T(Lzoc?2G~qt3 zkCFFgd?v@ZPtU~magK*`4*rhM;`OO7`fJ70XeVAFt%s*a7$y7_2lKd|bR_CJSlBk; z6W}~icXJ%Xo1Tu*1DU6v3^6V2l{T?$Q$ITI;PaXx`@ zW2Ou3L$0T-(ye=>=g2^3Qs1${$F+vQ{)76W+}|zG8qTBQJ!kw?morwoFix*CE)Ced zmFq{T=ZpLfgceB;E~8w}TnKhD;boo1HQ96c1^Yl&N2M#`u=9cE8<`(p7cbJjT&AUO z7gNWH+5H;R=W)F_f3k1v(N}p`IugdZW7U*`IbG$KkFVLlkKhTe70O-;=W>^8P|q9x-Q()JaCV|)j?jSzc+7eXV6< z6wZs{K3yHV^esP*^6qu`cd%btkL8l@Mr0G>1*@-IPApq$qdZPK{bW2_nlD~;5f|GQ z^?}uq)+bm`xCXnZ>8XcvZHAlbTdD4uUZDfui(^nfe6l?9KT zFWCP+4@@|ds|GKw-o)=wX@xlLn51#a&jtB!kaL+cX&lDkJ^>D4d@1v4=GU&p{ko|C zHRS&}V|_RC=yRU4V4ID3!@hB_`xB-<$TRI1KHpcTx!{;)G$!%y#(sH$t|BAN#lD~F z&R>O-x`F=B{QGpjlyz<~c75G5wo;m@u8YxO<#F9xRsS`fIlw-y#5w`P!>RixT#=W; zS>40ZENN95qsCm~o&A^2NHbGRl(DqZeaFMJfghp+=V=b|!6*10&R?RBUr%jyZ_s$2 zYHb(aWx7k)gFE_aqFyG>!}Gij`9~W$4<-6NJr}lEm%4rN-G+Qt-W0CF z9oqM6_t#79QC9WrWWUQ_<)!kO2+zTrQ##igIcFRj(*kqm-^eHY9(?Y^`7Df&H??5m zwWl=p&zVp3vGhe5a9Me{cNz5Ec`(y6UQ_;sxJs*u6=Yo^HnaAjH>NslKl9`4#8)E! zkcPy)-=!h&s|NiyA2f)w5aSqGUerx%H>5Jx_tkaZ>XF7&Xmepse;Ava^$Vhk7LDk@tHP@^Lcp%TVFg+MZQ7aqm&oS>zPmD ze&V^zfiMR1CEr-bZ2tQ3hro}{wr%21l3R>NRZp5tnda$x%8F+)o_FeRo=q8DiynWv zzLS&}eE)(nqwQ3$74E{&ad14)je}3s^yqHve_t7tUy$#LHe^m^1IDYSOrOd8YvX$O zYCVh<{yES6PW7#?Ykzt5a_0Ci=*#WO$=!>p=(?3Vu@{_+95>^0qMEgra-N|7V{?h^ ziuUcA`$Gr0Rk(}8<8xr}6z!JQuUw2=aYw`@*Skl1rS@aLyKjcyvN;gO!Dr=YzcaV4 z=l*lvFVtU{*NdvpRR*(;{&1F6?OJHj*{M$O~6^YOIN2AlD?2a`QTTB{#E$(+su=aglJm^W}Q_*Y8x zi_@64(&l{H;G-xHJezsQp7r&8fhqEL?M{BHMxPv|n5c{ltdFd~C^wQ*5i_o6*=O zir*%C?n5rH4Z^cX<2mo(AoIzOiJ?DwoCY~YxwBhJ)gkP$d*tg-clTL69()UD-e-Iu z(5WCxL8qjZjD6S>NI1a1kCu1j%kmx^_-|~h>i+PQu&>G}IC~e*KarjR)8)o=^nC^ZBFDozEddeAE5c=R+n22QLh=dtq%uzW#KtPH3MaebD|Do1MHa4BVRM&GO8$GgoW-%QeohruTeJ)0`M z@YOElJPh_$;0Yc+f(+1WzIax4akd4UUEL zFoYLy_dJ}BrF5JvdX(=YJc7?LFY^!R$4qIXov3z3peI>b!dH6{^QaMPt{!2Te#?Hr z)=E3>vn+j*aa?0uqc#J_ka4-T$Wv0?bGcIPS&>e}Got2FRxz*iBk2UTvg7?zL>`UVh9?JC{GodVN@Id@K$LxEe0ph6qz_FmbUvVzlvr}4aIn0^S_${8XQJhWrDG%^?P~374nDkDZ7PC zziqC10%LuVbKiYgQok1B3cq_zI5(_ZKRT7~xjj4N)_bTQNvnbl9DJBaYib9ke9-vq zl=Xy*G-MoiM>Duo@9k}0^{SMf`K{kz$n?UWK35vDP^l8m-_FC=@eY1Z*1y;$@{VRY zTkzG0N0f2pRQxQ}J=N7SvqP*FV?0&5`TB<3{fIQdc<5r8&Ub81H`q6IBjFn8NvZaW zWXF>qFg{`L+Hqd2+#l@>z)Ac52UFR=@xAK1mXqd(`GjqfQQ-+Mldi!Z(l`eE4m>1V zlTG>Wh&U1X1N85D$0gM>n=5nwskRFot0o>cn{ryoqdvJad+Qk|TFghEJVzOtGZNqq zJRVE3=dvw1OS3!_&Qy!EGRh=renc8FcwDkGsbex0;jF1-YcQ=aHEq$ZLS5LV8}lfP zgFM!{w(l7G2ipq1|2E@Om#2D)eQ4zw-hQPrNnhvaOIyh|*R;wvMZ2o&!KL@YM?3}J z_Dl63pX{IXunb274N%Sr_*1T5>zS3N*M36YAjg^~(G>6F*bZLW-#+#)sqCmz)Lj1- zXkAL5=vkmu<@!~j?2dtM{Xfprn1A-`6pftD^uwPHT3UX*WjeGgUnQ_KHz-HcyWMrhVLPN zj*maywP<*Wd9`s*W_^QH|%xq01^HtZYQFMKkdxyEq_ycKye z$toGIyffmq_O}%o&Nk8_qmS&9dg46Mj+^FAqD?uthy3HVzS_-+H*0TVobM{`ot4T? zSLFX-tA?gEVqMa=;+6XE5&pCF#}><*uji%2|3{dP0*|#9+}RrW;}we%=d{` zCfQTkFT9eE%XYzA_Y&Tg3qA)p7vY}dQRf)8(!I==twAI2}lbWKxg zGixh%(3dwpM<(*sGzO_I#_2rcf6Nbzz7b`C+MTV+_wt%R*K;yNYLh-``K^36z1tYB z#uJOr;%|g+bwFH7Z3FJ7O3#HAa8owSpHu%(-Z!fJkM4Y~<>co`f_HXBE=qc`KC6-7 zTXf$&>KYJ-8TVQ3A-_E`CG+eG{rts!KABtR1-mk7Qr|Z8HPYtFh`dYs>-c&7wX60Z&ZB2dN_}5s)1BSZ$u=U_wX7$!Rq(Y>#ax_tdC{Us?k^Z4V! zDB`eijJEzjp{lG0PnER47?J!2YxL4^S_{jMY*VYPm19O&)-1aHNrmXLbc0N z8)kdex6t1)#5$7tmC@HzJCl0Wi6ftF`SXiC9zS_ z{>EImzK8XaDQ!6a3mGm)oAU5Z_t2W;r$Gl}uVM3h&KET9eBnOM0|w8Vs~yv_qwZ3# z!Bb8Qx()UsV=0#9e4?h?kbhzCwes-$^8NK3Kw`aaNH*tjn?ZFYam~A?_8RjkpBtYa z>&WpL?BlwXNWUsqXlJwx^t|^;>k^%^KKXYlf38<;h2WL6@|~f+5_7v;e`Mo|XP#)Y z+8+HBIuLBqz#_m#_yem0(ika=;Gg-5_!?oC2Ub6m2h0WT;ym-|IM0anN1Y#mw*HR3+z9M%!|$*!@eGr$P!?v6{un##Z;*rE9(`{Uac-_~n~(g)e*pWH^1g_#_1p7%P-%1F z`)~Y~$)624ughPFOCTNTA4q({Ge^|(1D;2IvPNDm&2it&+4h0C*=Jm=?NV3JJ_}yF zb^N>r*2?0SQFp`c5#qgOTh~J6lb!&phcaEKhWxl^sw=D;pI{dbd&U&y$!shVeFW0$#cBW-)OzFhS(1(ut z;ricG+l7z;)m8%g5kWuW@ey%TUiG|A4s37^M6`4hneBK4qz{sjl+t zu(q#UKZ5d}P?pU6UfS7?b#2O2`8U>Y@>Am_fzAEBi$ z`N8|R{{!*7$LH(J-|PPoxBW$Vw9474)${p%T0Ebte3Y+i!{1Mo1MuB_IO1&Zdrv}Z zCaf)bg!t#+6EPkF-giOFAJ((v^9R^V@9{GqIaR;iUlp0_3tj9#8-2(V+c;I;P5GN= zp+$Ze;;Zto6NfWAj<#s^V10-UnUcMHltrU1u+E%m;5|0^qc&w2EmYf$b&Pam+;=sR z&raa4tk)jjk5pTmbXl8y6IuoAYeCNGZLfWGq1wMGm+L;L((!yOSq}CAei5%ZrP{o@n@C`Fxx2$02}jev7hs0e%ztD|>4-(_M%Y-6=y^Jw=kr6~{AEt3jj)FgJErjCjmodOv5hu5f8wg*-c6QMl#g01?++!tzSdytELVSL%-d}H?f!|< zFn^52?z1iD4=o>YAzwwuKe8B?(JCC!y^c|5hKB9ZYTiHO^!nxIqyMhi+Dvoi1CpD| z*bITq5ZDZXnL*$=B-_uVTlnu;-nF&;84sKPW&{EB_V*GKab}9A$!8)qbceaFqs*7L z`F`Vr0Cuy>U32Q*+qSOZJ1o^drC#8fJR4uDH@9f65Fno7K=-UP76%>hAlDs22X5du z&_=wev1!P&#J`WPADC;MoAb{e0==E1FEdu37>&k0qq8r~^&EqDIlt4M8tClzCjA|I z?FE+Y{#IRER{xE(obeF%9hys>oAb5~0b?v!PaDo;&GyG&e$MU>9{(ivfj=2P*KYBA zkCA;nXBwNqLz(X+_`0+sKnH?&;gGwWo&mghSha2;L$zd|~Ki6`l|r}zi?F7t6g_WFaiz{9Jo z8;)^C(gEIuwV;#1TlyoOC=2+Ddg16>CQCon`?weT;P1o%@b0c1{(7u^s`P&d-;kr8 zXUtvV_wf1;EmXguDKB!r5c#(>fUyrWfPDjB(X7s3ci?Qhw7yUrb^QD0u;;()BEZ3i zl|`X<52d|No7Mk^@Q!(~N7#3UvG9Dph3Yf&_yb`NoQ{#b!GYHXe$M)zFt^J!*7U^v zoKyW_FWT}@Sn~z{hi0q}av(bCcFWstd{Rm`o2zSDpe2t590+TnC)PciGB?#cLEdN2 zv%aTHeO~cg`-i>nBkcWx4KJQ&rae)bqih%D!NJ>;T(*s8BSW7v*)Q-5@dUl(e|r}V zye!BKlbr89({_43GdNdw9UpYy!E`p_O!IF}*E$6B^JIGp-j9s29dQ4dW1r-E#`pWn z;yJ!|XYa$A52Qs`%xpmHQxo}0Nw3$ z>27~#_210T9DGoDz<7UYyf(j#--q*$!I3)t;Agm&m$eu7cqWZD1a-RE)L+b_eE?oi z$#7uBLY$jWtn&}~ImBACwtMlS$BDTfKG}UJ;SJ1h?EwSO;qce8HT98Rsg?dur%KV>#;w#9tdcb9vX zqU#v(>#&F9(7x(+QNJI&CfS17w{v@a&b_74w-w8n?`QFlcExjSOEqH=T3!P(-)-7d z%X1$+eZ0!>y&bE+-PO7B?}~j<-Ur^RgMX$}`6p=q_mVAG*$~;_KJ~zOj`Q61!7A20 z*|Y67<|xnsXcTm3USjhntMB*qz57F{-?7itm_EG}`PpH~4UG4`-|-S+H4}g??xJn7T>yTh_1G)c5#WXn5tzUtE6luh8G>+H>^z zK?iPvCLHOwc6E;ZWlD=4a8Q1qJ>M%E#W=dULmpzT-dm)a2R*c9t@ z-bY(bHQ<2vF`lPdfu^1dZ)IOd9;qk2~kb_h}_|t2Dec$lx3J9Hi9Q{_^Oqi4H)M zev#g9zD^AR#{Bk-22`Z!-pBKntB-*fZv~d`Mb-~{{`Qpzdhii7;>)2Cq^aN8tcDFC zlY_Hwkq#&iu;126&Dyp3-9!iw&wV#zxXLp%`Z~v6O1#H+kOc;`4`jUHA=0dZE*#GL z>(v>y%g%hje)#$j>shLwq8yNq{nswk-9GS=iM1{K6EQOHx9yP^3^mT{Uig|f%^KKa z{XAoT^Q~8o);`SqKU?0Qj^Ne3m$AM49C@&D<_B`X^};%%A)dYTQ+}Udf735vJFXbl zl`ngJ+^79PZI^gE*O-*`(dG?Rhq3!p)vY|u_=oePnx)hC7j21yL57rOC^yJUq!rj2 z-@GAYa_G_iA(pUQAKoT+Ms%}2Y<@R61mZftq1StEc{;~ApL*F}S4!#$(KpmMKl=fE zM!N)_LHwXGUGU@g=JTzOdt%x4k*}?fx;E81^sh5l4&|(!$Mbx^G2{8Wo_Uh&MCt_6 zf8zx5{Y5|MMj!YRcIAJA9~_}QA;yhV+)w!d%D&OLxRmpa^ZC}ty|H6`i&ie7-Ho%^ z)lrFYaVz%*T88%U=rrAvWog$|bH(=qpv0&PUmsY8j04E12)U zbX=9$l2@eZT?ETevLs4?oe6G$2dEHO7RZH$Jc}IFiyeyBZIY;zpZ&U$}Q; zTXa8pU=jV{Oo-YlWEA?aN*m|(eI2zozOe18KV7)GH_np#N@M?|Jnxu0_Kx-Vm^RMB zifg{gwe#%->E9|{fQIMOhP{7{6XZODch0r93n3eJbw2!;{pH#|;OGebJVXC>S4ZVP zp=YDr<$?DtDLod>RGVwtH7-63}#V*2*`dWCj>KFa3CaOTIc zG3Ud3U=!qkbdDJF=BCK|C*)Zh=s|gKMYEWZ*}@Px^Gw?L%h~4FKgC%oZ--u8>^fP? zSyEd0BF6tvX+Rfe6VsntBfqPEw&>5AZM_?NKIq-fj(x`Cb8~pY*azAkWIyx(`-HBx zzt7`yKFSBu0&$|RH2R|X>^pX)y~DbJY1W$->oktZHLBvI{0RTw1H=Ob-K6g+E1pTe zKLHG$00*kX?@8h#L4vij?rcW{N!#Bs2xpIea=wUzkI8Tb_t`B7T zK|E`HK4I2fuDn7#)n13}`Q@p~R-cDf?BjX^$6P#RZvWg^)@J#{5zlH`vS?-3F3*T( zl{ZVbKHe>BA9)TqxxahmEOdS1ev>)eWKIu-InCO=VAEy3Jnnod;7nTUbJaq%oAdV$ zcKidq>KJ)Lv7V*vqc0)u{1jyp$DJ`BVaz`{6Z>+KQ@S{R3xDUQlK-Mu$NWDlhqy-* z=emN|-SK?Uqnwk_qOUK6?3?;PeRpKzSRC`lv->z@GG4IF0n-z-1*qfYX+JVyun%;# zSDur~Ux+Jmzz_5(+wPvup>4F=a+V_JgxBtIJwoJVgO@_9THZIxwZ!&KJJfbt4J{}S z&_@cMT~51)?Thp@+cSr=ibu9*t>r($OV7qvbvL}Jf4*VqTJGq>ZK~rn#uht3@beoB z)LkBaw(wC7%#XeI@(1vB*5|&?w14p&^ZHeeGqB@V9g-%1%fzVF4)!eOnU+;dr$3rA zY*$GWmz7pE@`LT8W2b~Zfj`7~WdrhYAHE=-PKa79-(5q$(k9=Wvug#kXsO zVU)9?3?1${Tl3XV9eX-F;(h$CGNz_j_H3TPuPdjc{Rt0(tXHZAn=kkf&bdk7ywZN= zUu|^0_GA(Fqb+Xob^Y2E(dPy;I&kfL#gZgnHS=T7*}yfQ$Mde3EANH3|Jtz>j>*R2 zGq`YfH|Lk9wd+0j%&~!gpY&PUEcUikTLZ0lkNa%)cZ__GGvU8I7u{MPo&4H*^5Zz! zeUwFNH`q7E|10(*pVsg2gq!kxpY6T#f;l)GX!04_gM zjzA~)M5}pIde@r}Gw~T|yEN8$#{C4CNi!2Jc!efh-qSv`t2pC4g64mmJjenOZz7)8 zZ%1ZW+}F8!7xqv&)%AC&&Q`AO$!@u}AwQ0j-G}*Iu8k^-YWpeXwSVybU$xSPd|KCr zhP{b7%QoU#@F52H>^M#Afp!LC3B&>8QS;@_<{msc#_`8D{mt3rSKnYRj5zMbH)Q9r zO$le^sU!n3=BxRg=-J5Pf%~QK3_V;Obl;5LozUB{>c9FshQEUUYC>GjOxlcn^BcuE zJlWX$UOWlBsJ;GT?Y^`J+7b5v^LsY_)7s|xOZ69mC;LEmK1-R0@jDHfjq_h?wbXy< z5@kZ~j4vG;bFb0q`qTwa6y7^32WIxE>GDkWojvTo?D*4;mSO{)7x3^0Y)4!RKeb%d z9Ny7?Dy$=asNVyuJ`v7zbv1j34p+Dup;NO}qPl$Og4;`bu!TM8woFC3>=Z^iE z2_49ffqA5B!Yb^uS+4#H9pIhH0Kk7a@k)HXE zO0`MPluvaOui(Jf9J??Fv$H6QX{LMbf(JW}t;jRr_dD<(d>kHrEWh&^J9YM|FjlWi zxGo&L7Z%RH@%1so^8fIQI3wO9+^GgyYd-tMxUcOk)xM8B*sDDb8n74l=ff}d#%KAW z>Gsq#=C(ic^JQZ^rOiL#K^(u@3h{mE)}t=~2Yw=N%C7?oF`>j#5X*D9{kFf(?c7)w zaW0%SO`mQ9k6bSv=^C*>(~Z+)-@(z-#es;M!!6rQmX=rdWjyxL>Eub{nZWOB7=we5 z7k2OJs4eMlANd#F{}c51sqW3EtvrlXM|}_XNT9EW6Zd*1yy92f-^7pD>1vhVn#5O; z{eas@m=h6K)`olMhwa%7gy&gHYCswG)eP zqV*GBmO|*SwE9?KYaSE#-!!bt1HZ0q6>>2184~yqnU$eP0AZ-?(18+?{ zE#XJHIyaKMLwaIQD)=62ck;U)yto{Exs>~}`TYc2gKKnn@vCq;W>%~1JL%YhgHGLhc|Iae#`saKAw?+aEurGb0ebbdI zeZwajeOb`&eEP8Wop#62*Cf-;6yHr(8OIU%ec3g2()t(+A7z3lA2?RRRDAKi_#2lW z)RUM;*y)|WaSSH5DbR9oAdSDW8@knP2?r=^E9la@ri;}}uvpr;Y8&AS-kj|_aBJr{ z{=#JHIiLH^you+-hq1>;pW2h^nd^1gR%0)=UAA?mwt=_5&^=A61CBB9ny{}e<8_I1 zEAe6P(_C|$qCB^Xu~7C8z6q`gPi734R$w?;TIN+edm**aXK3Ly%;^LA%d1||OkXti z+L+(z;Ew(AUBO1u9?$o2jaRGVFV4p~KX^?K_%vOev;7WkzRr3g9hi|$u?4WYN8WDU ze7(lIyB=1*Om|21|Iiiph-;YYCv&}kxpxnFS0tJyOcMOP_vak6@8XCw*>Ra?99e7G z;;8)y@(222Gr29=&}$z+PrDp`btU$sJ+^#iW2U>bn#FxPjOqaI!-Muc_fdBqi-tqDgVr#1sBG3VYRR2-_O^*@O8~pKj+)@mxee) zX=oSt)5pO(ViAK)^lD&yJNNZCm&fHg;T-5l7@H`knnt)wSNz`7KDc9QtGpdL^Fe+; zCr!=o{;o%G6ZtQ?bc;Eu|8~J*nm@-4S5B(f3qXBi#CpJ4}6-p z1wYrK$DjQ2r``AG&A)FvZ>1&R$JYI&(H9nNu=MqM;XjTW!|2)**Vh>fP5i_;;)g%R zdIJ4+92xHuQ;##>afktt=7=9b|LCZHTbox(hmUbDWTHXfe8^;Wl$3UTUgbNq>HZ+U zmTNb;1}N1J4Sv2l-umWEss8!AvhV8W?PE6o?pTWLDPMkAvs)his$&=Dxc0h6Okd#F zkru#X7L7eOpBK69tCkyexfMZ{^;w$-{Nt;+v|HrvH=0}8+6kVoi85Y{X|-|C)B~~q z?Y{k1tM)@~`xJJ7-TKQTM{hI@pJ79g8UH)VFT-QK?X?L$v`n`91ao&)#Es(Got4n- zaotQiWU*T8pIadphH=m7^MMY8c~z><%6DF@XCa?MoG*4R>j*RyzXdTrCnKjGh+Xv4 zL1yP{BXk^O-2MJ&zT1-7>*sxb1YbaT2>M+AiLzh5-S7zfu!E1Kxtbq4Wp;IqfD>U& zTz&VH*VroN^}%r;-}ttVxHRd*x@Dl#xJS?&;xJRa>*KnlJn)a~Uv@J3**Wq$0Rhr@awc5zg9S%@6I3fBIFTyioM>G|q!LJ*6$`80r1SaDDY6 zVk_w*^#14S1@M5qm9zTyE4A-!<_FJyj;IjDo-0BeZDf{Oq2`b{i>dn2HoTQ zNCO&jL%^*h7p8N;(7it{O-=bWrYn7V-qO;V4w{9q_nfA_(&~n-$a$@;+`sHW#P`+U$2VSET@bvusF^)6mA5{w?rW{COT5f7>kILe z*t*c6j6rxj-v)U?@Qp62Ey3n7=Xl_J+1?v7BAf!W{63p!*FfPXz9-CScvnuQ zcWkh~4rV$5cE#M9YSMQCi({n+5oadY@q$fRd!crSe7nf=bH}<+4`W>k^=R8NJ@UhA zJinE`*0CM*=F3=!`+#%cCv*C(#G~K~ul|zpeZszJ{|dGc)<3E<)25x_f%svokLCBH z;Q2bfjcQLrM)=5lx6{}VVdq%&msY)UuH_qhYI$)yg1@Oj>y!P}@r4#2o_yk&Y=<5= z8u0@BD2}Chf;}g*_vhP*p8s;|ljav?LFYWO$fHTd0S7J=-vfTN*T19OoiA%P_wFay zls>Sz2R)yTg*dyAGMvxR^KRoo9VN0SCsR!;Ij&IKH1+RUru&7 z`0x$Jj-+zY{r3UCL#(nk-sp&zqC9O*5`OOoZ}*l zeee2Q_vtT|mwz^%O>{}VbZ*nDms$N}poa>{&0$yG}f<0Z{hiz*c zb)tlW`Lfe}fFHUb{`o7xBjaVB;6B74L-((hc3D*gEka z#P{hBudntD5!;V!ag_8<8speSdNi_kv%dMRyL9)~`lrCf+s(J5ekZQ9vBK{PX#2P6 z3wH6LSERGXCM#>I_u(v~j#YaZ4?~>#cFS1r#@MjtW^S=)Q`PA=&%(7|0p9ovr%Rvz z9^=qzA?7@z*^WK>TpB<8%A6EBPWB&@AF0?*rLbCB^<4@kG57-vQ?5Dk)^ERPA z_je{vPrZe440{2j@1zg$-343<<7|ECkwI?U=C3Qn*TlFN(qg9=iE=2u2V>$=dBv;x zl566X-ST_L7Sql4*|iD8ej?`&?aptP?bs(+oo}J{0YYb61kDLP8ug7<+7HjVz&SiV z_KY9T&JS!0CnbA)h-txR1zp#?uDEVX%KxgxKhCp>^}4SN0; zGiev|FYMV9=kN-~drgwxZr8S%%INP{{RYOg=GZt61=%jp3cor2>gg$8x?%@;6ZAE+ z9gWir@l1IlrSnSrABc2iqCFtddhykC#*ovNhot@BNkc}}rx@Wdc#7wWn09^sNmrDi znfL5dz5dP(11`Hd24Rfa2PgPxn)RRkEM5|5G~+2PH6O6A=(|Yi<+sS@=lLyX^GowG z(t5sSwBkp-*osnKuh5Sk=IO7^?>MPHIcx_i$d8U|AcX8eByop&i`fT zulcs_$NEs_4*Pz(?y)hi8#rg1p&7qUdFboKzAK~!H&=vy#q`4mr1T4UWgX?N%HcuJ zg_kX4UCqjd9v)}ijK^5iSDK}QcM^B6{lYl{EYF0;H}+XK2lU`b@hF?~iM9q9gWr+H zlxo;>M#FwCsTSASGUtyJpCaC<&rhV|()LK7>+GR67WPm}`P0>yNZXdlme!m5RWdum z>ae#$ccu?UzLZZF zJ~}rSiXX_w^!Xv@JfbbYc5R41#^9o8!>O&a_RP3kbofh|^PelW%{X`PcR)C0^Q|#{ z>fd2p%Y(Oc=gwu(9@0nrW|9q+|0d4WaXr$p1V&%apW}vq0pC*XMEO971?8Nz)Gpxo zsVT-Z@;T%z)4QjQxMw?aIGfrM~*`MMg*F3fH3LgCj;+o~;W9M?y8 z%jZ)z?eARq_xX3;JEqPl=XMT$4LiU5pRs#)J|8?{H)u#$!=x@>9QO%!fYRWp?$w?0 zyJn5@4gn7XpK$Et8N9na?ZFcF=c!^p3-;4>%J;qWb}@ajTw^SxF9n_v#@?|(*ZF#? ze?G5ay&bsi+^78q&u7`QEel&(u#rQ<@0qR)&RYoUZgRYyc|RU!-Hop<%17* ziboj^H^!bm9-Gq4_mw)Y(4NS8>SGecA@wlwzm_XClAO|*V-9c9`ulfXd)67o*?!-Mm@n77WUwf`C zRyjodv_N0Nv+o06fsY6a)}C|+oPb_Qv*NrT`R-(Jn(KSd|7rN5h_9Y&dGnbcdUtM! z-z(J~68^=qyNS(kuB5S-28XdL*GKa<0Cjrgxl@zux2`TIkB{`i@)(ov8W)=CbMJP> zaD`=O{5dz4Ih$dres{BYFMTK3oX+J-a$bxR>c{_)>Z9LhSeLjDJzfx+Z(Yke$No;( zN%xVdKAY;_SgwrID;d*K;-^2LPMvR8Sc8g=nC$mqPKU9BC$sNVO6y(&N%m9k%jjwv zhd}d_K1W?w=k+Vs;PUPc$0Fk`Qk~HE z|77fb8|RQRePi6RvCsPj>knxwYUB+uU-hdU_c(8h@nUhWC%+>b{0!OR87Z$-B7u2>uC{J0%iKTN(%?-!%{^lY`_S;hCWc{Z-sd2bv0w_Z24c{XLP zuRM(XkL6hr{1w(@jA8z`H3k{_N8^`O`<#L8fZx92XckyF-|OR;?r{0;czC)K@;D${v?f4 z{tH^d~gUdo2apoUvSR=CkYYqu^f}yx*~m`d*RVq`5wRm8S$;D%Ve+X*?kM z<~fYnu5*FD^ghV@<@ybCDQA7>?P6_L#M%0luik3nS{!_}ACiwdw%J_IjlF)s##)X$ zdb2%^gAai?Z*4w~d$jpY7`KL8n0-HfJLLI%zu9Z+KZsxamE+!oSERP&ujq*l`Y>Qj zy5&JLw(jd3cxk@uX7}*9FM@-$X5VJ=uAlulrTey3+tp0+Yz%4K-<)}O5%OHax0l+* z8n2ThpAUAH+_;agl9Vghr znx)^E-f1H%#=EGaF&A!;h8A?X#=Mo@>(5DYr14SgFB|;1`q>L(#PhhFpLxo<^YL}W z2_C%jjDid|*5NZv%3MkOR~A=N8ob-RwJa;FYc3C8P5Um8x2N)_y1Gx6v?%bg7S0Qn zUqNH;mhOZ1mrhm3rtmH7PgEMZ+xeNJ_l-FgWiv>wqr(W79_-F+rxcj3A2% zTlqI>4=JvHg;wtfzTIXSk;jko@^$ej%ALtij7{B`o_!N+8H?SO`PCoNyO~@A&muzn`W)XLM0bBGDoU-#$F=>o=yWe?Gszk$H^#8sf0Bab(^`c}jR2x0LD&XNR!% zUs@3Db`xZ~Fhx-gqzcVd7>%7_2wXx(e{u=!!@a{PFF;3U9L7w{|KKO=T zkoersI9Dg5^~}96M!;`1JYt6JL5Y+=I-xU)y-JlJWey#8Rz1-`yAGV1(~Ca zU5J>$)E;o?K=IHzGha@_*O$`F_Ud|FXcjS|VHB^%yzP5_mo7}C)f3`8?`O`xHs5ybhprJ$bF`pGHk<3&JIf_b zJJ6Y-OVJIUXSu;QQ5rc;91LxR?glx(qjq4v-ub#$AK=W!F7{E_4ctH7xeiVIG-tQ( zKGPWb`D)8Tl%otL9OYr~{xbN%sRbS% za7!F94_EdX9G4w>J6A7b&WF9fq5H~ak>;0%4qFJ=SSEVvYxGY8n;XP^>by!C2~NBT zd2{E*8!(PkF^{g})1;zf@m@3=Q+wZ4=+ zTr7*{-_SqsVfcNXCp4GxH(XwPX{GAxkZljn$J3*Cz3U*%qK zDZN+yUf{zyi8dExgeho!+%F)|diH?H?-PN(`8Hr5_K=b;(3Umoqe0fg)}F6_A@1>8 zy$N}Dl)A4;<-10C=cpHKQa0Yy{&by;e?bfOigPR2`;GYu@^_q<_1~ehza^F7RT?NB zi7Sr1b2G!4sm44@6Zhs0Iwta6(&=1N2rgX^=X^uv2r+<>-w)2&Pn+*U{@!yUj1lqO zQqC}Qo`?38Ypi1*4C`oFzjCSP1|c8J%YGX9`Zwe~yzZa?->`<>!aK#o*BQ@TONCU>NhZEpPU%~nTrts2cu7*uYF;B>c~JHssG*W z)vwx~#ck*RN!YiE^)SLE%nQ!N>1Qn+>&5?XQ|A8(-suW__qUJP{B5l9Wn4LfXX5&w zuy0cdJ)ZRAv0D@DzLU-U;0JJC>$8DwyeHKk;|m6_&e5J^TVU*mqMVO!D5amTYos;9 zN7!%sAJ@K&$85Qm59c~op5+^?cy>@KJM0s0Oc7wo zApX9C^&icp{7o${XS}}J@l15Qw|(%d`91pWPw%4PF5vY*oP*6`JtyPw(SDa`1vbX7 zr9EC;a6GWhoaaP~5B%z5s(XLRU(<-2^G+idFC6!T!S zc}aqWYxo(9f6w>Bwf(^6q*>^5jAbU@1zJ(NC9f;SeL@E-0shVDNF$zu`q%Lt<=fco zKKOuHQv_W<1ljUB<&)IUjK_^i_bI#KM`%{g@AJE8_wzjH`VjWnb@l^tA1CH~h~H8! zD$GrvC;J8YPMm?S!0*TT2;=?#vv(zMZdK*J@ZQtsQ=pyX-W05e@8JfD3o4?ZLuV%I zOj)#uB8v*BhzNor%Y!1If(i(3ETZxh1q9i5Ej!32yFe*rZA;sk%uM!Vrg{JW`R++> zGB?RhCYj01q`%+vCO7w<>_hYf!Uexn`10q0deI+h3TeyR2vN|A0? zpv(Hc{i^)4T82aH@GrG|$5sQV6-;?cY=R~EUF>b}v&!Gc=fJaW zZs_*i&T~Zw40tl{0p41=w<{ZtwPsc>SHuY0{UFTMfpvUD^o0pA<7I0f#oFt$1Mcv? zIP%V?$wGiRTCnyB;xO^h1--Y)cd*}?$)oLKfS>eu;<2rN2h8j|PhSegnvU+H7Y zMu&J^d!84kHdTH%Bc7t|rb2tTA607~A?{oGIi1?-+-ofakPG_JoshGa#p-X z`T8ro$1~_RTZr?h)?Vkg)WA&%*<}$G)I_ zVZf6w-o;_Qwsr|pQ%;7vc@IiF)o{H@w2Uh zXQ$l`1UeAtK%fJG4g@+7m|GA~#p-9bff)vYrQ1FAh9&0MN}iAF^A4%=a$z%jNv&xx7KDC1=2eBysA;)1YxUdty#s*` z1eyy0=CNe_2lM?Ar|A#$dq$PI$(-zn2j9uwBk=uH`iq(WjxmFz0ll8+ggk-vcRHUt z5a>XleIS6^NneKVH=sup^p5Pa7w{PPoM6m0@8k0>5uCwf-uwaNc+=0~-2CBkhI2fR zI3E0#xni+rgQ~NjpXppX5a>Xl{UE^e@QA%Uf$~1jWcGMR3;E0fM)XU4I+rcZ+cR!M z#cts24xjJ!MT_~l&<5!O^5~T0R(DfNtNozT8BqrU&4+;B7zqW8k#FtMq}s8~7~vo-G=s~FFCQr3qI|3V14tnF>%_hTsEOOEa+&U*4(E^D?T-|m9g`}DIt#$ZXFEaXG| zHSCAE-UTZ=ab6loo$H)~Kp$!&AaBmC)TxO_$Xhht=UxAib^%21g3(=g#+Um3Qt3@v)z9-9nz!PO0Z-~8&Q-nZO+U2byodh7 z{!Hg{2LiJl0y{6d_f0`_WLY6^k;;`mV%4Ka+&!K1@Ar-UCS=4utH%&DVoRAL4QIdZ z=-y`efAT);0cTP!piPfFzli%n1Lz-+Ga}h%x{Y;j(QP5W!?ltdJFf5_wO#%+Z{D(! z{DJz}&b0%9*$4sV{zl%M2W)aP&$?4~R{1B91LuzEm^b8(tx$jEI_m1otL^X% z`$gQR91vBxJVZxG7HR`mRa-MpvXP;cAKe13_U`y^=q@t-^zI)0%RCuIGb z$@l%e zSEFrp-s?c15eQJ;N8a*;>I?+D!yktKZk?rzNA2|v0-g;juE*c?+S}mwdr@#-%Pgh1&6*698E`J(V&-m; zxt7`l`!HwGEi$jL-Z=puSj2n}*%7u)8h}`X-`Q=2%~%}RF_n*k@K)=9wFr6Q`!}QQ zP8%HvRDwYFW~-6MJar-WwZ#)HT{$|=@6?GS$dy;={i(PQef$52`+eR)%U_pUXOGG0 zdF!hl*dtbR4$gKy$;V3h1+|k@O-i7((*W52mG0v=(_gp4 z7qb6C*3=~J&^g2rZ?wez%w#^p=Wqt?uk^KW3`z#`=5Fiw1||d5&bt}{tVzxDaO9`t z^|J59wLiuyx1Gl zDvyDDPNjRh2CLFDx9xYHn_Cb7&pe-dwlof2Y-5zGp?fw>k8KrXArl{@?En?{zQib) zsh1Ylk@J0tIzyBhwsZ-LE^2)u3OfkllUYUF=iIvIubR(u1Nb|>RdZ##IB)e$9lwlN z<#CP!v?v=ihngCH(8@V<+MZqraL-1J%#DK7!eL`O*C9Dw|ToHQkgDzRoKa^8$% zCJi8sV%&4Tnb}j1rE~2-zzG78Eru6>uRkX?u0l+V!X55s@UIQQHag4eGzWbz=#8zk zWc0GQ9pTgJ{GXY8X)Ixcg7T1tALDB%m2#C)% z^`)sAL#Lma1%d8uR{bYr)<-Enie4+WKHEONif89%%B&D0XW8mW$Lw}G!G9$idq>Zl z?0f>K9V2=`d40!W#0aOQj+_yna=Z&0xf8VhMjF5~nOs}MZ%p}ECJW=v`>lll_1UmD z^Q`bog&*MgQMG^79`Yvt(Ap8SrgiYYTZNtt*=%%z|F8=J7sjCRCd3JZ}`6_(@!Js_8a3r5PN{K34#V>MTheyzuicCop)v*1X!OwKGC%4<$p7`2*Z_pC zIQ7vF8W3EV+NO~{Ci_mn6FZDK3W@t72jrAYf^!RpO}6dM`!gQ`;DhIp$6GS;M#i%$ zUpZ~ZX6lPGafZF#kx0Lhc?M(O1a$0!Vgs}LFa=i`zi;`>9@}wVzOH;dJadCMf7Ws( z?8;~h+ZXym+5;Wx3)2Zl$U*;W%~R0;#v!P@r(>bnA-_s8P*oq!5#>I>(yES;j z^I>27{nJ0X`bXgJ8t$XLi?O!Y_gAw8(1On@n>h6GiMT4Zdf4eMcH3J3aRRWZ$BEnE zsc5sD_v^EP$+3Q4s>f}+t$Yq=5f0b70AMiA+~u@g;{GYE?6=c)dqV&*>-Q5kh%;j2 zPu`XxsGL-u$1 z8F>W`79TotfKPmv_av4OXa9Sxd>qJsvMp$9jBWBSXr?FR9xLx>D!wC*<07E}u_6t? z_&RdHR6xJeN2?(ahP}<6f1COIVXMm{w$b$aVkU9YS$@|!zH_hjF31;h_=A6chkTv! zeiLzD&Owqkpxr0zwFGz;8T95^u@h|ANSg%C>R#XK`MYVq-^^M01GF5FR<(>E2OQ<* z{mwHT2+RZs0B3(h+@*{QTwKXGr#^4&)5J-q*kIc8lnn39Jk+~jXzPAoM)}aG^E>1I zh-Ej)F5eb)00@&^Z08~UTMWMgxe>;do{bosW}f|>Ed2Y8u?ytvu%-c)E?^|ioow5k z_d5`nZV2#99P{r{x2MbxIbfwemxr7n`-`s5T>mBcJop{7VPigECNfHI7hSnbUl>M= z@2IjFAa3#=?LUNGq0Sqk2Ee|0{y}5(K+vIda6xnPfa>-qbM5!0&!FtWSYgn&anMLL zr;wTG_y#8f*v`At4gsFgK@4t^ayM}Q-oWNhmDt}x-pq+&4`lvQdyKGr7M@?oZK~*k z*bF590pE=n>B}+EO*mQ5gM%1LA-Vv35DV58YK3(hPZjEgOgmu81`zTj&#~JCNelQr z=!u7K5^?d;FA>u1;me_vt~wC0HTu$n&6cAZmaTpb9MLjXMb zD8>jVzHg3x;y~B~koU(r`%vx^D93-EiI7^rK~JK<8~sM~Q@S6<`Gck;D03iR*uR1c z*KDJIYxirgAP3m|0wUvlv)#wu8D9qi)gh4Il(o7kn^VW7oDRF(Eqtd(;cPnV>=V#5 zfc`-_KLZ+Y0^b|hYW07G&G;p33%P<1;646>jeh_&k*?l$-VMqgD0gSZ|6xz&x0C~z zQPGufd5Gh+!mqjZG8vP~h&-pakLXn;3;m;F4mAF=@b0Zuu-Y4_)Sc`sS0 z=Y{kC$1~P|HbN!yFU(GRjGyDw>%57x!zy2?)N-RuP-GtHGt*MfrOx4Z9`8WF5du5- z9zzcE^g8@KY=3|?L-;*ljDCrJ0Pztkn6V%n&d~3bl3uU2C={?(>~KZ}KP2uJzMg z$hmJVwC##FLiWE`{3Bxbr2Zp*)YNhATk1U7fk5*h0KK%I_)6bB@s3x>t3%-F-|R*% z1Ghb)?)Mw;-YMe4%PW7E--vyd?}G+BNPOmR@E^wf-t{~3zTfw}3ERQ*yx)s+K)lC) z5i|b9ej|0Pe49=GFWVIU&E+;4pmK-^y=?#dud`_-I&%>SEHTGVvf}q4r^jH!%jCn} z+*J`{>{6WnIibglyE?lF91l}>=lk^O(ay{<Vi2B@QU_-veWXHM-40i4Y_mV1wTk@5J* zYZud_Wo~}Tt@?YlEB7_zSrzxf7+BWls{VZb4X_XVUTDwYM&bkL!%7|ZSMA-I{u^tX z!dGI|Nmsgn@(Yj_!Y0zmBQ(<&cm|C>XvPku+)w`$<8V|CKeO?;TI}lwQJ>=yw;%`@ z={xA#C;w-Sz-9CDo4S2>rauQc;7*YPM%4L9We=eK4pCrsWi{^%?$`l|19Bq`FqhOIE2^;_&z}cV2{~t`bXCp z!EA>BWcKT6_t$53xEFEO=R31~-t96!sTGlaMC>p;D>bx{_)nS~@TMc}GS1F8W+w!~ z-sIj2n$${vqcp3CZ=V_u!zt^d8w8{J*6E`kkqNbIpZJ0)@e?Sdg=-Gm%!9f)-x z&@2eR7k`;u2cRs#eOPZE%ZMlTUcV<=IJ@I!e5|2GI~aTbr|4XNZr+=X&-gtX-UB#f zi!=c7(p(24i8{#XzS%hE&YY$n0>Q4qohfV52WZ8msCs3VF7OY(H+e?bm)Si$fA!m| zjaK3SW4HI4qkC3+&i)%}2Ct?cjQTHW5bO~*`8^{$*`Jw9pZmPSA7Ly8*8_PJs0%0> zK$?IvQoGJ2Sajw(br1lLx!vMB1scG-*5p6D&p*^K${;rQ_7L)Yg?&SgHBE4acR$Aa z4lK-U?`HInF92 zu;EULvv^lKK47eThY<{laoBc5I3afhKl3!iUPX*0P#=l^wkPB@#!=N*_ zsf7T}^gn6w97_h!_^+>$%a6G_S&y5~mj5x=C-$p4?}M{`HP5!O29U=8u$gilJ2I3`^84UDzy*g(-JtOP%IvXK?+*_(95Aj><02=QrU5mLrsk4$M zSa~isQl^iX>Gg-~Si2Tz#G8GRGyt(ks4?cOSIkCuyO~qOmtI2HP(RYTfYQZ`RHD&Z z%WnGZJTvzo;NM~`^6O_UJhtP!;CTg_fZW}&5uW{{EWo|WtHiNoR_u`8VRP z_5+X}$Qc2N55oSlejB!I0<6Q4{HfnJ{Ej|z!=A9={Qz>na`LDiV=$;k7w|@R>ocNP z?2Pko`kws?-v5H8AG@2mfc>}5XPl$<#1?D1N*X{NOXPygGxpqT9awcBFc}Ca{D-W` z9Nr=eB!+M{{Tc2x;NFTueuh;p5kP~Pd`t%~^`2X6+GGHc7?l)5_ zcV2Yw1RBt9q`yJ_&pzM-czo%0PrbqZZl%wtL-yu~twvC#=VrtGuqUy*@O{=~R(=7M zM-e%aAK)46*)Ye>9HtinIJ^HR(J#fmNBbOO)I*-sV(hc)V0#)Ro{|?)<`;QjMC^cy z4y2LmBk`ir|BXzHcI-ay%UgxL*^l6i|8>eYfZUmId|qYH4uQF`E^y}%S;W{I?o^Wu76j^n?3?E!3x^@NDEXP4{8Kz`+>gC z{mMT+Lmcn-Whd0dWG+#}13oME2RVyGI>0}^2YRzAWTwvZ8zZ5f%|<u5 zUu-3LK5<>0B{tHCB~IMPeF<9eWAO1~{GQ>(mD+~?a9`rORWrlVFT~bJ`9aeIw7C(v zhMs}Gay#h4Ys{U&xI@qm*RjWy#xPUvWB%V37?a*cK?7n!15j&lF62_1DeE+o#@V|d zzXfFON#&vpnnODTJc(0b|2rT4>;JB2{@wGI|2uSxmE!BN`yj>MNV$UhoVH2I38V+K z8SuW?B+{$u>2Ib#8!%$0lJ8UZqRvHFEZvgyb7)|u&#g25xe0-wXY^Rg^Ab-~v5w#v z=s=D#0r6h{!58@;p~hT-{hs7o=K=%)hhJ&kOWAF_@za^VbuP@kGspIYK%@CQp@V!E zHiav})9$C8o4Nq`z19gx7qI6?k)!-j)+I*`;?GcTz`fT)#^{$dL{Nj^lz?aW82x=V zUm-IsS2Vzkcg_jV=7nzaxzP_7@@Do0AN~jYgRcUQAA=9^Dx6!{E3o-1U2cYtx<4wW z4Q*UQI|6w;@{weUvVN4dK*k;A(8oorL&SD*Z(Z2T7S0d^=dalYH4boYF}9bRerNJC zfkh)-w86~eIdsO}J`f0@-Y#nK1W=p%o_=HOfq|_u@2NGqFzivl_toS>l*c6oSU&R} z@N)xr_Z8s4e?mCAEk^-J(o0qG=VY#=)=pbt*P#ti9`Ly zPzZ4d7YO|r%j?*rT635!zjtpo>Sc@tVD#I$m|uCetm9PAgEcq9W^Nhw%`4j0PTYn3 zoeO#5`#X(?v8mw057Qqdyjs<}hP=*LVI40#qHvSX2p`YPBwf#P-_%(fXpcy|ZZ_DlXW%HJ8irgVb;S5ZS@&M%x=>Mv1(ihM#_#IPY=pzZ; z`0wyd7S1BL?PDfA2VLkY$VS&M_2s@7hCT2LM*5vg7bZWpbl%c`0}a?K=*^r&c_3g8 ze`c{~!No_#9zbD@n zJDslgZGYeXS=u5!0e()11FQ!YjQI3BqEfW*TfU#6M(I`6Z_Mx!>!ddAQW&w_S; z0kPHxLk8Fcwbgd*^(GDu7|BaP6B8mQ(T+OQzq@Z^zNf2rE~_2J-WkKxLjXL|559PJ zKX^8EWZ)0>hZEl0@mBC!Vos7P%g^fL49`2m$WKE(ys`|S+l!JvD|&)>zDdO!Xy^@e zWw!5KkW#ileQ&N@SyvhS|C{)Gjx?BSL?5cQl`x*7&lo*$i8uXj)_v-pm;d*<0-Me} zix3EVk~<+*?hfL-l9P)$KV9H#5qJ6SfH?-edgPtO)^SeNNWCh$xPG?Ul{K$&7X2As zYHz3W**yfhH+$9#yiZe}q7J5U9=6uIupSrrjo}TH?cgIlDPqP>4jHkV+_&C&ayCGK z^*pdg{vvoQ@nS{WTs0hZ`YpOqWXj~-v=?glpuYVtNLOm}etVnIagGT&KoWE144;PA zWUt$)dg7S}`hA(-^m#|FCLDp|)YJYq&=o7>U&T4WizS{a@knUl8pCf4ezs!+m@M>X z#`~eJ6`Lbx?Ag@Km7K5my|&5Q@zCvO$wv))(;#$gQ$xXTCO<)4!iw3eiv}ot0(FBK z`>WfV-fMo7nNR~!`S;AxGY1yNwg>*7C^(*4R}MRz4LVq0oKE$5Ho#7ke4(eSlWVmc zPP6TMzbE@K@ci{$d$G&e{c&#bz2v!q4G=Ls`)GI}{suLKvMI`*kSVIz|4Z6Z&nlW^ zCZ5u5wZ}ETY0!xNEa;7Px5s!YkAk@smKdo&1-!!tk@ivsU@m9-8>-f@H<6>vhFE}a z?O~?>hi5c+-~L?Z^X!2D^y=e?_kxoO@9MzIay%`5S8~5MaiJYjh}ZoM`>Io)P_=HU zx0d@7>}^li=a4u4W7OPPztmXwmgyRUn|@)--wyQx{+D?fVCx@JXMsGiQ_J5C7>O11 zxk66({_bY##65i3!SWm|{N&y=xIkNFYbEXPk9<=7@u^Lhp5$a@SCHbfFAdaP*;9J4prd&iHN;CqtA$6 zM;u4Km0LI-;)PCS?XCQymQ}L zV^8|c!A}5pe+YZt>y+b|mjN}wzh-ZZHWT8w^8dmAZ`t`_he^Ib-wOEv_XG6;iO)zq z?e-joy`RqK_J=^w9Qh*G-?G6qiSG+tpj-*L;&eL{z)S8Ddw)IlM9EvrckOMp+vi=7 zR}f=F{xQjAf|!O?J8tz*!Pn8M@%AoQy>-BoJdAnI=(8t$Nq2dbvk>tE%lqzuE)QDu za1e16i1Qoe^Td0;gBT#>1xefr-~SJoW0kSmgl z3f5TEIM1~t>`4b8`#%rdF4$b{<6zBDAmujF8pclXPiStA zboZ99FYCGUGi!jBw|q7x+gkt6JHA;bblykoyyx>bJbelxjqv&P?Pr#&D5njR#7 zs?!EX+^zD*-2QQi88A{MXYfF49wT1VYhO^hiFfqO_JCULQ3SmspI3ZeVkTJUNa@n3 zQ@E`5+m)Y)AXXye&E6I=(;32%{GI+;;=JH}HWe_^KkCJqtjeua{v}`x?>%7VUJIDf z5A=E5*J7{SV&|83h5%}2-N}8^Ox}-kd`jkp4t%qH?%1d25GShBj&iypz60d;ebKH% z*xPN_&p|VNp6EI1OtX?hHd5%t#9HjdlG-#gaZVg_uxoT@;y%yj z)rXViIBNecdOC7#59-`H>?_nY@^SJj>#UGV`yz7nU~I}3xe?k?=}Q64eY&q}*gsR|<1|kAr%x82 zPdzqxJFb_1rra;*d=WRkME9w3^Bb8VE1t+D?n~RG8EEsDx~-Xb1>d;Q(s?LnsaOcy zi!FA@F-rd$aucvF!ap_MlU|V5lLm;7Cbb-OP4?wEqlDF?;Ag% z)%{L&`>Y`q@T7YKM(Q}g7J1>*G16JU{#o*WJs$W#4ugMmh`K@n@0y>!CBu^+CCo`MF-)a;Ctw(m4Cgu~&(| zQg#V|ioT|@O~CyvkmWxF-f|*n?ajdVHCnC`n+j{wCe(iBeb{X-!Q7SJ z!x%QyQ#=Lhk!S82_*c?FV`iN}Wu6C-{TX}LD884zN7{v zimxAb|BH27Gx^GMHvMKMNgis^0EOGYt@Zd@$vwvPApFSh`C08l*gXitKJWUirepp) z&U@%ThzA@K@t^Vs@}hocI>yqhKEp=rQ_$yEFh-N}QTPqNiR{s0eOgOrEBKxwJO^it z7ib^Pupx%^0?KwuC-A1O56_Rjy;ajLHLpAqJiDd^Ot~EF?ZE?i5p7o?x zVSg6lDFVj2ZGlhMeIBv^+k^bOZ?neQigsB0PS#ZCy>y3@m2kCqKdR;rd(<_0+`YcF z@8{mJ?0ycgb?#fi(a2q?+i8a@&dGhaQNNlVKk5y9je7|4Cihgq7DD)d2D}KoKQYub zxJW)S_?d9JCYmrdlt`3*>3E(fGTH zH52GNg-#dy!dB|HNBWGFCHZZdG2Ujh0lDQJk_2G;KbhZiCqag$9fW-87Ov zY}Qzt*$#BD+qmbsX1sz{&4?U8tn-TVrTb&N*JBUfN4qNTIl+eZ{xIrS)8<4OjQf}| z8?sL)*sT!HA+oO0ZIRP5>>YiVZa3gfp2dA)`F;=5_uJ(PJ?99HoBCXJJamh%#pg&< zKo|4=1w-%C^9^`M?$Z0mO@H(usC}mVFS?cKztX2cyj3*7h))VvL*CJ!$o^1gAF-d} zmh6-~a{6alIs(YLSL^i?{EywoT%n~;wa>SZCnfRFMk+};l7AexrCXu)Tl%kd`Q1Kp z$X=UMerLQ1-qZsfeE z)40LMdY&QfD!tvap-?vm@#Lq~LvJW6KsQ^f$J;Jf(6I>j5oIUR0L0{ccQRvxd}EIf z8mDicdk*nIB{7qe>7yC%Qa+aT8#q_^S|o%=DN$_L>)QoOYQ{w(^Y()^A{U|6VGIzm< zlz&q*`)IT+$xVqED8xe(ZuFU0dR2w}Ej*91J$>JNM`(KL*?^fiIqZwSzkDnKGy4Jf z3QvO`k=4Fg%KxP}!*-*JKC(^7z;}Zeewb~s9v0}p9iN9?0JS9k&RB!;KBnq>z_X#Z z9$YM)V{P0e3G-4db0a-NL5S=PthzSXoKuIPrQAMoej8Nxa0CEk3A&z0v+K?A-5+ zFYB|Y69^qho~`GIGoX8-KGrz#gEi*#{i(bHW)1n{+`&`mgUIixHMFVdxxxN|T7bes zYPF^C-#2!yes{XAOH@qQ$YHR5jd3lly;ZQ;lb&HOWa0ZYCNoC**g*sC6CTSwIb_8M zrsDpt!M9DO?Pk1-Ixk`J4B9AY-$9+!%yiKv)+t5*er>@U- zx!ycWiE~_kU=3KVksX$xJDC%4NUr>e^=yTp%Iw}kqb)sc5#LxKpRrOk$H@H z)t%tp?0bymWQ`rlOzz;&C|pMcZrV>*Dd3Hse;U8%zdHfOEt(RVSf zJqZ5-?_2a0=<)L`M#wvI1oqIwd`^v(elX=@W6e787xF&xDivy1M&MgjHU(e2 z<@7Y8HBRe6^^IXQ& zU|rUO?&Tnxzz!j~j<}{=57K5{iSLwMxZW21t_G)#q!l=y_fn(db9%40Yw+W$U8O5U z$92rD-<$Zg)ALht4|Q6+jBzL5Am3Fy&WL`gm3=e6F#Ty*t8wA?Q;GM*eO$y7O$*?g z#@cX9poiQ`f3fGYCrpCKj|zL?Z!{lK@KQ1X&a+xF1>Rd5@y2#j&l~XrX-fhvXz4k( ze#AF$uNUX!G|%STBd>>T{6V{)nD%qRq89xoE=GLW-!!tnXmbE;4?|xMatw+Nsj*0X zQSM>I$FLq^C$7xjtH25LJZbrRh6=u(M#k&(PA~Fq5l$kfMUjIquJS4Pjl`)=&$s>_ zY4_Nu=(xkm?g_XSWV>efkGX$<_7LS`RdKnklJgtg>$LHe`w}y2pwIsz7LYTu-hq_c`RxbEubh8~L|D&iZG>NIuxT*=nzDqghwhYk&;?1jkVe z|ElbJ*R+Fs7k1*9-F}5!R7Y~o6XTAAw(>rCFwa=ytjr(knLF}reH8NeFJTYbpnWtV zD{_r>`;D%5$$QZ0MiuT47}>Mx8Dl-qhP~rQ@_dS==PKEov^!{K3UL+nv^TkD^P7as zWZ|qRc@_3@OLAbqAAh384OzFQuAS@s>kbEGY!iKn*{7ki9bdZlV!H&8y=hp|@u^x-xH}pQ* z1)<~GaB@WPW3D-20p7fZXR2^E`G53#m9DhUgHP-Yz2XtJE4;p*IOE3VC$=1=lW$UK zY|Xe28_0QtiPj-GN5U!K%e7orEFVACq*2o?HHnVL_3nX@R3D%AKAGm!7bWg@}slBqq`^;w0zn4Pxyc;}n18D(mJf!uI zuddvA(PeK!9P+zR*ZMx#fC^`fCIef(OPjXZI|bdgnR9n)%d+Ev=j*XEHWD%c&XU2e z0zJDZpYwa-u85iWqMN!h0i2}`BEAT1tRv3r{UU2V8Ga#hoy2xHwWH7Nr=H-s20gD< zUXhdYDxm>{vD8l#``d`?yq?V%ALQ0s2xr2WSM1%Uo&kUk-3^-m6wjrB)+A`7Vn3v{ zdW|U?($6pYo3_)G$Kg2b#i_n0dxvu%e)A7|H4B^JFZ{mYci7hhy1*Ck+%=rj=Ro@? zTj6}k600`D5B#XLNW8JX)5zz3BXJ68j3s*)eAum|lbko=A^&P0Z@-!T3u!#}uv%}N zcMJQ5Cd6FX-{EKGE5X?J0X@B$v{iULotaU3C+t- zn%9Jp=N-878hHuffpxU)?N-1bs@4H|%o;sMUO(f@?-ceWPVjp+yvKgO(r4zM!yfn< z^r^Lk3*l$;uW}wu`V;?50QK*}-W2L#CST_M(d`pv?4Pu`(pXz{pLr%Q|JCfDG5~3S zz#$!7Jb(3+$Gz}jF7jcK1MNqS?*=H;$ zTVp<#kOoi&m%Y?1{IRz~+A6j@_SHxZ5BgMKzq^gJUdB7F;IHwqxmV;G5}4ee*MVn6 z>F2TRZwmME`6~VUR9_dHum2BZ$T;^7@td<85 zQsQ{T$lb#^uw7n>kJJnC2l+p}4l(l&;qNT+O4vU3W}Fr5SlhXoYiQg28zWof7cicm zvBJnnb0lKM*JN)*I=tlfL_E?ej}c=(+R>_|M9-lpZ?v=)&4%8 z=bR~%ODte6-f!ei33SC8mg)Syn8|aujB8#cEmKt6l4{ z=@&URcZE;vMXs^PqTD~j>NDrZJwzFvYrsDF9O*OpIruyNl50X{<|XI_4}kw)3>-cV zevv&xp4?*Ul6JehoAHs3sra3)6`Kz%$Zs7m^4lt!VaEHQqxzAr;hQ|ir)s1bvBP1X zKHP7{zK&cjCxa)Pk1_oM&-?<{pI7?BbMQ@#{9M`^IgHn!XXH@W`OtRk8}#c882Qjr z_yMbpnKp0gMx=$beZ!}B9LDn}!0b8T$}s3!j=Y_?2N-37M)v27`zvpgay5Kq_lQ2A z^H8|OE5Uaq&%kcMbzy(Xc3~tC;{%%~V*gNwV%ze5t?!%qJwu1`9n9e}JO0D(cM@r$ z$oR4^EE=WuNb^5S?v03%#(JcB^qv#GFFKO4S9+7LRhY|^-X`wQ4kq#&<$w{Zu1NY< ztsF=I@o5q4E3OM=So(KSA8dzu`viV3;kpyAg|1gxV|`!C)s(-9`;6V%sAz-UcXk?~ zWOy@yeV<$fSb?`^{)uxpR|k#J-=Ve{}+<>_}Og}8>U5YT&Jd&RY3(93`T@af> zSn)gY8~B)@{okoi{A^rfik2Aot&kA@(kAcnN0RG`zz%jtnGjT@Xbf-T&dm8rG1+b&y%pzh2 z(JtD*6L<~(VDeF%mj%6wuZKJ|uE)nK$F8sZEIts%5Ya|K`-8SYXn5H7x$IvJ2k1Um z^gHC72d~}-`dpg$EN#lU1-6af6pYw6*N*fT<6n!sO236{@s(cGjdZ*Id@hK3vz!m{ zjO~GLoos&}zRzDxE&eNbArGhcB!TZ*7eb6*Ou-)dKz4fKZtRdL{T%ef6)suwGwLZy z-T>C@EPb*$-%uy+gOJq}tzn={@4ne?kt;Yi+J5YKSn0ER7SC7uz1n@<5AjoEI>x9n##+WE{`n3nqF}1#8a$0d@hM=5tH_9?t}7o>-mlxn(8b$=?wb< zu7B75KJlOZHpUa`g^SbkLPrS$B|DkfhkF;SESzJuk9E2~gAQNG*b#x1aw(1vu*vC7mH(;2L2K4|3B6{3$0%t}_+Guj%VFuCdd)>w6lmvoZ z(V{0BPm90XpOw#i->F^MbFJO;QTUIC6mIXOVvRC?2|M_XAiEp^{caet3G$c3j|%w) zKkjCY-99t0aQqm`8K?oS_&@gWVfJ?^bCE8#78ZJ~%dhm+CKO$Qzil1%?HBB0Yq!q_ zZnNq?5l?kLaCkOUfR>q*Xxv*xi+NTVKZMd4{F3r9IA8`*bJtI z@0YLXT>Y-;40R*qHvvE6K2iHa*-+B!=x?pCM^vn0h2JK1yWf}lneYzDcS+sJ-XQd^ z>A?Sjtk1m)x$A;<%GYRzPxo(K@>3lf+BCN9QVzHhbQLC*>06Hj}fh+!uoL$(6{d^#|HN zc?Q1Px|RP%p9Id;HHDwR#&HMvJNYbi9>h2H+55veo|~qQ{|mg>ANMI_TjYqI+WK1d zxi|W`*NA=yGMma71e}6j(6U*2QjhPr)k9TdY3k_{a>XMW|7A~QF0J-H1m?m?^|4#Xe>Kz0uF`*}kG|ZI)e;XY}oDVZU5IHTci7 z*vPkwJqZ8B(-B{K%H#U%^PcSIUT?IUxFq;lj17QnaC@`jgTT*k)N8{lXwI$G#?2UZ z#CR+UE*RPY_VZ7Jwj!@~@(Az%#QVjs!Jd5t>%B^1{u9dXO**XUM3Fvd8lmmp%;{^_ zvu6V?yPbpJJbkd5#}i)kUx0=_4SYWxdtldk&k!?5Cu4``(|}ICoOGn0wm&nmj5TSi zt+U(T!0#6#ZX`ikul%@(PlzD5rhZ?}Jj5TDJ?s`9bw8(MeZq|JqpwEyKhv)PUt)Kz zo8`k)xkG4ogO9fn}O-?4r29Ig?4Ws`*&eeRJhh8NH;1iNBCQSov`R$!T$~YR{UeSen;JV#Nw;%_@2bo6NezLzJ^$U-(#)^ zu?AVaYe9VEo`J4-F<`rjG@WOVh#SmlM%}VjoABi?1wVJa^^ueDY>ofoH&2b)W4BrfJI3cR<{x&MCD@z$A`dz0a3@X)nyFtyr-dIq##o;b z$P>u%gxZmS0rzt`olrDi@gKw)V;>Se+!rE8H+8=?y-nKh1vgVqV!S)n3vb335eGqA z&jg(A0-rDFW%SJwUXaPou2dt*-XHXW58?e=n5zTlH%iX<*q^h1_IE|U%>6@{(~pF+ zt%HZ`m@nj2-El{Z&x{W;E~A;YA;OLQ3D3_x9r2D$YaFi~Caw4kJUCwbZL0RgIQRKp zSSQ3VOe#KTci;H;h)={7>YJc3V>{wpW-I2nk`fn=fg+6Z}3^M zR|x*ouVi0qwD&de=5g*b$~cpQHThp$@c_oIg64hC-q&pY47uh)usK4lEn-)OYG>3EN}5XrTL`1h{Soe{(I709pO z_vcCHI9~oyFBExaMCor_OXMQ`ryKd5_5_^eKaVgWZjz^1xQW_FQm>wQBCCxJXGni8 z`$yN2N<8WGJnaq0?{N@Nw=@+02e95W3(tFD`<#X`Mt>Ru{d(xI27jb@8;n~=4OW!U1 z##m>LP2@+$T_)n~_Ry{Y9AI_Qati))))3bBo9_^O&i$^^9_lC^e zC&Qk^?vNdh0+0O_@bGfz9`^%YPe4~*3wdmuHVNX1mLIsU`6o1j@@@_^LgzK2t|5Gq z?^W_k(`E*n&mBB7$F|sp@OX`%f)nJ|7LF3{e&{C87S|U2-p}!JjFst>C`T z#tUK$tI5+jFZ?OFfigbm>Cbp}xze5i9sV$UME8;Y$oo$85@Wy|MQ>nTp7=49+M7%F z7b8~@VQj6RIy0=|no+a5yrxYpxG?Vq_dDxg@sBZv-Nw4A=T&R9IR}2vD!g$^WYEar)imZIpj!y-x7?>v;x(W1$W`k>_jrB3H^794pU7m$%37)bpW>AcwrY z1|ZMgXEm?J+!M!F%l!g74r~KyRcECrhlq`v^OHH|{yN~vH$DE{X-+fcUau#*mx7Io z-IbaGq@@EmYdKTqFx$s@;5PaHWSz-AbLf-U7n>+(Jq$H+P?eo^?27O5#4&7d|l3&rB@hO*s0Jc~-p7JMt^ojnp1Oyu_aR{5WI6 zse8^{Xio3UX@lfiifxy?pK(DO6-@`;zluDf`?(n*V#D?aUappy7+vR5c(N0pcetl$ z4&_P6^1rLrC;HlxV|Lb63iLq9E~FED7WNFBIZJ)HQoGgkg-Y+t<@><*;|W*lg*Y>~ zk-8)NIzM)TiQgFf?178;d-7`j&OCJkzVwei`sP&&`CgxAWWU9*10Z+O3i#XSjBnKm zR#SNozR3gi8q%guer@s0c-WUNdu<3nU%WtUsq|l`Qr++qIK4$* zGwM^VV!jCKV~q8}S&$Py-Sz6u`yfuV*NE+neX--0KqIgxtDXgNYQJ-@ehBy%tbHGR zedM(Y548@=d_1(z)@l)JzYG2zAio#gLB&2Q9`20ePIymwLvrPl?r=Z3>6>E%{(p`1 z!t&+WXo+J#{9VZdB44CdxQR)w1%uA-h{g6M?qaMrY$iW-f(LSH2YO)}=v}b({fp;E z-%fjfs4J;zSMzzDPmb#WALDtEY0i!6G>6%E4?6mC;v981;soNbZl%4x)?6c=41AQC zt4S{`I~{PV77s4RNj^_qf6THkWS&RPv%1dBF^(H~WK<0x_x-cq5wTZ!M{DMss0EA8 z@AKyEg!mT7R8RBlFyUd><@@GGKTZ3Qvez1k7;G$vt52>+KG%QpFKCQE)Q9*&fN&q?1#gv)hFmTlY3U;)yThkGUrf-yrITSn#UY8JSW&XD9@diB@R$(OR+XQ z_Q_zsnY+BFYp{BW}jk9J7^!kkSJLzwmDdT0$ zJI44BZ(zSYsg~V~aT!>*A3;x0bs}v3@;dQO9KF?7mgP#(qmWSqUyd zV=N4M^R&nZnwJ;l4EmVaPCuS=Y6G^Tt4L$&(*dMTlzOi`cc7Wv3;ylrA`Rq5a2nrS zx<`6?@Y}c+BYy_X{w8&L;x=@xTqQX0Y#ePelmloJz`OhK++3yS+wDGTZ{4fx8JGk2 z6LQ%=ugq7qr?t}@XiF!prEXTV={Lgf`HuMWh|8$!@le2*?yWRe@QF`gkK9EbK{?Ti z=W(DnQT03a(Zfz{p$70ygnRuqNokj7m&@D5xwLzfEed(&%75#8uM7d!n5GX`)#dc2 ze<}FSGn(n4N;tv#Ka?{i?^Z6~XT17B*nGafH|z+oQ7y0BTYLNhUiVY(Kju!SZG*fD zdvXSx7hSd~{IiE5_t7Bn6+9R?D6(;VvcB+Y?au@6T!FSeTWNJdU778G%hzyk2)(I& zE{M3TXoN5I4ySf;PVW}Q%atDKRtG4`0IUIRZ6|I1*FaA8}4r8o;S&7ag)_x(@^g{MmFTbXK4?S|D zzXJaIeIq-Ohfs&CQxDk4@nBr*LfhU@l0%5~5*Zu8u|j^0B1W>J-Ljq6x>;l6`O}Cm z|31L@fIj50K)lY4$Xiho^NSepM-O}};N^{-2>aeIh}VQK{QY-U8b>c`*NU7#Jppz> z@A`K)E7x$PKAU-;vCCq|l=Ch^%hGKtw?OVb8SA9-B#Yl=aUEa|0e`-~=bwj(zF|KKudl`T*P%@htKib|tbx_2#k7&P@?9tTk zX}hvNH`_k_Hq^H}ZA+J&Aj?k00mNRqVfh&VCJeVyB7x$$FdVb)SN*2)62L6}-({$(q>TYp2iP zACC$jl^hm`vB|JDb~}xcIdVeA#+$g;i*|jtI9R|ZrJr-IYzK1ubBG75RR3olWWggq z(?_glE#6#LpQ`4d#F?iKtOifNISPA%wgVK#Ysog%zRQ^>@U@Zd`K5J->9g2F(G=7y zh7Q7UAZ{A6kOW3*QbjjZnPn##F^Jn{X3heL|bf!{$!cCfQm z#}UfaTqB_$nKj7cTruw#WkmG1MxJG^9)b7p;q{@%nwGG(PzqdEuQH?>`(u|!rUX=FN7O) zMEU`!pFxgbzWIkyWB6~-LH`K+N8XyW8VBmQt>Spg*Pz|MhkU8U+!s35iSUFS?E2{* zH{+-fPkAnV>RdnU-+FNn>nHv^k@Hh&=zWmMoc$Z1^I_Cy{}<&s_Sc|Yp&(-j-%tL` zEgYHWgs_M0$?CfrO|$s$MN`2=t)squ^J!nqK)$(OlC8qd{z{3dfvjsZ`fV6MfGId&d= zbl;$S73#`Y@ynsMpPBn9^2q(sZw@jJ05zJJ+fQoj5M~?)@2P&RzQ894*Kye|u{(Oq z7kr?(&m%|lOMc(b`=$$?_4IoP@|Pn|UyL-4dxTf`wua#cQM9q1wwm=U>l}oQl1Q-qoBQ(rkA(F=HP7yd}$S@H*w-^X4+?k~q0)&ri*d!Q34yBqhEmVc_-^PF%{&IM^1 z6g=S~#wApnW2N7aTjpajmilD?$_=t-OLKrRo(+2yal<-RRnH-cdxdk1*EG{l7+Sc( z2zo|7%3P12r)MaB?#(?3y10&TF7R@|UD??zUas+sFyJ%%4f_Fjo_rDGzZ*4f&J7x) z2cquohZtwa7`95VZ1g_kO(A!F7P`W>vG|gKoWs^II=L#@NX5 zkrplGIbv`8Hum`m-}5GHiQ6*AexqZU>h~~@qlE8BeGkGPa=?{SJ&tf!ypMX9rbX^? zU-&QMHMwtK*Bb;+|Av#jn|5BTy!ehN8-8PqjRix{Y?d20a@-$~B^`gZqxW0`Sk$ z7CKd9hM)CY#7E1%Uyr;G8;#VpQ}V2lIi&0Gk? zAHfMZXUZ76sTdFZsNdL2oxM~x2)DSep5vH_huGi$g6!^?$1%_q+aB|K)J9(#;I9g2 zVAq#=U&I&WCqA!IzRz;Fg^{}pxN$9UfqCTm&3J_~MawqDSf6mlRXg4~epWF-klj|7 z_Yb-GE9{%J(pkr$Z7?nJ6V5ci7CPyA=fU~WZ6FhT9pm{C`{(-9n{&T>9#i!l`C$K# z`>NYmwQ#`5e~-C>KDOCEJpE?s;?LuA_q=8Q&Rie|fJXR@b(4nGRL!eWKcK^f+EkAnJIJZu@ODGHByEzQh@k z)(9U1e?zR7Q=jGcz}MbE8yw#gz8+I{H{kfItkqclJimv$qX!dLYvc*7fX5c?HZmua zww>P;wCiP|D;49W6#VsAk%#=m(l+MOx1ygY>>Yhq&=^woFsw;`pD}tIbu8F1kENf& z51oIeLnh=IUd(kb>Hy4fkj$6+l$tqD#`QoRA0saXPP@Q;iBY1>1+b5!=FoSYbo^bm zcxeG-P^q7XXQ^L`T;s?yE8r2I71@|}GK)s7M(vW1xPd`n^N}u$;cVJ7s9zJ7dJH^o zA-Zw)OsBT!k48-LnxZ^b*SM9fk88Qu^Gb<7aIts&hk4&VmTIy9$B3ArgVNc^UN{nk)}nJ+mJJnq!&up@uUwcZ>#ftc0mCA>Mz83&G#rP zLT0|d6|+Q)(RH==H}PI_@DQKjW4V)OE}h2Hk9=lm^Ky=vc4j??D*IctJ@I$p`D}-@ z1~lMeH~CSROAd7Bhp?T#EWSs^j}Sj)4iozsRx-taF@Ctyd`REGr!!U@T7|Jz#(&B@ ztodCIuYei<0Dag+x>TtReV?>S{DjyaH?ZcT(|Bj|ypAuIf)QXi1P&ZKE}MhjDGl zlf_2>Kk67&NqlGk?FVR)<@Ug+6@P=N?r10`G~PzMlRWGniEKWNf-_*XhdK zrVpp;q#`%IqshaNYcy3`2E83>}q%W>g4M;m|SeqC;b-Bj-#{7wb$AUlU+wJW; zeP+E$@?6R%_03V{N;@w5>PJj#dHemo?7!G{ZCypft#DOz(~{AcLz;CMX4OB|ROKGC zc#D;5B{_s#ly93=6D^wzW5poTt}W^S!^#hUc&J~LJzCdSo<-wcE6V+X_lz|ZT*#*I z+i{h2e0GL-tAh}8lM$XzzBNIHaDgMVXJy2nhg=7+c^qG>o(Andh(o_Z%O;vOHh_=t z&1Dpwga2~GE$@U3a0y{#)jD-C#{!-z+@_q5{+{h!kl(_-XsmssXiFg$(*B;C&(V$4 zjfEEHuBnvc$lm8n_{_6nQe&=o2AJ@t?|&wYf3jmluJhxCPje55{TH_N*kouQ@aHk| zO~F~(ladx)$w2PT{dj5F-n7qw`5wTdFD&ZWLObfjKO4_Nz7{ig<{tC&RpXnNZub=W z%`l!d%f5>-!)|cH{*3T^&Y$!TXV!i~I%}U}wa+}O6gJbKRpn=R?0r=Jtm6%q7`X|# z-5BR8GOmoVPME6oq>LdnfVPuj%zU*uwexR?*DBFbx%N2UcS$>sxtU|**?Y*jY3+NW zoXfq7*q4UrU(gdfROqMJ$21>>oN?{Ig4EX4#?D+s0dM*avB@jHgt8O3->0&#sVlGz z(2?6w$9?B&eeyTp+LvLQf0g^1{x9jTUK}ay8~*rv{YJE*SW(6rb04~&x1AnHyd=(b z$MR+Q52S8MDl2oWcYgLhhLk?Wbw+)|U%QQquC)$R{X6vgBQ0L9Xq(XN)TFm#M#(r(zZ-fJ6fV$5F(uN0HSxW5KSQ`q-@`RDS!v%_I;uA=`^!(p$tCKv6sW$NwvyZst4 zU;hB)5T!qGUX9QJwU^DzAMNe(Gxq#XNgLh!1Iy>Dze~Ox$`jCYHh^b-ro69q{tjL6 zLxS&0&QkGT{0_NqTF$~xC#)ndt+qQ6k67abe$X!GdRCRjs?Sjf4(7(OFMdS%qSEvF ze#A(fM!dIdeD&`4k~4vjWzMK2Z)1MDU~KntoMIEBtWq!DD?BLnEqHKhCI6+KD}9a7 zflegffsA#lzF&TY4%J|uP#G83P_0AR8b%N=4H@sn25o_5YeSpNNS^0qP!DTD>_PdQ z+UYxVy?5NaY^xZmmzThkM3x!KY?q96YQms#=TYGb=4iCpkT~=%qny0zF3QyL024bn7 zgfFFP-`!oWF2eZEM&5!X@sjUy+&E*lztfnhJ8*8qQzeCuQc5(f=7oab#tR*kD>wKCqu0CJ9hxAbU zxtL#rc2VZwuhl;F`hF$%r*H2;PNiDU^Y_qO))=Uj^#Iqw+J|-EKJ(c~ydZS^bs=x| z;A(yPyOM8%ed8kXJeebH3ytu5k@aaS;kY3~3?Wvrsy|G{_8PGhnDatp9ElZ%{B8N` zv(Hu9cg$Z|q{$QTk8`N{N4T=jK8M&w(3M90feLnPTYR|jORK@Y-F^#pjqXg^XW4hv zKH_?I&OObW2YkPr`$h8`$^ghyS2O-8=o#(_8zbLxyVlIl2!8)ukp{cQMbL;*wWncE zgq)E&3I2kTJk9no&@P2@+h@_PAZy+zFD0HK7N&4UD4Px$Cs{gRGA0&a^<+BuG4=+tgQ{4dD8UeBm1YWw@U>Pq#Ny zu2{q8E!{`$C9xC7kDV#wZ{;{y^NRe2v81E{l*#co&RS{B>Nh-p_1obSd#Olg+}OAH zjKuDdWt+v9ICFODS2~(k;}SobjX@Zb{$VGZETEAnP(#v=Tf*+vd@C z_Hfwm9_Ab=!&iwJgin1O@jAKGQimLJj8y=ieazagXs6mZoqy9ZK){Gj$b*h`_`R%y z^SciH0cRxd5Zb-05)9h)KIrH#iSNSSL_Y!_xvO2rJ00WdH#0ZcWlr)6`;{SgYDWwZ^G` zmvuliO#TF!Dao_$lNk%^ zdc)Tyd)-|upRz9zH`F?zPJ4M=yL~*+!*Kw<8BbiPZ=Bf_Umoa$w6D$^)apa` zRelA{iK%uJ-SK8lujWe(`LZ7eO}m|V-&o(^&5#oyuWWGQ&3eu_oBVC$rGpG$KTA;& zSIg&w?@~Jq{S4C&q1)v()U{%Bz*yM=E5rn-^-y%xo4c*rIPLAW(`V?5Kc}rjctTX= zDGlLlM>~zNoyNj5q}3;KC2=ZS(Vhnb<>QKB*0Dqs@TJsM5XQ z3%-5XyvA#7l=nGR-1D}e<nr%%>g zgG~Mk+o^G$Me$t=_mYg=tJF`QH?b9X?ggYzY}Y!U(;&YWS-+6;&rA(g^Cj|31nA)J zC)c<1NqlD918q{3=4!tW8_2n`9vYwPzQWf<{c8+;PPAXb4)!PFiEW+Knkf6YH#y^M zeS{r-Lx^LYVDAUrt+>DJFrUkxS-VNg0Hm9Oo5}Uv##3)`0~h9<0FQizJg0(ONj|3d z3;SXFpcS`QiaDg5iX7!X)qa2Bg^lXN#2xYH5vL#*=isM1v65a(A3kW(>3B}^do~=C zBlDF$(>cUge9sLml`qjupE*hVS8@a444>{9<^3|pF<_dcy@#-&u0h-feLBDV*_rYk zw64V7ul$|f%*8Wh{L?*7(8=4l2M9Or{g7w$G}c9NGcMG6`-j+A9C1|fYi(PHeeZGn zRm?LLG^2Z=PRw&0%jA4{if5v4sTZhnU81H#U{T&NFXz&EPrU(ohmS_A=%~;?eJ0Ow zP71$lva{_wpBI}3c|L54S5(RcS`OQ=5&IO@;nAWmqi()$8wcw?OD+=7+6iZqP@CaY zt{H70gf;4IUtT_*nerVmnGvp;mJccSV;xg_qJGg#nZtCCv(K}khdyt_M_qxsH~;N+ zJjf+~zTkd&yo=(y%C11Y8T%S>G^sCFYInewLO%V>C6o{NM?04MHG%_zvzq20*69`K z!r@ANsLcXbM$zsUFn;QY~6l2f}zJ;&-FuHUi?XZ^Z_$FXU_Cj zTeDXF7DSyy>W|j>a%;~jo~`VzToc&t9zp%giaEc;wmx>3JgaiDs5PvO;}iF}=&=$5 zPrCtp-9Kf$5GNS)FU)=nczQc=PWXoM;WvVJcn7jRXov9p%)PK5Z0FQx`8~uGABUWY z8B12M=3TGM;RJ`$d$jSf58%l?_iACKcA6aFSw)qaB@am?PoJN2%EqcC%##{!`gs|qlwG|2Pp;y)tnV+xX&h! z;D5g(y;6Hb)kAIs@5x&^UpWg4y)(i5A5MLi-($Ts?AMD(cZ6<|=4skl%Cj4Thi*g3 zV2ZW~Tyj?+rs&<}<7wnObg45VX7bTS-fi|ffaMLgJ*f0dC`y0;x-$33c_I2S=_2YoZ$+{iHE&ZbBtrLAb><{&Uj|6`n7TTohkCWdvfOGOr zfzC1ihl@HfJMNhO5Ai2vM=bhi_20-CYS9X|8C7-&@i`{ob9DA|R$Hf9zXiRoy<2EY zW~kP)t@@qvW51D&b00CU0`0}z4~QGzqg8!&+7$=@pI+tq6W;uTf9rSM^P^R5E$|Tp zp>wa{dt%#fMCKKo;hIZaK;~ZP9J@KK3-o{5w5?iwJcm!*X%y#Ve_1;ITAulH>a+Zw zziYTJU?lG4SR3~z*24M1?+OPjyj%l4XF}X2`66Q}%g0&ocbo?>A@{^cWBgTox%IYN z&-36nH*-BC&MYc+S2I21aud`u=XQ7&wxnM!h3qH$EWO-KU5$_@jyl;Jg+2=o)~V|Y zUjsfXSs(s{(U3QHlvC^;&+FnGU)j05M)7_aI=<9Mqr3z8=6s&@ueA=$JI=g_lodqB zpA79O;I~D?CVW@2zNW>%iz}SYZNgTAI(Ny-s@Y^}%}f8j9rCsNy|G7y9wLr%;Tl8# zHWgRe_NaSFe1zmwg6_Avdz<`!PDTH7wGY2B{JDJ@Ct_q?Mvb&UEjX~2EOO6YBfMJ4 z-VN3Ap?)Xy2DwSR=^s0t&tvQ|_P*_0Uc-{;~={&YKR{96pa1NgX> zbegy%IOS$f7oJfv0(mHT8)(2^>3g>K!OzV1#W;OF$9o}vzq#{(=(0U zgI;qS+omq3d@61o}e)e*AB` znJaUWK_*e>6vU6uHXGJA9B|z}MhzQ@GlG0^z27(VzVdPM>@dzrpZA5WsDI>Mb9;WP zyq#*_5ofm#X)ft4bsYGEUUC!bSgp-kzaj46t0HfR&y@W}jPz@a{jK$G=l7|B0M67M zOq$HS(EvVfWDa5_bE9@2e1!k&q|=3vHw!lZNBNxeqj02A+{b(3&r^Denfha&v96{+ zk2(lw#GlFQ#WtY!WDWea<8r;9?PnLWV?l53B&QXHjpA#B32`4Z_;x27iPL-a-eb;Z z%1N*@syTDb{oaJ*nCflY>G>Y-=)2U}Emf;&NS$kDymY{fe!%JZ&b_IJ0A#Z-Qzm5G zZGE^e{F3i7FEjM#bN75}e!`ip#hzE*8Zt8~|HtHUj(wKs_)paGUlO9~e7QG$1oQ%x z+ne-C_JI~3<-O&ri`okxgf4s=Vfb0tXTg`Rnpai}to)tHe5Wq~G#Glg z(0STs;oC({?accp({81Ap=Ufq*-Y;%(wmSO|4F6iJNKs+0*vWVYf=~P3(k;-lJ}yG z2m31MkzsG<^Yl51&N-Q!-okgeUnxI;)~rFh70)Bup@=yB{g5l91gb$M9tonw<6Wl~cEeujU|n|Im2>9IBA8Z=^u zbFJ+CVBJ>jJcH_wGy@Lp+6HRE9z)vfp6?UyCD*v%U=I3r=6I((1FYo<`tUfPE8<+k z@q#L5mt!T(!Fk^kc3yOE%^Ehy0n`T_Uwn#+2hpC{Dt@nZ8Sy&`TvNw)Z?kHGZwqk< zAAv1g=|PBh(7w?Jmu{D@n76dqdx&}%WijE)l5-UFbkb)G^f)8l;T_pN={5VHPlexu zo{^90XFAvEgFwG8{Vm3U==G^>dy9gO7yWtQ>u2oY5IZD(tJo>b$x9p%+TSSNi$9P1 zM&Xku{TKg&iFUJcC`#?zgsLG#xr25B%&$>91K)M=Hw0LY$vC~aC;diZLXH&WXEbxm zzyrqxHo87{_VHS^2+QX<+3!I!vjfLq`8o@=bwcLw|4g=Rz7HGABWz#j%dnDfaAxeZ zX0+XDV{#B!Vq_0c@}}s(HSj|CC2dm*2aVM4nOo0k1Oa1sZ`hUAX?>#cvq6HZ)KQfT zZDwAAj_N9A9lizR0!a@k-vWHhdjGk@VK;a_;Tgy#35$or24ZHu>jaxV@9;-}YwMJ) zQt`xLHMapXcQk!%{Yt*tpbpy?i&= zARggZWgj;bv!uu1@+$hQqETil6EIS{mXC|y;mh|!r&edB#oxv_2P;>nk@&6CJ5zlx zV2ti6vQko=R3%sa-2Q_lz9srq9@zSa=GRbycrmQHdbW#wA4XI^Z?vi+W8<)39^ zc8R|gX;6*#Wqtx5@JTaup5GTU%f~7{_S|(vc}U3$m}j9LAAEf6^AGv9enR?5ejsDU zxhtH3s%L~ep9C5I5yD2~uBciU$T;`a(@y8v=0l(#{&)D8bzGkV?#sTWEX(!7n%y4p z#U{k3>5=uCg8|_FYVMWEp9!?`f1o|s#J{BbsCJF`GW&q9j!OSiqvLZ=lQ&v=$@4rH z=`_ZGC$%47qwq*zqvRn4bD?kP<#?}ca&U1DJ0Z^ka)Yen`7p?GPb~4~>yA10Besk4 zBORyRN96v@xKr%66WluYS_1**)j+JvJ;W8sv1sRgiZ5Z!C3X+jT+loG)oLTb{`e8_ zRM81bzjmM}_CB0Gvu0mP=iW-8-{YKoh~?uzO##rI+{5HmllLh)jmz<#iZ2$rm`)%U z@iE&W*LeBZAdh?)`D^YK`cRS!5%3Y2ANiTDb~@u%-qv({N50)7a313-r?|s`tx{Vr zMgF5&HOYW2=F{B>GDKfpZ+|_gGIguygIvQvsWj??&;A5 zjFD{-NAowXEB#!J+4hUM#;A(~J;`&N@_*<)_0^Bux1c-fjG)MuQ{m4O+F0;Gz{bEc zOL|Q9YZ!GoAv0x##+Hmt(GJoC)VH|7{?1JL95EA54WLFuam@Km5Zkj9dC8U(@5v|n zXHmyz1J0bSZgxCTd0(CH)ggemypy>12kiqKbgm*&I(l;U)g=JgD4*6O(7R* z+%Xp_>nuPId4_ONHty1MOe$uHv^VUDw}q|G4qG>$_gt7gAY#T&4tnA}*h^mxd6LHf zuYX3nEOiCM@;K%mXRZS9n;qTs(Rrpd5a9VX;nfm%DukXT6By zxq$my>va?5h-!2|>lb6<$45RdoDDj(TA%ti$f93^UZBq9+Vto$JgwGuL%%6qrXar> zsY@5nkG@@xNncqHC}fVEM?1Ip97=dT$Ea|ha0Bf)Q@=M;u8c?MGe|6uMeMYk>RO#}~h-e17fzopIH{g=@AU4eav__k2*D z7fL^(Xc^`!{=r;+(Y$-#gnDOx4jHkVWKQ|EvT3$EoLf1*h%b97_c3&I;I72%sxz42 zS%@=TSgUX33;HbR$C&8a!o%J3eLg>|Y(S(xz@rO#%-4Oj`UIcc0rch;;ywE<(^*=H z{|aB(Ct(EGo$EB#r8pY{9sgR6uU~B3j<~OFeHj0YtI4%9aT>9fSo_0N{bb~Wh?vKnP+Fa=uR_lZOh^))K+rrrP5p(Qr@(U-pb?!|q1b}ZUwwL>m`Jc;o|pUBmubeM&8o%x~GB==cdIylMgd>+vFI5c&=n z$vc>%%4xiaU)dY;S;_IyZ{fi6c~8mwlnW3G`G2iE!|OE0X56C>2DMvnMqZ*rF;B=j z;^&b(Ey+jW%i9HUpx1Cu$r{Ix@uTJ`Yyyw&hbY+6 z{jq1(mq9%{RXlcpqo|DG%}bGk<1s=I(-; zptndK5NfvaoKj%;`buj0gT)AKAlU4-%k$$;Nf)_LUv=$ z?yIF%G-|>h4cVpkbJ1(P$X~|bN-;#}ik_ayyja+l4^g)8e{*tn?Y+YCN8-8Q>z%RC z$i+@G!8`}jNV#YEPnCW%S>D_CJfL`Abggtp%H>t@wwb{Q18;U_EgMBUB~b6 zyB_i!l)bOyzL{!!xzG6mPp^T$Z!7eei|sucdgL~q^(ddu-Xq2jLte&4t^=+e?tiFb zR@JXueh>Y(e+aN%f30_m2Gf_7$+#-KFMMy;_=J2!J#ptcmKAi&LH@v(5llMI%`x^5zF@JwoGJ@EIQoTz8 z!{^Z^B3|OVDZJ9N0QC)wp+7)xc)J~A9<4Jg&7q_(RJ=`m{7M#2A@@aoQorMW#=d(4 z$>Xb0$v80d$9w1>;yP)tCO|bgT=BU@GoCFp7yq~{kpH+7&8jvadJu4ZOH6OD;$BX2 zKY?>?c@-~&-E^bVeOvt+b$jS!?_LN$G3Ev}KPUfN8p<9WH8PK$+P!^VG&p=2Y6PkM z8#NaTjM0$)6=uQgUCWH@nK5Iq59gk~23%gy{687XR(l6ox1RRu)I4eH&3P-vTiA}A zw~MNcWkqoOQ0Nsm!!}T|f$=csIJpyF`EgBRkjfqpW_BPiZMhT8?B@*{*OHI9KUsFZ zN8T?eJ;7^DBB$ri)U~LYJ{Nk>i=q=@&%-=XFY68c_e2Mp=Ai$`d(i8D+>3s3{0;H4 zWk2g49M$!}hn5+smHnuHg7d;!_jdrXl`b(JSe^I9pG{quzF(Ymy_Nkks*QvB;8-6A zl&v^nKQ~X;SFs4lCFU(57v=WwyseJs^q9K~-rYwWxSp{;dF*RgitpnLvZwP4)i7($ z=i_r=|9lJk-J9I&L?2@;Q_fKJD_M~Ip%X1}qd>pSN$4MUHhRZxz5wCEl!y68fryuM19h+vyRxkC~ju&WlbQS|C6(T$*ZH?Lp8;l z+@7PaPagu5`@;Jw=2mI%tM`@UKWye-+xlP=bMn6B>$f1KDEXjY3~KZ;uD(05?aFPj0{0udRzfB#`_Gc^~UxLg$n9 zAhHu{#j@4&`(dwNUTq}Q^I~Rp74*p>*H9^Ew^3Q{`S(hGQJyJzY^LsYi+v)m$%K#Y zxe_1Pvd`iKt@0JM?5+Z>!(#7J=GSVI2=9|;VyNd=dH+^; z9}A^UM7|Dm@$7EWscduQ)8PIl`#a~E*X6U^wuqTIe=2$33ggl=G>$;j9JXrwm&f-h z2a=`rVCEini;b02z*>-~|8u9U>zDKguX42E?$5R-j+gF#QP7XN&)%q63P~%Q^sjulUEvyaa!}V;)>3b?_mrVXtP< z^{GeHDo5RT%jb<%%6N~xZu-o^2Iz<%tww1-{Q9WzyMgy0-&u0B zPJXias+L!gWz=~u+bAC9#^x;4k*ob}MOVQ)TtJYkowg&X)?T3J>O=V=%d3wrf zt{cnO`y|d~o#bUiIM1~LdgCsMbE|lP%~MT!O^hgf2k?vV{LCqS(53Iu{s=_TcZqtS@P8g> z^3$HrrG$ftOQCBjo1x@GuH=zvd#_|ReeL3}A9>rYk0$#_z_z&>HG~H^R-v^Kna?p% z=NJ8%y{r6M35=!20)-9-x<8x0?K1h5I zI??N}A)fSx%==(PBqfZrmhJm~vj1|%aw_Lezt;#5fkn`f z!5eSIjr_Zm4PH@U-U$54JE?nGGQUAys%5|6x)+^W+4)B1xu|*ISU1|)GXQ(T80Dtu z#*NB+oAiaC>$w*LgmY`xEJ;UN7~HR3CC{zmoG6)aU7EF;(X0!xF5%MkafDK8ak@9U6J8Q~|zw@u}J=A$V)#ZdM4T;m+nwqo3k#~)?yp`1nUF5hZZE(_(?a$HNwV)5KtDfd|ifqN~VN!|y)?}0w^ z2y*CuL?0l0|3`J^3e}^UP6W`S>3y}_KWt`K(@vnx3x4u=?zI-#?)qKqF+&sjZOk0G z7rfybi-b&GO}nHd^XrZ0SJE@6YFc9dW8b<@*MeQO?~;$n*YhUE!sEO0{R{&ou(IA^?5=?&j!>5vom`Hbsi%;m^V_9n@~iBhr~6^lkEs4-;|NF}z|1DwSwDQ-`=}q#H$dND<2HUdO=9mWT|AUp>_&&at`X=o z1ds>*K#BML)TPV$tNd@u{Iufxm{}aa-&X)rmzdu8eCWx)W1NhhWj<+z^PGg*&M8vj=Enxv}N^?fUnGe~*0;TJ&{|K&K&q`b_JZXYb3o8O8U(9O?w8r;Mo~zy1b^LHHTd zo4haatg}VWF1NEooi^usJQ@Ka5HVAqS9vwaGnr<&uk!b3d*;Yfi@OIr zm88yQJX%Gp0$oWKRRWu^*A&F-;RQo#;NIyvaoDSZ%?R~`ev#3{pYBgH)=Nf@dLI7uIo}kT3+3Y`m z@t%o^oHpq2>!aP7nsQdz8!{fuC0M`4Um9h+V(%CB>9g>iw5s)3ydZ#FR%P>BdbN+f zbm4pEu=8vSe0#6f+wTFit@K~_FX#Q-ThxKP;HAH#5$F^I&O35AfLhm(t4go-k@pX% z`g`I_D~_OM#TwQLsTa3CBY%(RC$=ro_$$xaSb4>->O9+eXGZ_7oe}6mO<4N-8T0Z< z?^k;Wee}F9dVRdO*h8=HsW4n|!uSWF`|p-|+>P@+$I0*q;)%4|1D6JL|^` z>3g&z0&%nWs^#x*Q1;V~mh-vH8;Y38J6KQDFHEAw;3c#ptn){W(5iG3%Kf;Jd!y&@ zcMkW9MtvWRKszCTI9xw%-^O{0{WS`f%rCr#v+5DQap*Tke?VHTGf4IFzv6w`B#1w3 zTG(~R2mGc}-%lgZE(qXU-7cwxS8qKmOV?L6bb;q%rmeRvdsv8`Qm^hWetwJhpNMvC z*ER0jWua<1eMbPfwik))S7&=_l&2EYqc4s;3Ete;Z{|PYJCS8KL~g?C)?QU3t!lr3 z+#gc*emq!s)=#^nj70sXMxbpFKuxLF>a+XV_cyC_{W$B;8Og6SW6-FFyTgi4*2@1A zypMfqi;nR%W481&S{VW6+A}tXJl9%yN%0Y7F!v3}<2#oa|7ptEm{nclbK}8cf;mJr z#wS7Zu#)@Bf`zAhx}JW2E68#c1cjneRov#C$kZO zXfU;Y>hrVo?`2f&8|VqU(PQF^GmR7Nf9U@n?0=~pD0WKmp_!&zKVKuzN(iv;Blvlb z#L*-kUgl3zIx~HHj0?ive{x0F)(^G<{gV4%9G=?$ml$JQg7N*n(CD45K)0qrBhX3+ z^al1Hj~H0-U^|-mdwHwxT>j;_Q9P@a=%)NfykU>~*&pyeVj1eJ(G)!bT4_w07L7o& z5eOTDS5D5MXFtiL;(PJ&rN7r~n!G*_@&6a3=HDbAKWzV!+z*?n?|8kR{+>o)wjmHR zbGJ*}k37Wsdt;*Oo0*4Oz1AK4e}9pDrE>lkd!KbhaOPm`Y@1I#UX6fv1Xj&OuHIJ0 z{iy$|Ja{9QLcPzG-tRGU-|P#H{=Bqzsr?H&e_qA+ml~;CW{xubWQ{=UBY-pR^WtW9 zz&if|d%t*Vc;0w->r*ZiMuW-EQU1$W#l#r%k?5y{-T(b|8H1)%BQR44goA@;EJxq& zsFC?M^njk3qNCpDE;R=}%etTJ4MyIlA0yJ8zP#T1>ECGtW)lLa?|D_!NUdAgwWTf3 zyfbeR^}hzm_u{L}ET2vDr^l)hnC%Eejr3;8?MeAkQ*5^5smHGoXeR{XW_op>nZB}} zXx6l91T+E~0gZr0KqH_L&IE>?#O|lz@bwpi%%TdfYcefZ$A7x&H57n)I)8V$&EQ2>Rd0C0bV0stKh0$AjP z0LVNGorCeOaeIny2>iYCjKJIfocCyq&i}{tf4X2Mp#Q}5adKe(o2;a#&uru8?f(~} z?}@(`Y=8hD0Vn`kfC*p)xBz}Y1ds$|fro%HpaJLr`hW>w0oVYJfE(Zq1OOqx8z2&h z0X_gJKn9Qv6hjZg1mh)!1BN?>A4Uj9I7SRc5=I8bH;h7zGK@NmHjF-uQH)uP zRg4{sV~i_I5GD~O1tudV2c{sV6y_sLO-y~v7nly1UYJ3cZ!zOB(=oqe{=}@tY{MMD zoWxwg+`~Kr0U$yU73cwoA0!1*0_lKELG~bT5CrrdlnVL|Dh1VpdO+i#70^ED5(@{5 z9E%x?A4?WX4a)$_2FnWzf)$IEf%OBc8mkj)9BUN|j)lS|#%93g#g@TV$2P`xzz)EU z#7@P|$F9Qe!k)z5#6HKt!J)$8#F4~N!!f~e#tFuW#rc8*!)e7C#aYKW!^Op=#pT77 z$JN2L!u7?C#QlW(6SoC-40jV3iARVB#uLF)!865k#|y(t#Vf*V#v8-i#=FM9kI#-T zgRg^ci~kBg0Y4YN9)ARX6aSiklz@{!o|>4|*^2UiH1Pdxs?WBOSOt=KZ?+ z)A!Fv$w`Gtbx7SvV@Zoh`$+$g;gWHXDU;cfy(P;b>mXYv2a&UqE0Nofhm+@$cad*W z;81WF4AGq@zCkg1<+;Dbi&LGZW&IK-FE=4XcuJ2qE z+&J7)+%DW-xPS9t@`&>|@?`Ri@M7{x@H+8+;T`3}=9A`g=ljk#%}>Pth~J;Tn15A( zO5mwLm_Uudp&*-}sbHdDk044&T*y@@S7=_COjtuWOt@b7ScFH!MkG^YLX=okMHDJp zD~b@~6|)n|5}Oex7uOPx5^op3m5`M1mME3jkz|v6DfwA)T8cvInN+M)uQaywV`-># zqx7YWgp99Dxy+F)zpRUFk?bEiPB}ZdT)B057I`cAZ}KY&%nC0RzACIJK2Wq${I0n6 zkoBSM!~BQakGLN>Kl=IT@UhTi@5fb-FO+1ILX}#dfS#y6dH-ZsnM~P0IYW6#g+;|d zrC0^7Dy|x=+Ny@Frm2>wHm%O6ZlhkP4%d*>2-WD+B+`7YnW4G%l;^4U(|RpTEe)+? ztp#m%ZFlV&9Y9B2Cs}9l8Rs*vXAQbIy3ceob+`0{^+NP|o|8Yfd|veYT>r6tg8sY# zmw}%_yCI38xnZH8$LE}*40TVhCCzD!JTvJ2SeA6>CWwR8sO>=ScNb{)| zoG$`j^jXkZxLPz>l2}?3p}+lRIvZFlVC z?UL-a?d9#0?Eg5(J0v^oI4U}(I_^6uIb}E@oYkDOoiAK;Tnb$7T#Z~|Zg_4l-Rj)Q z++Ex|J(xTKJw`owJtI9=ykxvmyx`uN-uXTlJ}-P~eJRjnYS53%FT!utU%@}q9~od6 zP!UKP=pH!uisx1ItL-3_puAwLV4L9fkOv`eLRLc`g?@uzK&&C{P*&($=;mwH*FWA6 zym5Im6ebjw6m}YJ6kh+9@$Kuk>k+CE#gX?Sy(6ciWTUd*VZC#DHykY*{V5vt-u8We zj8IH!%uTFKY=4|^TzcGHynXy|fS?AEpu&6MrO;CIuy}Cu=2Fe`Nmn{^Mzi zWlCSFSZa0}L7HFMO8V3E+D~ksK76{#aLky@e4GjU%)Et}~|D3H{!`z-csl38`y8M{@+XDB3l^=RPItwKW3yT3;5;tYq#uW*;Kh^d3%LaMQJ5_<>xB$s`u3x)vv1I zHI6kawI;QrbsBZ;^|JL<4FU}XjSm_#n<$$SnhBcUHs7@bwVbtjw(hq%w5_*Ww$FDM zcT9BZb`E!GcJ+3vba(VT>S^hf?``ap>8tCP>aQ7)7^of;AFLV@AF3Lb7_J_X9I5>+ z{kwitZnSw!ajb3p$$0mK`ozGb_T=c4!PLyO`Si++?aZH9*IC4z|J?Qb>jmtE_lx%z zQNWMZA@|4c;#NBl4$lM`h>tuG#M9 zp6A~6e&hk^!RJG+!^)$FM}u%<_$I;|ad#YhLVHqhDt6j__Uvrw-0l4O;yscU`QuXR zvggX^YWq6i2KOfYmix9IrHNX&bGy5{n+KEtObm>_-~TQkY|vl9!NLMzi;t z!2)2CVt`07?m7W5n$uvT=>uB+a+SY=feFIG#zC7zK!{eTx{o#=1j0lci;acmGZ>-h zd;p6So9uz`BOG#FD_j;Y3XwO7-|$!;m$gyqjl$VPt-ZtW38<)PXzAEFIJvla#Ka{e zrKDw)o+zuRs;O%{*EcXULK7_;TRVFPM<-_=UqAnVz*j-xZzCe3-bKGpO8%IVnwI`4 z<9kkSUVg!k!lLqu%Bt#`+PeDoj?S*`p5DIxvGIw?sp*;7xz)Axjm@p?KRdgKEMN%04jR)R=hWxQV-{Mw0|(7pnekh+UP>vt))&RRL7(M*c1 z7lmU)=XuIfJ9=y(@s)IDkHr$atP=Wj_1m*1Hp6k@R};;K%iDUhx(RyXdds>x`rAu+ z`n^5J@=kkH3RYJW8OCop!7%O0e3DE?DBZ6Ii+vO#p>w$Xh9Il;CLAXD;Cno+QQJF9 zuRmY^76DQBE2I!59`K_m1MHXkzXzUYZ$A{%6TSn^YV*rOmL{FX<>Tk#*0+kb^rE%Z z?tpsHv!cT~sMj1!BU4BBtmp__v#tN><9CC=sEGs(m>4}2)?|5{02}+BBz&MHO8ca* zHpXeBaK|7^@dO1Oq0I5lRQ*v@u2eJMWa=RI{dv#2?qFtwap1s&w*YmEX^sfqgkQ0c zYT|Da(|8(qu=_!m>3g{`9o*2u^qV(YnI_^%ArRylMmED23fy>|e*0KYu z^#tDWGS8nD(I1hxJ1yC-&O%GnbDzb2@<3Jba_B#A-W~hC;ZSz#{3_<$&G}(p?EAQn z<=ImuA`as>&Jvbs3@0~5rApDdIrB5Y<7_$~0fopC?Eaedv2Tf5PhW z@08z6mBX+2 zwjF`xDe+mZzt0_HCl@i8Bk|ueSdu$-d2Ad`P$Cw%v-G1~_dx`{hiMhr%j*gMz3}lo z(|83)B{hXQHLqbwPTT2~{<9pC-B9O2;Us6;N^4C9XI%N+z+_fW!R*BjUM0w|k%!~% zf`S4Ms;a7D2gJsu)K(vgn54r2h7CCHW_DZ)-0%*-3Q?Te9t+|35R8T8^j8fPlUm0z za5GsKQv-5_>WPv$CzH!`m=25}i;3_7ik<)7i2APkzG`;i53(1y@acd3|>u+a6 z3$6tBe%~gr!pA7q>FO%nsvyeWElQkGh8C_@;cEdm6nkrm33_U7XM~nSIn-}*%fgIp z;~2iS+6Z4FJ~O^~&6bc!xs`;P%%-UN)B&aoY_}%We8g1aSx#dfUiVShs?`opQUO>` zEhT)IC9C-(ccPTJsv=%w$*${z{(HlPfM2nzIGfBBDr&rmNfI@Af#B6Aa|_KH^Ht3S z9@KLs%yFb?y27#QY=SNKd>(XWbN7KZh{^g+sw#+{E^bR+WPg8o%+MVBifi<}MKmfv z=u9x-4j_flBO~23H4!AyVkJz6T4|~07Xd8n3;h9aSS1!hast^lHLCZkg16hiNS?F< ze7Ko@C4xVplMqI>Q=hC%bY)*!eUbW`V$e=a_v1@EX`|@RmBI5o`Utwy`*%P)w&#*v znFIo_NME&dDuU!l5G3uRF=n!iVpP}; zzursb8ZuC_fwH{wbRe@`P(@hDe8=jt#E4(k9<^C!j7bLfx0yXuNrz{LSI50ixXji~ zN9SDVX7iCX=K=_=tP}*f#gv@pidn-mmc&mmci(9&VDJ!S^SHP4@u9U40XNorM4guy z`$%M_x@)o%OdAzdtQU_#f}LdAsc|AFnRQ|4M%GZ8bFs(P$sV?=sbR<+)zfXOA{~cPt%m*pNM6Z(hRlB1>{D z+MrH99&?5nFPa6_upWN)m*>^#h7z1AGR$2DrG9b?I8ITQSX1j(@&DB!$XZ5udg;Jm zlwb1RVl==|@46ZqRd@-zAE^4agp;q?qT90nh^r;Lv*p-BX;q{JRUez+(cYYefbJ{j zE%vpjPYP5X52eQIlYbBYEy&ZcYdoEeOgL9SaC~G->UUvOk-AhSvQE`$iyXZJLXDe*(-kbJ0K}bR?GyPXOtLt8)T%smH^sa zB27ut8~5NlZyq}Ce&QMmx}DmY$>>a!xdS3XDd|pkd$K-$O!GZ#0j(keWspHx!;K8K zHCke*?Lyt(>zDdO4XQgv9MWLzz@p)TmY_e;9V|biQIr-2{fDRrMzlUVkrzeB0xOUz z6%l28l@nypq!T$X^)~T?=y2%n&$6pq=ValJ8VIF?rsdC*wWuS-JQ1vQ_{!wFNa`kz z{#bRE>ndl8`MIyr-q5rvlh^i%xw>K@T!r5;)KEvx3GVVmfxpYH4DI*y0@GOKLm?jz z!Wp^mdmWjp9^fL91jA6+F10HziR6!X(p)_m zW{mn=!#^jb_>GU^6q$Tm!slKzdN2+bmOAT&WO zaYCcG@);t5Oe}QH5<%V;IL8jT?|gSMyhv;+%^K9dS!H7njJrn;B&POZHm9Q}v>UWd z0Y=>=(Lx7d*YMM!-=#7WYjAkY-yDaP4qe0?^r?&>n(;YFT`;CRCqZ(jb;;_4-vQ-m z@2d#IVi+VMSiu1z!UUhshSvajZx_Upz#0KEBY{}OFl<~KHa&1M-+;=4Plv%j`O&Nk z-`^@`53VXd)^WMqojRX|%C}Sv-J;Ug<1{-^K1#$TeReff4>MCdl|*8_3qE^_)UAg} zTSi$BC%>qxFtdMF+HM3D%3(QRBX9hms&rgP^AZo2qjWDw@P@NlZW6{hMI~Dt6J}Rm zs|5bs!MyR|O+5~8>$==)>>!=}WqAF`nocyIFTFhR*=9o=oo17acT%m2pP^?gt-7uK z%If4thL;X>-f?u?3Yl5A?#{?4wwfT8?eJ#$$8npx86I;b?iu=n=Uq&XKD=0e>1kqU zZwPL*NtM;sUUq>??!WngTvpd2?3iP}n0;F4gB80+Z%zPiozl28wkq|Q4lZp^lGa-d#0SmaPCi! z)F9$Gq6c2VQ}IC!JKUf*;bJ$e`})3$+h3Wg5X(0d;pZ=syPza?&7%kB1NNp)6s*Eh z+YeNk5fhjYo9R4l5{Vy0rlo^F?Txg3kJOz*RM9;!@ON=u4#7d3CAlX5{3e9%POJdf%dY0&hYHxy7donIl-3DZOcLGo;V2)jKGXz z@yJJF0XVky1|~|Sp8c}k>aDm)^)`lElWTU!ZoSy4gB+o@#)m8Us0$h&v7FPRDWW4VH`!1D3S%vC-2+C=hsq%~c9WbDI z?cdwm8O|Ad2V|G{r3RfE>E`fqug5>U1LR#0Bkf>Xm_QO?iK2d0a0I4X_28)cX5{z@ z)5C2x{~pQ%Fn-HQUQkQ;O(r8mQAP+8C-JGb&6^P##qId2lVJm+uEISTU(2>{S6Tr! z9>v|GeMBOB;aHT!iMK8>2e_l9}Gx>PGD`#*Ol;?RLv~){Z;i{=r(( z()n^Q;Aw^nP6=j;T90|5tKTE$`co$I#>G`aA&=WfH#7X$O|f;od(&6={zljHIJNVS z`e|#Q)|a?$qYkn-+<5itBg(Y}4D4}#6Ms{?IKB%)- z(6~Fm7D|b9_BrRWnRL4YnrKOD*9#4$&R63?RV;gECD@Pavxd$yHt&GGW^m`tkt94J z+Eaa4EcB<5shj3ud@?#ihdu1+`tjONm4ob)E~79J+;zimmarAx{e&S+QsLH%Tb>Cy z5}!STb=^I~=WmXtlEuO}l~JD!-h!s9h0T@#r1BccyQ4dRtN}M^K_2_V05&P`aQX}XuaaccqT6_v8AqXTlK~%LxsgAo>;fvf)Pu|cA za`%ZazfQDBhL5B0!x`^@ZmAo$^*B97^~&1SDD`oEE0xF@9EVT2Nop6r(k~z7yeN+G zm6lB>DLU1{>FF>HGMh8)aKQrMGpLao{lNjY3Ik)hg?EY!@{C8Lh7K$<9T?oAJ4-d7 z4N<8_lXpPc66BymfoetMGT8KJUjK0NdlO5|WsTn`)nQK6*y%p6Anh|;cNwECqZ@`7 zh&(yQ>wCMsWuf2vh_#1jzF9Y0e@aPr_;INpFAcVEISP*R^9bon)k$f^N{rVIg%*@R zY@uyXoBHV%d)XI{=O#-}4>&swmmNhmSi?%wX>b)~3!eHQx7USg<3p6irqWEoKlO9= z;ghC8$4$YG5C>F$Gx;=1GH_YDg6g?xPx0{1rr47*+<}4P$DqiK#4ID`wH8msm8|Tt z!$df1tN;G*9)*&i@9K-+KpwONI8s0bq_+Fj%{F?8lTm5rMiyJw(A)vh<|JFZIT%|E z_c3eWaf`3O&ogfGk--d}bET6O>2h#Iy2d?f>B)1#Cq5fUqLk=jF^&P|nl#JpdyrIGfR1p0Y#(f5=5p8J?L9nv_B7i;cGH9;=;WFieZOV7Oi zyz0@}FnP^j8Z*t$dYX z{m%!yN%IxRH?@+x)CZyP6V=-a6kcFP_sG+%5xtt|=!(hxSV<~!#x_X@-r?~RR?_f^ z`z{Cv({VqK*|o#nH8*)b`Z$&~&ER5x4^ID%ic(S2e`sr^+h{xqVHM`vaDw|;Nj-yJA0ig z_cbv#XsyYH{+9POGW%LAP-o3&z|YU-oLKi*<(usj(T$WocZpul+JQ)B@>mD)EqfGQ zyVD4UtisqW-L28!(drR>(R1fiH;ui^Z&zI?2}pFd*$wM$9$%x09l5HQ>mZJPvv#P= z^e<#^x2|QxW#nP)Vi!cmDGLjkc&)H>G;Z%f;UpBdc>oRY1av>QcYfaZfGL`U>$=wb z_V!bYJHKMLuqs2^({ism9mp5Ok)WFnp@FhGHywwV@PyKl(S6j@+Yzo2k-@Em>Ve(D zi|V~e$LSpP?60XcEVkE+Cx*{>ZTyc0b}Yo#2wI(<-8%c_3mkXnt)OEScJ7nABQ2jJ>y2N}UacyHsx6Ub_7r)_0k)N%@SDh;kD4tN*O z+yPW7ft05@9fGqJ9WD2!r1^dS%pDlvtm!t}{066=;({Vt&tY>umiL8d{C8Fb4J}O3 z3<`7a4sg$J9NBDUfO|-Gb4?AJcC?)a4Z+|gEeBf}`yr$7?Q;W!QT!}h#*E{+1Guq4 zkBya$vFvs~*4M2L>%LYfDg5GG6%Ko=F(97loP4O**usqTH9%ZW|uJuUY%ae4T>T?*O?2&SS$$ zh{o+a%lN5_i|t&QO6_`{%!yunKbe9->ju*#F{*?ygQ0xAO+^O$N|i*Wyg|It5<$$LlB2rN@rz;l^Dc_}+ICAbc?)mY|K-VajkZf9vIa5z z2G(7bY?Xz<|ugh@phW3)~dHsg?oe17gC_Wv# z100uU9lGk}JXq#;%GkR;_vdq2e-!fSwfbxurpCIZ=hY1{L7Jc!H){A%_sF}L)yqa* z@$;g*O)7p5E>Tzd>;b;l(gq9jMx4&6*F|aOR!xl;p=O~f z=ND2b-^BA@+XWEh#=Z6yCk1lIvc>(|bQIRy!d{IQwA7h7G)Db(YJCIndS3|dnKV$1 z!Ah8Hap)8NvF!dA`B#aX8|go8Ej$YgcomQp6FNzGk@w_X^g23!&Ga_g@SFpPWXoif z5c&~V91@Hy@Vf&jJi@HTd@pm=R*oy{O7r#9*rL-fgUfn&DE!#Sc(U8UbK&b-Z`O^^ zbQl6t7k|#alL=X+=&ZX`?G?^ak5AxR-=G~YX!IfYgP zbdw;B;d3?Vw zl^WjNN_14~#Ar2YKOlG=M%_~q@)Ggv+7?EkJHN0Pm9eF_+L%YLu`P{3%?aa;O~^kL zoHIf{Lwe&@<@tZ!$g(#Q%NmG1h2h)2(!+HiQvw{@Q=ic}u`XynW~Bu0ucP-;X7G_1-(GN+ zUPsX$^%&n=%4Ty6)fdmF_D}hgg{J--@d|Ujqfx#utJ*8Wt}PlR9+VnN0p-1Cz=?mX zn>WN%@~34e+a=JWlL|~%d0m|8;?J?WpdmS5DdOtUkm5-L3ZL*lhYc>zqZt9W;qCXW z`s#7_40%JUTH$$4Nxl8!LG#29-*q8xviKRZK97(t(oaYEWd5XgqOvLxCXJJ&~$Lv+T@ z8!k<7AmnYRpBH5nY1OYGO z@kXYq!fvXr`lp{?YHJL4y5o2Uo}W_oAoo^K5$i4;ue{u+GLmBX&vGtBX6fj?d zA^``$$a*L0#+j8$%PP8kwDY-dahHZ*j*+Sg2{^iK$^f41nXyMTej@{Mz+Xv-X%<9+ zczR%iHFFv%PbOY+z8nzJW!d%?UY9rG`zp*ydAi+UITtiySr(>To3MSfGrniR`eO&~ zOyy&?pvu_Q)SkW(Hz6XEg~tqk#PISACOdsYTpWnAStUp~#Fkprmi?6TBkmDRdiG0y za0s}*3OPa%=%W{hFj3xQ`n3pyUs#x3ouU#~JZQPr%stUZ6!Ixr-_H%xj$7nemlmOq z8|4uyj4G78ou;&McDx^*Q2*MyHBbPnqIBI-jg)U9rwc>`yW+A2a!r$K<-8T8X zYJ9Xt9IRO}^V_WPnJQ%_F#0gVuQ54N&+ARSHAWwY(J2;?hhpO)FPp;9uNLz!9H z+vc$~?8bOysYtCMTJwYLW7u!lY+j>q;h!sLf}FC;y|PfBg83n5d$;p*zA_AEWloRf z1#h4Dg^ldKP*LS@l}W34mfpeDiN0eiRjKW;1#gf|wGI}f;SOk%yb;g4ak%|-8e`@X zrT&c!y?qkx-ZGMN$6}H=XU$siS>67~NW{mIyI9#g$8I)_HQQyn`^xOD(gUYFw!6n@~$m=A2Q^z$sLMj6$CDaGwCu?#>MIrcTV&K#P-|ni9 z%q!SAG2f*t6B!8b3iQ4NcdK%8X00ovoCmamXT8B#h4uB0cD2ckUT*qV4#}}A3~(uc zXgXPkGO~c_shq%d#f}A-TsruvvZmms=7|I6HC+g2J(AJqHcMfYDswY8v5mj4-o;5r zkZfqPXX|^}1M}{PG_3E*m7#9X04A6nL)=A%5J{<*h3YzCZ-{*1<6oD~_}kNEFeSL} zn6nI@*UkJn=)@~tes83JW0NajVz)$PS6tT7yaryK8MvDNR_uLXH?M~_JnyF{iQwj; zhzkCY_uOE>vog6`27pK!;ZvT z9>Le>_!s02BCC&W{3v47Dmgxg3{PPtHkjf4Z)C&TyE76QnhBA?+%VoW#`gu{BljY# zJX0Je&i!P;*=Uwwu86q<;YW{yz6)Kl5)b+ukV>pXuiHtqoUqQrsyYC?WJTDYsjYex zjeh?S-qUK7J;(0$JRpnhMcs@3hrKIdB#SmZV2hM1Fq-%Njdi3T8;qyW@pAiy7Xs6& z@ZqE>J?FV_4UROk=M0iU`Gb%xv1BJ}dAV)4LmM2oTE5d~}NvC-oLJ8h0qbboY?BV6qlYQ-VgKfp_v3&QI{^G$^pUdYe-ZPsw ze(>Fqu6O~EeEv5&YuR^KZ)Sx43?z?-_qEZMT8wwqA9XY-TQwWZ9>3ety)Hy=grxS` zXyk5H#dq$lNJzQ!WaYUL-;k+_Ik9EYo45lMbxD)Bh0(+<$)w!FGFh^jecrc-p3Zl^NLx$s0N>R}7tS6F z*<;e`-dcGfFAG|P=$hQyM&aHPQ6_4I$(xxXYvGhBa(rs;zFGF$&?DQbAn@$uu9f$V zSXu4tkLO_x9=0tb-!%hS-}LWM#j6^BIpf(&<|7Xi z&sRca&#rt6R0fa5lB#!XcXf`7Nwehlu1La>xP%|Otc9rx-yMx9=%N?LngiQ8#YDM> z2s8R@4P!aR9V-v`ZDHw&OM!*K_r1X}XGbIF(+w{}n~>m_SCftzKY4K(gU?{UN%~W} z6+0y}1o0NtG5g0<%g(#L?0=j1MRTan;6Gx1&*en$JgcsL8uFmSLvGovwrU~~9m|{! zU9ArvY_nGJp}@M|S#%lkXjf;K`kh-&^O(ScBw;Zh72S2zRTkcjGDH)3ZIL2ge64Ac zQ{Nc%tU+a&;QLPrxH>_KEJdp9Yp}1oJf4)oy`TfUWtTg^GA?1XQ6a|9`S{2Vd3j#$ zvK7*^F3(PH7KV4BPEdZkMKr4Nnh0!hv(7*!Gwrp0fQPhPJ^1iRE`4pF=S{>1YJa(G zsJ6B&U0+$*3!83;^^nL!htKFvYlxgpH11ywWEKhgJ%AFgEt5FUdhGwutc_r4Ej7`1z();U*-52%P$pH7!d ze!I+-6c$bXl4C_LKNHs+?lt0kZi!?o=xjK`h0FwzL{ay;kkIGsh!RG+WjQK||BQ|V zG&pqlgo#2LqCXHdt4jE2&adRx=Rq4RPh`r2vO4JT1N^T~?*P^-J)~Wc@$Q@$c@Klz zuKzjH*=%*iy=*JCFA}jUUgIr)Ts9NlWK%=S8+kW+pL(;>Y{XU6X?uld!%GqpS}K=s zc+r6~NoxO?^P$IxuBNyj$;4i~4nQzA(e~)Gtnh>(Ph_)>pF8Bsa}wI?>EDCMGDQAZ z&zj0!aYWR?`*j@??a3rH_=3`bC0#2bdwVd)oU?NrMYArCy<)?mLDjC-Q(B%Sj`7S-FU^J~Gc<<3)D+p# zsiZRgz<8znS8dYN$kzN;WtTC2dKS)p5!a=@sH|j7V0fnj{n%+j#j>Ucn@~|Hg~uGw zBXwmIenS=-+|6_^qo)-`8#7R$lDJ-TyKv9vOmFfb&8k~hQ}+9nTAc$>!A-IMC*ajCy$8eVPFveI-7Y&ik%$GC zER69|ab^C{4GaNQgbigkq$~TE;9IEe$n1urjKC+l7nXwAp?v8{4j&uTbhA1Ar~IEi z>KRDv3i@)KAMusXsWr~)O&6Nt9c$JfdTV%1-9Nf=83n(F!o7(<3PYBHbW(B7;So6! zxR$34qZ0uue%qjG?2rO@i&lMjhm6@}gAl;=U+Cruj$(8yvUq-5l&Q6;p^10qn< z1x9kgVp#i^jRP$RwOE#1UWH>DMA)YUk=EH|7_gR^Gbsn+zCL>K^NH7jTv-RttEb*V z5$TBA@g(h_1TV`xyV0b99nPC##2pZ&$T`SPYqMLmh{F9tazjpUtV0rb@g(8kQKoKn ztqHx?b5DcZXU!WC#9=ykxeg4F7}J4w&=QCfv`scd+zYy{g=-P!QuW(mVqTj{S)WDS z!?`>+Cd?--`1S0{OScWgl@YybOvWuZ%4XoL<}mu*jf8C&DUZr4n3(0P=Aj!It&sff zh(^51+P7C>)rzL^F7cD6xZB58V#PIg0Bo#S^F*F6uBX5?NMB^2>ilfIL{BK0%z{24 zsL-zg9HGP3h+=f|{lc-{neov-b@!lw$p&^)VQJZ3hnVS?(jf1g_K!7~N7bL7$Q(Dq zNYmGLRy<=e@>G&6i}zCxPBpgu5a6}g#KBS3s;xB6@Cy~F4til+-9|L%$ZGMrCn?pX zUla*9XtByx$i;hiUWhv@e)MBHI}QQ28N0?Oh*lP>2anyd&O!tOMgh2|(q$M8R?{loJ_Rz{zf zxhF*b9pw3ZRtiUS#6~Q$yD?({mOrd-d5*r_au!M@r(N&X;66sR`-r07AX%>N^3j4B z7I!NjEdY{+O}cq|;bikt+s(CbKL(NS>nM$w-t62KR(@&}y;ceg<1M6;u2SpdWqpj` z%j>i^p?uwIK&#*FO-}ro03H}7H(}j)2PmC-+}jJ5vs1lYY@)kKMpY?j|K8`@s-HZ> z8WA;BDP~AS?K^C>Zqr8|l*+)R+oTxX(-dzw6^ShtZ6c+=rM&)7GdS34^PuUK`4x(` z6+z*N^l#r&eSawXqg24wq3#$@q|oMd&S$O{wdSXcJ}dmPg@(&FEXF0JQXGVZFbZCKYT&MRd5F$mJU!ih;?Dm$)34tldCp7FhU!pz7zWB#(o#YIPrDzv~H5hzA{x(-OAo*p``NNFpVVdI4$mIFke6p`WwI+jK~gS`D=n^N zQwraL?LFCFr9b5+rumv^M@;VtW+<|v9|kD=7Bsh{pN6`|`E*VxaK$_+M?_{=H$NHw z8XPEifTuR67kd;Z)mdz~D8$t2Qkp7lxqy0L>xWO+Eq!EPg_fPin5B*|$1FY98 zEv+1G11F#?o8gBYfK~vs>}M7^yrrW_QWrV;4cQJR>uxp}R8^^OiswyXvX&E`?csB) z`u>5pSglU2T)&S78q|acQDCW1H+D~KHqnPtk0aV{ZudSn)i?b11M%k~4IflXU6Md} zlS$IYpIL0!ZM#82M}Fi)6-fV>D3*bELAQfLDi0HK8^PB>$bjeX7H-7giZOD^;@#;? zyy5v!+nLNSo$Qs}>#m4jJtF)KUyaN~Nh?)LT(Sfaito&xPs4I5l4uGl%SXkgGI(Xp zUa`e1ZF7g#h$e7TJT-XyR>4$tOS7fxh96OX)juU=AMw$>KRuVAU^-!Z2E8BTnyBK3 z8aOS@dzW3Zg+u_cStOS0yS{>{|_O&ta9( zwvBk=;H5m5b2s+cx^=eg45a1xB|<3>)uRvJEj^GXZT^kHJg6Zw6YSQ385El}0|SHE zX;sMPc2Qq+IAZg}Wnw}hCzm5>-7--LjZVdTGEHqQQ{pkRe!TOaBw%w60U`}TtroUy zSzaezyNs&?>1hU^yf!X}DY|OEymiNNfH6SNib=X4;0C1I`l6X~v!%eba#KdXTfG0z z#5z^x_^$`@^2M6%0`=MNzaHK2i z7QF)t(_5V1MX`;Gg>PTh^9=oX>+^1L{^H5kp4=~lHfih3bhngPu6vfBTHtEIQ%%Tu zzioB-++RDGZsm-9LMNTdxEJd7Bh78y*pM`Bq+;OoJp9@Nmp(f@@C_xyl7@Wp^%<_# z0{Jt|HeU3b?(XXY(6b07{vJl3j0=*8M21{C80%qyrq8e<-J7jjp3%3tB7gQRaJEJk zLFib5MN_~^!E1We8k79F>E|EJfBcaJLKR0Sx3h^)aS+teqw}{~ut60O+6IU*EGI<1 zprq;hiUVVm&5>mU@=K>)$qPGua09W!wm{6NVAmyh5`qhN6{?lYUdy@euX%>r7c4&Z zhFb+Qrst>g!h0fO=x z^VlhsFDsxs0N=W=XxU=7UgGQHMW?y&l7kQ4)`T*-t(C6t;=igOzMd@3+S=)wZ7%T| z6;LvSQVJd^&%-Nj=q`OvPDU_XWBSsyi1=f1xK=6zUNK9_2l-z@*U&MSCYKe9ChYhM3R(B$ z(2@nLrl#LCi)HT^3YqUeWMy6_%0(q4A^goUG-p@yoID2j!2`ZG`#0x`lxs|&p>pG3 z*Qfg5YQ$cQosEh^(lo2L@H}~JKl&As>LlKB?|8EA_3l`zgk&iB1+PT&T)vMjOCuV3 zEwvk{I8Y#W3L93O*Vs})_7P8HS-qao;flAPLH{S9u|e1ao;qIPy*IBt11T@IJDVO$ z-AMYDYqHmu-?t}j_XgIpem?BQO_G9*w9G~6hj&|uAh0qZJJcOehl*5dmA%w>c7Jbg z^048yH{GP&7;RwrPO>ISnj`3djJqy zlTB2=R{HZEC0-S=UJtF<@tU=$(9GuVY@4M>OnK38KY(3$WGIViS+5R(8`C_=;5p(U zm_jE%yctD87hxzH_S&0}>iXLVGaZu8fRa{9Ap6sGJ}xEqc%0FBi{P>RoFKNc{e=A%BVPWUO|KEREM(GNlAdF-aEvS+W^ojwK%KGVp3+ z_#7|%;Ze6#Z*gkq{DP#_m?k9Em9`LHfjZ1b7D5%c~M)`GH&0`;r6Ha}=uq+2$Wakv5b_8+72t5J7n! zg}642$N%IC{7t0S2 z&v#Z=6`xcA)x40dm0}3rHll z+dM&Mr|GfY_=Cf-jb}(*vYCXCozf&r7$!%O4>mPSFfStTFd2Us{0F35YO?B=5k{sx zF4^wxTqr9F<~YG0SsfRT@A_uHUg3<%;W^1Uaxic z{%HHV{t0ubNvi(USFLYt_86B)yv4p*q1w0bl-f`IbSIEL=sn0ErPE86;=eI};Fx;k z79X@;r85_rYlHS{P>Gcx-OB*O0F(B0IpARr;re9FU#?_7?!PvFH1Yla0JT5u)GU+! z^Vf=kS#i>qNjGED)}_*XJ*jwtMom7(^7*6X*4`G7sO(PAYuJ@YQ(W_DF7HHqJSfzw zE=p;(`Yl;Rw)6fqbHd&myz!rjEcCk+^DeKYSd-;q7d=_93EHD9NCyBD+PAb%f*vT> zH34t^m3LxsmV3zB1yT5nocjaU(!Hz0UjsD1gWn5vjbl*rZZ)f`H?WFO!xpkk3VeeZ zQ_cxI?anchE8;k>kMi874J8VSb(;5Lwbkt9ZI$=x*2mv)e}=iPbBc$xr7Co8xuqoI zrS(z1t=nrWC%ZVGgnk!?Nw#kxnsky~;$s<%%DFp99ll(3v2VFaR= zWRgjIk?h}RSu_4|O_xkpR$p#})@_!uvlZI?x|8i%R@#c=<@=;?J!{j9V$K%ij=SOJ zrS_{9vzfQaYlX)k>^a-cG1-O&a1?sgj~VV4oHv?X+H932UBM4#9)xko z!5u*uu2;jlvgy7lvze9byX`Tnasc-(sJQ9r*O-0uiM7b60pN_7T5+)19WUzM*w7# z$5MH(DDn5~RpRdnO*83@Yp+_yS80j7iOQA9W=Huz4aW}0p~qZ(rLJf;{vrDv{BeV* zY2n(|R3TbKWdWjQJyrIuLF062-Bn1LqYSpQuGWo#jIca7JcIgvwe^@DJk99)@vF|X z(n{;_v-_IA;(R_k!;FI$9#=!zy>)}-diiR-E}gn{KP&$LXfJ?25-c?KwS&XD#ndw{ z#*W4ox|#(c_vIkV;lFmHo(@l@J~Q~c@cUlyWxk^)iaZ~ycyhy1yuB9BscDm1TA=`> zN*SZcXN^wL!r{;mHgWw0YMvN^BlFo*pCqN8o$M-AC=e1UAuleof&V3d0q}SqdD8o-+QuOrrvD+ zVlRJZ?R(?SqknIp=sMP*vMVw}V`t_pd$PsZw(a4F!6AN9a!CTci^YHNNB;ndz8v^i zbqfy(`Iobp*xJo;6G`OnEwv^t6oPh*yaF&wo!tKJXXYTvb+=*EUxtF`Ym-onmuDf)GTy6 zogc(nP3Di{4-dBMguaENlz#N@R{2yE&Kx!f+Uly!wX4BDX#W5Z&3WU-nW1EDMg^Yr zL}6FvY=W)WaDy1&sOy^khCgpV*c-#2w8odHTepThS*Gf?B5R9(5L)QJX%@){>#`R6 zM9DK2j6({Um?r(Io?NH!)AsBAn7m=|8{$j&d&Zhvo*&gMky6#wWcwY=3aUy+B#Mn7 z+9G8oLu8DqlY#WSFQ0v8vUTIhCw(_vHH+)%y6e*ZPH%zsbpHT$?z{g0pY>?YLX(`{`cYuT@YM{JQU`47xd5km4y z9AIFRv}1$M(_gXo{1b1--?dfzcQ+QZXx1JN=PA z;ETQm{hz#fW8!VIYx<>)xwf^tzS}6b+(~Hw$I9e0E_wNKI1EK;n{lRJmrdSW^H;QE zwfi^I`q0A(k>W8i+m^b3|5Ngw2+1s{@sLvT^lFkK=8RR6BB&&=& z7C9spW9#0n!TT!fniMg^Yp7|rQL5m{Fp}M~?4UTu)vNNn&xxq!Rh}Y#>wOwm)k0JEO8aXS5?=SzSu-Dpe9Nct6Acj;byarj$&Yvr%xdUC4;t7bpL-8Xc5 zx?pWQ9y-(fO`|RAOW!{&aEVtqWiexws!)aUIrr>gso8o8z(8edc7)c9|xc*Zuh({%g0>9~#j zrTJ5LP0jaLq2-7iA4>e+_~-us2Il-q_&Ya@{88a_(fmiHYY|>8kB4jnE$)>RsILkV z?Xp7?7L1j4C(A|~x44h@+Q&$=({1Covw}2+AC)430>>Y>90Be6RXtkRPcFtuZW(@I z6btgG80Ecj>w#Y8Q=a0o$-307XKSabe|Eoir_bi}X=YINRTZtXTd&akTJS&o5iiC+ zvCg;Qe-i1s^7!NIx}B}oscmgy)^q9Y7r3cG$%FgsT{HjLIj z9k%&*G}kE4fyvy%ui;-Ioo875#X8uEjkkGP?$N(Uru0hx0CMhq=0lO?`D7IwEj1La z8`)k~Tgj!Xd;HG<@IS*XOH}=^yk+7IQbQ%5hb2vqj}02&q-_%l3#j_ZrXkhgcls)~Q_kUttv27D^<2Y|jk{7mt7rQ=OY zUDEtWmJ6k56U5QmcySF0s5%t*1gF!1xVG~%<2XgzIf-`zu{jKc#Gn+G59+9n&1^^O7-OA5uV*+7qx5sPoNA`d4&aAghHO0k(!!+!mGfx&_l;i%oM&Ffj;;TMn*7p3r;m(A% z&-4EPhB}=GT35N&ELoe*(qt0=zcU!f$j8?VzO|#E=!vK3_fSVP*AdNeE#yXd z$Yo{R?8j=mFhv+tO^B)$1$ftuem`ix3x8`b3HWzWy1uu#@ztb|Sb2JU?`Jc_3!gIO zQ^}3?t8G_sSh|G-oL8&-H`bQw#!EudF}HS&OMUDHBdvJ(J~DBw^?J8l+}6KU7i8CU zXKVM5-NA^Yt?ePw*YbAP{QhR!+kLLqHdFi9@C`&^5)H;O&FhY}aDL?u!Mkzk>VKti zegxA#BWV6EzE2QpUJlp1KXLYRZ8e>Z&YOK|N?e$`nQ?G?JRUxR{O2lLY7<*K~QRmRJJ=-6$(2A0E6rPeJh|a0)*}ie>~NBt#Q#* zu0J~GsfNC&-kpAo$%@iBcXY|^>0W93N&d>775$|C9%|km@kWbftLeJDA{&V2kLDpx zZRwTtzj# z%*~hIz6oPiMZiL&oVMoqR0S{Ltc}jDx~JFr>UtmA5sWj+q zm5pVHgq4vchlt{cfPt9%5B{}YO;lcO0^UaFqY^;;>x;ATkBT+DXGMm`!fmA4TwT0+ zoZ69&OPdsp+XTfNf;Wz6(Cn5O<#^s$1HrV)Vf1@QCY6AOA1njQ$TpA5t$Xw`+R#ca zY5FAp0L=3$RfXG1-ntMAN#uaE(k;9rs0aC0UyJpX(0nsI_VKhas_ls5=bA&p{=T)+ zCY=l|x<)q=FHOgR_zo(MjC82=iR@#Me8}RFr^YeLfCv7CT{z4fqlcTO{rg!rt4-8MT-34bP6eZ=?J`y^_|}EdmWgPLE86+0rBD$vL%O zxbrzH8ZK5ik$l3=@(*y+ehpuEU&nSj_2-SeQ>e!O0B5usJDXT^xXMKFhe)G^WDLyj zBNmZ?b{qwcB#+ZH9WiE_X%$y$;YYSOHIMO6ME=|G?d6}^t}Ft=Slrvb*{#X>L!tii z^1${WSBw3h>}XFC?B_k*lD)01l1XiS66(%}h~}uNM)qAU`ugkE+P=r1{8ISUu4=y@ z{1f0`3hA1(+I)shYhLjUt<{>?+uA_$TfLvovb)S~k~DbL5rVPXAS&iRPSz1_?GCZFMd!&_@TLs*l<-XORMaja{zSX)`#Yj;5sGd!sRB4kG#Q7(5d zju_RQ%I-R1)6a#1wi&nZ2^l%#{{YutNsNuX*jm=zb@SK$2HPXf!plx)-|jfwdA#?T z2JO=*=cYF8$MZFJTDDj%ZCXY1JhzS62n07g=k=%F!m`P?<;uG8j&aBMRX-YdgHO_K zENw0?ZEbZ8N!lArc_men>7!PWf-hDoitKjh01|pv!`G)P)qB}r^*zYT;78`qiabX@ z!|xqyULTs^+)bh1+-dU203KwJ$kDS8OtPBNv+;M@;u}xMzj$~2zpwuQSXLkHSKx{K z8T(Io;?=gv_N_$fVU+G*F41m){5VZ_X54l9w|8Ldyeu-UCuw^ zAME|&4}%)d&uMveXT1LM6#3&BJna51tJ4Gw@OTw>!(KMO(otnb^HpP0&UYXqBXH`z zm?O1)<>8BcE93XUodZ}5*H?<4WYpeR#`U>WK_NZ9Y=;DPanR%sEBLqj71`?AV&7>2 z?d}02nAdLdc|Ev1pUmghzJC>;W*J^KrdgP-Y0_;s*GIYLWq6D}I|)x4hJNKln)$1r zIO;zbbq!`8v-yo2J(r%nEA$8c2~p!qpA&w|(plXsF|UX9i>oL$ed!}$mphyNt@F6; zz#mij`SAnco}u9#MJ*aHwOl-HAcPOzg&lhHoM#+!gU=Q9cl;7v?=;B1BU^@&Yk1pE zWD(_~C56yt1bXcm@A80fc&^xI)a$h<_BY2;PK%3r=yad(Qr&TU52R@RIEvd(E#Y~H zdyDN(9X1O@M_Y@3GG{3#l_mAPjBOfC7;z{v36Tsf^k2eFd;St_dTWb0qn6fmie#E5 z4;-lRs|N%gLC;=?k6P!vX=2maG>lodvu`JoK3ohR!nD6;D^D-O7Z(bmFDGS&)xbqq zcLl)h^0NLyyo{e2D*2T{X*JJmO zbN+Ms)p*lkNX9`F9LwALr7pM10tHWMpyaU#ldV)1mV=aoi_e&AV^u zP&=xOGM`_iTR*%&eq!7TrpvZZyNq$pKb>hV;Q<~s^3FQ<;-X~0A1^20rC3j%PH;WX ztv(>66Q8FT{VF}3%jdDhTKpW*d^Oc!xrDoUS3pZR{LH5$!H&#lh)K`ndXYgs1ZQxcW{e-;LRTEYqBTo(TTrtCio-SBB| z>_|S>HCBe)d;_+GUsQ)2LF>KGX8J47pRt$gX+E zp9@N-lKl-RwyMrPW(1Ks_?qBuV-pC<4Dpa(jD9uc{{XiKg|!b7d=}O<9S>TM!#e)} zg-UA{nzW_bD@fpUmSZeS9D;cKv~nVN> z*){v4_jdvb9^Jp6J$U+8!P3P#PIY&*o}KL4w#{3ns?Beq?#`N;Q1br(BlEZR#?(#k z?IWpbPj7VG*w9g}!$F<49jE#m9n2x~V29XG^`t!XsUVjEkv1P#EPD3wmq*e7yC4;+uK z;?YFXTw1-lDlY7>Jh2A{BiAE2=dWJF^5JFU{{XLE>+tLT3K!#V2WjzHY9HCUL@45Vo z;dhFdY_A5gjM=pW{mI(tChpX_**A3c(c4QS=s$}e2d(AQt#0DCQ!==XR2{0}{#H^y zi?apmmE+%g;77qNJK%4X&xII(e})F-sgMg@9CUqjuuXcVL~PYLzVCJl+1> z-Xi#gY|#CqP14?ABM@1_$;rs;BNjRDh9d{1N8qoBy6&nZj`L8CO~e4v%6{qN8NlPN zdiD0N z@w75t%M^k+^!7LxD{~_-etFss(bu8R(!Dd_{{V;M@c#gUZBb!Z%>4Y*qNue0c|iv+(c!5i_nUySjLWIo{pSK`B&U zC6ueQ!WG;}RYsAvxlpGw<8O}qY2d5vCO--I0&f{x-WeM5WYz6#MV{BU0a#4Hw)A_J zQi1+!c+rB1jFESC7r$svEbaTCkAJ04bswD{2kY%#q$d=Yx|-L&=+o1o!s653$0hKc zJXsfkEdK!EHva&Gd;StrB;x7xFF05-dD3|%F*olrVe*UT`1j+CS4kY8joj|f^zTMn zA2-Uwfs%Qr7|JIBTaQZ3-L}!ELCV=f%8#u$F8-Z;#ws<7A9Y6Da(Wt&V4h>-j&aGV zUSa5P{x0x;jITZ@Xu7VI;;S1U3u*96_Wc4)BuOr>DUMZ4-Wdsn6`B^pL{4L1O9ugM z_@m-Bli>Y0uBjxesjZt{9XC(6mr%Z%C6XxQk{f7It|mp2c$Bh6aAaJ8n(O0SpO`V) z0Oavm{wVN%o#QPc>rd4*>m6H7zE3XZ&hiAF-sL*&bYNA78NmmTD=M@U?RBE;mF@cc z!Zj`Gaz75fE@(d#^%LNGV3l^WyEP&);~q4~M=9@iw`1vQ1~B!7iaHEYUN)pGJ*k~quA{GSzHGJh4)gOB$> zKdx$riNK3oErn)|a0k$gWA&#dqvz^3QgXm*QpcQhD=ROrv8Vh-iD0{$W%-LD$?~r~ zG5ss^`j(y^y-5E6Gk<%NK8~7dq+Xd?bG`_U;fbkAMnMFmvIu_X}YT{u**Df zA+(hTocUt8VDP}9M+4O&nbwV}slwq;JRX((;(r8bZA_0H>(Xg{ z8@!uzkSr+d&j{ZjRZ>pgFc{;4az9Gsye0cm_@hJd2A`t%Zdp82H<+(|sNT6T#LtF& z^YV-wg#_#jxlz!`@#n&tw~f3XHKvnsBIZO}m>G;85)Pv(hM;k>vemSU6yrK?`I3Ki^iR)6 z*!#W{#bYoOXN}9JFKC~`7S~NBB%S3I+Ri=n(ES1ZmwqNk@n7P{hxB;u+B?anSGj<; z_{t~GD9!-kGw5^J;MeG-h~w`s9Fd$8{(bB7hyDoL;gjM|iXJfWBFL+2q-pl{al-4l z<(79Z=c!?p8*R$tJs+UtSKr!=`QE;p>LT;+t1Rd!*Lwz^0pWp@Oa|9KgJ*MPOk{+b`v&>d4J*C zkL>>d?5lX}7CRdlZ9lbFQu2P!WVXOAi_zje%^)H|WJ3*<}hVg7EtIXig+HKj^1ii_q>{T1}veJ%LCwXrd%+i9jI z-HZP3a7$BVxiIY?I}&)~{{XE`n&fRC%b%@PzPVPE50`Tfyaqp?^sai96jp|jjozk$ zT&&#g^4M|iY73-Nb0V_8sOW1x()=m;3iZj)e_G_eJ7~Tw_>JI%(>xdBXnY5)E$lP4 zsQNT|w&+lj(RngD51fSsv-d$A@-b4SR?(=`Z`JMiUH!x0lltzKwf*>W1GS14NcPs}}T&Mgk`(1iSM~1ZfzY#2#Fh=*$DThx;C5X%} zQsux)n8JX+=*Chckrje2_f4og7j>xUI@X`A-f6xh)AdJ^G`5pYSkzj}7!q5`rP(H3 z#y|;G%uI;;*)Sm!+b>g3hJ`cFmyjP;> z`UTY2K-t>bHRC9?Xd!hj*LL>Pd7GL*%J%UTE4K$}X6{Mc6ngX~n#+Pme?wl+Jb8Ii z*SW-#z3yg7Xj5N~U_M>V8=v-Ff5Zxtf}rH{w*$3WxQTJQsluK;jZ$V*xMkcnkCz^R zF%|Q5DJo8Bb$9#p{SN5MnR+A3ygh3v@aCm7o4@2?)DBAEDvQg2G57#@^{JEMCYA9I zUezr0+li*Q@a^CBcBHafOK#TJ2#F|P-YV_}IiYoQ%CgF(Nd~vBtrYlr;b9TxYuTDS zz5_EzSo5Cz`*Yi<=Jmf1An{uGuIp0Om-|0UZ8bFSv~07CyrM}Y^1ubACOFr0g|;dX zfjr4fpBab3!W8O4TS-O!Ex*sdpONlLq@0smrq|?o-|aQ=Uen-T!pr?tS|9Ce#W7gN zA?8PJ6oJM-2XO-j0N3X=&Y?B^+_x7J$vwpG(n%w+W|4`>B=;neNj|me{{Xh{#u+{- zd}X+|R$s8_VB0{dK^x8tho*Co6oZ~n`kL|3+gl%{e^vNl!w|~ysNyFZRIHSrlzz(l ko!^^lqCb%QTH>i@xx2zsf4alB73VLvFTHZFC4Q{`*~2~ecmMzZ literal 0 HcmV?d00001 diff --git a/public/img/portrait-2.jpg b/public/img/portrait-2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f84ba57c4a49d05b73dfe6adfedfadd1b3b7ce69 GIT binary patch literal 22907 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 E*)a^$hyVZp literal 0 HcmV?d00001 diff --git a/public/img/portrait-3.jpg b/public/img/portrait-3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a8116a03d0a0edb86b3f16b93678cafe9e8e97eb GIT binary patch literal 20292 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 literal 0 HcmV?d00001 diff --git a/public/img/startup.svg b/public/img/startup.svg new file mode 100644 index 0000000..32b19a8 --- /dev/null +++ b/public/img/startup.svg @@ -0,0 +1 @@ +template-dark.png \ No newline at end of file diff --git a/public/img/template-dark.png b/public/img/template-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..7df82da1b971b95456e819b2308f5e1f9e0cdc62 GIT binary patch literal 57551 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 CbveNR literal 0 HcmV?d00001 diff --git a/public/img/template-light.png b/public/img/template-light.png new file mode 100644 index 0000000000000000000000000000000000000000..967c0d64194a06d1e23c436327f3ab018c8a33c6 GIT binary patch literal 56603 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�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 literal 0 HcmV?d00001 diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..b704ab0 --- /dev/null +++ b/public/index.html @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + %REACT_APP_NAME% + + + + + + + + + + + + + + +
+ + + diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json new file mode 100644 index 0000000..9d32d04 --- /dev/null +++ b/public/locales/en/translation.json @@ -0,0 +1,431 @@ +{ + "admin": { + "drawer": { + "menu": { + "dashboard": "Dashboard", + "help": "Help", + "home": "Home", + "connect": "Connect", + "settings": "Settings", + "deviceManagement": "Manage", + "vault": "Vault", + "graphql": "Apollo", + "harbor": "Harbor" + } + }, + "header": { + "notifications": { + "empty": { + "title": "Your notification list is empty" + }, + "seeAll": "See all notifications" + } + }, + "home": { + "achievement": { + "action": "View Profile", + "description": "You have completed {{progress}}% of your profile. Your current progress is great.", + "title": "Congratulations {{name}}" + }, + "followers": { + "units": { + "likes": "Likes", + "love": "Love", + "smiles": "Smiles" + } + }, + "meeting": { + "title": "Meetings" + }, + "targets": { + "income": "Income", + "followers": "Followers", + "title": "Targets", + "views": "Views" + }, + "views": { + "action": "View Dashboard", + "unit": "Views" + }, + "welcome": { + "message": "Connect and Manage your network of IoT Devices.", + "subTitle": "Welcome back to FINDR", + "title": "Hi {{name}}," + } + } + }, + "auth": { + "forgotPassword": { + "form": { + "action": "Send Confirmation", + "back": "Back to login", + "email": { + "label": "E-mail Address" + } + }, + "notifications": { + "success": "Email has been sent!" + }, + "subTitle": "To get a validation code, enter the e-mail address associated to your account.", + "title": "Get validation code" + }, + "forgotPasswordSubmit": { + "form": { + "action": "Reset Password", + "back": "Back to login", + "code": { + "label": "Code" + }, + "confirmPassword": { + "label": "Confirm Password" + }, + "newPassword": { + "label": "New Password" + } + }, + "notifications": { + "success": "Your password has been changed!" + }, + "subTitle": "Enter the code you received by mail and choose a new password.", + "title": "Change Your Password" + }, + "login": { + "forgotPasswordLink": "Forgot Password?", + "form": { + "email": { + "label": "E-mail Address" + }, + "password": { + "label": "Password" + } + }, + "newAccountLink": "Don't have an account? Sign Up!", + "submit": "Sign In", + "title": "SIGN IN" + }, + "register": { + "back": "Back to login", + "form": { + "email": { + "label": "E-mail Address" + }, + "firstName": { + "label": "First Name" + }, + "jobTitle": { + "label": "Job Title", + "options": { + "e": "Engineer", + "d": "Developer", + "o": "Other" + } + }, + "lastName": { + "label": "Last Name" + } + }, + "notifications": { + "success": "Your account has been created successfully!" + }, + "submit": "Register", + "title": "Register" + } + }, + "common": { + "backHome": "Back to Home", + "cancel": "Cancel", + "confirm": "Confirm", + "confirmation": "Confirmation", + "delete": "Delete", + "edit": "Edit", + "errors": { + "forbidden": { + "subTitle": "You don't have access to view this page" + }, + "notFound": { + "subTitle": "Sorry, we couldn’t find the page you’re looking for. Perhaps you’ve misdeviceTyped the URL? Be sure to check your spelling.", + "title": "Oops!" + }, + "unexpected": { + "subTitle": "Something went wrong! If the problem persists, contact us!", + "title": "Oops!" + }, + "underConstructions": { + "subTitle": "We are actively working on this page.", + "title": "Under constructions!" + } + }, + "reset": "Reset", + "retry": "Try Again", + "selected": "Selected", + "snackbar": { + "error": "Error", + "success": "Success" + }, + "today": "Today", + "update": "Update", + "validations": { + "email": "Invalid email address", + "max": "Must be {{size}} characters or less", + "min": "Must be {{size}} characters or more", + "passwordMatch": "Password does not match", + "required": "Required" + } + }, + "dashboard": { + "activity": { + "title": "Activity" + }, + "budget": { + "legend": { + "unit": "Budget ($K)" + }, + "title": "Department" + }, + "orderProgress": { + "title": "Orders" + }, + "overview": { + "orders": "Orders", + "sales": "Sales", + "devices": "Devices", + "visits": "GB" + }, + "progress": { + "title": "Progress" + }, + "salesByAge": { + "title": "Devices by Age" + }, + "salesByCategory": { + "legend": { + "books": "Books", + "movies": "Movies & TV", + "software": "Software" + }, + "title": "Sales by Category" + }, + "salesHistory": { + "title": "Sales History", + "unit": "$ today" + }, + "salesProgress": { + "title": "Sales" + }, + "teams": { + "columns": { + "progress": "Progress", + "team": "Team", + "value": "Value" + }, + "title": "Teams Progress" + }, + "devices": { + "title": "Recent Devices" + }, + "visitProgress": { + "title": "Visits" + }, + "title": "Dashboard" + }, + "faq": { + "noAnswerLink": "Can't find it here? Check out our Help Center.", + "questions": { + "title1": "What is FINDR?", + "answer1": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.", + "title2": "How does it work?", + "answer2": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.", + "title3": "What are the features available?", + "answer3": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.", + "title4": "Who is maintaining the project?", + "answer4": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.", + "title5": "What is the license of the project?", + "answer5": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.", + "title6": "How can I contribute to the project?", + "answer6": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget." + }, + "title": "Frequently Asked Questions" + }, + "help": { + "menu": { + "contact": "Contact", + "faq": "FAQ", + "guide": "Guide", + "support": "Support" + }, + "title": "Help Center" + }, + "landing": { + "cta": { + "main": "Sign In", + "mainAuth": "Continue as {{name}}", + "secondary": "Source code" + }, + "features": { + "more": "More on Github", + "title": "Main Features" + }, + "title": "open-source application made with TypeScript for managing your IoT devices" + }, + "notifications": { + "newComment": "{{device}} has written a new comment on your profile", + "unreadMessages": "You have {{quantity}} unread messages" + }, + "profile": { + "activity": { + "empty": "Logs of your activity will show up here!", + "logs": { + "eventAdded": "You added a new event: {{resource}}", + "eventUpdated": "You updated event: {{resource}}", + "deviceAdded": "You added a new device: {{resource}}", + "deviceDeleted": "You deleted device: {{resource}}", + "deviceUpdated": "You updated device: {{resource}}" + } + }, + "completion": { + "title": "Profile Completion" + }, + "info": { + "form": { + "email": { + "label": "Email Address" + }, + "firstName": { + "label": "First Name" + }, + "jobTitle": { + "label": "Job Title", + "options": { + "e": "Engineer", + "d": "Developer", + "o": "Other" + } + }, + "lastName": { + "label": "Last Name" + } + }, + "title": "Update Profile" + }, + "menu": { + "activity": "Activity", + "info": "Information", + "password": "Password" + }, + "notifications": { + "informationUpdated": "Information updated!", + "passwordChanged": "Password changed!" + }, + "password": { + "form": { + "current": { + "label": "Current Password" + }, + "confirm": { + "label": "Confirm Password" + }, + "new": { + "label": "New Password" + } + }, + "title": "Change Your password" + } + }, + "settings": { + "drawer": { + "direction": { + "label": "Direction", + "options": { + "ltr": "LTR", + "rtl": "RTL" + } + }, + "language": { + "label": "Language", + "options": { + "en": "English", + "fr": "Français" + } + }, + "mode": { + "label": "Mode", + "options": { + "dark": "Dark", + "light": "Light" + } + }, + "sidebar": { + "label": "Sidebar", + "options": { + "collapsed": "Collapsed", + "full": "Full" + } + }, + "title": "Settings" + } + }, + "deviceManagement": { + "confirmations": { + "delete": "Are you sure you want to delete this device?" + }, + "form": { + "disabled": { + "label": "Disabled" + }, + "macAddress": { + "label": "Mac Address" + }, + "firstName": { + "label": "Name" + }, + "upConnector": { + "label": "Destination", + "options": { + "s": "S3", + "d": "Dynamo DB", + "b": "Blob Store" + } + }, + "downConnector": { + "label": "Protocol", + "options": { + "m": "MQTT", + "h": "HTPP" + } + }, + "lastName": { + "label": "Template" + }, + "deviceType": { + "label": "Type" + } + }, + "modal": { + "add": { + "action": "Add", + "title": "Add Device" + }, + "edit": { + "action": "Edit", + "title": "Edit Device" + } + }, + "notifications": { + "addSuccess": "{{device}} has been added!", + "deleteSuccess": "Device(s) have been deleted!", + "updateSuccess": "{{device}} has been updated!" + }, + "table": { + "headers": { + "actions": "Actions", + "upConnector": "Destination", + "deviceType": "Type", + "status": "Status", + "device": "Device" + } + }, + "toolbar": { + "title": "Devices" + } + } +} \ No newline at end of file diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json new file mode 100644 index 0000000..65305c1 --- /dev/null +++ b/public/locales/fr/translation.json @@ -0,0 +1,421 @@ +{ + "admin": { + "drawer": { + "menu": { + "dashboard": "Dashboard", + "help": "Aide", + "home": "Home", + "projects": "Projets", + "settings": "Paramètres", + "deviceManagement": "Utilisateurs" + } + }, + "header": { + "notifications": { + "empty": { + "title": "Votre liste de notifications est vide" + }, + "seeAll": "Voir toutes les notifications" + } + }, + "home": { + "achievement": { + "action": "Voir mon profil", + "description": "Vous avez complété {{progress}}% de votre profile. Vos progrès sont fantastiques.", + "title": "Félicitations {{name}}" + }, + "followers": { + "units": { + "likes": "Likes", + "love": "Love", + "smiles": "Smiles" + } + }, + "meeting": { + "title": "Réunions" + }, + "targets": { + "income": "Revenus", + "followers": "Followers", + "title": "Objectifs", + "views": "Vues" + }, + "views": { + "action": "Voir le dashboard", + "unit": "Vues" + }, + "welcome": { + "message": "Cette page a été conçue pour afficher les informations importantes de l'application", + "subTitle": "Heureux de vous revoir!", + "title": "Bonjour {{name}}," + } + } + }, + "auth": { + "forgotPassword": { + "form": { + "action": "Envoyer le code", + "back": "Retourner sur la page de login", + "email": { + "label": "Adresse E-mail" + } + }, + "notifications": { + "success": "Un e-mail a été envoyé!" + }, + "subTitle": "Pour obtenir un code de validation, entrez d'abord l'adresse e-mail que vous avez ajoutée à votre compte.", + "title": "Obtenir un code de validation" + }, + "forgotPasswordSubmit": { + "form": { + "action": "Réinitialiser le mot de passe", + "back": "Retourner sur la page de login", + "code": { + "label": "Code" + }, + "confirmPassword": { + "label": "Confirmer le mot de passe" + }, + "newPassword": { + "label": "Nouveau mot de passe" + } + }, + "notifications": { + "success": "Votre mot de passe a été modifié!" + }, + "subTitle": "Entrez le code reçu via e-mail et choisissez votre nouveau mot de passe.", + "title": "Changer le mot de passe" + }, + "login": { + "forgotPasswordLink": "Mot de passe oublié?", + "form": { + "email": { + "label": "Adresse E-mail" + }, + "password": { + "label": "Mot de passe" + } + }, + "newAccountLink": "Vous n'avez pas encore de compte? Enregistrez-vous!", + "submit": "Se connecter", + "title": "Se connecter" + }, + "register": { + "back": "Retourner sur la page de login", + "form": { + "email": { + "label": "Adresse E-mail" + }, + "firstName": { + "label": "Prénom" + }, + "jobTitle": { + "label": "Genre", + "options": { + "f": "F", + "m": "M", + "n": "NC" + } + }, + "lastName": { + "label": "Nom" + } + }, + "notifications": { + "success": "Votre compte a été créé avec succès!" + }, + "submit": "S'enregistrer", + "title": "S'enregistrer" + } + }, + "common": { + "backHome": "Retourner sur la page d'accueil", + "cancel": "Annuler", + "confirm": "Confirmer", + "confirmation": "Confirmation", + "delete": "Supprimer", + "edit": "Editer", + "errors": { + "forbidden": { + "subTitle": "Vous n'avez pas les accès suffisant pour accéder à cette page" + }, + "notFound": { + "subTitle": "Désolé, nous n'avons pas pu trouver la page que vous cherchez. L'URL peut être incorrect.", + "title": "Oups!" + }, + "unexpected": { + "subTitle": "Une erreur s'est produite! Si le problème persiste, contactez-nous!", + "title": "Oups!" + }, + "underConstructions": { + "subTitle": "Nous travaillons activement sur cette page", + "title": "Chantier en cours!" + } + }, + "reset": "Reset", + "retry": "Réessayer", + "selected": "sélectionné(s)", + "snackbar": { + "error": "Erreur", + "success": "Succès" + }, + "today": "Aujourd'hui", + "update": "Modifier", + "validations": { + "email": "Adresse e-mail invalide", + "max": "Maximum {{size}} caractères autorisés", + "min": "Minimum {{size}} caractères autorisés", + "passwordMatch": "Les mots de passe ne correspondent pas", + "required": "Requis" + } + }, + "dashboard": { + "activity": { + "title": "Activité" + }, + "budget": { + "legend": { + "unit": "Budget ($K)" + }, + "title": "Budget" + }, + "orderProgress": { + "title": "Commandes" + }, + "overview": { + "orders": "Commandes", + "sales": "Ventes", + "devices": "Utilisateurs", + "visits": "Visites" + }, + "progress": { + "title": "Progrès" + }, + "salesByAge": { + "title": "Ventes par tranche d'âge" + }, + "salesByCategory": { + "legend": { + "books": "Livres", + "movies": "Films et TV", + "software": "Software" + }, + "title": "Ventes par catégorie" + }, + "salesHistory": { + "title": "Historique des ventes", + "unit": "$ aujourd'hui" + }, + "salesProgress": { + "title": "Ventes" + }, + "teams": { + "columns": { + "progress": "Progrès", + "team": "Equipe", + "value": "Valeur" + }, + "title": "Progrès des équipes" + }, + "devices": { + "title": "Utilisateurs récents" + }, + "visitProgress": { + "title": "Visites" + }, + "title": "Dashboard" + }, + "faq": { + "noAnswerLink": "Vous ne trouvez pas la réponse à votre question? Retournez à la page d'aide.", + "questions": { + "title1": "Qu'est ce que React Admin?", + "answer1": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.", + "title2": "Comment fonctionne-t-il?", + "answer2": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.", + "title3": "Quelles sont les fonctionnalités disponibles?", + "answer3": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.", + "title4": "Qui maintient le projet?", + "answer4": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.", + "title5": "Quelle est la licence utilisée?", + "answer5": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget.", + "title6": "Comment puis-je contribuer au projet?", + "answer6": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortiseget." + }, + "title": "Questions Fréquemment Posées" + }, + "help": { + "menu": { + "contact": "Contact", + "faq": "FAQ", + "guide": "Guide", + "support": "Support" + }, + "title": "Centre d'aide" + }, + "landing": { + "cta": { + "main": "Se connecter", + "mainAuth": "Continuer en tant que {{name}}", + "secondary": "Code source" + }, + "features": { + "more": "Plus sur Github", + "title": "Principales fonctionnalités" + }, + "title": "Open-source dashboard application fait avec React" + }, + "notifications": { + "newComment": "{{device}} a écrit un commentaire sur votre profil", + "unreadMessages": "Vous avez {{quantity}} messages non-lus" + }, + "profile": { + "activity": { + "empty": "Les logs de votre activité seront affichés ici!", + "logs": { + "eventAdded": "Vous avez ajouté un nouvel évènement: {{resource}}", + "eventUpdated": "Vous avez modifié l'évènement: {{resource}}", + "deviceAdded": "Vous avez ajouté un nouvel utilisateur: {{resource}}", + "deviceDeleted": "Vous avez supprimé l'utilisateur: {{resource}}", + "deviceUpdated": "Vous avez modifié l'utilisateur: {{resource}}" + } + }, + "completion": { + "title": "Avancée du profil" + }, + "info": { + "form": { + "email": { + "label": "Adresse Email" + }, + "firstName": { + "label": "Prénom" + }, + "jobTitle": { + "label": "Genre", + "options": { + "f": "Femme", + "m": "Homme", + "n": "NC" + } + }, + "lastName": { + "label": "Nom" + } + }, + "title": "Modifier le profil" + }, + "menu": { + "activity": "Activité", + "info": "Information", + "password": "Mot de passe" + }, + "notifications": { + "informationUpdated": "Information modifiée!", + "passwordChanged": "Mot de passe modifié!" + }, + "password": { + "form": { + "current": { + "label": "Mot de passe actuel" + }, + "confirm": { + "label": "Confirmer le mot de passe" + }, + "new": { + "label": "Nouveau mot de passe" + } + }, + "title": "Changer votre mot de passe" + } + }, + "settings": { + "drawer": { + "direction": { + "label": "Direction", + "options": { + "ltr": "LTR", + "rtl": "RTL" + } + }, + "language": { + "label": "Langage", + "options": { + "en": "English", + "fr": "Français" + } + }, + "mode": { + "label": "Mode", + "options": { + "dark": "Foncé", + "light": "Clair" + } + }, + "sidebar": { + "label": "Barre latérale", + "options": { + "collapsed": "Réduite", + "full": "Complète" + } + }, + "title": "Paramètres" + } + }, + "deviceManagement": { + "confirmations": { + "delete": "Etes-vous certain de vouloir supprimer cet utilisateur?" + }, + "form": { + "disabled": { + "label": "Désactivé" + }, + "email": { + "label": "Addresse E-mail" + }, + "firstName": { + "label": "Prénom" + }, + "jobTitle": { + "label": "Genre", + "options": { + "f": "Femme", + "m": "Homme", + "n": "NC" + } + }, + "lastName": { + "label": "Nom" + }, + "deviceType": { + "label": "Rôle" + } + }, + "modal": { + "add": { + "action": "Ajouter", + "title": "Ajouter un utilisateur" + }, + "edit": { + "action": "Modifier", + "title": "Modifier l'utilisateur" + } + }, + "notifications": { + "addSuccess": "{{device}} a été ajouté!", + "deleteSuccess": "L(es) utilisateur(s) ont été supprimés!", + "updateSuccess": "{{device}} a été modifié!" + }, + "table": { + "headers": { + "actions": "Actions", + "jobTitle": "Genre", + "deviceType": "Rôle", + "status": "Statut", + "device": "Utilisateur" + } + }, + "toolbar": { + "title": "Utilisateurs" + } + } +} diff --git a/public/logo.svg b/public/logo.svg new file mode 100644 index 0000000..94187d7 --- /dev/null +++ b/public/logo.svg @@ -0,0 +1,18 @@ + + + + + diff --git a/public/logo192.png b/public/logo192.png new file mode 100644 index 0000000000000000000000000000000000000000..da8f253e114aeb611d33060000ac4f214a421d07 GIT binary patch literal 3498 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 literal 0 HcmV?d00001 diff --git a/public/logo512.png b/public/logo512.png new file mode 100644 index 0000000000000000000000000000000000000000..9cf9ba7fba56c64044f6c310472d3bedffabe26c GIT binary patch literal 10444 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 \ No newline at end of file diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index d2f8422..0000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/App.test.tsx b/src/App.test.tsx new file mode 100644 index 0000000..e2614ce --- /dev/null +++ b/src/App.test.tsx @@ -0,0 +1,9 @@ +import { render, screen } from "@testing-library/react"; +import React from "react"; +import App from "./App"; + +test("renders learn react link", () => { + render(); + const linkElement = screen.getByText(/learn react/i); + expect(linkElement).toBeInTheDocument(); +}); diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..516e216 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,53 @@ +import * as Sentry from "@sentry/react"; +import React from "react"; +import { QueryClient, QueryClientProvider } from "react-query"; +import { ReactQueryDevtools } from "react-query/devtools"; +import AppRoutes from "./AppRoutes"; +import AuthProvider from "./auth/contexts/AuthProvider"; +import Loader from "./core/components/Loader"; +import QueryWrapper from "./core/components/QueryWrapper"; +import SettingsProvider from "./core/contexts/SettingsProvider"; +import SnackbarProvider from "./core/contexts/SnackbarProvider"; +import usePageTracking from "./core/hooks/usePageTracking"; + +if (process.env.NODE_ENV === "production") { + Sentry.init({ + dsn: process.env.REACT_APP_SENTRY_DSN, + }); +} + +// Create a client +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + retry: 0, + suspense: true, + }, + }, +}); + +function App() { + usePageTracking(); + + return ( + }> + + + + + + + + + + + + + + + + ); +} + +export default App; diff --git a/src/AppRoutes.tsx b/src/AppRoutes.tsx new file mode 100644 index 0000000..7a8bc7d --- /dev/null +++ b/src/AppRoutes.tsx @@ -0,0 +1,107 @@ +import { lazy } from "react"; +import { Navigate, Route, Routes } from "react-router-dom"; +import PrivateRoute from "./core/components/PrivateRoute"; + +// Admin +const Admin = lazy(() => import("./admin/pages/Admin")); +const Dashboard = lazy(() => import("./admin/pages/Dashboard")); +const Faq = lazy(() => import("./admin/pages/Faq")); +const HelpCenter = lazy(() => import("./admin/pages/HelpCenter")); +const Home = lazy(() => import("./admin/pages/Home")); +const Profile = lazy(() => import("./admin/pages/Profile")); +const ProfileActivity = lazy(() => import("./admin/pages/ProfileActivity")); +const ProfileInformation = lazy( + () => import("./admin/pages/ProfileInformation") +); +const ProfilePassword = lazy(() => import("./admin/pages/ProfilePassword")); + +// Auth +const ForgotPassword = lazy(() => import("./auth/pages/ForgotPassword")); +const ForgotPasswordSubmit = lazy( + () => import("./auth/pages/ForgotPasswordSubmit") +); +const Login = lazy(() => import("./auth/pages/Login")); +const Register = lazy(() => import("./auth/pages/Register")); + + +// Core +const Forbidden = lazy(() => import("./core/pages/Forbidden")); +const NotFound = lazy(() => import("./core/pages/NotFound")); +const UnderConstructions = lazy( + () => import("./core/pages/UnderConstructions") +); + +// Landing +const Landing = lazy(() => import("./landing/pages/Landing")); + +// Devices +const DeviceManagement = lazy(() => import("./devices/pages/DeviceManagement")); + +const AppRoutes = () => { + return ( + + } /> + }> + } /> + } /> + } /> + } /> + }> + } /> + } /> + } /> + + + } + /> + + } + /> + + } + /> + + } + /> + } /> + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } + /> + + ); +}; + +export default AppRoutes; diff --git a/src/admin/components/AdminAppBar.tsx b/src/admin/components/AdminAppBar.tsx new file mode 100644 index 0000000..4467bf3 --- /dev/null +++ b/src/admin/components/AdminAppBar.tsx @@ -0,0 +1,27 @@ +import AppBar from "@material-ui/core/AppBar"; +import { drawerCollapsedWidth, drawerWidth } from "../../core/config/layout"; +import { useSettings } from "../../core/contexts/SettingsProvider"; + +type AdminAppBarProps = { + children: React.ReactNode; +}; + +const AdminAppBar = ({ children }: AdminAppBarProps) => { + const { collapsed } = useSettings(); + const width = collapsed ? drawerCollapsedWidth : drawerWidth; + + return ( + + {children} + + ); +}; + +export default AdminAppBar; diff --git a/src/admin/components/AdminDrawer.tsx b/src/admin/components/AdminDrawer.tsx new file mode 100644 index 0000000..547eb67 --- /dev/null +++ b/src/admin/components/AdminDrawer.tsx @@ -0,0 +1,207 @@ +import Avatar from "@material-ui/core/Avatar"; +import Box from "@material-ui/core/Box"; +import Drawer from "@material-ui/core/Drawer"; +import List from "@material-ui/core/List"; +import ListItem from "@material-ui/core/ListItem"; +import ListItemAvatar from "@material-ui/core/ListItemAvatar"; +import ListItemText from "@material-ui/core/ListItemText"; +import AccountTreeIcon from "@material-ui/icons/AccountTree"; +import BarChartIcon from "@material-ui/icons/BarChart"; +import EventIcon from "@material-ui/icons/Event"; +import HelpCenterIcon from "@material-ui/icons/HelpCenter"; +import HomeIcon from "@material-ui/icons/Home"; +import SensorIcon from "@material-ui/icons/Sensors"; +import PersonIcon from "@material-ui/icons/Person"; +import SettingsIcon from "@material-ui/icons/Settings"; +import LockIcon from "@material-ui/icons/Lock"; +import HubIcon from '@mui/icons-material/Hub'; +import VerifiedUserIcon from '@mui/icons-material/VerifiedUser'; +import { useTranslation } from "react-i18next"; +import { NavLink } from "react-router-dom"; +import { useAuth } from "../../auth/contexts/AuthProvider"; +import Logo from "../../core/components/Logo"; +import { drawerCollapsedWidth, drawerWidth } from "../../core/config/layout"; + +type AdminDrawerProps = { + collapsed: boolean; + mobileOpen: boolean; + onDrawerToggle: () => void; + onSettingsToggle: () => void; +}; + +export const menuItems = [ + { + icon: HomeIcon, + key: "admin.drawer.menu.home", + path: "/admin", + }, + { + icon: BarChartIcon, + key: "admin.drawer.menu.dashboard", + path: "/admin/dashboard", + }, + { + icon: SensorIcon, + key: "admin.drawer.menu.connect", + path: "/admin/connect", + }, + { + icon: AccountTreeIcon, + key: "admin.drawer.menu.deviceManagement", + path: "/admin/device-management", + }, + { + icon: LockIcon, + key: "admin.drawer.menu.vault", + path: "/admin/vault", + }, + { + icon: HubIcon, + key: "admin.drawer.menu.graphql", + path: "/admin/graphql", + }, + { + icon: VerifiedUserIcon, + key: "admin.drawer.menu.harbor", + path: "/admin/harbor", + } +]; + +const AdminDrawer = ({ + collapsed, + mobileOpen, + onDrawerToggle, + onSettingsToggle, +}: AdminDrawerProps) => { + const { userInfo } = useAuth(); + const { t } = useTranslation(); + + const width = collapsed ? drawerCollapsedWidth : drawerWidth; + + const drawer = ( + + + + {menuItems.map((item) => ( + + + + + + + + + ))} + + + + + + + + + + {userInfo && ( + + )} + + + + + + + + + + + + + + + + + + + + ); + + return ( + + {/* The implementation can be swapped with js to avoid SEO duplication of links. */} + + {drawer} + + + {drawer} + + + ); +}; + +export default AdminDrawer; diff --git a/src/admin/components/AdminToolbar.tsx b/src/admin/components/AdminToolbar.tsx new file mode 100644 index 0000000..ef6298b --- /dev/null +++ b/src/admin/components/AdminToolbar.tsx @@ -0,0 +1,37 @@ +import IconButton from "@material-ui/core/IconButton"; +import Toolbar from "@material-ui/core/Toolbar"; +import Typography from "@material-ui/core/Typography"; +import MenuIcon from "@material-ui/icons/Menu"; +import { useSettings } from "../../core/contexts/SettingsProvider"; + +type AdminToolbarProps = { + children?: React.ReactNode; + title?: string; +}; + +const AdminToolbar = ({ children, title }: AdminToolbarProps) => { + const { toggleDrawer } = useSettings(); + + return ( + + + + + + {title} + + {children} + + ); +}; + +export default AdminToolbar; diff --git a/src/admin/components/RecentNotifications.tsx b/src/admin/components/RecentNotifications.tsx new file mode 100644 index 0000000..27425f8 --- /dev/null +++ b/src/admin/components/RecentNotifications.tsx @@ -0,0 +1,140 @@ +import Avatar from "@material-ui/core/Avatar"; +import Badge from "@material-ui/core/Badge"; +import Box from "@material-ui/core/Box"; +import Button from "@material-ui/core/Button"; +import IconButton from "@material-ui/core/IconButton"; +import List from "@material-ui/core/List"; +import ListItem from "@material-ui/core/ListItem"; +import ListItemAvatar from "@material-ui/core/ListItemAvatar"; +import ListItemText from "@material-ui/core/ListItemText"; +import Popover from "@material-ui/core/Popover"; +import NotificationsIcon from "@material-ui/icons/Notifications"; +import PersonIcon from "@material-ui/icons/Person"; +import formatDistanceToNow from "date-fns/formatDistanceToNow"; +import { useMemo, useState } from "react"; +import { Trans, useTranslation } from "react-i18next"; +import { NavLink } from "react-router-dom"; +import Empty from "../../core/components/Empty"; +import Loader from "../../core/components/Loader"; +import Result from "../../core/components/Result"; +import { useDateLocale } from "../../core/hooks/useDateLocale"; +import { notificationKeys } from "../config/notification"; +import { useNotifications } from "../hooks/useNotifications"; + +const RecentNotifications = () => { + const locale = useDateLocale(); + const { t } = useTranslation(); + + const [anchorEl, setAnchorEl] = useState(null); + + const { data, isError, isLoading } = useNotifications(); + + const open = Boolean(anchorEl); + + const unreadCount = useMemo( + () => data && data.filter((notification) => notification.unread).length, + [data] + ); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + return ( + + + + + + + + + {!isLoading && !isError && data && data.length > 0 && ( + + {data.map((notification) => ( + + + + + + + }} + defaults="{{ user }} did someting {{ quantity }} times" + i18nKey={notificationKeys[notification.code]} + values={notification.params} + /> + } + secondary={formatDistanceToNow( + new Date(notification.createdAt), + { addSuffix: true, locale } + )} + /> + + ))} + + )} + {!isLoading && !isError && (!data || data.length === 0) && ( + + )} + {isError && ( + + )} + {isLoading && } + + + + + + + ); +}; + +export default RecentNotifications; diff --git a/src/admin/config/activity.ts b/src/admin/config/activity.ts new file mode 100644 index 0000000..6f86f23 --- /dev/null +++ b/src/admin/config/activity.ts @@ -0,0 +1,7 @@ +export const logKeys: { [key: string]: string } = { + eventAdded: "profile.activity.logs.eventAdded", + eventUpdated: "profile.activity.logs.eventUpdated", + userAdded: "profile.activity.logs.eventAdded", + deviceDeleted: "profile.activity.logs.deviceDeleted", + deviceUpdated: "profile.activity.logs.deviceUpdated", +}; diff --git a/src/admin/config/notification.ts b/src/admin/config/notification.ts new file mode 100644 index 0000000..097d059 --- /dev/null +++ b/src/admin/config/notification.ts @@ -0,0 +1,4 @@ +export const notificationKeys: { [key: string]: string } = { + newComment: "notifications.newComment", + unreadMessages: "notifications.unreadMessages", +}; diff --git a/src/admin/hooks/useActivityLogs.ts b/src/admin/hooks/useActivityLogs.ts new file mode 100644 index 0000000..65640bf --- /dev/null +++ b/src/admin/hooks/useActivityLogs.ts @@ -0,0 +1,12 @@ +import axios from "axios"; +import { useQuery } from "react-query"; +import { ActivityLog } from "../types/activityLog"; + +const fetchActivityLogs = async (): Promise => { + const { data } = await axios.get("/api/activity-logs"); + return data; +}; + +export function useActivityLogs() { + return useQuery("activity-logs", () => fetchActivityLogs()); +} diff --git a/src/admin/hooks/useNotifications.ts b/src/admin/hooks/useNotifications.ts new file mode 100644 index 0000000..55e410a --- /dev/null +++ b/src/admin/hooks/useNotifications.ts @@ -0,0 +1,14 @@ +import axios from "axios"; +import { useQuery } from "react-query"; +import { Notification } from "../types/notification"; + +const fetchNotifications = async (): Promise => { + const { data } = await axios.get("/api/notifications"); + return data; +}; + +export function useNotifications() { + return useQuery("notifications", () => fetchNotifications(), { + suspense: false, + }); +} diff --git a/src/admin/hooks/useProfileInfo.ts b/src/admin/hooks/useProfileInfo.ts new file mode 100644 index 0000000..d368124 --- /dev/null +++ b/src/admin/hooks/useProfileInfo.ts @@ -0,0 +1,12 @@ +import axios from "axios"; +import { useQuery } from "react-query"; +import { ProfileInfo } from "../types/profileInfo"; + +const fetchProfileInfo = async (): Promise => { + const { data } = await axios.get("/api/profile-info"); + return data; +}; + +export function useProfileInfo() { + return useQuery("profile-info", () => fetchProfileInfo()); +} diff --git a/src/admin/hooks/useUpdateProfileInfo.ts b/src/admin/hooks/useUpdateProfileInfo.ts new file mode 100644 index 0000000..b857a70 --- /dev/null +++ b/src/admin/hooks/useUpdateProfileInfo.ts @@ -0,0 +1,22 @@ +import axios from "axios"; +import { useMutation, useQueryClient } from "react-query"; +import { ProfileInfo } from "../types/profileInfo"; + +const updateProfileInfo = async ( + profileInfo: ProfileInfo +): Promise => { + const { data } = await axios.put("/api/profile-info", profileInfo); + return data; +}; + +export function useUpdateProfileInfo() { + const queryClient = useQueryClient(); + + const { isLoading, mutateAsync } = useMutation(updateProfileInfo, { + onSuccess: (profileInfo: ProfileInfo) => { + queryClient.setQueryData(["profile-info"], profileInfo); + }, + }); + + return { isUpdating: isLoading, updateProfileInfo: mutateAsync }; +} diff --git a/src/admin/pages/Admin.tsx b/src/admin/pages/Admin.tsx new file mode 100644 index 0000000..ceaba09 --- /dev/null +++ b/src/admin/pages/Admin.tsx @@ -0,0 +1,41 @@ +import Box from "@material-ui/core/Box"; +import Toolbar from "@material-ui/core/Toolbar"; +import { useState } from "react"; +import { Outlet } from "react-router-dom"; +import QueryWrapper from "../../core/components/QueryWrapper"; +import SettingsDrawer from "../../core/components/SettingsDrawer"; +import { useSettings } from "../../core/contexts/SettingsProvider"; +import AdminDrawer from "../components/AdminDrawer"; + +const AdminLayout = () => { + const [settingsOpen, setSettingsOpen] = useState(false); + + const { collapsed, open, toggleDrawer } = useSettings(); + + const handleSettingsToggle = () => { + setSettingsOpen(!settingsOpen); + }; + + return ( + + + + + + + + + + + ); +}; + +export default AdminLayout; diff --git a/src/admin/pages/Dashboard.tsx b/src/admin/pages/Dashboard.tsx new file mode 100644 index 0000000..b64a565 --- /dev/null +++ b/src/admin/pages/Dashboard.tsx @@ -0,0 +1,105 @@ +import Grid from "@material-ui/core/Grid"; +import AttachMoneyIcon from "@material-ui/icons/AttachMoney"; +import ShoppingBasketIcon from "@material-ui/icons/ShoppingBasket"; +import SupervisorAccountIcon from "@material-ui/icons/SupervisorAccount"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import AdminAppBar from "../components/AdminAppBar"; +import AdminToolbar from "../components/AdminToolbar"; +import ActivityWidget from "../widgets/ActivityWidget"; +import BudgetWidget from "../widgets/BudgetWidget"; +import CircleProgressWidget from "../widgets/CircleProgressWidget"; +import OverviewWidget from "../widgets/OverviewWidget"; +import ProgressWidget from "../widgets/ProgressWidget"; +import SalesByAgeWidget from "../widgets/SalesByAgeWidget"; +import SalesByCategoryWidget from "../widgets/SalesByCategoryWidget"; +import SalesHistoryWidget from "../widgets/SalesHistoryWidget"; +import TeamProgressWidget from "../widgets/TeamProgressWidget"; +import DevicesWidget from "../widgets/DevicesWidget"; + +const overviewItems = [ + { + unit: "dashboard.overview.visits", + value: "20 700", + }, + { + unit: "dashboard.overview.sales", + value: "$ 1 550", + }, + { + unit: "dashboard.overview.orders", + value: "149", + }, + { + unit: "dashboard.overview.devices", + value: "657", + }, +]; + +const Dashboard = () => { + const { t } = useTranslation(); + + return ( + + + + + + {overviewItems.map((item, index) => ( + + + + ))} + + + + + + + + + + + } + mb={2} + title={t("dashboard.visitProgress.title")} + value={75} + /> + } + mb={2} + title={t("dashboard.orderProgress.title")} + value={50} + /> + } + title={t("dashboard.salesProgress.title")} + value={25} + /> + + + + + + + + + + + + + + + + + + + ); +}; + +export default Dashboard; diff --git a/src/admin/pages/Faq.tsx b/src/admin/pages/Faq.tsx new file mode 100644 index 0000000..2fb2542 --- /dev/null +++ b/src/admin/pages/Faq.tsx @@ -0,0 +1,79 @@ +import Accordion from "@material-ui/core/Accordion"; +import AccordionDetails from "@material-ui/core/AccordionDetails"; +import AccordionSummary from "@material-ui/core/AccordionSummary"; +import Container from "@material-ui/core/Container"; +import Link from "@material-ui/core/Link"; +import Typography from "@material-ui/core/Typography"; +import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Link as RouterLink } from "react-router-dom"; +import AdminAppBar from "../components/AdminAppBar"; +import AdminToolbar from "../components/AdminToolbar"; + +const questions = [ + { + title: "faq.questions.title1", + answer: "faq.questions.answer1", + }, + { + title: "faq.questions.title2", + answer: "faq.questions.answer2", + }, + { + title: "faq.questions.title3", + answer: "faq.questions.answer3", + }, + { + title: "faq.questions.title4", + answer: "faq.questions.answer4", + }, + { + title: "faq.questions.title5", + answer: "faq.questions.answer5", + }, + { + title: "faq.questions.title6", + answer: "faq.questions.answer6", + }, +]; + +const Faq = () => { + const { t } = useTranslation(); + + return ( + + + + + + + {t("faq.title")} + + {questions.map((question, index) => ( + + }> + + {t(question.title)} + + + + + {t(question.answer)} + + + + ))} + + {t("faq.noAnswerLink")} + + + + ); +}; + +export default Faq; diff --git a/src/admin/pages/HelpCenter.tsx b/src/admin/pages/HelpCenter.tsx new file mode 100644 index 0000000..d5d0c5b --- /dev/null +++ b/src/admin/pages/HelpCenter.tsx @@ -0,0 +1,136 @@ +import Avatar from "@material-ui/core/Avatar"; +import Badge from "@material-ui/core/Badge"; +import Card from "@material-ui/core/Card"; +import CardActionArea from "@material-ui/core/CardActionArea"; +import CardContent from "@material-ui/core/CardContent"; +import CardHeader from "@material-ui/core/CardHeader"; +import Container from "@material-ui/core/Container"; +import Grid from "@material-ui/core/Grid"; +import Typography from "@material-ui/core/Typography"; +import HelpIcon from "@material-ui/icons/Help"; +import MailIcon from "@material-ui/icons/Mail"; +import SchoolIcon from "@material-ui/icons/School"; +import SupportIcon from "@material-ui/icons/Support"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Link as RouterLink } from "react-router-dom"; +import { ReactComponent as HelpSvg } from "../../core/assets/help.svg"; +import SvgContainer from "../../core/components/SvgContainer"; +import AdminAppBar from "../components/AdminAppBar"; +import AdminToolbar from "../components/AdminToolbar"; + +const HelpCenter = () => { + const { t } = useTranslation(); + + return ( + + + + + + + + + + + + + + + + + } + /> + + + + {t("help.menu.guide")} + + + + + + + + + + + + + } + /> + + + {t("help.menu.faq")} + + + + + + + + + + + + } + /> + + + {t("help.menu.support")} + + + + + + + + + + + + } + /> + + + {t("help.menu.contact")} + + + + + + + + ); +}; + +export default HelpCenter; diff --git a/src/admin/pages/Home.tsx b/src/admin/pages/Home.tsx new file mode 100644 index 0000000..b5e302c --- /dev/null +++ b/src/admin/pages/Home.tsx @@ -0,0 +1,38 @@ +import Grid from "@material-ui/core/Grid"; +import Stack from "@material-ui/core/Stack"; +import React from "react"; +import AdminAppBar from "../components/AdminAppBar"; +import AdminToolbar from "../components/AdminToolbar"; +import RecentNotifications from "../components/RecentNotifications"; +import AchievementWidget from "../widgets/AchievementWidget"; +import FollowersWidget from "../widgets/FollowersWidget"; +import PersonalTargetsWidget from "../widgets/PersonalTargetsWidget"; +import ViewsWidget from "../widgets/ViewsWidget"; +import WelcomeWidget from "../widgets/WelcomeWidget"; + + +const Home = () => { + return ( + + + + + + + + + + {/* + + + + + + + + */} + + ); +}; + +export default Home; diff --git a/src/admin/pages/Profile.tsx b/src/admin/pages/Profile.tsx new file mode 100644 index 0000000..2460e14 --- /dev/null +++ b/src/admin/pages/Profile.tsx @@ -0,0 +1,117 @@ +import Avatar from "@material-ui/core/Avatar"; +import Box from "@material-ui/core/Box"; +import Fab from "@material-ui/core/Fab"; +import Grid from "@material-ui/core/Grid"; +import Tab from "@material-ui/core/Tab"; +import Tabs from "@material-ui/core/Tabs"; +import Typography from "@material-ui/core/Typography"; +import ExitToAppIcon from "@material-ui/icons/ExitToApp"; +import PersonIcon from "@material-ui/icons/Person"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import { NavLink, Outlet } from "react-router-dom"; +import { useAuth } from "../../auth/contexts/AuthProvider"; +import QueryWrapper from "../../core/components/QueryWrapper"; +import { useSnackbar } from "../../core/contexts/SnackbarProvider"; +import AdminAppBar from "../components/AdminAppBar"; +import AdminToolbar from "../components/AdminToolbar"; +import CircleProgressWidget from "../widgets/CircleProgressWidget"; + +const profileMenuItems = [ + { + key: "profile.menu.activity", + path: "", + }, + { + key: "profile.menu.info", + path: "./information", + }, + { + key: "profile.menu.password", + path: "./password", + }, +]; + +const Profile = () => { + const { isLoggingOut, logout, userInfo } = useAuth(); + const snackbar = useSnackbar(); + const { t } = useTranslation(); + + const handleLogout = () => { + logout().catch(() => + snackbar.error(t("common.errors.unexpected.subTitle")) + ); + }; + + return ( + + + + + + + + + + + + + + + {`${userInfo?.firstName} ${userInfo?.lastName}`} + {userInfo?.role} + + + + + + + {profileMenuItems.map((item) => ( + + ))} + + + + + + + + + ); +}; + +export default Profile; diff --git a/src/admin/pages/ProfileActivity.tsx b/src/admin/pages/ProfileActivity.tsx new file mode 100644 index 0000000..e8ab45f --- /dev/null +++ b/src/admin/pages/ProfileActivity.tsx @@ -0,0 +1,62 @@ +import Box from "@material-ui/core/Box"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import Typography from "@material-ui/core/Typography"; +import Timeline from "@material-ui/lab/Timeline"; +import TimelineConnector from "@material-ui/lab/TimelineConnector"; +import TimelineContent from "@material-ui/lab/TimelineContent"; +import TimelineDot from "@material-ui/lab/TimelineDot"; +import TimelineItem from "@material-ui/lab/TimelineItem"; +import TimelineSeparator from "@material-ui/lab/TimelineSeparator"; +import formatDistanceToNow from "date-fns/formatDistanceToNow"; +import { Trans, useTranslation } from "react-i18next"; +import Empty from "../../core/components/Empty"; +import { useDateLocale } from "../../core/hooks/useDateLocale"; +import { logKeys } from "../config/activity"; +import { useActivityLogs } from "../hooks/useActivityLogs"; + +const ProfileActivity = () => { + const locale = useDateLocale(); + const { t } = useTranslation(); + + const { data } = useActivityLogs(); + + if (!data || data.length === 0) { + return ; + } + + return ( + + + {data.map((log) => ( + + + + + + + + + }} + defaults="You modify resource {{ resouce }}" + i18nKey={logKeys[log.code]} + values={log.params} + /> + + {formatDistanceToNow(new Date(log.createdAt), { + addSuffix: true, + locale, + })} + + + + + + ))} + + + ); +}; + +export default ProfileActivity; diff --git a/src/admin/pages/ProfileInformation.tsx b/src/admin/pages/ProfileInformation.tsx new file mode 100644 index 0000000..2f7b401 --- /dev/null +++ b/src/admin/pages/ProfileInformation.tsx @@ -0,0 +1,149 @@ +import Button from "@material-ui/core/Button"; +import Card from "@material-ui/core/Card"; +import CardActions from "@material-ui/core/CardActions"; +import CardContent from "@material-ui/core/CardContent"; +import CardHeader from "@material-ui/core/CardHeader"; +import FormControl from "@material-ui/core/FormControl"; +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import FormLabel from "@material-ui/core/FormLabel"; +import Radio from "@material-ui/core/Radio"; +import RadioGroup from "@material-ui/core/RadioGroup"; +import TextField from "@material-ui/core/TextField"; +import LoadingButton from "@material-ui/lab/LoadingButton"; +import { useFormik } from "formik"; +import { useTranslation } from "react-i18next"; +import * as Yup from "yup"; +import { useUpdateProfileInfo } from "../../admin/hooks/useUpdateProfileInfo"; +import { useSnackbar } from "../../core/contexts/SnackbarProvider"; +import { useProfileInfo } from "../hooks/useProfileInfo"; +import { ProfileInfo } from "../types/profileInfo"; + +const jobTitles = [ + { label: "profile.info.form.jobTitle.options.e", value: "Engineer" }, + { label: "profile.info.form.jobTitle.options.d", value: "Developer" }, + { label: "profile.info.form.jobTitle.options.o", value: "Other" }, +]; + +const ProfileInformation = () => { + const snackbar = useSnackbar(); + const { t } = useTranslation(); + + const { data } = useProfileInfo(); + const { isUpdating, updateProfileInfo } = useUpdateProfileInfo(); + + const formik = useFormik({ + initialValues: { + email: data ? data.email : "", + firstName: data ? data.firstName : "", + jobTitle: data ? data.jobTitle : undefined, + job: data ? data.job : "", + lastName: data ? data.lastName : "", + }, + validationSchema: Yup.object({ + email: Yup.string() + .email(t("common.validations.email")) + .required(t("common.validations.required")), + firstName: Yup.string() + .max(20, t("common.validations.max", { size: 20 })) + .required(t("common.validations.required")), + lastName: Yup.string() + .max(30, t("common.validations.max", { size: 30 })) + .required(t("common.validations.required")), + }), + onSubmit: (values) => handleSubmit(values), + }); + + const handleSubmit = async (values: Partial) => { + updateProfileInfo({ ...values, id: data?.id } as ProfileInfo) + .then(() => { + snackbar.success(t("profile.notifications.informationUpdated")); + }) + .catch(() => { + snackbar.error(t("common.errors.unexpected.subTitle")); + }); + }; + + return ( +
+ + + + + + + + {t("profile.info.form.jobTitle.label")} + + + {jobTitles.map((jobTitle) => ( + } + label={t(jobTitle.label)} + /> + ))} + + + + + + + + {t("common.update")} + + + +
+ ); +}; + +export default ProfileInformation; diff --git a/src/admin/pages/ProfilePassword.tsx b/src/admin/pages/ProfilePassword.tsx new file mode 100644 index 0000000..23000be --- /dev/null +++ b/src/admin/pages/ProfilePassword.tsx @@ -0,0 +1,122 @@ +import Card from "@material-ui/core/Card"; +import CardActions from "@material-ui/core/CardActions"; +import CardContent from "@material-ui/core/CardContent"; +import CardHeader from "@material-ui/core/CardHeader"; +import TextField from "@material-ui/core/TextField"; +import LoadingButton from "@material-ui/lab/LoadingButton"; +import { useFormik } from "formik"; +import { useTranslation } from "react-i18next"; +import * as Yup from "yup"; +import { useUpdatePassword } from "../../auth/hooks/useUpdatePassword"; +import { useSnackbar } from "../../core/contexts/SnackbarProvider"; + +const ProfilePassword = () => { + const snackbar = useSnackbar(); + const { t } = useTranslation(); + + const { isUpdating, updatePassword } = useUpdatePassword(); + + const formik = useFormik({ + initialValues: { + oldPassword: "", + newPassword: "", + confirmPassword: "", + }, + validationSchema: Yup.object({ + oldPassword: Yup.string() + .min(8, t("common.validations.min", { size: 8 })) + .required(t("common.validations.required")), + newPassword: Yup.string() + .min(8, t("common.validations.min", { size: 8 })) + .required(t("common.validations.required")), + confirmPassword: Yup.string() + .oneOf([Yup.ref("newPassword")], t("common.validations.passwordMatch")) + .required(t("common.validations.required")), + }), + onSubmit: (values) => + handleUpdatePassword(values.oldPassword, values.newPassword), + }); + + const handleUpdatePassword = async ( + oldPassword: string, + newPassword: string + ) => { + updatePassword({ oldPassword, newPassword }) + .then(() => { + formik.resetForm(); + snackbar.success(t("profile.notifications.passwordChanged")); + }) + .catch(() => { + snackbar.error(t("common.errors.unexpected.subTitle")); + }); + }; + + return ( +
+ + + + + + + + + + {t("common.update")} + + + +
+ ); +}; + +export default ProfilePassword; diff --git a/src/admin/types/activityLog.ts b/src/admin/types/activityLog.ts new file mode 100644 index 0000000..00db556 --- /dev/null +++ b/src/admin/types/activityLog.ts @@ -0,0 +1,7 @@ +export interface ActivityLog { + id: string; + actor: string; + code: string; + createdAt: number; + params?: { [key: string]: string }; +} diff --git a/src/admin/types/notification.ts b/src/admin/types/notification.ts new file mode 100644 index 0000000..11833f3 --- /dev/null +++ b/src/admin/types/notification.ts @@ -0,0 +1,10 @@ +export interface Notification { + id: string; + code: string; + createdAt: number; + params?: { + quantity?: string; + user?: string; + }; + unread: boolean; +} diff --git a/src/admin/types/profileInfo.ts b/src/admin/types/profileInfo.ts new file mode 100644 index 0000000..a2ad9d1 --- /dev/null +++ b/src/admin/types/profileInfo.ts @@ -0,0 +1,9 @@ +export interface ProfileInfo { + id: string; + avatar?: string; + email: string; + firstName: string; + gender?: "F" | "M" | "NC"; + job: string; + lastName: string; +} diff --git a/src/admin/widgets/AchievementWidget.tsx b/src/admin/widgets/AchievementWidget.tsx new file mode 100644 index 0000000..7aec2e7 --- /dev/null +++ b/src/admin/widgets/AchievementWidget.tsx @@ -0,0 +1,49 @@ +import Avatar from "@material-ui/core/Avatar"; +import Button from "@material-ui/core/Button"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import Typography from "@material-ui/core/Typography"; +import StarIcon from "@material-ui/icons/Star"; +import { useTranslation } from "react-i18next"; +import { Link as RouterLink } from "react-router-dom"; +import { useAuth } from "../../auth/contexts/AuthProvider"; + +const AchievementWidget = () => { + const { userInfo } = useAuth(); + const { t } = useTranslation(); + + return ( + + + + + + + {t("admin.home.achievement.title", { name: userInfo?.firstName })} + + + {t("admin.home.achievement.description", { + progress: userInfo?.progress, + })} + + + + + ); +}; + +export default AchievementWidget; diff --git a/src/admin/widgets/ActivityWidget.tsx b/src/admin/widgets/ActivityWidget.tsx new file mode 100644 index 0000000..afa5e2e --- /dev/null +++ b/src/admin/widgets/ActivityWidget.tsx @@ -0,0 +1,88 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import CardHeader from "@material-ui/core/CardHeader"; +import { useTheme } from "@material-ui/core/styles"; +import { useTranslation } from "react-i18next"; +import { Line, LineChart, ResponsiveContainer, Tooltip, XAxis } from "recharts"; + +const data = [ + { + name: "Jan", + pv: 2400, + }, + { + name: "Feb", + pv: 1398, + }, + { + name: "Mar", + pv: 9800, + }, + { + name: "Apr", + pv: 3908, + }, + { + name: "May", + pv: 4800, + }, + { + name: "Jun", + pv: 3800, + }, + { + name: "Jul", + pv: 4300, + }, +]; + +const ActivityWidget = () => { + const { t } = useTranslation(); + const theme = useTheme(); + + return ( + + + + + + + + + + + + + ); +}; + +export default ActivityWidget; diff --git a/src/admin/widgets/BudgetWidget.tsx b/src/admin/widgets/BudgetWidget.tsx new file mode 100644 index 0000000..ddf9eb3 --- /dev/null +++ b/src/admin/widgets/BudgetWidget.tsx @@ -0,0 +1,77 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import CardHeader from "@material-ui/core/CardHeader"; +import { useTheme } from "@material-ui/core/styles"; +import { useTranslation } from "react-i18next"; +import { + PolarAngleAxis, + Radar, + RadarChart, + ResponsiveContainer, + Tooltip, +} from "recharts"; + +const data = [ + { + subject: "Marketing", + A: 110, + }, + { + subject: "Research", + A: 98, + }, + { + subject: "Sales", + A: 86, + }, + { + subject: "Ops", + A: 99, + }, + { + subject: "HR", + A: 85, + }, + { + subject: "Dev", + A: 65, + }, +]; + +const BudgetWidget = () => { + const { t } = useTranslation(); + const theme = useTheme(); + + return ( + + + + + + + + + + + + + ); +}; + +export default BudgetWidget; diff --git a/src/admin/widgets/CircleProgressWidget.tsx b/src/admin/widgets/CircleProgressWidget.tsx new file mode 100644 index 0000000..5495988 --- /dev/null +++ b/src/admin/widgets/CircleProgressWidget.tsx @@ -0,0 +1,63 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import CardHeader from "@material-ui/core/CardHeader"; +import useTheme from "@material-ui/core/styles/useTheme"; +import { + PolarAngleAxis, + RadialBar, + RadialBarChart, + ResponsiveContainer, +} from "recharts"; + +type CircleProgressWidgetProps = { + height?: number; + title: string; + value: number; +}; + +const CircleProgressWidget = ({ + height = 120, + title, + value, +}: CircleProgressWidgetProps) => { + const theme = useTheme(); + + return ( + + + + + + + + + + + + ); +}; + +export default CircleProgressWidget; diff --git a/src/admin/widgets/DevicesWidget.tsx b/src/admin/widgets/DevicesWidget.tsx new file mode 100644 index 0000000..3142230 --- /dev/null +++ b/src/admin/widgets/DevicesWidget.tsx @@ -0,0 +1,82 @@ +import Avatar from "@material-ui/core/Avatar"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import CardHeader from "@material-ui/core/CardHeader"; +import IconButton from "@material-ui/core/IconButton"; +import List from "@material-ui/core/List"; +import ListItem from "@material-ui/core/ListItem"; +import ListItemAvatar from "@material-ui/core/ListItemAvatar"; +import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction"; +import ListItemText from "@material-ui/core/ListItemText"; +import { useTheme } from "@material-ui/core/styles"; +import ChevronRightIcon from "@material-ui/icons/ChevronRight"; +import DevicesOther from "@material-ui/icons/DevicesOther"; +import { useTranslation } from "react-i18next"; +import { Link as RouterLink } from "react-router-dom"; + +const devices = [ + { + id: "1", + firstName: "Rhys", + upConnector: "S3", + lastName: "Arriaga", + deviceType: "Sensor", + }, + { + id: "2", + firstName: "Laura", + upConnector: "Dynamo DB", + lastName: "Core", + deviceType: "Camera", + }, + { + id: "3", + firstName: "Joshua", + upConnector: "Blob Store", + lastName: "Jagger", + deviceType: "Camera", + }, +]; + +const DevicesWidget = () => { + const theme = useTheme(); + const { t } = useTranslation(); + + return ( + + + + + {devices.map((device) => ( + + + + + + + + + + + + + + ))} + + + + ); +}; + +export default DevicesWidget; diff --git a/src/admin/widgets/FollowersWidget.tsx b/src/admin/widgets/FollowersWidget.tsx new file mode 100644 index 0000000..e989521 --- /dev/null +++ b/src/admin/widgets/FollowersWidget.tsx @@ -0,0 +1,72 @@ +import Avatar from "@material-ui/core/Avatar"; +import Box from "@material-ui/core/Box"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import Typography from "@material-ui/core/Typography"; +import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown"; +import ArrowDropUpIcon from "@material-ui/icons/ArrowDropUp"; +import ArrowRightIcon from "@material-ui/icons/ArrowRight"; +import EmojiEmotionsIcon from "@material-ui/icons/EmojiEmotions"; +import FavoriteIcon from "@material-ui/icons/Favorite"; +import ThumbUpIcon from "@material-ui/icons/ThumbUp"; +import React from "react"; +import { useTranslation } from "react-i18next"; + +const socials = [ + { + bgcolor: "primary.main", + icon: , + name: "Likes", + trend: , + unitKey: "admin.home.followers.units.likes", + value: "26,789", + }, + { + bgcolor: "error.main", + icon: , + name: "Love", + trend: , + unitKey: "admin.home.followers.units.love", + value: "6,754", + }, + { + bgcolor: "warning.main", + icon: , + name: "Smiles", + trend: , + unitKey: "admin.home.followers.units.smiles", + value: "52,789", + }, +]; + +const FollowersWidget = () => { + const { t } = useTranslation(); + + return ( + + {socials.map((social) => ( + + + + {social.icon} + + + + {social.value} + + + {t(social.name)} + + + {social.trend} + + + ))} + + ); +}; + +export default FollowersWidget; diff --git a/src/admin/widgets/OverviewWidget.tsx b/src/admin/widgets/OverviewWidget.tsx new file mode 100644 index 0000000..e163cc1 --- /dev/null +++ b/src/admin/widgets/OverviewWidget.tsx @@ -0,0 +1,26 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import Typography from "@material-ui/core/Typography"; + +type OverviewWidgetProps = { + color?: "primary" | "warning" | "error"; + description: string; + title: string; +}; + +const OverviewWidget = ({ description, title }: OverviewWidgetProps) => { + return ( + + + + {title} + + + {description} + + + + ); +}; + +export default OverviewWidget; diff --git a/src/admin/widgets/PersonalTargetsWidget.tsx b/src/admin/widgets/PersonalTargetsWidget.tsx new file mode 100644 index 0000000..3bdd82d --- /dev/null +++ b/src/admin/widgets/PersonalTargetsWidget.tsx @@ -0,0 +1,61 @@ +import Box from "@material-ui/core/Box"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import CardHeader from "@material-ui/core/CardHeader"; +import LinearProgress from "@material-ui/core/LinearProgress"; +import List from "@material-ui/core/List"; +import ListItem from "@material-ui/core/ListItem"; +import ListItemText from "@material-ui/core/ListItemText"; +import Typography from "@material-ui/core/Typography"; +import { useTranslation } from "react-i18next"; + +const targets = [ + { name: "Views", nameKey: "admin.home.targets.views", value: 75 }, + { name: "Followers", nameKey: "admin.home.targets.followers", value: 50 }, + { name: "Income", nameKey: "admin.home.targets.income", value: 25 }, +]; + +const PersonalTargetsWidget = () => { + const { t } = useTranslation(); + + return ( + + + + + {targets.map((target) => ( + + + + + {t(target.nameKey)} + + + + {`${target.value}%`} + + + = 75 + ? "primary.main" + : target.value <= 25 + ? "error.main" + : "warning.main", + }} + color="inherit" + variant="determinate" + value={target.value} + /> + + + ))} + + + + ); +}; + +export default PersonalTargetsWidget; diff --git a/src/admin/widgets/ProgressWidget.tsx b/src/admin/widgets/ProgressWidget.tsx new file mode 100644 index 0000000..c84a184 --- /dev/null +++ b/src/admin/widgets/ProgressWidget.tsx @@ -0,0 +1,47 @@ +import Avatar from "@material-ui/core/Avatar"; +import Box from "@material-ui/core/Box"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import LinearProgress from "@material-ui/core/LinearProgress"; +import Typography from "@material-ui/core/Typography"; + +type ProgressWidgetProps = { + avatar: React.ReactNode; + mb?: number; + title: string; + value: number; +}; + +const ProgressWidget = ({ + avatar, + mb = 0, + title, + value, +}: ProgressWidgetProps) => { + return ( + + + {avatar} + + + + {title} + + + + {`${value}%`} + + + + + + + ); +}; + +export default ProgressWidget; diff --git a/src/admin/widgets/SalesByAgeWidget.tsx b/src/admin/widgets/SalesByAgeWidget.tsx new file mode 100644 index 0000000..5a8bcd7 --- /dev/null +++ b/src/admin/widgets/SalesByAgeWidget.tsx @@ -0,0 +1,80 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import CardHeader from "@material-ui/core/CardHeader"; +import { useTheme } from "@material-ui/core/styles"; +import { useTranslation } from "react-i18next"; +import { + Legend, + PolarAngleAxis, + RadialBar, + RadialBarChart, + ResponsiveContainer, +} from "recharts"; + +const SalesByAgeWidget = () => { + const { t } = useTranslation(); + const theme = useTheme(); + + const data = [ + { + name: "18-39", + uv: 30, + fill: theme.palette.text.secondary, + }, + { + name: "40-59", + uv: 45, + fill: theme.palette.error.main, + }, + { + name: "60-79", + uv: 60, + fill: theme.palette.warning.main, + }, + { + name: "80+", + uv: 75, + fill: theme.palette.primary.main, + }, + ]; + + return ( + + + + + + + + + + + + + ); +}; + +export default SalesByAgeWidget; diff --git a/src/admin/widgets/SalesByCategoryWidget.tsx b/src/admin/widgets/SalesByCategoryWidget.tsx new file mode 100644 index 0000000..2487c5b --- /dev/null +++ b/src/admin/widgets/SalesByCategoryWidget.tsx @@ -0,0 +1,66 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import CardHeader from "@material-ui/core/CardHeader"; +import { useTheme } from "@material-ui/core/styles"; +import { useTranslation } from "react-i18next"; +import { Legend, Pie, PieChart, ResponsiveContainer, Tooltip } from "recharts"; + +const SalesByCategoryWidget = () => { + const { t } = useTranslation(); + const theme = useTheme(); + + const data = [ + { + name: t("dashboard.salesByCategory.legend.books"), + fill: theme.palette.primary.main, + value: 400, + }, + { + name: t("dashboard.salesByCategory.legend.movies"), + fill: theme.palette.warning.main, + value: 300, + }, + { + name: t("dashboard.salesByCategory.legend.software"), + fill: theme.palette.error.main, + value: 300, + }, + ]; + + return ( + + + + + + + + + + + + + + ); +}; + +export default SalesByCategoryWidget; diff --git a/src/admin/widgets/SalesHistoryWidget.tsx b/src/admin/widgets/SalesHistoryWidget.tsx new file mode 100644 index 0000000..bc36d34 --- /dev/null +++ b/src/admin/widgets/SalesHistoryWidget.tsx @@ -0,0 +1,83 @@ +import Box from "@material-ui/core/Box"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import CardHeader from "@material-ui/core/CardHeader"; +import { useTheme } from "@material-ui/core/styles"; +import Typography from "@material-ui/core/Typography"; +import TrendingUpIcon from "@material-ui/icons/TrendingUp"; +import { useTranslation } from "react-i18next"; +import { Bar, BarChart, ResponsiveContainer } from "recharts"; + +type SalesWidgetProps = { + value: number; +}; + +const SalesWidget = ({ value }: SalesWidgetProps) => { + const { t } = useTranslation(); + const theme = useTheme(); + + const data = [ + { + name: "Mon", + uv: 4000, + }, + { + name: "Tue", + uv: 3000, + }, + { + name: "Wed", + uv: 2000, + }, + { + name: "Thu", + uv: 2780, + }, + { + name: "Fri", + uv: 1890, + }, + { + name: "Sat", + uv: 2390, + }, + ]; + + return ( + + + + + + + + + + + + {value} + + + {t("dashboard.salesHistory.unit")} + + + + + + + ); +}; + +export default SalesWidget; diff --git a/src/admin/widgets/TeamProgressWidget.tsx b/src/admin/widgets/TeamProgressWidget.tsx new file mode 100644 index 0000000..49f5a45 --- /dev/null +++ b/src/admin/widgets/TeamProgressWidget.tsx @@ -0,0 +1,111 @@ +import Box from "@material-ui/core/Box"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import CardHeader from "@material-ui/core/CardHeader"; +import LinearProgress from "@material-ui/core/LinearProgress"; +import Table from "@material-ui/core/Table"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; +import TableContainer from "@material-ui/core/TableContainer"; +import TableHead from "@material-ui/core/TableHead"; +import TableRow from "@material-ui/core/TableRow"; +import Typography from "@material-ui/core/Typography"; +import { useTranslation } from "react-i18next"; + +const teams = [ + { + id: "1", + color: "primary.main", + name: "Marketing Team", + progress: 75, + value: 122, + }, + { + id: "2", + color: "warning.main", + name: "Operations Team", + progress: 50, + value: 82, + }, + { + id: "3", + color: "error.main", + name: "Sales Team", + progress: 25, + value: 39, + }, + { + id: "4", + color: "text.secondary", + name: "Research Team", + progress: 10, + value: 9, + }, +]; + +const TeamProgressWidget = () => { + const { t } = useTranslation(); + + return ( + + + + + + + + {t("dashboard.teams.columns.team")} + {t("dashboard.teams.columns.progress")} + + {t("dashboard.teams.columns.value")} + + + + + {teams.map((team) => ( + + + + {team.name} + + + + + + + + + {`${team.progress}%`} + + + + {team.value} + + ))} + +
+
+
+
+ ); +}; + +export default TeamProgressWidget; diff --git a/src/admin/widgets/ViewsWidget.tsx b/src/admin/widgets/ViewsWidget.tsx new file mode 100644 index 0000000..581aaeb --- /dev/null +++ b/src/admin/widgets/ViewsWidget.tsx @@ -0,0 +1,117 @@ +import Avatar from "@material-ui/core/Avatar"; +import Box from "@material-ui/core/Box"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import IconButton from "@material-ui/core/IconButton"; +import useTheme from "@material-ui/core/styles/useTheme"; +import Typography from "@material-ui/core/Typography"; +import ChevronRightIcon from "@material-ui/icons/ChevronRight"; +import DashboardIcon from "@material-ui/icons/Dashboard"; +import { useTranslation } from "react-i18next"; +import { Link as RouterLink } from "react-router-dom"; +import { Area, AreaChart, ResponsiveContainer, Tooltip, XAxis } from "recharts"; + +const data = [ + { + name: "Jan", + fb: 2.5, + }, + { + name: "Feb", + fb: 1.4, + }, + { + name: "Mar", + fb: 6, + }, + { + name: "Avr", + fb: 4, + }, +]; + +const views = "6.967.431"; + +const ViewsWidget = () => { + const theme = useTheme(); + const { t } = useTranslation(); + + return ( + + + + {t("admin.home.views.unit")} + + + {views} + + + + + + + + + + + + + + + + + + {t("admin.home.views.action")} + + + + + + + + + + ); +}; + +export default ViewsWidget; diff --git a/src/admin/widgets/WelcomeWidget.tsx b/src/admin/widgets/WelcomeWidget.tsx new file mode 100644 index 0000000..29f5fd6 --- /dev/null +++ b/src/admin/widgets/WelcomeWidget.tsx @@ -0,0 +1,43 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import Typography from "@material-ui/core/Typography"; +import { useTranslation } from "react-i18next"; +import { useAuth } from "../../auth/contexts/AuthProvider"; +import { ReactComponent as WelcomeSvg } from "../../core/assets/welcome.svg"; +import SvgContainer from "../../core/components/SvgContainer"; + +const WelcomeWidget = () => { + const { userInfo } = useAuth(); + const { t } = useTranslation(); + + return ( + + + + {t("admin.home.welcome.title", { name: userInfo?.firstName })} + + + {t("admin.home.welcome.subTitle")} + + + {t("admin.home.welcome.message")} + + {/* + + */} + + + ); +}; + +export default WelcomeWidget; diff --git a/src/auth/contexts/AuthProvider.tsx b/src/auth/contexts/AuthProvider.tsx new file mode 100644 index 0000000..e52c330 --- /dev/null +++ b/src/auth/contexts/AuthProvider.tsx @@ -0,0 +1,82 @@ +import React, { createContext, useContext } from "react"; +import { useLocalStorage } from "../../core/hooks/useLocalStorage"; +import { useLogin } from "../hooks/useLogin"; +import { useLogout } from "../hooks/useLogout"; +import { useUserInfo } from "../hooks/useUserInfo"; +import { UserInfo } from "../types/userInfo"; + +interface AuthContextInterface { + hasRole: (roles?: string[]) => {}; + isLoggingIn: boolean; + isLoggingOut: boolean; + login: (email: string, password: string) => Promise; + logout: () => Promise; + userInfo?: UserInfo; +} + +export const AuthContext = createContext({} as AuthContextInterface); + +type AuthProviderProps = { + children?: React.ReactNode; +}; + +const AuthProvider = ({ children }: AuthProviderProps) => { + const [authKey, setAuthKey] = useLocalStorage("authkey", ""); + + const { isLoggingIn, login } = useLogin(); + const { isLoggingOut, logout } = useLogout(); + const { data: userInfo } = useUserInfo(authKey); + + const hasRole = (roles?: string[]) => { + if (!roles || roles.length === 0) { + return true; + } + if (!userInfo) { + return false; + } + return roles.includes(userInfo.role); + }; + + const handleLogin = async (email: string, password: string) => { + return login({ email, password }) + .then((key: string) => { + setAuthKey(key); + return key; + }) + .catch((err) => { + throw err; + }); + }; + + const handleLogout = async () => { + return logout() + .then((data) => { + setAuthKey(""); + return data; + }) + .catch((err) => { + throw err; + }); + }; + + return ( + + {children} + + ); +}; + +export function useAuth() { + return useContext(AuthContext); +} + +export default AuthProvider; diff --git a/src/auth/hooks/useForgotPassword.ts b/src/auth/hooks/useForgotPassword.ts new file mode 100644 index 0000000..9916dee --- /dev/null +++ b/src/auth/hooks/useForgotPassword.ts @@ -0,0 +1,12 @@ +import axios from "axios"; +import { useMutation } from "react-query"; + +const forgotPassword = async ({ email }: { email: string }) => { + const { data } = await axios.post("/api/forgot-password", { email }); + return data; +}; + +export function useForgotPassword() { + const { isLoading, mutateAsync } = useMutation(forgotPassword); + return { isLoading, forgotPassword: mutateAsync }; +} diff --git a/src/auth/hooks/useForgotPasswordSubmit.ts b/src/auth/hooks/useForgotPasswordSubmit.ts new file mode 100644 index 0000000..c87bced --- /dev/null +++ b/src/auth/hooks/useForgotPasswordSubmit.ts @@ -0,0 +1,21 @@ +import axios from "axios"; +import { useMutation } from "react-query"; + +const forgotPasswordSubmit = async ({ + code, + newPassword, +}: { + code: string; + newPassword: string; +}) => { + const { data } = await axios.post("/api/forgot-password-submit", { + code, + newPassword, + }); + return data; +}; + +export function useForgotPasswordSubmit() { + const { isLoading, mutateAsync } = useMutation(forgotPasswordSubmit); + return { isLoading, forgotPasswordSubmit: mutateAsync }; +} diff --git a/src/auth/hooks/useLogin.ts b/src/auth/hooks/useLogin.ts new file mode 100644 index 0000000..3afa156 --- /dev/null +++ b/src/auth/hooks/useLogin.ts @@ -0,0 +1,19 @@ +import axios from "axios"; +import { useMutation } from "react-query"; + +const login = async ({ + email, + password, +}: { + email: string; + password: string; +}): Promise => { + const { data } = await axios.post("/api/login", { email, password }); + return data; +}; + +export function useLogin() { + const { isLoading, mutateAsync } = useMutation(login); + + return { isLoggingIn: isLoading, login: mutateAsync }; +} diff --git a/src/auth/hooks/useLogout.ts b/src/auth/hooks/useLogout.ts new file mode 100644 index 0000000..e81f423 --- /dev/null +++ b/src/auth/hooks/useLogout.ts @@ -0,0 +1,13 @@ +import axios from "axios"; +import { useMutation } from "react-query"; + +const logout = async (): Promise => { + const { data } = await axios.post("/api/logout"); + return data; +}; + +export function useLogout() { + const { isLoading, mutateAsync } = useMutation(logout); + + return { isLoggingOut: isLoading, logout: mutateAsync }; +} diff --git a/src/auth/hooks/useRegister.ts b/src/auth/hooks/useRegister.ts new file mode 100644 index 0000000..82e6883 --- /dev/null +++ b/src/auth/hooks/useRegister.ts @@ -0,0 +1,13 @@ +import axios from "axios"; +import { useMutation } from "react-query"; +import { UserInfo } from "../types/userInfo"; + +const register = async (userInfo: UserInfo): Promise => { + const { data } = await axios.post("/api/register", userInfo); + return data; +}; + +export function useRegister() { + const { isLoading, mutateAsync } = useMutation(register); + return { isRegistering: isLoading, register: mutateAsync }; +} diff --git a/src/auth/hooks/useUpdatePassword.ts b/src/auth/hooks/useUpdatePassword.ts new file mode 100644 index 0000000..04e3fbe --- /dev/null +++ b/src/auth/hooks/useUpdatePassword.ts @@ -0,0 +1,21 @@ +import axios from "axios"; +import { useMutation } from "react-query"; + +const updatePassword = async ({ + oldPassword, + newPassword, +}: { + oldPassword: string; + newPassword: string; +}) => { + const { data } = await axios.put("/api/password", { + oldPassword, + newPassword, + }); + return data; +}; + +export function useUpdatePassword() { + const { isLoading, mutateAsync } = useMutation(updatePassword); + return { isUpdating: isLoading, updatePassword: mutateAsync }; +} diff --git a/src/auth/hooks/useUserInfo.ts b/src/auth/hooks/useUserInfo.ts new file mode 100644 index 0000000..d886423 --- /dev/null +++ b/src/auth/hooks/useUserInfo.ts @@ -0,0 +1,14 @@ +import axios from "axios"; +import { useQuery } from "react-query"; +import { UserInfo } from "../types/userInfo"; + +const fetchUserInfo = async (key?: string): Promise => { + const { data } = await axios.get("/api/user-info", { params: { key } }); + return data; +}; + +export function useUserInfo(key?: string) { + return useQuery(["user-info", key], () => fetchUserInfo(key), { + enabled: !!key, + }); +} diff --git a/src/auth/pages/ForgotPassword.tsx b/src/auth/pages/ForgotPassword.tsx new file mode 100644 index 0000000..690e74b --- /dev/null +++ b/src/auth/pages/ForgotPassword.tsx @@ -0,0 +1,96 @@ +import Box from "@material-ui/core/Box"; +import Button from "@material-ui/core/Button"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import LoadingButton from "@material-ui/lab/LoadingButton"; +import { useFormik } from "formik"; +import { useTranslation } from "react-i18next"; +import { Link as RouterLink, useNavigate } from "react-router-dom"; +import * as Yup from "yup"; +import BoxedLayout from "../../core/components/BoxedLayout"; +import { useSnackbar } from "../../core/contexts/SnackbarProvider"; +import { useForgotPassword } from "../hooks/useForgotPassword"; + +const ForgotPassword = () => { + const navigate = useNavigate(); + const snackbar = useSnackbar(); + const { t } = useTranslation(); + + const { forgotPassword, isLoading } = useForgotPassword(); + + const formik = useFormik({ + initialValues: { + email: "", + }, + validationSchema: Yup.object({ + email: Yup.string() + .email(t("common.validations.email")) + .required(t("common.validations.required")), + }), + onSubmit: ({ email }) => handleForgotPassword(email), + }); + + const handleForgotPassword = async (email: string) => { + forgotPassword({ email }) + .then(() => { + snackbar.success(t("auth.forgotPassword.notifications.success")); + navigate(`/${process.env.PUBLIC_URL}/forgot-password-submit`); + }) + .catch(() => { + snackbar.error(t("common.errors.unexpected.subTitle")); + }); + }; + + return ( + + + {t("auth.forgotPassword.title")} + + {t("auth.forgotPassword.subTitle")} + + + + {t("auth.forgotPassword.form.action")} + + + + + ); +}; + +export default ForgotPassword; diff --git a/src/auth/pages/ForgotPasswordSubmit.tsx b/src/auth/pages/ForgotPasswordSubmit.tsx new file mode 100644 index 0000000..90783fe --- /dev/null +++ b/src/auth/pages/ForgotPasswordSubmit.tsx @@ -0,0 +1,135 @@ +import Box from "@material-ui/core/Box"; +import Button from "@material-ui/core/Button"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import LoadingButton from "@material-ui/lab/LoadingButton"; +import { useFormik } from "formik"; +import { useTranslation } from "react-i18next"; +import { Link, useNavigate } from "react-router-dom"; +import * as Yup from "yup"; +import BoxedLayout from "../../core/components/BoxedLayout"; +import { useSnackbar } from "../../core/contexts/SnackbarProvider"; +import { useForgotPasswordSubmit } from "../hooks/useForgotPasswordSubmit"; + +const ForgotPasswordSubmit = () => { + const navigate = useNavigate(); + const snackbar = useSnackbar(); + const { t } = useTranslation(); + + const { forgotPasswordSubmit, isLoading } = useForgotPasswordSubmit(); + + const formik = useFormik({ + initialValues: { + code: "", + newPassword: "", + confirmPassword: "", + }, + validationSchema: Yup.object({ + code: Yup.string().required(t("common.validations.required")), + newPassword: Yup.string().required(t("common.validations.required")), + confirmPassword: Yup.string().required(t("common.validations.required")), + }), + onSubmit: ({ code, newPassword }) => + handleSubmitPassword(code, newPassword), + }); + + const handleSubmitPassword = async (code: string, newPassword: string) => { + forgotPasswordSubmit({ code, newPassword }) + .then(() => { + snackbar.success(t("auth.forgotPasswordSubmit.notifications.success")); + navigate(`/${process.env.PUBLIC_URL}/login`); + }) + .catch(() => { + snackbar.error(t("common.errors.unexpected.subTitle")); + }); + }; + + return ( + + + {t("auth.forgotPasswordSubmit.title")} + + + {t("auth.forgotPasswordSubmit.subTitle")} + + + + + + + {t("auth.forgotPasswordSubmit.form.action")} + + + + + ); +}; + +export default ForgotPasswordSubmit; diff --git a/src/auth/pages/Login.tsx b/src/auth/pages/Login.tsx new file mode 100644 index 0000000..aee53bf --- /dev/null +++ b/src/auth/pages/Login.tsx @@ -0,0 +1,122 @@ +import Box from "@material-ui/core/Box"; +import Button from "@material-ui/core/Button"; +import Grid from "@material-ui/core/Grid"; +import Link from "@material-ui/core/Link"; +import Paper from "@material-ui/core/Paper"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import LoadingButton from "@material-ui/lab/LoadingButton"; +import { useFormik } from "formik"; +import { useTranslation } from "react-i18next"; +import { Link as RouterLink, useNavigate } from "react-router-dom"; +import * as Yup from "yup"; +import BoxedLayout from "../../core/components/BoxedLayout"; +import { useSnackbar } from "../../core/contexts/SnackbarProvider"; +import { useAuth } from "../contexts/AuthProvider"; + +const Login = () => { + const { isLoggingIn, login } = useAuth(); + const navigate = useNavigate(); + const snackbar = useSnackbar(); + const { t } = useTranslation(); + + const handleLogin = (email: string, password: string) => { + login(email, password) + .then(() => + navigate(`/${process.env.PUBLIC_URL}/admin`, { replace: true }) + ) + .catch(() => snackbar.error(t("common.errors.unexpected.subTitle"))); + }; + + const formik = useFormik({ + initialValues: { + email: "demo@example.com", + password: "guWEK<'r/-47-XG3", + }, + validationSchema: Yup.object({ + email: Yup.string() + .email(t("common.validations.email")) + .required(t("common.validations.required")), + password: Yup.string() + .min(8, t("common.validations.min", { size: 8 })) + .required(t("common.validations.required")), + }), + onSubmit: (values) => handleLogin(values.email, values.password), + }); + + return ( + + + {t("auth.login.title")} + + + + + + + {t("auth.login.forgotPasswordLink")} + + + + {t("auth.login.submit")} + + + + + ); +}; + +export default Login; diff --git a/src/auth/pages/Register.tsx b/src/auth/pages/Register.tsx new file mode 100644 index 0000000..f1e49e0 --- /dev/null +++ b/src/auth/pages/Register.tsx @@ -0,0 +1,166 @@ +import Box from "@material-ui/core/Box"; +import Button from "@material-ui/core/Button"; +import FormControl from "@material-ui/core/FormControl"; +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import FormLabel from "@material-ui/core/FormLabel"; +import Radio from "@material-ui/core/Radio"; +import RadioGroup from "@material-ui/core/RadioGroup"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import LoadingButton from "@material-ui/lab/LoadingButton"; +import { useFormik } from "formik"; +import { useTranslation } from "react-i18next"; +import { Link, useNavigate } from "react-router-dom"; +import * as Yup from "yup"; +import BoxedLayout from "../../core/components/BoxedLayout"; +import { useSnackbar } from "../../core/contexts/SnackbarProvider"; +import { useRegister } from "../hooks/useRegister"; +import { UserInfo } from "../types/userInfo"; + +const jobTitles = [ + { label: "auth.register.form.jobTitle.options.e", value: "Engineer" }, + { label: "auth.register.form.jobTitle.options.d", value: "Developer" }, + { label: "auth.register.form.jobTitle.options.o", value: "Other" }, +]; + +const Register = () => { + const navigate = useNavigate(); + const snackbar = useSnackbar(); + const { t } = useTranslation(); + + const { isRegistering, register } = useRegister(); + + const formik = useFormik({ + initialValues: { + email: "", + firstName: "", + jobTitle: "F", + lastName: "", + }, + validationSchema: Yup.object({ + email: Yup.string() + .email("Invalid email address") + .required(t("common.validations.required")), + firstName: Yup.string() + .max(20, t("common.validations.max", { size: 20 })) + .required(t("common.validations.required")), + lastName: Yup.string() + .max(30, t("common.validations.max", { size: 30 })) + .required(t("common.validations.required")), + }), + onSubmit: (values) => handleRegister(values), + }); + + const handleRegister = async (values: Partial) => { + register(values as UserInfo) + .then(() => { + snackbar.success(t("auth.register.notifications.success")); + navigate(`/${process.env.PUBLIC_URL}/login`); + }) + .catch(() => { + snackbar.error(t("common.errors.unexpected.subTitle")); + }); + }; + + return ( + + + {t("auth.register.title")} + + + + + + + {t("auth.register.form.jobTitle.label")} + + + {jobTitles.map((jobTitle) => ( + } + key={jobTitle.value} + disabled={isRegistering} + label={t(jobTitle.label)} + value={jobTitle.value} + /> + ))} + + + + + {t("auth.register.submit")} + + + + + ); +}; + +export default Register; diff --git a/src/auth/types/userInfo.ts b/src/auth/types/userInfo.ts new file mode 100644 index 0000000..3791a66 --- /dev/null +++ b/src/auth/types/userInfo.ts @@ -0,0 +1,10 @@ +export interface UserInfo { + id: string; + avatar?: string; + email: string; + firstName: string; + job: string; + lastName: string; + progress: number; + role: string; +} diff --git a/src/core/assets/403.svg b/src/core/assets/403.svg new file mode 100644 index 0000000..19c34aa --- /dev/null +++ b/src/core/assets/403.svg @@ -0,0 +1 @@ +#19 security \ No newline at end of file diff --git a/src/core/assets/404.svg b/src/core/assets/404.svg new file mode 100644 index 0000000..125f52d --- /dev/null +++ b/src/core/assets/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/core/assets/confirm.svg b/src/core/assets/confirm.svg new file mode 100644 index 0000000..0982499 --- /dev/null +++ b/src/core/assets/confirm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/core/assets/constructions.svg b/src/core/assets/constructions.svg new file mode 100644 index 0000000..6c9278f --- /dev/null +++ b/src/core/assets/constructions.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/core/assets/empty.svg b/src/core/assets/empty.svg new file mode 100644 index 0000000..e407734 --- /dev/null +++ b/src/core/assets/empty.svg @@ -0,0 +1 @@ +#18 search engine \ No newline at end of file diff --git a/src/core/assets/error.svg b/src/core/assets/error.svg new file mode 100644 index 0000000..326e3f4 --- /dev/null +++ b/src/core/assets/error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/core/assets/findr_ash.svg b/src/core/assets/findr_ash.svg new file mode 100644 index 0000000..e34190f --- /dev/null +++ b/src/core/assets/findr_ash.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core/assets/findr_cinder.svg b/src/core/assets/findr_cinder.svg new file mode 100644 index 0000000..cf5c2c9 --- /dev/null +++ b/src/core/assets/findr_cinder.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core/assets/findr_red.svg b/src/core/assets/findr_red.svg new file mode 100644 index 0000000..b5c6ea5 --- /dev/null +++ b/src/core/assets/findr_red.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core/assets/findr_red_blocky.svg b/src/core/assets/findr_red_blocky.svg new file mode 100644 index 0000000..2e4b29c --- /dev/null +++ b/src/core/assets/findr_red_blocky.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/core/assets/help.svg b/src/core/assets/help.svg new file mode 100644 index 0000000..4f0209d --- /dev/null +++ b/src/core/assets/help.svg @@ -0,0 +1,5 @@ + + + + +#24 questions \ No newline at end of file diff --git a/src/core/assets/logo.svg b/src/core/assets/logo.svg new file mode 100644 index 0000000..99516cd --- /dev/null +++ b/src/core/assets/logo.svg @@ -0,0 +1,11 @@ + + + + + diff --git a/src/core/assets/success.svg b/src/core/assets/success.svg new file mode 100644 index 0000000..b7356fa --- /dev/null +++ b/src/core/assets/success.svg @@ -0,0 +1 @@ +_ \ No newline at end of file diff --git a/src/core/assets/welcome.svg b/src/core/assets/welcome.svg new file mode 100644 index 0000000..9b716e0 --- /dev/null +++ b/src/core/assets/welcome.svg @@ -0,0 +1 @@ +#111_report analysis_twocolour \ No newline at end of file diff --git a/src/core/components/BoxedLayout.tsx b/src/core/components/BoxedLayout.tsx new file mode 100644 index 0000000..93e3de2 --- /dev/null +++ b/src/core/components/BoxedLayout.tsx @@ -0,0 +1,64 @@ +import AppBar from "@material-ui/core/AppBar"; +import Box from "@material-ui/core/Box"; +import Container from "@material-ui/core/Container"; +import GlobalStyles from "@material-ui/core/GlobalStyles"; +import IconButton from "@material-ui/core/IconButton"; +import useTheme from "@material-ui/core/styles/useTheme"; +import Toolbar from "@material-ui/core/Toolbar"; +import SettingsIcon from "@material-ui/icons/Settings"; +import React, { useState } from "react"; +import Logo from "./Logo"; +import SettingsDrawer from "./SettingsDrawer"; + +type BoxedLayoutProps = { + children: React.ReactNode; +}; + +const BoxedLayout = ({ children }: BoxedLayoutProps) => { + const theme = useTheme(); + const [settingsOpen, setSettingsOpen] = useState(false); + + const handleSettingsToggle = () => { + setSettingsOpen(!settingsOpen); + }; + + return ( + + + + + + + + + + + + + + {children} + + + + + + + ); +}; + +export default BoxedLayout; diff --git a/src/core/components/ConfirmDialog.tsx b/src/core/components/ConfirmDialog.tsx new file mode 100644 index 0000000..e70ab2d --- /dev/null +++ b/src/core/components/ConfirmDialog.tsx @@ -0,0 +1,66 @@ +import Button from "@material-ui/core/Button"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogContentText from "@material-ui/core/DialogContentText"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import LoadingButton from "@material-ui/lab/LoadingButton"; +import { useTranslation } from "react-i18next"; +import { ReactComponent as ConfirmSvg } from "../assets/confirm.svg"; +import SvgContainer from "./SvgContainer"; + +type ConfirmDialogProps = { + description?: string; + onClose: () => void; + onConfirm: () => void; + open: boolean; + pending: boolean; + title: string; +}; + +const ConfirmDialog = ({ + description, + onClose, + onConfirm, + open, + pending, + title, +}: ConfirmDialogProps) => { + const { t } = useTranslation(); + + return ( + + + + + + + {title} + + {description && ( + + {description} + + )} + + + + + {t("common.confirm")} + + + + ); +}; + +export default ConfirmDialog; diff --git a/src/core/components/Empty.tsx b/src/core/components/Empty.tsx new file mode 100644 index 0000000..18fe0f6 --- /dev/null +++ b/src/core/components/Empty.tsx @@ -0,0 +1,13 @@ +import { ReactComponent as EmptySvg } from "../assets/empty.svg"; +import Result from "./Result"; + +type EmptyProps = { + message?: string; + title: string; +}; + +const Empty = ({ message, title }: EmptyProps) => { + return } subTitle={message} title={title} />; +}; + +export default Empty; diff --git a/src/core/components/Footer.tsx b/src/core/components/Footer.tsx new file mode 100644 index 0000000..b5b55a5 --- /dev/null +++ b/src/core/components/Footer.tsx @@ -0,0 +1,25 @@ +import Box from "@material-ui/core/Box"; +import Link from "@material-ui/core/Link"; +import Typography from "@material-ui/core/Typography"; +import { Link as RouterLink } from "react-router-dom"; + +const Footer = () => { + return ( + + + {"© "} + + {process.env.REACT_APP_NAME} + {" "} + {new Date().getFullYear()} + {"."} + + + ); +}; + +export default Footer; diff --git a/src/core/components/Loader.tsx b/src/core/components/Loader.tsx new file mode 100644 index 0000000..1505c58 --- /dev/null +++ b/src/core/components/Loader.tsx @@ -0,0 +1,31 @@ +import { useTheme } from "@material-ui/core/styles"; +import Logo from "./Logo"; + +const Loader = () => { + const theme = useTheme(); + return ( + + ); +}; + +export default Loader; diff --git a/src/core/components/Logo.tsx b/src/core/components/Logo.tsx new file mode 100644 index 0000000..57d4d07 --- /dev/null +++ b/src/core/components/Logo.tsx @@ -0,0 +1,17 @@ +import Box, { BoxProps } from "@material-ui/core/Box"; +import { ReactComponent as LogoSvg } from "../assets/findr_red_blocky.svg"; + +type LogoProps = { + colored?: boolean; + size?: number; +} & BoxProps; + +const Logo = ({ colored = false, size = 300, ...boxProps }: LogoProps) => { + return ( + + + + ); +}; + +export default Logo; diff --git a/src/core/components/PrivateRoute.tsx b/src/core/components/PrivateRoute.tsx new file mode 100644 index 0000000..46517f5 --- /dev/null +++ b/src/core/components/PrivateRoute.tsx @@ -0,0 +1,25 @@ +import { Navigate, Route, RouteProps } from "react-router"; +import { useAuth } from "../../auth/contexts/AuthProvider"; + +type PrivateRouteProps = { + roles?: string[]; +} & RouteProps; + +const PrivateRoute = ({ + children, + roles, + ...routeProps +}: PrivateRouteProps) => { + const { hasRole, userInfo } = useAuth(); + + if (userInfo) { + if (!hasRole(roles)) { + return ; + } + return ; + } else { + return ; + } +}; + +export default PrivateRoute; diff --git a/src/core/components/QueryWrapper.tsx b/src/core/components/QueryWrapper.tsx new file mode 100644 index 0000000..c7b6095 --- /dev/null +++ b/src/core/components/QueryWrapper.tsx @@ -0,0 +1,38 @@ +import Button from "@material-ui/core/Button"; +import React from "react"; +import { ErrorBoundary } from "react-error-boundary"; +import { useTranslation } from "react-i18next"; +import { useQueryErrorResetBoundary } from "react-query"; +import Loader from "./Loader"; +import Result from "./Result"; + +type QueryWrapperProps = { + children: React.ReactNode; +}; + +const QueryWrapper = ({ children }: QueryWrapperProps) => { + const { reset } = useQueryErrorResetBoundary(); + const { t } = useTranslation(); + + return ( + ( + resetErrorBoundary()} variant="contained"> + {t("common.retry")} + + } + status="error" + subTitle={t("common.errors.unexpected.subTitle")} + title={t("common.errors.unexpected.title")} + /> + )} + > + }>{children} + + ); +}; + +export default QueryWrapper; diff --git a/src/core/components/Result.tsx b/src/core/components/Result.tsx new file mode 100644 index 0000000..53d068a --- /dev/null +++ b/src/core/components/Result.tsx @@ -0,0 +1,61 @@ +import Box from "@material-ui/core/Box"; +import Container from "@material-ui/core/Container"; +import Typography from "@material-ui/core/Typography"; +import React from "react"; +import { ReactComponent as ErrorSvg } from "../assets/error.svg"; +import { ReactComponent as SuccessSvg } from "../assets/success.svg"; +import SvgContainer from "./SvgContainer"; + +type ResultImageProps = { + customImage?: React.ReactNode; + status?: "error" | "success"; +}; + +const ResultImage = ({ customImage, status }: ResultImageProps) => { + let image = customImage; + + if (!image) { + if (status === "error") { + image = ; + } else if (status === "success") { + image = ; + } + } + + return image ? {image} : null; +}; + +type ResultProps = { + extra?: React.ReactNode; + image?: React.ReactNode; + maxWidth?: "xs" | "sm"; + status?: "error" | "success"; + subTitle?: string; + title: string; +}; + +const Result = ({ + extra, + image, + maxWidth = "xs", + status, + subTitle, + title, +}: ResultProps) => { + return ( + + + + + + + {title} + + {subTitle && {subTitle}} + {extra && {extra}} + + + ); +}; + +export default Result; diff --git a/src/core/components/SelectToolbar.tsx b/src/core/components/SelectToolbar.tsx new file mode 100644 index 0000000..99622fa --- /dev/null +++ b/src/core/components/SelectToolbar.tsx @@ -0,0 +1,49 @@ +import Box from "@material-ui/core/Box"; +import Fab from "@material-ui/core/Fab"; +import Toolbar from "@material-ui/core/Toolbar"; +import Tooltip from "@material-ui/core/Tooltip"; +import CloseIcon from "@material-ui/icons/Close"; +import DeleteIcon from "@material-ui/icons/Delete"; +import { useTranslation } from "react-i18next"; + +interface SelectToolbarProps { + onCancel: () => void; + onDelete: (deviceIds: string[]) => void; + processing: boolean; + selected: string[]; +} + +const SelectToolbar = ({ + onCancel, + onDelete, + processing, + selected, +}: SelectToolbarProps) => { + const { t } = useTranslation(); + + const numSelected = selected.length; + + return ( + + + + {numSelected} {t("common.selected")} + + + + {numSelected > 0 && ( + + onDelete(selected)} + > + + + + )} + + ); +}; + +export default SelectToolbar; diff --git a/src/core/components/SettingsDrawer.tsx b/src/core/components/SettingsDrawer.tsx new file mode 100644 index 0000000..fc441d9 --- /dev/null +++ b/src/core/components/SettingsDrawer.tsx @@ -0,0 +1,162 @@ +import Box from "@material-ui/core/Box"; +import Drawer from "@material-ui/core/Drawer"; +import FormControl from "@material-ui/core/FormControl"; +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import IconButton from "@material-ui/core/IconButton"; +import Radio from "@material-ui/core/Radio"; +import RadioGroup from "@material-ui/core/RadioGroup"; +import ToggleButton from "@material-ui/core/ToggleButton"; +import ToggleButtonGroup from "@material-ui/core/ToggleButtonGroup"; +import Typography from "@material-ui/core/Typography"; +import CloseIcon from "@material-ui/icons/Close"; +import { useTranslation } from "react-i18next"; +import { drawerWidth } from "../config/layout"; +import { useSettings } from "../contexts/SettingsProvider"; + +type SettingsDrawerProps = { + onDrawerToggle: () => void; + open: boolean; +}; + +const SettingsDrawer = ({ onDrawerToggle, open }: SettingsDrawerProps) => { + const { + changeCollapsed, + changeDirection, + changeMode, + collapsed, + direction, + mode, + } = useSettings(); + const { i18n, t } = useTranslation(); + + const handleDirectionChange = (_: any, direction: "ltr" | "rtl") => { + changeDirection(direction); + }; + + const handleLanguageChange = (event: React.ChangeEvent) => { + i18n.changeLanguage((event.target as HTMLInputElement).value); + }; + + const handleModeChange = (_: any, mode: string) => { + changeMode(mode); + }; + + const handleSidebarChange = (_: any, collapsed: boolean) => { + changeCollapsed(collapsed); + }; + + return ( + + + {t("settings.drawer.title")} + + + + + + + {t("settings.drawer.language.label")} + + + + } + label={t("settings.drawer.language.options.en")} + /> + } + label={t("settings.drawer.language.options.fr")} + /> + + + + {t("settings.drawer.mode.label")} + + + + {t("settings.drawer.mode.options.light")} + + + {t("settings.drawer.mode.options.dark")} + + + + {t("settings.drawer.direction.label")} + + + + {t("settings.drawer.direction.options.ltr")} + + + {t("settings.drawer.direction.options.rtl")} + + + + {t("settings.drawer.sidebar.label")} + + + + {t("settings.drawer.sidebar.options.collapsed")} + + + {t("settings.drawer.sidebar.options.full")} + + + + + ); +}; + +export default SettingsDrawer; diff --git a/src/core/components/SvgContainer.tsx b/src/core/components/SvgContainer.tsx new file mode 100644 index 0000000..268b864 --- /dev/null +++ b/src/core/components/SvgContainer.tsx @@ -0,0 +1,28 @@ +import Box from "@material-ui/core/Box"; +import { useTheme } from "@material-ui/core/styles"; +import React from "react"; + +type SvgContainerProps = { + children: React.ReactNode; +}; + +const SvgContainer = ({ children }: SvgContainerProps) => { + const theme = useTheme(); + return ( + + {children} + + ); +}; + +export default SvgContainer; diff --git a/src/core/config/i18n.ts b/src/core/config/i18n.ts new file mode 100644 index 0000000..6bea986 --- /dev/null +++ b/src/core/config/i18n.ts @@ -0,0 +1,19 @@ +import i18n from "i18next"; +import LanguageDetector from "i18next-browser-languagedetector"; +import Backend from "i18next-xhr-backend"; +import { initReactI18next } from "react-i18next"; + +i18n + .use(Backend) + .use(LanguageDetector) + .use(initReactI18next) // passes i18n down to react-i18next + .init({ + backend: { + loadPath: `${process.env.PUBLIC_URL}/locales/{{lng}}/translation.json`, + }, + fallbackLng: "en", + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + }, + supportedLngs: ["en", "fr"], + }); diff --git a/src/core/config/layout.ts b/src/core/config/layout.ts new file mode 100644 index 0000000..14a9f5f --- /dev/null +++ b/src/core/config/layout.ts @@ -0,0 +1,2 @@ +export const drawerCollapsedWidth = 104; +export const drawerWidth = 280; diff --git a/src/core/contexts/SettingsProvider.tsx b/src/core/contexts/SettingsProvider.tsx new file mode 100644 index 0000000..a6b3ba3 --- /dev/null +++ b/src/core/contexts/SettingsProvider.tsx @@ -0,0 +1,96 @@ +import { ThemeProvider as MuiThemeProvider } from "@material-ui/core"; +import CssBaseline from "@material-ui/core/CssBaseline"; +import AdapterDateFns from "@material-ui/lab/AdapterDateFns"; +import LocalizationProvider from "@material-ui/lab/LocalizationProvider"; +import React, { + createContext, + useContext, + useEffect, + useMemo, + useState, +} from "react"; +import { useLocalStorage } from "../hooks/useLocalStorage"; +import { createTheme } from "../theme"; + +interface SettingsContextInterface { + collapsed: boolean; + direction: string; + mode: string; + open: boolean; + changeCollapsed: (collapsed: boolean) => void; + changeDirection: (direction: "ltr" | "rtl") => void; + changeMode: (mode: string) => void; + toggleDrawer: () => void; +} + +export const SettingsContext = createContext({} as SettingsContextInterface); + +type SettingsProviderProps = { + children: React.ReactNode; +}; + +const SettingsProvider = ({ children }: SettingsProviderProps) => { + const [collapsed, setCollapsed] = useLocalStorage("sidebarcollapsed", false); + const [direction, setDirection] = useLocalStorage("direction", "ltr"); + const [mode, setMode] = useLocalStorage("mode", "light"); + const [open, setOpen] = useState(false); + + useEffect(() => { + document.body.dir = direction; + }, [direction]); + + const theme = useMemo( + () => createTheme(direction as "ltr" | "rtl", mode as "dark" | "light"), + [direction, mode] + ); + + const changeCollapsed = (collapsed: boolean) => { + if (typeof collapsed === "boolean") { + setCollapsed(collapsed); + } + }; + + const changeDirection = (direction: "ltr" | "rtl") => { + if (direction) { + setDirection(direction); + } + }; + + const changeMode = (mode: string) => { + if (mode) { + setMode(mode); + } + }; + + const toggleDrawer = () => { + setOpen(!open); + }; + + return ( + + + + + {children} + + + + ); +}; + +export function useSettings() { + return useContext(SettingsContext); +} + +export default SettingsProvider; diff --git a/src/core/contexts/SnackbarProvider.tsx b/src/core/contexts/SnackbarProvider.tsx new file mode 100644 index 0000000..22bb52d --- /dev/null +++ b/src/core/contexts/SnackbarProvider.tsx @@ -0,0 +1,76 @@ +import Alert, { Color } from "@material-ui/core/Alert"; +import AlertTitle from "@material-ui/core/AlertTitle"; +import Snackbar from "@material-ui/core/Snackbar"; +import React, { createContext, useContext, useState } from "react"; +import { useTranslation } from "react-i18next"; + +interface SnackbarContextInterface { + error: (newMessage: string) => void; + success: (newMessage: string) => void; +} + +export const SnackbarContext = createContext({} as SnackbarContextInterface); + +type SnackbarProviderProps = { + children: React.ReactNode; +}; + +const SnackbarProvider = ({ children }: SnackbarProviderProps) => { + const { t } = useTranslation(); + const [open, setOpen] = useState(false); + const [message, setMessage] = useState(""); + const [title, setTitle] = useState(""); + const [severity, setSeverity] = useState(undefined); + + const handleClose = ( + event: React.SyntheticEvent | React.MouseEvent, + reason?: string + ) => { + if (reason === "clickaway") { + return; + } + + setOpen(false); + }; + + const error = (newMessage: string) => { + setTitle(t("common.snackbar.error")); + setMessage(newMessage); + setSeverity("error"); + setOpen(true); + }; + + const success = (newMessage: string) => { + setTitle(t("common.snackbar.success")); + setMessage(newMessage); + setSeverity("success"); + setOpen(true); + }; + + return ( + + {children} + + + {title} + {message} + + + + ); +}; + +export function useSnackbar() { + return useContext(SnackbarContext); +} + +export default SnackbarProvider; diff --git a/src/core/hooks/useDateLocale.ts b/src/core/hooks/useDateLocale.ts new file mode 100644 index 0000000..844a32d --- /dev/null +++ b/src/core/hooks/useDateLocale.ts @@ -0,0 +1,18 @@ +import { Locale } from "date-fns"; +import en from "date-fns/locale/en-US"; +import fr from "date-fns/locale/fr"; +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; + +const locales: { [key: string]: Locale } = { en, fr }; + +export function useDateLocale(): Locale | undefined { + const [locale, setLocale] = useState(undefined); + const { i18n } = useTranslation(); + + useEffect(() => { + setLocale(locales[i18n.language]); + }, [i18n.language]); + + return locale; +} diff --git a/src/core/hooks/useLocalStorage.ts b/src/core/hooks/useLocalStorage.ts new file mode 100644 index 0000000..d64e270 --- /dev/null +++ b/src/core/hooks/useLocalStorage.ts @@ -0,0 +1,40 @@ +// https://usehooks.com/useLocalStorage/ + +import { useState } from "react"; + +export function useLocalStorage( + key: string, + initialValue: T +): [T, (value: T) => void] { + // State to store our value + // Pass initial state function to useState so logic is only executed once + const [storedValue, setStoredValue] = useState(() => { + try { + // Get from local storage by key + const item = window.localStorage.getItem(key); + // Parse stored json or if none return initialValue + return item ? JSON.parse(item) : initialValue; + } catch (error) { + // If error also return initialValue + console.log(error); + return initialValue; + } + }); + // Return a wrapped version of useState's setter function that ... + // ... persists the new value to localStorage. + const setValue = (value: T) => { + try { + // Allow value to be a function so we have same API as useState + const valueToStore = + value instanceof Function ? value(storedValue) : value; + // Save state + setStoredValue(valueToStore); + // Save to local storage + window.localStorage.setItem(key, JSON.stringify(valueToStore)); + } catch (error) { + // A more advanced implementation would handle the error case + console.log(error); + } + }; + return [storedValue, setValue]; +} diff --git a/src/core/hooks/usePageTracking.ts b/src/core/hooks/usePageTracking.ts new file mode 100644 index 0000000..4f46157 --- /dev/null +++ b/src/core/hooks/usePageTracking.ts @@ -0,0 +1,25 @@ +import { useEffect, useState } from "react"; +import { useLocation } from "react-router-dom"; + +const usePageTracking = () => { + const location = useLocation(); + const [initialized, setInitialized] = useState(false); + + useEffect(() => { + const trackingId = process.env.REACT_APP_GA_TRACKING_ID; + if (trackingId) { + setInitialized(true); + } + }, []); + + useEffect(() => { + if (initialized) { + (window as any).gtag("send", "page_view", { + page_location: window.location.href, + page_path: window.location.pathname, + }); + } + }, [initialized, location]); +}; + +export default usePageTracking; diff --git a/src/core/pages/Forbidden.tsx b/src/core/pages/Forbidden.tsx new file mode 100644 index 0000000..b1fabfa --- /dev/null +++ b/src/core/pages/Forbidden.tsx @@ -0,0 +1,30 @@ +import Button from "@material-ui/core/Button"; +import { useTranslation } from "react-i18next"; +import { Link as RouterLink } from "react-router-dom"; +import { ReactComponent as ForbiddenSvg } from "../assets/403.svg"; +import Result from "../components/Result"; + +const Forbidden = () => { + const { t } = useTranslation(); + + return ( + + {t("common.backHome")} + + } + image={} + maxWidth="sm" + subTitle={t("common.errors.forbidden.subTitle")} + title={t("common.errors.unexpected.title")} + /> + ); +}; + +export default Forbidden; diff --git a/src/core/pages/NotFound.tsx b/src/core/pages/NotFound.tsx new file mode 100644 index 0000000..4109781 --- /dev/null +++ b/src/core/pages/NotFound.tsx @@ -0,0 +1,30 @@ +import Button from "@material-ui/core/Button"; +import { useTranslation } from "react-i18next"; +import { Link as RouterLink } from "react-router-dom"; +import Result from "../../core/components/Result"; +import { ReactComponent as NotFoundSvg } from "../assets/404.svg"; + +const NotFound = () => { + const { t } = useTranslation(); + + return ( + + {t("common.backHome")} + + } + image={} + maxWidth="sm" + subTitle={t("common.errors.notFound.subTitle")} + title={t("common.errors.notFound.title")} + /> + ); +}; + +export default NotFound; diff --git a/src/core/pages/UnderConstructions.tsx b/src/core/pages/UnderConstructions.tsx new file mode 100644 index 0000000..9121cd7 --- /dev/null +++ b/src/core/pages/UnderConstructions.tsx @@ -0,0 +1,30 @@ +import Button from "@material-ui/core/Button"; +import { useTranslation } from "react-i18next"; +import { Link as RouterLink } from "react-router-dom"; +import Result from "../../core/components/Result"; +import { ReactComponent as ConstructionsSvg } from "../assets/constructions.svg"; + +const UnderConstructions = () => { + const { t } = useTranslation(); + + return ( + + {t("common.backHome")} + + } + image={} + maxWidth="sm" + subTitle={t("common.errors.underConstructions.subTitle")} + title={t("common.errors.underConstructions.title")} + /> + ); +}; + +export default UnderConstructions; diff --git a/src/core/theme/components.tsx b/src/core/theme/components.tsx new file mode 100644 index 0000000..108f3e0 --- /dev/null +++ b/src/core/theme/components.tsx @@ -0,0 +1,316 @@ +import { Theme } from "@material-ui/core"; +import CheckCircle from "@material-ui/icons/CheckCircle"; +import RadioButtonUnchecked from "@material-ui/icons/RadioButtonUnchecked"; +import RemoveCircle from "@material-ui/icons/RemoveCircle"; + +export const createThemeComponents = (theme: Theme) => ({ + MuiAccordion: { + styleOverrides: { + root: { + borderRadius: theme.shape.borderRadius, + marginBottom: theme.spacing(3), + "&.Mui-expanded:last-of-type": { + marginBottom: theme.spacing(3), + }, + "&:before": { + content: "none", + }, + }, + }, + }, + MuiAccordionDetails: { + styleOverrides: { + root: { + padding: theme.spacing(1, 3, 3), + }, + }, + }, + MuiAccordionSummary: { + styleOverrides: { + root: { + padding: theme.spacing(3), + }, + content: { + margin: 0, + }, + }, + }, + MuiAppBar: { + defaultProps: { + elevation: 0, + }, + styleOverrides: { + root: { + "&.MuiAppBar-colorDefault": { + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + }, + }, + }, + }, + MuiAvatar: { + styleOverrides: { + root: { + color: "inherit", + backgroundColor: theme.palette.background.default, + }, + }, + }, + MuiButton: { + defaultProps: { + disableElevation: true, + }, + styleOverrides: { + root: { + padding: "16px 24px", + textTransform: "none" as any, + }, + label: { + fontWeight: theme.typography.fontWeightMedium, + }, + text: { + padding: "16px 16px", + }, + }, + }, + MuiButtonBase: { + defaultProps: { + disableRipple: true, // No more ripple, on the whole application + }, + }, + MuiCardActions: { + styleOverrides: { + root: { + justifyContent: "flex-end", + padding: "0 24px 24px 24px", + }, + }, + }, + MuiCardContent: { + styleOverrides: { + root: { + padding: theme.spacing(3), + }, + }, + }, + MuiCardHeader: { + styleOverrides: { + root: { + padding: "24px 24px 0 24px", + }, + }, + }, + MuiCheckbox: { + defaultProps: { + checkedIcon: , + indeterminateIcon: , + icon: , + }, + }, + MuiChip: { + styleOverrides: { + label: { + fontWeight: theme.typography.fontWeightMedium, + }, + }, + }, + MuiDialogActions: { + styleOverrides: { + root: { + padding: 24, + }, + }, + }, + MuiDialogTitle: { + styleOverrides: { + root: { + padding: 24, + "& .MuiTypography-root": { + fontSize: "1.25rem", + }, + }, + }, + }, + MuiDrawer: { + styleOverrides: { + paper: { + border: "none; !important", + }, + }, + }, + MuiFab: { + styleOverrides: { + root: { + boxShadow: "none", + lineHeight: "inherit", + textTransform: "none" as any, + "&.MuiFab-secondary": { + color: theme.palette.text.primary, + }, + }, + }, + }, + MuiFilledInput: { + defaultProps: { + disableUnderline: true, + }, + styleOverrides: { + root: { + borderRadius: theme.shape.borderRadius, + }, + }, + }, + MuiInternalClock: { + styleOverrides: { + clock: { + backgroundColor: theme.palette.background.default, + }, + }, + }, + MuiInternalDateTimePickerTabs: { + styleOverrides: { + tabs: { + backgroundColor: theme.palette.background.default, + "& MuiTabs-indicator": { + height: 0, + }, + }, + }, + }, + MuiLinearProgress: { + styleOverrides: { + root: { + borderRadius: 16, + height: 12, + }, + }, + }, + MuiList: { + defaultProps: { + disablePadding: true, + }, + }, + MuiListItem: { + styleOverrides: { + root: { + borderRadius: 16, + paddingTop: 12, + paddingBottom: 12, + "&.Mui-selected": { + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + }, + }, + }, + }, + MuiListItemIcon: { + styleOverrides: { + root: { + minWidth: 40, + }, + }, + }, + MuiMenu: { + styleOverrides: { + list: { + paddingRight: 8, + paddingLeft: 8, + }, + }, + }, + MuiMenuItem: { + styleOverrides: { + root: { + paddingTop: 12, + paddingBottom: 12, + }, + }, + }, + MuiOutlinedInput: { + styleOverrides: { + input: { + "&:-webkit-autofill": { + WebkitBoxShadow: `0 0 0 30px ${theme.palette.background.paper} inset`, + }, + }, + }, + }, + MuiPaper: { + defaultProps: { + elevation: 0, + }, + styleOverrides: { + root: { + backgroundImage: "none", + }, + }, + }, + MuiRadio: { + defaultProps: { + color: "primary" as "primary", + }, + }, + MuiTab: { + styleOverrides: { + root: { + borderRadius: "50rem", + padding: "10px 16px", + maxWidth: "initial !important", + minHeight: "initial !important", + minWidth: "initial !important", + textTransform: "none" as any, + "&.Mui-selected": { + backgroundColor: theme.palette.background.paper, + color: theme.palette.text.primary, + }, + }, + }, + }, + MuiTableCell: { + styleOverrides: { + root: { + borderBottom: `1px solid ${theme.palette.divider}`, + padding: "24px 16px", + }, + sizeSmall: { + padding: "12px 16px", + }, + }, + }, + MuiTimeline: { + styleOverrides: { + root: { + padding: "0 0 0 16px", + }, + }, + }, + MuiTimelineContent: { + styleOverrides: { + root: { + padding: "12px 16px", + }, + }, + }, + MuiToggleButton: { + styleOverrides: { + root: { + color: theme.palette.text.secondary, + borderRadius: "12px !important", + border: "none", + textTransform: "none" as any, + "&.Mui-selected": { + backgroundColor: theme.palette.background.paper, + color: theme.palette.text.primary, + }, + }, + }, + }, + MuiToggleButtonGroup: { + styleOverrides: { + root: { + backgroundColor: theme.palette.background.default, + padding: 5, + }, + }, + }, +}); diff --git a/src/core/theme/index.ts b/src/core/theme/index.ts new file mode 100644 index 0000000..223bd76 --- /dev/null +++ b/src/core/theme/index.ts @@ -0,0 +1,32 @@ +import { createTheme as createMuiTheme } from "@material-ui/core"; +import { createThemeComponents } from "./components"; +import mixins from "./mixins"; +import { darkPalette, lightPalette } from "./palette"; +import shape from "./shape"; +import transitions from "./transitions"; +import typography from "./typography"; + +export const createTheme = ( + direction: "ltr" | "rtl", + mode: "dark" | "light" +) => { + const palette = mode === "dark" ? darkPalette : lightPalette; + + // Create base theme + const baseTheme = createMuiTheme({ + direction, + mixins, + palette, + shape, + transitions, + typography, + }); + + // Inject base theme to be used in components + return createMuiTheme( + { + components: createThemeComponents(baseTheme), + }, + baseTheme + ); +}; diff --git a/src/core/theme/mixins.ts b/src/core/theme/mixins.ts new file mode 100644 index 0000000..9cd5cd1 --- /dev/null +++ b/src/core/theme/mixins.ts @@ -0,0 +1,13 @@ +const mixins = { + toolbar: { + minHeight: 80, + "@media (min-width:600px)": { + minHeight: 104, + }, + "@media (min-width:0px) and (orientation: landscape)": { + minHeight: 80, + }, + }, +}; + +export default mixins; diff --git a/src/core/theme/palette.ts b/src/core/theme/palette.ts new file mode 100644 index 0000000..66b8d0e --- /dev/null +++ b/src/core/theme/palette.ts @@ -0,0 +1,100 @@ +import { PaletteMode } from "@material-ui/core"; + +const palette = { + grey: { + "50": "#fbfaff", + "100": "#f5f5f6", //smoke from dish palette + "200": "#e3e3ec", + "300": "#d2d2da", + "400": "#adadb5", + "500": "#8d8d94", + "600": "#65656c", + "700": "#525259", //cinder from dish palette + "800": "#34343a", + "900": "#14141a", + }, +}; + +export const darkPalette = { + ...palette, + contrastThreshold: 4.5, + mode: "dark" as PaletteMode, + error: { + main: "#f01446", + }, + info: { + main: "#4FC3F7", + }, + primary: { + main: "#e3e3ec", + contrastText: palette.grey[900], + }, + secondary: { + main: palette.grey[900], + }, + success: { + main: "#81C784", + }, + warning: { + main: "#d41940", + }, + dish: { + main: "#9d2235", //ember from dish palette + }, + text: { + primary: palette.grey[100], + secondary: palette.grey[300], + disabled: palette.grey[600], + }, + divider: palette.grey[600], + background: { + paper: palette.grey[800], + default: palette.grey[700], + }, + action: { + selectedOpacity: 0, + selected: palette.grey[700], + }, +}; + +export const lightPalette = { + ...palette, + contrastThreshold: 3, + mode: "light" as PaletteMode, + error: { + main: "#f01446", //dish blaze + }, + info: { + main: "#00B0FF", + }, + primary: { + main: "#9d2235", + contrastText: "#FFF", + }, + secondary: { + main: "#FFF", + }, + success: { + main: "#00E676", + }, + warning: { + main: "#d41940", + }, + dish: { + main: "#9d2235", //ember from dish palette + }, + text: { + primary: palette.grey[700], + secondary: palette.grey[500], + disabled: palette.grey[300], + }, + divider: palette.grey[200], + background: { + paper: "#FFF", + default: palette.grey[100], + }, + action: { + selectedOpacity: 0, + selected: palette.grey[100], + }, +}; diff --git a/src/core/theme/shape.ts b/src/core/theme/shape.ts new file mode 100644 index 0000000..d130526 --- /dev/null +++ b/src/core/theme/shape.ts @@ -0,0 +1,5 @@ +const shape = { + borderRadius: 16, +}; + +export default shape; diff --git a/src/core/theme/transitions.ts b/src/core/theme/transitions.ts new file mode 100644 index 0000000..984c4ee --- /dev/null +++ b/src/core/theme/transitions.ts @@ -0,0 +1,13 @@ +const transitions = { + duration: { + shortest: 75, + shorter: 100, + short: 125, + standard: 150, + complex: 175, + enteringScreen: 115, + leavingScreen: 95, + }, +}; + +export default transitions; diff --git a/src/core/theme/typography.ts b/src/core/theme/typography.ts new file mode 100644 index 0000000..2a94bbd --- /dev/null +++ b/src/core/theme/typography.ts @@ -0,0 +1,58 @@ +const typography = { + fontFamily: "Raleway, sans-serif", + fontWeightMedium: 700, + fontWeightBold: 800, + h1: { + fontWeight: 800, + fontSize: "3.5rem", + letterSpacing: 0.3, + }, + h2: { + fontWeight: 800, + fontSize: "2.5rem", + letterSpacing: 0.3, + }, + h3: { + fontWeight: 800, + fontSize: "2.375rem", + letterSpacing: 1.5, + }, + h4: { + fontWeight: 800, + fontSize: "2.25rem", + letterSpacing: 0.3, + }, + h5: { + fontWeight: 800, + fontSize: "2.125rem", + letterSpacing: 0, + }, + h6: { + fontWeight: 700, + fontSize: "2rem", + letterSpacing: 0, + }, + subtitle1: { + letterSpacing: 0.2, + }, + subtitle2: { + letterSpacing: 0, + }, + body1: { + letterSpacing: 0, + }, + body2: { + letterSpacing: 0, + }, + button: { + letterSpacing: 0, + }, + caption: { + letterSpacing: 0, + }, + overline: { + letterSpacing: 0, + }, +}; + +export default typography; diff --git a/src/core/utils/crudUtils.ts b/src/core/utils/crudUtils.ts new file mode 100644 index 0000000..6864c19 --- /dev/null +++ b/src/core/utils/crudUtils.ts @@ -0,0 +1,24 @@ +export function addOne(items: T[] = [], newItem: T) { + return [...items, newItem]; +} + +export function removeOne( + items: T[] = [], + itemId: string +) { + return items.filter((item) => item.id !== itemId); +} + +export function removeMany( + items: T[] = [], + itemIds: string[] +) { + return items.filter((item) => !itemIds.includes(item.id)); +} + +export function updateOne( + items: T[] = [], + updatedItem: T +) { + return items.map((item) => (item.id === updatedItem.id ? updatedItem : item)); +} diff --git a/src/core/utils/selectUtils.ts b/src/core/utils/selectUtils.ts new file mode 100644 index 0000000..44383c5 --- /dev/null +++ b/src/core/utils/selectUtils.ts @@ -0,0 +1,22 @@ +export const selectAll = (list: any, key = "id") => + list.map((item: any) => item[key]); + +export const selectOne = (selected: any, id: string) => { + const selectedIndex = selected.indexOf(id); + let newSelected: string[] = []; + + if (selectedIndex === -1) { + newSelected = newSelected.concat(selected, id); + } else if (selectedIndex === 0) { + newSelected = newSelected.concat(selected.slice(1)); + } else if (selectedIndex === selected.length - 1) { + newSelected = newSelected.concat(selected.slice(0, -1)); + } else if (selectedIndex > 0) { + newSelected = newSelected.concat( + selected.slice(0, selectedIndex), + selected.slice(selectedIndex + 1) + ); + } + + return newSelected; +}; diff --git a/src/devices/components/DeviceDialog.tsx b/src/devices/components/DeviceDialog.tsx new file mode 100644 index 0000000..979020a --- /dev/null +++ b/src/devices/components/DeviceDialog.tsx @@ -0,0 +1,227 @@ +import Button from "@material-ui/core/Button"; +import Checkbox from "@material-ui/core/Checkbox"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import FormControl from "@material-ui/core/FormControl"; +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import FormLabel from "@material-ui/core/FormLabel"; +import MenuItem from "@material-ui/core/MenuItem"; +import Radio from "@material-ui/core/Radio"; +import RadioGroup from "@material-ui/core/RadioGroup"; +import TextField from "@material-ui/core/TextField"; +import LoadingButton from "@material-ui/lab/LoadingButton"; +import { useFormik } from "formik"; +import { useTranslation } from "react-i18next"; +import * as Yup from "yup"; +import React, { useState } from 'react'; +import { Device } from "../types/device"; + +const upConnectors = [ + { label: "deviceManagement.form.upConnector.options.s", value: "S3" }, + { label: "deviceManagement.form.upConnector.options.d", value: "Dynamo DB" }, + { label: "deviceManagement.form.upConnector.options.b", value: "Blob Store" }, +]; + +const downConnectors = [ + { label: "deviceManagement.form.downConnector.options.h", value: "HTTP" }, + { label: "deviceManagement.form.downConnector.options.m", value: "MQTT" }, +]; + +const deviceTypes = ["Sensor", "Camera"]; + +type DeviceDialogProps = { + onAdd: (device: Partial) => void; + onClose: () => void; + onUpdate: (device: Device) => void; + open: boolean; + processing: boolean; + device?: Device; +}; + +const DeviceDialog = ({ + onAdd, + onClose, + onUpdate, + open, + processing, + device, +}: DeviceDialogProps) => { + const { t } = useTranslation(); + + const editMode = Boolean(device && device.id); + + const handleSubmit = (values: Partial) => { + if (device && device.id) { + onUpdate({ ...values, id: device.id } as Device); + } else { + onAdd(values); + } + }; + + const formik = useFormik({ + initialValues: { + disabled: device ? device.disabled : false, + macAddress: device ? device.macAddress : "", + firstName: device ? device.firstName : "", + upConnector: device ? device.upConnector : "S3", + lastName: device ? device.lastName : "", + deviceType: device ? device.deviceType : "", + }, + validationSchema: Yup.object({ + macAddress: Yup.string() + .max(20, t("common.validations.max", { size: 20 })) + .required(t("common.validations.required")), + firstName: Yup.string() + .max(20, t("common.validations.max", { size: 20 })) + .required(t("common.validations.required")), + lastName: Yup.string() + .max(30, t("common.validations.max", { size: 30 })) + .required(t("common.validations.required")), + deviceType: Yup.string().required(t("common.validations.required")), + }), + onSubmit: handleSubmit, + }); + + return ( + +
+ + {editMode + ? t("deviceManagement.modal.edit.title") + : t("deviceManagement.modal.add.title")} + + + + + + + {t("deviceManagement.form.downConnector.label")} + + + {downConnectors.map((downConnector) => ( + } + label={t(downConnector.label)} + /> + ))} + + + + + {t("deviceManagement.form.upConnector.label")} + + + {upConnectors.map((upConnector) => ( + } + label={t(upConnector.label)} + /> + ))} + + + + + {deviceTypes.map((deviceType) => ( + + {deviceType} + + ))} + + + } + label={t("deviceManagement.form.disabled.label")} + /> + + + + + + {editMode + ? t("deviceManagement.modal.edit.action") + : t("deviceManagement.modal.add.action")} + + +
+
+ ); +}; + +export default DeviceDialog; diff --git a/src/devices/components/DeviceTable.tsx b/src/devices/components/DeviceTable.tsx new file mode 100644 index 0000000..7e53664 --- /dev/null +++ b/src/devices/components/DeviceTable.tsx @@ -0,0 +1,330 @@ +import Avatar from "@material-ui/core/Avatar"; +import Box from "@material-ui/core/Box"; +import Checkbox from "@material-ui/core/Checkbox"; +import Chip from "@material-ui/core/Chip"; +import IconButton from "@material-ui/core/IconButton"; +import ListItemIcon from "@material-ui/core/ListItemIcon"; +import Menu from "@material-ui/core/Menu"; +import MenuItem from "@material-ui/core/MenuItem"; +import Table from "@material-ui/core/Table"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; +import TableContainer from "@material-ui/core/TableContainer"; +import TableHead from "@material-ui/core/TableHead"; +import TablePagination from "@material-ui/core/TablePagination"; +import TableRow from "@material-ui/core/TableRow"; +import Typography from "@material-ui/core/Typography"; +import DeleteIcon from "@material-ui/icons/Delete"; +import EditIcon from "@material-ui/icons/Edit"; +import MoreVertIcon from "@material-ui/icons/MoreVert"; +import DevicesOther from "@material-ui/icons/DevicesOther"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import Empty from "../../core/components/Empty"; +import * as selectUtils from "../../core/utils/selectUtils"; +import { Device } from "../types/device"; + +interface HeadCell { + id: string; + label: string; + align: "center" | "left" | "right"; +} + +const headCells: HeadCell[] = [ + { + id: "device", + align: "left", + label: "deviceManagement.table.headers.device", + }, + { + id: "upConnector", + align: "center", + label: "deviceManagement.table.headers.upConnector", + }, + { + id: "deviceType", + align: "center", + label: "deviceManagement.table.headers.deviceType", + }, + { + id: "status", + align: "center", + label: "deviceManagement.table.headers.status", + }, +]; + +interface EnhancedTableProps { + numSelected: number; + onSelectAllClick: (event: React.ChangeEvent) => void; + rowCount: number; +} + +function EnhancedTableHead({ + onSelectAllClick, + numSelected, + rowCount, +}: EnhancedTableProps) { + const { t } = useTranslation(); + + return ( + + + + 0 && numSelected < rowCount} + checked={rowCount > 0 && numSelected === rowCount} + onChange={onSelectAllClick} + inputProps={{ + "aria-label": "select all devices", + }} + /> + + {headCells.map((headCell) => ( + + {t(headCell.label)} + + ))} + + {t("deviceManagement.table.headers.actions")} + + + + ); +} + +type DeviceRowProps = { + index: number; + onCheck: (id: string) => void; + onDelete: (deviceIds: string[]) => void; + onEdit: (device: Device) => void; + processing: boolean; + selected: boolean; + device: Device; +}; + +const DeviceRow = ({ + index, + onCheck, + onDelete, + onEdit, + processing, + selected, + device, +}: DeviceRowProps) => { + const [anchorEl, setAnchorEl] = useState(null); + const { t } = useTranslation(); + + const labelId = `enhanced-table-checkbox-${index}`; + const openActions = Boolean(anchorEl); + + const handleOpenActions = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleCloseActions = () => { + setAnchorEl(null); + }; + + const handleDelete = () => { + handleCloseActions(); + onDelete([device.id]); + }; + + const handleEdit = () => { + handleCloseActions(); + onEdit(device); + }; + + return ( + + + onCheck(device.id)} + /> + + + + + + + + + {`${device.lastName} ${device.firstName}`} + + + {device.macAddress} + + + + + {device.upConnector} + {device.deviceType} + + {device.disabled ? ( + + ) : ( + + )} + + + + + + + + + + {" "} + {t("common.edit")} + + + + + {" "} + {t("common.delete")} + + + + + ); +}; + +type DeviceTableProps = { + processing: boolean; + onDelete: (deviceIds: string[]) => void; + onEdit: (device: Device) => void; + onSelectedChange: (selected: string[]) => void; + selected: string[]; + devices?: Device[]; +}; + +const DeviceTable = ({ + onDelete, + onEdit, + onSelectedChange, + processing, + selected, + devices = [], +}: DeviceTableProps) => { + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(5); + + const handleSelectAllClick = (event: React.ChangeEvent) => { + if (event.target.checked) { + const newSelecteds = selectUtils.selectAll(devices); + onSelectedChange(newSelecteds); + return; + } + onSelectedChange([]); + }; + + const handleClick = (id: string) => { + let newSelected: string[] = selectUtils.selectOne(selected, id); + onSelectedChange(newSelected); + }; + + const handleChangePage = (event: unknown, newPage: number) => { + setPage(newPage); + }; + + const handleChangeRowsPerPage = ( + event: React.ChangeEvent + ) => { + setRowsPerPage(parseInt(event.target.value, 10)); + setPage(0); + }; + + const isSelected = (id: string) => selected.indexOf(id) !== -1; + + if (devices.length === 0) { + return ; + } + + return ( + + + + + + {devices + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + .map((device, index) => ( + + ))} + +
+
+ +
+ ); +}; + +export default DeviceTable; diff --git a/src/devices/hooks/useAddDevice.ts b/src/devices/hooks/useAddDevice.ts new file mode 100644 index 0000000..6f9463a --- /dev/null +++ b/src/devices/hooks/useAddDevice.ts @@ -0,0 +1,23 @@ +import axios from "axios"; +import { useMutation, useQueryClient } from "react-query"; +import { addOne } from "../../core/utils/crudUtils"; +import { Device } from "../types/device"; + +const addDevice = async (device: Device): Promise => { + const { data } = await axios.post("/api/devices", device); + return data; +}; + +export function useAddDevice() { + const queryClient = useQueryClient(); + + const { isLoading, mutateAsync } = useMutation(addDevice, { + onSuccess: (device: Device) => { + queryClient.setQueryData(["devices"], (oldDevices) => + addOne(oldDevices, device) + ); + }, + }); + + return { isAdding: isLoading, addDevice: mutateAsync }; +} diff --git a/src/devices/hooks/useDeleteDevices.ts b/src/devices/hooks/useDeleteDevices.ts new file mode 100644 index 0000000..26cdcca --- /dev/null +++ b/src/devices/hooks/useDeleteDevices.ts @@ -0,0 +1,23 @@ +import axios from "axios"; +import { useMutation, useQueryClient } from "react-query"; +import { removeMany } from "../../core/utils/crudUtils"; +import { Device } from "../types/device"; + +const deleteDevices = async (deviceIds: string[]): Promise => { + const { data } = await axios.delete("/api/devices", { data: deviceIds }); + return data; +}; + +export function useDeleteDevices() { + const queryClient = useQueryClient(); + + const { isLoading, mutateAsync } = useMutation(deleteDevices, { + onSuccess: (deviceIds: string[]) => { + queryClient.setQueryData(["devices"], (oldDevices) => + removeMany(oldDevices, deviceIds) + ); + }, + }); + + return { isDeleting: isLoading, deleteDevices: mutateAsync }; +} diff --git a/src/devices/hooks/useDevices.ts b/src/devices/hooks/useDevices.ts new file mode 100644 index 0000000..dfd475f --- /dev/null +++ b/src/devices/hooks/useDevices.ts @@ -0,0 +1,12 @@ +import axios from "axios"; +import { useQuery } from "react-query"; +import { Device } from "../types/device"; + +const fetchDevices = async (): Promise => { + const { data } = await axios.get("/api/devices"); + return data; +}; + +export function useDevices() { + return useQuery("devices", () => fetchDevices()); +} diff --git a/src/devices/hooks/useUpdateDevice.ts b/src/devices/hooks/useUpdateDevice.ts new file mode 100644 index 0000000..ab17b8e --- /dev/null +++ b/src/devices/hooks/useUpdateDevice.ts @@ -0,0 +1,23 @@ +import axios from "axios"; +import { useMutation, useQueryClient } from "react-query"; +import { updateOne } from "../../core/utils/crudUtils"; +import { Device } from "../types/device"; + +const updateDevice = async (device: Device): Promise => { + const { data } = await axios.put("/api/devices", device); + return data; +}; + +export function useUpdateDevice() { + const queryClient = useQueryClient(); + + const { isLoading, mutateAsync } = useMutation(updateDevice, { + onSuccess: (device: Device) => { + queryClient.setQueryData(["devices"], (oldDevices) => + updateOne(oldDevices, device) + ); + }, + }); + + return { isUpdating: isLoading, updateDevice: mutateAsync }; +} diff --git a/src/devices/pages/DeviceManagement.tsx b/src/devices/pages/DeviceManagement.tsx new file mode 100644 index 0000000..c5deb5a --- /dev/null +++ b/src/devices/pages/DeviceManagement.tsx @@ -0,0 +1,159 @@ +import Fab from "@material-ui/core/Fab"; +import AddIcon from "@material-ui/icons/Add"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import AdminAppBar from "../../admin/components/AdminAppBar"; +import AdminToolbar from "../../admin/components/AdminToolbar"; +import ConfirmDialog from "../../core/components/ConfirmDialog"; +import SelectToolbar from "../../core/components/SelectToolbar"; +import { useSnackbar } from "../../core/contexts/SnackbarProvider"; +import DeviceDialog from "../components/DeviceDialog"; +import DeviceTable from "../components/DeviceTable"; +import { useAddDevice } from "../hooks/useAddDevice"; +import { useDeleteDevices } from "../hooks/useDeleteDevices"; +import { useUpdateDevice } from "../hooks/useUpdateDevice"; +import { useDevices } from "../hooks/useDevices"; +import { Device } from "../types/device"; + +const DeviceManagement = () => { + const snackbar = useSnackbar(); + const { t } = useTranslation(); + + const [openConfirmDeleteDialog, setOpenConfirmDeleteDialog] = useState(false); + const [openDeviceDialog, setOpenDeviceDialog] = useState(false); + const [selected, setSelected] = useState([]); + const [deviceDeleted, setDeviceDeleted] = useState([]); + const [deviceUpdated, setDeviceUpdated] = useState(undefined); + + const { addDevice, isAdding } = useAddDevice(); + const { deleteDevices, isDeleting } = useDeleteDevices(); + const { isUpdating, updateDevice } = useUpdateDevice(); + const { data } = useDevices(); + + const processing = isAdding || isDeleting || isUpdating; + + const handleAddDevice = async (device: Partial) => { + addDevice(device as Device) + .then(() => { + snackbar.success( + t("DeviceManagement.notifications.addSuccess", { + device: `${device.firstName} ${device.lastName}`, + }) + ); + setOpenDeviceDialog(false); + }) + .catch(() => { + snackbar.error(t("common.errors.unexpected.subTitle")); + }); + }; + + const handleDeleteDevices = async () => { + deleteDevices(deviceDeleted) + .then(() => { + snackbar.success(t("DeviceManagement.notifications.deleteSuccess")); + setSelected([]); + setDeviceDeleted([]); + setOpenConfirmDeleteDialog(false); + }) + .catch(() => { + snackbar.error(t("common.errors.unexpected.subTitle")); + }); + }; + + const handleUpdateDevice = async (device: Device) => { + updateDevice(device) + .then(() => { + snackbar.success( + t("DeviceManagement.notifications.updateSuccess", { + device: `${device.firstName} ${device.lastName}`, + }) + ); + setOpenDeviceDialog(false); + }) + .catch(() => { + snackbar.error(t("common.errors.unexpected.subTitle")); + }); + }; + + const handleCancelSelected = () => { + setSelected([]); + }; + + const handleCloseConfirmDeleteDialog = () => { + setOpenConfirmDeleteDialog(false); + }; + + const handleCloseDeviceDialog = () => { + setDeviceUpdated(undefined); + setOpenDeviceDialog(false); + }; + + const handleOpenConfirmDeleteDialog = (deviceIds: string[]) => { + setDeviceDeleted(deviceIds); + setOpenConfirmDeleteDialog(true); + }; + + const handleOpenDeviceDialog = (device?: Device) => { + setDeviceUpdated(device); + setOpenDeviceDialog(true); + }; + + const handleSelectedChange = (newSelected: string[]) => { + setSelected(newSelected); + }; + + return ( + + + {!selected.length ? ( + + handleOpenDeviceDialog()} + size="small" + > + + + + ) : ( + + )} + + + + {openDeviceDialog && ( + + )} + + ); +}; + +export default DeviceManagement; diff --git a/src/devices/types/device.ts b/src/devices/types/device.ts new file mode 100644 index 0000000..5e02a12 --- /dev/null +++ b/src/devices/types/device.ts @@ -0,0 +1,10 @@ +export interface Device { + id: string; + avatar?: string; + disabled: boolean; + macAddress: string; + firstName: string; + upConnector?: "S3" | "DynamoDB" | "Blob Store"; + lastName: string; + deviceType: string; +} diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 0000000..410541e --- /dev/null +++ b/src/index.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import { BrowserRouter } from "react-router-dom"; +import App from "./App"; +import "./core/config/i18n"; +import "./mocks/server"; +import reportWebVitals from "./reportWebVitals"; + +ReactDOM.render( + + + + + , + document.getElementById("root") +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/src/landing/components/LandingLayout.tsx b/src/landing/components/LandingLayout.tsx new file mode 100644 index 0000000..8b2bc30 --- /dev/null +++ b/src/landing/components/LandingLayout.tsx @@ -0,0 +1,51 @@ +import AppBar from "@material-ui/core/AppBar"; +import IconButton from "@material-ui/core/IconButton"; +import Paper from "@material-ui/core/Paper"; +import Toolbar from "@material-ui/core/Toolbar"; +import Typography from "@material-ui/core/Typography"; +import SettingsIcon from "@material-ui/icons/Settings"; +import React, { useState } from "react"; +import Footer from "../../core/components/Footer"; +import Logo from "../../core/components/Logo"; +import SettingsDrawer from "../../core/components/SettingsDrawer"; + +type LandingLayoutProps = { + children: React.ReactNode; +}; + +const LandingLayout = ({ children }: LandingLayoutProps) => { + const [settingsOpen, setSettingsOpen] = useState(false); + + const handleSettingsToggle = () => { + setSettingsOpen(!settingsOpen); + }; + + return ( + + + + + + {process.env.REACT_APP_NAME} + + + + + + + +
{children}
+