├── .circleci └── config.yml ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── backend ├── .gitignore ├── Procfile ├── accounts │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ ├── token.py │ ├── urls.py │ └── views.py ├── config │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── conftest.py ├── db.sqlite3 ├── manage.py ├── media │ └── users │ │ └── placeholder.png ├── pytest.ini ├── requirements │ ├── base.txt │ ├── local.txt │ └── production.txt ├── static │ └── test.js └── templates │ └── index.html ├── frontend ├── .eslintignore ├── .gitignore ├── LICENSE ├── README.md ├── config-overrides.js ├── jsconfig.json ├── package.json ├── public │ ├── _redirects │ ├── assets │ │ ├── bg_card.png │ │ ├── bg_gradient.jpeg │ │ ├── icons │ │ │ ├── flags │ │ │ │ ├── ic_flag_cn.svg │ │ │ │ ├── ic_flag_de.svg │ │ │ │ ├── ic_flag_en.svg │ │ │ │ ├── ic_flag_fr.svg │ │ │ │ ├── ic_flag_kr.svg │ │ │ │ ├── ic_flag_sa.svg │ │ │ │ ├── ic_flag_us.svg │ │ │ │ └── ic_flag_vn.svg │ │ │ └── navbar │ │ │ │ ├── ic_analytics.svg │ │ │ │ ├── ic_banking.svg │ │ │ │ ├── ic_blog.svg │ │ │ │ ├── ic_booking.svg │ │ │ │ ├── ic_calendar.svg │ │ │ │ ├── ic_cart.svg │ │ │ │ ├── ic_chat.svg │ │ │ │ ├── ic_dashboard.svg │ │ │ │ ├── ic_ecommerce.svg │ │ │ │ ├── ic_invoice.svg │ │ │ │ ├── ic_kanban.svg │ │ │ │ ├── ic_mail.svg │ │ │ │ ├── ic_menu_item.svg │ │ │ │ └── ic_user.svg │ │ ├── illustrations │ │ │ ├── illustration_components.png │ │ │ ├── illustration_dashboard.png │ │ │ ├── illustration_empty_cart.svg │ │ │ ├── illustration_empty_content.svg │ │ │ ├── illustration_empty_mail.svg │ │ │ ├── illustration_invite.png │ │ │ ├── illustration_login.png │ │ │ └── illustration_register.png │ │ ├── overlay.svg │ │ └── placeholder.svg │ ├── favicon │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ └── favicon.ico │ ├── fonts │ │ ├── CircularStd-Bold.otf │ │ ├── CircularStd-Book.otf │ │ ├── CircularStd-Medium.otf │ │ ├── Roboto-Bold.ttf │ │ ├── Roboto-Regular.ttf │ │ └── index.css │ ├── index.html │ ├── logo │ │ ├── logo_full.jpg │ │ ├── logo_full.svg │ │ └── logo_single.svg │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.js │ ├── _mock │ │ ├── _analytics.js │ │ ├── _app.js │ │ ├── _banking.js │ │ ├── _booking.js │ │ ├── _calenderViewOptions.js │ │ ├── _countries.js │ │ ├── _countriesAndStates.js │ │ ├── _ecommerce.js │ │ ├── _invoice.js │ │ ├── _mock.js │ │ ├── _others.js │ │ ├── _plans.js │ │ ├── _roles.js │ │ ├── _timeZoneOptions.js │ │ ├── _top100Films.js │ │ ├── _user.js │ │ ├── address.js │ │ ├── boolean.js │ │ ├── company.js │ │ ├── email.js │ │ ├── funcs.js │ │ ├── index.js │ │ ├── map │ │ │ ├── cities.js │ │ │ ├── countries.js │ │ │ └── map-style-basic-v8.json │ │ ├── name.js │ │ ├── number.js │ │ ├── phoneNumber.js │ │ ├── role.js │ │ └── text.js │ ├── assets │ │ ├── icon_plan_free.js │ │ ├── icon_plan_premium.js │ │ ├── icon_plan_starter.js │ │ ├── icon_sent.js │ │ ├── illustration_403.js │ │ ├── illustration_404.js │ │ ├── illustration_500.js │ │ ├── illustration_background.js │ │ ├── illustration_booking.js │ │ ├── illustration_checkin.js │ │ ├── illustration_checkout.js │ │ ├── illustration_coming_soon.js │ │ ├── illustration_doc.js │ │ ├── illustration_maintenance.js │ │ ├── illustration_motivation.js │ │ ├── illustration_order_complete.js │ │ ├── illustration_seo.js │ │ ├── illustration_upload.js │ │ └── index.js │ ├── components │ │ ├── Avatar.js │ │ ├── BadgeStatus.js │ │ ├── Breadcrumbs.js │ │ ├── CopyClipboard.js │ │ ├── EmptyContent.js │ │ ├── HeaderBreadcrumbs.js │ │ ├── Iconify.js │ │ ├── Image.js │ │ ├── InputStyle.js │ │ ├── Label.js │ │ ├── LightboxModal.js │ │ ├── LoadingScreen.js │ │ ├── Logo.js │ │ ├── Markdown.js │ │ ├── MenuPopover.js │ │ ├── MyAvatar.js │ │ ├── NotistackProvider.js │ │ ├── Page.js │ │ ├── ProgressBar.js │ │ ├── ScrollToTop.js │ │ ├── Scrollbar.js │ │ ├── SearchNotFound.js │ │ ├── SocialsButton.js │ │ ├── SvgIconStyle.js │ │ ├── TextIconLabel.js │ │ ├── TextMaxLine.js │ │ ├── animate │ │ │ ├── DialogAnimate.js │ │ │ ├── FabButtonAnimate.js │ │ │ ├── IconButtonAnimate.js │ │ │ ├── MotionContainer.js │ │ │ ├── MotionLazyContainer.js │ │ │ ├── MotionViewport.js │ │ │ ├── TextAnimate.js │ │ │ ├── features.js │ │ │ ├── index.js │ │ │ └── variants │ │ │ │ ├── actions.js │ │ │ │ ├── background.js │ │ │ │ ├── bounce.js │ │ │ │ ├── container.js │ │ │ │ ├── fade.js │ │ │ │ ├── flip.js │ │ │ │ ├── index.js │ │ │ │ ├── path.js │ │ │ │ ├── rotate.js │ │ │ │ ├── scale.js │ │ │ │ ├── slide.js │ │ │ │ ├── transition.js │ │ │ │ └── zoom.js │ │ ├── carousel │ │ │ ├── CarouselArrowIndex.js │ │ │ ├── CarouselArrows.js │ │ │ ├── CarouselDots.js │ │ │ └── index.js │ │ ├── chart │ │ │ ├── BaseOptionChart.js │ │ │ ├── ChartStyle.js │ │ │ └── index.js │ │ ├── color-utils │ │ │ ├── ColorManyPicker.js │ │ │ ├── ColorPreview.js │ │ │ ├── ColorSinglePicker.js │ │ │ └── index.js │ │ ├── editor │ │ │ ├── EditorToolbar.js │ │ │ ├── EditorToolbarStyle.js │ │ │ └── index.js │ │ ├── emoji-picker │ │ │ ├── EmojiPicker.js │ │ │ └── index.js │ │ ├── hook-form │ │ │ ├── FormProvider.js │ │ │ ├── RHFCheckbox.js │ │ │ ├── RHFEditor.js │ │ │ ├── RHFNumberField.js │ │ │ ├── RHFRadioGroup.js │ │ │ ├── RHFSelect.js │ │ │ ├── RHFSlider.js │ │ │ ├── RHFSwitch.js │ │ │ ├── RHFTextField.js │ │ │ ├── RHFUpload.js │ │ │ └── index.js │ │ ├── map │ │ │ ├── MapControl.js │ │ │ ├── MapMarker.js │ │ │ ├── MapPopup.js │ │ │ ├── index.js │ │ │ └── style.js │ │ ├── mega-menu │ │ │ ├── MegaMenuDesktopHorizon.js │ │ │ ├── MegaMenuDesktopVertical.js │ │ │ ├── MegaMenuMobile.js │ │ │ ├── MenuCarousel.js │ │ │ ├── MenuConfig.js │ │ │ ├── MenuHotProducts.js │ │ │ └── index.js │ │ ├── nav-section │ │ │ ├── horizontal │ │ │ │ ├── NavItem.js │ │ │ │ ├── NavList.js │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── index.js │ │ │ └── vertical │ │ │ │ ├── NavItem.js │ │ │ │ ├── NavList.js │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ ├── organizational-chart │ │ │ ├── index.js │ │ │ └── node │ │ │ │ ├── GroupNode.js │ │ │ │ ├── SimpleNode.js │ │ │ │ ├── StandardNode.js │ │ │ │ └── index.js │ │ ├── settings │ │ │ ├── ThemeColorPresets.js │ │ │ ├── ThemeContrast.js │ │ │ ├── ThemeLocalization.js │ │ │ ├── ThemeRtlLayout.js │ │ │ ├── drawer │ │ │ │ ├── BoxMask.js │ │ │ │ ├── SettingColorPresets.js │ │ │ │ ├── SettingContrast.js │ │ │ │ ├── SettingDirection.js │ │ │ │ ├── SettingFullscreen.js │ │ │ │ ├── SettingLayout.js │ │ │ │ ├── SettingMode.js │ │ │ │ ├── SettingStretch.js │ │ │ │ ├── ToggleButton.js │ │ │ │ └── index.js │ │ │ └── index.js │ │ ├── skeleton │ │ │ ├── SkeletonConversationItem.js │ │ │ ├── SkeletonKanbanColumn.js │ │ │ ├── SkeletonMailSidebarItem.js │ │ │ ├── SkeletonMap.js │ │ │ ├── SkeletonPost.js │ │ │ ├── SkeletonPostItem.js │ │ │ ├── SkeletonProduct.js │ │ │ ├── SkeletonProductItem.js │ │ │ └── index.js │ │ ├── table │ │ │ ├── TableEmptyRows.js │ │ │ ├── TableHeadCustom.js │ │ │ ├── TableMoreMenu.js │ │ │ ├── TableNoData.js │ │ │ ├── TableSelectedActions.js │ │ │ ├── TableSkeleton.js │ │ │ └── index.js │ │ └── upload │ │ │ ├── BlockContent.js │ │ │ ├── MultiFilePreview.js │ │ │ ├── RejectionFiles.js │ │ │ ├── UploadAvatar.js │ │ │ ├── UploadMultiFile.js │ │ │ ├── UploadSingleFile.js │ │ │ └── index.js │ ├── config.js │ ├── contexts │ │ ├── CollapseDrawerContext.js │ │ ├── JWTContext.js │ │ └── SettingsContext.js │ ├── guards │ │ ├── AuthGuard.js │ │ ├── GuestGuard.js │ │ └── RoleBasedGuard.js │ ├── hooks │ │ ├── useAuth.js │ │ ├── useCollapseDrawer.js │ │ ├── useCountdown.js │ │ ├── useDateRangePicker.js │ │ ├── useIsMountedRef.js │ │ ├── useLocalStorage.js │ │ ├── useLocales.js │ │ ├── useOffSetTop.js │ │ ├── useResponsive.js │ │ ├── useSettings.js │ │ ├── useTable.js │ │ ├── useTabs.js │ │ └── useToggle.js │ ├── index.js │ ├── layouts │ │ ├── LogoOnlyLayout.js │ │ ├── dashboard │ │ │ ├── header │ │ │ │ ├── AccountPopover.js │ │ │ │ ├── ContactsPopover.js │ │ │ │ ├── LanguagePopover.js │ │ │ │ ├── NotificationsPopover.js │ │ │ │ ├── Searchbar.js │ │ │ │ └── index.js │ │ │ ├── index.js │ │ │ └── navbar │ │ │ │ ├── CollapseButton.js │ │ │ │ ├── NavConfig.js │ │ │ │ ├── NavbarAccount.js │ │ │ │ ├── NavbarDocs.js │ │ │ │ ├── NavbarHorizontal.js │ │ │ │ └── NavbarVertical.js │ │ └── main │ │ │ ├── MainFooter.js │ │ │ ├── MainHeader.js │ │ │ ├── MenuConfig.js │ │ │ ├── MenuDesktop.js │ │ │ ├── MenuMobile.js │ │ │ └── index.js │ ├── locales │ │ ├── ar.js │ │ ├── cn.js │ │ ├── en.js │ │ ├── fr.js │ │ ├── i18n.js │ │ └── vn.js │ ├── pages │ │ ├── Page403.js │ │ ├── Page404.js │ │ ├── Page500.js │ │ ├── auth │ │ │ ├── Login.js │ │ │ ├── NewPassword.js │ │ │ ├── Register.js │ │ │ ├── ResetPassword.js │ │ │ └── VerifyCode.js │ │ └── dashboard │ │ │ ├── GeneralApp.js │ │ │ ├── UserAccount.js │ │ │ └── UserProfile.js │ ├── redux │ │ ├── rootReducer.js │ │ ├── slices │ │ │ └── user.js │ │ └── store.js │ ├── reportWebVitals.js │ ├── routes │ │ ├── index.js │ │ └── paths.js │ ├── sections │ │ ├── @dashboard │ │ │ └── user │ │ │ │ └── account │ │ │ │ ├── AccountBilling.js │ │ │ │ ├── AccountBillingAddressBook.js │ │ │ │ ├── AccountBillingInvoiceHistory.js │ │ │ │ ├── AccountBillingPaymentMethod.js │ │ │ │ ├── AccountChangePassword.js │ │ │ │ ├── AccountGeneral.js │ │ │ │ ├── AccountNotifications.js │ │ │ │ ├── AccountSocialLinks.js │ │ │ │ └── index.js │ │ └── auth │ │ │ ├── login │ │ │ ├── LoginForm.js │ │ │ └── index.js │ │ │ ├── new-password │ │ │ ├── NewPasswordForm.js │ │ │ └── index.js │ │ │ ├── register │ │ │ ├── RegisterForm.js │ │ │ └── index.js │ │ │ ├── reset-password │ │ │ ├── ResetPasswordForm.js │ │ │ └── index.js │ │ │ └── verify-code │ │ │ ├── VerifyCodeForm.js │ │ │ └── index.js │ ├── service-worker.js │ ├── serviceWorkerRegistration.js │ ├── theme │ │ ├── breakpoints.js │ │ ├── index.js │ │ ├── overrides │ │ │ ├── Accordion.js │ │ │ ├── Alert.js │ │ │ ├── Autocomplete.js │ │ │ ├── Avatar.js │ │ │ ├── Backdrop.js │ │ │ ├── Badge.js │ │ │ ├── Breadcrumbs.js │ │ │ ├── Button.js │ │ │ ├── ButtonGroup.js │ │ │ ├── Card.js │ │ │ ├── Checkbox.js │ │ │ ├── Chip.js │ │ │ ├── ControlLabel.js │ │ │ ├── CssBaseline.js │ │ │ ├── CustomIcons.js │ │ │ ├── DataGrid.js │ │ │ ├── Dialog.js │ │ │ ├── Drawer.js │ │ │ ├── Fab.js │ │ │ ├── Input.js │ │ │ ├── Link.js │ │ │ ├── List.js │ │ │ ├── LoadingButton.js │ │ │ ├── Menu.js │ │ │ ├── Pagination.js │ │ │ ├── Paper.js │ │ │ ├── Popover.js │ │ │ ├── Progress.js │ │ │ ├── Radio.js │ │ │ ├── Rating.js │ │ │ ├── Select.js │ │ │ ├── Skeleton.js │ │ │ ├── Slider.js │ │ │ ├── Stepper.js │ │ │ ├── SvgIcon.js │ │ │ ├── Switch.js │ │ │ ├── Table.js │ │ │ ├── Tabs.js │ │ │ ├── Timeline.js │ │ │ ├── ToggleButton.js │ │ │ ├── Tooltip.js │ │ │ ├── TreeView.js │ │ │ ├── Typography.js │ │ │ └── index.js │ │ ├── palette.js │ │ ├── shadows.js │ │ └── typography.js │ └── utils │ │ ├── axios.js │ │ ├── createAvatar.js │ │ ├── cssStyles.js │ │ ├── flattenArray.js │ │ ├── formatNumber.js │ │ ├── formatTime.js │ │ ├── getColorName.js │ │ ├── getColorPresets.js │ │ ├── getFileData.js │ │ ├── getFileFormat.js │ │ ├── getFontValue.js │ │ ├── highlight.js │ │ ├── jwt.js │ │ ├── loadColorOptions.js │ │ ├── roleOptions.js │ │ └── uuidv4.js └── yarn.lock └── image └── README ├── 1671452013971.png └── 1671452027931.png /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | python: circleci/python@1.5.0 5 | 6 | jobs: 7 | build-and-test: 8 | docker: 9 | - image: cimg/python:3.10.2 10 | steps: 11 | - checkout 12 | - run: 13 | command: | 14 | python3 -m venv venv 15 | . venv/bin/activate 16 | - python/install-packages: 17 | pkg-manager: pip 18 | pip-dependency-file: backend/requirements/local.txt 19 | - run: 20 | name: Running tests 21 | command: | 22 | python3 backend/manage.py test 23 | - store_artifacts: 24 | path: test-reports/ 25 | destination: python_app 26 | 27 | workflows: 28 | sample: 29 | jobs: 30 | - build-and-test 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Faisal Nazik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /backend/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn config.wsgi -------------------------------------------------------------------------------- /backend/accounts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/backend/accounts/__init__.py -------------------------------------------------------------------------------- /backend/accounts/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AccountsConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'accounts' 7 | -------------------------------------------------------------------------------- /backend/accounts/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/backend/accounts/migrations/__init__.py -------------------------------------------------------------------------------- /backend/accounts/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from rest_framework.validators import UniqueValidator 3 | from django.core.validators import EmailValidator 4 | from rest_framework_simplejwt.tokens import RefreshToken 5 | from .models import User 6 | 7 | logger = __import__("logging").getLogger(__name__) 8 | 9 | 10 | class RegisterUserSerializer(serializers.ModelSerializer): 11 | token = serializers.SerializerMethodField() 12 | email = serializers.EmailField( 13 | validators=[EmailValidator(), UniqueValidator(queryset=User.objects.all())] 14 | ) 15 | 16 | class Meta: 17 | model = User 18 | fields = ["name", "email", "password", "token"] 19 | extra_kwargs = { 20 | "password": {"write_only": True, "min_length": 8}, 21 | } 22 | 23 | def get_token(self, user): 24 | token = RefreshToken.for_user(user) 25 | data = {"refresh": str(token), "access": str(token.access_token)} 26 | return data 27 | 28 | 29 | class UserProfileSeralizer(serializers.ModelSerializer): 30 | """Use this serializer to get the user profile""" 31 | 32 | class Meta: 33 | model = User 34 | fields = ["id", "name", "email", "avatar"] 35 | 36 | def update(self, instance, validated_data): 37 | instance.name = validated_data.get("name", instance.name) 38 | instance.email = validated_data.get("email", instance.email) 39 | instance.avatar = validated_data.get("avatar", instance.avatar) 40 | instance.save() 41 | return instance 42 | -------------------------------------------------------------------------------- /backend/accounts/tests.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from django.contrib.auth import get_user_model 3 | from .models import User 4 | import json 5 | from django.test import Client 6 | 7 | 8 | @pytest.mark.django_db 9 | def test_user_creation(): 10 | # Create a new user using the create_user method of the UserManager 11 | user = User.objects.create_user( 12 | email="test@example.com", name="Test User", password="password" 13 | ) 14 | 15 | # Get the user model 16 | UserModel = get_user_model() 17 | 18 | # Check that the created user is an instance of the user model 19 | assert isinstance(user, UserModel) 20 | 21 | 22 | @pytest.mark.django_db 23 | def test_superuser_creation(): 24 | # Create a new superuser using the create_superuser method of the UserManager 25 | superuser = User.objects.create_superuser( 26 | email="test@example.com", name="Test Superuser", password="password" 27 | ) 28 | 29 | # Check that the created user is a superuser 30 | assert superuser.is_superuser 31 | 32 | 33 | @pytest.mark.django_db 34 | def test_user_string_representation(): 35 | # Create a new user 36 | user = User.objects.create_user( 37 | email="test@example.com", name="Test User", password="password" 38 | ) 39 | 40 | # Check that the string representation of the user is the name 41 | assert str(user) == "Test User" 42 | -------------------------------------------------------------------------------- /backend/accounts/token.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/backend/accounts/token.py -------------------------------------------------------------------------------- /backend/accounts/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from rest_framework_simplejwt import views as jwt_views 3 | from .views import RegisterUserView, UserProfileView 4 | 5 | urlpatterns = [ 6 | path("register/", RegisterUserView.as_view(), name="register"), 7 | path("login/", jwt_views.TokenObtainPairView.as_view(), name="token_obtain_pair"), 8 | path("token/refresh/", jwt_views.TokenRefreshView.as_view(), name="token_refresh"), 9 | path("profile/", UserProfileView.as_view(), name="user_profile"), 10 | ] 11 | -------------------------------------------------------------------------------- /backend/accounts/views.py: -------------------------------------------------------------------------------- 1 | from .models import User 2 | from rest_framework import generics 3 | from rest_framework import permissions 4 | from rest_framework.views import APIView 5 | from rest_framework.response import Response 6 | from .serializers import RegisterUserSerializer, UserProfileSeralizer 7 | from rest_framework.throttling import UserRateThrottle, AnonRateThrottle 8 | 9 | # import ValidationError from rest_framework.exceptions 10 | from rest_framework.exceptions import ValidationError 11 | from rest_framework_simplejwt.tokens import RefreshToken 12 | from rest_framework_simplejwt.views import TokenObtainPairView 13 | 14 | 15 | class RegisterUserView(generics.CreateAPIView): 16 | """Register a new user and return a token for the user""" 17 | 18 | permission_classes = [permissions.AllowAny] 19 | throttle_classes = [AnonRateThrottle] 20 | serializer_class = RegisterUserSerializer 21 | 22 | def perform_create(self, serializer): 23 | user = serializer.save() 24 | token = serializer.get_token(user) 25 | serializer.validated_data["token"] = token 26 | return super().perform_create(serializer) 27 | 28 | 29 | class UserProfileView(generics.RetrieveUpdateAPIView): 30 | """Get and update user profile""" 31 | 32 | serializer_class = UserProfileSeralizer 33 | permission_classes = [permissions.IsAuthenticated] 34 | throttle_classes = [UserRateThrottle] 35 | 36 | def get_object(self): 37 | return self.request.user 38 | -------------------------------------------------------------------------------- /backend/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/backend/config/__init__.py -------------------------------------------------------------------------------- /backend/config/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for config project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /backend/config/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for config project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /backend/conftest.py: -------------------------------------------------------------------------------- 1 | from rest_framework.test import APIClient 2 | import pytest 3 | import django 4 | 5 | django.setup() 6 | 7 | 8 | @pytest.mark.django_db 9 | @pytest.fixture(autouse=True) 10 | def enable_db_access_for_all_tests(db): 11 | pass 12 | 13 | 14 | @pytest.fixture 15 | def client(): 16 | django.setup() 17 | return APIClient() 18 | -------------------------------------------------------------------------------- /backend/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/backend/db.sqlite3 -------------------------------------------------------------------------------- /backend/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /backend/media/users/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/backend/media/users/placeholder.png -------------------------------------------------------------------------------- /backend/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | DJANGO_SETTINGS_MODULE = config.settings 3 | python_files = tests.py test_*.py *_tests.py 4 | addopts = --maxfail=1 --disable-pytest-warnings -p no:warnings -------------------------------------------------------------------------------- /backend/requirements/base.txt: -------------------------------------------------------------------------------- 1 | aniso8601==9.0.1 2 | argon2-cffi==21.3.0 3 | argon2-cffi-bindings==21.2.0 4 | asgiref==3.5.2 5 | attrs==22.1.0 6 | black==22.3.0 7 | certifi==2023.7.22 8 | cffi==1.15.1 9 | charset-normalizer==2.1.1 10 | click==8.1.3 11 | colorama==0.4.6 12 | coreapi==2.3.3 13 | coreschema==0.0.4 14 | Django==4.1.13 15 | django-cors-headers==3.13.0 16 | djangorestframework==3.14.0 17 | djangorestframework-simplejwt==5.2.2 18 | drf-spectacular==0.25.1 19 | drf-yasg==1.21.4 20 | exceptiongroup==1.0.4 21 | Flask==2.2.5 22 | Flask-Cors==3.0.10 23 | flask-restplus==0.13.0 24 | idna==3.4 25 | inflection==0.5.1 26 | iniconfig==1.1.1 27 | itsdangerous==2.1.2 28 | itypes==1.2.0 29 | Jinja2==3.1.3 30 | jsonschema==4.17.3 31 | MarkupSafe==2.1.1 32 | mypy-extensions==0.4.3 33 | packaging==22.0 34 | pathspec==0.10.3 35 | Pillow==10.0.1 36 | platformdirs==2.6.0 37 | pluggy==1.0.0 38 | psycopg2-binary==2.9.5 39 | pycparser==2.21 40 | PyJWT==2.6.0 41 | pyrsistent==0.19.2 42 | pytest==7.2.0 43 | pytest-django==4.5.2 44 | pytz==2022.7 45 | PyYAML==6.0 46 | requests==2.31.0 47 | ruamel.yaml==0.17.21 48 | ruamel.yaml.clib==0.2.7 49 | six==1.16.0 50 | sqlparse==0.4.4 51 | swagger-ui==0.1.2 52 | tomli==2.0.1 53 | tzdata==2022.7 54 | uritemplate==4.1.1 55 | urllib3==1.26.18 56 | Werkzeug==3.0.1 57 | whitenoise==6.2.0 58 | -------------------------------------------------------------------------------- /backend/requirements/local.txt: -------------------------------------------------------------------------------- 1 | -r ./base.txt 2 | 3 | black==22.3.0 -------------------------------------------------------------------------------- /backend/requirements/production.txt: -------------------------------------------------------------------------------- 1 | -r ./base.txt 2 | 3 | gunicorn -------------------------------------------------------------------------------- /backend/static/test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/backend/static/test.js -------------------------------------------------------------------------------- /backend/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | API 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /frontend/.eslintignore: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /frontend/LICENSE: -------------------------------------------------------------------------------- 1 | We do not want anyone to see our source code, but if for any reason our code 2 | is stolen or otherwise obtained, we want to have a license that does not 3 | allow disclosure of any kind -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | Project is running live on the link 2 | 3 | https://main.d8xxd1waej5k1.amplifyapp.com/ 4 | 5 | 6 | 7 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 8 | 9 | ## 1.Install 10 | 11 | ### npm 12 | 13 | ``` 14 | npm i 15 | or 16 | npm i --legacy-peer-deps 17 | ``` 18 | 19 | ### yarn 20 | 21 | ``` 22 | yarn install 23 | ``` 24 | 25 | ## 2.Start 26 | 27 | ```sh 28 | npm start 29 | or 30 | yarn start 31 | ``` 32 | 33 | ## 3.Build 34 | 35 | ```sh 36 | npm run build or yarn build 37 | ``` 38 | 39 | Builds the app for production to the `build` folder.`
` 40 | It correctly bundles React in production mode and optimizes the build for the best performance. 41 | 42 | The build is minified and the filenames include the hashes.`
` 43 | 44 | Your app is ready to be deployed. 45 | 46 | ## User Guide 47 | 48 | You can find detailed instructions on using Create React App and many tips in [its documentation](https://facebook.github.io/create-react-app/). 49 | -------------------------------------------------------------------------------- /frontend/config-overrides.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const WorkBoxPlugin = require('workbox-webpack-plugin'); 3 | 4 | module.exports = function override(config) { 5 | config.resolve.fallback = { 6 | process: require.resolve('process/browser'), 7 | zlib: require.resolve('browserify-zlib'), 8 | stream: require.resolve('stream-browserify'), 9 | util: require.resolve('util'), 10 | buffer: require.resolve('buffer'), 11 | asset: require.resolve('assert'), 12 | }; 13 | 14 | // https://stackoverflow.com/questions/69135310/workaround-for-cache-size-limit-in-create-react-app-pwa-service-worker 15 | config.plugins.forEach((plugin) => { 16 | if (plugin instanceof WorkBoxPlugin.InjectManifest) { 17 | plugin.config.maximumFileSizeToCacheInBytes = 50 * 1024 * 1024; 18 | } 19 | }); 20 | 21 | config.plugins = [ 22 | ...config.plugins, 23 | new webpack.ProvidePlugin({ 24 | process: 'process/browser.js', 25 | Buffer: ['buffer', 'Buffer'], 26 | }), 27 | ]; 28 | 29 | return config; 30 | }; 31 | -------------------------------------------------------------------------------- /frontend/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "baseUrl": "." 6 | }, 7 | "include": [ 8 | "src/**/*" 9 | ], 10 | "exclude": [ 11 | "node_modules" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /frontend/public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /frontend/public/assets/bg_card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/assets/bg_card.png -------------------------------------------------------------------------------- /frontend/public/assets/bg_gradient.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/assets/bg_gradient.jpeg -------------------------------------------------------------------------------- /frontend/public/assets/icons/flags/ic_flag_cn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | ic_flag_cn 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/flags/ic_flag_de.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/flags/ic_flag_en.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/flags/ic_flag_fr.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/flags/ic_flag_vn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | ic_flag_vn 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/navbar/ic_banking.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/navbar/ic_blog.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/navbar/ic_booking.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/navbar/ic_calendar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/navbar/ic_cart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/navbar/ic_chat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/navbar/ic_dashboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/navbar/ic_invoice.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/navbar/ic_kanban.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | ic_kanban 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/navbar/ic_mail.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/navbar/ic_menu_item.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | ic_menu_item 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /frontend/public/assets/icons/navbar/ic_user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/assets/illustrations/illustration_components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/assets/illustrations/illustration_components.png -------------------------------------------------------------------------------- /frontend/public/assets/illustrations/illustration_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/assets/illustrations/illustration_dashboard.png -------------------------------------------------------------------------------- /frontend/public/assets/illustrations/illustration_invite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/assets/illustrations/illustration_invite.png -------------------------------------------------------------------------------- /frontend/public/assets/illustrations/illustration_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/assets/illustrations/illustration_login.png -------------------------------------------------------------------------------- /frontend/public/assets/illustrations/illustration_register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/assets/illustrations/illustration_register.png -------------------------------------------------------------------------------- /frontend/public/assets/overlay.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/assets/placeholder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /frontend/public/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /frontend/public/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /frontend/public/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /frontend/public/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /frontend/public/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/favicon/favicon.ico -------------------------------------------------------------------------------- /frontend/public/fonts/CircularStd-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/fonts/CircularStd-Bold.otf -------------------------------------------------------------------------------- /frontend/public/fonts/CircularStd-Book.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/fonts/CircularStd-Book.otf -------------------------------------------------------------------------------- /frontend/public/fonts/CircularStd-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/fonts/CircularStd-Medium.otf -------------------------------------------------------------------------------- /frontend/public/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/index.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'CircularStd'; 3 | font-weight: 400; 4 | font-style: normal; 5 | src: local('CircularStd'), url('CircularStd-Book.otf') format('opentype'); 6 | } 7 | @font-face { 8 | font-family: 'CircularStd'; 9 | font-weight: 500; 10 | font-style: normal; 11 | src: local('CircularStd'), url('CircularStd-Medium.otf') format('opentype'); 12 | } 13 | @font-face { 14 | font-family: 'CircularStd'; 15 | font-weight: 700; 16 | font-style: normal; 17 | src: local('CircularStd'), url('CircularStd-Bold.otf') format('opentype'); 18 | } 19 | -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Project Title 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /frontend/public/logo/logo_full.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/public/logo/logo_full.jpg -------------------------------------------------------------------------------- /frontend/public/logo/logo_single.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "name", 3 | "short_name": "shortname", 4 | "display": "standalone", 5 | "start_url": "/dashboard/app", 6 | "theme_color": "#000000", 7 | "background_color": "#ffffff", 8 | "icons": [ 9 | { 10 | "src": "favicon/android-chrome-192x192.png", 11 | "sizes": "192x192", 12 | "type": "image/png" 13 | }, 14 | { 15 | "src": "favicon/android-chrome-512x512.png", 16 | "sizes": "512x512", 17 | "type": "image/png" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/src/App.js: -------------------------------------------------------------------------------- 1 | // routes 2 | import Router from './routes'; 3 | // theme 4 | import ThemeProvider from './theme'; 5 | // components 6 | import ThemeSettings from './components/settings'; 7 | import { ChartStyle } from './components/chart'; 8 | import ScrollToTop from './components/ScrollToTop'; 9 | import { ProgressBarStyle } from './components/ProgressBar'; 10 | import NotistackProvider from './components/NotistackProvider'; 11 | import MotionLazyContainer from './components/animate/MotionLazyContainer'; 12 | 13 | // ---------------------------------------------------------------------- 14 | 15 | export default function App() { 16 | return ( 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /frontend/src/_mock/_analytics.js: -------------------------------------------------------------------------------- 1 | // components 2 | import Iconify from '../components/Iconify'; 3 | // 4 | import _mock from './_mock'; 5 | 6 | // ---------------------------------------------------------------------- 7 | 8 | export const _analyticPost = [...Array(5)].map((_, index) => ({ 9 | id: _mock.id(index), 10 | title: _mock.text.title(index), 11 | description: _mock.text.description(index), 12 | image: _mock.image.cover(index), 13 | postedAt: _mock.time(index), 14 | })); 15 | 16 | export const _analyticOrderTimeline = [...Array(5)].map((_, index) => ({ 17 | id: _mock.id(index), 18 | title: [ 19 | '1983, orders, $4220', 20 | '12 Invoices have been paid', 21 | 'Order #37745 from September', 22 | 'New order placed #XF-2356', 23 | 'New order placed #XF-2346', 24 | ][index], 25 | type: `order${index + 1}`, 26 | time: _mock.time(index), 27 | })); 28 | 29 | export const _analyticTraffic = [ 30 | { 31 | name: 'FaceBook', 32 | value: 323234, 33 | icon: , 34 | }, 35 | { 36 | name: 'Google', 37 | value: 341212, 38 | icon: , 39 | }, 40 | { 41 | name: 'Linkedin', 42 | value: 411213, 43 | icon: , 44 | }, 45 | { 46 | name: 'Twitter', 47 | value: 443232, 48 | icon: , 49 | }, 50 | ]; 51 | -------------------------------------------------------------------------------- /frontend/src/_mock/_booking.js: -------------------------------------------------------------------------------- 1 | import _mock from './_mock'; 2 | import { randomInArray } from './funcs'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export const _bookings = [...Array(5)].map((_, index) => ({ 7 | id: _mock.id(index), 8 | name: _mock.name.fullName(index), 9 | avatar: _mock.image.avatar(index), 10 | checkIn: _mock.time(index), 11 | checkOut: _mock.time(index), 12 | phoneNumber: _mock.phoneNumber(index), 13 | status: randomInArray(['pending', 'un_paid', 'paid']), 14 | roomType: randomInArray(['double', 'king', 'single']), 15 | })); 16 | 17 | export const _bookingsOverview = [...Array(3)].map((_, index) => ({ 18 | status: ['Pending', 'Cancel', 'Done'][index], 19 | quantity: _mock.number.percent(index) * 1000, 20 | value: _mock.number.percent(index), 21 | })); 22 | 23 | export const _bookingReview = [...Array(5)].map((_, index) => ({ 24 | id: _mock.id(index), 25 | name: _mock.name.fullName(index), 26 | description: _mock.text.description(index), 27 | avatar: _mock.image.avatar(index), 28 | rating: _mock.number.rating(index), 29 | postedAt: _mock.time(index), 30 | tags: ['Great Sevice', 'Recommended', 'Best Price'], 31 | })); 32 | 33 | export const _bookingNew = [...Array(5)].map((_, index) => ({ 34 | id: _mock.id(index), 35 | name: _mock.name.fullName(index), 36 | avatar: _mock.image.avatar(index), 37 | bookdAt: _mock.time(index), 38 | roomNumber: 'A-21', 39 | roomType: randomInArray(['double', 'king', 'single']), 40 | person: '3-5', 41 | cover: `https://minimal-assets-api-dev.vercel.app/assets/images/rooms/room-${index + 1}.jpg`, 42 | })); 43 | -------------------------------------------------------------------------------- /frontend/src/_mock/_calenderViewOptions.js: -------------------------------------------------------------------------------- 1 | export const calenderViewOptions = [ 2 | { value: 'daily', label: 'Daily' }, 3 | { value: 'weekly', label: 'Weekly' }, 4 | { value: 'monthly', label: 'Monthly' } 5 | ] 6 | -------------------------------------------------------------------------------- /frontend/src/_mock/_roles.js: -------------------------------------------------------------------------------- 1 | export const roles = [ 2 | { value: 2, label: 'Administator' }, 3 | { value: 3, label: 'Sales Representative' }, 4 | { value: 4, label: 'Dispatcher' }, 5 | { value: 5, label: 'Carrier' }, 6 | { value: 6, label: 'Customer' }, 7 | { value: 7, label: 'Accountant' } 8 | ] 9 | -------------------------------------------------------------------------------- /frontend/src/_mock/_timeZoneOptions.js: -------------------------------------------------------------------------------- 1 | export const timeZoneOptions = [ 2 | { value: 'UTC', label: 'Eastern Time Zone' }, 3 | { value: 'UTC + 3.0', label: '(UTC + 3.0) New FoundLand Time Zone' }, 4 | { value: 'UTC - 1', label: '(UTC - 1) Central Standard Time' }, 5 | { value: 'UTC - 2', label: '(UTC - 2) Mountain Standard Time' }, 6 | { value: 'UTC - 3', label: '(UTC - 3) Pacific Standard Time' } 7 | ] 8 | -------------------------------------------------------------------------------- /frontend/src/_mock/boolean.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export const boolean = [ 4 | true, 5 | true, 6 | true, 7 | false, 8 | false, 9 | true, 10 | false, 11 | false, 12 | false, 13 | false, 14 | true, 15 | true, 16 | true, 17 | false, 18 | false, 19 | false, 20 | true, 21 | false, 22 | false, 23 | false, 24 | true, 25 | false, 26 | false, 27 | true, 28 | true, 29 | true, 30 | false, 31 | false, 32 | true, 33 | true, 34 | false, 35 | true, 36 | false, 37 | true, 38 | true, 39 | true, 40 | false, 41 | true, 42 | false, 43 | false 44 | ]; 45 | -------------------------------------------------------------------------------- /frontend/src/_mock/company.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export const company = [ 4 | 'Lueilwitz and Sons', 5 | 'Gleichner, Mueller and Tromp', 6 | 'Nikolaus - Leuschke', 7 | 'Hegmann, Kreiger and Bayer', 8 | 'Grimes Inc', 9 | 'Durgan - Murazik', 10 | 'Altenwerth, Medhurst and Roberts', 11 | 'Raynor Group', 12 | 'Mraz, Donnelly and Collins', 13 | 'Padberg - Bailey', 14 | 'Heidenreich, Stokes and Parker', 15 | 'Pagac and Sons', 16 | 'Rempel, Hand and Herzog', 17 | 'Dare - Treutel', 18 | 'Kihn, Marquardt and Crist', 19 | 'Nolan - Kunde', 20 | 'Wuckert Inc', 21 | 'Dibbert Inc', 22 | 'Goyette and Sons', 23 | 'Feest Group', 24 | 'Bosco and Sons', 25 | 'Bartell - Kovacek', 26 | 'Schimmel - Raynor', 27 | 'Tremblay LLC', 28 | 'Hills - Mitchell', 29 | 'Rogahn LLC', 30 | 'Kuhn, Osinski and Morar', 31 | 'Schmitt Inc', 32 | 'Breitenberg - Rosenbaum', 33 | "O'Keefe, Schneider and Mraz", 34 | 'Rohan, Langworth and Kling', 35 | 'Morar and Sons', 36 | 'Mraz LLC', 37 | 'Rowe, Parisian and Kub', 38 | 'Marquardt - Hane', 39 | 'Medhurst Group', 40 | 'Nikolaus - Lang', 41 | 'Effertz, Mohr and Olson', 42 | 'Anderson - Kris', 43 | 'Runolfsson Group' 44 | ]; 45 | -------------------------------------------------------------------------------- /frontend/src/_mock/email.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export const email = [ 4 | 'nannie_abernathy70@yahoo.com', 5 | 'ashlynn_ohara62@gmail.com', 6 | 'milo.farrell@hotmail.com', 7 | 'violet.ratke86@yahoo.com', 8 | 'letha_lubowitz24@yahoo.com', 9 | 'aditya_greenfelder31@gmail.com', 10 | 'lenna_bergnaum27@hotmail.com', 11 | 'luella.ryan33@gmail.com', 12 | 'joana.simonis84@gmail.com', 13 | 'marjolaine_white94@gmail.com', 14 | 'vergie_block82@hotmail.com', 15 | 'vito.hudson@hotmail.com', 16 | 'tyrel_greenholt@gmail.com', 17 | 'dwight.block85@yahoo.com', 18 | 'mireya13@hotmail.com', 19 | 'dasia_jenkins@hotmail.com', 20 | 'benny89@yahoo.com', 21 | 'dawn.goyette@gmail.com', 22 | 'zella_hickle4@yahoo.com', 23 | 'avery43@hotmail.com', 24 | 'olen_legros@gmail.com', 25 | 'jimmie.gerhold73@hotmail.com', 26 | 'genevieve.powlowski@hotmail.com', 27 | 'louie.kuphal39@gmail.com', 28 | 'enoch.cruickshank@gmail.com', 29 | 'arlo_mccullough@gmail.com', 30 | 'sadie18@yahoo.com', 31 | 'aric67@gmail.com', 32 | 'mack_deckow53@gmail.com', 33 | 'constantin91@yahoo.com', 34 | 'lonny84@hotmail.com', 35 | 'gus56@hotmail.com', 36 | 'brennon64@yahoo.com', 37 | 'hortense.streich@hotmail.com', 38 | 'kallie_powlowski57@hotmail.com', 39 | 'meghan.kemmer@hotmail.com', 40 | 'bella.mraz14@yahoo.com', 41 | 'barney88@gmail.com', 42 | 'diamond_johns@hotmail.com', 43 | 'gus80@hotmail.com' 44 | ]; 45 | -------------------------------------------------------------------------------- /frontend/src/_mock/funcs.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export function randomNumber(number) { 4 | return Math.floor(Math.random() * number) + 1; 5 | } 6 | 7 | export function randomNumberRange(min, max) { 8 | return Math.floor(Math.random() * (max - min + 1)) + min; 9 | } 10 | 11 | export function randomInArray(array) { 12 | return array[Math.floor(Math.random() * array.length)]; 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/_mock/index.js: -------------------------------------------------------------------------------- 1 | import _mock from './_mock'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export * from './_app'; 6 | export * from './_user'; 7 | export * from './_plans'; 8 | export * from './_others'; 9 | export * from './_booking'; 10 | export * from './_banking'; 11 | export * from './_invoice'; 12 | export * from './_ecommerce'; 13 | export * from './_analytics'; 14 | export * from './_countries'; 15 | export * from './_top100Films'; 16 | 17 | export default _mock; 18 | -------------------------------------------------------------------------------- /frontend/src/_mock/number.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export const price = [ 4 | 16.19, 35.71, 34.3, 93.1, 55.47, 89.09, 44.39, 26.92, 45.35, 26.96, 78.22, 35.54, 90.69, 63.61, 5 | 67.55, 94.75, 75.78, 39.6, 52.84, 72.8, 83.08, 85.02, 69.22, 60.96, 84.7, 16.68, 78.83, 58.07, 6 | 65.8, 55.69, 87.55, 44.74, 27.42, 84, 76.17, 43.83, 76.39, 17.42, 42.3, 12.45 7 | ]; 8 | 9 | export const rating = [ 10 | 2.5, 2, 4.9, 2, 4, 5, 4.9, 5, 3.7, 2.5, 2, 4.9, 4.8, 4, 2, 3.7, 1.4, 2.4, 1.8, 5, 2.9, 3.9, 3.9, 11 | 1.8, 5, 2.6, 3.1, 3.9, 1.2, 3.2, 4.1, 5, 4.5, 4.1, 2.3, 2.4, 5, 3.1, 4.9, 1.7 12 | ]; 13 | 14 | export const age = [ 15 | 52, 43, 56, 25, 22, 53, 38, 50, 55, 37, 16, 27, 55, 41, 52, 32, 34, 52, 31, 53, 23, 48, 43, 41, 16 | 19, 21, 17, 29, 32, 54, 38, 34, 49, 33, 55, 50, 24, 27, 23, 23 17 | ]; 18 | 19 | export const percent = [ 20 | 8.62, 86.36, 73.99, 79, 63.41, 58.79, 12.32, 88.44, 45.06, 91.64, 88.41, 73.08, 39.14, 89.34, 21 | 43.37, 34.45, 24.04, 80.96, 72.91, 47.59, 2.46, 3.33, 99.31, 47.6, 34.09, 50.61, 66.13, 46.69, 22 | 92.43, 31.41, 90.85, 36.32, 38.84, 25.6, 87.61, 1.31, 89.32, 41.23, 85.9, 62.63 23 | ]; 24 | -------------------------------------------------------------------------------- /frontend/src/_mock/phoneNumber.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export const phoneNumber = [ 4 | '365-374-4961', 5 | '904-966-2836', 6 | '399-757-9909', 7 | '692-767-2903', 8 | '990-588-5716', 9 | '955-439-2578', 10 | '226-924-4058', 11 | '552-917-1454', 12 | '285-840-9338', 13 | '306-269-2446', 14 | '883-373-6253', 15 | '476-509-8866', 16 | '201-465-1954', 17 | '538-295-9408', 18 | '531-492-6028', 19 | '981-699-7588', 20 | '500-268-4826', 21 | '205-952-3828', 22 | '222-255-5190', 23 | '408-439-8033', 24 | '272-940-8266', 25 | '812-685-8057', 26 | '353-801-5212', 27 | '606-285-8928', 28 | '202-767-8621', 29 | '222-830-0731', 30 | '964-940-3166', 31 | '262-702-2396', 32 | '886-261-9789', 33 | '352-390-5069', 34 | '343-907-8334', 35 | '575-347-2399', 36 | '749-228-5604', 37 | '774-452-2071', 38 | '607-841-0447', 39 | '395-619-2157', 40 | '233-834-0373', 41 | '586-880-2602', 42 | '746-772-0722', 43 | '638-615-3365,' 44 | ]; 45 | -------------------------------------------------------------------------------- /frontend/src/_mock/role.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export const role = [ 4 | 'ux designer', 5 | 'full stack designer', 6 | 'backend developer', 7 | 'ux designer', 8 | 'ux designer', 9 | 'project manager', 10 | 'leader', 11 | 'backend developer', 12 | 'project manager', 13 | 'ui designer', 14 | 'ui/ux designer', 15 | 'ui/ux designer', 16 | 'ui designer', 17 | 'backend developer', 18 | 'backend developer', 19 | 'front end developer', 20 | 'backend developer', 21 | 'full stack designer', 22 | 'full stack developer', 23 | 'backend developer', 24 | 'ux designer', 25 | 'ui designer', 26 | 'project manager', 27 | 'ui/ux designer', 28 | 'ui designer', 29 | 'project manager', 30 | 'full stack developer', 31 | 'hr manager', 32 | 'hr manager', 33 | 'ui/ux designer', 34 | 'project manager', 35 | 'full stack designer', 36 | 'ui designer', 37 | 'leader', 38 | 'front end developer', 39 | 'ui/ux designer', 40 | 'project manager', 41 | 'ui/ux designer', 42 | 'ui designer', 43 | 'full stack designer', 44 | ]; 45 | -------------------------------------------------------------------------------- /frontend/src/assets/illustration_background.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | // @mui 3 | import { useTheme } from '@mui/material/styles'; 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | function BackgroundIllustration() { 8 | const theme = useTheme(); 9 | 10 | const PRIMARY_MAIN = theme.palette.primary.main; 11 | 12 | return ( 13 | <> 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | ); 29 | } 30 | 31 | export default memo(BackgroundIllustration); 32 | -------------------------------------------------------------------------------- /frontend/src/assets/index.js: -------------------------------------------------------------------------------- 1 | export { default as DocIllustration } from './illustration_doc'; 2 | export { default as SeoIllustration } from './illustration_seo'; 3 | export { default as UploadIllustration } from './illustration_upload'; 4 | export { default as ForbiddenIllustration } from './illustration_403'; 5 | export { default as SeverErrorIllustration } from './illustration_500'; 6 | export { default as BookingIllustration } from './illustration_booking'; 7 | export { default as CheckInIllustration } from './illustration_checkin'; 8 | export { default as PageNotFoundIllustration } from './illustration_404'; 9 | export { default as CheckOutIllustration } from './illustration_checkout'; 10 | export { default as MotivationIllustration } from './illustration_motivation'; 11 | export { default as ComingSoonIllustration } from './illustration_coming_soon'; 12 | export { default as MaintenanceIllustration } from './illustration_maintenance'; 13 | export { default as OrderCompleteIllustration } from './illustration_order_complete'; 14 | 15 | export { default as SentIcon } from './icon_sent'; 16 | export { default as PlanFreeIcon } from './icon_plan_free'; 17 | export { default as PlanStarterIcon } from './icon_plan_starter'; 18 | export { default as PlanPremiumIcon } from './icon_plan_premium'; 19 | -------------------------------------------------------------------------------- /frontend/src/components/Avatar.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { forwardRef } from 'react'; 3 | import { useTheme } from '@mui/material/styles'; 4 | import { Avatar as MUIAvatar } from '@mui/material'; 5 | 6 | // ---------------------------------------------------------------------- 7 | 8 | const Avatar = forwardRef(({ color = 'default', children, sx, ...other }, ref) => { 9 | const theme = useTheme(); 10 | 11 | if (color === 'default') { 12 | return ( 13 | 14 | {children} 15 | 16 | ); 17 | } 18 | 19 | return ( 20 | 30 | {children} 31 | 32 | ); 33 | }); 34 | 35 | Avatar.propTypes = { 36 | children: PropTypes.node, 37 | sx: PropTypes.object, 38 | color: PropTypes.oneOf(['default', 'primary', 'secondary', 'info', 'success', 'warning', 'error']), 39 | }; 40 | 41 | export default Avatar; 42 | -------------------------------------------------------------------------------- /frontend/src/components/CopyClipboard.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { useSnackbar } from 'notistack'; 4 | import { CopyToClipboard } from 'react-copy-to-clipboard'; 5 | // @mui 6 | import { Tooltip, TextField, IconButton, InputAdornment } from '@mui/material'; 7 | // 8 | import Iconify from './Iconify'; 9 | 10 | // ---------------------------------------------------------------------- 11 | 12 | CopyClipboard.propTypes = { 13 | value: PropTypes.string, 14 | }; 15 | 16 | export default function CopyClipboard({ value, ...other }) { 17 | const { enqueueSnackbar } = useSnackbar(); 18 | const [state, setState] = useState({ 19 | value, 20 | copied: false, 21 | }); 22 | 23 | const handleChange = (event) => { 24 | setState({ value: event.target.value, copied: false }); 25 | }; 26 | 27 | const onCopy = () => { 28 | setState({ ...state, copied: true }); 29 | if (state.value) { 30 | enqueueSnackbar('Copied!'); 31 | } 32 | }; 33 | 34 | return ( 35 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ), 51 | }} 52 | {...other} 53 | /> 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /frontend/src/components/EmptyContent.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // @mui 3 | import { styled } from '@mui/material/styles'; 4 | import { Typography } from '@mui/material'; 5 | // 6 | import Image from './Image'; 7 | 8 | // ---------------------------------------------------------------------- 9 | 10 | const RootStyle = styled('div')(({ theme }) => ({ 11 | height: '100%', 12 | display: 'flex', 13 | textAlign: 'center', 14 | alignItems: 'center', 15 | flexDirection: 'column', 16 | justifyContent: 'center', 17 | padding: theme.spacing(8, 2), 18 | })); 19 | 20 | // ---------------------------------------------------------------------- 21 | 22 | EmptyContent.propTypes = { 23 | title: PropTypes.string.isRequired, 24 | img: PropTypes.string, 25 | description: PropTypes.string, 26 | }; 27 | 28 | export default function EmptyContent({ title, description, img, ...other }) { 29 | return ( 30 | 31 | empty content 38 | 39 | 40 | {title} 41 | 42 | 43 | {description && ( 44 | 45 | {description} 46 | 47 | )} 48 | 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /frontend/src/components/HeaderBreadcrumbs.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // @mui 3 | import { Box, Typography, Link } from '@mui/material'; 4 | // 5 | import Breadcrumbs from './Breadcrumbs'; 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | HeaderBreadcrumbs.propTypes = { 10 | links: PropTypes.array, 11 | action: PropTypes.node, 12 | heading: PropTypes.string.isRequired, 13 | moreLink: PropTypes.oneOfType([PropTypes.string, PropTypes.array]), 14 | sx: PropTypes.object, 15 | }; 16 | 17 | export default function HeaderBreadcrumbs({ links, action, heading, moreLink = '' || [], sx, ...other }) { 18 | return ( 19 | 20 | 21 | 22 | 23 | {heading} 24 | 25 | 26 | 27 | 28 | {action && {action}} 29 | 30 | 31 | 32 | {typeof moreLink === 'string' ? ( 33 | 34 | {moreLink} 35 | 36 | ) : ( 37 | moreLink.map((href) => ( 38 | 47 | {href} 48 | 49 | )) 50 | )} 51 | 52 | 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /frontend/src/components/Iconify.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // icons 3 | import { Icon } from '@iconify/react'; 4 | // @mui 5 | import { Box } from '@mui/material'; 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | Iconify.propTypes = { 10 | icon: PropTypes.oneOfType([PropTypes.element, PropTypes.string]), 11 | sx: PropTypes.object, 12 | }; 13 | 14 | export default function Iconify({ icon, sx, ...other }) { 15 | return ; 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/components/InputStyle.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { styled } from '@mui/material/styles'; 3 | import { TextField } from '@mui/material'; 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | const InputStyle = styled(TextField, { 8 | shouldForwardProp: (prop) => prop !== 'stretchStart', 9 | })(({ stretchStart, theme }) => ({ 10 | '& .MuiOutlinedInput-root': { 11 | transition: theme.transitions.create(['box-shadow', 'width'], { 12 | easing: theme.transitions.easing.easeInOut, 13 | duration: theme.transitions.duration.shorter, 14 | }), 15 | '&.Mui-focused': { 16 | boxShadow: theme.customShadows.z12, 17 | }, 18 | ...(stretchStart && { 19 | width: stretchStart, 20 | '&.Mui-focused': { 21 | boxShadow: theme.customShadows.z12, 22 | [theme.breakpoints.up('sm')]: { 23 | width: stretchStart + 60, 24 | }, 25 | }, 26 | }), 27 | }, 28 | '& fieldset': { 29 | borderWidth: `1px !important`, 30 | borderColor: `${theme.palette.grey[500_32]} !important`, 31 | }, 32 | })); 33 | 34 | export default InputStyle; 35 | -------------------------------------------------------------------------------- /frontend/src/components/Logo.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import { Link as RouterLink } from 'react-router-dom' 3 | // @mui 4 | // import { useTheme } from '@mui/material/styles' 5 | import { Box } from '@mui/material' 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | Logo.propTypes = { 10 | disabledLink: PropTypes.bool, 11 | sx: PropTypes.object 12 | } 13 | 14 | export default function Logo({ disabledLink = false, sx }) { 15 | // const theme = useTheme() 16 | 17 | // const PRIMARY_LIGHT = theme.palette.primary.light 18 | 19 | // const PRIMARY_MAIN = theme.palette.primary.main 20 | 21 | // const PRIMARY_DARK = theme.palette.primary.dark 22 | 23 | // OR 24 | // ------------------------------------------------------- 25 | // const logo = ( 26 | // your path 29 | // sx={{ width: 40, height: 40, cursor: 'pointer', ...sx }} 30 | // /> 31 | // ); 32 | 33 | const logo = 34 | 35 | if (disabledLink) { 36 | return <>{logo} 37 | } 38 | 39 | return {logo} 40 | } 41 | -------------------------------------------------------------------------------- /frontend/src/components/MyAvatar.js: -------------------------------------------------------------------------------- 1 | // hooks 2 | import { useSelector } from 'react-redux' 3 | // utils 4 | import createAvatar from '../utils/createAvatar' 5 | // 6 | import Avatar from './Avatar' 7 | 8 | // ---------------------------------------------------------------------- 9 | 10 | export default function MyAvatar({ ...other }) { 11 | // get user 12 | const { user } = useSelector((state) => state?.user) 13 | 14 | return ( 15 | 16 | {createAvatar(user?.name).name} 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/components/Page.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { Helmet } from 'react-helmet-async'; 3 | import { forwardRef } from 'react'; 4 | // @mui 5 | import { Box } from '@mui/material'; 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | const Page = forwardRef(({ children, title = '', meta, ...other }, ref) => ( 10 | <> 11 | 12 | {`${title}`} 13 | {meta} 14 | 15 | 16 | 17 | {children} 18 | 19 | 20 | )); 21 | 22 | Page.propTypes = { 23 | children: PropTypes.node.isRequired, 24 | title: PropTypes.string, 25 | meta: PropTypes.node, 26 | }; 27 | 28 | export default Page; 29 | -------------------------------------------------------------------------------- /frontend/src/components/ProgressBar.js: -------------------------------------------------------------------------------- 1 | import NProgress from 'nprogress'; 2 | import { useEffect, useMemo } from 'react'; 3 | // @mui 4 | import { useTheme } from '@mui/material/styles'; 5 | import { GlobalStyles } from '@mui/material'; 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | export function ProgressBarStyle() { 10 | const theme = useTheme(); 11 | 12 | return ( 13 | 40 | ); 41 | } 42 | 43 | export default function ProgressBar() { 44 | NProgress.configure({ 45 | showSpinner: false, 46 | }); 47 | 48 | useMemo(() => { 49 | NProgress.start(); 50 | }, []); 51 | 52 | useEffect(() => { 53 | NProgress.done(); 54 | }, []); 55 | 56 | return null; 57 | } 58 | -------------------------------------------------------------------------------- /frontend/src/components/ScrollToTop.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { useLocation } from 'react-router-dom'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export default function ScrollToTop() { 7 | const { pathname } = useLocation(); 8 | 9 | useEffect(() => { 10 | window.scrollTo(0, 0); 11 | }, [pathname]); 12 | 13 | return null; 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/components/SearchNotFound.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { Paper, Typography } from '@mui/material'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | SearchNotFound.propTypes = { 7 | searchQuery: PropTypes.string, 8 | }; 9 | 10 | export default function SearchNotFound({ searchQuery = '', ...other }) { 11 | return searchQuery ? ( 12 | 13 | 14 | Not found 15 | 16 | 17 | No results found for   18 | "{searchQuery}". Try checking for typos or using complete words. 19 | 20 | 21 | ) : ( 22 | Please enter keywords 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/components/SvgIconStyle.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { Box } from '@mui/material'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | SvgIconStyle.propTypes = { 7 | src: PropTypes.string.isRequired, 8 | sx: PropTypes.object, 9 | }; 10 | 11 | export default function SvgIconStyle({ src, sx }) { 12 | return ( 13 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/components/TextIconLabel.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // @mui 3 | import { Stack } from '@mui/material'; 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | TextIconLabel.propTypes = { 8 | endIcon: PropTypes.bool, 9 | icon: PropTypes.element, 10 | sx: PropTypes.object, 11 | value: PropTypes.node, 12 | }; 13 | 14 | export default function TextIconLabel({ icon, value, endIcon = false, sx, ...other }) { 15 | return ( 16 | 25 | {!endIcon && icon} 26 | {value} 27 | {endIcon && icon} 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /frontend/src/components/TextMaxLine.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { forwardRef } from 'react'; 3 | // @mui 4 | import { Typography, Link } from '@mui/material'; 5 | // utils 6 | import GetFontValue from '../utils/getFontValue'; 7 | 8 | // ---------------------------------------------------------------------- 9 | 10 | const TextMaxLine = forwardRef( 11 | ({ asLink, variant = 'body1', line = 2, persistent = false, children, sx, ...other }, ref) => { 12 | const { lineHeight } = GetFontValue(variant); 13 | 14 | const style = { 15 | overflow: 'hidden', 16 | textOverflow: 'ellipsis', 17 | display: '-webkit-box', 18 | WebkitLineClamp: line, 19 | WebkitBoxOrient: 'vertical', 20 | ...(persistent && { 21 | height: lineHeight * line, 22 | }), 23 | ...sx, 24 | }; 25 | 26 | if (asLink) { 27 | return ( 28 | 29 | {children} 30 | 31 | ); 32 | } 33 | 34 | return ( 35 | 36 | {children} 37 | 38 | ); 39 | } 40 | ); 41 | 42 | TextMaxLine.propTypes = { 43 | asLink: PropTypes.bool, 44 | children: PropTypes.node.isRequired, 45 | line: PropTypes.number, 46 | persistent: PropTypes.bool, 47 | sx: PropTypes.object, 48 | variant: PropTypes.oneOf([ 49 | 'body1', 50 | 'body2', 51 | 'button', 52 | 'caption', 53 | 'h1', 54 | 'h2', 55 | 'h3', 56 | 'h4', 57 | 'h5', 58 | 'h6', 59 | 'inherit', 60 | 'overline', 61 | 'subtitle1', 62 | 'subtitle2', 63 | ]), 64 | }; 65 | 66 | export default TextMaxLine; 67 | -------------------------------------------------------------------------------- /frontend/src/components/animate/MotionContainer.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { m } from 'framer-motion'; 3 | // @mui 4 | import { Box } from '@mui/material'; 5 | // 6 | import { varContainer } from './variants'; 7 | 8 | // ---------------------------------------------------------------------- 9 | 10 | MotionContainer.propTypes = { 11 | action: PropTypes.bool, 12 | animate: PropTypes.bool, 13 | children: PropTypes.node.isRequired 14 | }; 15 | 16 | export default function MotionContainer({ animate, action = false, children, ...other }) { 17 | if (action) { 18 | return ( 19 | 26 | {children} 27 | 28 | ); 29 | } 30 | 31 | return ( 32 | 33 | {children} 34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /frontend/src/components/animate/MotionLazyContainer.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { LazyMotion } from 'framer-motion'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | // eslint-disable-next-line import/extensions 7 | const loadFeatures = () => import('./features.js').then((res) => res.default); 8 | 9 | MotionLazyContainer.propTypes = { 10 | children: PropTypes.node 11 | }; 12 | 13 | export default function MotionLazyContainer({ children }) { 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/components/animate/MotionViewport.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import { m } from 'framer-motion' 3 | // @mui 4 | import { Box } from '@mui/material' 5 | // hooks 6 | import useResponsive from '../../hooks/useResponsive' 7 | // 8 | import { varContainer } from '.' 9 | 10 | // ---------------------------------------------------------------------- 11 | 12 | MotionViewport.propTypes = { 13 | children: PropTypes.node.isRequired, 14 | disableAnimatedMobile: PropTypes.bool 15 | } 16 | 17 | export default function MotionViewport({ children, disableAnimatedMobile = false, ...other }) { 18 | const isMobile = useResponsive('down', 'sm') 19 | 20 | if (isMobile && disableAnimatedMobile) { 21 | return {children} 22 | } 23 | 24 | return ( 25 | 33 | {children} 34 | 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /frontend/src/components/animate/TextAnimate.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { m } from 'framer-motion'; 3 | // @mui 4 | import { Box } from '@mui/material'; 5 | // 6 | import { varFade } from './variants'; 7 | 8 | // ---------------------------------------------------------------------- 9 | 10 | TextAnimate.propTypes = { 11 | text: PropTypes.string.isRequired, 12 | variants: PropTypes.object, 13 | sx: PropTypes.object 14 | }; 15 | 16 | export default function TextAnimate({ text, variants, sx, ...other }) { 17 | return ( 18 | 28 | {text.split('').map((letter, index) => ( 29 | 30 | {letter} 31 | 32 | ))} 33 | 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/components/animate/features.js: -------------------------------------------------------------------------------- 1 | import { domMax } from 'framer-motion'; 2 | 3 | export default domMax; 4 | -------------------------------------------------------------------------------- /frontend/src/components/animate/index.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export * from './variants'; 4 | 5 | export { default as DialogAnimate } from './DialogAnimate'; 6 | export { default as TextAnimate } from './TextAnimate'; 7 | 8 | export { default as FabButtonAnimate } from './FabButtonAnimate'; 9 | export { default as IconButtonAnimate } from './IconButtonAnimate'; 10 | 11 | export { default as MotionViewport } from './MotionViewport'; 12 | export { default as MotionContainer } from './MotionContainer'; 13 | export { default as MotionLazyContainer } from './MotionLazyContainer'; 14 | -------------------------------------------------------------------------------- /frontend/src/components/animate/variants/actions.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export const varHover = (scale) => ({ 4 | hover: { 5 | scale: scale || 1.1 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /frontend/src/components/animate/variants/container.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export const varContainer = (props) => { 4 | const staggerIn = props?.staggerIn || 0.05; 5 | const delayIn = props?.staggerIn || 0.05; 6 | const staggerOut = props?.staggerIn || 0.05; 7 | 8 | return { 9 | animate: { 10 | transition: { 11 | staggerChildren: staggerIn, 12 | delayChildren: delayIn 13 | } 14 | }, 15 | exit: { 16 | transition: { 17 | staggerChildren: staggerOut, 18 | staggerDirection: -1 19 | } 20 | } 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /frontend/src/components/animate/variants/flip.js: -------------------------------------------------------------------------------- 1 | import { varTranEnter, varTranExit } from './transition'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export const varFlip = (props) => { 6 | const durationIn = props?.durationIn; 7 | const durationOut = props?.durationOut; 8 | const easeIn = props?.easeIn; 9 | const easeOut = props?.easeOut; 10 | 11 | return { 12 | // IN 13 | inX: { 14 | initial: { rotateX: -180, opacity: 0 }, 15 | animate: { rotateX: 0, opacity: 1, transition: varTranEnter({ durationIn, easeIn }) }, 16 | exit: { rotateX: -180, opacity: 0, transition: varTranExit({ durationOut, easeOut }) } 17 | }, 18 | inY: { 19 | initial: { rotateY: -180, opacity: 0 }, 20 | animate: { rotateY: 0, opacity: 1, transition: varTranEnter({ durationIn, easeIn }) }, 21 | exit: { rotateY: -180, opacity: 0, transition: varTranExit({ durationOut, easeOut }) } 22 | }, 23 | 24 | // OUT 25 | outX: { 26 | initial: { rotateX: 0, opacity: 1 }, 27 | animate: { rotateX: 70, opacity: 0, transition: varTranExit({ durationOut, easeOut }) } 28 | }, 29 | outY: { 30 | initial: { rotateY: 0, opacity: 1 }, 31 | animate: { rotateY: 70, opacity: 0, transition: varTranExit({ durationOut, easeOut }) } 32 | } 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /frontend/src/components/animate/variants/index.js: -------------------------------------------------------------------------------- 1 | export * from './path'; 2 | export * from './fade'; 3 | export * from './zoom'; 4 | export * from './flip'; 5 | export * from './slide'; 6 | export * from './scale'; 7 | export * from './bounce'; 8 | export * from './rotate'; 9 | export * from './actions'; 10 | export * from './container'; 11 | export * from './transition'; 12 | export * from './background'; 13 | -------------------------------------------------------------------------------- /frontend/src/components/animate/variants/path.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export const TRANSITION = { 4 | duration: 2, 5 | ease: [0.43, 0.13, 0.23, 0.96] 6 | }; 7 | 8 | export const varPath = { 9 | animate: { 10 | fillOpacity: [0, 0, 1], 11 | pathLength: [1, 0.4, 0], 12 | transition: TRANSITION 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /frontend/src/components/animate/variants/rotate.js: -------------------------------------------------------------------------------- 1 | // 2 | import { varTranEnter, varTranExit } from './transition'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export const varRotate = (props) => { 7 | const durationIn = props?.durationIn; 8 | const durationOut = props?.durationOut; 9 | const easeIn = props?.easeIn; 10 | const easeOut = props?.easeOut; 11 | 12 | return { 13 | // IN 14 | in: { 15 | initial: { opacity: 0, rotate: -360 }, 16 | animate: { opacity: 1, rotate: 0, transition: varTranEnter({ durationIn, easeIn }) }, 17 | exit: { opacity: 0, rotate: -360, transition: varTranExit({ durationOut, easeOut }) } 18 | }, 19 | 20 | // OUT 21 | out: { 22 | initial: { opacity: 1, rotate: 0 }, 23 | animate: { opacity: 0, rotate: -360, transition: varTranExit({ durationOut, easeOut }) } 24 | } 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /frontend/src/components/animate/variants/scale.js: -------------------------------------------------------------------------------- 1 | // 2 | import { varTranEnter, varTranExit } from './transition'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export const varScale = (props) => { 7 | const durationIn = props?.durationIn; 8 | const durationOut = props?.durationOut; 9 | const easeIn = props?.easeIn; 10 | const easeOut = props?.easeOut; 11 | 12 | return { 13 | // IN 14 | inX: { 15 | initial: { scaleX: 0, opacity: 0 }, 16 | animate: { scaleX: 1, opacity: 1, transition: varTranEnter({ durationIn, easeIn }) }, 17 | exit: { scaleX: 0, opacity: 0, transition: varTranExit({ durationOut, easeOut }) } 18 | }, 19 | inY: { 20 | initial: { scaleY: 0, opacity: 0 }, 21 | animate: { scaleY: 1, opacity: 1, transition: varTranEnter({ durationIn, easeIn }) }, 22 | exit: { scaleY: 0, opacity: 0, transition: varTranExit({ durationOut, easeOut }) } 23 | }, 24 | 25 | // OUT 26 | outX: { 27 | initial: { scaleX: 1, opacity: 1 }, 28 | animate: { scaleX: 0, opacity: 0, transition: varTranEnter({ durationIn, easeIn }) } 29 | }, 30 | outY: { 31 | initial: { scaleY: 1, opacity: 1 }, 32 | animate: { scaleY: 0, opacity: 0, transition: varTranEnter({ durationIn, easeIn }) } 33 | } 34 | }; 35 | }; 36 | -------------------------------------------------------------------------------- /frontend/src/components/animate/variants/transition.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export const varTranHover = (props) => { 4 | const duration = props?.duration || 0.32; 5 | const ease = props?.ease || [0.43, 0.13, 0.23, 0.96]; 6 | 7 | return { duration, ease }; 8 | }; 9 | 10 | export const varTranEnter = (props) => { 11 | const duration = props?.durationIn || 0.64; 12 | const ease = props?.easeIn || [0.43, 0.13, 0.23, 0.96]; 13 | 14 | return { duration, ease }; 15 | }; 16 | 17 | export const varTranExit = (props) => { 18 | const duration = props?.durationOut || 0.48; 19 | const ease = props?.easeOut || [0.43, 0.13, 0.23, 0.96]; 20 | 21 | return { duration, ease }; 22 | }; 23 | -------------------------------------------------------------------------------- /frontend/src/components/carousel/index.js: -------------------------------------------------------------------------------- 1 | export { default as CarouselDots } from './CarouselDots'; 2 | export { default as CarouselArrows } from './CarouselArrows'; 3 | export { default as CarouselArrowIndex } from './CarouselArrowIndex'; 4 | -------------------------------------------------------------------------------- /frontend/src/components/chart/index.js: -------------------------------------------------------------------------------- 1 | export { default as ChartStyle } from './ChartStyle'; 2 | export { default as BaseOptionChart } from './BaseOptionChart'; 3 | -------------------------------------------------------------------------------- /frontend/src/components/color-utils/ColorPreview.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // @mui 3 | import { alpha, styled } from '@mui/material/styles'; 4 | import { Box, Typography } from '@mui/material'; 5 | 6 | // ---------------------------------------------------------------------- 7 | 8 | const RootStyle = styled(Box)(() => ({ 9 | display: 'flex', 10 | alignItems: 'center', 11 | justifyContent: 'flex-end', 12 | })); 13 | 14 | const IconStyle = styled('div')(({ theme }) => ({ 15 | marginLeft: -4, 16 | borderRadius: '50%', 17 | width: theme.spacing(2), 18 | height: theme.spacing(2), 19 | border: `solid 2px ${theme.palette.background.paper}`, 20 | boxShadow: `inset -1px 1px 2px ${alpha(theme.palette.common.black, 0.24)}`, 21 | })); 22 | 23 | // ---------------------------------------------------------------------- 24 | 25 | ColorPreview.propTypes = { 26 | colors: PropTypes.arrayOf(PropTypes.string), 27 | limit: PropTypes.number, 28 | sx: PropTypes.object, 29 | }; 30 | 31 | export default function ColorPreview({ colors, limit = 3, sx }) { 32 | const showColor = colors.slice(0, limit); 33 | const moreColor = colors.length - limit; 34 | 35 | return ( 36 | 37 | {showColor.map((color, index) => ( 38 | 39 | ))} 40 | 41 | {colors.length > limit && {`+${moreColor}`}} 42 | 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /frontend/src/components/color-utils/index.js: -------------------------------------------------------------------------------- 1 | export { default as ColorPreview } from './ColorPreview'; 2 | export { default as ColorManyPicker } from './ColorManyPicker'; 3 | export { default as ColorSinglePicker } from './ColorSinglePicker'; 4 | -------------------------------------------------------------------------------- /frontend/src/components/emoji-picker/index.js: -------------------------------------------------------------------------------- 1 | export { default as EmojiPicker } from './EmojiPicker'; 2 | -------------------------------------------------------------------------------- /frontend/src/components/hook-form/FormProvider.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // form 3 | import { FormProvider as Form } from 'react-hook-form'; 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | FormProvider.propTypes = { 8 | children: PropTypes.node.isRequired, 9 | methods: PropTypes.object.isRequired, 10 | onSubmit: PropTypes.func, 11 | }; 12 | 13 | export default function FormProvider({ children, onSubmit, methods }) { 14 | return ( 15 |
16 | {children}
17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/components/hook-form/RHFEditor.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // form 3 | import { useFormContext, Controller } from 'react-hook-form'; 4 | // @mui 5 | import { FormHelperText } from '@mui/material'; 6 | // 7 | import Editor from '../editor'; 8 | 9 | // ---------------------------------------------------------------------- 10 | 11 | RHFEditor.propTypes = { 12 | name: PropTypes.string, 13 | }; 14 | 15 | export default function RHFEditor({ name, ...other }) { 16 | const { control } = useFormContext(); 17 | 18 | return ( 19 | ( 23 | 30 | {error?.message} 31 | 32 | } 33 | {...other} 34 | /> 35 | )} 36 | /> 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /frontend/src/components/hook-form/RHFNumberField.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | // form 3 | import { useFormContext, Controller } from 'react-hook-form' 4 | // @mui 5 | import { TextField } from '@mui/material' 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | RHFNumberField.propTypes = { 10 | name: PropTypes.string 11 | } 12 | 13 | export default function RHFNumberField({ name, ...other }) { 14 | const { control } = useFormContext() 15 | 16 | return ( 17 | ( 21 | 30 | )} 31 | /> 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /frontend/src/components/hook-form/RHFRadioGroup.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | // form 3 | import { useFormContext, Controller } from 'react-hook-form' 4 | // @mui 5 | import { Radio, RadioGroup, FormHelperText, FormControlLabel } from '@mui/material' 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | RHFRadioGroup.propTypes = { 10 | name: PropTypes.string, 11 | options: PropTypes.array 12 | } 13 | 14 | export default function RHFRadioGroup({ name, options, ...other }) { 15 | const { control } = useFormContext() 16 | 17 | return ( 18 | ( 22 |
23 | 24 | {options.map((option) => ( 25 | } label={option.label} /> 26 | ))} 27 | 28 | 29 | {!!error && ( 30 | 31 | {error.message} 32 | 33 | )} 34 |
35 | )} 36 | /> 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /frontend/src/components/hook-form/RHFSelect.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // form 3 | import { useFormContext, Controller } from 'react-hook-form'; 4 | // @mui 5 | import { TextField } from '@mui/material'; 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | RHFSelect.propTypes = { 10 | children: PropTypes.node, 11 | name: PropTypes.string, 12 | }; 13 | 14 | export default function RHFSelect({ name, children, ...other }) { 15 | const { control } = useFormContext(); 16 | 17 | return ( 18 | ( 22 | 31 | {children} 32 | 33 | )} 34 | /> 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /frontend/src/components/hook-form/RHFSlider.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | // form 3 | import { useFormContext, Controller } from 'react-hook-form' 4 | // @mui 5 | import { Slider } from '@mui/material' 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | RHFSlider.propTypes = { 10 | name: PropTypes.string 11 | } 12 | 13 | export default function RHFSlider({ name, ...other }) { 14 | const { control } = useFormContext() 15 | 16 | return } /> 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/components/hook-form/RHFSwitch.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // form 3 | import { useFormContext, Controller } from 'react-hook-form'; 4 | // @mui 5 | import { Switch, FormControlLabel } from '@mui/material'; 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | RHFSwitch.propTypes = { 10 | name: PropTypes.string, 11 | }; 12 | 13 | export default function RHFSwitch({ name, ...other }) { 14 | const { control } = useFormContext(); 15 | 16 | return ( 17 | } /> 20 | } 21 | {...other} 22 | /> 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/components/hook-form/RHFTextField.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | // form 3 | import { useFormContext, Controller } from 'react-hook-form' 4 | // @mui 5 | import { TextField } from '@mui/material' 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | RHFTextField.propTypes = { 10 | name: PropTypes.string 11 | } 12 | 13 | export default function RHFTextField({ name, ...other }) { 14 | const { control } = useFormContext() 15 | 16 | return ( 17 | ( 21 | 29 | )} 30 | /> 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /frontend/src/components/hook-form/index.js: -------------------------------------------------------------------------------- 1 | export * from './RHFUpload' 2 | export * from './RHFCheckbox' 3 | 4 | export { default as RHFSwitch } from './RHFSwitch' 5 | export { default as RHFSelect } from './RHFSelect' 6 | export { default as RHFEditor } from './RHFEditor' 7 | export { default as RHFSlider } from './RHFSlider' 8 | export { default as RHFTextField } from './RHFTextField' 9 | export { default as RHFNumberField } from './RHFNumberField' 10 | export { default as RHFRadioGroup } from './RHFRadioGroup' 11 | 12 | export { default as FormProvider } from './FormProvider' 13 | -------------------------------------------------------------------------------- /frontend/src/components/map/MapControl.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { NavigationControl, FullscreenControl, ScaleControl, GeolocateControl } from 'react-map-gl'; 3 | // 4 | import { ControlStyle } from './style'; 5 | 6 | // ---------------------------------------------------------------------- 7 | 8 | MapControl.propTypes = { 9 | hideFullscreenControl: PropTypes.bool, 10 | hideGeolocateControl: PropTypes.bool, 11 | hideNavigationnControl: PropTypes.bool, 12 | hideScaleControl: PropTypes.bool, 13 | }; 14 | 15 | export default function MapControl({ 16 | hideScaleControl, 17 | hideGeolocateControl, 18 | hideFullscreenControl, 19 | hideNavigationnControl, 20 | }) { 21 | return ( 22 | <> 23 | 24 | 25 | {!hideGeolocateControl && } 26 | 27 | {!hideFullscreenControl && } 28 | 29 | {!hideScaleControl && } 30 | 31 | {!hideNavigationnControl && } 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /frontend/src/components/map/MapMarker.js: -------------------------------------------------------------------------------- 1 | import { Marker } from 'react-map-gl'; 2 | // @mui 3 | import { Box } from '@mui/material'; 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | const SIZE = 20; 8 | 9 | const ICON = `M20.2,15.7L20.2,15.7c1.1-1.6,1.8-3.6,1.8-5.7c0-5.6-4.5-10-10-10S2,4.5,2,10c0,2,0.6,3.9,1.6,5.4c0,0.1,0.1,0.2,0.2,0.3 10 | c0,0,0.1,0.1,0.1,0.2c0.2,0.3,0.4,0.6,0.7,0.9c2.6,3.1,7.4,7.6,7.4,7.6s4.8-4.5,7.4-7.5c0.2-0.3,0.5-0.6,0.7-0.9 11 | C20.1,15.8,20.2,15.8,20.2,15.7z`; 12 | 13 | // ---------------------------------------------------------------------- 14 | 15 | export default function MapMarker({ ...other }) { 16 | return ( 17 | 18 | theme.palette.error.main, 26 | transform: `translate(${-SIZE / 2}px,${-SIZE}px)`, 27 | }} 28 | > 29 | 30 | 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /frontend/src/components/map/MapPopup.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // 3 | import { PopupStyle } from './style'; 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | MapPopup.propTypes = { 8 | children: PropTypes.node, 9 | sx: PropTypes.object, 10 | }; 11 | 12 | export default function MapPopup({ sx, children, ...other }) { 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/components/map/index.js: -------------------------------------------------------------------------------- 1 | export * from './style'; 2 | 3 | export { default as MapPopup } from './MapPopup'; 4 | export { default as MapMarker } from './MapMarker'; 5 | export { default as MapControl } from './MapControl'; 6 | -------------------------------------------------------------------------------- /frontend/src/components/mega-menu/MenuHotProducts.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { Link as RouterLink } from 'react-router-dom'; 3 | // @mui 4 | import { Link, Typography, Box } from '@mui/material'; 5 | 6 | // ---------------------------------------------------------------------- 7 | 8 | MenuHotProducts.propTypes = { 9 | tags: PropTypes.array.isRequired, 10 | }; 11 | 12 | export default function MenuHotProducts({ tags, ...other }) { 13 | return ( 14 | 15 | 16 | Hot Products: 17 | 18 |   19 | {tags.map((tag, index) => ( 20 | theme.transitions.create('all'), 29 | '&:hover': { color: 'primary.main' }, 30 | }} 31 | > 32 | {index === 0 ? tag.name : `, ${tag.name} `} 33 | 34 | ))} 35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /frontend/src/components/mega-menu/index.js: -------------------------------------------------------------------------------- 1 | export { default as MegaMenuDesktopVertical } from './MegaMenuDesktopVertical'; 2 | export { default as MegaMenuDesktopHorizon } from './MegaMenuDesktopHorizon'; 3 | export { default as MegaMenuMobile } from './MegaMenuMobile'; 4 | export { default as MenuConfig } from './MenuConfig'; 5 | -------------------------------------------------------------------------------- /frontend/src/components/nav-section/horizontal/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import { memo } from 'react' 3 | // @mui 4 | import { Stack } from '@mui/material' 5 | // 6 | import NavList from './NavList' 7 | 8 | // ---------------------------------------------------------------------- 9 | 10 | const hideScrollbar = { 11 | msOverflowStyle: 'none', 12 | scrollbarWidth: 'none', 13 | overflowY: 'scroll', 14 | '&::-webkit-scrollbar': { 15 | display: 'none' 16 | } 17 | } 18 | 19 | NavSectionHorizontal.propTypes = { 20 | navConfig: PropTypes.array 21 | } 22 | 23 | function NavSectionHorizontal({ navConfig }) { 24 | return ( 25 | 26 | 27 | {navConfig.map((group) => ( 28 | 29 | {group.items.map((list) => ( 30 | 31 | ))} 32 | 33 | ))} 34 | 35 | 36 | ) 37 | } 38 | 39 | export default memo(NavSectionHorizontal) 40 | -------------------------------------------------------------------------------- /frontend/src/components/nav-section/index.js: -------------------------------------------------------------------------------- 1 | import { matchPath } from 'react-router-dom'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export { default as NavSectionVertical } from './vertical'; 6 | export { default as NavSectionHorizontal } from './horizontal'; 7 | 8 | export function isExternalLink(path) { 9 | return path.includes('http'); 10 | } 11 | 12 | export function getActive(path, pathname) { 13 | return path ? !!matchPath({ path, end: false }, pathname) : false; 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/components/nav-section/vertical/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // @mui 3 | import { List, Box } from '@mui/material'; 4 | // hooks 5 | import useLocales from '../../../hooks/useLocales'; 6 | // 7 | import { ListSubheaderStyle } from './style'; 8 | import NavList from './NavList'; 9 | 10 | // ---------------------------------------------------------------------- 11 | 12 | NavSectionVertical.propTypes = { 13 | isCollapse: PropTypes.bool, 14 | navConfig: PropTypes.array, 15 | }; 16 | 17 | export default function NavSectionVertical({ navConfig, isCollapse, ...other }) { 18 | const { translate } = useLocales(); 19 | 20 | return ( 21 | 22 | {navConfig.map((group) => ( 23 | 24 | 31 | {translate(group.subheader)} 32 | 33 | 34 | {group.items.map((list) => ( 35 | 42 | ))} 43 | 44 | ))} 45 | 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /frontend/src/components/organizational-chart/node/SimpleNode.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // @mui 3 | import { alpha } from '@mui/material/styles'; 4 | import { Typography, Card } from '@mui/material'; 5 | 6 | // ---------------------------------------------------------------------- 7 | 8 | SimpleNode.propTypes = { 9 | node: PropTypes.shape({ 10 | name: PropTypes.string, 11 | }), 12 | sx: PropTypes.object, 13 | }; 14 | 15 | export default function SimpleNode({ node, sx }) { 16 | return ( 17 | alpha(theme.palette.primary.main, 0.08), 26 | border: (theme) => `1px solid ${alpha(theme.palette.primary.main, 0.24)}`, 27 | ...sx, 28 | }} 29 | > 30 | {node.name} 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /frontend/src/components/organizational-chart/node/index.js: -------------------------------------------------------------------------------- 1 | export { default as GroupNode } from './GroupNode'; 2 | export { default as SimpleNode } from './SimpleNode'; 3 | export { default as StandardNode } from './StandardNode'; 4 | -------------------------------------------------------------------------------- /frontend/src/components/settings/ThemeColorPresets.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { useMemo } from 'react'; 3 | // @mui 4 | import { alpha, ThemeProvider, createTheme, useTheme } from '@mui/material/styles'; 5 | // hooks 6 | import useSettings from '../../hooks/useSettings'; 7 | // 8 | import componentsOverride from '../../theme/overrides'; 9 | 10 | // ---------------------------------------------------------------------- 11 | 12 | ThemeColorPresets.propTypes = { 13 | children: PropTypes.node, 14 | }; 15 | 16 | export default function ThemeColorPresets({ children }) { 17 | const defaultTheme = useTheme(); 18 | 19 | const { setColor } = useSettings(); 20 | 21 | const themeOptions = useMemo( 22 | () => ({ 23 | ...defaultTheme, 24 | palette: { 25 | ...defaultTheme.palette, 26 | primary: setColor, 27 | }, 28 | customShadows: { 29 | ...defaultTheme.customShadows, 30 | primary: `0 8px 16px 0 ${alpha(setColor.main, 0.24)}`, 31 | }, 32 | }), 33 | [setColor, defaultTheme] 34 | ); 35 | 36 | const theme = createTheme(themeOptions); 37 | 38 | theme.components = componentsOverride(theme); 39 | 40 | return {children}; 41 | } 42 | -------------------------------------------------------------------------------- /frontend/src/components/settings/ThemeLocalization.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // @mui 3 | import { ThemeProvider, createTheme, useTheme } from '@mui/material/styles'; 4 | // hooks 5 | import useLocales from '../../hooks/useLocales'; 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | ThemeLocalization.propTypes = { 10 | children: PropTypes.node.isRequired, 11 | }; 12 | 13 | export default function ThemeLocalization({ children }) { 14 | const defaultTheme = useTheme(); 15 | 16 | const { currentLang } = useLocales(); 17 | 18 | const theme = createTheme(defaultTheme, currentLang.systemValue); 19 | 20 | return {children}; 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/components/settings/ThemeRtlLayout.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { useEffect } from 'react'; 3 | // rtl 4 | import rtlPlugin from 'stylis-plugin-rtl'; 5 | // emotion 6 | import createCache from '@emotion/cache'; 7 | import { CacheProvider } from '@emotion/react'; 8 | // @mui 9 | import { useTheme } from '@mui/material/styles'; 10 | 11 | // ---------------------------------------------------------------------- 12 | 13 | ThemeRtlLayout.propTypes = { 14 | children: PropTypes.node, 15 | }; 16 | 17 | export default function ThemeRtlLayout({ children }) { 18 | const theme = useTheme(); 19 | 20 | useEffect(() => { 21 | document.dir = theme.direction; 22 | }, [theme.direction]); 23 | 24 | const cacheRtl = createCache({ 25 | key: theme.direction === 'rtl' ? 'rtl' : 'css', 26 | stylisPlugins: theme.direction === 'rtl' ? [rtlPlugin] : [], 27 | }); 28 | 29 | return {children}; 30 | } 31 | -------------------------------------------------------------------------------- /frontend/src/components/settings/drawer/BoxMask.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // @mui 3 | import { Radio, FormControlLabel } from '@mui/material'; 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | BoxMask.propTypes = { 8 | value: PropTypes.string, 9 | }; 10 | 11 | export default function BoxMask({ value }) { 12 | return ( 13 | } 17 | sx={{ 18 | m: 0, 19 | top: 0, 20 | right: 0, 21 | bottom: 0, 22 | left: 0, 23 | position: 'absolute', 24 | }} 25 | /> 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/components/settings/drawer/SettingFullscreen.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | // @mui 3 | import { alpha } from '@mui/material/styles'; 4 | import { Button } from '@mui/material'; 5 | // 6 | import Iconify from '../../Iconify'; 7 | 8 | // ---------------------------------------------------------------------- 9 | 10 | export default function SettingFullscreen() { 11 | const [fullscreen, setFullscreen] = useState(false); 12 | 13 | const toggleFullScreen = () => { 14 | if (!document.fullscreenElement) { 15 | document.documentElement.requestFullscreen(); 16 | setFullscreen(true); 17 | } else if (document.exitFullscreen) { 18 | document.exitFullscreen(); 19 | setFullscreen(false); 20 | } 21 | }; 22 | 23 | return ( 24 | 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /frontend/src/components/settings/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // 3 | import SettingsDrawer from './drawer'; 4 | // 5 | import ThemeContrast from './ThemeContrast'; 6 | import ThemeRtlLayout from './ThemeRtlLayout'; 7 | import ThemeColorPresets from './ThemeColorPresets'; 8 | import ThemeLocalization from './ThemeLocalization'; 9 | 10 | // ---------------------------------------------------------------------- 11 | 12 | ThemeSettings.propTypes = { 13 | children: PropTypes.node.isRequired, 14 | }; 15 | 16 | export default function ThemeSettings({ children }) { 17 | return ( 18 | 19 | 20 | 21 | 22 | {children} 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/components/skeleton/SkeletonConversationItem.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { Stack, Skeleton } from '@mui/material'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export default function SkeletonConversationItem() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/components/skeleton/SkeletonKanbanColumn.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { Stack, Skeleton, Box, Paper } from '@mui/material'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export default function SkeletonKanbanColumn() { 7 | return ( 8 | 15 | {[...Array(3)].map((_, index) => ( 16 | 17 | 18 | 19 | {index === 0 && ( 20 | 21 | )} 22 | {index !== 2 && ( 23 | 24 | )} 25 | 26 | 27 | ))} 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /frontend/src/components/skeleton/SkeletonMailSidebarItem.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { Stack, Skeleton } from '@mui/material'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export default function SkeletonMailSidebarItem() { 7 | return ( 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/components/skeleton/SkeletonMap.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { Stack, Skeleton } from '@mui/material'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export default function SkeletonMap() { 7 | return ( 8 | 9 | {[...Array(5)].map((_, index) => ( 10 | 15 | ))} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/components/skeleton/SkeletonPost.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { Box, Skeleton } from '@mui/material'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export default function SkeletonPost() { 7 | return ( 8 | <> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/components/skeleton/SkeletonPostItem.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { Box, Skeleton, Grid } from '@mui/material'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export default function SkeletonPostItem() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/components/skeleton/SkeletonProduct.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { Grid, Skeleton } from '@mui/material'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export default function SkeletonProduct() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/components/skeleton/SkeletonProductItem.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { Card, Skeleton, Stack } from '@mui/material'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export default function SkeletonProductItem() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /frontend/src/components/skeleton/index.js: -------------------------------------------------------------------------------- 1 | export { default as SkeletonMap } from './SkeletonMap'; 2 | export { default as SkeletonPost } from './SkeletonPost'; 3 | export { default as SkeletonProduct } from './SkeletonProduct'; 4 | export { default as SkeletonPostItem } from './SkeletonPostItem'; 5 | export { default as SkeletonProductItem } from './SkeletonProductItem'; 6 | export { default as SkeletonKanbanColumn } from './SkeletonKanbanColumn'; 7 | export { default as SkeletonMailSidebarItem } from './SkeletonMailSidebarItem'; 8 | export { default as SkeletonConversationItem } from './SkeletonConversationItem'; 9 | -------------------------------------------------------------------------------- /frontend/src/components/table/TableEmptyRows.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | // @mui 3 | import { TableRow, TableCell } from '@mui/material' 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | TableEmptyRows.propTypes = { 8 | emptyRows: PropTypes.number, 9 | height: PropTypes.number 10 | } 11 | 12 | export default function TableEmptyRows({ emptyRows, height }) { 13 | if (!emptyRows) { 14 | return null 15 | } 16 | 17 | return ( 18 | 25 | 26 | 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/components/table/TableMoreMenu.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | // @mui 3 | import { IconButton } from '@mui/material' 4 | // 5 | import Iconify from '../Iconify' 6 | import MenuPopover from '../MenuPopover' 7 | 8 | // ---------------------------------------------------------------------- 9 | 10 | TableMoreMenu.propTypes = { 11 | actions: PropTypes.node, 12 | open: PropTypes.object, 13 | onClose: PropTypes.func, 14 | onOpen: PropTypes.func 15 | } 16 | 17 | export default function TableMoreMenu({ actions, open, onClose, onOpen }) { 18 | return ( 19 | <> 20 | 21 | 22 | 23 | 24 | 42 | {actions} 43 | 44 | 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /frontend/src/components/table/TableNoData.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | // @mui 3 | import { TableRow, TableCell } from '@mui/material' 4 | // 5 | import EmptyContent from '../EmptyContent' 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | TableNoData.propTypes = { 10 | isNotFound: PropTypes.bool 11 | } 12 | 13 | export default function TableNoData({ isNotFound }) { 14 | return ( 15 | 16 | {isNotFound ? ( 17 | 18 | 24 | 25 | ) : ( 26 | 27 | )} 28 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /frontend/src/components/table/TableSelectedActions.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | // @mui 3 | import { Checkbox, Typography, Stack } from '@mui/material' 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | TableSelectedActions.propTypes = { 8 | dense: PropTypes.bool, 9 | actions: PropTypes.node, 10 | rowCount: PropTypes.number, 11 | numSelected: PropTypes.number, 12 | onSelectAllRows: PropTypes.func, 13 | sx: PropTypes.object 14 | } 15 | 16 | export default function TableSelectedActions({ dense, actions, rowCount, numSelected, onSelectAllRows, sx, ...other }) { 17 | return ( 18 | 39 | 0 && numSelected < rowCount} 41 | checked={rowCount > 0 && numSelected === rowCount} 42 | onChange={(event) => onSelectAllRows(event.target.checked)} 43 | /> 44 | 45 | 56 | {numSelected} selected 57 | 58 | 59 | {actions && actions} 60 | 61 | ) 62 | } 63 | -------------------------------------------------------------------------------- /frontend/src/components/table/TableSkeleton.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { TableRow, TableCell, Skeleton, Stack } from '@mui/material' 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export default function TableSkeleton({ ...other }) { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/components/table/index.js: -------------------------------------------------------------------------------- 1 | export { default as TableNoData } from './TableNoData'; 2 | export { default as TableMoreMenu } from './TableMoreMenu'; 3 | export { default as TableSkeleton } from './TableSkeleton'; 4 | export { default as TableEmptyRows } from './TableEmptyRows'; 5 | export { default as TableHeadCustom } from './TableHeadCustom'; 6 | export { default as TableSelectedActions } from './TableSelectedActions'; 7 | -------------------------------------------------------------------------------- /frontend/src/components/upload/BlockContent.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { Box, Typography, Stack } from '@mui/material'; 3 | // assets 4 | import { UploadIllustration } from '../../assets'; 5 | 6 | // ---------------------------------------------------------------------- 7 | 8 | export default function BlockContent() { 9 | return ( 10 | 17 | 18 | 19 | 20 | 21 | Drop or Select file 22 | 23 | 24 | 25 | Drop files here or click  26 | 31 | browse 32 | 33 |  thorough your machine 34 | 35 | 36 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /frontend/src/components/upload/RejectionFiles.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // @mui 3 | import { alpha } from '@mui/material/styles'; 4 | import { Box, Paper, Typography } from '@mui/material'; 5 | // utils 6 | import { fData } from '../../utils/formatNumber'; 7 | import getFileData from '../../utils/getFileData'; 8 | 9 | // ---------------------------------------------------------------------- 10 | 11 | RejectionFiles.propTypes = { 12 | fileRejections: PropTypes.array.isRequired, 13 | }; 14 | 15 | export default function RejectionFiles({ fileRejections }) { 16 | return ( 17 | alpha(theme.palette.error.main, 0.08), 25 | }} 26 | > 27 | {fileRejections.map(({ file, errors }) => { 28 | const { path, size } = getFileData(file); 29 | 30 | return ( 31 | 32 | 33 | {path} - {size ? fData(size) : ''} 34 | 35 | 36 | {errors.map((error) => ( 37 | 38 | {error.message} 39 | 40 | ))} 41 | 42 | ); 43 | })} 44 | 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /frontend/src/components/upload/index.js: -------------------------------------------------------------------------------- 1 | export { default as BlockContent } from './BlockContent'; 2 | export { default as RejectionFiles } from './RejectionFiles'; 3 | export { default as MultiFilePreview } from './MultiFilePreview'; 4 | 5 | export { default as UploadAvatar } from './UploadAvatar'; 6 | export { default as UploadMultiFile } from './UploadMultiFile'; 7 | export { default as UploadSingleFile } from './UploadSingleFile'; 8 | -------------------------------------------------------------------------------- /frontend/src/guards/AuthGuard.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { useState } from 'react'; 3 | import { Navigate, useLocation } from 'react-router-dom'; 4 | // hooks 5 | import useAuth from '../hooks/useAuth'; 6 | // pages 7 | import Login from '../pages/auth/Login'; 8 | // components 9 | import LoadingScreen from '../components/LoadingScreen'; 10 | 11 | // ---------------------------------------------------------------------- 12 | 13 | AuthGuard.propTypes = { 14 | children: PropTypes.node, 15 | }; 16 | 17 | export default function AuthGuard({ children }) { 18 | const { isAuthenticated, isInitialized } = useAuth(); 19 | 20 | const { pathname } = useLocation(); 21 | 22 | const [requestedLocation, setRequestedLocation] = useState(null); 23 | 24 | if (!isInitialized) { 25 | return ; 26 | } 27 | 28 | if (!isAuthenticated) { 29 | if (pathname !== requestedLocation) { 30 | setRequestedLocation(pathname); 31 | } 32 | return ; 33 | } 34 | 35 | if (requestedLocation && pathname !== requestedLocation) { 36 | setRequestedLocation(null); 37 | return ; 38 | } 39 | 40 | return <>{children}; 41 | } 42 | -------------------------------------------------------------------------------- /frontend/src/guards/GuestGuard.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { Navigate } from 'react-router-dom'; 3 | // hooks 4 | import useAuth from '../hooks/useAuth'; 5 | // routes 6 | import { PATH_DASHBOARD } from '../routes/paths'; 7 | // components 8 | import LoadingScreen from '../components/LoadingScreen'; 9 | 10 | // ---------------------------------------------------------------------- 11 | 12 | GuestGuard.propTypes = { 13 | children: PropTypes.node, 14 | }; 15 | 16 | export default function GuestGuard({ children }) { 17 | const { isAuthenticated, isInitialized } = useAuth(); 18 | 19 | if (isAuthenticated) { 20 | return ; 21 | } 22 | 23 | if (!isInitialized) { 24 | return ; 25 | } 26 | 27 | return <>{children}; 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/guards/RoleBasedGuard.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { m } from 'framer-motion'; 3 | // @mui 4 | import { Container, Typography } from '@mui/material'; 5 | // hooks 6 | import useAuth from '../hooks/useAuth'; 7 | // components 8 | import { MotionContainer, varBounce } from '../components/animate'; 9 | // assets 10 | import { ForbiddenIllustration } from '../assets'; 11 | 12 | // ---------------------------------------------------------------------- 13 | 14 | RoleBasedGuard.propTypes = { 15 | hasContent: PropTypes.bool, 16 | roles: PropTypes.arrayOf(PropTypes.string), // Example ['admin', 'leader'] 17 | children: PropTypes.node.isRequired, 18 | }; 19 | 20 | export default function RoleBasedGuard({ hasContent, roles, children }) { 21 | // Logic here to get current user role 22 | const { user } = useAuth(); 23 | 24 | // const currentRole = 'user'; 25 | const currentRole = user?.role; // admin; 26 | 27 | if (typeof roles !== 'undefined' && !roles.includes(currentRole)) { 28 | return hasContent ? ( 29 | 30 | 31 | 32 | Permission Denied 33 | 34 | 35 | 36 | 37 | You do not have permission to access this page 38 | 39 | 40 | 41 | 42 | 43 | 44 | ) : null; 45 | } 46 | 47 | return <>{children}; 48 | } 49 | -------------------------------------------------------------------------------- /frontend/src/hooks/useAuth.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | // 3 | import { AuthContext } from '../contexts/JWTContext'; 4 | // import { AuthContext } from '../contexts/Auth0Context'; 5 | // import { AuthContext } from '../contexts/FirebaseContext'; 6 | // import { AuthContext } from '../contexts/AwsCognitoContext'; 7 | 8 | // ---------------------------------------------------------------------- 9 | 10 | const useAuth = () => { 11 | const context = useContext(AuthContext); 12 | 13 | if (!context) throw new Error('Auth context must be use inside AuthProvider'); 14 | 15 | return context; 16 | }; 17 | 18 | export default useAuth; 19 | -------------------------------------------------------------------------------- /frontend/src/hooks/useCollapseDrawer.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { CollapseDrawerContext } from '../contexts/CollapseDrawerContext'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | const useCollapseDrawer = () => useContext(CollapseDrawerContext); 7 | 8 | export default useCollapseDrawer; 9 | -------------------------------------------------------------------------------- /frontend/src/hooks/useCountdown.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function useCountdown(date) { 6 | const [countdown, setCountdown] = useState({ 7 | days: '00', 8 | hours: '00', 9 | minutes: '00', 10 | seconds: '00' 11 | }); 12 | 13 | useEffect(() => { 14 | const interval = setInterval(() => setNewTime(), 1000); 15 | return () => clearInterval(interval); 16 | // eslint-disable-next-line react-hooks/exhaustive-deps 17 | }, []); 18 | 19 | const setNewTime = () => { 20 | const startTime = date; 21 | const endTime = new Date(); 22 | const distanceToNow = startTime - endTime; 23 | 24 | const getDays = Math.floor(distanceToNow / (1000 * 60 * 60 * 24)); 25 | const getHours = `0${Math.floor((distanceToNow % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))}`.slice(-2); 26 | const getMinutes = `0${Math.floor((distanceToNow % (1000 * 60 * 60)) / (1000 * 60))}`.slice(-2); 27 | const getSeconds = `0${Math.floor((distanceToNow % (1000 * 60)) / 1000)}`.slice(-2); 28 | 29 | setCountdown({ 30 | days: getDays || '000', 31 | hours: getHours || '000', 32 | minutes: getMinutes || '000', 33 | seconds: getSeconds || '000' 34 | }); 35 | }; 36 | 37 | return countdown; 38 | } 39 | 40 | // Usage 41 | // const countdown = useCountdown(new Date('07/07/2022 21:30')); 42 | -------------------------------------------------------------------------------- /frontend/src/hooks/useDateRangePicker.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { isSameDay, isSameMonth } from 'date-fns'; 3 | // 4 | import useToggle from './useToggle'; 5 | 6 | // ---------------------------------------------------------------------- 7 | 8 | export default function useDateRangePicker(date) { 9 | const { toggle: openPicker, onOpen: onOpenPicker, onClose: onClosePicker } = useToggle(); 10 | 11 | const [startTime, setStartTime] = useState(date[0]); 12 | 13 | const [endTime, setEndTime] = useState(date[1]); 14 | 15 | const isSameDays = startTime && endTime ? isSameDay(new Date(startTime), new Date(endTime)) : false; 16 | 17 | const isSameMonths = startTime && endTime ? isSameMonth(new Date(startTime), new Date(endTime)) : false; 18 | 19 | const handleChangeStartTime = (newValue) => { 20 | setStartTime(newValue); 21 | }; 22 | 23 | const handleChangeEndTime = (newValue) => { 24 | setEndTime(newValue); 25 | }; 26 | 27 | return { 28 | startTime, 29 | endTime, 30 | onChangeStartTime: handleChangeStartTime, 31 | onChangeEndTime: handleChangeEndTime, 32 | // 33 | openPicker, 34 | onOpenPicker, 35 | onClosePicker, 36 | // 37 | isSameDays, 38 | isSameMonths, 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /frontend/src/hooks/useIsMountedRef.js: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from 'react'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function useIsMountedRef() { 6 | const isMounted = useRef(true); 7 | 8 | useEffect( 9 | () => () => { 10 | isMounted.current = false; 11 | }, 12 | [] 13 | ); 14 | 15 | return isMounted; 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/hooks/useLocalStorage.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function useLocalStorage(key, defaultValue) { 6 | const [value, setValue] = useState(() => { 7 | const storedValue = localStorage.getItem(key); 8 | 9 | return storedValue === null ? defaultValue : JSON.parse(storedValue); 10 | }); 11 | 12 | useEffect(() => { 13 | const listener = (e) => { 14 | if (e.storageArea === localStorage && e.key === key) { 15 | setValue(JSON.parse(e.newValue)); 16 | } 17 | }; 18 | window.addEventListener('storage', listener); 19 | 20 | return () => { 21 | window.removeEventListener('storage', listener); 22 | }; 23 | }, [key, defaultValue]); 24 | 25 | const setValueInLocalStorage = (newValue) => { 26 | setValue((currentValue) => { 27 | const result = typeof newValue === 'function' ? newValue(currentValue) : newValue; 28 | 29 | localStorage.setItem(key, JSON.stringify(result)); 30 | 31 | return result; 32 | }); 33 | }; 34 | 35 | return [value, setValueInLocalStorage]; 36 | } 37 | -------------------------------------------------------------------------------- /frontend/src/hooks/useLocales.js: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next'; 2 | import useSettings from './useSettings'; 3 | // config 4 | import { allLangs, defaultLang } from '../config'; 5 | 6 | // ---------------------------------------------------------------------- 7 | 8 | export default function useLocales() { 9 | const { i18n, t: translate } = useTranslation(); 10 | 11 | const { onChangeDirectionByLang } = useSettings(); 12 | 13 | const langStorage = localStorage.getItem('i18nextLng'); 14 | 15 | const currentLang = allLangs.find((_lang) => _lang.value === langStorage) || defaultLang; 16 | 17 | const handleChangeLanguage = (newlang) => { 18 | i18n.changeLanguage(newlang); 19 | onChangeDirectionByLang(newlang); 20 | }; 21 | 22 | return { 23 | onChangeLang: handleChangeLanguage, 24 | translate: (text, options) => translate(text, options), 25 | currentLang, 26 | allLangs, 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/hooks/useOffSetTop.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function useOffSetTop(top) { 6 | const [offsetTop, setOffSetTop] = useState(false); 7 | const isTop = top || 100; 8 | 9 | useEffect(() => { 10 | window.onscroll = () => { 11 | if (window.pageYOffset > isTop) { 12 | setOffSetTop(true); 13 | } else { 14 | setOffSetTop(false); 15 | } 16 | }; 17 | return () => { 18 | window.onscroll = null; 19 | }; 20 | }, [isTop]); 21 | 22 | return offsetTop; 23 | } 24 | 25 | // Usage 26 | // const offset = useOffSetTop(100); 27 | -------------------------------------------------------------------------------- /frontend/src/hooks/useResponsive.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { useTheme } from '@mui/material/styles' 3 | import useMediaQuery from '@mui/material/useMediaQuery' 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | export default function useResponsive(query, key, start, end) { 8 | const theme = useTheme() 9 | 10 | const mediaUp = useMediaQuery(theme.breakpoints.up(key)) 11 | 12 | const mediaDown = useMediaQuery(theme.breakpoints.down(key)) 13 | 14 | const mediaBetween = useMediaQuery(theme.breakpoints.between(start, end)) 15 | 16 | const mediaOnly = useMediaQuery(theme.breakpoints.only(key)) 17 | 18 | if (query === 'up') { 19 | return mediaUp 20 | } 21 | 22 | if (query === 'down') { 23 | return mediaDown 24 | } 25 | 26 | if (query === 'between') { 27 | return mediaBetween 28 | } 29 | 30 | if (query === 'only') { 31 | return mediaOnly 32 | } 33 | return null 34 | } 35 | -------------------------------------------------------------------------------- /frontend/src/hooks/useSettings.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { SettingsContext } from '../contexts/SettingsContext'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | const useSettings = () => useContext(SettingsContext); 7 | 8 | export default useSettings; 9 | -------------------------------------------------------------------------------- /frontend/src/hooks/useTabs.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function useTabs(defaultValues) { 6 | const [currentTab, setCurrentTab] = useState(defaultValues || ''); 7 | 8 | return { 9 | currentTab, 10 | onChangeTab: (event, newValue) => { 11 | setCurrentTab(newValue); 12 | }, 13 | setCurrentTab, 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/hooks/useToggle.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function useToggle(defaultChecked) { 6 | const [toggle, setToggle] = useState(defaultChecked || false); 7 | 8 | return { 9 | toggle, 10 | onToggle: () => setToggle(!toggle), 11 | onOpen: () => setToggle(true), 12 | onClose: () => setToggle(false), 13 | setToggle, 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/layouts/LogoOnlyLayout.js: -------------------------------------------------------------------------------- 1 | import { Outlet } from 'react-router-dom' 2 | // @mui 3 | import { styled } from '@mui/material/styles' 4 | // components 5 | import Logo from '../components/Logo' 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | const HeaderStyle = styled('header')(({ theme }) => ({ 10 | top: 0, 11 | left: 0, 12 | lineHeight: 0, 13 | width: '100%', 14 | position: 'absolute', 15 | padding: theme.spacing(3, 3, 0), 16 | [theme.breakpoints.up('sm')]: { 17 | padding: theme.spacing(5, 5, 0) 18 | } 19 | })) 20 | 21 | // ---------------------------------------------------------------------- 22 | 23 | export default function LogoOnlyLayout() { 24 | return ( 25 | <> 26 | 27 | 28 | 29 | 30 | 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /frontend/src/layouts/dashboard/navbar/NavConfig.js: -------------------------------------------------------------------------------- 1 | // routes 2 | import { PATH_DASHBOARD, PATH_PAGE } from '../../../routes/paths' 3 | // components 4 | import Label from '../../../components/Label' 5 | import Iconify from '../../../components/Iconify' 6 | import SvgIconStyle from '../../../components/SvgIconStyle' 7 | 8 | // ---------------------------------------------------------------------- 9 | 10 | const getIcon = (name) => 11 | 12 | const ICONS = { 13 | blog: getIcon('ic_blog'), 14 | cart: getIcon('ic_cart'), 15 | chat: getIcon('ic_chat'), 16 | mail: getIcon('ic_mail'), 17 | user: getIcon('ic_user'), 18 | kanban: getIcon('ic_kanban'), 19 | banking: getIcon('ic_banking'), 20 | booking: getIcon('ic_booking'), 21 | invoice: getIcon('ic_invoice'), 22 | calendar: getIcon('ic_calendar'), 23 | ecommerce: getIcon('ic_ecommerce'), 24 | analytics: getIcon('ic_analytics'), 25 | dashboard: getIcon('ic_dashboard'), 26 | menuItem: getIcon('ic_menu_item') 27 | } 28 | 29 | const navConfig = [ 30 | // GENERAL 31 | // ---------------------------------------------------------------------- 32 | { 33 | subheader: '', 34 | items: [ 35 | { title: 'home', path: PATH_DASHBOARD.general.app, icon: ICONS.dashboard }, 36 | ] 37 | } 38 | 39 | ] 40 | 41 | export default navConfig 42 | -------------------------------------------------------------------------------- /frontend/src/layouts/dashboard/navbar/NavbarDocs.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { Stack, Button, Typography } from '@mui/material'; 3 | // hooks 4 | import useAuth from '../../../hooks/useAuth'; 5 | import useLocales from '../../../hooks/useLocales'; 6 | // routes 7 | import { PATH_DOCS } from '../../../routes/paths'; 8 | // assets 9 | import { DocIllustration } from '../../../assets'; 10 | 11 | // ---------------------------------------------------------------------- 12 | 13 | export default function NavbarDocs() { 14 | const { user } = useAuth(); 15 | 16 | const { translate } = useLocales(); 17 | 18 | return ( 19 | 20 | {/* */} 21 | {/* 22 |
23 | 24 | {translate('docs.hi')}, {user?.displayName} 25 | 26 | 27 | {translate('docs.description')} 28 | 29 |
*/} 30 | 31 | {/* */} 34 |
35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /frontend/src/layouts/dashboard/navbar/NavbarHorizontal.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | // @mui 3 | import { styled } from '@mui/material/styles'; 4 | import { Container, AppBar } from '@mui/material'; 5 | // config 6 | import { HEADER } from '../../../config'; 7 | // components 8 | import { NavSectionHorizontal } from '../../../components/nav-section'; 9 | // 10 | import navConfig from './NavConfig'; 11 | 12 | // ---------------------------------------------------------------------- 13 | 14 | const RootStyle = styled(AppBar)(({ theme }) => ({ 15 | transition: theme.transitions.create('top', { 16 | easing: theme.transitions.easing.easeInOut, 17 | duration: theme.transitions.duration.shorter, 18 | }), 19 | width: '100%', 20 | position: 'fixed', 21 | zIndex: theme.zIndex.appBar, 22 | padding: theme.spacing(1, 0), 23 | boxShadow: theme.customShadows.z8, 24 | top: HEADER.DASHBOARD_DESKTOP_OFFSET_HEIGHT, 25 | backgroundColor: theme.palette.background.default, 26 | })); 27 | 28 | // ---------------------------------------------------------------------- 29 | 30 | function NavbarHorizontal() { 31 | return ( 32 | 33 | 34 | 35 | 36 | 37 | ); 38 | } 39 | 40 | export default memo(NavbarHorizontal); 41 | -------------------------------------------------------------------------------- /frontend/src/layouts/main/index.js: -------------------------------------------------------------------------------- 1 | import { Outlet } from 'react-router-dom' 2 | // @mui 3 | import { Box, Stack } from '@mui/material' 4 | // components 5 | // import Logo from '../../components/Logo'; 6 | // 7 | import MainFooter from './MainFooter' 8 | import MainHeader from './MainHeader' 9 | 10 | // ---------------------------------------------------------------------- 11 | 12 | export default function MainLayout() { 13 | // const { pathname } = useLocation(); 14 | 15 | // const isHome = pathname === '/'; 16 | 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/locales/i18n.js: -------------------------------------------------------------------------------- 1 | import i18n from 'i18next'; 2 | import LanguageDetector from 'i18next-browser-languagedetector'; 3 | import { initReactI18next } from 'react-i18next'; 4 | // config 5 | import { defaultLang } from '../config'; 6 | // 7 | import enLocales from './en'; 8 | import frLocales from './fr'; 9 | import vnLocales from './vn'; 10 | import cnLocales from './cn'; 11 | import arLocales from './ar'; 12 | 13 | // ---------------------------------------------------------------------- 14 | 15 | i18n 16 | .use(LanguageDetector) 17 | .use(initReactI18next) 18 | .init({ 19 | resources: { 20 | en: { translations: enLocales }, 21 | fr: { translations: frLocales }, 22 | vn: { translations: vnLocales }, 23 | cn: { translations: cnLocales }, 24 | ar: { translations: arLocales }, 25 | }, 26 | lng: localStorage.getItem('i18nextLng') || defaultLang.value, 27 | fallbackLng: defaultLang.value, 28 | debug: false, 29 | ns: ['translations'], 30 | defaultNS: 'translations', 31 | interpolation: { 32 | escapeValue: false, 33 | }, 34 | }); 35 | 36 | export default i18n; 37 | -------------------------------------------------------------------------------- /frontend/src/pages/dashboard/GeneralApp.js: -------------------------------------------------------------------------------- 1 | import { Container } from '@mui/material' 2 | 3 | import useSettings from '../../hooks/useSettings' 4 | // routes 5 | import { PATH_DASHBOARD } from '../../routes/paths' 6 | 7 | // components 8 | import Page from '../../components/Page' 9 | import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs' 10 | 11 | export default function GeneralApp() { 12 | const { themeStretch } = useSettings() 13 | 14 | return ( 15 | 16 | 17 | 18 | 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/pages/dashboard/UserProfile.js: -------------------------------------------------------------------------------- 1 | import { Container } from '@mui/material' 2 | import useSettings from '../../hooks/useSettings' 3 | // routes 4 | import { PATH_DASHBOARD } from '../../routes/paths' 5 | // hooks 6 | import useAuth from '../../hooks/useAuth' 7 | // components 8 | import Page from '../../components/Page' 9 | import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs' 10 | // sections 11 | 12 | // ---------------------------------------------------------------------- 13 | 14 | export default function UserProfile() { 15 | const { themeStretch } = useSettings() 16 | const { user } = useAuth() 17 | 18 | return ( 19 | 20 | 21 | 29 | 30 | 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /frontend/src/redux/rootReducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import storage from 'redux-persist/lib/storage' 3 | // slices 4 | 5 | import userReducer from './slices/user' 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | const rootPersistConfig = { 10 | key: 'root', 11 | storage, 12 | keyPrefix: 'redux-', 13 | whitelist: [] 14 | } 15 | 16 | const rootReducer = combineReducers({ 17 | user: userReducer 18 | }) 19 | 20 | export { rootPersistConfig, rootReducer } 21 | -------------------------------------------------------------------------------- /frontend/src/redux/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from '@reduxjs/toolkit'; 2 | import { useDispatch as useAppDispatch, useSelector as useAppSelector } from 'react-redux'; 3 | import { persistStore, persistReducer } from 'redux-persist'; 4 | import { rootPersistConfig, rootReducer } from './rootReducer'; 5 | 6 | // ---------------------------------------------------------------------- 7 | 8 | const store = configureStore({ 9 | reducer: persistReducer(rootPersistConfig, rootReducer), 10 | middleware: (getDefaultMiddleware) => 11 | getDefaultMiddleware({ 12 | serializableCheck: false, 13 | immutableCheck: false, 14 | }), 15 | }); 16 | 17 | const persistor = persistStore(store); 18 | 19 | const { dispatch } = store; 20 | 21 | const useSelector = useAppSelector; 22 | 23 | const useDispatch = () => useAppDispatch(); 24 | 25 | export { store, persistor, dispatch, useSelector, useDispatch }; 26 | -------------------------------------------------------------------------------- /frontend/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = (onPerfEntry) => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /frontend/src/routes/paths.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | function path(root, sublink) { 4 | return `${root}${sublink}` 5 | } 6 | 7 | const ROOTS_AUTH = '/auth' 8 | const ROOTS_DASHBOARD = '/dashboard' 9 | 10 | // ---------------------------------------------------------------------- 11 | 12 | export const PATH_AUTH = { 13 | root: ROOTS_AUTH, 14 | login: path(ROOTS_AUTH, '/login'), 15 | register: path(ROOTS_AUTH, '/register'), 16 | loginUnprotected: path(ROOTS_AUTH, '/login-unprotected'), 17 | registerUnprotected: path(ROOTS_AUTH, '/register-unprotected'), 18 | verify: path(ROOTS_AUTH, '/verify'), 19 | resetPassword: path(ROOTS_AUTH, '/reset-password'), 20 | newPassword: path(ROOTS_AUTH, '/new-password') 21 | } 22 | 23 | export const PATH_PAGE = { 24 | page403: '/403', 25 | page404: '/404', 26 | page500: '/500' 27 | } 28 | 29 | export const PATH_DASHBOARD = { 30 | root: ROOTS_DASHBOARD, 31 | general: { 32 | app: path(ROOTS_DASHBOARD, '/app') 33 | }, 34 | 35 | permissionDenied: path(ROOTS_DASHBOARD, '/permission-denied'), 36 | user: { 37 | root: path(ROOTS_DASHBOARD, '/user'), 38 | 39 | profile: path(ROOTS_DASHBOARD, '/user/profile'), 40 | account: path(ROOTS_DASHBOARD, '/user/account') 41 | } 42 | } 43 | 44 | export const PATH_DOCS = '' 45 | export const API_DOCS = '' 46 | -------------------------------------------------------------------------------- /frontend/src/sections/@dashboard/user/account/AccountBillingInvoiceHistory.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { Link as RouterLink } from 'react-router-dom'; 3 | // @mui 4 | import { Stack, Link, Button, Typography } from '@mui/material'; 5 | // utils 6 | import { fDate } from '../../../../utils/formatTime'; 7 | import { fCurrency } from '../../../../utils/formatNumber'; 8 | // components 9 | import Iconify from '../../../../components/Iconify'; 10 | 11 | // ---------------------------------------------------------------------- 12 | 13 | AccountBillingInvoiceHistory.propTypes = { 14 | invoices: PropTypes.array, 15 | }; 16 | 17 | export default function AccountBillingInvoiceHistory({ invoices }) { 18 | return ( 19 | 20 | 21 | Invoice History 22 | 23 | 24 | 25 | {invoices.map((invoice) => ( 26 | 27 | 28 | {fDate(invoice.createdAt)} 29 | 30 | {fCurrency(invoice.price)} 31 | 32 | PDF 33 | 34 | 35 | ))} 36 | 37 | 38 | 41 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /frontend/src/sections/@dashboard/user/account/index.js: -------------------------------------------------------------------------------- 1 | export { default as AccountBilling } from './AccountBilling'; 2 | export { default as AccountBillingAddressBook } from './AccountBillingAddressBook'; 3 | export { default as AccountBillingInvoiceHistory } from './AccountBillingInvoiceHistory'; 4 | export { default as AccountBillingPaymentMethod } from './AccountBillingPaymentMethod'; 5 | export { default as AccountChangePassword } from './AccountChangePassword'; 6 | export { default as AccountGeneral } from './AccountGeneral'; 7 | export { default as AccountNotifications } from './AccountNotifications'; 8 | export { default as AccountSocialLinks } from './AccountSocialLinks'; 9 | -------------------------------------------------------------------------------- /frontend/src/sections/auth/login/index.js: -------------------------------------------------------------------------------- 1 | export { default as LoginForm } from './LoginForm'; 2 | -------------------------------------------------------------------------------- /frontend/src/sections/auth/new-password/index.js: -------------------------------------------------------------------------------- 1 | export { default as NewPasswordForm } from './NewPasswordForm'; 2 | -------------------------------------------------------------------------------- /frontend/src/sections/auth/register/index.js: -------------------------------------------------------------------------------- 1 | export { default as RegisterForm } from './RegisterForm'; 2 | -------------------------------------------------------------------------------- /frontend/src/sections/auth/reset-password/index.js: -------------------------------------------------------------------------------- 1 | export { default as ResetPasswordForm } from './ResetPasswordForm'; 2 | -------------------------------------------------------------------------------- /frontend/src/sections/auth/verify-code/index.js: -------------------------------------------------------------------------------- 1 | export { default as VerifyCodeForm } from './VerifyCodeForm'; 2 | -------------------------------------------------------------------------------- /frontend/src/theme/breakpoints.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | const breakpoints = { 4 | values: { 5 | xs: 0, 6 | sm: 600, 7 | md: 900, 8 | lg: 1200, 9 | xl: 1536 10 | } 11 | }; 12 | 13 | export default breakpoints; 14 | -------------------------------------------------------------------------------- /frontend/src/theme/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { useMemo } from 'react'; 3 | // @mui 4 | import { CssBaseline } from '@mui/material'; 5 | import { createTheme, ThemeProvider as MUIThemeProvider, StyledEngineProvider } from '@mui/material/styles'; 6 | // hooks 7 | import useSettings from '../hooks/useSettings'; 8 | // 9 | import palette from './palette'; 10 | import typography from './typography'; 11 | import breakpoints from './breakpoints'; 12 | import componentsOverride from './overrides'; 13 | import shadows, { customShadows } from './shadows'; 14 | 15 | // ---------------------------------------------------------------------- 16 | 17 | ThemeProvider.propTypes = { 18 | children: PropTypes.node, 19 | }; 20 | 21 | export default function ThemeProvider({ children }) { 22 | const { themeMode, themeDirection } = useSettings(); 23 | 24 | const isLight = themeMode === 'light'; 25 | 26 | const themeOptions = useMemo( 27 | () => ({ 28 | palette: isLight ? palette.light : palette.dark, 29 | typography, 30 | breakpoints, 31 | shape: { borderRadius: 8 }, 32 | direction: themeDirection, 33 | shadows: isLight ? shadows.light : shadows.dark, 34 | customShadows: isLight ? customShadows.light : customShadows.dark, 35 | }), 36 | [isLight, themeDirection] 37 | ); 38 | 39 | const theme = createTheme(themeOptions); 40 | 41 | theme.components = componentsOverride(theme); 42 | 43 | return ( 44 | 45 | 46 | 47 | {children} 48 | 49 | 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Accordion.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Accordion(theme) { 4 | return { 5 | MuiAccordion: { 6 | styleOverrides: { 7 | root: { 8 | '&.Mui-expanded': { 9 | boxShadow: theme.customShadows.z8, 10 | borderRadius: theme.shape.borderRadius, 11 | }, 12 | '&.Mui-disabled': { 13 | backgroundColor: 'transparent', 14 | }, 15 | }, 16 | }, 17 | }, 18 | MuiAccordionSummary: { 19 | styleOverrides: { 20 | root: { 21 | paddingLeft: theme.spacing(2), 22 | paddingRight: theme.spacing(1), 23 | '&.Mui-disabled': { 24 | opacity: 1, 25 | color: theme.palette.action.disabled, 26 | '& .MuiTypography-root': { 27 | color: 'inherit', 28 | }, 29 | }, 30 | }, 31 | expandIconWrapper: { 32 | color: 'inherit', 33 | }, 34 | }, 35 | }, 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Autocomplete.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Autocomplete(theme) { 4 | return { 5 | MuiAutocomplete: { 6 | styleOverrides: { 7 | paper: { 8 | boxShadow: theme.customShadows.dropdown, 9 | }, 10 | listbox: { 11 | padding: theme.spacing(0, 1), 12 | '& .MuiAutocomplete-option': { 13 | padding: theme.spacing(1), 14 | margin: theme.spacing(1, 0), 15 | borderRadius: theme.shape.borderRadius, 16 | }, 17 | }, 18 | }, 19 | }, 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Avatar.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Avatar(theme) { 4 | return { 5 | MuiAvatar: { 6 | styleOverrides: { 7 | colorDefault: { 8 | color: theme.palette.text.secondary, 9 | backgroundColor: theme.palette.grey[400], 10 | }, 11 | }, 12 | }, 13 | MuiAvatarGroup: { 14 | styleOverrides: { 15 | avatar: { 16 | fontSize: 16, 17 | fontWeight: theme.typography.fontWeightMedium, 18 | '&:first-of-type': { 19 | fontSize: 14, 20 | color: theme.palette.primary.main, 21 | backgroundColor: theme.palette.primary.lighter, 22 | }, 23 | }, 24 | }, 25 | }, 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Backdrop.js: -------------------------------------------------------------------------------- 1 | import { alpha } from '@mui/material/styles'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function Backdrop(theme) { 6 | const varLow = alpha(theme.palette.grey[900], 0.48); 7 | const varHigh = alpha(theme.palette.grey[900], 1); 8 | 9 | return { 10 | MuiBackdrop: { 11 | styleOverrides: { 12 | root: { 13 | background: [ 14 | `rgb(22,28,36)`, 15 | `-moz-linear-gradient(75deg, ${varLow} 0%, ${varHigh} 100%)`, 16 | `-webkit-linear-gradient(75deg, ${varLow} 0%, ${varHigh} 100%)`, 17 | `linear-gradient(75deg, ${varLow} 0%, ${varHigh} 100%)`, 18 | ], 19 | '&.MuiBackdrop-invisible': { 20 | background: 'transparent', 21 | }, 22 | }, 23 | }, 24 | }, 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Badge.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Badge() { 4 | return { 5 | MuiBadge: { 6 | styleOverrides: { 7 | dot: { 8 | width: 10, 9 | height: 10, 10 | borderRadius: '50%', 11 | }, 12 | }, 13 | }, 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Breadcrumbs.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Breadcrumbs(theme) { 4 | return { 5 | MuiBreadcrumbs: { 6 | styleOverrides: { 7 | separator: { 8 | marginLeft: theme.spacing(2), 9 | marginRight: theme.spacing(2), 10 | }, 11 | }, 12 | }, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Button.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Button(theme) { 4 | return { 5 | MuiButton: { 6 | styleOverrides: { 7 | root: { 8 | '&:hover': { 9 | boxShadow: 'none', 10 | }, 11 | }, 12 | sizeLarge: { 13 | height: 48, 14 | }, 15 | // contained 16 | containedInherit: { 17 | color: theme.palette.grey[800], 18 | boxShadow: theme.customShadows.z8, 19 | '&:hover': { 20 | backgroundColor: theme.palette.grey[400], 21 | }, 22 | }, 23 | containedPrimary: { 24 | boxShadow: theme.customShadows.primary, 25 | }, 26 | containedSecondary: { 27 | boxShadow: theme.customShadows.secondary, 28 | }, 29 | containedInfo: { 30 | boxShadow: theme.customShadows.info, 31 | }, 32 | containedSuccess: { 33 | boxShadow: theme.customShadows.success, 34 | }, 35 | containedWarning: { 36 | boxShadow: theme.customShadows.warning, 37 | }, 38 | containedError: { 39 | boxShadow: theme.customShadows.error, 40 | }, 41 | // outlined 42 | outlinedInherit: { 43 | border: `1px solid ${theme.palette.grey[500_32]}`, 44 | '&:hover': { 45 | backgroundColor: theme.palette.action.hover, 46 | }, 47 | }, 48 | textInherit: { 49 | '&:hover': { 50 | backgroundColor: theme.palette.action.hover, 51 | }, 52 | }, 53 | }, 54 | }, 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/ButtonGroup.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function ButtonGroup(theme) { 4 | const styleContained = (color) => ({ 5 | props: { variant: 'contained', color }, 6 | style: { boxShadow: theme.customShadows[color] }, 7 | }); 8 | 9 | return { 10 | MuiButtonGroup: { 11 | variants: [ 12 | { 13 | props: { variant: 'contained', color: 'inherit' }, 14 | style: { boxShadow: theme.customShadows.z8 }, 15 | }, 16 | styleContained('primary'), 17 | styleContained('secondary'), 18 | styleContained('info'), 19 | styleContained('success'), 20 | styleContained('warning'), 21 | styleContained('error'), 22 | 23 | { 24 | props: { disabled: true }, 25 | style: { 26 | boxShadow: 'none', 27 | '& .MuiButtonGroup-grouped.Mui-disabled': { 28 | color: theme.palette.action.disabled, 29 | borderColor: `${theme.palette.action.disabledBackground} !important`, 30 | '&.MuiButton-contained': { 31 | backgroundColor: theme.palette.action.disabledBackground, 32 | }, 33 | }, 34 | }, 35 | }, 36 | ], 37 | 38 | styleOverrides: { 39 | root: { 40 | '&:hover': { 41 | boxShadow: 'none', 42 | }, 43 | }, 44 | }, 45 | }, 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Card.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Card(theme) { 4 | return { 5 | MuiCard: { 6 | styleOverrides: { 7 | root: { 8 | position: 'relative', 9 | boxShadow: theme.customShadows.card, 10 | borderRadius: Number(theme.shape.borderRadius) * 2, 11 | zIndex: 0, // Fix Safari overflow: hidden with border radius 12 | }, 13 | }, 14 | }, 15 | MuiCardHeader: { 16 | defaultProps: { 17 | titleTypographyProps: { variant: 'h6' }, 18 | subheaderTypographyProps: { variant: 'body2', marginTop: theme.spacing(0.5) }, 19 | }, 20 | styleOverrides: { 21 | root: { 22 | padding: theme.spacing(3, 3, 0), 23 | }, 24 | }, 25 | }, 26 | MuiCardContent: { 27 | styleOverrides: { 28 | root: { 29 | padding: theme.spacing(3), 30 | }, 31 | }, 32 | }, 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Checkbox.js: -------------------------------------------------------------------------------- 1 | import { CheckboxIcon, CheckboxCheckedIcon, CheckboxIndeterminateIcon } from './CustomIcons'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function Checkbox(theme) { 6 | return { 7 | MuiCheckbox: { 8 | defaultProps: { 9 | icon: , 10 | checkedIcon: , 11 | indeterminateIcon: , 12 | }, 13 | 14 | styleOverrides: { 15 | root: { 16 | padding: theme.spacing(1), 17 | '&.Mui-checked.Mui-disabled, &.Mui-disabled': { 18 | color: theme.palette.action.disabled, 19 | }, 20 | '& .MuiSvgIcon-fontSizeMedium': { 21 | width: 24, 22 | height: 24, 23 | }, 24 | '& .MuiSvgIcon-fontSizeSmall': { 25 | width: 20, 26 | height: 20, 27 | }, 28 | svg: { 29 | fontSize: 24, 30 | '&[font-size=small]': { 31 | fontSize: 20, 32 | }, 33 | }, 34 | }, 35 | }, 36 | }, 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Chip.js: -------------------------------------------------------------------------------- 1 | import { CloseIcon } from './CustomIcons'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function Chip(theme) { 6 | return { 7 | MuiChip: { 8 | defaultProps: { 9 | deleteIcon: , 10 | }, 11 | 12 | styleOverrides: { 13 | colorDefault: { 14 | '& .MuiChip-avatarMedium, .MuiChip-avatarSmall': { 15 | color: theme.palette.text.secondary, 16 | }, 17 | }, 18 | outlined: { 19 | borderColor: theme.palette.grey[500_32], 20 | '&.MuiChip-colorPrimary': { 21 | borderColor: theme.palette.primary.main, 22 | }, 23 | '&.MuiChip-colorSecondary': { 24 | borderColor: theme.palette.secondary.main, 25 | }, 26 | }, 27 | // 28 | avatarColorInfo: { 29 | color: theme.palette.info.contrastText, 30 | backgroundColor: theme.palette.info.dark, 31 | }, 32 | avatarColorSuccess: { 33 | color: theme.palette.success.contrastText, 34 | backgroundColor: theme.palette.success.dark, 35 | }, 36 | avatarColorWarning: { 37 | color: theme.palette.warning.contrastText, 38 | backgroundColor: theme.palette.warning.dark, 39 | }, 40 | avatarColorError: { 41 | color: theme.palette.error.contrastText, 42 | backgroundColor: theme.palette.error.dark, 43 | }, 44 | }, 45 | }, 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/ControlLabel.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function ControlLabel(theme) { 4 | return { 5 | MuiFormControlLabel: { 6 | styleOverrides: { 7 | label: { 8 | ...theme.typography.body2, 9 | }, 10 | }, 11 | }, 12 | MuiFormHelperText: { 13 | styleOverrides: { 14 | root: { 15 | marginTop: theme.spacing(1), 16 | }, 17 | }, 18 | }, 19 | MuiFormLabel: { 20 | styleOverrides: { 21 | root: { 22 | color: theme.palette.text.disabled, 23 | }, 24 | }, 25 | }, 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/CssBaseline.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function CssBaseline() { 4 | return { 5 | MuiCssBaseline: { 6 | styleOverrides: { 7 | '*': { 8 | margin: 0, 9 | padding: 0, 10 | boxSizing: 'border-box', 11 | }, 12 | html: { 13 | width: '100%', 14 | height: '100%', 15 | WebkitOverflowScrolling: 'touch', 16 | }, 17 | body: { 18 | width: '100%', 19 | height: '100%', 20 | }, 21 | '#root': { 22 | width: '100%', 23 | height: '100%', 24 | }, 25 | input: { 26 | '&[type=number]': { 27 | MozAppearance: 'textfield', 28 | '&::-webkit-outer-spin-button': { 29 | margin: 0, 30 | WebkitAppearance: 'none', 31 | }, 32 | '&::-webkit-inner-spin-button': { 33 | margin: 0, 34 | WebkitAppearance: 'none', 35 | }, 36 | }, 37 | }, 38 | img: { 39 | display: 'block', 40 | maxWidth: '100%', 41 | }, 42 | }, 43 | }, 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Dialog.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Dialog(theme) { 4 | return { 5 | MuiDialog: { 6 | styleOverrides: { 7 | paper: { 8 | boxShadow: theme.customShadows.dialog, 9 | '&.MuiPaper-rounded': { 10 | borderRadius: Number(theme.shape.borderRadius) * 2, 11 | }, 12 | '&.MuiDialog-paperFullScreen': { 13 | borderRadius: 0, 14 | }, 15 | '&.MuiDialog-paper .MuiDialogActions-root': { 16 | padding: theme.spacing(3), 17 | }, 18 | '@media (max-width: 600px)': { 19 | margin: theme.spacing(2), 20 | }, 21 | '@media (max-width: 663.95px)': { 22 | '&.MuiDialog-paperWidthSm.MuiDialog-paperScrollBody': { 23 | maxWidth: '100%', 24 | }, 25 | }, 26 | }, 27 | paperFullWidth: { 28 | width: '100%', 29 | }, 30 | }, 31 | }, 32 | MuiDialogTitle: { 33 | styleOverrides: { 34 | root: { 35 | padding: theme.spacing(3, 3, 0), 36 | }, 37 | }, 38 | }, 39 | MuiDialogContent: { 40 | styleOverrides: { 41 | root: { 42 | borderTop: 0, 43 | borderBottom: 0, 44 | padding: theme.spacing(3), 45 | }, 46 | }, 47 | }, 48 | MuiDialogActions: { 49 | styleOverrides: { 50 | root: { 51 | '& > :not(:first-of-type)': { 52 | marginLeft: theme.spacing(1.5), 53 | }, 54 | }, 55 | }, 56 | }, 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Drawer.js: -------------------------------------------------------------------------------- 1 | import { alpha } from '@mui/material'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function Drawer(theme) { 6 | const isLight = theme.palette.mode === 'light'; 7 | 8 | return { 9 | MuiDrawer: { 10 | styleOverrides: { 11 | modal: { 12 | '&[role="presentation"]': { 13 | '& .MuiDrawer-paperAnchorLeft': { 14 | boxShadow: `8px 24px 24px 12px ${alpha(theme.palette.grey[900], isLight ? 0.16 : 0.48)}`, 15 | }, 16 | '& .MuiDrawer-paperAnchorRight': { 17 | boxShadow: `-8px 24px 24px 12px ${alpha(theme.palette.grey[900], isLight ? 0.16 : 0.48)}`, 18 | }, 19 | }, 20 | }, 21 | }, 22 | }, 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Fab.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Fab(theme) { 4 | return { 5 | MuiFab: { 6 | defaultProps: { 7 | color: 'primary', 8 | }, 9 | 10 | styleOverrides: { 11 | root: { 12 | boxShadow: theme.customShadows.z8, 13 | '&:hover': { 14 | boxShadow: 'none', 15 | backgroundColor: theme.palette.grey[400], 16 | }, 17 | }, 18 | primary: { 19 | boxShadow: theme.customShadows.primary, 20 | '&:hover': { 21 | backgroundColor: theme.palette.primary.dark, 22 | }, 23 | }, 24 | secondary: { 25 | boxShadow: theme.customShadows.secondary, 26 | '&:hover': { 27 | backgroundColor: theme.palette.secondary.dark, 28 | }, 29 | }, 30 | extended: { 31 | '& svg': { 32 | marginRight: theme.spacing(1), 33 | }, 34 | }, 35 | }, 36 | }, 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Link.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Link() { 4 | return { 5 | MuiLink: { 6 | defaultProps: { 7 | underline: 'hover', 8 | }, 9 | }, 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/List.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function List(theme) { 4 | return { 5 | MuiListItemIcon: { 6 | styleOverrides: { 7 | root: { 8 | color: 'inherit', 9 | minWidth: 'auto', 10 | marginRight: theme.spacing(2), 11 | }, 12 | }, 13 | }, 14 | MuiListItemAvatar: { 15 | styleOverrides: { 16 | root: { 17 | minWidth: 'auto', 18 | marginRight: theme.spacing(2), 19 | }, 20 | }, 21 | }, 22 | MuiListItemText: { 23 | styleOverrides: { 24 | root: { 25 | marginTop: 0, 26 | marginBottom: 0, 27 | }, 28 | multiline: { 29 | marginTop: 0, 30 | marginBottom: 0, 31 | }, 32 | }, 33 | }, 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/LoadingButton.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function LoadingButton() { 4 | return { 5 | MuiLoadingButton: { 6 | styleOverrides: { 7 | root: { 8 | '&.MuiButton-text': { 9 | '& .MuiLoadingButton-startIconPendingStart': { 10 | marginLeft: 0, 11 | }, 12 | '& .MuiLoadingButton-endIconPendingEnd': { 13 | marginRight: 0, 14 | }, 15 | }, 16 | }, 17 | }, 18 | }, 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Menu.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Menu(theme) { 4 | return { 5 | MuiMenuItem: { 6 | styleOverrides: { 7 | root: { 8 | '&.Mui-selected': { 9 | backgroundColor: theme.palette.action.selected, 10 | '&:hover': { 11 | backgroundColor: theme.palette.action.hover, 12 | }, 13 | }, 14 | }, 15 | }, 16 | }, 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Pagination.js: -------------------------------------------------------------------------------- 1 | import { alpha } from '@mui/material/styles'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function Pagination(theme) { 6 | return { 7 | MuiPaginationItem: { 8 | styleOverrides: { 9 | root: { 10 | '&.Mui-selected': { 11 | fontWeight: theme.typography.fontWeightBold, 12 | }, 13 | }, 14 | textPrimary: { 15 | '&.Mui-selected': { 16 | color: theme.palette.primary.main, 17 | backgroundColor: alpha(theme.palette.primary.main, 0.08), 18 | '&:hover, &.Mui-focusVisible': { 19 | backgroundColor: `${alpha(theme.palette.primary.main, 0.24)} !important`, 20 | }, 21 | }, 22 | }, 23 | outlined: { 24 | border: `1px solid ${theme.palette.grey[500_32]}`, 25 | }, 26 | outlinedPrimary: { 27 | '&.Mui-selected': { 28 | backgroundColor: alpha(theme.palette.primary.main, 0.08), 29 | border: `1px solid ${alpha(theme.palette.primary.main, 0.24)}`, 30 | }, 31 | }, 32 | }, 33 | }, 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Paper.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Paper(theme) { 4 | return { 5 | MuiPaper: { 6 | defaultProps: { 7 | elevation: 0, 8 | }, 9 | 10 | variants: [ 11 | { 12 | props: { variant: 'outlined' }, 13 | style: { borderColor: theme.palette.grey[500_12] }, 14 | }, 15 | ], 16 | 17 | styleOverrides: { 18 | root: { 19 | backgroundImage: 'none', 20 | }, 21 | }, 22 | }, 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Popover.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Popover(theme) { 4 | return { 5 | MuiPopover: { 6 | styleOverrides: { 7 | paper: { 8 | boxShadow: theme.customShadows.dropdown, 9 | borderRadius: Number(theme.shape.borderRadius) * 1.5, 10 | }, 11 | }, 12 | }, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Progress.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Progress(theme) { 4 | const isLight = theme.palette.mode === 'light'; 5 | 6 | return { 7 | MuiLinearProgress: { 8 | styleOverrides: { 9 | root: { 10 | borderRadius: 4, 11 | overflow: 'hidden', 12 | }, 13 | bar: { 14 | borderRadius: 4, 15 | }, 16 | colorPrimary: { 17 | backgroundColor: theme.palette.primary[isLight ? 'lighter' : 'darker'], 18 | }, 19 | buffer: { 20 | backgroundColor: 'transparent', 21 | }, 22 | }, 23 | }, 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Radio.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Radio(theme) { 4 | return { 5 | MuiRadio: { 6 | styleOverrides: { 7 | root: { 8 | padding: theme.spacing(1), 9 | svg: { 10 | fontSize: 24, 11 | '&[font-size=small]': { 12 | fontSize: 20, 13 | }, 14 | }, 15 | }, 16 | }, 17 | }, 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Rating.js: -------------------------------------------------------------------------------- 1 | import { StarIcon } from './CustomIcons'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | const ICON_SMALL = { width: 20, height: 20 }; 6 | const ICON_LARGE = { width: 28, height: 28 }; 7 | 8 | export default function Rating(theme) { 9 | return { 10 | MuiRating: { 11 | defaultProps: { 12 | emptyIcon: , 13 | icon: , 14 | }, 15 | 16 | styleOverrides: { 17 | root: { 18 | '&.Mui-disabled': { 19 | opacity: 0.48, 20 | }, 21 | }, 22 | iconEmpty: { color: theme.palette.grey[500_48] }, 23 | sizeSmall: { '& svg': { ...ICON_SMALL } }, 24 | sizeLarge: { '& svg': { ...ICON_LARGE } }, 25 | }, 26 | }, 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Select.js: -------------------------------------------------------------------------------- 1 | import { InputSelectIcon } from './CustomIcons'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function Select() { 6 | return { 7 | MuiSelect: { 8 | defaultProps: { 9 | IconComponent: InputSelectIcon, 10 | }, 11 | }, 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Skeleton.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Skeleton(theme) { 4 | return { 5 | MuiSkeleton: { 6 | defaultProps: { 7 | animation: 'wave', 8 | }, 9 | 10 | styleOverrides: { 11 | root: { 12 | backgroundColor: theme.palette.background.neutral, 13 | }, 14 | }, 15 | }, 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Slider.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Slider(theme) { 4 | const isLight = theme.palette.mode === 'light'; 5 | 6 | return { 7 | MuiSlider: { 8 | defaultProps: { 9 | size: 'small', 10 | }, 11 | 12 | styleOverrides: { 13 | root: { 14 | '&.Mui-disabled': { 15 | color: theme.palette.action.disabled, 16 | }, 17 | }, 18 | markLabel: { 19 | fontSize: 13, 20 | color: theme.palette.text.disabled, 21 | }, 22 | valueLabel: { 23 | borderRadius: 8, 24 | backgroundColor: theme.palette.grey[isLight ? 800 : 700], 25 | }, 26 | }, 27 | }, 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Stepper.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Stepper(theme) { 4 | return { 5 | MuiStepConnector: { 6 | styleOverrides: { 7 | line: { 8 | borderColor: theme.palette.divider, 9 | }, 10 | }, 11 | }, 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/SvgIcon.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function SvgIcon() { 4 | return { 5 | MuiSvgIcon: { 6 | styleOverrides: { 7 | fontSizeSmall: { 8 | width: 20, 9 | height: 20, 10 | fontSize: 'inherit', 11 | }, 12 | fontSizeLarge: { 13 | width: 32, 14 | height: 32, 15 | fontSize: 'inherit', 16 | }, 17 | }, 18 | }, 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Switch.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Switch(theme) { 4 | const isLight = theme.palette.mode === 'light'; 5 | 6 | return { 7 | MuiSwitch: { 8 | styleOverrides: { 9 | thumb: { 10 | boxShadow: theme.customShadows.z1, 11 | }, 12 | track: { 13 | opacity: 1, 14 | backgroundColor: theme.palette.grey[500], 15 | }, 16 | switchBase: { 17 | left: 0, 18 | right: 'auto', 19 | '&:not(:.Mui-checked)': { 20 | color: theme.palette.grey[isLight ? 100 : 300], 21 | }, 22 | '&.Mui-checked.Mui-disabled, &.Mui-disabled': { 23 | color: theme.palette.grey[isLight ? 400 : 600], 24 | }, 25 | '&.Mui-disabled+.MuiSwitch-track': { 26 | opacity: 1, 27 | backgroundColor: `${theme.palette.action.disabledBackground} !important`, 28 | }, 29 | }, 30 | }, 31 | }, 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Tabs.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Tabs(theme) { 4 | return { 5 | MuiTabs: { 6 | styleOverrides: { 7 | scrollButtons: { 8 | width: 48, 9 | borderRadius: '50%', 10 | }, 11 | }, 12 | }, 13 | MuiTab: { 14 | styleOverrides: { 15 | root: { 16 | padding: 0, 17 | fontWeight: theme.typography.fontWeightMedium, 18 | borderTopLeftRadius: theme.shape.borderRadius, 19 | borderTopRightRadius: theme.shape.borderRadius, 20 | '&.Mui-selected': { 21 | color: theme.palette.text.primary, 22 | }, 23 | '&:not(:last-of-type)': { 24 | marginRight: theme.spacing(5), 25 | }, 26 | '@media (min-width: 600px)': { 27 | minWidth: 48, 28 | }, 29 | }, 30 | labelIcon: { 31 | minHeight: 48, 32 | flexDirection: 'row', 33 | '& > *:first-of-type': { 34 | marginBottom: 0, 35 | marginRight: theme.spacing(1), 36 | }, 37 | }, 38 | wrapped: { 39 | flexDirection: 'row', 40 | whiteSpace: 'nowrap', 41 | }, 42 | textColorInherit: { 43 | opacity: 1, 44 | color: theme.palette.text.secondary, 45 | }, 46 | }, 47 | }, 48 | MuiTabPanel: { 49 | styleOverrides: { 50 | root: { 51 | padding: 0, 52 | }, 53 | }, 54 | }, 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Timeline.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Timeline(theme) { 4 | return { 5 | MuiTimelineDot: { 6 | styleOverrides: { 7 | root: { 8 | boxShadow: 'none', 9 | }, 10 | }, 11 | }, 12 | 13 | MuiTimelineConnector: { 14 | styleOverrides: { 15 | root: { 16 | backgroundColor: theme.palette.divider, 17 | }, 18 | }, 19 | }, 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/ToggleButton.js: -------------------------------------------------------------------------------- 1 | import { alpha } from '@mui/material/styles'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function ToggleButton(theme) { 6 | const style = (color) => ({ 7 | props: { color }, 8 | style: { 9 | '&:hover': { 10 | borderColor: alpha(theme.palette[color].main, 0.48), 11 | backgroundColor: alpha(theme.palette[color].main, theme.palette.action.hoverOpacity), 12 | }, 13 | '&.Mui-selected': { 14 | borderColor: alpha(theme.palette[color].main, 0.48), 15 | }, 16 | }, 17 | }); 18 | 19 | return { 20 | MuiToggleButton: { 21 | variants: [ 22 | { 23 | props: { color: 'standard' }, 24 | style: { 25 | '&.Mui-selected': { 26 | backgroundColor: theme.palette.action.selected, 27 | }, 28 | }, 29 | }, 30 | style('primary'), 31 | style('secondary'), 32 | style('info'), 33 | style('success'), 34 | style('warning'), 35 | style('error'), 36 | ], 37 | }, 38 | MuiToggleButtonGroup: { 39 | styleOverrides: { 40 | root: { 41 | borderRadius: theme.shape.borderRadius, 42 | backgroundColor: theme.palette.background.paper, 43 | border: `solid 1px ${theme.palette.grey[500_12]}`, 44 | '& .MuiToggleButton-root': { 45 | margin: 4, 46 | borderColor: 'transparent !important', 47 | borderRadius: `${theme.shape.borderRadius}px !important`, 48 | }, 49 | }, 50 | }, 51 | }, 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Tooltip.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Tooltip(theme) { 4 | const isLight = theme.palette.mode === 'light'; 5 | 6 | return { 7 | MuiTooltip: { 8 | styleOverrides: { 9 | tooltip: { 10 | backgroundColor: theme.palette.grey[isLight ? 800 : 700], 11 | }, 12 | arrow: { 13 | color: theme.palette.grey[isLight ? 800 : 700], 14 | }, 15 | }, 16 | }, 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/TreeView.js: -------------------------------------------------------------------------------- 1 | import { TreeViewCollapseIcon, TreeViewExpandIcon, TreeViewEndIcon } from './CustomIcons'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function TreeView(theme) { 6 | return { 7 | MuiTreeView: { 8 | defaultProps: { 9 | defaultCollapseIcon: , 10 | defaultExpandIcon: , 11 | defaultEndIcon: , 12 | }, 13 | }, 14 | MuiTreeItem: { 15 | styleOverrides: { 16 | label: { ...theme.typography.body2 }, 17 | iconContainer: { width: 'auto' }, 18 | }, 19 | }, 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/theme/overrides/Typography.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Typography(theme) { 4 | return { 5 | MuiTypography: { 6 | styleOverrides: { 7 | paragraph: { 8 | marginBottom: theme.spacing(2), 9 | }, 10 | gutterBottom: { 11 | marginBottom: theme.spacing(1), 12 | }, 13 | }, 14 | }, 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/utils/axios.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | // config 3 | import { HOST_API } from '../config' 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | const axiosInstance = axios.create({ 8 | baseURL: HOST_API 9 | }) 10 | 11 | axiosInstance.interceptors.response.use( 12 | (response) => response, 13 | (error) => Promise.reject((error.response && error.response.data) || 'Something went wrong') 14 | ) 15 | 16 | export default axiosInstance 17 | -------------------------------------------------------------------------------- /frontend/src/utils/createAvatar.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | const PRIMARY_NAME = ['A', 'N', 'H', 'L', 'Q', '9', '8']; 4 | const INFO_NAME = ['F', 'G', 'T', 'I', 'J', '1', '2', '3']; 5 | const SUCCESS_NAME = ['K', 'D', 'Y', 'B', 'O', '4', '5']; 6 | const WARNING_NAME = ['P', 'E', 'R', 'S', 'C', 'U', '6', '7']; 7 | const ERROR_NAME = ['V', 'W', 'X', 'M', 'Z']; 8 | 9 | function getFirstCharacter(name) { 10 | return name && name.charAt(0).toUpperCase(); 11 | } 12 | 13 | function getAvatarColor(name) { 14 | if (PRIMARY_NAME.includes(getFirstCharacter(name))) return 'primary'; 15 | if (INFO_NAME.includes(getFirstCharacter(name))) return 'info'; 16 | if (SUCCESS_NAME.includes(getFirstCharacter(name))) return 'success'; 17 | if (WARNING_NAME.includes(getFirstCharacter(name))) return 'warning'; 18 | if (ERROR_NAME.includes(getFirstCharacter(name))) return 'warning'; 19 | return 'default'; 20 | } 21 | 22 | export default function createAvatar(name) { 23 | return { 24 | name: getFirstCharacter(name), 25 | color: getAvatarColor(name), 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/utils/flattenArray.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function flattenArray(list, key = 'children') { 4 | let children = []; 5 | 6 | const flatten = list?.map((item) => { 7 | if (item[key] && item[key].length) { 8 | children = [...children, ...item[key]]; 9 | } 10 | return item; 11 | }); 12 | 13 | return flatten?.concat(children.length ? flattenArray(children, key) : children); 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/utils/formatNumber.js: -------------------------------------------------------------------------------- 1 | import numeral from 'numeral'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export function fCurrency(number) { 6 | return numeral(number).format(Number.isInteger(number) ? '$0,0' : '$0,0.00'); 7 | } 8 | 9 | export function fPercent(number) { 10 | return numeral(number / 100).format('0.0%'); 11 | } 12 | 13 | export function fNumber(number) { 14 | return numeral(number).format(); 15 | } 16 | 17 | export function fShortenNumber(number) { 18 | return numeral(number).format('0.00a').replace('.00', ''); 19 | } 20 | 21 | export function fData(number) { 22 | return numeral(number).format('0.0 b'); 23 | } 24 | -------------------------------------------------------------------------------- /frontend/src/utils/formatTime.js: -------------------------------------------------------------------------------- 1 | import { format, getTime, formatDistanceToNow } from 'date-fns'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export function fDate(date) { 6 | return format(new Date(date), 'dd MMMM yyyy'); 7 | } 8 | 9 | export function fDateTime(date) { 10 | return format(new Date(date), 'dd MMM yyyy HH:mm'); 11 | } 12 | 13 | export function fTimestamp(date) { 14 | return getTime(new Date(date)); 15 | } 16 | 17 | export function fDateTimeSuffix(date) { 18 | return format(new Date(date), 'dd/MM/yyyy hh:mm p'); 19 | } 20 | 21 | export function fToNow(date) { 22 | return formatDistanceToNow(new Date(date), { 23 | addSuffix: true, 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/utils/getColorName.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function getColorName(hex) { 4 | let color; 5 | 6 | switch (hex) { 7 | case '#00AB55': 8 | color = 'Green'; 9 | break; 10 | case '#000000': 11 | color = 'Black'; 12 | break; 13 | case '#FFFFFF': 14 | color = 'White'; 15 | break; 16 | case '#FFC0CB': 17 | color = 'Pink'; 18 | break; 19 | case '#FF4842': 20 | color = 'Red'; 21 | break; 22 | case '#1890FF': 23 | color = 'Blue'; 24 | break; 25 | case '#94D82D': 26 | color = 'Greenyellow'; 27 | break; 28 | case '#FFC107': 29 | color = 'Orange'; 30 | break; 31 | default: 32 | color = hex; 33 | } 34 | 35 | return color; 36 | } 37 | -------------------------------------------------------------------------------- /frontend/src/utils/getFileData.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function getFileData(file, index) { 4 | if (typeof file === 'string') { 5 | return { 6 | key: index ? `${file}-${index}` : file, 7 | preview: file, 8 | }; 9 | } 10 | 11 | return { 12 | key: index ? `${file.name}-${index}` : file.name, 13 | name: file.name, 14 | size: file.size, 15 | path: file.path, 16 | type: file.type, 17 | preview: file.preview, 18 | lastModified: file.lastModified, 19 | lastModifiedDate: file.lastModifiedDate, 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/utils/highlight.js: -------------------------------------------------------------------------------- 1 | import hljs from 'highlight.js'; 2 | import 'highlight.js/styles/atom-one-dark-reasonable.css'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | hljs.configure({ 7 | languages: ['javascript', 'jsx', 'sh', 'bash', 'html', 'scss', 'css', 'json'], 8 | }); 9 | 10 | if (typeof window !== 'undefined') { 11 | window.hljs = hljs; 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/utils/loadColorOptions.js: -------------------------------------------------------------------------------- 1 | export const loadTypesAndColorsArray = [ 2 | { key: 'Pending', value: '#ffe208' }, //yellow 3 | { key: 'Open', value: '#ed3c4f' }, 4 | { key: 'Covered', value: '#3726ad' }, 5 | { key: 'Dispatched', value: '#2e0000' }, 6 | { key: 'Loading', value: '#6c98e0' }, 7 | { key: 'On Route', value: '#5ccd57' }, 8 | { key: 'Un Loading', value: '#96aeab' }, 9 | { key: 'In Yard', value: '#e064be' }, 10 | { key: 'Delivered', value: '#20ad00' }, 11 | { key: 'Completed', value: '#002400' }, 12 | { key: 'Deleted', value: '#a4bb8a' } 13 | ] 14 | -------------------------------------------------------------------------------- /frontend/src/utils/roleOptions.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/frontend/src/utils/roleOptions.js -------------------------------------------------------------------------------- /frontend/src/utils/uuidv4.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // ---------------------------------------------------------------------- 3 | 4 | export default function uuidv4() { 5 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { 6 | const r = (Math.random() * 16) | 0, 7 | v = c === 'x' ? r : (r & 0x3) | 0x8; 8 | return v.toString(16); 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /image/README/1671452013971.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/image/README/1671452013971.png -------------------------------------------------------------------------------- /image/README/1671452027931.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faisalnazik/Django-React-Redux-Boilerplate/5101f11ff5efbc54e901d33e504936f3f745b15a/image/README/1671452027931.png --------------------------------------------------------------------------------