├── .gitignore ├── README.md ├── build ├── asset-manifest.json ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json ├── robots.txt └── static │ ├── js │ ├── 147.bb72a7f4.chunk.js │ ├── 147.bb72a7f4.chunk.js.LICENSE.txt │ ├── 147.bb72a7f4.chunk.js.map │ ├── 207.53e720df.chunk.js │ ├── 207.53e720df.chunk.js.map │ ├── 338.ce6ba00d.chunk.js │ ├── 338.ce6ba00d.chunk.js.map │ ├── 339.44b407fa.chunk.js │ ├── 339.44b407fa.chunk.js.map │ ├── 345.c55149fb.chunk.js │ ├── 345.c55149fb.chunk.js.map │ ├── 376.81977eb6.chunk.js │ ├── 376.81977eb6.chunk.js.map │ ├── 472.f35afe9a.chunk.js │ ├── 472.f35afe9a.chunk.js.map │ ├── 572.087c84b7.chunk.js │ ├── 572.087c84b7.chunk.js.map │ ├── 631.8197571d.chunk.js │ ├── 631.8197571d.chunk.js.map │ ├── 678.8a7704c8.chunk.js │ ├── 678.8a7704c8.chunk.js.map │ ├── 787.35cea4b2.chunk.js │ ├── 787.35cea4b2.chunk.js.map │ ├── main.1ae3cbd8.js │ ├── main.1ae3cbd8.js.LICENSE.txt │ └── main.1ae3cbd8.js.map │ └── media │ └── logo.45af386bcce066c1c6ab.ico ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── assets ├── Illustration │ └── NoChat.js └── Images │ └── logo.ico ├── components ├── AntSwitch.js ├── CallElement.js ├── Chat │ ├── Footer.js │ ├── Header.js │ └── index.js ├── ChatElement.js ├── Iconify.js ├── Image │ ├── Image.js │ ├── getRatio.js │ └── index.js ├── LoadingScreen.js ├── Scrollbar.js ├── Search │ ├── Search.js │ ├── SearchIconWrapper.js │ ├── StyledInputBase.js │ └── index.js ├── UserElement.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 ├── hook-form │ ├── FormProvider.js │ ├── RHFAutocomplete.js │ ├── RHFCodes.js │ ├── RHFTextField.js │ ├── RHFUpload.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 └── upload │ ├── UploadAvatar.js │ ├── index.js │ └── preview │ └── AvatarPreview.js ├── config.js ├── contexts └── SettingsContext.js ├── data └── index.js ├── hooks ├── useEventListener.js ├── useLocalStorage.js ├── useLocales.js ├── useResponsive.js └── useSettings.js ├── index.js ├── layouts ├── auth │ └── index.js ├── dashboard │ ├── BottomNav.js │ ├── ProfileMenu.js │ ├── SideNav.js │ └── index.js └── main │ └── index.js ├── pages ├── Page404.js ├── auth │ ├── Login.js │ ├── NewPassword.js │ ├── Register.js │ ├── ResetPassword.js │ └── Verify.js └── dashboard │ ├── Call.js │ ├── Chats.js │ ├── Conversation.js │ ├── GeneralApp.js │ ├── Group.js │ └── Settings │ ├── Profile.js │ └── index.js ├── redux ├── rootReducer.js ├── slices │ ├── app.js │ ├── audioCall.js │ ├── auth.js │ ├── conversation.js │ └── videoCall.js └── store.js ├── reportWebVitals.js ├── routes ├── index.js └── paths.js ├── sections ├── Dashboard │ ├── Audio │ │ ├── CallDialog.js │ │ └── CallNotification.js │ ├── Contact.js │ ├── Conversation.js │ ├── CreateGroup.js │ ├── Friends.js │ ├── Settings │ │ ├── ProfileForm.js │ │ ├── ShortcutDialog.js │ │ └── ThemeDialog.js │ ├── SharedMessages.js │ ├── StarredMessages.js │ ├── StartCall.js │ └── video │ │ ├── CallDialog.js │ │ └── CallNotification.js └── auth │ ├── AuthSocial.js │ ├── LoginForm.js │ ├── NewPasswordForm.js │ ├── RegisterForm.js │ ├── ResetPasswordForm.js │ └── VerifyForm.js ├── socket.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 ├── s3.js ├── truncate.js └── uuidv4.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | # /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /build/asset-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "main.js": "/static/js/main.1ae3cbd8.js", 4 | "static/js/338.ce6ba00d.chunk.js": "/static/js/338.ce6ba00d.chunk.js", 5 | "static/js/345.c55149fb.chunk.js": "/static/js/345.c55149fb.chunk.js", 6 | "static/js/376.81977eb6.chunk.js": "/static/js/376.81977eb6.chunk.js", 7 | "static/js/472.f35afe9a.chunk.js": "/static/js/472.f35afe9a.chunk.js", 8 | "static/js/207.53e720df.chunk.js": "/static/js/207.53e720df.chunk.js", 9 | "static/js/678.8a7704c8.chunk.js": "/static/js/678.8a7704c8.chunk.js", 10 | "static/js/787.35cea4b2.chunk.js": "/static/js/787.35cea4b2.chunk.js", 11 | "static/js/339.44b407fa.chunk.js": "/static/js/339.44b407fa.chunk.js", 12 | "static/js/147.bb72a7f4.chunk.js": "/static/js/147.bb72a7f4.chunk.js", 13 | "static/js/572.087c84b7.chunk.js": "/static/js/572.087c84b7.chunk.js", 14 | "static/js/631.8197571d.chunk.js": "/static/js/631.8197571d.chunk.js", 15 | "static/media/logo.ico": "/static/media/logo.45af386bcce066c1c6ab.ico", 16 | "index.html": "/index.html", 17 | "main.1ae3cbd8.js.map": "/static/js/main.1ae3cbd8.js.map", 18 | "338.ce6ba00d.chunk.js.map": "/static/js/338.ce6ba00d.chunk.js.map", 19 | "345.c55149fb.chunk.js.map": "/static/js/345.c55149fb.chunk.js.map", 20 | "376.81977eb6.chunk.js.map": "/static/js/376.81977eb6.chunk.js.map", 21 | "472.f35afe9a.chunk.js.map": "/static/js/472.f35afe9a.chunk.js.map", 22 | "207.53e720df.chunk.js.map": "/static/js/207.53e720df.chunk.js.map", 23 | "678.8a7704c8.chunk.js.map": "/static/js/678.8a7704c8.chunk.js.map", 24 | "787.35cea4b2.chunk.js.map": "/static/js/787.35cea4b2.chunk.js.map", 25 | "339.44b407fa.chunk.js.map": "/static/js/339.44b407fa.chunk.js.map", 26 | "147.bb72a7f4.chunk.js.map": "/static/js/147.bb72a7f4.chunk.js.map", 27 | "572.087c84b7.chunk.js.map": "/static/js/572.087c84b7.chunk.js.map", 28 | "631.8197571d.chunk.js.map": "/static/js/631.8197571d.chunk.js.map" 29 | }, 30 | "entrypoints": [ 31 | "static/js/main.1ae3cbd8.js" 32 | ] 33 | } -------------------------------------------------------------------------------- /build/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmonk-yt/chat-app-latest/fc6bc01d83da7ab566c5699425b8129e5b33077e/build/favicon.ico -------------------------------------------------------------------------------- /build/index.html: -------------------------------------------------------------------------------- 1 | Chat App
-------------------------------------------------------------------------------- /build/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmonk-yt/chat-app-latest/fc6bc01d83da7ab566c5699425b8129e5b33077e/build/logo192.png -------------------------------------------------------------------------------- /build/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmonk-yt/chat-app-latest/fc6bc01d83da7ab566c5699425b8129e5b33077e/build/logo512.png -------------------------------------------------------------------------------- /build/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /build/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /build/static/js/147.bb72a7f4.chunk.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ 2 | -------------------------------------------------------------------------------- /build/static/js/207.53e720df.chunk.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkchat=self.webpackChunkchat||[]).push([[207],{5207:function(e,n,t){t.r(n);t(2791);var c=t(184);n.default=function(){return(0,c.jsx)(c.Fragment,{children:"404"})}}}]); 2 | //# sourceMappingURL=207.53e720df.chunk.js.map -------------------------------------------------------------------------------- /build/static/js/207.53e720df.chunk.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"static/js/207.53e720df.chunk.js","mappings":"6HAOA,UALgB,WACd,OAAO,qCACR,C","sources":["pages/Page404.js"],"sourcesContent":["import React from \"react\";\n\nconst Page404 = () => {\n return <>404;\n};\n\n\nexport default Page404;"],"names":[],"sourceRoot":""} -------------------------------------------------------------------------------- /build/static/js/678.8a7704c8.chunk.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkchat=self.webpackChunkchat||[]).push([[678],{9678:function(a,c,e){e.r(c);var t=e(7631);c.default=t.g}}]); 2 | //# sourceMappingURL=678.8a7704c8.chunk.js.map -------------------------------------------------------------------------------- /build/static/js/678.8a7704c8.chunk.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"static/js/678.8a7704c8.chunk.js","mappings":"sHAEA,UAAeA,EAAf,C","sources":["components/animate/features.js"],"sourcesContent":["import { domMax } from 'framer-motion';\n\nexport default domMax;\n"],"names":["domMax"],"sourceRoot":""} -------------------------------------------------------------------------------- /build/static/media/logo.45af386bcce066c1c6ab.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmonk-yt/chat-app-latest/fc6bc01d83da7ab566c5699425b8129e5b33077e/build/static/media/logo.45af386bcce066c1c6ab.ico -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@dhaiwat10/react-link-preview": "^1.15.0", 7 | "@emoji-mart/data": "^1.0.6", 8 | "@emoji-mart/react": "^1.0.1", 9 | "@emotion/react": "^11.10.4", 10 | "@emotion/styled": "^11.10.4", 11 | "@hookform/resolvers": "^2.9.10", 12 | "@iconify/react": "^4.0.0", 13 | "@mui/lab": "^5.0.0-alpha.113", 14 | "@mui/material": "^5.11.1", 15 | "@reduxjs/toolkit": "^1.9.0", 16 | "@testing-library/jest-dom": "^5.16.5", 17 | "@testing-library/react": "^13.4.0", 18 | "@testing-library/user-event": "^13.5.0", 19 | "aws-sdk": "^2.1348.0", 20 | "axios": "^1.2.6", 21 | "emoji-mart": "^5.2.2", 22 | "framer-motion": "^7.5.3", 23 | "phosphor-react": "^1.4.1", 24 | "prop-types": "^15.8.1", 25 | "react": "^18.2.0", 26 | "react-dom": "^18.2.0", 27 | "react-dropzone": "^14.2.3", 28 | "react-embed": "^3.6.0", 29 | "react-helmet-async": "^1.3.0", 30 | "react-hook-form": "^7.41.0", 31 | "react-i18next": "^11.18.6", 32 | "react-lazy-load-image-component": "^1.5.6", 33 | "react-redux": "^8.0.5", 34 | "react-router-dom": "^6.4.2", 35 | "react-scripts": "5.0.1", 36 | "redux": "^4.2.0", 37 | "redux-persist": "^6.0.0", 38 | "simplebar-react": "^2.4.3", 39 | "socket.io-client": "^4.6.0", 40 | "stylis-plugin-rtl": "^2.0.2", 41 | "uuidv4": "^6.2.13", 42 | "web-vitals": "^2.1.4", 43 | "yup": "^0.32.11", 44 | "zego-express-engine-webrtc": "^2.23.0" 45 | }, 46 | "scripts": { 47 | "start": "react-scripts start", 48 | "build": "react-scripts build", 49 | "test": "react-scripts test", 50 | "eject": "react-scripts eject" 51 | }, 52 | "eslintConfig": { 53 | "extends": [ 54 | "react-app", 55 | "react-app/jest" 56 | ] 57 | }, 58 | "browserslist": { 59 | "production": [ 60 | ">0.2%", 61 | "not dead", 62 | "not op_mini all" 63 | ], 64 | "development": [ 65 | "last 1 chrome version", 66 | "last 1 firefox version", 67 | "last 1 safari version" 68 | ] 69 | }, 70 | "devDependencies": { 71 | "@faker-js/faker": "^7.5.0" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmonk-yt/chat-app-latest/fc6bc01d83da7ab566c5699425b8129e5b33077e/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 33 | Chat App 34 | 35 | 36 | 37 |
38 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmonk-yt/chat-app-latest/fc6bc01d83da7ab566c5699425b8129e5b33077e/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmonk-yt/chat-app-latest/fc6bc01d83da7ab566c5699425b8129e5b33077e/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | // routes 2 | // theme 3 | // components 4 | import React, { useEffect } from "react"; 5 | import { useDispatch, useSelector } from "react-redux"; 6 | import Snackbar from "@mui/material/Snackbar"; 7 | import MuiAlert from "@mui/material/Alert"; 8 | import ThemeSettings from "./components/settings"; 9 | import ThemeProvider from "./theme"; 10 | import Router from "./routes"; 11 | import { closeSnackBar } from "./redux/slices/app"; 12 | import { socket } from "./socket"; 13 | 14 | const vertical = "bottom"; 15 | const horizontal = "center"; 16 | 17 | const Alert = React.forwardRef((props, ref) => ( 18 | 19 | )); 20 | 21 | function App() { 22 | const dispatch = useDispatch(); 23 | 24 | const { severity, message, open } = useSelector( 25 | (state) => state.app.snackbar 26 | ); 27 | 28 | return ( 29 | <> 30 | 31 | 32 | {" "} 33 | {" "} 34 | 35 | 36 | 37 | {message && open ? ( 38 | { 44 | console.log("This is clicked"); 45 | dispatch(closeSnackBar()); 46 | }} 47 | > 48 | { 50 | console.log("This is clicked"); 51 | dispatch(closeSnackBar()); 52 | }} 53 | severity={severity} 54 | sx={{ width: "100%" }} 55 | > 56 | {message} 57 | 58 | 59 | ) : ( 60 | <> 61 | )} 62 | 63 | ); 64 | } 65 | 66 | export default App; 67 | -------------------------------------------------------------------------------- /src/assets/Images/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingmonk-yt/chat-app-latest/fc6bc01d83da7ab566c5699425b8129e5b33077e/src/assets/Images/logo.ico -------------------------------------------------------------------------------- /src/components/AntSwitch.js: -------------------------------------------------------------------------------- 1 | import { Switch } from "@mui/material"; 2 | import { styled } from "@mui/material/styles"; 3 | 4 | const AntSwitch = styled(Switch)(({ theme }) => ({ 5 | width: 40, 6 | height: 20, 7 | padding: 0, 8 | display: "flex", 9 | "&:active": { 10 | "& .MuiSwitch-thumb": { 11 | width: 15, 12 | }, 13 | "& .MuiSwitch-switchBase.Mui-checked": { 14 | transform: "translateX(9px)", 15 | }, 16 | }, 17 | "& .MuiSwitch-switchBase": { 18 | padding: 2, 19 | "&.Mui-checked": { 20 | transform: "translateX(20px)", 21 | color: "#fff", 22 | "& + .MuiSwitch-track": { 23 | opacity: 1, 24 | backgroundColor: theme.palette.primary.main, 25 | }, 26 | }, 27 | }, 28 | "& .MuiSwitch-thumb": { 29 | boxShadow: "0 2px 4px 0 rgb(0 35 11 / 20%)", 30 | width: 16, 31 | height: 16, 32 | borderRadius: 8, 33 | transition: theme.transitions.create(["width"], { 34 | duration: 200, 35 | }), 36 | }, 37 | "& .MuiSwitch-track": { 38 | borderRadius: 20 / 2, 39 | opacity: 1, 40 | backgroundColor: 41 | theme.palette.mode === "dark" 42 | ? "rgba(255,255,255,.35)" 43 | : "rgba(0,0,0,.25)", 44 | boxSizing: "border-box", 45 | }, 46 | })); 47 | 48 | export default AntSwitch; 49 | -------------------------------------------------------------------------------- /src/components/Chat/index.js: -------------------------------------------------------------------------------- 1 | export {default as ChatHeader} from "./Header"; 2 | export {default as ChatFooter} from "./Footer"; -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/Image/Image.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { forwardRef } from 'react'; 3 | import { LazyLoadImage } from 'react-lazy-load-image-component'; 4 | // @mui 5 | import { Box } from '@mui/material'; 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | const Image = forwardRef( 10 | ({ disabledEffect = false, effect = 'blur', sx, ...other }, ref) => { 11 | const content = ( 12 | 20 | ); 21 | 22 | return ( 23 | 39 | {content} 40 | 41 | ); 42 | } 43 | ); 44 | 45 | Image.propTypes = { 46 | sx: PropTypes.object, 47 | effect: PropTypes.string, 48 | disabledEffect: PropTypes.bool, 49 | }; 50 | 51 | export default Image; 52 | -------------------------------------------------------------------------------- /src/components/Image/getRatio.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function getRatio(ratio = '1/1') { 4 | return { 5 | '4/3': 'calc(100% / 4 * 3)', 6 | '3/4': 'calc(100% / 3 * 4)', 7 | '6/4': 'calc(100% / 6 * 4)', 8 | '4/6': 'calc(100% / 4 * 6)', 9 | '16/9': 'calc(100% / 16 * 9)', 10 | '9/16': 'calc(100% / 9 * 16)', 11 | '21/9': 'calc(100% / 21 * 9)', 12 | '9/21': 'calc(100% / 9 * 21)', 13 | '1/1': '100%', 14 | }[ratio]; 15 | } 16 | -------------------------------------------------------------------------------- /src/components/Image/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Image'; 2 | -------------------------------------------------------------------------------- /src/components/LoadingScreen.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const LoadingScreen = () => { 4 | return <>Loading...; 5 | }; 6 | 7 | export default LoadingScreen; 8 | -------------------------------------------------------------------------------- /src/components/Scrollbar.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import SimpleBarReact from 'simplebar-react'; 3 | // @mui 4 | import { alpha, styled } from '@mui/material/styles'; 5 | import { Box } from '@mui/material'; 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | const RootStyle = styled('div')(() => ({ 10 | flexGrow: 1, 11 | height: '100%', 12 | overflow: 'scroll', 13 | })); 14 | 15 | const SimpleBarStyle = styled(SimpleBarReact)(({ theme }) => ({ 16 | // maxHeight: '100%', 17 | '& .simplebar-scrollbar': { 18 | '&:before': { 19 | backgroundColor: alpha(theme.palette.grey[600], 0.48), 20 | }, 21 | '&.simplebar-visible:before': { 22 | opacity: 1, 23 | }, 24 | }, 25 | '& .simplebar-track.simplebar-vertical': { 26 | width: 10, 27 | }, 28 | '& .simplebar-track.simplebar-horizontal .simplebar-scrollbar': { 29 | height: 6, 30 | }, 31 | '& .simplebar-mask': { 32 | zIndex: 'inherit', 33 | }, 34 | "& .simplebar-placeholder": { 35 | height: '0 !important', 36 | } 37 | })); 38 | 39 | // ---------------------------------------------------------------------- 40 | 41 | Scrollbar.propTypes = { 42 | children: PropTypes.node.isRequired, 43 | sx: PropTypes.object, 44 | }; 45 | 46 | export default function Scrollbar({ children, sx, ...other }) { 47 | const userAgent = typeof navigator === 'undefined' ? 'SSR' : navigator.userAgent; 48 | 49 | const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent); 50 | 51 | if (isMobile) { 52 | return ( 53 | 54 | {children} 55 | 56 | ); 57 | } 58 | 59 | return ( 60 | 61 | 62 | {children} 63 | 64 | 65 | ); 66 | } 67 | 68 | export {SimpleBarStyle}; 69 | -------------------------------------------------------------------------------- /src/components/Search/Search.js: -------------------------------------------------------------------------------- 1 | import { styled, alpha } from "@mui/material/styles"; 2 | 3 | const Search = styled("div")(({ theme }) => ({ 4 | position: "relative", 5 | borderRadius: 20, 6 | backgroundColor: alpha(theme.palette.background.paper, 1), 7 | marginRight: theme.spacing(2), 8 | marginLeft: 0, 9 | width: "100%", 10 | })); 11 | 12 | export default Search; 13 | -------------------------------------------------------------------------------- /src/components/Search/SearchIconWrapper.js: -------------------------------------------------------------------------------- 1 | import { styled } from "@mui/material/styles"; 2 | 3 | const SearchIconWrapper = styled("div")(({ theme }) => ({ 4 | padding: theme.spacing(0, 2), 5 | height: "100%", 6 | position: "absolute", 7 | pointerEvents: "none", 8 | display: "flex", 9 | alignItems: "center", 10 | justifyContent: "center", 11 | })); 12 | 13 | export default SearchIconWrapper; 14 | -------------------------------------------------------------------------------- /src/components/Search/StyledInputBase.js: -------------------------------------------------------------------------------- 1 | import { InputBase } from "@mui/material"; 2 | import {styled} from "@mui/material/styles"; 3 | 4 | 5 | const StyledInputBase = styled(InputBase)(({ theme }) => ({ 6 | color: "inherit", 7 | "& .MuiInputBase-input": { 8 | padding: theme.spacing(1, 1, 1, 0), 9 | // vertical padding + font size from searchIcon 10 | paddingLeft: `calc(1em + ${theme.spacing(4)})`, 11 | width: "100%", 12 | }, 13 | })); 14 | 15 | export default StyledInputBase; -------------------------------------------------------------------------------- /src/components/Search/index.js: -------------------------------------------------------------------------------- 1 | export { default as Search } from "./Search"; 2 | export { default as SearchIconWrapper } from "./SearchIconWrapper"; 3 | export { default as StyledInputBase } from "./StyledInputBase"; 4 | -------------------------------------------------------------------------------- /src/components/animate/DialogAnimate.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { m, AnimatePresence } from 'framer-motion'; 3 | // @mui 4 | import { Dialog, Box, Paper } from '@mui/material'; 5 | // 6 | import { varFade } from './variants'; 7 | 8 | // ---------------------------------------------------------------------- 9 | 10 | DialogAnimate.propTypes = { 11 | children: PropTypes.node.isRequired, 12 | onClose: PropTypes.func, 13 | open: PropTypes.bool.isRequired, 14 | sx: PropTypes.object, 15 | variants: PropTypes.object 16 | }; 17 | 18 | export default function DialogAnimate({ open = false, variants, onClose, children, sx, ...other }) { 19 | return ( 20 | 21 | {open && ( 22 | ( 28 | 45 | 46 | 47 | {props.children} 48 | 49 | 50 | )} 51 | {...other} 52 | > 53 | {children} 54 | 55 | )} 56 | 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /src/components/animate/FabButtonAnimate.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { m } from 'framer-motion'; 3 | import { forwardRef } from 'react'; 4 | // @mui 5 | import { useTheme } from '@mui/material/styles'; 6 | import { Box, Fab } from '@mui/material'; 7 | 8 | // ---------------------------------------------------------------------- 9 | 10 | const FabButtonAnimate = forwardRef(({ color = 'primary', size = 'large', children, sx, sxWrap, ...other }, ref) => { 11 | const theme = useTheme(); 12 | 13 | if (color === 'default' || color === 'inherit' || color === 'primary' || color === 'secondary') { 14 | return ( 15 | 16 | 17 | {children} 18 | 19 | 20 | ); 21 | } 22 | 23 | return ( 24 | 25 | 39 | {children} 40 | 41 | 42 | ); 43 | }); 44 | 45 | FabButtonAnimate.propTypes = { 46 | children: PropTypes.node.isRequired, 47 | color: PropTypes.oneOf(['inherit', 'default', 'primary', 'secondary', 'info', 'success', 'warning', 'error']), 48 | size: PropTypes.oneOf(['small', 'medium', 'large']), 49 | sx: PropTypes.object, 50 | sxWrap: PropTypes.object 51 | }; 52 | 53 | export default FabButtonAnimate; 54 | 55 | // ---------------------------------------------------------------------- 56 | 57 | const varSmall = { 58 | hover: { scale: 1.07 }, 59 | tap: { scale: 0.97 } 60 | }; 61 | 62 | const varMedium = { 63 | hover: { scale: 1.06 }, 64 | tap: { scale: 0.98 } 65 | }; 66 | 67 | const varLarge = { 68 | hover: { scale: 1.05 }, 69 | tap: { scale: 0.99 } 70 | }; 71 | 72 | AnimateWrap.propTypes = { 73 | children: PropTypes.node.isRequired, 74 | size: PropTypes.oneOf(['small', 'medium', 'large']), 75 | sxWrap: PropTypes.object 76 | }; 77 | 78 | function AnimateWrap({ size, children, sxWrap }) { 79 | const isSmall = size === 'small'; 80 | const isLarge = size === 'large'; 81 | 82 | return ( 83 | 93 | {children} 94 | 95 | ); 96 | } 97 | -------------------------------------------------------------------------------- /src/components/animate/IconButtonAnimate.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { m } from 'framer-motion'; 3 | import { forwardRef } from 'react'; 4 | // @mui 5 | import { Box, IconButton } from '@mui/material'; 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | const IconButtonAnimate = forwardRef(({ children, size = 'medium', ...other }, ref) => ( 10 | 11 | 12 | {children} 13 | 14 | 15 | )); 16 | 17 | IconButtonAnimate.propTypes = { 18 | children: PropTypes.node.isRequired, 19 | color: PropTypes.oneOf(['inherit', 'default', 'primary', 'secondary', 'info', 'success', 'warning', 'error']), 20 | size: PropTypes.oneOf(['small', 'medium', 'large']) 21 | }; 22 | 23 | export default IconButtonAnimate; 24 | 25 | // ---------------------------------------------------------------------- 26 | 27 | const varSmall = { 28 | hover: { scale: 1.1 }, 29 | tap: { scale: 0.95 } 30 | }; 31 | 32 | const varMedium = { 33 | hover: { scale: 1.09 }, 34 | tap: { scale: 0.97 } 35 | }; 36 | 37 | const varLarge = { 38 | hover: { scale: 1.08 }, 39 | tap: { scale: 0.99 } 40 | }; 41 | 42 | AnimateWrap.propTypes = { 43 | children: PropTypes.node.isRequired, 44 | size: PropTypes.oneOf(['small', 'medium', 'large']) 45 | }; 46 | 47 | function AnimateWrap({ size, children }) { 48 | const isSmall = size === 'small'; 49 | const isLarge = size === 'large'; 50 | 51 | return ( 52 | 61 | {children} 62 | 63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/animate/features.js: -------------------------------------------------------------------------------- 1 | import { domMax } from 'framer-motion'; 2 | 3 | export default domMax; 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/animate/variants/actions.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export const varHover = (scale) => ({ 4 | hover: { 5 | scale: scale || 1.1 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /src/components/animate/variants/background.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export const varBgColor = (props) => { 4 | const colors = props?.colors || ['#19dcea', '#b22cff']; 5 | const duration = props?.duration || 5; 6 | const ease = props?.ease || 'linear'; 7 | 8 | return { 9 | animate: { 10 | background: colors, 11 | transition: { duration, ease } 12 | } 13 | }; 14 | }; 15 | 16 | // ---------------------------------------------------------------------- 17 | 18 | export const varBgKenburns = (props) => { 19 | const duration = props?.duration || 5; 20 | const ease = props?.ease || 'easeOut'; 21 | 22 | return { 23 | top: { 24 | animate: { 25 | scale: [1, 1.25], 26 | y: [0, -15], 27 | transformOrigin: ['50% 16%', 'top'], 28 | transition: { duration, ease } 29 | } 30 | }, 31 | right: { 32 | animate: { 33 | scale: [1, 1.25], 34 | x: [0, 20], 35 | y: [0, -15], 36 | transformOrigin: ['84% 50%', 'right'], 37 | transition: { duration, ease } 38 | } 39 | }, 40 | bottom: { 41 | animate: { 42 | scale: [1, 1.25], 43 | y: [0, 15], 44 | transformOrigin: ['50% 84%', 'bottom'], 45 | transition: { duration, ease } 46 | } 47 | }, 48 | left: { 49 | animate: { 50 | scale: [1, 1.25], 51 | x: [0, -20], 52 | y: [0, 15], 53 | transformOrigin: ['16% 50%', 'left'], 54 | transition: { duration, ease } 55 | } 56 | } 57 | }; 58 | }; 59 | 60 | // ---------------------------------------------------------------------- 61 | 62 | export const varBgPan = (props) => { 63 | const colors = props?.colors || ['#ee7752', '#e73c7e', '#23a6d5', '#23d5ab']; 64 | const duration = props?.duration || 5; 65 | const ease = props?.ease || 'linear'; 66 | 67 | const gradient = (deg) => `linear-gradient(${deg}deg, ${colors})`; 68 | 69 | return { 70 | top: { 71 | animate: { 72 | backgroundImage: [gradient(0), gradient(0)], 73 | backgroundPosition: ['center 99%', 'center 1%'], 74 | backgroundSize: ['100% 600%', '100% 600%'], 75 | transition: { duration, ease } 76 | } 77 | }, 78 | right: { 79 | animate: { 80 | backgroundPosition: ['1% center', '99% center'], 81 | backgroundImage: [gradient(270), gradient(270)], 82 | backgroundSize: ['600% 100%', '600% 100%'], 83 | transition: { duration, ease } 84 | } 85 | }, 86 | bottom: { 87 | animate: { 88 | backgroundImage: [gradient(0), gradient(0)], 89 | backgroundPosition: ['center 1%', 'center 99%'], 90 | backgroundSize: ['100% 600%', '100% 600%'], 91 | transition: { duration, ease } 92 | } 93 | }, 94 | left: { 95 | animate: { 96 | backgroundPosition: ['99% center', '1% center'], 97 | backgroundImage: [gradient(270), gradient(270)], 98 | backgroundSize: ['600% 100%', '600% 100%'], 99 | transition: { duration, ease } 100 | } 101 | } 102 | }; 103 | }; 104 | -------------------------------------------------------------------------------- /src/components/animate/variants/bounce.js: -------------------------------------------------------------------------------- 1 | import { varTranEnter, varTranExit } from './transition'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export const varBounce = (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 | in: { 14 | initial: {}, 15 | animate: { 16 | scale: [0.3, 1.1, 0.9, 1.03, 0.97, 1], 17 | opacity: [0, 1, 1, 1, 1, 1], 18 | transition: varTranEnter({ durationIn, easeIn }), 19 | }, 20 | exit: { 21 | scale: [0.9, 1.1, 0.3], 22 | opacity: [1, 1, 0], 23 | }, 24 | }, 25 | inUp: { 26 | initial: {}, 27 | animate: { 28 | y: [720, -24, 12, -4, 0], 29 | scaleY: [4, 0.9, 0.95, 0.985, 1], 30 | opacity: [0, 1, 1, 1, 1], 31 | transition: { ...varTranEnter({ durationIn, easeIn }) }, 32 | }, 33 | exit: { 34 | y: [12, -24, 720], 35 | scaleY: [0.985, 0.9, 3], 36 | opacity: [1, 1, 0], 37 | transition: varTranExit({ durationOut, easeOut }), 38 | }, 39 | }, 40 | inDown: { 41 | initial: {}, 42 | animate: { 43 | y: [-720, 24, -12, 4, 0], 44 | scaleY: [4, 0.9, 0.95, 0.985, 1], 45 | opacity: [0, 1, 1, 1, 1], 46 | transition: varTranEnter({ durationIn, easeIn }), 47 | }, 48 | exit: { 49 | y: [-12, 24, -720], 50 | scaleY: [0.985, 0.9, 3], 51 | opacity: [1, 1, 0], 52 | transition: varTranExit({ durationOut, easeOut }), 53 | }, 54 | }, 55 | inLeft: { 56 | initial: {}, 57 | animate: { 58 | x: [-720, 24, -12, 4, 0], 59 | scaleX: [3, 1, 0.98, 0.995, 1], 60 | opacity: [0, 1, 1, 1, 1], 61 | transition: varTranEnter({ durationIn, easeIn }), 62 | }, 63 | exit: { 64 | x: [0, 24, -720], 65 | scaleX: [1, 0.9, 2], 66 | opacity: [1, 1, 0], 67 | transition: varTranExit({ durationOut, easeOut }), 68 | }, 69 | }, 70 | inRight: { 71 | initial: {}, 72 | animate: { 73 | x: [720, -24, 12, -4, 0], 74 | scaleX: [3, 1, 0.98, 0.995, 1], 75 | opacity: [0, 1, 1, 1, 1], 76 | transition: varTranEnter({ durationIn, easeIn }), 77 | }, 78 | exit: { 79 | x: [0, -24, 720], 80 | scaleX: [1, 0.9, 2], 81 | opacity: [1, 1, 0], 82 | transition: varTranExit({ durationOut, easeOut }), 83 | }, 84 | }, 85 | 86 | // OUT 87 | out: { 88 | animate: { scale: [0.9, 1.1, 0.3], opacity: [1, 1, 0] }, 89 | }, 90 | outUp: { 91 | animate: { y: [-12, 24, -720], scaleY: [0.985, 0.9, 3], opacity: [1, 1, 0] }, 92 | }, 93 | outDown: { 94 | animate: { y: [12, -24, 720], scaleY: [0.985, 0.9, 3], opacity: [1, 1, 0] }, 95 | }, 96 | outLeft: { 97 | animate: { x: [0, 24, -720], scaleX: [1, 0.9, 2], opacity: [1, 1, 0] }, 98 | }, 99 | outRight: { 100 | animate: { x: [0, -24, 720], scaleX: [1, 0.9, 2], opacity: [1, 1, 0] }, 101 | }, 102 | }; 103 | }; 104 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/animate/variants/fade.js: -------------------------------------------------------------------------------- 1 | import { varTranEnter, varTranExit } from './transition'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export const varFade = (props) => { 6 | const distance = props?.distance || 120; 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 }, 16 | animate: { opacity: 1, transition: varTranEnter }, 17 | exit: { opacity: 0, transition: varTranExit } 18 | }, 19 | inUp: { 20 | initial: { y: distance, opacity: 0 }, 21 | animate: { y: 0, opacity: 1, transition: varTranEnter({ durationIn, easeIn }) }, 22 | exit: { y: distance, opacity: 0, transition: varTranExit({ durationOut, easeOut }) } 23 | }, 24 | inDown: { 25 | initial: { y: -distance, opacity: 0 }, 26 | animate: { y: 0, opacity: 1, transition: varTranEnter({ durationIn, easeIn }) }, 27 | exit: { y: -distance, opacity: 0, transition: varTranExit({ durationOut, easeOut }) } 28 | }, 29 | inLeft: { 30 | initial: { x: -distance, opacity: 0 }, 31 | animate: { x: 0, opacity: 1, transition: varTranEnter({ durationIn, easeIn }) }, 32 | exit: { x: -distance, opacity: 0, transition: varTranExit({ durationOut, easeOut }) } 33 | }, 34 | inRight: { 35 | initial: { x: distance, opacity: 0 }, 36 | animate: { x: 0, opacity: 1, transition: varTranEnter({ durationIn, easeIn }) }, 37 | exit: { x: distance, opacity: 0, transition: varTranExit({ durationOut, easeOut }) } 38 | }, 39 | 40 | // OUT 41 | out: { 42 | initial: { opacity: 1 }, 43 | animate: { opacity: 0, transition: varTranEnter({ durationIn, easeIn }) }, 44 | exit: { opacity: 1, transition: varTranExit({ durationOut, easeOut }) } 45 | }, 46 | outUp: { 47 | initial: { y: 0, opacity: 1 }, 48 | animate: { y: -distance, opacity: 0, transition: varTranEnter({ durationIn, easeIn }) }, 49 | exit: { y: 0, opacity: 1, transition: varTranExit({ durationOut, easeOut }) } 50 | }, 51 | outDown: { 52 | initial: { y: 0, opacity: 1 }, 53 | animate: { y: distance, opacity: 0, transition: varTranEnter({ durationIn, easeIn }) }, 54 | exit: { y: 0, opacity: 1, transition: varTranExit({ durationOut, easeOut }) } 55 | }, 56 | outLeft: { 57 | initial: { x: 0, opacity: 1 }, 58 | animate: { x: -distance, opacity: 0, transition: varTranEnter({ durationIn, easeIn }) }, 59 | exit: { x: 0, opacity: 1, transition: varTranExit({ durationOut, easeOut }) } 60 | }, 61 | outRight: { 62 | initial: { x: 0, opacity: 1 }, 63 | animate: { x: distance, opacity: 0, transition: varTranEnter({ durationIn, easeIn }) }, 64 | exit: { x: 0, opacity: 1, transition: varTranExit({ durationOut, easeOut }) } 65 | } 66 | }; 67 | }; 68 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/animate/variants/slide.js: -------------------------------------------------------------------------------- 1 | // 2 | import { varTranEnter, varTranExit } from './transition'; 3 | 4 | // ---------------------------------------------------------------------- 5 | 6 | export const varSlide = (props) => { 7 | const distance = props?.distance || 160; 8 | const durationIn = props?.durationIn; 9 | const durationOut = props?.durationOut; 10 | const easeIn = props?.easeIn; 11 | const easeOut = props?.easeOut; 12 | 13 | return { 14 | // IN 15 | inUp: { 16 | initial: { y: distance }, 17 | animate: { y: 0, transition: varTranEnter({ durationIn, easeIn }) }, 18 | exit: { y: distance, transition: varTranExit({ durationOut, easeOut }) } 19 | }, 20 | inDown: { 21 | initial: { y: -distance }, 22 | animate: { y: 0, transition: varTranEnter({ durationIn, easeIn }) }, 23 | exit: { y: -distance, transition: varTranExit({ durationOut, easeOut }) } 24 | }, 25 | inLeft: { 26 | initial: { x: -distance }, 27 | animate: { x: 0, transition: varTranEnter({ durationIn, easeIn }) }, 28 | exit: { x: -distance, transition: varTranExit({ durationOut, easeOut }) } 29 | }, 30 | inRight: { 31 | initial: { x: distance }, 32 | animate: { x: 0, transition: varTranEnter({ durationIn, easeIn }) }, 33 | exit: { x: distance, transition: varTranExit({ durationOut, easeOut }) } 34 | }, 35 | 36 | // OUT 37 | outUp: { 38 | initial: { y: 0 }, 39 | animate: { y: -distance, transition: varTranEnter({ durationIn, easeIn }) }, 40 | exit: { y: 0, transition: varTranExit({ durationOut, easeOut }) } 41 | }, 42 | outDown: { 43 | initial: { y: 0 }, 44 | animate: { y: distance, transition: varTranEnter({ durationIn, easeIn }) }, 45 | exit: { y: 0, transition: varTranExit({ durationOut, easeOut }) } 46 | }, 47 | outLeft: { 48 | initial: { x: 0 }, 49 | animate: { x: -distance, transition: varTranEnter({ durationIn, easeIn }) }, 50 | exit: { x: 0, transition: varTranExit({ durationOut, easeOut }) } 51 | }, 52 | outRight: { 53 | initial: { x: 0 }, 54 | animate: { x: distance, transition: varTranEnter({ durationIn, easeIn }) }, 55 | exit: { x: 0, transition: varTranExit({ durationOut, easeOut }) } 56 | } 57 | }; 58 | }; 59 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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, 9 | methods: PropTypes.object, 10 | onSubmit: PropTypes.func, 11 | }; 12 | 13 | export default function FormProvider({ children, onSubmit, methods }) { 14 | return ( 15 |
16 | {children}
17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/components/hook-form/RHFAutocomplete.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // form 3 | import { useFormContext, Controller } from 'react-hook-form'; 4 | // @mui 5 | import { Autocomplete, TextField } from '@mui/material'; 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | RHFAutocomplete.propTypes = { 10 | name: PropTypes.string, 11 | label: PropTypes.string, 12 | helperText: PropTypes.node, 13 | }; 14 | 15 | export default function RHFAutocomplete({ name, label, helperText, ...other }) { 16 | const { control, setValue } = useFormContext(); 17 | 18 | return ( 19 | ( 23 | setValue(name, newValue, { shouldValidate: true })} 26 | renderInput={(params) => ( 27 | 33 | )} 34 | {...other} 35 | /> 36 | )} 37 | /> 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /src/components/hook-form/RHFCodes.js: -------------------------------------------------------------------------------- 1 | 2 | import { useRef } from "react"; 3 | // form 4 | import { useFormContext, Controller } from "react-hook-form"; 5 | // @mui 6 | import { Stack, TextField } from "@mui/material"; 7 | 8 | 9 | export default function RHFCodes({ keyName = "", inputs = [], ...other }) { 10 | const codesRef = useRef(null); 11 | 12 | const { control } = useFormContext(); 13 | 14 | const handleChangeWithNextField = (event, handleChange) => { 15 | const { maxLength, value, name } = event.target; 16 | 17 | const fieldIndex = name.replace(keyName, ""); 18 | 19 | const fieldIntIndex = Number(fieldIndex); 20 | 21 | const nextfield = document.querySelector( 22 | `input[name=${keyName}${fieldIntIndex + 1}]` 23 | ); 24 | 25 | if (value.length > maxLength) { 26 | event.target.value = value[0]; 27 | } 28 | 29 | if (value.length >= maxLength && fieldIntIndex < 6 && nextfield !== null) { 30 | nextfield.focus(); 31 | } 32 | 33 | handleChange(event); 34 | }; 35 | 36 | return ( 37 | 38 | {inputs.map((name, index) => ( 39 | ( 44 | { 50 | handleChangeWithNextField(event, field.onChange); 51 | }} 52 | onFocus={(event) => event.currentTarget.select()} 53 | InputProps={{ 54 | sx: { 55 | width: { xs: 36, sm: 56 }, 56 | height: { xs: 36, sm: 56 }, 57 | "& input": { p: 0, textAlign: "center" }, 58 | }, 59 | }} 60 | inputProps={{ 61 | maxLength: 1, 62 | type: "number", 63 | }} 64 | {...other} 65 | /> 66 | )} 67 | /> 68 | ))} 69 | 70 | ); 71 | } 72 | -------------------------------------------------------------------------------- /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 | helperText: PropTypes.node, 12 | }; 13 | 14 | export default function RHFTextField({ name, helperText, ...other }) { 15 | const { control } = useFormContext(); 16 | 17 | return ( 18 | ( 22 | 31 | )} 32 | /> 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/components/hook-form/RHFUpload.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 { UploadAvatar } from '../upload'; 8 | 9 | // ---------------------------------------------------------------------- 10 | 11 | RHFUploadAvatar.propTypes = { 12 | name: PropTypes.string, 13 | }; 14 | 15 | // ---------------------------------------------------------------------- 16 | 17 | export function RHFUploadAvatar({ name, ...other }) { 18 | const { control } = useFormContext(); 19 | 20 | return ( 21 | ( 25 |
26 | 34 | 35 | {!!error && ( 36 | 37 | {error.message} 38 | 39 | )} 40 |
41 | )} 42 | /> 43 | ); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/components/hook-form/index.js: -------------------------------------------------------------------------------- 1 | export * from './RHFUpload'; 2 | export { default } from './FormProvider'; 3 | 4 | 5 | export { default as RHFTextField } from './RHFTextField'; 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/settings/ThemeContrast.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { useMemo } from 'react'; 3 | // @mui 4 | import { CssBaseline } from '@mui/material'; 5 | import { alpha, ThemeProvider, createTheme, useTheme } from '@mui/material/styles'; 6 | // hooks 7 | import useSettings from '../../hooks/useSettings'; 8 | // 9 | import componentsOverride from '../../theme/overrides'; 10 | 11 | // ---------------------------------------------------------------------- 12 | 13 | ThemeContrast.propTypes = { 14 | children: PropTypes.node, 15 | }; 16 | 17 | export default function ThemeContrast({ children }) { 18 | const defaultTheme = useTheme(); 19 | 20 | const { themeContrast } = useSettings(); 21 | 22 | const isLight = defaultTheme.palette.mode === 'light'; 23 | 24 | const shadowColor = isLight ? defaultTheme.palette.grey[500] : defaultTheme.palette.common.black; 25 | 26 | const styles = { 27 | bgDefault: defaultTheme.palette.background.default, 28 | bgBold: isLight ? defaultTheme.palette.grey[100] : defaultTheme.palette.grey[900], 29 | cardDefault: defaultTheme.components?.MuiCard?.styleOverrides?.root, 30 | cardBold: { 31 | zIndex: 0, 32 | position: 'relative', 33 | borderRadius: Number(defaultTheme.shape.borderRadius) * 2, 34 | boxShadow: `0 0 1px 0 ${alpha(shadowColor, 0.48)}, 0 2px 4px -1px ${alpha(shadowColor, 0.24)}`, 35 | }, 36 | }; 37 | 38 | const themeOptions = useMemo( 39 | () => ({ 40 | ...defaultTheme, 41 | palette: { 42 | ...defaultTheme.palette, 43 | background: { 44 | ...defaultTheme.palette.background, 45 | default: themeContrast === 'bold' ? styles.bgBold : styles.bgDefault, 46 | }, 47 | }, 48 | components: { 49 | MuiCard: { 50 | styleOverrides: { 51 | root: themeContrast === 'bold' ? styles.cardBold : styles.cardDefault, 52 | }, 53 | }, 54 | }, 55 | }), 56 | 57 | [defaultTheme, themeContrast, styles.bgBold, styles.bgDefault, styles.cardBold, styles.cardDefault] 58 | ); 59 | 60 | const theme = createTheme(themeOptions); 61 | 62 | theme.components = { 63 | ...componentsOverride(theme), 64 | MuiCard: themeOptions.components?.MuiCard, 65 | }; 66 | 67 | return ( 68 | 69 | 70 | {children} 71 | 72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/settings/drawer/SettingColorPresets.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { alpha, styled } from '@mui/material/styles'; 3 | import { Box, Grid, RadioGroup, CardActionArea } from '@mui/material'; 4 | // hooks 5 | import useSettings from '../../../hooks/useSettings'; 6 | // 7 | import BoxMask from './BoxMask'; 8 | 9 | // ---------------------------------------------------------------------- 10 | 11 | const BoxStyle = styled(CardActionArea)(({ theme }) => ({ 12 | height: 48, 13 | display: 'flex', 14 | alignItems: 'center', 15 | justifyContent: 'center', 16 | color: theme.palette.text.disabled, 17 | border: `solid 1px ${theme.palette.grey[500_12]}`, 18 | borderRadius: Number(theme.shape.borderRadius) * 1.25, 19 | })); 20 | 21 | // ---------------------------------------------------------------------- 22 | 23 | export default function SettingColorPresets() { 24 | const { themeColorPresets, onChangeColor, colorOption } = useSettings(); 25 | 26 | return ( 27 | 28 | 29 | {colorOption.map((color) => { 30 | const colorName = color.name; 31 | const colorValue = color.value; 32 | const isSelected = themeColorPresets === colorName; 33 | 34 | return ( 35 | 36 | 45 | 53 | theme.transitions.create('all', { 54 | easing: theme.transitions.easing.easeInOut, 55 | duration: theme.transitions.duration.shorter, 56 | }), 57 | ...(isSelected && { transform: 'none' }), 58 | }} 59 | /> 60 | 61 | 62 | 63 | 64 | ); 65 | })} 66 | 67 | 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /src/components/settings/drawer/SettingContrast.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { styled } from '@mui/material/styles'; 3 | import { Grid, RadioGroup, CardActionArea } from '@mui/material'; 4 | // hooks 5 | import useSettings from '../../../hooks/useSettings'; 6 | // 7 | import Iconify from '../../Iconify'; 8 | import BoxMask from './BoxMask'; 9 | 10 | // ---------------------------------------------------------------------- 11 | 12 | const BoxStyle = styled(CardActionArea)(({ theme }) => ({ 13 | height: 72, 14 | display: 'flex', 15 | alignItems: 'center', 16 | justifyContent: 'center', 17 | color: theme.palette.text.disabled, 18 | border: `solid 1px ${theme.palette.grey[500_12]}`, 19 | borderRadius: Number(theme.shape.borderRadius) * 1.25, 20 | })); 21 | 22 | // ---------------------------------------------------------------------- 23 | 24 | export default function SettingContrast() { 25 | const { themeContrast, onChangeContrast } = useSettings(); 26 | 27 | return ( 28 | 29 | 30 | {['default', 'bold'].map((contrast, index) => { 31 | const isSelected = themeContrast === contrast; 32 | 33 | return ( 34 | 35 | theme.customShadows.z20, 40 | }), 41 | }} 42 | > 43 | 44 | 45 | 46 | 47 | ); 48 | })} 49 | 50 | 51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /src/components/settings/drawer/SettingDirection.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { styled } from '@mui/material/styles'; 3 | import { Grid, RadioGroup, CardActionArea } from '@mui/material'; 4 | // hooks 5 | import useSettings from '../../../hooks/useSettings'; 6 | // 7 | import Iconify from '../../Iconify'; 8 | import BoxMask from './BoxMask'; 9 | 10 | // ---------------------------------------------------------------------- 11 | 12 | const BoxStyle = styled(CardActionArea)(({ theme }) => ({ 13 | height: 72, 14 | display: 'flex', 15 | alignItems: 'center', 16 | justifyContent: 'center', 17 | color: theme.palette.text.disabled, 18 | border: `solid 1px ${theme.palette.grey[500_12]}`, 19 | borderRadius: Number(theme.shape.borderRadius) * 1.25, 20 | })); 21 | 22 | // ---------------------------------------------------------------------- 23 | 24 | export default function SettingDirection() { 25 | const { themeDirection, onChangeDirection } = useSettings(); 26 | 27 | return ( 28 | 29 | 30 | {['ltr', 'rtl'].map((direction, index) => { 31 | const isSelected = themeDirection === direction; 32 | 33 | return ( 34 | 35 | theme.customShadows.z20, 40 | }), 41 | }} 42 | > 43 | 48 | 49 | 50 | 51 | ); 52 | })} 53 | 54 | 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/settings/drawer/SettingMode.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { styled } from '@mui/material/styles'; 3 | import { Grid, RadioGroup, CardActionArea } from '@mui/material'; 4 | // hooks 5 | import useSettings from '../../../hooks/useSettings'; 6 | // 7 | import Iconify from '../../Iconify'; 8 | import BoxMask from './BoxMask'; 9 | 10 | // ---------------------------------------------------------------------- 11 | 12 | const BoxStyle = styled(CardActionArea)(({ theme }) => ({ 13 | height: 72, 14 | display: 'flex', 15 | alignItems: 'center', 16 | justifyContent: 'center', 17 | color: theme.palette.text.disabled, 18 | border: `solid 1px ${theme.palette.grey[500_12]}`, 19 | borderRadius: Number(theme.shape.borderRadius) * 1.25, 20 | })); 21 | 22 | // ---------------------------------------------------------------------- 23 | 24 | export default function SettingMode() { 25 | const { themeMode, onChangeMode } = useSettings(); 26 | 27 | return ( 28 | 29 | 30 | {['light', 'dark'].map((mode, index) => { 31 | const isSelected = themeMode === mode; 32 | 33 | return ( 34 | 35 | theme.customShadows.z20, 41 | }), 42 | }} 43 | > 44 | 45 | 46 | 47 | 48 | ); 49 | })} 50 | 51 | 52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /src/components/settings/drawer/SettingStretch.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { styled } from '@mui/material/styles'; 3 | import { CardActionArea, Stack } from '@mui/material'; 4 | // hooks 5 | import useSettings from '../../../hooks/useSettings'; 6 | // 7 | import Iconify from '../../Iconify'; 8 | 9 | // ---------------------------------------------------------------------- 10 | 11 | const BoxStyle = styled(CardActionArea)(({ theme }) => ({ 12 | padding: theme.spacing(2), 13 | color: theme.palette.text.disabled, 14 | border: `solid 1px ${theme.palette.grey[500_12]}`, 15 | backgroundColor: theme.palette.background.neutral, 16 | borderRadius: Number(theme.shape.borderRadius) * 1.25, 17 | })); 18 | 19 | // ---------------------------------------------------------------------- 20 | 21 | export default function SettingStretch() { 22 | const { themeStretch, onToggleStretch } = useSettings(); 23 | 24 | const ICON_SIZE = { 25 | width: themeStretch ? 24 : 18, 26 | height: themeStretch ? 24 : 18, 27 | }; 28 | 29 | return ( 30 | theme.palette.primary.main, 35 | }), 36 | }} 37 | > 38 | theme.customShadows.z12, 51 | transition: (theme) => theme.transitions.create('width'), 52 | ...(themeStretch && { 53 | width: 1, 54 | color: 'primary.main', 55 | }), 56 | }} 57 | > 58 | 59 | 60 | 61 | 62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /src/components/settings/drawer/ToggleButton.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // @mui 3 | import { alpha, styled } from '@mui/material/styles'; 4 | import { Tooltip } from '@mui/material'; 5 | // utils 6 | import cssStyles from '../../../utils/cssStyles'; 7 | // 8 | import Iconify from '../../Iconify'; 9 | import { IconButtonAnimate } from '../../animate'; 10 | 11 | // ---------------------------------------------------------------------- 12 | 13 | const RootStyle = styled('span')(({ theme }) => ({ 14 | ...cssStyles(theme).bgBlur({ opacity: 0.64 }), 15 | right: 0, 16 | top: '50%', 17 | position: 'fixed', 18 | marginTop: theme.spacing(-3), 19 | padding: theme.spacing(0.5), 20 | zIndex: theme.zIndex.drawer + 2, 21 | borderRadius: '24px 0 20px 24px', 22 | boxShadow: `-12px 12px 32px -4px ${alpha( 23 | theme.palette.mode === 'light' ? theme.palette.grey[600] : theme.palette.common.black, 24 | 0.36 25 | )}`, 26 | })); 27 | 28 | const DotStyle = styled('span')(({ theme }) => ({ 29 | top: 8, 30 | width: 8, 31 | height: 8, 32 | right: 10, 33 | borderRadius: '50%', 34 | position: 'absolute', 35 | backgroundColor: theme.palette.error.main, 36 | })); 37 | 38 | // ---------------------------------------------------------------------- 39 | 40 | ToggleButton.propTypes = { 41 | notDefault: PropTypes.bool, 42 | onToggle: PropTypes.func, 43 | open: PropTypes.bool, 44 | }; 45 | 46 | export default function ToggleButton({ notDefault, open, onToggle }) { 47 | return ( 48 | 49 | {notDefault && !open && } 50 | 51 | 52 | theme.transitions.create('all'), 58 | '&:hover': { 59 | color: 'primary.main', 60 | bgcolor: (theme) => alpha(theme.palette.primary.main, theme.palette.action.hoverOpacity), 61 | }, 62 | }} 63 | > 64 | 65 | 66 | 67 | 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/upload/index.js: -------------------------------------------------------------------------------- 1 | export { default as AvatarPreview } from './preview/AvatarPreview'; 2 | export { default as UploadAvatar } from './UploadAvatar'; 3 | -------------------------------------------------------------------------------- /src/components/upload/preview/AvatarPreview.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | // 3 | import Image from '../../Image'; 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | AvatarPreview.propTypes = { 8 | file: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), 9 | }; 10 | 11 | export default function AvatarPreview({ file }) { 12 | if (!file) { 13 | return null; 14 | } 15 | 16 | const imgUrl = typeof file === 'string' ? file : file.preview; 17 | 18 | return ( 19 | avatar 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { enUS, frFR, zhCN, viVN, arSD } from '@mui/material/locale'; 3 | 4 | // routes 5 | import { PATH_DASHBOARD } from "./routes/paths"; 6 | 7 | export const BASE_URL = "https://api.chat.codingmonk.in/"; 8 | 9 | export const S3_BUCKET_NAME = 'codingmonk'; 10 | export const AWS_ACCESS_KEY = 'AKIARPJQ4HSYLBIK2TDE'; 11 | export const AWS_SECRET_ACCESS_KEY = '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'; 12 | export const AWS_S3_REGION = 'ap-south-1'; // eg. ap-south-1 13 | 14 | export const defaultSettings = { 15 | themeMode: "light", 16 | themeDirection: "ltr", 17 | themeContrast: "default", 18 | themeLayout: "horizontal", 19 | themeColorPresets: "default", 20 | themeStretch: false, 21 | }; 22 | 23 | export const NAVBAR = { 24 | BASE_WIDTH: 260, 25 | DASHBOARD_WIDTH: 280, 26 | DASHBOARD_COLLAPSE_WIDTH: 88, 27 | // 28 | DASHBOARD_ITEM_ROOT_HEIGHT: 48, 29 | DASHBOARD_ITEM_SUB_HEIGHT: 40, 30 | DASHBOARD_ITEM_HORIZONTAL_HEIGHT: 32, 31 | }; 32 | 33 | export const allLangs = [ 34 | { 35 | label: 'English', 36 | value: 'en', 37 | systemValue: enUS, 38 | icon: '/assets/icons/flags/ic_flag_en.svg', 39 | }, 40 | { 41 | label: 'French', 42 | value: 'fr', 43 | systemValue: frFR, 44 | icon: '/assets/icons/flags/ic_flag_fr.svg', 45 | }, 46 | { 47 | label: 'Vietnamese', 48 | value: 'vn', 49 | systemValue: viVN, 50 | icon: '/assets/icons/flags/ic_flag_vn.svg', 51 | }, 52 | { 53 | label: 'Chinese', 54 | value: 'cn', 55 | systemValue: zhCN, 56 | icon: '/assets/icons/flags/ic_flag_cn.svg', 57 | }, 58 | { 59 | label: 'Arabic (Sudan)', 60 | value: 'ar', 61 | systemValue: arSD, 62 | icon: '/assets/icons/flags/ic_flag_sa.svg', 63 | }, 64 | ]; 65 | 66 | export const defaultLang = allLangs[0]; // English 67 | 68 | 69 | 70 | // DEFAULT ROOT PATH 71 | export const DEFAULT_PATH = PATH_DASHBOARD.general.app; // as '/app' 72 | -------------------------------------------------------------------------------- /src/hooks/useEventListener.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useLayoutEffect } from 'react'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect; 6 | 7 | function useEventListener(eventName, handler, element, options) { 8 | // Create a ref that stores handler 9 | const savedHandler = useRef(handler); 10 | 11 | useIsomorphicLayoutEffect(() => { 12 | savedHandler.current = handler; 13 | }, [handler]); 14 | 15 | useEffect(() => { 16 | // Define the listening target 17 | const targetElement = element?.current || window; 18 | if (!(targetElement && targetElement.addEventListener)) { 19 | return; 20 | } 21 | 22 | // Create event listener that calls handler function stored in ref 23 | const eventListener = (event) => savedHandler.current(event); 24 | 25 | targetElement.addEventListener(eventName, eventListener, options); 26 | 27 | // Remove event listener on cleanup 28 | // eslint-disable-next-line consistent-return 29 | return () => { 30 | targetElement.removeEventListener(eventName, eventListener); 31 | }; 32 | }, [eventName, element, options]); 33 | } 34 | 35 | export default useEventListener; 36 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | import { HelmetProvider } from "react-helmet-async"; 5 | import App from "./App"; 6 | import reportWebVitals from "./reportWebVitals"; 7 | 8 | import { Provider as ReduxProvider } from 'react-redux'; 9 | 10 | // contexts 11 | import SettingsProvider from "./contexts/SettingsContext"; 12 | import { store } from "./redux/store"; 13 | 14 | const root = ReactDOM.createRoot(document.getElementById("root")); 15 | 16 | root.render( 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | 30 | // If you want to start measuring performance in your app, pass a function 31 | // to log results (for example: reportWebVitals(console.log)) 32 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 33 | reportWebVitals(); 34 | -------------------------------------------------------------------------------- /src/layouts/auth/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Container, Stack } from "@mui/material"; 3 | import { Navigate, Outlet } from "react-router-dom"; 4 | 5 | import Logo from "../../assets/Images/logo.ico"; 6 | import { useSelector } from "react-redux"; 7 | 8 | const AuthLayout = () => { 9 | const { isLoggedIn } = useSelector((state) => state.auth); 10 | 11 | if (isLoggedIn) { 12 | return ; 13 | } 14 | 15 | return ( 16 | <> 17 | 18 | 19 | 24 | Logo 25 | 26 | 27 | 28 | 29 | 30 | ); 31 | }; 32 | 33 | export default AuthLayout; 34 | -------------------------------------------------------------------------------- /src/layouts/dashboard/BottomNav.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTheme } from "@mui/material/styles"; 3 | import { Box, IconButton, Stack } from "@mui/material"; 4 | import ProfileMenu from "./ProfileMenu"; 5 | import { Nav_Buttons } from "../../data"; 6 | 7 | const BottomNav = () => { 8 | const theme = useTheme(); 9 | 10 | const [selectedTab, setSelectedTab] = React.useState(0); 11 | 12 | const handleChangeTab = (index) => { 13 | setSelectedTab(index); 14 | }; 15 | 16 | return ( 17 | 28 | 36 | {Nav_Buttons.map((el) => { 37 | return el.index === selectedTab ? ( 38 | 39 | 40 | {el.icon} 41 | 42 | 43 | ) : ( 44 | { 46 | handleChangeTab(el.index); 47 | }} 48 | sx={{ 49 | width: "max-content", 50 | color: 51 | theme.palette.mode === "light" 52 | ? "#080707" 53 | : theme.palette.text.primary, 54 | }} 55 | > 56 | {el.icon} 57 | 58 | ); 59 | })} 60 | 61 | 62 | 63 | ); 64 | }; 65 | 66 | export default BottomNav; 67 | -------------------------------------------------------------------------------- /src/layouts/dashboard/ProfileMenu.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Avatar, Box, Fade, Menu, MenuItem, Stack } from "@mui/material"; 3 | 4 | import { faker } from "@faker-js/faker"; 5 | 6 | import { Profile_Menu } from "../../data"; 7 | import { useDispatch, useSelector } from "react-redux"; 8 | import { LogoutUser } from "../../redux/slices/auth"; 9 | import { socket } from "../../socket"; 10 | import { useNavigate } from "react-router-dom"; 11 | import { AWS_S3_REGION, S3_BUCKET_NAME } from "../../config"; 12 | 13 | const ProfileMenu = () => { 14 | const {user} = useSelector((state) => state.app); 15 | const navigate = useNavigate(); 16 | const dispatch = useDispatch(); 17 | const [anchorEl, setAnchorEl] = React.useState(null); 18 | const openMenu = Boolean(anchorEl); 19 | const handleClick = (event) => { 20 | setAnchorEl(event.currentTarget); 21 | }; 22 | const handleClose = () => { 23 | setAnchorEl(null); 24 | }; 25 | 26 | const user_id = window.localStorage.getItem("user_id"); 27 | 28 | const user_name = user?.firstName; 29 | const user_img = `https://${S3_BUCKET_NAME}.s3.${AWS_S3_REGION}.amazonaws.com/${user?.avatar}`; 30 | 31 | return ( 32 | <> 33 | 42 | 61 | 62 | 63 | {Profile_Menu.map((el, idx) => ( 64 | 65 | { 67 | if(idx === 0) { 68 | navigate("/profile"); 69 | } 70 | else if(idx === 1) { 71 | navigate("/settings"); 72 | } 73 | else { 74 | dispatch(LogoutUser()); 75 | socket.emit("end", {user_id}); 76 | } 77 | }} 78 | sx={{ width: 100 }} 79 | direction="row" 80 | alignItems={"center"} 81 | justifyContent="space-between" 82 | > 83 | {el.title} 84 | {el.icon} 85 | {" "} 86 | 87 | ))} 88 | 89 | 90 | 91 | 92 | ); 93 | }; 94 | 95 | export default ProfileMenu; 96 | -------------------------------------------------------------------------------- /src/layouts/main/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useSelector } from "react-redux"; 3 | import { Navigate, Outlet } from "react-router-dom"; 4 | 5 | const MainLayout = () => { 6 | 7 | const {isLoggedIn} = useSelector((state) => state.auth); 8 | 9 | if (!isLoggedIn) { 10 | return ; 11 | } 12 | return ( 13 | <> 14 |
Main Layout
15 | 16 | 17 | 18 | ); 19 | }; 20 | 21 | export default MainLayout; 22 | -------------------------------------------------------------------------------- /src/pages/Page404.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Page404 = () => { 4 | return <>404; 5 | }; 6 | 7 | 8 | export default Page404; -------------------------------------------------------------------------------- /src/pages/auth/Login.js: -------------------------------------------------------------------------------- 1 | import { Link as RouterLink } from "react-router-dom"; 2 | // sections 3 | import { Stack, Typography, Link } from "@mui/material"; 4 | import AuthSocial from "../../sections/auth/AuthSocial"; 5 | import Login from "../../sections/auth/LoginForm"; 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | export default function LoginPage() { 10 | return ( 11 | <> 12 | 13 | Login to Tawk 14 | 15 | 16 | New user? 17 | 18 | 23 | Create an account 24 | 25 | 26 | 27 | {/* Form */} 28 | 29 | 30 | 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/pages/auth/NewPassword.js: -------------------------------------------------------------------------------- 1 | import { Stack, Typography, Link } from "@mui/material"; 2 | import { Link as RouterLink } from "react-router-dom"; 3 | import React from "react"; 4 | import { CaretLeft } from "phosphor-react"; 5 | import NewPasswordForm from "../../sections/auth/NewPasswordForm"; 6 | 7 | const NewPassword = () => { 8 | return ( 9 | <> 10 | 11 | 12 | Reset Password 13 | 14 | 15 | 16 | Please set your new password. 17 | 18 | 19 | 20 | {/* NewPasswordForm */} 21 | 22 | 23 | 24 | 36 | 37 | Return to sign in 38 | 39 | 40 | ); 41 | }; 42 | 43 | export default NewPassword; 44 | -------------------------------------------------------------------------------- /src/pages/auth/Register.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link as RouterLink } from 'react-router-dom'; 3 | // @mui 4 | import { Stack, Typography, Link } from '@mui/material'; 5 | 6 | import RegisterForm from "../../sections/auth/RegisterForm"; 7 | import AuthSocial from '../../sections/auth/AuthSocial'; 8 | 9 | // ---------------------------------------------------------------------- 10 | 11 | export default function Register() { 12 | return ( 13 | <> 14 | 15 | Get started with Tawk. 16 | 17 | 18 | Already have an account? 19 | 20 | 21 | Sign in 22 | 23 | 24 | 25 | {/* Form */} 26 | 27 | 28 | 32 | {'By signing up, I agree to '} 33 | 34 | Terms of Service 35 | 36 | {' and '} 37 | 38 | Privacy Policy 39 | 40 | . 41 | 42 | 43 | 44 | 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /src/pages/auth/ResetPassword.js: -------------------------------------------------------------------------------- 1 | import { Stack, Typography, Link } from "@mui/material"; 2 | import { Link as RouterLink } from "react-router-dom"; 3 | import React from "react"; 4 | import { CaretLeft } from "phosphor-react"; 5 | import AuthResetPasswordForm from "../../sections/auth/ResetPasswordForm"; 6 | 7 | const ResetPassword = () => { 8 | return ( 9 | <> 10 | 11 | 12 | Forgot your password? 13 | 14 | 15 | 16 | Please enter the email address associated with your account and We 17 | will email you a link to reset your password. 18 | 19 | 20 | 21 | {/* Reset Password Form */} 22 | 23 | 24 | 36 | 37 | Return to sign in 38 | 39 | 40 | ); 41 | }; 42 | 43 | export default ResetPassword; 44 | -------------------------------------------------------------------------------- /src/pages/auth/Verify.js: -------------------------------------------------------------------------------- 1 | import { Link as RouterLink } from "react-router-dom"; 2 | // sections 3 | import { Stack, Typography, Link } from "@mui/material"; 4 | import AuthSocial from "../../sections/auth/AuthSocial"; 5 | import Login from "../../sections/auth/LoginForm"; 6 | import VerifyForm from "../../sections/auth/VerifyForm"; 7 | 8 | // ---------------------------------------------------------------------- 9 | 10 | export default function LoginPage() { 11 | return ( 12 | <> 13 | 14 | Please Verify OTP 15 | 16 | 17 | 18 | Sent to email (shreyanshshah242@gmail.com) 19 | 20 | 21 | 22 | {/* Form */} 23 | 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/dashboard/GeneralApp.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTheme } from "@mui/material/styles"; 3 | import { Box, Stack, Typography } from "@mui/material"; 4 | 5 | import { Link, useSearchParams } from "react-router-dom"; 6 | import ChatComponent from "./Conversation"; 7 | import Chats from "./Chats"; 8 | import Contact from "../../sections/dashboard/Contact"; 9 | import NoChat from "../../assets/Illustration/NoChat"; 10 | import { useSelector } from "react-redux"; 11 | import StarredMessages from "../../sections/dashboard/StarredMessages"; 12 | import Media from "../../sections/dashboard/SharedMessages"; 13 | 14 | const GeneralApp = () => { 15 | const [searchParams] = useSearchParams(); 16 | 17 | const theme = useTheme(); 18 | 19 | const { sideBar, room_id, chat_type } = useSelector((state) => state.app); 20 | 21 | return ( 22 | <> 23 | 24 | 25 | 42 | {chat_type === "individual" && 43 | room_id !== null ? ( 44 | 45 | ) : ( 46 | 52 | 53 | 54 | Select a conversation or start a{" "} 55 | 62 | new one 63 | 64 | 65 | 66 | )} 67 | 68 | {sideBar.open && 69 | (() => { 70 | switch (sideBar.type) { 71 | case "CONTACT": 72 | return ; 73 | 74 | case "STARRED": 75 | return ; 76 | 77 | case "SHARED": 78 | return ; 79 | 80 | default: 81 | break; 82 | } 83 | })()} 84 | 85 | 86 | ); 87 | }; 88 | 89 | export default GeneralApp; 90 | -------------------------------------------------------------------------------- /src/pages/dashboard/Settings/Profile.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { Box, IconButton, Stack, Typography } from "@mui/material"; 3 | import { CaretLeft } from "phosphor-react"; 4 | import ProfileForm from "../../../sections/dashboard/Settings/ProfileForm"; 5 | import { useDispatch } from "react-redux"; 6 | import { FetchUserProfile } from "../../../redux/slices/app"; 7 | 8 | const Profile = () => { 9 | const dispatch = useDispatch(); 10 | 11 | useEffect(() => { 12 | dispatch(FetchUserProfile()); 13 | }, []); 14 | 15 | return ( 16 | <> 17 | 18 | {/* Left Pane */} 19 | 26 | theme.palette.mode === "light" 27 | ? "#F8FAFF" 28 | : theme.palette.background, 29 | 30 | boxShadow: "0px 0px 2px rgba(0, 0, 0, 0.25)", 31 | }} 32 | > 33 | 34 | {/* Header */} 35 | 36 | 37 | 38 | 39 | 40 | Profile 41 | 42 | 43 | {/* Profile Edit Form */} 44 | 45 | 46 | 47 | 48 | {/* Right Pane */} 49 | 54 | theme.palette.mode === "light" 55 | ? "#FFF" 56 | : theme.palette.background.paper, 57 | borderBottom: "6px solid #0162C4", 58 | }} 59 | > 60 | 61 | 62 | ); 63 | }; 64 | 65 | export default Profile; 66 | -------------------------------------------------------------------------------- /src/redux/rootReducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import storage from 'redux-persist/lib/storage'; 3 | // slices 4 | import appReducer from './slices/app'; 5 | import audioCallReducer from './slices/audioCall'; 6 | import videoCallReducer from './slices/videoCall'; 7 | import authReducer from './slices/auth'; 8 | import conversationReducer from './slices/conversation'; 9 | 10 | // ---------------------------------------------------------------------- 11 | 12 | const rootPersistConfig = { 13 | key: 'root', 14 | storage, 15 | keyPrefix: 'redux-', 16 | // whitelist: [], 17 | // blacklist: [], 18 | }; 19 | 20 | const rootReducer = combineReducers({ 21 | app: appReducer, 22 | auth: authReducer, 23 | conversation: conversationReducer, 24 | audioCall: audioCallReducer, 25 | videoCall: videoCallReducer, 26 | }); 27 | 28 | export { rootPersistConfig, rootReducer }; 29 | -------------------------------------------------------------------------------- /src/redux/slices/videoCall.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | import { socket } from "../../socket"; 3 | import axios from "../../utils/axios"; 4 | 5 | const initialState = { 6 | open_video_dialog: false, 7 | open_video_notification_dialog: false, 8 | call_queue: [], // can have max 1 call at any point of time 9 | incoming: false, 10 | }; 11 | 12 | const slice = createSlice({ 13 | name: "videoCall", 14 | initialState, 15 | reducers: { 16 | pushToVideoCallQueue(state, action) { 17 | // check video_call_queue in redux store 18 | 19 | if (state.call_queue.length === 0) { 20 | state.call_queue.push(action.payload.call); 21 | if (action.payload.incoming) { 22 | state.open_video_notification_dialog = true; // this will open up the call dialog 23 | state.incoming = true; 24 | } 25 | else { 26 | state.open_video_dialog = true; 27 | state.incoming = false; 28 | } 29 | } else { 30 | // if queue is not empty then emit user_is_busy => in turn server will send this event to sender of call 31 | socket.emit("user_is_busy_video_call", { ...action.payload }); 32 | } 33 | 34 | // Ideally queue should be managed on server side 35 | }, 36 | resetVideoCallQueue(state, action) { 37 | state.call_queue = []; 38 | state.open_video_notification_dialog = false; 39 | state.incoming = false; 40 | }, 41 | closeNotificationDialog(state, action) { 42 | state.open_video_notification_dialog = false; 43 | }, 44 | updateCallDialog(state, action) { 45 | state.open_video_dialog = action.payload.state; 46 | state.open_video_notification_dialog = false; 47 | }, 48 | }, 49 | }); 50 | 51 | // Reducer 52 | export default slice.reducer; 53 | 54 | // ---------------------------------------------------------------------- 55 | 56 | export const StartVideoCall = (id) => { 57 | return async (dispatch, getState) => { 58 | dispatch(slice.actions.resetVideoCallQueue()); 59 | axios 60 | .post( 61 | "/user/start-video-call", 62 | { id }, 63 | { 64 | headers: { 65 | "Content-Type": "application/json", 66 | Authorization: `Bearer ${getState().auth.token}`, 67 | }, 68 | } 69 | ) 70 | .then((response) => { 71 | console.log(response); 72 | dispatch( 73 | slice.actions.pushToVideoCallQueue({ 74 | call: response.data.data, 75 | incoming: false, 76 | }) 77 | ); 78 | }) 79 | .catch((err) => { 80 | console.log(err); 81 | }); 82 | }; 83 | }; 84 | 85 | export const PushToVideoCallQueue = (call) => { 86 | return async (dispatch, getState) => { 87 | dispatch(slice.actions.pushToVideoCallQueue({call, incoming: true})); 88 | }; 89 | }; 90 | 91 | export const ResetVideoCallQueue = () => { 92 | return async (dispatch, getState) => { 93 | dispatch(slice.actions.resetVideoCallQueue()); 94 | }; 95 | }; 96 | 97 | export const CloseVideoNotificationDialog = () => { 98 | return async (dispatch, getState) => { 99 | dispatch(slice.actions.closeNotificationDialog()); 100 | }; 101 | }; 102 | 103 | export const UpdateVideoCallDialog = ({ state }) => { 104 | return async (dispatch, getState) => { 105 | dispatch(slice.actions.updateCallDialog({ state })); 106 | }; 107 | }; 108 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | import { Suspense, lazy } from "react"; 2 | import { Navigate, useRoutes } from "react-router-dom"; 3 | 4 | // layouts 5 | import DashboardLayout from "../layouts/dashboard"; 6 | import AuthLayout from "../layouts/auth"; 7 | 8 | // config 9 | import { DEFAULT_PATH } from "../config"; 10 | import LoadingScreen from "../components/LoadingScreen"; 11 | 12 | const Loadable = (Component) => (props) => { 13 | return ( 14 | }> 15 | 16 | 17 | ); 18 | }; 19 | 20 | export default function Router() { 21 | return useRoutes([ 22 | { 23 | path: "/auth", 24 | element: , 25 | children: [ 26 | { path: "login", element: }, 27 | { path: "register", element: }, 28 | { path: "reset-password", element: }, 29 | { path: "new-password", element: }, 30 | {path: "verify", element: }, 31 | ], 32 | }, 33 | { 34 | path: "/", 35 | element: , 36 | children: [ 37 | { element: , index: true }, 38 | { path: "app", element: }, 39 | { path: "group", element: }, 40 | { path: "settings", element: }, 41 | { path: "conversation", element: }, 42 | { path: "chats", element: }, 43 | { path: "contact", element: }, 44 | { path: "profile", element: }, 45 | 46 | {path: "call", element: }, 47 | 48 | { path: "404", element: }, 49 | { path: "*", element: }, 50 | ], 51 | }, 52 | 53 | { path: "*", element: }, 54 | ]); 55 | } 56 | 57 | const GeneralApp = Loadable( 58 | lazy(() => import("../pages/dashboard/GeneralApp")) 59 | ); 60 | const Conversation = Loadable( 61 | lazy(() => import("../pages/dashboard/Conversation")) 62 | ); 63 | const Chats = Loadable(lazy(() => import("../pages/dashboard/Chats"))); 64 | const Group = Loadable(lazy(() => import("../pages/dashboard/Group"))); 65 | const CallPage = Loadable(lazy(() => import("../pages/dashboard/Call"))); 66 | const Contact = Loadable(lazy(() => import("../sections/dashboard/Contact"))); 67 | const Page404 = Loadable(lazy(() => import("../pages/Page404"))); 68 | 69 | const LoginPage = Loadable(lazy(() => import("../pages/auth/Login"))); 70 | const VerifyPage = Loadable(lazy(() => import("../pages/auth/Verify"))); 71 | const RegisterPage = Loadable(lazy(() => import("../pages/auth/Register"))); 72 | const ResetPasswordPage = Loadable( 73 | lazy(() => import("../pages/auth/ResetPassword")) 74 | ); 75 | const NewPasswordPage = Loadable( 76 | lazy(() => import("../pages/auth/NewPassword")) 77 | ); 78 | 79 | // Settings 80 | const Settings = Loadable(lazy(() => import("../pages/dashboard/Settings"))); 81 | const Profile = Loadable( 82 | lazy(() => import("../pages/dashboard/Settings/Profile")) 83 | ); 84 | -------------------------------------------------------------------------------- /src/routes/paths.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | function path(root, sublink) { 4 | return `${root}${sublink}`; 5 | } 6 | 7 | const ROOTS_DASHBOARD = "/"; 8 | 9 | export const PATH_DASHBOARD = { 10 | root: ROOTS_DASHBOARD, 11 | general: { 12 | app: path(ROOTS_DASHBOARD, "app"), 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/sections/Dashboard/Audio/CallNotification.js: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | import { 3 | Avatar, 4 | Button, 5 | Dialog, 6 | DialogActions, 7 | DialogContent, 8 | Slide, 9 | Stack, 10 | } from "@mui/material"; 11 | import React from "react"; 12 | import { useDispatch, useSelector } from "react-redux"; 13 | import { 14 | ResetAudioCallQueue, 15 | UpdateAudioCallDialog, 16 | } from "../../../redux/slices/audioCall"; 17 | import { socket } from "../../../socket"; 18 | import { AWS_S3_REGION, S3_BUCKET_NAME } from "../../../config"; 19 | 20 | const Transition = React.forwardRef(function Transition(props, ref) { 21 | return ; 22 | }); 23 | 24 | const CallNotification = ({ open, handleClose }) => { 25 | const dispatch = useDispatch(); 26 | 27 | const { user } = useSelector((state) => state.app); 28 | const [call_details] = useSelector((state) => state.audioCall.call_queue); 29 | 30 | const handleAccept = () => { 31 | socket.emit("audio_call_accepted", { ...call_details }); 32 | dispatch(UpdateAudioCallDialog({ state: true })); 33 | }; 34 | 35 | const handleDeny = () => { 36 | // 37 | socket.emit("audio_call_denied", { ...call_details }); 38 | dispatch(ResetAudioCallQueue()); 39 | handleClose(); 40 | }; 41 | 42 | return ( 43 | <> 44 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 64 | 65 | 66 | 67 | 68 | 71 | 74 | 75 | 76 | 77 | ); 78 | }; 79 | 80 | export default CallNotification; 81 | -------------------------------------------------------------------------------- /src/sections/Dashboard/CreateGroup.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import * as Yup from "yup"; 3 | import { 4 | Button, 5 | Dialog, 6 | DialogContent, 7 | DialogTitle, 8 | Slide, 9 | Stack, 10 | } from "@mui/material"; 11 | 12 | import { yupResolver } from "@hookform/resolvers/yup"; 13 | import { useForm } from "react-hook-form"; 14 | import FormProvider from "../../components/hook-form/FormProvider"; 15 | import { RHFTextField } from "../../components/hook-form"; 16 | import RHFAutocomplete from "../../components/hook-form/RHFAutocomplete"; 17 | 18 | const Transition = React.forwardRef(function Transition(props, ref) { 19 | return ; 20 | }); 21 | 22 | const TAGS_OPTION = [ 23 | "Toy Story 3", 24 | "Logan", 25 | "Full Metal Jacket", 26 | "Dangal", 27 | "The Sting", 28 | "2001: A Space Odyssey", 29 | "Singin' in the Rain", 30 | "Toy Story", 31 | "Bicycle Thieves", 32 | "The Kid", 33 | "Inglourious Basterds", 34 | "Snatch", 35 | "3 Idiots", 36 | ]; 37 | 38 | const CreateGroupForm = ({ handleClose }) => { 39 | const NewGroupSchema = Yup.object().shape({ 40 | title: Yup.string().required("Title is required"), 41 | 42 | members: Yup.array().min(2, "Must have at least 2 members"), 43 | }); 44 | 45 | const defaultValues = { 46 | title: "", 47 | 48 | tags: [], 49 | }; 50 | 51 | const methods = useForm({ 52 | resolver: yupResolver(NewGroupSchema), 53 | defaultValues, 54 | }); 55 | 56 | const { 57 | reset, 58 | watch, 59 | setValue, 60 | handleSubmit, 61 | formState: { isSubmitting, isValid }, 62 | } = methods; 63 | 64 | const onSubmit = async (data) => { 65 | try { 66 | // API Call 67 | console.log("DATA", data); 68 | } catch (error) { 69 | console.error(error); 70 | } 71 | }; 72 | 73 | return ( 74 | 75 | 76 | 77 | option)} 83 | ChipProps={{ size: "medium" }} 84 | /> 85 | 91 | 92 | 95 | 96 | 97 | 98 | ); 99 | }; 100 | 101 | const CreateGroup = ({ open, handleClose }) => { 102 | return ( 103 | 113 | {"Create New Group"} 114 | 115 | 116 | {/* Create Group Form */} 117 | 118 | 119 | 120 | ); 121 | }; 122 | 123 | export default CreateGroup; 124 | -------------------------------------------------------------------------------- /src/sections/Dashboard/Friends.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { Dialog, DialogContent, Slide, Stack, Tab, Tabs } from "@mui/material"; 3 | import { useDispatch, useSelector } from "react-redux"; 4 | import { 5 | FetchFriendRequests, 6 | FetchFriends, 7 | FetchUsers, 8 | } from "../../redux/slices/app"; 9 | import { FriendElement, FriendRequestElement, UserElement } from "../../components/UserElement"; 10 | 11 | const Transition = React.forwardRef(function Transition(props, ref) { 12 | return ; 13 | }); 14 | 15 | const UsersList = () => { 16 | const dispatch = useDispatch(); 17 | 18 | const { users } = useSelector((state) => state.app); 19 | 20 | useEffect(() => { 21 | dispatch(FetchUsers()); 22 | }, []); 23 | 24 | return ( 25 | <> 26 | {users.map((el, idx) => { 27 | return ; 28 | })} 29 | 30 | ); 31 | }; 32 | 33 | const FriendsList = () => { 34 | const dispatch = useDispatch(); 35 | 36 | const { friends } = useSelector((state) => state.app); 37 | 38 | useEffect(() => { 39 | dispatch(FetchFriends()); 40 | }, []); 41 | 42 | return ( 43 | <> 44 | {friends.map((el, idx) => { 45 | return ; 46 | })} 47 | 48 | ); 49 | }; 50 | 51 | const RequestsList = () => { 52 | const dispatch = useDispatch(); 53 | 54 | const { friendRequests } = useSelector((state) => state.app); 55 | 56 | useEffect(() => { 57 | dispatch(FetchFriendRequests()); 58 | }, []); 59 | 60 | return ( 61 | <> 62 | {friendRequests.map((el, idx) => { 63 | return ; 64 | })} 65 | 66 | ); 67 | }; 68 | 69 | const Friends = ({ open, handleClose }) => { 70 | const [value, setValue] = React.useState(0); 71 | 72 | const handleChange = (event, newValue) => { 73 | setValue(newValue); 74 | }; 75 | 76 | return ( 77 | 87 | {/* {"Friends"} */} 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | {(() => { 99 | switch (value) { 100 | case 0: // display all users in this list 101 | return ; 102 | 103 | case 1: // display friends in this list 104 | return ; 105 | 106 | case 2: // display request in this list 107 | return ; 108 | 109 | default: 110 | break; 111 | } 112 | })()} 113 | 114 | 115 | 116 | 117 | ); 118 | }; 119 | 120 | export default Friends; 121 | -------------------------------------------------------------------------------- /src/sections/Dashboard/Settings/ProfileForm.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useState } from "react"; 2 | import * as Yup from "yup"; 3 | // form 4 | import { useForm } from "react-hook-form"; 5 | import { yupResolver } from "@hookform/resolvers/yup"; 6 | import FormProvider from "../../../components/hook-form/FormProvider"; 7 | import { RHFTextField, RHFUploadAvatar } from "../../../components/hook-form"; 8 | import { Stack } from "@mui/material"; 9 | import { LoadingButton } from "@mui/lab"; 10 | import { useDispatch, useSelector } from "react-redux"; 11 | import { UpdateUserProfile } from "../../../redux/slices/app"; 12 | import { AWS_S3_REGION, S3_BUCKET_NAME } from "../../../config"; 13 | 14 | const ProfileForm = () => { 15 | const dispatch = useDispatch(); 16 | const [file, setFile] = useState(); 17 | const { user } = useSelector((state) => state.app); 18 | 19 | const ProfileSchema = Yup.object().shape({ 20 | firstName: Yup.string().required("Name is required"), 21 | about: Yup.string().required("About is required"), 22 | avatar: Yup.string().required("Avatar is required").nullable(true), 23 | }); 24 | 25 | const defaultValues = { 26 | firstName: user?.firstName, 27 | about: user?.about, 28 | avatar: `https://${S3_BUCKET_NAME}.s3.${AWS_S3_REGION}.amazonaws.com/${user?.avatar}`, 29 | }; 30 | 31 | const methods = useForm({ 32 | resolver: yupResolver(ProfileSchema), 33 | defaultValues, 34 | }); 35 | 36 | const { 37 | reset, 38 | watch, 39 | control, 40 | setValue, 41 | handleSubmit, 42 | formState: { isSubmitting, isSubmitSuccessful }, 43 | } = methods; 44 | 45 | const values = watch(); 46 | 47 | const onSubmit = async (data) => { 48 | try { 49 | // Send API request 50 | console.log("DATA", data); 51 | dispatch( 52 | UpdateUserProfile({ 53 | firstName: data?.firstName, 54 | about: data?.about, 55 | avatar: file, 56 | }) 57 | ); 58 | } catch (error) { 59 | console.error(error); 60 | } 61 | }; 62 | 63 | const handleDrop = useCallback( 64 | (acceptedFiles) => { 65 | const file = acceptedFiles[0]; 66 | 67 | setFile(file); 68 | 69 | const newFile = Object.assign(file, { 70 | preview: URL.createObjectURL(file), 71 | }); 72 | 73 | if (file) { 74 | setValue("avatar", newFile, { shouldValidate: true }); 75 | } 76 | }, 77 | [setValue] 78 | ); 79 | 80 | return ( 81 | 82 | 83 | 84 | 85 | 90 | 91 | 92 | 93 | 100 | Save 101 | 102 | 103 | 104 | 105 | ); 106 | }; 107 | 108 | export default ProfileForm; 109 | -------------------------------------------------------------------------------- /src/sections/Dashboard/Settings/ThemeDialog.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | Dialog, 4 | Slide, 5 | Button, 6 | DialogActions, 7 | DialogContent, 8 | DialogTitle, 9 | Radio, 10 | RadioGroup, 11 | FormControl, 12 | FormControlLabel, 13 | } from "@mui/material"; 14 | 15 | const Transition = React.forwardRef(function Transition(props, ref) { 16 | return ; 17 | }); 18 | 19 | const ThemeDialog = ({ open, handleClose }) => { 20 | return ( 21 | <> 22 | 30 | {"Choose Theme"} 31 | 32 | 33 | 38 | } 41 | label="Light" 42 | /> 43 | } label="Dark" /> 44 | } 47 | label="System Default" 48 | /> 49 | 50 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | 60 | ); 61 | }; 62 | 63 | export default ThemeDialog; 64 | -------------------------------------------------------------------------------- /src/sections/Dashboard/StarredMessages.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTheme } from "@mui/material/styles"; 3 | import { Box, IconButton, Stack, Typography } from "@mui/material"; 4 | import { ArrowLeft } from "phosphor-react"; 5 | import useResponsive from "../../hooks/useResponsive"; 6 | import { useDispatch } from "react-redux"; 7 | import { UpdateSidebarType } from "../../redux/slices/app"; 8 | import { Conversation } from "../../pages/dashboard/Conversation"; 9 | 10 | const StarredMessages = () => { 11 | const dispatch = useDispatch(); 12 | 13 | const theme = useTheme(); 14 | 15 | const isDesktop = useResponsive("up", "md"); 16 | 17 | return ( 18 | 19 | 20 | 30 | 36 | { 38 | dispatch(UpdateSidebarType("CONTACT")); 39 | }} 40 | > 41 | 42 | 43 | Starred Messages 44 | 45 | 46 | 55 | 56 | 57 | 58 | 59 | ); 60 | }; 61 | 62 | export default StarredMessages; 63 | -------------------------------------------------------------------------------- /src/sections/Dashboard/StartCall.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { 3 | Dialog, 4 | DialogContent, 5 | DialogTitle, 6 | Slide, 7 | Stack, 8 | } from "@mui/material"; 9 | import { 10 | Search, 11 | SearchIconWrapper, 12 | StyledInputBase, 13 | } from "../../components/Search"; 14 | import { MagnifyingGlass } from "phosphor-react"; 15 | import { CallElement } from "../../components/CallElement"; 16 | import { CallList } from "../../data"; 17 | import { useDispatch, useSelector } from "react-redux"; 18 | import { FetchAllUsers } from "../../redux/slices/app"; 19 | import {faker} from "@faker-js/faker"; 20 | 21 | const Transition = React.forwardRef(function Transition(props, ref) { 22 | return ; 23 | }); 24 | 25 | const StartCall = ({ open, handleClose }) => { 26 | const {all_users} = useSelector((state) => state.app); 27 | const dispatch = useDispatch(); 28 | useEffect(() => { 29 | dispatch(FetchAllUsers()); 30 | }, []); 31 | 32 | console.log(CallList, all_users, "Call List Info"); 33 | 34 | const list = all_users.map((el) => ({ 35 | id: el?._id, 36 | name: `${el?.firstName} ${el?.lastName}`, 37 | image: faker.image.avatar(), 38 | })); 39 | 40 | return ( 41 | 51 | {"Start New Conversation"} 52 | 53 | {/* 54 | 55 | 56 | 57 | 61 | */} 62 | 63 | 64 | 65 | 66 | {list.map((el, idx) => { 67 | return ; 68 | })} 69 | 70 | 71 | 72 | 73 | ); 74 | }; 75 | 76 | export default StartCall; 77 | -------------------------------------------------------------------------------- /src/sections/Dashboard/video/CallNotification.js: -------------------------------------------------------------------------------- 1 | import { faker } from "@faker-js/faker"; 2 | import { 3 | Avatar, 4 | Button, 5 | Dialog, 6 | DialogActions, 7 | DialogContent, 8 | Slide, 9 | Stack, 10 | } from "@mui/material"; 11 | import React from "react"; 12 | import { useDispatch, useSelector } from "react-redux"; 13 | import { 14 | ResetVideoCallQueue, 15 | UpdateVideoCallDialog, 16 | } from "../../../redux/slices/videoCall"; 17 | import { socket } from "../../../socket"; 18 | import { AWS_S3_REGION, S3_BUCKET_NAME } from "../../../config"; 19 | 20 | const Transition = React.forwardRef(function Transition(props, ref) { 21 | return ; 22 | }); 23 | 24 | const CallNotification = ({ open, handleClose }) => { 25 | const dispatch = useDispatch(); 26 | const { user } = useSelector((state) => state.app); 27 | const [call_details] = useSelector((state) => state.videoCall.call_queue); 28 | 29 | const handleAccept = () => { 30 | socket.emit("video_call_accepted", { ...call_details }); 31 | dispatch(UpdateVideoCallDialog({ state: true })); 32 | }; 33 | 34 | const handleDeny = () => { 35 | // 36 | socket.emit("video_call_denied", { ...call_details }); 37 | dispatch(ResetVideoCallQueue()); 38 | handleClose(); 39 | }; 40 | 41 | return ( 42 | <> 43 | 50 | 51 | 52 | 53 | 57 | 58 | 59 | 63 | 64 | 65 | 66 | 67 | 70 | 73 | 74 | 75 | 76 | ); 77 | }; 78 | 79 | export default CallNotification; 80 | -------------------------------------------------------------------------------- /src/sections/auth/AuthSocial.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { Divider, IconButton, Stack } from '@mui/material'; 3 | import { GithubLogo, GoogleLogo, TwitterLogo } from 'phosphor-react'; 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | export default function AuthSocial() { 8 | 9 | 10 | const handleGoogleLogin = async () => { 11 | 12 | }; 13 | 14 | const handleGithubLogin = async () => { 15 | 16 | }; 17 | 18 | const handleTwitterLogin = async () => { 19 | 20 | }; 21 | 22 | return ( 23 |
24 | 34 | OR 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /src/sections/auth/ResetPasswordForm.js: -------------------------------------------------------------------------------- 1 | import * as Yup from "yup"; 2 | // form 3 | import { yupResolver } from "@hookform/resolvers/yup"; 4 | import { useForm } from "react-hook-form"; 5 | // components 6 | import FormProvider, { RHFTextField } from "../../components/hook-form"; 7 | import { Button } from "@mui/material"; 8 | import { useDispatch, useSelector } from "react-redux"; 9 | import { ForgotPassword } from "../../redux/slices/auth"; 10 | import { LoadingButton } from "@mui/lab"; 11 | 12 | // ---------------------------------------------------------------------- 13 | 14 | export default function AuthResetPasswordForm() { 15 | const { isLoading } = useSelector((state) => state.auth); 16 | const dispatch = useDispatch(); 17 | const ResetPasswordSchema = Yup.object().shape({ 18 | email: Yup.string() 19 | .required("Email is required") 20 | .email("Email must be a valid email address"), 21 | }); 22 | 23 | const methods = useForm({ 24 | resolver: yupResolver(ResetPasswordSchema), 25 | defaultValues: { email: "demo@tawk.com" }, 26 | }); 27 | 28 | const { handleSubmit } = methods; 29 | 30 | const onSubmit = async (data) => { 31 | try { 32 | // Send API Request 33 | dispatch(ForgotPassword(data)); 34 | } catch (error) { 35 | console.error(error); 36 | } 37 | }; 38 | 39 | return ( 40 | 41 | 42 | 43 | 53 | theme.palette.mode === "light" ? "common.white" : "grey.800", 54 | "&:hover": { 55 | bgcolor: "text.primary", 56 | color: (theme) => 57 | theme.palette.mode === "light" ? "common.white" : "grey.800", 58 | }, 59 | }} 60 | > 61 | Send Request 62 | 63 | 64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /src/sections/auth/VerifyForm.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import * as Yup from "yup"; 3 | // form 4 | import { useForm } from "react-hook-form"; 5 | import { yupResolver } from "@hookform/resolvers/yup"; 6 | // @mui 7 | import { Stack, IconButton, InputAdornment, Button } from "@mui/material"; 8 | // components 9 | import FormProvider, { RHFTextField } from "../../components/hook-form"; 10 | import { Eye, EyeSlash } from "phosphor-react"; 11 | import RHFCodes from "../../components/hook-form/RHFCodes"; 12 | import { useDispatch, useSelector } from "react-redux"; 13 | import { VerifyEmail } from "../../redux/slices/auth"; 14 | 15 | // ---------------------------------------------------------------------- 16 | 17 | export default function VerifyForm() { 18 | const dispatch = useDispatch(); 19 | const { email } = useSelector((state) => state.auth); 20 | const VerifyCodeSchema = Yup.object().shape({ 21 | code1: Yup.string().required("Code is required"), 22 | code2: Yup.string().required("Code is required"), 23 | code3: Yup.string().required("Code is required"), 24 | code4: Yup.string().required("Code is required"), 25 | code5: Yup.string().required("Code is required"), 26 | code6: Yup.string().required("Code is required"), 27 | }); 28 | 29 | const defaultValues = { 30 | code1: "", 31 | code2: "", 32 | code3: "", 33 | code4: "", 34 | code5: "", 35 | code6: "", 36 | }; 37 | 38 | const methods = useForm({ 39 | mode: "onChange", 40 | resolver: yupResolver(VerifyCodeSchema), 41 | defaultValues, 42 | }); 43 | 44 | const { 45 | handleSubmit, 46 | formState: { isSubmitting, errors }, 47 | } = methods; 48 | 49 | const onSubmit = async (data) => { 50 | try { 51 | // Send API Request 52 | dispatch( 53 | VerifyEmail({ 54 | email, 55 | otp: `${data.code1}${data.code2}${data.code3}${data.code4}${data.code5}${data.code6}`, 56 | }) 57 | ); 58 | } catch (error) { 59 | console.error(error); 60 | } 61 | }; 62 | 63 | return ( 64 | 65 | 66 | 70 | 71 | 90 | 91 | 92 | ); 93 | } 94 | -------------------------------------------------------------------------------- /src/socket.js: -------------------------------------------------------------------------------- 1 | import io from "socket.io-client"; // Add this 2 | 3 | let socket; 4 | 5 | const connectSocket = (user_id) => { 6 | socket = io("https://api.chat.codingmonk.in/", { 7 | query: `user_id=${user_id}`, 8 | }); 9 | } // Add this -- our server will run on port 4000, so we connect to it from here 10 | 11 | export {socket, connectSocket}; 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 { 6 | createTheme, 7 | ThemeProvider as MUIThemeProvider, 8 | StyledEngineProvider, 9 | } from "@mui/material/styles"; 10 | // hooks 11 | import useSettings from "../hooks/useSettings.js"; 12 | // 13 | import palette from "./palette"; 14 | import typography from "./typography"; 15 | import breakpoints from "./breakpoints"; 16 | import componentsOverride from "./overrides"; 17 | import shadows, { customShadows } from "./shadows"; 18 | 19 | // ---------------------------------------------------------------------- 20 | 21 | ThemeProvider.propTypes = { 22 | children: PropTypes.node, 23 | }; 24 | 25 | export default function ThemeProvider({ children }) { 26 | const { themeMode, themeDirection } = useSettings(); 27 | 28 | const isLight = themeMode === "light"; 29 | 30 | const themeOptions = useMemo( 31 | () => ({ 32 | palette: isLight ? palette.light : palette.dark, 33 | typography, 34 | breakpoints, 35 | shape: { borderRadius: 8 }, 36 | direction: themeDirection, 37 | shadows: isLight ? shadows.light : shadows.dark, 38 | customShadows: isLight ? customShadows.light : customShadows.dark, 39 | }), 40 | [isLight, themeDirection] 41 | ); 42 | 43 | const theme = createTheme(themeOptions); 44 | 45 | theme.components = componentsOverride(theme); 46 | 47 | return ( 48 | 49 | 50 | 51 | {children} 52 | 53 | 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/theme/overrides/Alert.js: -------------------------------------------------------------------------------- 1 | import { ErrorIcon, InfoIcon, SuccessIcon, WarningIcon } from './CustomIcons'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | export default function Alert(theme) { 6 | const isLight = theme.palette.mode === 'light'; 7 | 8 | const standardStyle = (color) => ({ 9 | color: theme.palette[color][isLight ? 'darker' : 'lighter'], 10 | backgroundColor: theme.palette[color][isLight ? 'lighter' : 'darker'], 11 | '& .MuiAlert-icon': { 12 | color: theme.palette[color][isLight ? 'main' : 'light'], 13 | }, 14 | }); 15 | 16 | const filledStyle = (color) => ({ 17 | color: theme.palette[color].contrastText, 18 | }); 19 | 20 | const outlinedStyle = (color) => ({ 21 | color: theme.palette[color][isLight ? 'darker' : 'lighter'], 22 | border: `solid 1px ${theme.palette[color][isLight ? 'light' : 'dark']}`, 23 | backgroundColor: theme.palette[color][isLight ? 'lighter' : 'darker'], 24 | '& .MuiAlert-icon': { 25 | color: theme.palette[color][isLight ? 'main' : 'light'], 26 | }, 27 | }); 28 | 29 | return { 30 | MuiAlert: { 31 | defaultProps: { 32 | iconMapping: { 33 | info: , 34 | success: , 35 | warning: , 36 | error: , 37 | }, 38 | }, 39 | 40 | styleOverrides: { 41 | message: { 42 | '& .MuiAlertTitle-root': { 43 | marginBottom: theme.spacing(0.5), 44 | }, 45 | }, 46 | action: { 47 | '& button:not(:first-of-type)': { 48 | marginLeft: theme.spacing(1), 49 | }, 50 | }, 51 | 52 | standardInfo: standardStyle('info'), 53 | standardSuccess: standardStyle('success'), 54 | standardWarning: standardStyle('warning'), 55 | standardError: standardStyle('error'), 56 | 57 | filledInfo: filledStyle('info'), 58 | filledSuccess: filledStyle('success'), 59 | filledWarning: filledStyle('warning'), 60 | filledError: filledStyle('error'), 61 | 62 | outlinedInfo: outlinedStyle('info'), 63 | outlinedSuccess: outlinedStyle('success'), 64 | outlinedWarning: outlinedStyle('warning'), 65 | outlinedError: outlinedStyle('error'), 66 | }, 67 | }, 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/theme/overrides/DataGrid.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function DataGrid(theme) { 4 | return { 5 | MuiDataGrid: { 6 | styleOverrides: { 7 | root: { 8 | borderRadius: 0, 9 | border: `1px solid transparent`, 10 | '& .MuiTablePagination-root': { 11 | borderTop: 0, 12 | }, 13 | }, 14 | cell: { 15 | borderBottom: `1px solid ${theme.palette.divider}`, 16 | }, 17 | columnSeparator: { 18 | color: theme.palette.divider, 19 | }, 20 | toolbarContainer: { 21 | padding: theme.spacing(2), 22 | backgroundColor: theme.palette.background.neutral, 23 | '& .MuiButton-root': { 24 | marginRight: theme.spacing(1.5), 25 | color: theme.palette.text.primary, 26 | '&:hover': { 27 | backgroundColor: theme.palette.action.hover, 28 | }, 29 | }, 30 | }, 31 | paper: { 32 | boxShadow: theme.customShadows.dropdown, 33 | }, 34 | menu: { 35 | '& .MuiPaper-root': { 36 | boxShadow: theme.customShadows.dropdown, 37 | }, 38 | '& .MuiMenuItem-root': { 39 | ...theme.typography.body2, 40 | '& .MuiListItemIcon-root': { 41 | minWidth: 'auto', 42 | }, 43 | }, 44 | }, 45 | panelFooter: { 46 | padding: theme.spacing(2), 47 | justifyContent: 'flex-end', 48 | borderTop: `1px solid ${theme.palette.divider}`, 49 | '& .MuiButton-root': { 50 | '&:first-of-type': { 51 | marginRight: theme.spacing(1.5), 52 | color: theme.palette.text.primary, 53 | '&:hover': { 54 | backgroundColor: theme.palette.action.hover, 55 | }, 56 | }, 57 | '&:last-of-type': { 58 | color: theme.palette.common.white, 59 | backgroundColor: theme.palette.primary.main, 60 | '&:hover': { 61 | backgroundColor: theme.palette.primary.dark, 62 | }, 63 | }, 64 | }, 65 | }, 66 | filterForm: { 67 | padding: theme.spacing(1.5, 0), 68 | '& .MuiFormControl-root': { 69 | margin: theme.spacing(0, 0.5), 70 | }, 71 | '& .MuiInput-root': { 72 | marginTop: theme.spacing(3), 73 | '&::before, &::after': { 74 | display: 'none', 75 | }, 76 | '& .MuiNativeSelect-select, .MuiInput-input': { 77 | ...theme.typography.body2, 78 | padding: theme.spacing(0.75, 1), 79 | borderRadius: theme.shape.borderRadius, 80 | backgroundColor: theme.palette.background.neutral, 81 | }, 82 | '& .MuiSvgIcon-root': { 83 | right: 4, 84 | }, 85 | }, 86 | }, 87 | }, 88 | }, 89 | }; 90 | } 91 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/theme/overrides/Input.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Input(theme) { 4 | return { 5 | MuiInputBase: { 6 | styleOverrides: { 7 | root: { 8 | '&.Mui-disabled': { 9 | '& svg': { color: theme.palette.text.disabled }, 10 | }, 11 | }, 12 | input: { 13 | '&::placeholder': { 14 | opacity: 1, 15 | color: theme.palette.text.disabled, 16 | }, 17 | }, 18 | }, 19 | }, 20 | MuiInput: { 21 | styleOverrides: { 22 | underline: { 23 | '&:before': { 24 | borderBottomColor: theme.palette.grey[500_56], 25 | }, 26 | }, 27 | }, 28 | }, 29 | MuiFilledInput: { 30 | styleOverrides: { 31 | root: { 32 | backgroundColor: theme.palette.grey[500_12], 33 | '&:hover': { 34 | backgroundColor: theme.palette.grey[500_16], 35 | }, 36 | '&.Mui-focused': { 37 | backgroundColor: theme.palette.action.focus, 38 | }, 39 | '&.Mui-disabled': { 40 | backgroundColor: theme.palette.action.disabledBackground, 41 | }, 42 | }, 43 | underline: { 44 | '&:before': { 45 | borderBottomColor: theme.palette.grey[500_56], 46 | }, 47 | }, 48 | }, 49 | }, 50 | MuiOutlinedInput: { 51 | styleOverrides: { 52 | root: { 53 | '& .MuiOutlinedInput-notchedOutline': { 54 | borderColor: theme.palette.grey[500_32], 55 | }, 56 | '&.Mui-disabled': { 57 | '& .MuiOutlinedInput-notchedOutline': { 58 | borderColor: theme.palette.action.disabledBackground, 59 | }, 60 | }, 61 | }, 62 | }, 63 | }, 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/theme/overrides/Menu.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Menu(theme) { 4 | return { 5 | MuiMenuItem: { 6 | styleOverrides: { 7 | root: { 8 | fontSize: 14, 9 | fontWeight: 600, 10 | '&.Mui-selected': { 11 | backgroundColor: theme.palette.action.selected, 12 | '&:hover': { 13 | backgroundColor: theme.palette.action.hover, 14 | }, 15 | }, 16 | }, 17 | }, 18 | }, 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/theme/overrides/Table.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------- 2 | 3 | export default function Table(theme) { 4 | return { 5 | MuiTableRow: { 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 | MuiTableCell: { 18 | styleOverrides: { 19 | root: { 20 | borderBottom: 'none', 21 | }, 22 | head: { 23 | color: theme.palette.text.secondary, 24 | backgroundColor: theme.palette.background.neutral, 25 | '&:first-of-type': { 26 | paddingLeft: theme.spacing(3), 27 | borderTopLeftRadius: theme.shape.borderRadius, 28 | borderBottomLeftRadius: theme.shape.borderRadius, 29 | boxShadow: `inset 8px 0 0 ${theme.palette.background.paper}`, 30 | }, 31 | '&:last-of-type': { 32 | paddingRight: theme.spacing(3), 33 | borderTopRightRadius: theme.shape.borderRadius, 34 | borderBottomRightRadius: theme.shape.borderRadius, 35 | boxShadow: `inset -8px 0 0 ${theme.palette.background.paper}`, 36 | }, 37 | }, 38 | stickyHeader: { 39 | backgroundColor: theme.palette.background.paper, 40 | backgroundImage: `linear-gradient(to bottom, ${theme.palette.background.neutral} 0%, ${theme.palette.background.neutral} 100%)`, 41 | }, 42 | body: { 43 | '&:first-of-type': { 44 | paddingLeft: theme.spacing(3), 45 | }, 46 | '&:last-of-type': { 47 | paddingRight: theme.spacing(3), 48 | }, 49 | }, 50 | }, 51 | }, 52 | MuiTablePagination: { 53 | styleOverrides: { 54 | root: { 55 | borderTop: `solid 1px ${theme.palette.divider}`, 56 | }, 57 | toolbar: { 58 | height: 64, 59 | }, 60 | select: { 61 | '&:focus': { 62 | borderRadius: theme.shape.borderRadius, 63 | }, 64 | }, 65 | selectIcon: { 66 | width: 20, 67 | height: 20, 68 | marginTop: -4, 69 | }, 70 | }, 71 | }, 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | article: { 14 | fontWeight: 700 15 | } 16 | }, 17 | }, 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/theme/overrides/index.js: -------------------------------------------------------------------------------- 1 | import Fab from './Fab'; 2 | import Card from './Card'; 3 | import Chip from './Chip'; 4 | import Tabs from './Tabs'; 5 | import Menu from './Menu'; 6 | import Link from './Link'; 7 | import Lists from './List'; 8 | import Table from './Table'; 9 | import Alert from './Alert'; 10 | import Badge from './Badge'; 11 | import Paper from './Paper'; 12 | import Input from './Input'; 13 | import Radio from './Radio'; 14 | import Drawer from './Drawer'; 15 | import Dialog from './Dialog'; 16 | import Avatar from './Avatar'; 17 | import Rating from './Rating'; 18 | import Slider from './Slider'; 19 | import Button from './Button'; 20 | import Switch from './Switch'; 21 | import Select from './Select'; 22 | import SvgIcon from './SvgIcon'; 23 | import Tooltip from './Tooltip'; 24 | import Popover from './Popover'; 25 | import Stepper from './Stepper'; 26 | import DataGrid from './DataGrid'; 27 | import Skeleton from './Skeleton'; 28 | import Backdrop from './Backdrop'; 29 | import Progress from './Progress'; 30 | import Timeline from './Timeline'; 31 | import TreeView from './TreeView'; 32 | import Checkbox from './Checkbox'; 33 | import Accordion from './Accordion'; 34 | import Typography from './Typography'; 35 | import Pagination from './Pagination'; 36 | import Breadcrumbs from './Breadcrumbs'; 37 | import ButtonGroup from './ButtonGroup'; 38 | import CssBaseline from './CssBaseline'; 39 | import Autocomplete from './Autocomplete'; 40 | import ToggleButton from './ToggleButton'; 41 | import ControlLabel from './ControlLabel'; 42 | import LoadingButton from './LoadingButton'; 43 | 44 | // ---------------------------------------------------------------------- 45 | 46 | export default function ComponentsOverrides(theme) { 47 | return Object.assign( 48 | Fab(theme), 49 | Tabs(theme), 50 | Chip(theme), 51 | Card(theme), 52 | Menu(theme), 53 | Link(theme), 54 | Input(theme), 55 | Radio(theme), 56 | Badge(theme), 57 | Lists(theme), 58 | Table(theme), 59 | Paper(theme), 60 | Alert(theme), 61 | Switch(theme), 62 | Select(theme), 63 | Button(theme), 64 | Rating(theme), 65 | Dialog(theme), 66 | Avatar(theme), 67 | Slider(theme), 68 | Drawer(theme), 69 | Stepper(theme), 70 | Tooltip(theme), 71 | Popover(theme), 72 | SvgIcon(theme), 73 | Checkbox(theme), 74 | DataGrid(theme), 75 | Skeleton(theme), 76 | Timeline(theme), 77 | TreeView(theme), 78 | Backdrop(theme), 79 | Progress(theme), 80 | Accordion(theme), 81 | Typography(theme), 82 | Pagination(theme), 83 | ButtonGroup(theme), 84 | Breadcrumbs(theme), 85 | CssBaseline(theme), 86 | Autocomplete(theme), 87 | ControlLabel(theme), 88 | ToggleButton(theme), 89 | LoadingButton(theme) 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /src/theme/typography.js: -------------------------------------------------------------------------------- 1 | import { pxToRem, responsiveFontSizes } from '../utils/getFontValue'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | const FONT_PRIMARY = 'Manrope, Public Sans, sans-serif'; // Google Font 6 | // const FONT_SECONDARY = 'CircularStd, sans-serif'; // Local Font 7 | 8 | const typography = { 9 | fontFamily: FONT_PRIMARY, 10 | fontWeightRegular: 400, 11 | fontWeightMedium: 600, 12 | fontWeightBold: 700, 13 | h1: { 14 | fontWeight: 700, 15 | lineHeight: 80 / 64, 16 | fontSize: pxToRem(40), 17 | letterSpacing: 2, 18 | ...responsiveFontSizes({ sm: 52, md: 58, lg: 64 }), 19 | }, 20 | h2: { 21 | fontWeight: 700, 22 | lineHeight: 64 / 48, 23 | fontSize: pxToRem(32), 24 | ...responsiveFontSizes({ sm: 40, md: 44, lg: 48 }), 25 | }, 26 | h3: { 27 | fontWeight: 700, 28 | lineHeight: 1.5, 29 | fontSize: pxToRem(24), 30 | ...responsiveFontSizes({ sm: 26, md: 30, lg: 32 }), 31 | }, 32 | h4: { 33 | fontWeight: 700, 34 | lineHeight: 1.5, 35 | fontSize: pxToRem(20), 36 | ...responsiveFontSizes({ sm: 20, md: 24, lg: 24 }), 37 | }, 38 | h5: { 39 | fontWeight: 700, 40 | lineHeight: 1.5, 41 | fontSize: pxToRem(18), 42 | ...responsiveFontSizes({ sm: 19, md: 20, lg: 20 }), 43 | }, 44 | h6: { 45 | fontWeight: 700, 46 | lineHeight: 28 / 18, 47 | fontSize: pxToRem(17), 48 | ...responsiveFontSizes({ sm: 18, md: 18, lg: 18 }), 49 | }, 50 | subtitle1: { 51 | fontWeight: 600, 52 | lineHeight: 1.5, 53 | fontSize: pxToRem(16), 54 | }, 55 | subtitle2: { 56 | fontWeight: 700, 57 | lineHeight: 22 / 14, 58 | fontSize: pxToRem(14), 59 | }, 60 | body1: { 61 | lineHeight: 1.5, 62 | fontSize: pxToRem(16), 63 | }, 64 | body2: { 65 | lineHeight: 22 / 14, 66 | fontSize: pxToRem(14), 67 | fontWeight: 600, 68 | }, 69 | caption: { 70 | lineHeight: 1.5, 71 | fontSize: pxToRem(12), 72 | fontWeight: 600, 73 | }, 74 | overline: { 75 | fontWeight: 700, 76 | lineHeight: 1.5, 77 | fontSize: pxToRem(12), 78 | textTransform: 'uppercase', 79 | }, 80 | button: { 81 | fontWeight: 700, 82 | lineHeight: 24 / 14, 83 | fontSize: pxToRem(14), 84 | textTransform: 'capitalize', 85 | }, 86 | article: { 87 | fontWeight: 700, 88 | } 89 | }; 90 | 91 | export default typography; 92 | -------------------------------------------------------------------------------- /src/utils/axios.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | // config 3 | import { BASE_URL } from '../config'; 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | const axiosInstance = axios.create({ baseURL: BASE_URL }); 8 | 9 | axiosInstance.interceptors.response.use( 10 | (response) => response, 11 | (error) => Promise.reject((error.response && error.response.data) || 'Something went wrong') 12 | ); 13 | 14 | export default axiosInstance; 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/utils/cssStyles.js: -------------------------------------------------------------------------------- 1 | import { alpha } from '@mui/material/styles'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | function getDirection(value = 'bottom') { 6 | return { 7 | top: 'to top', 8 | right: 'to right', 9 | bottom: 'to bottom', 10 | left: 'to left', 11 | }[value]; 12 | } 13 | 14 | // ---------------------------------------------------------------------- 15 | 16 | export default function cssStyles(theme) { 17 | return { 18 | bgBlur: (props) => { 19 | const color = props?.color || theme?.palette.background.default || '#000000'; 20 | const blur = props?.blur || 6; 21 | const opacity = props?.opacity || 0.8; 22 | 23 | return { 24 | backdropFilter: `blur(${blur}px)`, 25 | WebkitBackdropFilter: `blur(${blur}px)`, // Fix on Mobile 26 | backgroundColor: alpha(color, opacity), 27 | }; 28 | }, 29 | bgGradient: (props) => { 30 | const direction = getDirection(props?.direction); 31 | const startColor = props?.startColor || `${alpha('#000000', 0)} 0%`; 32 | const endColor = props?.endColor || '#000000 75%'; 33 | 34 | return { 35 | background: `linear-gradient(${direction}, ${startColor}, ${endColor});`, 36 | }; 37 | }, 38 | bgImage: (props) => { 39 | const url = props?.url || '/assets/bg_gradient.jpg'; 40 | const direction = getDirection(props?.direction); 41 | const startColor = props?.startColor || alpha(theme?.palette.grey[900] || '#000000', 0.88); 42 | const endColor = props?.endColor || alpha(theme?.palette.grey[900] || '#000000', 0.88); 43 | 44 | return { 45 | background: `linear-gradient(${direction}, ${startColor}, ${endColor}), url(${url})`, 46 | backgroundSize: 'cover', 47 | backgroundRepeat: 'no-repeat', 48 | backgroundPosition: 'center center', 49 | }; 50 | }, 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/utils/getColorPresets.js: -------------------------------------------------------------------------------- 1 | // theme 2 | import palette from "../theme/palette"; 3 | 4 | export const colorPresets = [ 5 | // DEFAULT 6 | { 7 | name: "default", 8 | ...palette.light.primary, 9 | }, 10 | // PURPLE 11 | { 12 | name: "purple", 13 | lighter: "#EBD6FD", 14 | light: "#B985F4", 15 | main: "#7635dc", 16 | dark: "#431A9E", 17 | darker: "#200A69", 18 | contrastText: "#fff", 19 | }, 20 | // CYAN 21 | { 22 | name: "cyan", 23 | lighter: "#D1FFFC", 24 | light: "#76F2FF", 25 | main: "#1CCAFF", 26 | dark: "#0E77B7", 27 | darker: "#053D7A", 28 | contrastText: palette.light.grey[800], 29 | }, 30 | // BLUE 31 | { 32 | name: "blue", 33 | lighter: "#D1E9FC", 34 | light: "#76B0F1", 35 | main: "#2065D1", 36 | dark: "#103996", 37 | darker: "#061B64", 38 | 39 | contrastText: "#fff", 40 | }, 41 | // ORANGE 42 | { 43 | name: "orange", 44 | lighter: "#FEF4D4", 45 | light: "#FED680", 46 | main: "#fda92d", 47 | dark: "#B66816", 48 | darker: "#793908", 49 | contrastText: palette.light.grey[800], 50 | }, 51 | // RED 52 | { 53 | name: "red", 54 | lighter: "#FFE3D5", 55 | light: "#FFC1AC", 56 | main: "#FF3030", 57 | dark: "#B71833", 58 | darker: "#7A0930", 59 | contrastText: "#fff", 60 | }, 61 | ]; 62 | 63 | export const defaultPreset = colorPresets[0]; 64 | export const purplePreset = colorPresets[1]; 65 | export const cyanPreset = colorPresets[2]; 66 | export const bluePreset = colorPresets[3]; 67 | export const orangePreset = colorPresets[4]; 68 | export const redPreset = colorPresets[5]; 69 | 70 | export default function getColorPresets(presetsKey) { 71 | return { 72 | purple: purplePreset, 73 | cyan: cyanPreset, 74 | blue: bluePreset, 75 | orange: orangePreset, 76 | red: redPreset, 77 | default: defaultPreset, 78 | }[presetsKey]; 79 | } 80 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/utils/getFileFormat.js: -------------------------------------------------------------------------------- 1 | // components 2 | import Image from '../components/Image'; 3 | import Iconify from '../components/Iconify'; 4 | 5 | // ---------------------------------------------------------------------- 6 | 7 | const FORMAT_IMG = ['jpg', 'jpeg', 'gif', 'bmp', 'png']; 8 | const FORMAT_VIDEO = ['m4v', 'avi', 'mpg', 'mp4', 'webm']; 9 | const FORMAT_WORD = ['doc', 'docx']; 10 | const FORMAT_EXCEL = ['xls', 'xlsx']; 11 | const FORMAT_POWERPOINT = ['ppt', 'pptx']; 12 | const FORMAT_PDF = ['pdf']; 13 | const FORMAT_PHOTOSHOP = ['psd']; 14 | const FORMAT_ILLUSTRATOR = ['ai', 'esp']; 15 | 16 | export function getFileType(fileUrl = '') { 17 | return (fileUrl && fileUrl.split('.').pop()) || ''; 18 | } 19 | 20 | export function getFileName(fileUrl) { 21 | return fileUrl.substring(fileUrl.lastIndexOf('/') + 1).replace(/\.[^/.]+$/, ''); 22 | } 23 | 24 | export function getFileFullName(fileUrl) { 25 | return fileUrl.split('/').pop(); 26 | } 27 | 28 | export function getFileFormat(fileUrl) { 29 | let format; 30 | 31 | switch (fileUrl.includes(getFileType(fileUrl))) { 32 | case FORMAT_IMG.includes(getFileType(fileUrl)): 33 | format = 'image'; 34 | break; 35 | case FORMAT_VIDEO.includes(getFileType(fileUrl)): 36 | format = 'video'; 37 | break; 38 | case FORMAT_WORD.includes(getFileType(fileUrl)): 39 | format = 'word'; 40 | break; 41 | case FORMAT_EXCEL.includes(getFileType(fileUrl)): 42 | format = 'excel'; 43 | break; 44 | case FORMAT_POWERPOINT.includes(getFileType(fileUrl)): 45 | format = 'powerpoint'; 46 | break; 47 | case FORMAT_PDF.includes(getFileType(fileUrl)): 48 | format = 'pdf'; 49 | break; 50 | case FORMAT_PHOTOSHOP.includes(getFileType(fileUrl)): 51 | format = 'photoshop'; 52 | break; 53 | case FORMAT_ILLUSTRATOR.includes(getFileType(fileUrl)): 54 | format = 'illustrator'; 55 | break; 56 | default: 57 | format = getFileType(fileUrl); 58 | } 59 | 60 | return format; 61 | } 62 | 63 | const getIcon = (name) => ( 64 | {name} 69 | ); 70 | 71 | export function getFileThumb(fileUrl) { 72 | let thumb; 73 | switch (getFileFormat(fileUrl)) { 74 | case 'video': 75 | thumb = getIcon('format_video'); 76 | break; 77 | case 'word': 78 | thumb = getIcon('format_word'); 79 | break; 80 | case 'excel': 81 | thumb = getIcon('format_excel'); 82 | break; 83 | case 'powerpoint': 84 | thumb = getIcon('format_powerpoint'); 85 | break; 86 | case 'pdf': 87 | thumb = getIcon('format_pdf'); 88 | break; 89 | case 'photoshop': 90 | thumb = getIcon('format_photoshop'); 91 | break; 92 | case 'illustrator': 93 | thumb = getIcon('format_ai'); 94 | break; 95 | case 'image': 96 | thumb = {fileUrl}; 97 | break; 98 | default: 99 | thumb = ; 100 | } 101 | return thumb; 102 | } 103 | -------------------------------------------------------------------------------- /src/utils/getFontValue.js: -------------------------------------------------------------------------------- 1 | // @mui 2 | import { useTheme } from '@mui/material/styles'; 3 | // hooks 4 | import useResponsive from '../hooks/useResponsive'; 5 | 6 | // ---------------------------------------------------------------------- 7 | 8 | export default function GetFontValue(variant) { 9 | const theme = useTheme(); 10 | 11 | const breakpoints = useWidth(); 12 | 13 | const key = theme.breakpoints.up(breakpoints === 'xl' ? 'lg' : breakpoints); 14 | 15 | const hasResponsive = 16 | variant === 'h1' || 17 | variant === 'h2' || 18 | variant === 'h3' || 19 | variant === 'h4' || 20 | variant === 'h5' || 21 | variant === 'h6'; 22 | 23 | const getFont = 24 | hasResponsive && theme.typography[variant][key] ? theme.typography[variant][key] : theme.typography[variant]; 25 | 26 | const fontSize = remToPx(getFont.fontSize); 27 | 28 | const lineHeight = Number(theme.typography[variant].lineHeight) * fontSize; 29 | 30 | const { fontWeight } = theme.typography[variant]; 31 | 32 | const { letterSpacing } = theme.typography[variant]; 33 | 34 | return { fontSize, lineHeight, fontWeight, letterSpacing }; 35 | } 36 | 37 | // ---------------------------------------------------------------------- 38 | 39 | export function remToPx(value) { 40 | return Math.round(parseFloat(value) * 16); 41 | } 42 | 43 | export function pxToRem(value) { 44 | return `${value / 16}rem`; 45 | } 46 | 47 | export function responsiveFontSizes({ sm, md, lg }) { 48 | return { 49 | '@media (min-width:600px)': { 50 | fontSize: pxToRem(sm), 51 | }, 52 | '@media (min-width:900px)': { 53 | fontSize: pxToRem(md), 54 | }, 55 | '@media (min-width:1200px)': { 56 | fontSize: pxToRem(lg), 57 | }, 58 | }; 59 | } 60 | 61 | // ---------------------------------------------------------------------- 62 | 63 | function useWidth() { 64 | const theme = useTheme(); 65 | 66 | const keys = [...theme.breakpoints.keys].reverse(); 67 | 68 | return ( 69 | keys.reduce((output, key) => { 70 | // eslint-disable-next-line react-hooks/rules-of-hooks 71 | const matches = useResponsive('up', key); 72 | 73 | return !output && matches ? key : output; 74 | }, null) || 'xs' 75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/utils/jwt.js: -------------------------------------------------------------------------------- 1 | import jwtDecode from 'jwt-decode'; 2 | // routes 3 | import { PATH_AUTH } from '../routes/paths'; 4 | // 5 | import axios from './axios'; 6 | 7 | // ---------------------------------------------------------------------- 8 | 9 | const isValidToken = (accessToken) => { 10 | if (!accessToken) { 11 | return false; 12 | } 13 | const decoded = jwtDecode(accessToken); 14 | 15 | const currentTime = Date.now() / 1000; 16 | 17 | return decoded.exp > currentTime; 18 | }; 19 | 20 | const handleTokenExpired = (exp) => { 21 | let expiredTimer; 22 | 23 | const currentTime = Date.now(); 24 | 25 | // Test token expires after 10s 26 | // const timeLeft = currentTime + 10000 - currentTime; // ~10s 27 | const timeLeft = exp * 1000 - currentTime; 28 | 29 | clearTimeout(expiredTimer); 30 | 31 | expiredTimer = setTimeout(() => { 32 | // eslint-disable-next-line no-alert 33 | alert('Token expired'); 34 | 35 | localStorage.removeItem('accessToken'); 36 | 37 | window.location.href = PATH_AUTH.login; 38 | }, timeLeft); 39 | }; 40 | 41 | const setSession = (accessToken) => { 42 | if (accessToken) { 43 | localStorage.setItem('accessToken', accessToken); 44 | axios.defaults.headers.common.Authorization = `Bearer ${accessToken}`; 45 | 46 | // This function below will handle when token is expired 47 | const { exp } = jwtDecode(accessToken); // ~3 days by codingmonks server 48 | handleTokenExpired(exp); 49 | } else { 50 | localStorage.removeItem('accessToken'); 51 | delete axios.defaults.headers.common.Authorization; 52 | } 53 | }; 54 | 55 | export { isValidToken, setSession }; 56 | -------------------------------------------------------------------------------- /src/utils/s3.js: -------------------------------------------------------------------------------- 1 | import AWS from 'aws-sdk'; 2 | import { AWS_ACCESS_KEY, AWS_S3_REGION, AWS_SECRET_ACCESS_KEY } from '../config'; 3 | 4 | const S3 = new AWS.S3({ 5 | signatureVersion: 'v4', 6 | region: AWS_S3_REGION, 7 | accessKeyId: AWS_ACCESS_KEY, // YOUR AWS ACCESS KEY 8 | secretAccessKey: AWS_SECRET_ACCESS_KEY, // YOUR AWS SECRET ACCESS KEY 9 | }); 10 | 11 | 12 | export default S3; -------------------------------------------------------------------------------- /src/utils/truncate.js: -------------------------------------------------------------------------------- 1 | function truncateString(str, num) { 2 | if (str.length > num) { 3 | return str.slice(0, num) + "..."; 4 | } else { 5 | return str; 6 | } 7 | } 8 | 9 | export default truncateString; 10 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------