├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public └── vite.svg ├── src ├── App.css ├── App.tsx ├── assets │ ├── icons │ │ ├── index.tsx │ │ └── src │ │ │ ├── icon-cancel.tsx │ │ │ ├── icon-checkmark.tsx │ │ │ ├── icon-congrats.tsx │ │ │ ├── icon-degree.tsx │ │ │ ├── icon-down-arrow-lg.tsx │ │ │ ├── icon-down-arrow-sm.tsx │ │ │ ├── icon-down-arrow.tsx │ │ │ ├── icon-google.tsx │ │ │ ├── icon-grey-heart.tsx │ │ │ ├── icon-language.tsx │ │ │ ├── icon-left-arrow.tsx │ │ │ ├── icon-lock.tsx │ │ │ ├── icon-mail.tsx │ │ │ ├── icon-map.tsx │ │ │ ├── icon-ok.tsx │ │ │ ├── icon-outlook.tsx │ │ │ ├── icon-plus.tsx │ │ │ ├── icon-right-arrow-lg.tsx │ │ │ ├── icon-right-arrow.tsx │ │ │ ├── icon-search.tsx │ │ │ ├── icon-star.tsx │ │ │ ├── icon-toolbox.tsx │ │ │ ├── icon-up-arrow.tsx │ │ │ ├── icon-user.tsx │ │ │ └── icon-white-heart.tsx │ └── imgs │ │ ├── index.tsx │ │ └── src │ │ ├── admin-avatar-lg.png │ │ ├── admin-avatar.png │ │ ├── bg-signup.jpg │ │ ├── img-banner.jpg │ │ ├── img-card-lg-01.png │ │ ├── img-card-lg-02.png │ │ ├── img-card-lg-03.png │ │ ├── img-card-md-01.png │ │ ├── img-card-md-02.png │ │ ├── img-card-md-03.png │ │ ├── img-card-md-04.png │ │ ├── table-user-avatar.png │ │ └── user-avatar.png ├── components │ ├── avatar │ │ ├── avatar.tsx │ │ └── index.tsx │ ├── button │ │ ├── button.tsx │ │ └── index.tsx │ ├── card │ │ ├── card.tsx │ │ └── index.tsx │ ├── checkbox │ │ ├── checkbox.tsx │ │ └── index.tsx │ ├── dropdown │ │ ├── dropdown.tsx │ │ └── index.tsx │ ├── input │ │ ├── index.tsx │ │ └── input.tsx │ ├── modal │ │ ├── index.tsx │ │ └── modal.tsx │ ├── radio-group │ │ ├── index.tsx │ │ └── radio-group.tsx │ ├── select │ │ ├── index.tsx │ │ └── select.tsx │ ├── slider │ │ ├── index.tsx │ │ └── slider.tsx │ ├── switch │ │ ├── index.tsx │ │ └── switch.tsx │ ├── table │ │ ├── index.tsx │ │ └── table.tsx │ ├── textarea │ │ ├── index.tsx │ │ └── textarea.tsx │ └── tour-stepper │ │ ├── index.tsx │ │ └── tour-stepper.tsx ├── index.css ├── layout │ ├── index.tsx │ └── nav.tsx ├── main.tsx ├── pages │ ├── admin │ │ ├── analytics.tsx │ │ ├── dashboard.tsx │ │ ├── index.tsx │ │ ├── requests.tsx │ │ ├── settings.tsx │ │ └── werknemers.tsx │ ├── all-courses │ │ └── all-courses.tsx │ ├── auth │ │ ├── layout │ │ │ └── index.tsx │ │ ├── new-password │ │ │ └── index.tsx │ │ ├── reset-password │ │ │ └── index.tsx │ │ └── signin │ │ │ └── index.tsx │ └── overview │ │ ├── components │ │ ├── banner.tsx │ │ └── tour.tsx │ │ └── index.tsx ├── router │ └── index.tsx └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vercel.json └── vite.config.ts /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default { 18 | // other rules... 19 | parserOptions: { 20 | ecmaVersion: 'latest', 21 | sourceType: 'module', 22 | project: ['./tsconfig.json', './tsconfig.node.json'], 23 | tsconfigRootDir: __dirname, 24 | }, 25 | } 26 | ``` 27 | 28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 31 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Vite + React + TS 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ace-academy", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "react-router-dom": "^6.20.1" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.2.43", 19 | "@types/react-dom": "^18.2.17", 20 | "@typescript-eslint/eslint-plugin": "^6.14.0", 21 | "@typescript-eslint/parser": "^6.14.0", 22 | "@vitejs/plugin-react": "^4.2.1", 23 | "autoprefixer": "^10.4.16", 24 | "eslint": "^8.55.0", 25 | "eslint-plugin-react-hooks": "^4.6.0", 26 | "eslint-plugin-react-refresh": "^0.4.5", 27 | "postcss": "^8.4.32", 28 | "tailwindcss": "^3.3.6", 29 | "typescript": "^5.2.2", 30 | "vite": "^5.0.8" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/App.css -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import './App.css' 2 | import Router from './router' 3 | import { BrowserRouter } from 'react-router-dom'; 4 | import AppLayout from './layout'; 5 | 6 | function App() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | ) 14 | } 15 | 16 | export default App 17 | -------------------------------------------------------------------------------- /src/assets/icons/index.tsx: -------------------------------------------------------------------------------- 1 | import IconGoogle from './src/icon-google.tsx' 2 | import IconOutlook from './src/icon-outlook.tsx' 3 | import IconMail from './src/icon-mail.tsx' 4 | import IconLock from './src/icon-lock.tsx' 5 | import IconCheckMark from './src/icon-checkmark.tsx' 6 | import IconDownArrow from './src/icon-down-arrow.tsx' 7 | import IconDegree from './src/icon-degree.tsx' 8 | import IconMap from './src/icon-map.tsx' 9 | import IconToolbox from './src/icon-toolbox.tsx' 10 | import IconCongrats from './src/icon-congrats.tsx' 11 | import IconSearch from './src/icon-search.tsx' 12 | import IconStar from './src/icon-star.tsx' 13 | import IconWhiteHeart from './src/icon-white-heart.tsx' 14 | import IconGreyHeart from './src/icon-grey-heart.tsx' 15 | import IconLeftArrow from './src/icon-left-arrow.tsx' 16 | import IconRightArrow from './src/icon-right-arrow.tsx' 17 | import IconLgRightArrow from './src/icon-right-arrow-lg.tsx' 18 | import IconLgDownArrow from './src/icon-down-arrow-lg.tsx' 19 | import IconSmDownArrow from './src/icon-down-arrow-sm.tsx' 20 | import IconUpArrow from './src/icon-up-arrow.tsx' 21 | import IconLanguage from './src/icon-language.tsx' 22 | import IconUser from './src/icon-user.tsx' 23 | import IconOk from './src/icon-ok.tsx' 24 | import IconCancel from './src/icon-cancel.tsx' 25 | import IconPlus from './src/icon-plus.tsx' 26 | 27 | export { 28 | IconGoogle, 29 | IconOutlook, 30 | IconMail, 31 | IconLock, 32 | IconCheckMark, 33 | IconDownArrow, 34 | IconDegree, 35 | IconMap, 36 | IconToolbox, 37 | IconCongrats, 38 | IconSearch, 39 | IconStar, 40 | IconWhiteHeart, 41 | IconGreyHeart, 42 | IconLeftArrow, 43 | IconRightArrow, 44 | IconLgDownArrow, 45 | IconUpArrow, 46 | IconLanguage, 47 | IconSmDownArrow, 48 | IconLgRightArrow, 49 | IconUser, 50 | IconOk, 51 | IconCancel, 52 | IconPlus 53 | } -------------------------------------------------------------------------------- /src/assets/icons/src/icon-cancel.tsx: -------------------------------------------------------------------------------- 1 | const IconCancel = () => { 2 | return ( 3 | 4 | 5 | 6 | 7 | ) 8 | } 9 | 10 | export default IconCancel 11 | 12 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-checkmark.tsx: -------------------------------------------------------------------------------- 1 | const IconCheckMark = () => { 2 | return ( 3 | 10 | 11 | 12 | 18 | 19 | 23 | 24 | 25 | 34 | 35 | 41 | 47 | 53 | 54 | 55 | 56 | 60 | 61 | 62 | 63 | 69 | 70 | 71 | 72 | ); 73 | }; 74 | 75 | export default IconCheckMark; 76 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-congrats.tsx: -------------------------------------------------------------------------------- 1 | const IconCongrats = () => { 2 | return ( 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ) 13 | } 14 | 15 | export default IconCongrats 16 | 17 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-degree.tsx: -------------------------------------------------------------------------------- 1 | const IconDegree = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconDegree -------------------------------------------------------------------------------- /src/assets/icons/src/icon-down-arrow-lg.tsx: -------------------------------------------------------------------------------- 1 | const IconLgDownArrow = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconLgDownArrow 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-down-arrow-sm.tsx: -------------------------------------------------------------------------------- 1 | const IconWhiteHeart = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconWhiteHeart 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-down-arrow.tsx: -------------------------------------------------------------------------------- 1 | const IconDownArrow = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconDownArrow -------------------------------------------------------------------------------- /src/assets/icons/src/icon-google.tsx: -------------------------------------------------------------------------------- 1 | const IconGoogle = () => { 2 | return ( 3 | 4 | 7 | 8 | ) 9 | } 10 | 11 | export default IconGoogle -------------------------------------------------------------------------------- /src/assets/icons/src/icon-grey-heart.tsx: -------------------------------------------------------------------------------- 1 | const IconGreyHeart = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconGreyHeart 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-language.tsx: -------------------------------------------------------------------------------- 1 | const IconLanguage = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconLanguage 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-left-arrow.tsx: -------------------------------------------------------------------------------- 1 | const IconLeftArrow = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconLeftArrow 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-lock.tsx: -------------------------------------------------------------------------------- 1 | const IconLock = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconLock 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-mail.tsx: -------------------------------------------------------------------------------- 1 | const IconMail = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconMail -------------------------------------------------------------------------------- /src/assets/icons/src/icon-map.tsx: -------------------------------------------------------------------------------- 1 | const IconMap = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconMap 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-ok.tsx: -------------------------------------------------------------------------------- 1 | const IconOk = () => { 2 | return ( 3 | 4 | 5 | 6 | 7 | ) 8 | } 9 | 10 | export default IconOk 11 | 12 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-outlook.tsx: -------------------------------------------------------------------------------- 1 | const IconOutlook = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconOutlook 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-plus.tsx: -------------------------------------------------------------------------------- 1 | const IconPlus = () => { 2 | return ( 3 | 4 | 5 | 6 | 7 | ) 8 | } 9 | 10 | export default IconPlus 11 | 12 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-right-arrow-lg.tsx: -------------------------------------------------------------------------------- 1 | const IconRightArrowLg = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconRightArrowLg -------------------------------------------------------------------------------- /src/assets/icons/src/icon-right-arrow.tsx: -------------------------------------------------------------------------------- 1 | const IconRightArrow = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconRightArrow 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-search.tsx: -------------------------------------------------------------------------------- 1 | const IconSearch = () => { 2 | return ( 3 | 4 | 5 | 6 | 7 | 8 | 9 | ) 10 | } 11 | 12 | export default IconSearch 13 | 14 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-star.tsx: -------------------------------------------------------------------------------- 1 | const IconStar = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconStar 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-toolbox.tsx: -------------------------------------------------------------------------------- 1 | const IconToolbox = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconToolbox 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-up-arrow.tsx: -------------------------------------------------------------------------------- 1 | const IconUpArrow = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconUpArrow 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-user.tsx: -------------------------------------------------------------------------------- 1 | const IconUser = () => { 2 | return ( 3 | 4 | 5 | 6 | 7 | ) 8 | } 9 | 10 | export default IconUser 11 | 12 | -------------------------------------------------------------------------------- /src/assets/icons/src/icon-white-heart.tsx: -------------------------------------------------------------------------------- 1 | const IconWhiteHeart = () => { 2 | return ( 3 | 4 | 5 | 6 | ) 7 | } 8 | 9 | export default IconWhiteHeart 10 | 11 | -------------------------------------------------------------------------------- /src/assets/imgs/index.tsx: -------------------------------------------------------------------------------- 1 | import BgSignUp from './src/bg-signup.jpg' 2 | import CardImgLg01 from './src/img-card-lg-01.png' 3 | import CardImgLg02 from './src/img-card-lg-02.png' 4 | import CardImgLg03 from './src/img-card-lg-03.png' 5 | import CardImgMd01 from './src/img-card-md-01.png' 6 | import CardImgMd02 from './src/img-card-md-02.png' 7 | import CardImgMd03 from './src/img-card-md-03.png' 8 | import CardImgMd04 from './src/img-card-md-04.png' 9 | import BannerImg from './src/img-banner.jpg' 10 | import UserAvatar from './src/user-avatar.png' 11 | import AdminAvatar from './src/admin-avatar.png' 12 | import TableUserAvatar from './src/table-user-avatar.png' 13 | import AdminAvatarLg from './src/admin-avatar-lg.png' 14 | 15 | export { 16 | BgSignUp, 17 | CardImgLg01, 18 | CardImgLg02, 19 | CardImgLg03, 20 | CardImgMd01, 21 | CardImgMd02, 22 | CardImgMd03, 23 | CardImgMd04, 24 | BannerImg, 25 | UserAvatar, 26 | AdminAvatar, 27 | TableUserAvatar, 28 | AdminAvatarLg 29 | } -------------------------------------------------------------------------------- /src/assets/imgs/src/admin-avatar-lg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/assets/imgs/src/admin-avatar-lg.png -------------------------------------------------------------------------------- /src/assets/imgs/src/admin-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/assets/imgs/src/admin-avatar.png -------------------------------------------------------------------------------- /src/assets/imgs/src/bg-signup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/assets/imgs/src/bg-signup.jpg -------------------------------------------------------------------------------- /src/assets/imgs/src/img-banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/assets/imgs/src/img-banner.jpg -------------------------------------------------------------------------------- /src/assets/imgs/src/img-card-lg-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/assets/imgs/src/img-card-lg-01.png -------------------------------------------------------------------------------- /src/assets/imgs/src/img-card-lg-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/assets/imgs/src/img-card-lg-02.png -------------------------------------------------------------------------------- /src/assets/imgs/src/img-card-lg-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/assets/imgs/src/img-card-lg-03.png -------------------------------------------------------------------------------- /src/assets/imgs/src/img-card-md-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/assets/imgs/src/img-card-md-01.png -------------------------------------------------------------------------------- /src/assets/imgs/src/img-card-md-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/assets/imgs/src/img-card-md-02.png -------------------------------------------------------------------------------- /src/assets/imgs/src/img-card-md-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/assets/imgs/src/img-card-md-03.png -------------------------------------------------------------------------------- /src/assets/imgs/src/img-card-md-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/assets/imgs/src/img-card-md-04.png -------------------------------------------------------------------------------- /src/assets/imgs/src/table-user-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/assets/imgs/src/table-user-avatar.png -------------------------------------------------------------------------------- /src/assets/imgs/src/user-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamesWangDev/ace-academy-react/4f61355a27d241223595f17657ad45d9944c603e/src/assets/imgs/src/user-avatar.png -------------------------------------------------------------------------------- /src/components/avatar/avatar.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useState, useRef, useEffect } from "react"; 2 | import { IconLgDownArrow, IconUpArrow, IconLanguage } from "../../assets/icons"; 3 | import { Button } from "../button"; 4 | import { useNavigate } from "react-router-dom"; 5 | 6 | interface AvatarProps { 7 | isAdmin?: boolean; 8 | img: string; 9 | status: "online" | "offline"; 10 | } 11 | 12 | const Avatar = ({img, status, isAdmin}: AvatarProps) => { 13 | const [showPanel, setShowPanel] = useState(false) 14 | const selectRef = useRef(null); 15 | 16 | useEffect(() => { 17 | setShowPanel(false) 18 | }, [isAdmin]) 19 | 20 | useEffect(() => { 21 | document.addEventListener('mousedown', handleClickOutside); 22 | 23 | return () => { 24 | document.removeEventListener('mousedown', handleClickOutside); 25 | }; 26 | }, []); 27 | 28 | const handleClickOutside = (event: MouseEvent) => { 29 | if (selectRef.current && !selectRef.current.contains(event.target as Node)) { 30 | setShowPanel(false); 31 | } 32 | }; 33 | 34 | const handleClickAvatar = useCallback(() => { 35 | setShowPanel(prev => !prev) 36 | }, [setShowPanel]) 37 | 38 | return ( 39 |
40 |
41 |
42 | avatar 43 | {!isAdmin &&
} 44 |
45 |
{showPanel ? : }
46 |
47 | {showPanel && } 48 |
49 | ) 50 | } 51 | 52 | interface OptionsPanelProps { 53 | isAdmin?: boolean; 54 | img: string; 55 | } 56 | 57 | const OptionsPanel = ({img, isAdmin}: OptionsPanelProps) => { 58 | const navigate = useNavigate(); 59 | 60 | return ( 61 |
62 |
63 | user avatar 64 |
65 |
Arnand Siem
66 |
Koppert
67 |
68 |
69 |
81 | ) 82 | } 83 | 84 | export default Avatar -------------------------------------------------------------------------------- /src/components/avatar/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Avatar } from './avatar' -------------------------------------------------------------------------------- /src/components/button/button.tsx: -------------------------------------------------------------------------------- 1 | interface ButtonProps { 2 | className?: string; 3 | type?: "default" | "primary" | "success" | "warning"; 4 | icon?: React.ReactNode; 5 | label: string; 6 | onClick?: () => void; 7 | disabled?: boolean; 8 | rounded?: boolean; 9 | size?: "md" | "lg"; 10 | } 11 | 12 | const Button = ({className, type = "default", icon, disabled = false, label, onClick, rounded = false, size = "lg"}: ButtonProps) => { 13 | const types = { 14 | default: `bg-white text-midblack ${!rounded && "hover:border-midgrey hover:bg-tangerine border border-black"}`, 15 | primary: `bg-primary-500 hover:bg-primary-400 ${rounded ? "text-midblack" : "text-white"} border border-primary-500`, 16 | success: `bg-success hover:bg-success ${rounded ? "text-midblack" : "text-white"} border border-success`, 17 | warning: `bg-warning hover:bg-warning ${rounded ? "text-midblack" : "text-white"} border border-warning`, 18 | } 19 | let buttonClassName = `px-6.5 ${size === "lg" ? "h-16" : "h-11"} flex ${rounded ? "rounded-15" : "rounded-2.5"} disabled:cursor-not-allowed disabled:bg-disabled-bg disabled:text-disabled-text disabled:border-disabled-text ${types[type]} ${className}` 20 | return ( 21 | 27 | ) 28 | } 29 | 30 | export default Button -------------------------------------------------------------------------------- /src/components/button/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Button } from './button' -------------------------------------------------------------------------------- /src/components/card/card.tsx: -------------------------------------------------------------------------------- 1 | import { IconStar, IconWhiteHeart, IconGreyHeart } from "../../assets/icons"; 2 | 3 | export interface CardProps { 4 | className?: string; 5 | data: CardData; 6 | } 7 | 8 | export interface CardData { 9 | price: number; 10 | name: string; 11 | title: string; 12 | like: boolean; 13 | ratings: number; 14 | img: string; 15 | size: "md" | "lg"; 16 | } 17 | 18 | const Card = ({className, data}: CardProps) => { 19 | const {price, name, title, like, ratings, img, size} = data 20 | const cardClassName = `flex flex-col ${size === "lg" ? "w-101 h-80 " : " w-75 h-90"} rounded-2.5 bg-white shadow-custom3 cursor-pointer ${className}` 21 | return ( 22 |
23 |
24 | card image 25 |
${price}
26 |
27 |
28 |
29 |
{name}
30 |
{title}
31 |
32 |
33 |
34 | 35 | {ratings}/5 36 |
37 |
38 | 39 | {like ? : } 40 | 41 |
42 |
43 |
44 |
45 | ) 46 | } 47 | 48 | export default Card -------------------------------------------------------------------------------- /src/components/card/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Card } from './card' -------------------------------------------------------------------------------- /src/components/checkbox/checkbox.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | interface CheckboxProps { 4 | className?: string; 5 | label?: string; 6 | } 7 | 8 | const Checkbox = ({className, label}: CheckboxProps) => { 9 | const checkboxClassName = `flex items-center ${className}` 10 | const [isChecked, setChecked] = useState(false); 11 | 12 | const handleCheckboxChange = () => { 13 | setChecked(!isChecked); 14 | }; 15 | 16 | return ( 17 |
18 | 25 | 47 |
48 | ) 49 | } 50 | 51 | export default Checkbox -------------------------------------------------------------------------------- /src/components/checkbox/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Checkbox } from './checkbox' -------------------------------------------------------------------------------- /src/components/dropdown/dropdown.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect } from 'react'; 2 | import { IconDownArrow, IconRightArrow } from '../../assets/icons'; 3 | 4 | interface DropdownProps { 5 | options: string[]; 6 | label?: string; 7 | className?: string; 8 | } 9 | 10 | const Dropdown: React.FC = ({ options, label, className }) => { 11 | const [isOpen, setIsOpen] = useState(false); 12 | const selectRef = useRef(null); 13 | 14 | useEffect(() => { 15 | document.addEventListener('mousedown', handleClickOutside); 16 | 17 | return () => { 18 | document.removeEventListener('mousedown', handleClickOutside); 19 | }; 20 | }, []); 21 | 22 | const handleClickOutside = (event: MouseEvent) => { 23 | if (selectRef.current && !selectRef.current.contains(event.target as Node)) { 24 | setIsOpen(false); 25 | } 26 | }; 27 | 28 | const dropdownClasses = `block ${className}`; 29 | 30 | return ( 31 |
32 |
33 | 40 | {isOpen && ( 41 |
    42 | {options.map((option) => ( 43 |
  • 47 | {option} 48 | 49 |
  • 50 | ))} 51 |
52 | )} 53 |
54 |
55 | ); 56 | }; 57 | 58 | export default Dropdown; 59 | -------------------------------------------------------------------------------- /src/components/dropdown/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Dropdown } from './dropdown' -------------------------------------------------------------------------------- /src/components/input/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Input } from './input' -------------------------------------------------------------------------------- /src/components/input/input.tsx: -------------------------------------------------------------------------------- 1 | interface InputProps { 2 | className?: string; 3 | label?: string; 4 | icon?: React.ReactNode; 5 | placeholder?: string; 6 | isRequired?: boolean; 7 | type?: "default" | "rounded" 8 | } 9 | 10 | const Input = ({className, label, icon, placeholder, isRequired = false, type = "default"}: InputProps) => { 11 | const inputClassName = `block ${className}` 12 | 13 | return ( 14 |
15 | {label &&
16 | {label} 17 |
} 18 |
19 | {icon &&
{icon}
} 20 | 21 |
22 |
23 | ) 24 | } 25 | 26 | export default Input -------------------------------------------------------------------------------- /src/components/modal/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Modal } from './modal' -------------------------------------------------------------------------------- /src/components/modal/modal.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react"; 2 | 3 | interface ModalProps { 4 | classNames? : string; 5 | children: React.ReactNode; 6 | visible: boolean; 7 | setVisible: (value: boolean) => void; 8 | } 9 | 10 | const Modal = ({classNames, children, visible, setVisible}: ModalProps) => { 11 | const selectRef = useRef(null); 12 | 13 | useEffect(() => { 14 | document.addEventListener('mousedown', handleClickOutside); 15 | 16 | return () => { 17 | document.removeEventListener('mousedown', handleClickOutside); 18 | }; 19 | }, []); 20 | 21 | const handleClickOutside = (event: MouseEvent) => { 22 | if (selectRef.current && !selectRef.current.contains(event.target as Node)) { 23 | setVisible(false); 24 | } 25 | }; 26 | 27 | 28 | const modalClassNames = `p-16 w-131 my-auto justify-center bg-white items-center overflow-x-hidden overflow-y-auto inset-0 z-50 my-auto rounded-5 shadow-custom ${classNames}`; 29 | 30 | return ( 31 | <> 32 | {visible &&
33 |
34 | {children} 35 |
36 |
37 |
} 38 | 39 | ) 40 | } 41 | 42 | export default Modal -------------------------------------------------------------------------------- /src/components/radio-group/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as RadioGroup } from './radio-group' -------------------------------------------------------------------------------- /src/components/radio-group/radio-group.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | interface CustomRadioGroupProps { 4 | className?: string; 5 | label?: string; 6 | options: RadioOption[] 7 | } 8 | 9 | interface RadioOption { 10 | value: string; 11 | label: string; 12 | } 13 | 14 | const CustomRadioGroup = ({className, label, options}: CustomRadioGroupProps) => { 15 | const [selectedOption, setSelectedOption] = useState(null); 16 | 17 | const handleOptionChange = (option: string) => { 18 | setSelectedOption(option); 19 | }; 20 | 21 | const radioClassName = `${className}` 22 | 23 | return ( 24 |
25 | {label &&
26 | {label} 27 |
} 28 |
29 | {options.map((option) => ( 30 |
handleOptionChange(option.value)} 34 | > 35 |
36 |
37 |
38 | 39 |
40 | ))} 41 |
42 |
43 | ); 44 | }; 45 | 46 | export default CustomRadioGroup; 47 | -------------------------------------------------------------------------------- /src/components/select/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Select } from './select' -------------------------------------------------------------------------------- /src/components/select/select.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect } from 'react'; 2 | import { IconDownArrow } from '../../assets/icons'; 3 | 4 | interface CustomSelectProps { 5 | options: string[]; 6 | label?: string; 7 | className?: string; 8 | placeholder?: string; 9 | icon?: React.ReactNode; 10 | } 11 | 12 | const CustomSelect: React.FC = ({ options, label, icon, className, placeholder }) => { 13 | const [selectedOption, setSelectedOption] = useState(null); 14 | const [isOpen, setIsOpen] = useState(false); 15 | const selectRef = useRef(null); 16 | 17 | const handleOptionClick = (option: string) => { 18 | setSelectedOption(option); 19 | setIsOpen(false); 20 | }; 21 | 22 | const handleClickOutside = (event: MouseEvent) => { 23 | if (selectRef.current && !selectRef.current.contains(event.target as Node)) { 24 | setIsOpen(false); 25 | } 26 | }; 27 | 28 | useEffect(() => { 29 | document.addEventListener('mousedown', handleClickOutside); 30 | 31 | return () => { 32 | document.removeEventListener('mousedown', handleClickOutside); 33 | }; 34 | }, []); 35 | 36 | const customSelectClasses = `block ${className}`; 37 | 38 | return ( 39 |
40 | {label && ( 41 |
42 | {label} 43 |
44 | )} 45 |
46 | 54 | {isOpen && ( 55 |
    56 | {options.map((option) => ( 57 |
  • handleOptionClick(option)} 61 | > 62 | {option} 63 |
  • 64 | ))} 65 |
66 | )} 67 |
68 |
69 | ); 70 | }; 71 | 72 | export default CustomSelect; 73 | -------------------------------------------------------------------------------- /src/components/slider/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Slider } from './slider' -------------------------------------------------------------------------------- /src/components/slider/slider.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { CardData } from '../card/card'; 3 | import Card from '../card/card'; 4 | import { IconLeftArrow, IconLgRightArrow } from '../../assets/icons'; 5 | 6 | interface SliderProps { 7 | className?: string; 8 | cards: CardData[]; 9 | cardsPerPage: number; 10 | label?: string; 11 | } 12 | 13 | const Slider: React.FC = ({ cards, cardsPerPage, label, className }) => { 14 | const [currentPage, setCurrentPage] = useState(1); 15 | 16 | const totalPages = Math.ceil(cards.length / cardsPerPage); 17 | 18 | const startIndex = (currentPage - 1) * cardsPerPage; 19 | const endIndex = startIndex + cardsPerPage; 20 | 21 | const visibleCards = cards.slice(startIndex, endIndex); 22 | 23 | const handleNextPage = () => { 24 | setCurrentPage((prevPage) => Math.min(prevPage + 1, totalPages)); 25 | }; 26 | 27 | const handlePrevPage = () => { 28 | setCurrentPage((prevPage) => Math.max(prevPage - 1, 1)); 29 | }; 30 | 31 | const pages = Array.from({ length: totalPages }, (_, index) => index); 32 | 33 | const sliderClassName = `mx-auto w-fit ${className}` 34 | 35 | return ( 36 |
37 |
38 | {label} 39 |
40 |
41 |
42 | {visibleCards.map((card, index) => ( 43 |
44 | 45 |
46 | ))} 47 |
48 |
49 |
50 | 56 | 62 |
63 |
64 | 65 |
66 |
67 | {pages.map((page: number) => { 68 | return ( 69 | <> 70 | { page === currentPage - 1 ? 71 | : 72 | } 73 | 74 | ) 75 | })} 76 |
77 |
78 | ); 79 | }; 80 | 81 | export default Slider; 82 | -------------------------------------------------------------------------------- /src/components/switch/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Switch } from './switch' -------------------------------------------------------------------------------- /src/components/switch/switch.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface SwitchProps { 4 | label?: string; 5 | checked: boolean; 6 | onChange: (checked: boolean) => void; 7 | } 8 | 9 | const Switch: React.FC = ({ label, checked, onChange }) => { 10 | const handleToggle = () => { 11 | onChange(!checked); 12 | }; 13 | 14 | return ( 15 |
16 |
21 |
22 |
23 | {label && {label}} 24 |
25 | ); 26 | }; 27 | 28 | export default Switch; 29 | -------------------------------------------------------------------------------- /src/components/table/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Table } from './table' -------------------------------------------------------------------------------- /src/components/table/table.tsx: -------------------------------------------------------------------------------- 1 | // CustomTable.tsx 2 | import React, { useState } from 'react'; 3 | 4 | interface TableColumn { 5 | header: string; 6 | width?: string; 7 | } 8 | 9 | interface TableProps { 10 | columns: TableColumn[]; 11 | data: any[][]; 12 | showCheckbox?: boolean; 13 | } 14 | 15 | const CustomTable: React.FC = ({ columns, data, showCheckbox }) => { 16 | const [selectedRows, setSelectedRows] = useState>(new Set()); 17 | 18 | const handleSelectAll = () => { 19 | if (selectedRows.size === data.length) { 20 | setSelectedRows(new Set()); 21 | } else { 22 | const allRows = Array.from({ length: data.length }, (_, index) => index); 23 | setSelectedRows(new Set(allRows)); 24 | } 25 | }; 26 | 27 | const handleRowCheckboxClick = (rowIndex: number) => { 28 | setSelectedRows((prevSelectedRows) => { 29 | const newSelectedRows = new Set(prevSelectedRows); 30 | if (newSelectedRows.has(rowIndex)) { 31 | newSelectedRows.delete(rowIndex); 32 | } else { 33 | newSelectedRows.add(rowIndex); 34 | } 35 | return newSelectedRows; 36 | }); 37 | }; 38 | 39 | return ( 40 |
41 | 42 | 43 | 44 | {showCheckbox && } 51 | {columns.map((column, index) => ( 52 | 55 | ))} 56 | 57 | 58 | 59 | {data.map((row, rowIndex) => ( 60 | 61 | {showCheckbox && } 69 | {row.map((cell, cellIndex) => ( 70 | 73 | ))} 74 | 75 | ))} 76 | 77 |
45 | 50 | 53 | {column.header} 54 |
62 | handleRowCheckboxClick(rowIndex)} 67 | /> 68 | 71 | {cell} 72 |
78 |
79 | ); 80 | }; 81 | 82 | export default CustomTable; 83 | -------------------------------------------------------------------------------- /src/components/textarea/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as TextArea } from './textarea' -------------------------------------------------------------------------------- /src/components/textarea/textarea.tsx: -------------------------------------------------------------------------------- 1 | interface TextAreaProps { 2 | label?: string; 3 | className?: string; 4 | placeholder?: string; 5 | } 6 | 7 | const TextArea = ({label, className, placeholder}: TextAreaProps) => { 8 | const textAreaClassName = `${className}` 9 | 10 | return ( 11 |
12 | {label &&
13 | {label} 14 |
} 15 |
16 |