diff --git a/package-lock.json b/package-lock.json index 1a624e5..e895457 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "query-string": "^9.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-icons": "^5.3.0", "react-router-dom": "^6.22.3", "react-scripts": "5.0.1", "sass": "^1.75.0", @@ -17305,6 +17306,14 @@ } } }, + "node_modules/react-icons": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", + "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/package.json b/package.json index 58036ed..0151ff4 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "query-string": "^9.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-icons": "^5.3.0", "react-router-dom": "^6.22.3", "react-scripts": "5.0.1", "sass": "^1.75.0", diff --git a/src/components/Drawers/RightSidebarForStudent.jsx b/src/components/Drawers/RightSidebarForStudent.jsx new file mode 100644 index 0000000..265dde7 --- /dev/null +++ b/src/components/Drawers/RightSidebarForStudent.jsx @@ -0,0 +1,153 @@ +import React from 'react'; +import { + Box, + VStack, + Text, + Image, + Avatar, + Drawer, + DrawerBody, + DrawerFooter, + DrawerHeader, + DrawerOverlay, + DrawerContent, + Skeleton, +} from '@chakra-ui/react'; +import PropTypes from 'prop-types'; + +import styles from './Drawer.module.scss'; + +import { useNavigate } from 'react-router-dom'; +import { + MdOutlineAssignment, + MdOutlineShoppingBag, + MdNotificationsNone, + MdLogout +} from 'react-icons/md'; +import { PiCertificate } from "react-icons/pi"; +import { IoBookOutline } from "react-icons/io5"; +import MenuBookOutlinedIcon from '@mui/icons-material/MenuBookOutlined'; +import AssignmentOutlinedIcon from '@mui/icons-material/AssignmentOutlined'; +import ShoppingBagOutlinedIcon from '@mui/icons-material/ShoppingBagOutlined'; +import WorkspacePremiumOutlinedIcon from '@mui/icons-material/WorkspacePremiumOutlined'; +import NotificationsNoneOutlinedIcon from '@mui/icons-material/NotificationsNoneOutlined'; +import LogoutIcon from '@mui/icons-material/Logout'; +import config from '~/config'; +import { isLoggedIn, removeLoginResponse } from '~/utils/authUtils'; +import SidebarItem from '~/components/Drawers/SidebarItem'; + +DrawerRightDefault.propTypes = { + isOpen: PropTypes.bool, + onClose: PropTypes.func, + user: PropTypes.object, + isUserLoading: PropTypes.bool, +}; + +DrawerRightDefault.defaultProps = { + user: { + username: 'john', + fullName: 'john', + email: 'john@gmail.com', + phoneNumber: '+84972640891', + bio: 'A student.', + gender: 'MALE', + dob: '2024-08-05', + role: 'ADMIN', + createdAt: '2024-08-05T13:47:06.794Z', + avatarSrc: '', + }, +}; + +function DrawerRightDefault(props) { + const user = props.user; + const navigate = useNavigate(); + + const handleLogout = () => { + removeLoginResponse(); + navigate(config.routes.login); + }; + + return ( + + + + + navigate(config.routes.user_profile_edit)} + _hover={{ + transform: 'scale(1.05)', + transition: 'transform 0.2s ease', + backgroundColor: '#f8f8f8', + cursor: 'pointer', + }} + > + + {!isLoggedIn() || props?.isUserLoading ? ( + + ) : ( + {user?.fullName} + )} + + + + + + navigate(config.routes.course_management_for_student)} + /> + + navigate('')} + /> + + navigate('')} + /> + + navigate('')} + /> + + navigate(config.routes.notifications_for_student)} + /> + + > + + + + + + + + + + + + ); +} + +export default DrawerRightDefault; diff --git a/src/components/Drawers/SidebarItem.jsx b/src/components/Drawers/SidebarItem.jsx new file mode 100644 index 0000000..75e1374 --- /dev/null +++ b/src/components/Drawers/SidebarItem.jsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { Box } from '@chakra-ui/react'; + +const SidebarItem = ({ icon: Icon, text, isActive, handleClick, size = 22 }) => { + return ( + + {Icon && } +

{text}

+
+ ); +}; + +export default SidebarItem; diff --git a/src/components/Navbars/HomeNavbar/HomeNavbar.jsx b/src/components/Navbars/HomeNavbar/HomeNavbar.jsx index f961d9c..c7d79c9 100644 --- a/src/components/Navbars/HomeNavbar/HomeNavbar.jsx +++ b/src/components/Navbars/HomeNavbar/HomeNavbar.jsx @@ -7,8 +7,11 @@ import DrawerRightDefault from '~/components/Drawers/DrawerRightDefault'; import AuthService from '~/services/authService'; import { isLoggedIn } from '~/utils/authUtils'; import useCustomToast from '~/hooks/useCustomToast'; +import { useNavigate } from 'react-router-dom'; +import config from '~/config'; function HomeNavbar(props) { + const navigate = useNavigate(); const { errorToast } = useCustomToast(); const { isOpen, onOpen, onClose } = useDisclosure(); const [user, setUser] = useState(null); @@ -37,7 +40,7 @@ function HomeNavbar(props) { Logo
- {isLoggedIn() ? ( @@ -48,7 +51,7 @@ function HomeNavbar(props) { ) : ( - )} diff --git a/src/components/Navbars/NavbarForStudent.jsx b/src/components/Navbars/NavbarForStudent.jsx new file mode 100644 index 0000000..5545875 --- /dev/null +++ b/src/components/Navbars/NavbarForStudent.jsx @@ -0,0 +1,95 @@ +import React, { useState, useEffect } from 'react'; +import './Navbar.scss'; +import TransientAppLogo from '~/assets/images/TransientAppLogo.svg'; +import Button from '~/components/Buttons/Button'; +import { useDisclosure, Avatar, Spacer } from '@chakra-ui/react'; +import AuthService from '~/services/authService'; +import { isLoggedIn } from '~/utils/authUtils'; +import useCustomToast from '~/hooks/useCustomToast'; +import { useNavigate } from 'react-router-dom'; +import config from '~/config'; +import RightSidebarForStudent from '~/components/Drawers/RightSidebarForStudent'; + +function HomeNavbar(props) { + const navigate = useNavigate(); + const { errorToast } = useCustomToast(); + const { isOpen, onOpen, onClose } = useDisclosure(); + const [user, setUser] = useState(null); + const [isUserLoading, setIsUserLoading] = useState(true); + + useEffect(() => { + const fetchUser = async () => { + setIsUserLoading(true); + AuthService.getCurUser() + .then((user) => { + setUser(user); + }) + .catch((e) => { + errorToast(e?.message); + }) + .finally(() => { + setIsUserLoading(false); + }); + }; + + fetchUser(); + }, []); + + return ( +
+ Logo +
+
+ + + + + + {isLoggedIn() ? ( + <> +
+ +
+ + + ) : ( + + )} +
+
+
+ ); +} +export default React.memo(HomeNavbar); diff --git a/src/config/routes.jsx b/src/config/routes.jsx index 2a64df6..51b3579 100644 --- a/src/config/routes.jsx +++ b/src/config/routes.jsx @@ -3,7 +3,10 @@ const routes = { login: '/login', register: '/register', forgot_password: '/forgot-password', - user_profile_edit: '/profile' + user_profile_edit: '/profile', + otp_validation: '/otp-validation', + course_management_for_student: '/course-management-for-student', + notifications_for_student: '/notifications-for-student' }; export default routes; diff --git a/src/pages/CourseManagementForStudent.jsx b/src/pages/CourseManagementForStudent.jsx new file mode 100644 index 0000000..8f80c1e --- /dev/null +++ b/src/pages/CourseManagementForStudent.jsx @@ -0,0 +1,261 @@ +import React, { useState } from 'react'; +import { + Box, + Button, + Text, + Flex, + Grid, + Badge, + VStack, + Image, + Divider, + Container, +} from '@chakra-ui/react'; +import { Icon } from '@chakra-ui/icons'; +import { + FaLayerGroup, + FaUsers, + FaStar, + FaCertificate, + FaTrophy, +} from 'react-icons/fa'; +import Footer from '~/components/Footer'; +import NavbarForStudent from '~/components/Navbars/NavbarForStudent'; + +const CourseManagementForStudent = () => { + const [visibleCourses, setVisibleCourses] = useState(4); + const stats = [ + { icon: FaLayerGroup, label: 'Bundles', value: 0 }, + { icon: FaUsers, label: 'Groups', value: 0 }, + { icon: FaStar, label: 'Reviews', value: 0 }, + { icon: FaCertificate, label: 'Certificates', value: 0 }, + { icon: FaTrophy, label: 'Points', value: 0 }, + ]; + + const placeholderImage = + 'https://firebasestorage.googleapis.com/v0/b/ute-21110120-web-exercises.appspot.com/o/1.png?alt=media&token=bfd6d816-a3d3-4134-85ec-eda61054a927'; + + const courses = [ + { + title: 'Character Art School: Complete Drawing Course', + category: 'Art', + level: 'Beginner', + imageUrl: '', + duration: '20 hours', + price: 100, + description: + 'Learn how to draw characters like a pro in this comprehensive drawing course.', + isPublish: true, + createdBy: 'John Doe', + createdAt: 'September 27, 2024', + }, + { + title: 'Learning Jazz like in San Francisco', + category: 'Music', + level: 'Intermediate', + imageUrl: '', + duration: '16 hours', + price: 150, + description: + 'Master jazz techniques and musical theory with a San Francisco twist.', + isPublish: true, + createdBy: 'Jane Smith', + createdAt: 'September 27, 2024', + }, + { + title: 'The Right Set for Landscape Photography', + category: 'Photography', + level: 'Advanced', + imageUrl: '', + duration: '14 hours', + price: 200, + description: 'Get the perfect setup for capturing beautiful landscapes.', + isPublish: true, + createdBy: 'Alex Johnson', + createdAt: 'September 27, 2024', + }, + { + title: 'Engine Creating on Unity from PRO', + category: 'Programming', + level: 'Professional', + imageUrl: '', + duration: '15 hours', + price: 300, + description: 'Learn advanced Unity techniques from industry pros.', + isPublish: true, + createdBy: 'Michael Green', + createdAt: 'September 27, 2024', + }, + { + title: 'Introduction to Java Programming', + category: 'Programming', + level: 'Beginner', + imageUrl: '', + duration: '10 hours', + price: 120, + description: + 'Learn the fundamentals of Java programming with hands-on examples.', + isPublish: true, + createdBy: 'Sarah Brown', + createdAt: 'September 27, 2024', + }, + { + title: 'Mastering React for Frontend Development', + category: 'Web Development', + level: 'Advanced', + imageUrl: '', + duration: '25 hours', + price: 250, + description: + 'Build powerful frontend web applications using React and Chakra UI.', + isPublish: true, + createdBy: 'David Martin', + createdAt: 'September 27, 2024', + }, + { + title: 'Digital Marketing for Beginners', + category: 'Marketing', + level: 'Beginner', + imageUrl: '', + duration: '12 hours', + price: 90, + description: + 'Learn the basics of digital marketing, from SEO to social media strategies.', + isPublish: true, + createdBy: 'Emma Wilson', + createdAt: 'September 27, 2024', + }, + { + title: 'Creative Writing: Master Storytelling', + category: 'Writing', + level: 'Intermediate', + imageUrl: '', + duration: '18 hours', + price: 160, + description: + 'Develop your storytelling skills with our hands-on creative writing course.', + isPublish: true, + createdBy: 'Lucas Brown', + createdAt: 'September 27, 2024', + }, + { + title: 'Data Science with Python', + category: 'Data Science', + level: 'Advanced', + imageUrl: '', + duration: '30 hours', + price: 350, + description: + 'Explore the world of data science using Python, Numpy, and Pandas.', + isPublish: true, + createdBy: 'Sophia Davis', + createdAt: 'September 27, 2024', + }, + { + title: 'Advanced Excel for Data Analysis', + category: 'Business', + level: 'Advanced', + imageUrl: '', + duration: '22 hours', + price: 220, + description: 'Learn how to analyze data using advanced Excel techniques.', + isPublish: true, + createdBy: 'Oliver Clark', + createdAt: 'September 27, 2024', + }, + ]; + + const showMoreCourses = () => { + setVisibleCourses((prevValue) => prevValue + 4); + }; + + return ( + + + + + + Enrolled Courses + + + + + {stats.map((stat, index) => ( + + + {stat.label} + {stat.value} + + ))} + + + + {courses.map((course, index) => ( + + + {course.title} + {course.isPublish && ( + + Published + + )} + + + + + {course.title} + + {course.description} + + {course.price} USD + Level: {course.level} + + + + + + ))} + + + {visibleCourses < courses.length && ( + + + + )} + + +