From 89716c89ad7452b1ae381098f9b88d9a919a30cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Tue, 22 Nov 2022 21:07:33 -0300 Subject: [PATCH 01/27] Add role based lazy loading --- src/app/AuthenticatedApp.js | 64 ++++--------------------------------- src/app/ProfessorApp.js | 5 +++ src/app/StudentApp.js | 58 +++++++++++++++++++++++++++++++++ src/services/mocks.js | 1 + 4 files changed, 71 insertions(+), 57 deletions(-) create mode 100644 src/app/ProfessorApp.js create mode 100644 src/app/StudentApp.js diff --git a/src/app/AuthenticatedApp.js b/src/app/AuthenticatedApp.js index 80d66be..0d6b9cf 100644 --- a/src/app/AuthenticatedApp.js +++ b/src/app/AuthenticatedApp.js @@ -1,66 +1,16 @@ -import { Navigate, Route, Routes, useNavigate } from 'react-router-dom'; -import { Container } from '@mui/system'; +import { lazy } from 'react'; import { useUser } from '../context/user'; -import { useAuthState } from '../context/auth'; -import MainMenu from '../components/MainMenu'; -import Home from '../screens/Home'; -import Information from '../screens/Information'; -import Calendar from '../screens/Calendar'; -import useLayoutType from '../hooks/useLayoutType'; -import Toolbar from '../components/Toolbar'; -import Classroom from '../screens/Classroom'; -import Assignment from '../screens/Assignment'; -import Profile from '../screens/Profile'; - -import { avatarMenuOptions, menuOptions } from './data'; - -import styles from './styles'; +const ProfessorApp = lazy(() => import('./ProfessorApp')); +const StudentApp = lazy(() => import('./StudentApp')); function AuthenticatedApp() { - const navigate = useNavigate(); const { state } = useUser(); - const { logout } = useAuthState(); - const layoutType = useLayoutType(); - const { container, toolbar } = styles[layoutType]; - return ( - state && - state.user && ( - <> - - Olá, {state.user.firstName} 👋 -

- } - layoutType={layoutType} - avatarMenuOptions={avatarMenuOptions(navigate, logout)} - user={state.user} - /> - - - - } /> - } /> - } /> - } /> - - } /> - - - } /> - - } /> - } /> - } /> - - - - ) + return state && state.user && state.user.role === 'STUDENT' ? ( + + ) : ( + ); } diff --git a/src/app/ProfessorApp.js b/src/app/ProfessorApp.js new file mode 100644 index 0000000..a29f32d --- /dev/null +++ b/src/app/ProfessorApp.js @@ -0,0 +1,5 @@ +function ProfessorApp() { + return

Professor app

; +} + +export default ProfessorApp; diff --git a/src/app/StudentApp.js b/src/app/StudentApp.js new file mode 100644 index 0000000..1122807 --- /dev/null +++ b/src/app/StudentApp.js @@ -0,0 +1,58 @@ +import { Navigate, Route, Routes, useNavigate } from 'react-router-dom'; +import { Container } from '@mui/system'; +import { useAuthState } from '../context/auth'; + +import MainMenu from '../components/MainMenu'; +import Home from '../screens/Home'; +import Information from '../screens/Information'; +import Calendar from '../screens/Calendar'; +import useLayoutType from '../hooks/useLayoutType'; +import Toolbar from '../components/Toolbar'; +import Classroom from '../screens/Classroom'; +import Assignment from '../screens/Assignment'; +import Profile from '../screens/Profile'; + +import { avatarMenuOptions, menuOptions } from './data'; + +import styles from './styles'; +function StudentApp({ user, pathname }) { + const navigate = useNavigate(); + const { logout } = useAuthState(); + const layoutType = useLayoutType(); + const { container, toolbar } = styles[layoutType]; + + return ( + <> + + Olá, {user.firstName} 👋 +

+ } + layoutType={layoutType} + avatarMenuOptions={avatarMenuOptions(navigate, logout)} + user={user} + /> + + + + } /> + } /> + } /> + } /> + + } /> + + + } /> + + } /> + } /> + } /> + + + + ); +} + +export default StudentApp; diff --git a/src/services/mocks.js b/src/services/mocks.js index fce3b29..a00ebd3 100644 --- a/src/services/mocks.js +++ b/src/services/mocks.js @@ -548,6 +548,7 @@ const user = { course: 0, termsAgreed: true, year: 2018, + role: 'STUDENT', }; const authFailure = { From beb7b3eee37b01783ea042abe7bfa83442eecd14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Tue, 22 Nov 2022 21:34:02 -0300 Subject: [PATCH 02/27] Update login and register mocked users --- src/context/auth.js | 5 ++--- src/services/mocks.js | 22 ++++++++++++++++++++-- src/services/user-service.js | 17 +++++++++++------ 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/context/auth.js b/src/context/auth.js index 655b8d1..bed6354 100644 --- a/src/context/auth.js +++ b/src/context/auth.js @@ -35,10 +35,9 @@ function AuthProvider(props) { const login = (email, password) => { setState({ ...state, status: 'pending' }); - let shouldFail = email !== 'teste@teste.com' || password !== '#teste1234'; - return getUser(shouldFail).then(data => { - if (shouldFail) { + return getUser(email, password).then(data => { + if (data.message) { return setState({ status: 'error', user: null, error: data }); } else { return setState({ status: 'success', user: data, error: null }); diff --git a/src/services/mocks.js b/src/services/mocks.js index a00ebd3..a3bfb97 100644 --- a/src/services/mocks.js +++ b/src/services/mocks.js @@ -534,7 +534,7 @@ const allPeople = [ }, ]; -const user = { +const studentUser = { id: '123', ra: '0021123', username: 'ronaldosilva', @@ -551,6 +551,23 @@ const user = { role: 'STUDENT', }; +const professorUser = { + id: '321', + ra: '0021123', + username: 'cazalbe', + email: 'carlos.junior@ifmg.edu.br', + password: '#carlos1234', // TODO: Remove this! + firstName: 'Carlos', + lastName: 'Severiano', + token: 'xkhfb9458hnsdfsi9q8345bsdf9b834yr', + phone: '31111111111', + avatar: 'https://i.pravatar.cc/300?img=61', + course: 0, + termsAgreed: true, + year: 2018, + role: 'PROFESSOR', +}; + const authFailure = { message: 'Falha na autenticação', }; @@ -561,7 +578,8 @@ export { allClassroomAnnouncements, allPeople, faq, - user, + studentUser, + professorUser, authFailure, allUpcomingAssignments, }; diff --git a/src/services/user-service.js b/src/services/user-service.js index d256832..998d760 100644 --- a/src/services/user-service.js +++ b/src/services/user-service.js @@ -3,7 +3,8 @@ import { allClassrooms, allAssignments, faq, - user, + studentUser, + professorUser, authFailure, allClassroomAnnouncements, allUpcomingAssignments, @@ -84,14 +85,18 @@ const getFaq = () => }; }); -const getUser = shouldFail => +const getUser = (email, password) => sleep(300).then(() => { - if (shouldFail) { - return authFailure; + let user; + if (email === 'p@test.com' && password === 'p123') { + user = professorUser; + } else if (email === 's@test.com' && password === 's123') { + user = studentUser; } else { - window.localStorage.setItem('$USER', JSON.stringify(user)); - return user; + return authFailure; } + window.localStorage.setItem('$USER', JSON.stringify(user)); + return user; }); const registerUser = (data, shouldFail) => From 9035f7f1023ed2e32fd4ab4c730ac1c9de08e3f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Tue, 22 Nov 2022 23:32:26 -0300 Subject: [PATCH 03/27] Split only routes and fix register redirect --- src/app/AuthenticatedApp.js | 57 +++++++++++++++++++++++++++++++---- src/app/ProfessorApp.js | 5 ---- src/app/ProfessorRoutes.js | 14 +++++++++ src/app/StudentApp.js | 58 ------------------------------------ src/app/StudentRoutes.js | 30 +++++++++++++++++++ src/app/data.js | 1 + src/context/auth.js | 5 ++-- src/services/user-service.js | 15 ++++++---- 8 files changed, 107 insertions(+), 78 deletions(-) delete mode 100644 src/app/ProfessorApp.js create mode 100644 src/app/ProfessorRoutes.js delete mode 100644 src/app/StudentApp.js create mode 100644 src/app/StudentRoutes.js diff --git a/src/app/AuthenticatedApp.js b/src/app/AuthenticatedApp.js index 0d6b9cf..b4a9412 100644 --- a/src/app/AuthenticatedApp.js +++ b/src/app/AuthenticatedApp.js @@ -1,16 +1,61 @@ import { lazy } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Container } from '@mui/system'; +import { useAuthState } from '../context/auth'; import { useUser } from '../context/user'; -const ProfessorApp = lazy(() => import('./ProfessorApp')); -const StudentApp = lazy(() => import('./StudentApp')); +import MainMenu from '../components/MainMenu'; +import useLayoutType from '../hooks/useLayoutType'; +import Toolbar from '../components/Toolbar'; + +import { avatarMenuOptions, menuOptions } from './data'; + +import styles from './styles'; + +const StudentRoutes = lazy(() => import('./StudentRoutes')); +const ProfessorRoutes = lazy(() => import('./ProfessorRoutes')); function AuthenticatedApp() { + const navigate = useNavigate(); const { state } = useUser(); + const { logout } = useAuthState(); + const layoutType = useLayoutType(); + const { container, toolbar } = styles[layoutType]; - return state && state.user && state.user.role === 'STUDENT' ? ( - - ) : ( - + const routeResolver = role => { + switch (role) { + case 'STUDENT': + return ; + case 'PROFESSOR': + return ; + default: + return null; + } + }; + + return ( + state && + state.user && ( + <> + + Olá, {state.user.firstName} 👋 +

+ } + layoutType={layoutType} + avatarMenuOptions={avatarMenuOptions(navigate, logout)} + user={state.user} + /> + + + {routeResolver(state.user.role)} + + + ) ); } diff --git a/src/app/ProfessorApp.js b/src/app/ProfessorApp.js deleted file mode 100644 index a29f32d..0000000 --- a/src/app/ProfessorApp.js +++ /dev/null @@ -1,5 +0,0 @@ -function ProfessorApp() { - return

Professor app

; -} - -export default ProfessorApp; diff --git a/src/app/ProfessorRoutes.js b/src/app/ProfessorRoutes.js new file mode 100644 index 0000000..0daac42 --- /dev/null +++ b/src/app/ProfessorRoutes.js @@ -0,0 +1,14 @@ +import { Navigate, Route, Routes } from 'react-router-dom'; + +function ProfessorRoutes() { + return ( + + Home} /> + } /> + } /> + } /> + + ); +} + +export default ProfessorRoutes; diff --git a/src/app/StudentApp.js b/src/app/StudentApp.js deleted file mode 100644 index 1122807..0000000 --- a/src/app/StudentApp.js +++ /dev/null @@ -1,58 +0,0 @@ -import { Navigate, Route, Routes, useNavigate } from 'react-router-dom'; -import { Container } from '@mui/system'; -import { useAuthState } from '../context/auth'; - -import MainMenu from '../components/MainMenu'; -import Home from '../screens/Home'; -import Information from '../screens/Information'; -import Calendar from '../screens/Calendar'; -import useLayoutType from '../hooks/useLayoutType'; -import Toolbar from '../components/Toolbar'; -import Classroom from '../screens/Classroom'; -import Assignment from '../screens/Assignment'; -import Profile from '../screens/Profile'; - -import { avatarMenuOptions, menuOptions } from './data'; - -import styles from './styles'; -function StudentApp({ user, pathname }) { - const navigate = useNavigate(); - const { logout } = useAuthState(); - const layoutType = useLayoutType(); - const { container, toolbar } = styles[layoutType]; - - return ( - <> - - Olá, {user.firstName} 👋 -

- } - layoutType={layoutType} - avatarMenuOptions={avatarMenuOptions(navigate, logout)} - user={user} - /> - - - - } /> - } /> - } /> - } /> - - } /> - - - } /> - - } /> - } /> - } /> - - - - ); -} - -export default StudentApp; diff --git a/src/app/StudentRoutes.js b/src/app/StudentRoutes.js new file mode 100644 index 0000000..b3d2961 --- /dev/null +++ b/src/app/StudentRoutes.js @@ -0,0 +1,30 @@ +import { Navigate, Route, Routes } from 'react-router-dom'; + +import Home from '../screens/Home'; +import Information from '../screens/Information'; +import Calendar from '../screens/Calendar'; +import Classroom from '../screens/Classroom'; +import Assignment from '../screens/Assignment'; +import Profile from '../screens/Profile'; + +function StudentRoutes() { + return ( + + } /> + } /> + } /> + } /> + + } /> + + + } /> + + } /> + } /> + } /> + + ); +} + +export default StudentRoutes; diff --git a/src/app/data.js b/src/app/data.js index e7048f5..647eb01 100644 --- a/src/app/data.js +++ b/src/app/data.js @@ -28,6 +28,7 @@ const menuOptions = activePath => [ isActive: activePath === '/home' || activePath === '/login' || + activePath === '/register' || activePath === '/profile' || activePath === '/' || activePath.indexOf('class') !== -1 || diff --git a/src/context/auth.js b/src/context/auth.js index bed6354..fd5104b 100644 --- a/src/context/auth.js +++ b/src/context/auth.js @@ -22,10 +22,9 @@ function AuthProvider(props) { const register = data => { setState({ ...state, status: 'pending' }); - let shouldFail = false; - return registerUser(data, shouldFail).then(data => { - if (shouldFail) { + return registerUser(data).then(data => { + if (data.message) { return setState({ status: 'error', user: null, error: data }); } else { return setState({ status: 'success', user: data, error: null }); diff --git a/src/services/user-service.js b/src/services/user-service.js index 998d760..0f0fb92 100644 --- a/src/services/user-service.js +++ b/src/services/user-service.js @@ -99,15 +99,18 @@ const getUser = (email, password) => return user; }); -const registerUser = (data, shouldFail) => +const registerUser = data => sleep(300).then(() => { - if (shouldFail) { - return authFailure; + let userData; + if (data.email === 'p@test.com') { + userData = { ...data, role: 'PROFESSOR' }; + } else if (data.email === 's@test.com') { + userData = { ...data, role: 'STUDENT' }; } else { - console.log(data); - window.localStorage.setItem('$USER', JSON.stringify(data)); - return data; + return authFailure; } + window.localStorage.setItem('$USER', JSON.stringify(data)); + return userData; }); export { From 87d2bd636286f5996552a2d74e515cd98c2e69c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Tue, 22 Nov 2022 23:47:07 -0300 Subject: [PATCH 04/27] Add placeholder routes --- src/app/ProfessorRoutes.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/ProfessorRoutes.js b/src/app/ProfessorRoutes.js index 0daac42..419cb42 100644 --- a/src/app/ProfessorRoutes.js +++ b/src/app/ProfessorRoutes.js @@ -3,6 +3,9 @@ import { Navigate, Route, Routes } from 'react-router-dom'; function ProfessorRoutes() { return ( + Calendar} /> + Profile} /> + Information} /> Home} /> } /> } /> From a580f2c199aa3347a79121fb611a72cbdd11c383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Sun, 27 Nov 2022 20:54:30 -0300 Subject: [PATCH 05/27] Initial structure for professor home screen --- src/app/ProfessorRoutes.js | 3 +- src/screens/professor/Home/View.js | 123 +++++++++++++++++++++++++++ src/screens/professor/Home/index.js | 38 +++++++++ src/screens/professor/Home/styles.js | 31 +++++++ 4 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 src/screens/professor/Home/View.js create mode 100644 src/screens/professor/Home/index.js create mode 100644 src/screens/professor/Home/styles.js diff --git a/src/app/ProfessorRoutes.js b/src/app/ProfessorRoutes.js index 419cb42..cec7984 100644 --- a/src/app/ProfessorRoutes.js +++ b/src/app/ProfessorRoutes.js @@ -1,4 +1,5 @@ import { Navigate, Route, Routes } from 'react-router-dom'; +import Home from '../screens/professor/Home'; function ProfessorRoutes() { return ( @@ -6,7 +7,7 @@ function ProfessorRoutes() { Calendar} /> Profile} /> Information} /> - Home} /> + } /> } /> } /> } /> diff --git a/src/screens/professor/Home/View.js b/src/screens/professor/Home/View.js new file mode 100644 index 0000000..64046f5 --- /dev/null +++ b/src/screens/professor/Home/View.js @@ -0,0 +1,123 @@ +import { Grid, Skeleton, Stack } from '@mui/material'; +import { Container } from '@mui/system'; +import ClassCard from '../../../components/ClassCard'; +import { createArrayFrom1ToN } from '../../../utils/createArrayFrom1ToN'; +import styles from './styles'; + +function View({ layoutType, classrooms, onClickClassCard }) { + const { container, divider, assignmentsStack } = styles[layoutType]; + + if (layoutType === 'desktop') { + return ( + + +

Minhas Turmas

+ + {classrooms === null ? ( + createArrayFrom1ToN(6).map(i => ( + + )) + ) : classrooms.length !== 0 ? ( + classrooms.map(classroom => ( + onClickClassCard(classroom.id)} + /> + )) + ) : ( + +

Nenhuma sala de aula encontrada!

+
+ )} +
+
+ +

Atividades para corrigir

+

Atribuídas

+ +

Atividade 1

+

Atividade 2

+
+
+
+ ); + } else if (layoutType === 'mobile') { + return ( + +

Minhas Turmas

+ + {classrooms === null ? ( + createArrayFrom1ToN(6).map(i => ( + + )) + ) : classrooms.length !== 0 ? ( + classrooms.map(classroom => ( + onClickClassCard(classroom.id)} + /> + )) + ) : ( + +

Nenhuma sala de aula encontrada!

+
+ )} +
+

Atividades para corrigir

+

Atribuídas

+ +

Atividade 1

+

Atividade 2

+
+
+ ); + } +} + +export default View; diff --git a/src/screens/professor/Home/index.js b/src/screens/professor/Home/index.js new file mode 100644 index 0000000..91eddc3 --- /dev/null +++ b/src/screens/professor/Home/index.js @@ -0,0 +1,38 @@ +import { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useUser } from '../../../context/user'; +import { useDocumentTitle } from '../../../hooks/useDocumentTitle'; +import useLayoutType from '../../../hooks/useLayoutType'; +import View from './View'; + +function Home() { + useDocumentTitle('Página Inicial'); + const navigate = useNavigate(); + const layoutType = useLayoutType(); + const { fetchClassrooms } = useUser(); + const [classrooms, setClassrooms] = useState(null); + + useEffect(() => { + async function getClassrooms() { + const result = await fetchClassrooms(); + setClassrooms(result.data); + } + getClassrooms(); + }, [fetchClassrooms]); + + const onClickClassCard = id => { + navigate(`/class/${id}`); + }; + + console.log(classrooms); + + return ( + + ); +} + +export default Home; diff --git a/src/screens/professor/Home/styles.js b/src/screens/professor/Home/styles.js new file mode 100644 index 0000000..1a8a842 --- /dev/null +++ b/src/screens/professor/Home/styles.js @@ -0,0 +1,31 @@ +// ========== Desktop ========== +const desktopContainer = { + height: '100vh', + margin: 0, +}; + +const desktop = { + container: desktopContainer, +}; + +// ========== Mobile ========== +const mobileContainer = { + height: 'inherit', + width: '100%', + padding: '10px 20px ', + margin: 0, +}; + +const mobile = { + container: mobileContainer, +}; + +// ========== Unset ========== +const unset = { + container: null, + divider: null, + assignmentsStack: null, +}; + +const styles = { desktop, mobile, unset }; +export default styles; From bbea9b2f0fe07661c558ccb97811df0663b23343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Tue, 29 Nov 2022 11:33:03 -0300 Subject: [PATCH 06/27] Split services between student and professor roles --- src/context/user.js | 85 ++++----------------------- src/screens/Assignment/index.js | 6 +- src/screens/Classroom/index.js | 31 ++++------ src/screens/Home/index.js | 10 ++-- src/screens/Information/index.js | 6 +- src/screens/professor/Home/index.js | 6 +- src/services/professor-service.js | 5 ++ src/services/student-service.js | 37 ++++++++++++ src/services/user-service-provider.js | 28 +++++++++ 9 files changed, 108 insertions(+), 106 deletions(-) create mode 100644 src/services/professor-service.js create mode 100644 src/services/student-service.js create mode 100644 src/services/user-service-provider.js diff --git a/src/context/user.js b/src/context/user.js index 23a3df4..afb4527 100644 --- a/src/context/user.js +++ b/src/context/user.js @@ -1,23 +1,14 @@ import { createContext, useContext, useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; +import { UserServiceProvider } from '../services/user-service-provider'; import { useAuthState } from './auth'; -import { - getAllAssignments, - getAssignmentById, - getAssignmentsByClassId, - getClassroomAnnouncementsById, - getClassroomById, - getClassrooms, - getFaq, - getPeopleByClassId, - getUpcomingAssignmentsByClassId, -} from '../services/user-service'; const UserContext = createContext(); function UserProvider(props) { const { user } = useAuthState(); const { pathname } = useLocation(); + const [userService, setUserService] = useState(null); const [state, setState] = useState({ user: null, error: null, @@ -26,73 +17,23 @@ function UserProvider(props) { useEffect(() => { setState({ user, pathname }); + + async function initUserService() { + if (user) { + const instance = await UserServiceProvider.getInstance(user); + setUserService(instance); + } + } + initUserService(); }, [user, pathname]); - const fetchClassrooms = () => getClassrooms(user.id); - - const fetchAllAssignments = () => getAllAssignments(user.id); - - const fetchAssignmentById = assignmentId => getAssignmentById(assignmentId); - - const fetchAssignmentsByClassId = classId => getAssignmentsByClassId(classId); - - const fetchClassroomById = classId => getClassroomById(classId); - - const fetchFAQ = () => getFaq(); - - const fetchClassroomAnnouncements = classId => - getClassroomAnnouncementsById(classId); - - const fetchUpcomingAssignmentsByClassId = classId => - getUpcomingAssignmentsByClassId(classId); - - const fetchPeopleByClassId = classId => getPeopleByClassId(classId); - - return ( - - ); + return ; } function useUser() { - const { - state, - fetchClassrooms, - fetchAssignmentById, - fetchAllAssignments, - fetchAssignmentsByClassId, - fetchClassroomById, - fetchFAQ, - fetchClassroomAnnouncements, - fetchUpcomingAssignmentsByClassId, - fetchPeopleByClassId, - } = useContext(UserContext); + const { state, userService } = useContext(UserContext); - return { - state, - fetchClassrooms, - fetchAllAssignments, - fetchAssignmentById, - fetchAssignmentsByClassId, - fetchClassroomById, - fetchFAQ, - fetchClassroomAnnouncements, - fetchUpcomingAssignmentsByClassId, - fetchPeopleByClassId, - }; + return { state, userService }; } export { UserProvider, useUser }; diff --git a/src/screens/Assignment/index.js b/src/screens/Assignment/index.js index 3559d3b..3357c2b 100644 --- a/src/screens/Assignment/index.js +++ b/src/screens/Assignment/index.js @@ -8,14 +8,14 @@ import View from './View'; function Assignment() { const params = useParams(); const layoutType = useLayoutType(); - const { fetchAssignmentById } = useUser(); + const { userService } = useUser(); const [assignment, setAssignment] = useState(null); const dropzone = useDropzone({ maxFiles: 5 }); useEffect(() => { async function getAssignmentById(assignmentId) { document.title = 'Carregando...'; - const result = await fetchAssignmentById(assignmentId); + const result = await userService.fetchAssignmentById(assignmentId); setAssignment(result.data); } @@ -27,7 +27,7 @@ function Assignment() { getAssignmentById(params.id); updateDocumentTitle(); - }, [params, fetchAssignmentById, assignment]); + }, [params, userService, userService.fetchAssignmentById, assignment]); return ( diff --git a/src/screens/Classroom/index.js b/src/screens/Classroom/index.js index 7410515..10fc044 100644 --- a/src/screens/Classroom/index.js +++ b/src/screens/Classroom/index.js @@ -8,13 +8,7 @@ import View from './View'; function Classroom() { const params = useParams(); const layoutType = useLayoutType(); - const { - fetchClassroomById, - fetchClassroomAnnouncements, - fetchUpcomingAssignmentsByClassId, - fetchAssignmentsByClassId, - fetchPeopleByClassId, - } = useUser(); + const { userService } = useUser(); const [classroom, setClassroom] = useState(null); const [tabData, setTabData] = useState(null); const [selectedTabOption, setSelectedTabOption] = useState( @@ -23,10 +17,11 @@ function Classroom() { const fetchAndPopulateAnnouncementsTabData = useCallback(async () => { setTabData({ tab: 'announcements', state: 'loading' }); - const announcements = await fetchClassroomAnnouncements(params.id); - const upcomingAssignments = await fetchUpcomingAssignmentsByClassId( + const announcements = await userService.fetchClassroomAnnouncements( params.id ); + const upcomingAssignments = + await userService.fetchUpcomingAssignmentsByClassId(params.id); setTabData({ tab: 'announcements', @@ -34,33 +29,29 @@ function Classroom() { announcements: [...announcements.data], upcomingAssignments: [...upcomingAssignments.data], }); - }, [ - fetchClassroomAnnouncements, - fetchUpcomingAssignmentsByClassId, - params.id, - ]); + }, [userService, params.id]); const fetchAndPopulateAssignmentsTabData = useCallback(async () => { setTabData({ tab: 'assignments', state: 'loading' }); - const assignments = await fetchAssignmentsByClassId(params.id); + const assignments = await userService.fetchAssignmentsByClassId(params.id); setTabData({ tab: 'assignments', state: 'idle', assignments: [...assignments.data], }); - }, [fetchAssignmentsByClassId, params.id]); + }, [userService, params.id]); const fetchAndPopulatePoepleTabData = useCallback(async () => { setTabData({ tab: 'people', state: 'loading' }); - const people = await fetchPeopleByClassId(params.id); + const people = await userService.fetchPeopleByClassId(params.id); setTabData({ tab: 'people', state: 'idle', people: [...people.data], }); - }, [fetchPeopleByClassId, params.id]); + }, [userService, params.id]); useEffect(() => { async function getSelectedTabData() { @@ -90,7 +81,7 @@ function Classroom() { useEffect(() => { async function getClassroomById(classId) { document.title = 'Carregando...'; - const result = await fetchClassroomById(classId); + const result = await userService.fetchClassroomById(classId); setClassroom(result.data); } @@ -102,7 +93,7 @@ function Classroom() { getClassroomById(params.id); updateDocumentTitle(); - }, [fetchClassroomById, params, classroom]); + }, [userService, userService.fetchClassroomById, params, classroom]); return ( { async function getClassrooms() { - const result = await fetchClassrooms(); + const result = await userService.fetchClassrooms(); setClassrooms(result.data); } getClassrooms(); - }, [fetchClassrooms]); + }, [userService, userService.fetchClassrooms]); useEffect(() => { async function getAssignments() { - const result = await fetchAllAssignments(); + const result = await userService.fetchAllAssignments(); setAssignments(result.data); } getAssignments(); - }, [fetchAllAssignments]); + }, [userService, userService.fetchAllAssignments]); const onClickClassCard = id => { navigate(`/class/${id}`); diff --git a/src/screens/Information/index.js b/src/screens/Information/index.js index b1f7965..1042060 100644 --- a/src/screens/Information/index.js +++ b/src/screens/Information/index.js @@ -9,16 +9,16 @@ import { sectors } from './data'; function Information() { useDocumentTitle('Informações'); const layoutType = useLayoutType(); - const { fetchFAQ } = useUser(); + const { userService } = useUser(); const [faq, setFaq] = useState(null); useEffect(() => { async function getClassrooms() { - const result = await fetchFAQ(); + const result = await userService.fetchFAQ(); setFaq(result.data); } getClassrooms(); - }, [fetchFAQ]); + }, [userService, userService.fetchFAQ]); return ; } diff --git a/src/screens/professor/Home/index.js b/src/screens/professor/Home/index.js index 91eddc3..32580b6 100644 --- a/src/screens/professor/Home/index.js +++ b/src/screens/professor/Home/index.js @@ -9,16 +9,16 @@ function Home() { useDocumentTitle('Página Inicial'); const navigate = useNavigate(); const layoutType = useLayoutType(); - const { fetchClassrooms } = useUser(); + const { userService } = useUser(); const [classrooms, setClassrooms] = useState(null); useEffect(() => { async function getClassrooms() { - const result = await fetchClassrooms(); + const result = await userService.fetchClassrooms(); setClassrooms(result.data); } getClassrooms(); - }, [fetchClassrooms]); + }, [userService, userService.fetchClassrooms]); const onClickClassCard = id => { navigate(`/class/${id}`); diff --git a/src/services/professor-service.js b/src/services/professor-service.js new file mode 100644 index 0000000..5589b51 --- /dev/null +++ b/src/services/professor-service.js @@ -0,0 +1,5 @@ +export default class ProfessorService { + constructor(user) { + this.user = user; + } +} diff --git a/src/services/student-service.js b/src/services/student-service.js new file mode 100644 index 0000000..5b9f297 --- /dev/null +++ b/src/services/student-service.js @@ -0,0 +1,37 @@ +import { + getAllAssignments, + getAssignmentById, + getAssignmentsByClassId, + getClassroomAnnouncementsById, + getClassroomById, + getClassrooms, + getFaq, + getPeopleByClassId, + getUpcomingAssignmentsByClassId, +} from './user-service'; + +export default class StudentService { + constructor(user) { + this.user = user; + } + + fetchClassrooms = () => getClassrooms(this.user.id); + + fetchAllAssignments = () => getAllAssignments(this.user.id); + + fetchAssignmentById = assignmentId => getAssignmentById(assignmentId); + + fetchAssignmentsByClassId = classId => getAssignmentsByClassId(classId); + + fetchClassroomById = classId => getClassroomById(classId); + + fetchFAQ = () => getFaq(); + + fetchClassroomAnnouncements = classId => + getClassroomAnnouncementsById(classId); + + fetchUpcomingAssignmentsByClassId = classId => + getUpcomingAssignmentsByClassId(classId); + + fetchPeopleByClassId = classId => getPeopleByClassId(classId); +} diff --git a/src/services/user-service-provider.js b/src/services/user-service-provider.js new file mode 100644 index 0000000..98224fc --- /dev/null +++ b/src/services/user-service-provider.js @@ -0,0 +1,28 @@ +export const UserServiceProvider = (function () { + let instance; + + async function createInstance(user) { + if (user.role === 'STUDENT') { + const service = await import('../services/student-service'); + if (service) { + return new service.default(user); + } + } else if (user.role === 'PROFESSOR') { + const service = await import('../services/professor-service'); + if (service) { + return new service.default(user); + } + } else { + throw new Error('Invalid Role!'); + } + } + + return { + getInstance: function (user) { + if (!instance) { + instance = createInstance(user); + } + return instance; + }, + }; +})(); From 2c181d49b06d79233f8f4f7064d5f842856cdbc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Tue, 29 Nov 2022 11:36:55 -0300 Subject: [PATCH 07/27] Use switch case to create instance --- src/services/user-service-provider.js | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/services/user-service-provider.js b/src/services/user-service-provider.js index 98224fc..31f51cf 100644 --- a/src/services/user-service-provider.js +++ b/src/services/user-service-provider.js @@ -2,18 +2,21 @@ export const UserServiceProvider = (function () { let instance; async function createInstance(user) { - if (user.role === 'STUDENT') { - const service = await import('../services/student-service'); - if (service) { - return new service.default(user); - } - } else if (user.role === 'PROFESSOR') { - const service = await import('../services/professor-service'); - if (service) { - return new service.default(user); - } - } else { - throw new Error('Invalid Role!'); + switch (user.role) { + case 'STUDENT': + const studentService = await import('../services/student-service'); + if (studentService) { + return new studentService.default(user); + } + break; + case 'PROFESSOR': + const professorService = await import('../services/professor-service'); + if (professorService) { + return new professorService.default(user); + } + break; + default: + throw new Error('Invalid Role!'); } } From b48114b8bb07a048f0228317caee10c2a4a11b00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Tue, 29 Nov 2022 11:41:23 -0300 Subject: [PATCH 08/27] Rename and move files --- src/context/auth.js | 2 +- src/context/user.js | 2 +- src/services/{professor-service.js => professor.js} | 0 src/services/{user-service-provider.js => provider.js} | 4 ++-- src/services/{student-service.js => student.js} | 2 +- src/{services/user-service.js => utils/mocks/api.js} | 4 ++-- src/{services/mocks.js => utils/mocks/responses.js} | 0 7 files changed, 7 insertions(+), 7 deletions(-) rename src/services/{professor-service.js => professor.js} (100%) rename src/services/{user-service-provider.js => provider.js} (79%) rename src/services/{student-service.js => student.js} (96%) rename src/{services/user-service.js => utils/mocks/api.js} (98%) rename src/{services/mocks.js => utils/mocks/responses.js} (100%) diff --git a/src/context/auth.js b/src/context/auth.js index fd5104b..3cf7d40 100644 --- a/src/context/auth.js +++ b/src/context/auth.js @@ -1,5 +1,5 @@ import { createContext, useContext, useEffect, useState } from 'react'; -import { getUser, registerUser } from '../services/user-service'; +import { getUser, registerUser } from '../utils/mocks/api'; const AuthContext = createContext(); diff --git a/src/context/user.js b/src/context/user.js index afb4527..4225201 100644 --- a/src/context/user.js +++ b/src/context/user.js @@ -1,6 +1,6 @@ import { createContext, useContext, useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; -import { UserServiceProvider } from '../services/user-service-provider'; +import { UserServiceProvider } from '../services/provider'; import { useAuthState } from './auth'; const UserContext = createContext(); diff --git a/src/services/professor-service.js b/src/services/professor.js similarity index 100% rename from src/services/professor-service.js rename to src/services/professor.js diff --git a/src/services/user-service-provider.js b/src/services/provider.js similarity index 79% rename from src/services/user-service-provider.js rename to src/services/provider.js index 31f51cf..30ddf81 100644 --- a/src/services/user-service-provider.js +++ b/src/services/provider.js @@ -4,13 +4,13 @@ export const UserServiceProvider = (function () { async function createInstance(user) { switch (user.role) { case 'STUDENT': - const studentService = await import('../services/student-service'); + const studentService = await import('./student'); if (studentService) { return new studentService.default(user); } break; case 'PROFESSOR': - const professorService = await import('../services/professor-service'); + const professorService = await import('./professor'); if (professorService) { return new professorService.default(user); } diff --git a/src/services/student-service.js b/src/services/student.js similarity index 96% rename from src/services/student-service.js rename to src/services/student.js index 5b9f297..1fcd107 100644 --- a/src/services/student-service.js +++ b/src/services/student.js @@ -8,7 +8,7 @@ import { getFaq, getPeopleByClassId, getUpcomingAssignmentsByClassId, -} from './user-service'; +} from '../utils/mocks/api'; export default class StudentService { constructor(user) { diff --git a/src/services/user-service.js b/src/utils/mocks/api.js similarity index 98% rename from src/services/user-service.js rename to src/utils/mocks/api.js index 0f0fb92..8735207 100644 --- a/src/services/user-service.js +++ b/src/utils/mocks/api.js @@ -1,4 +1,4 @@ -import { sleep } from '../utils/sleep'; +import { sleep } from '../sleep'; import { allClassrooms, allAssignments, @@ -9,7 +9,7 @@ import { allClassroomAnnouncements, allUpcomingAssignments, allPeople, -} from './mocks'; +} from './responses'; const getClassrooms = userId => sleep(300).then(() => { diff --git a/src/services/mocks.js b/src/utils/mocks/responses.js similarity index 100% rename from src/services/mocks.js rename to src/utils/mocks/responses.js From 38284ec7bf582e34edf8a2bd2276f6d81a62cd51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Tue, 29 Nov 2022 18:08:14 -0300 Subject: [PATCH 09/27] Update getInstance --- src/services/provider.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/provider.js b/src/services/provider.js index 30ddf81..038bf5c 100644 --- a/src/services/provider.js +++ b/src/services/provider.js @@ -21,9 +21,9 @@ export const UserServiceProvider = (function () { } return { - getInstance: function (user) { + getInstance: async function (user) { if (!instance) { - instance = createInstance(user); + instance = await createInstance(user); } return instance; }, From 2b1302ad3a6b90e9b3387339330b6ea6e6249a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Tue, 29 Nov 2022 18:24:13 -0300 Subject: [PATCH 10/27] Structure mocks in different apis --- src/context/auth.js | 6 +- src/services/professor.js | 4 + src/services/student.js | 32 ++---- src/utils/mocks/api.js | 230 +++++++++++++++++++------------------- 4 files changed, 136 insertions(+), 136 deletions(-) diff --git a/src/context/auth.js b/src/context/auth.js index 3cf7d40..59ccd82 100644 --- a/src/context/auth.js +++ b/src/context/auth.js @@ -1,5 +1,5 @@ import { createContext, useContext, useEffect, useState } from 'react'; -import { getUser, registerUser } from '../utils/mocks/api'; +import { CommonApi } from '../utils/mocks/api'; const AuthContext = createContext(); @@ -23,7 +23,7 @@ function AuthProvider(props) { const register = data => { setState({ ...state, status: 'pending' }); - return registerUser(data).then(data => { + return CommonApi.registerUser(data).then(data => { if (data.message) { return setState({ status: 'error', user: null, error: data }); } else { @@ -35,7 +35,7 @@ function AuthProvider(props) { const login = (email, password) => { setState({ ...state, status: 'pending' }); - return getUser(email, password).then(data => { + return CommonApi.getUser(email, password).then(data => { if (data.message) { return setState({ status: 'error', user: null, error: data }); } else { diff --git a/src/services/professor.js b/src/services/professor.js index 5589b51..9820227 100644 --- a/src/services/professor.js +++ b/src/services/professor.js @@ -1,5 +1,9 @@ +import { ProfessorApi } from '../utils/mocks/api'; + export default class ProfessorService { constructor(user) { this.user = user; } + + fetchClassrooms = () => ProfessorApi.getClassrooms(this.user.id); } diff --git a/src/services/student.js b/src/services/student.js index 1fcd107..294edd3 100644 --- a/src/services/student.js +++ b/src/services/student.js @@ -1,37 +1,29 @@ -import { - getAllAssignments, - getAssignmentById, - getAssignmentsByClassId, - getClassroomAnnouncementsById, - getClassroomById, - getClassrooms, - getFaq, - getPeopleByClassId, - getUpcomingAssignmentsByClassId, -} from '../utils/mocks/api'; +import { StudentApi } from '../utils/mocks/api'; export default class StudentService { constructor(user) { this.user = user; } - fetchClassrooms = () => getClassrooms(this.user.id); + fetchClassrooms = () => StudentApi.getClassrooms(this.user.id); - fetchAllAssignments = () => getAllAssignments(this.user.id); + fetchAllAssignments = () => StudentApi.getAllAssignments(this.user.id); - fetchAssignmentById = assignmentId => getAssignmentById(assignmentId); + fetchAssignmentById = assignmentId => + StudentApi.getAssignmentById(assignmentId); - fetchAssignmentsByClassId = classId => getAssignmentsByClassId(classId); + fetchAssignmentsByClassId = classId => + StudentApi.getAssignmentsByClassId(classId); - fetchClassroomById = classId => getClassroomById(classId); + fetchClassroomById = classId => StudentApi.getClassroomById(classId); - fetchFAQ = () => getFaq(); + fetchFAQ = () => StudentApi.getFaq(); fetchClassroomAnnouncements = classId => - getClassroomAnnouncementsById(classId); + StudentApi.getClassroomAnnouncementsById(classId); fetchUpcomingAssignmentsByClassId = classId => - getUpcomingAssignmentsByClassId(classId); + StudentApi.getUpcomingAssignmentsByClassId(classId); - fetchPeopleByClassId = classId => getPeopleByClassId(classId); + fetchPeopleByClassId = classId => StudentApi.getPeopleByClassId(classId); } diff --git a/src/utils/mocks/api.js b/src/utils/mocks/api.js index 8735207..9f31001 100644 --- a/src/utils/mocks/api.js +++ b/src/utils/mocks/api.js @@ -11,118 +11,122 @@ import { allPeople, } from './responses'; -const getClassrooms = userId => - sleep(300).then(() => { - console.log('Get classrooms ' + userId); - return { - data: allClassrooms, - }; - }); +const CommonApi = { + getUser: (email, password) => + sleep(300).then(() => { + let user; + if (email === 'p@test.com' && password === 'p123') { + user = professorUser; + } else if (email === 's@test.com' && password === 's123') { + user = studentUser; + } else { + return authFailure; + } + window.localStorage.setItem('$USER', JSON.stringify(user)); + return user; + }), -const getClassroomById = classId => - sleep(300).then(() => { - console.log('Get classroom by id ' + classId); - return { - data: allClassrooms.filter(c => c.id === classId)[0], - }; - }); - -const getClassroomAnnouncementsById = classId => - sleep(300).then(() => { - console.log('Get classroon announcements by id ' + classId); - return { - data: allClassroomAnnouncements.filter(c => c.classroom.id === classId), - }; - }); - -const getUpcomingAssignmentsByClassId = classId => - sleep(300).then(() => { - console.log('Getting upcoming assignments by class id ' + classId); - return { - data: allUpcomingAssignments.filter( - a => a.classrooms.filter(c => c.id === classId)[0] - ), - }; - }); - -const getAllAssignments = userId => - sleep(400).then(() => { - console.log('Getting all assignments ' + userId); - return { - data: allAssignments, - }; - }); - -const getAssignmentById = assignmentId => - sleep(400).then(() => { - console.log('Getting assignment by id ' + assignmentId); - return { - data: allAssignments.filter(a => a.id === assignmentId)[0], - }; - }); - -const getAssignmentsByClassId = classId => - sleep(300).then(() => { - console.log('Getting assignments by class id ' + classId); - return { - data: allAssignments.filter(a => a.classrooms[0].id === classId), - }; - }); - -const getPeopleByClassId = classId => - sleep(400).then(() => { - console.log('Getting people by class id ' + classId); - return { - data: allPeople.filter(p => p.classes[0].id === classId), - }; - }); - -const getFaq = () => - sleep(300).then(() => { - console.log('Fetching FAQ...'); - return { - data: faq, - }; - }); - -const getUser = (email, password) => - sleep(300).then(() => { - let user; - if (email === 'p@test.com' && password === 'p123') { - user = professorUser; - } else if (email === 's@test.com' && password === 's123') { - user = studentUser; - } else { - return authFailure; - } - window.localStorage.setItem('$USER', JSON.stringify(user)); - return user; - }); - -const registerUser = data => - sleep(300).then(() => { - let userData; - if (data.email === 'p@test.com') { - userData = { ...data, role: 'PROFESSOR' }; - } else if (data.email === 's@test.com') { - userData = { ...data, role: 'STUDENT' }; - } else { - return authFailure; - } - window.localStorage.setItem('$USER', JSON.stringify(data)); - return userData; - }); - -export { - getClassrooms, - getClassroomById, - getAllAssignments, - getAssignmentById, - getAssignmentsByClassId, - getClassroomAnnouncementsById, - getUpcomingAssignmentsByClassId, - getPeopleByClassId, - getFaq, - getUser, - registerUser, + registerUser: data => + sleep(300).then(() => { + let userData; + if (data.email === 'p@test.com') { + userData = { ...data, role: 'PROFESSOR' }; + } else if (data.email === 's@test.com') { + userData = { ...data, role: 'STUDENT' }; + } else { + return authFailure; + } + window.localStorage.setItem('$USER', JSON.stringify(data)); + return userData; + }), }; + +const StudentApi = { + ...CommonApi, + getClassrooms: userId => + sleep(300).then(() => { + console.log('Get classrooms ' + userId); + return { + data: allClassrooms, + }; + }), + + getClassroomById: classId => + sleep(300).then(() => { + console.log('Get classroom by id ' + classId); + return { + data: allClassrooms.filter(c => c.id === classId)[0], + }; + }), + + getClassroomAnnouncementsById: classId => + sleep(300).then(() => { + console.log('Get classroon announcements by id ' + classId); + return { + data: allClassroomAnnouncements.filter(c => c.classroom.id === classId), + }; + }), + + getUpcomingAssignmentsByClassId: classId => + sleep(300).then(() => { + console.log('Getting upcoming assignments by class id ' + classId); + return { + data: allUpcomingAssignments.filter( + a => a.classrooms.filter(c => c.id === classId)[0] + ), + }; + }), + + getAllAssignments: userId => + sleep(400).then(() => { + console.log('Getting all assignments ' + userId); + return { + data: allAssignments, + }; + }), + + getAssignmentById: assignmentId => + sleep(400).then(() => { + console.log('Getting assignment by id ' + assignmentId); + return { + data: allAssignments.filter(a => a.id === assignmentId)[0], + }; + }), + + getAssignmentsByClassId: classId => + sleep(300).then(() => { + console.log('Getting assignments by class id ' + classId); + return { + data: allAssignments.filter(a => a.classrooms[0].id === classId), + }; + }), + + getPeopleByClassId: classId => + sleep(400).then(() => { + console.log('Getting people by class id ' + classId); + return { + data: allPeople.filter(p => p.classes[0].id === classId), + }; + }), + + getFaq: () => + sleep(300).then(() => { + console.log('Fetching FAQ...'); + return { + data: faq, + }; + }), +}; + +const ProfessorApi = { + ...CommonApi, + getClassrooms: userId => + sleep(300).then(() => { + console.log('Get classrooms ' + userId); + return { + data: allClassrooms, + }; + }), +}; + +export { StudentApi, ProfessorApi, CommonApi }; From faf7890efa1b2393f8f1725ae81f46ed8fb6a63b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Wed, 30 Nov 2022 16:03:18 -0300 Subject: [PATCH 11/27] Add custom class card for professor --- src/components/ClassCard/index.js | 75 ++++++++++++++++------------ src/screens/professor/Home/View.js | 2 + src/screens/professor/Home/styles.js | 11 ++++ src/utils/mocks/api.js | 3 +- src/utils/mocks/responses.js | 36 +++++++++++++ 5 files changed, 94 insertions(+), 33 deletions(-) diff --git a/src/components/ClassCard/index.js b/src/components/ClassCard/index.js index 79bf495..6c78254 100644 --- a/src/components/ClassCard/index.js +++ b/src/components/ClassCard/index.js @@ -17,6 +17,7 @@ function ClassCard({ title, color, teachers, + course, layoutType, onClick, }) { @@ -45,23 +46,30 @@ function ClassCard({ > {title} - - - {teachers.map(t => ( - - ))} - - t.name).join(', ')}> - - {teachers.map(t => t.name).join(', ')} - - - + {teachers && ( + + + {teachers.map(t => ( + + ))} + + t.name).join(', ')}> + + {teachers.map(t => t.name).join(', ')} + + + + )} + {course && {course}} @@ -82,21 +90,24 @@ function ClassCard({ > {title} - - - {teachers.map(t => ( - - ))} - - - {teachers.map(t => t.name).join(', ')} - - + {teachers && ( + + + {teachers.map(t => ( + + ))} + + + {teachers.map(t => t.name).join(', ')} + + + )} + {course && {course}} diff --git a/src/screens/professor/Home/View.js b/src/screens/professor/Home/View.js index 64046f5..c7cb6a2 100644 --- a/src/screens/professor/Home/View.js +++ b/src/screens/professor/Home/View.js @@ -30,6 +30,7 @@ function View({ layoutType, classrooms, onClickClassCard }) { title={classroom.name} color={classroom.color} teachers={classroom.teachers} + course={classroom.course} layoutType={layoutType} onClick={() => onClickClassCard(classroom.id)} /> @@ -92,6 +93,7 @@ function View({ layoutType, classrooms, onClickClassCard }) { title={classroom.name} color={classroom.color} teachers={classroom.teachers} + course={classroom.course} layoutType={layoutType} onClick={() => onClickClassCard(classroom.id)} /> diff --git a/src/screens/professor/Home/styles.js b/src/screens/professor/Home/styles.js index 1a8a842..cd02a41 100644 --- a/src/screens/professor/Home/styles.js +++ b/src/screens/professor/Home/styles.js @@ -4,8 +4,13 @@ const desktopContainer = { margin: 0, }; +const desktopDivider = { + borderLeft: '4px solid #CFCFCF', +}; + const desktop = { container: desktopContainer, + divider: desktopDivider, }; // ========== Mobile ========== @@ -16,8 +21,14 @@ const mobileContainer = { margin: 0, }; +const mobileDivider = { + borderTop: '2px solid #CFCFCF', + paddingTop: '15px', +}; + const mobile = { container: mobileContainer, + divider: mobileDivider, }; // ========== Unset ========== diff --git a/src/utils/mocks/api.js b/src/utils/mocks/api.js index 9f31001..55e4889 100644 --- a/src/utils/mocks/api.js +++ b/src/utils/mocks/api.js @@ -9,6 +9,7 @@ import { allClassroomAnnouncements, allUpcomingAssignments, allPeople, + professorClassrooms, } from './responses'; const CommonApi = { @@ -124,7 +125,7 @@ const ProfessorApi = { sleep(300).then(() => { console.log('Get classrooms ' + userId); return { - data: allClassrooms, + data: professorClassrooms, }; }), }; diff --git a/src/utils/mocks/responses.js b/src/utils/mocks/responses.js index a3bfb97..661a166 100644 --- a/src/utils/mocks/responses.js +++ b/src/utils/mocks/responses.js @@ -95,6 +95,41 @@ const allClassrooms = [ }, ]; +const professorClassrooms = [ + { + id: '321', + name: 'Introdução à Ciência de Dados', + abbreviation: 'ICD', + color: '#006FF2', + course: 'BSI 2020', + appointmentSlots: [ + { weekDay: 'Quarta-feira', start: '10:00', end: '11:40' }, + { weekDay: 'Sexta-feira', start: '10:00', end: '11:40' }, + ], + }, + { + id: '123', + name: 'Teoria dos Grafos', + abbreviation: 'TDG', + color: '#d30000', + course: 'BSI 2018', + appointmentSlots: [ + { weekDay: 'Quarta-feira', start: '11:00', end: '12:00' }, + { weekDay: 'Segunda-feira', start: '10:00', end: '11:40' }, + ], + }, + { + id: '123', + name: 'Matemática Discreta', + abbreviation: 'MD', + color: '#149b00', + course: 'BSI 2020', + appointmentSlots: [ + { weekDay: 'Quarta-feira', start: '9:00', end: '10:00' }, + ], + }, +]; + const allAssignments = [ { id: '5435', @@ -582,4 +617,5 @@ export { professorUser, authFailure, allUpcomingAssignments, + professorClassrooms, }; From 2dee31526112782354a0cc39d453a42333dc41a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Wed, 30 Nov 2022 21:26:11 -0300 Subject: [PATCH 12/27] Add assignments to review card --- src/components/AssignmentCard/index.js | 47 ++++++++++--- src/screens/professor/Home/View.js | 91 +++++++++++++++++++++++--- src/screens/professor/Home/index.js | 15 ++++- src/services/professor.js | 3 + src/utils/mocks/api.js | 8 +++ src/utils/mocks/responses.js | 77 +++++++++++++++++++++- 6 files changed, 223 insertions(+), 18 deletions(-) diff --git a/src/components/AssignmentCard/index.js b/src/components/AssignmentCard/index.js index e893b3d..fc5758b 100644 --- a/src/components/AssignmentCard/index.js +++ b/src/components/AssignmentCard/index.js @@ -18,6 +18,10 @@ function AssignmentCard({ classrooms, dueDate, scores, + deliveredByStudents, + reviewed, + total, + isAssignmentToReview, layoutType, onClick, }) { @@ -61,16 +65,30 @@ function AssignmentCard({ {classrooms.map(c => c.name).join(', ')} + Data de entrega: {' '} {capitalizeFirstLetter( dayjs(dueDate).format('dddd, DD/MM | HH:mm[h]') )} - - Valor: - {scores.map(s => s.value).join(', ')} pts - + {deliveredByStudents >= 0 && total && ( + + Entregues: {' '} + {`${deliveredByStudents} de ${total}`} + + )} + {reviewed >= 0 && total && ( + + Corrigidas: {`${reviewed} de ${total}`} + + )} + {!isAssignmentToReview && ( + + Valor: + {scores.map(s => s.value).join(', ')} pts + + )} @@ -110,10 +128,23 @@ function AssignmentCard({ dayjs(dueDate).format('dddd, DD/MM | HH:mm[h]') )} - - Valor: - {scores.map(s => s.value).join(', ')} pts - + {deliveredByStudents >= 0 && total && ( + + Entregues: {' '} + {`${deliveredByStudents} de ${total}`} + + )} + {reviewed >= 0 && total && ( + + Corrigidas: {`${reviewed} de ${total}`} + + )} + {!isAssignmentToReview && ( + + Valor: + {scores.map(s => s.value).join(', ')} pts + + )} diff --git a/src/screens/professor/Home/View.js b/src/screens/professor/Home/View.js index c7cb6a2..0651aea 100644 --- a/src/screens/professor/Home/View.js +++ b/src/screens/professor/Home/View.js @@ -1,11 +1,18 @@ import { Grid, Skeleton, Stack } from '@mui/material'; import { Container } from '@mui/system'; +import AssignmentCard from '../../../components/AssignmentCard'; import ClassCard from '../../../components/ClassCard'; import { createArrayFrom1ToN } from '../../../utils/createArrayFrom1ToN'; import styles from './styles'; -function View({ layoutType, classrooms, onClickClassCard }) { - const { container, divider, assignmentsStack } = styles[layoutType]; +function View({ + layoutType, + classrooms, + assignmentsToReview, + onClickClassCard, +}) { + const { container, divider, assignmentsStack, onClickAssignmentCard } = + styles[layoutType]; if (layoutType === 'desktop') { return ( @@ -51,7 +58,6 @@ function View({ layoutType, classrooms, onClickClassCard }) {

Atividades para corrigir

-

Atribuídas

-

Atividade 1

-

Atividade 2

+ {assignmentsToReview === null ? ( + createArrayFrom1ToN(6).map(i => ( + + )) + ) : assignmentsToReview.length !== 0 ? ( + assignmentsToReview.map(assignment => ( + onClickAssignmentCard(assignment.id)} + /> + )) + ) : ( + +

Nenhuma atividade encontrada!

+
+ )}
@@ -105,7 +146,6 @@ function View({ layoutType, classrooms, onClickClassCard }) { )}

Atividades para corrigir

-

Atribuídas

-

Atividade 1

-

Atividade 2

+ {assignmentsToReview === null ? ( + createArrayFrom1ToN(6).map(i => ( + + )) + ) : assignmentsToReview.length !== 0 ? ( + assignmentsToReview.map(assignment => ( + onClickAssignmentCard(assignment.id)} + /> + )) + ) : ( + +

Nenhuma atividade encontrada!

+
+ )}
); diff --git a/src/screens/professor/Home/index.js b/src/screens/professor/Home/index.js index 32580b6..d16c20d 100644 --- a/src/screens/professor/Home/index.js +++ b/src/screens/professor/Home/index.js @@ -11,6 +11,7 @@ function Home() { const layoutType = useLayoutType(); const { userService } = useUser(); const [classrooms, setClassrooms] = useState(null); + const [assignmentsToReview, setAssignmentsToReview] = useState(null); useEffect(() => { async function getClassrooms() { @@ -20,17 +21,29 @@ function Home() { getClassrooms(); }, [userService, userService.fetchClassrooms]); + useEffect(() => { + async function getAssignmentsToReview() { + const result = await userService.fetchAssignmentsToReview(); + setAssignmentsToReview(result.data); + } + getAssignmentsToReview(); + }, [userService, userService.fetchAllAssignments]); + const onClickClassCard = id => { navigate(`/class/${id}`); }; - console.log(classrooms); + const onClickAssignmentCard = id => { + navigate(`/assignment/${id}`); + }; return ( ); } diff --git a/src/services/professor.js b/src/services/professor.js index 9820227..3599f67 100644 --- a/src/services/professor.js +++ b/src/services/professor.js @@ -6,4 +6,7 @@ export default class ProfessorService { } fetchClassrooms = () => ProfessorApi.getClassrooms(this.user.id); + + fetchAssignmentsToReview = () => + ProfessorApi.getAssignmentsToReview(this.user.id); } diff --git a/src/utils/mocks/api.js b/src/utils/mocks/api.js index 55e4889..08009c0 100644 --- a/src/utils/mocks/api.js +++ b/src/utils/mocks/api.js @@ -10,6 +10,7 @@ import { allUpcomingAssignments, allPeople, professorClassrooms, + assignmentsToReview, } from './responses'; const CommonApi = { @@ -128,6 +129,13 @@ const ProfessorApi = { data: professorClassrooms, }; }), + getAssignmentsToReview: userId => + sleep(400).then(() => { + console.log('Getting assignments to review' + userId); + return { + data: assignmentsToReview, + }; + }), }; export { StudentApi, ProfessorApi, CommonApi }; diff --git a/src/utils/mocks/responses.js b/src/utils/mocks/responses.js index 661a166..25eadd3 100644 --- a/src/utils/mocks/responses.js +++ b/src/utils/mocks/responses.js @@ -119,7 +119,7 @@ const professorClassrooms = [ ], }, { - id: '123', + id: '666', name: 'Matemática Discreta', abbreviation: 'MD', color: '#149b00', @@ -344,6 +344,80 @@ const allClassroomAnnouncements = [ }, ]; +const assignmentsToReview = [ + { + id: '0123', + type: 'assessment', + title: + 'Prova 1 - Armazenamento de Dados. Python em CD. Armazenamento Analítico', + dueDate: '2022-07-01 23:59', + scores: [ + { + classroomId: '321', + value: 30, + }, + ], + classrooms: professorClassrooms.filter(c => c.id === '321'), + status: 'OPEN', + deliveredByStudents: 10, + reviewed: 6, + total: 30, + }, + { + id: '0128', + type: 'assessment', + title: + 'Prova 2 - Visualização de Dados. Matemática e Estatística em CD. Análise de Dados', + dueDate: '2022-09-01 23:59', + scores: [ + { + classroomId: '321', + value: 30, + }, + ], + classrooms: professorClassrooms.filter(c => c.id === '321'), + status: 'CLOSED', + deliveredByStudents: 30, + reviewed: 1, + total: 30, + }, + { + id: '0129', + type: 'assessment', + title: 'Lista de Exercícios 1 - Caminhos e circuitos', + dueDate: '2022-09-01 23:59', + scores: [ + { + classroomId: '123', + value: 30, + }, + ], + classrooms: professorClassrooms.filter(c => c.id === '123'), + status: 'CLOSED', + deliveredByStudents: 30, + reviewed: 0, + total: 30, + }, + + { + id: '0130', + type: 'assessment', + title: 'Lista de Exercícios 2 - Tabela verdade', + dueDate: '2022-09-01 23:59', + scores: [ + { + classroomId: '666', + value: 30, + }, + ], + classrooms: professorClassrooms.filter(c => c.id === '666'), + status: 'OPEN', + deliveredByStudents: 0, + reviewed: 0, + total: 30, + }, +]; + const allUpcomingAssignments = [ { id: '5435', @@ -618,4 +692,5 @@ export { authFailure, allUpcomingAssignments, professorClassrooms, + assignmentsToReview, }; From 7377defce834c7352fd1a847ee96a0a367bd1376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Tue, 13 Dec 2022 21:29:44 -0300 Subject: [PATCH 13/27] Add initial structure for professor classroom screen --- src/app/ProfessorRoutes.js | 4 + .../Classroom/AnnouncementsTab/index.js | 268 ++++++++++++++++++ .../Classroom/AnnouncementsTab/styles.js | 48 ++++ .../Classroom/AssignmentsTab/index.js | 34 +++ .../Classroom/AssignmentsTab/styles.js | 0 .../professor/Classroom/Header/index.js | 62 ++++ .../professor/Classroom/Header/styles.js | 90 ++++++ src/screens/professor/Classroom/View.js | 39 +++ src/screens/professor/Classroom/index.js | 101 +++++++ src/screens/professor/Classroom/styles.js | 34 +++ src/screens/professor/Classroom/tabOptions.js | 20 ++ src/services/professor.js | 8 + src/utils/mocks/api.js | 46 ++- 13 files changed, 730 insertions(+), 24 deletions(-) create mode 100644 src/screens/professor/Classroom/AnnouncementsTab/index.js create mode 100644 src/screens/professor/Classroom/AnnouncementsTab/styles.js create mode 100644 src/screens/professor/Classroom/AssignmentsTab/index.js create mode 100644 src/screens/professor/Classroom/AssignmentsTab/styles.js create mode 100644 src/screens/professor/Classroom/Header/index.js create mode 100644 src/screens/professor/Classroom/Header/styles.js create mode 100644 src/screens/professor/Classroom/View.js create mode 100644 src/screens/professor/Classroom/index.js create mode 100644 src/screens/professor/Classroom/styles.js create mode 100644 src/screens/professor/Classroom/tabOptions.js diff --git a/src/app/ProfessorRoutes.js b/src/app/ProfessorRoutes.js index cec7984..ced0d15 100644 --- a/src/app/ProfessorRoutes.js +++ b/src/app/ProfessorRoutes.js @@ -1,4 +1,5 @@ import { Navigate, Route, Routes } from 'react-router-dom'; +import Classroom from '../screens/professor/Classroom'; import Home from '../screens/professor/Home'; function ProfessorRoutes() { @@ -6,6 +7,9 @@ function ProfessorRoutes() { Calendar} /> Profile} /> + + } /> + Information} /> } /> } /> diff --git a/src/screens/professor/Classroom/AnnouncementsTab/index.js b/src/screens/professor/Classroom/AnnouncementsTab/index.js new file mode 100644 index 0000000..4ca8c01 --- /dev/null +++ b/src/screens/professor/Classroom/AnnouncementsTab/index.js @@ -0,0 +1,268 @@ +import { + Button, + Card, + Container, + Grid, + Link, + Skeleton, + Stack, + Typography, +} from '@mui/material'; +import AnnouncementCard from '../../../../components/AnnouncementCard'; + +import styles from './styles'; +import jitsiLogo from '../../../../assets/jitsi.svg'; +import { createArrayFrom1ToN } from '../../../../utils/createArrayFrom1ToN'; + +function AnnouncementsTab({ layoutType, announcementsTabData, classroom }) { + const { container, emptyStateContainer } = styles[layoutType]; + + const layoutResolver = (state, layoutType) => { + if (layoutType === 'desktop') { + switch (state) { + case 'loading': + return ( + + + {createArrayFrom1ToN(3).map(i => ( + + ))} + + + {createArrayFrom1ToN(4).map(i => ( + + ))} + + + ); + + case 'idle': + return ( + + + + + + + Jitsi Meet +

Jitsi

+
+ + +
+
+ + +

Próximas Atividades

+
+
+ + +

+ Horários de Atendimento +

+ {classroom.appointmentSlots.map((appts, index) => ( + + {appts.weekDay}, {appts.start}h - {appts.end}h + + ))} +
+
+
+
+ + + {announcementsTabData.announcements.length !== 0 ? ( + announcementsTabData.announcements.map(announcement => ( + + )) + ) : ( + +

Nenhum comunicado encontrado!

+
+ )} +
+
+
+ ); + + case 'gone': + return null; + + default: + return null; + } + } else if (layoutType === 'mobile') { + switch (state) { + case 'loading': + return ( + + {createArrayFrom1ToN(3).map(i => ( + + ))} + {createArrayFrom1ToN(4).map(i => ( + + ))} + + ); + + case 'idle': + return ( + + + + + + Jitsi Meet +

Jitsi

+
+ + +
+
+ + +

Próximas Atividades

+
+
+ + +

Horários de Atendimento

+ {classroom.appointmentSlots.map((appts, index) => ( + + {appts.weekDay}, {appts.start}h - {appts.end}h + + ))} +
+
+
+ + {announcementsTabData.announcements.length !== 0 ? ( + announcementsTabData.announcements.map(announcement => ( + + )) + ) : ( + +

Nenhum comunicado encontrado!

+
+ )} +
+
+ ); + + case 'gone': + return null; + + default: + return null; + } + } + }; + + return layoutResolver( + announcementsTabData && announcementsTabData.state, + layoutType + ); +} + +export default AnnouncementsTab; diff --git a/src/screens/professor/Classroom/AnnouncementsTab/styles.js b/src/screens/professor/Classroom/AnnouncementsTab/styles.js new file mode 100644 index 0000000..d7d218a --- /dev/null +++ b/src/screens/professor/Classroom/AnnouncementsTab/styles.js @@ -0,0 +1,48 @@ +// ========== Desktop ========== +const desktopContainer = { + width: '100%', + height: '100vh', + backgroundColor: '#red', + padding: 0, + margin: 0, + marginTop: '50px', +}; + +const desktopEmptyStateContainer = { + display: 'flex', + justifyContent: 'center', + marginTop: '30px', +}; + +const desktop = { + container: desktopContainer, + emptyStateContainer: desktopEmptyStateContainer, +}; + +// ========== Mobile ========== +const mobileContainer = { + width: '90%', + backgroundColor: '#red', + padding: 0, + marginTop: '30px', + paddingBottom: '100px', +}; + +const mobileEmptyStateContainer = { + display: 'flex', + justifyContent: 'center', + marginTop: '30px', +}; + +const mobile = { + container: mobileContainer, + emptyStateContainer: mobileEmptyStateContainer, +}; + +// ========== Unset ========== +const unset = { + container: null, +}; + +const styles = { desktop, mobile, unset }; +export default styles; diff --git a/src/screens/professor/Classroom/AssignmentsTab/index.js b/src/screens/professor/Classroom/AssignmentsTab/index.js new file mode 100644 index 0000000..7f70767 --- /dev/null +++ b/src/screens/professor/Classroom/AssignmentsTab/index.js @@ -0,0 +1,34 @@ +function AssignmentsTab({ assignmentsTabData, layoutType }) { + const layoutResolver = (state, assignments, layoutType) => { + if (layoutType === 'desktop') { + switch (state) { + case 'loading': + return

Loading...

; + case 'idle': + return

Assignments Tab

; + case 'gone': + return null; + default: + return null; + } + } else if (layoutType === 'mobile') { + switch (state) { + case 'loading': + return

Loading...

; + case 'idle': + return

Assignments Tab

; + case 'gone': + return null; + default: + return null; + } + } + }; + return layoutResolver( + assignmentsTabData && assignmentsTabData.state, + assignmentsTabData && assignmentsTabData.assignments, + layoutType + ); +} + +export default AssignmentsTab; diff --git a/src/screens/professor/Classroom/AssignmentsTab/styles.js b/src/screens/professor/Classroom/AssignmentsTab/styles.js new file mode 100644 index 0000000..e69de29 diff --git a/src/screens/professor/Classroom/Header/index.js b/src/screens/professor/Classroom/Header/index.js new file mode 100644 index 0000000..6a4a1a8 --- /dev/null +++ b/src/screens/professor/Classroom/Header/index.js @@ -0,0 +1,62 @@ +import { + Avatar, + AvatarGroup, + Container, + Paper, + Skeleton, + Stack, + Tab, + Tabs, + Tooltip, + Typography, +} from '@mui/material'; +import { TAB_OPTIONS } from '../tabOptions'; +import styles from './styles'; + +function Header({ + layoutType, + classroom, + selectedTabOption, + onSelectTabOption, + isLoading, +}) { + const { title, paper, tabs, avatar, tooltip } = styles[layoutType]; + return classroom === null ? ( + + ) : ( + + +

{classroom.name}

+ + + {classroom.teachers.map(t => ( + + ))} + + t.name).join(', ')}> + + {classroom.teachers.map(t => t.name).join(', ')} + + + + + {Object.values(TAB_OPTIONS).map(option => ( + + ))} + +
+
+ ); +} + +export default Header; diff --git a/src/screens/professor/Classroom/Header/styles.js b/src/screens/professor/Classroom/Header/styles.js new file mode 100644 index 0000000..58d19aa --- /dev/null +++ b/src/screens/professor/Classroom/Header/styles.js @@ -0,0 +1,90 @@ +// ========== Desktop ========== +const desktopTitle = { + fontWeight: 500, +}; + +const desktopPaper = classColor => ({ + width: '100%', + borderTop: `5px solid ${classColor}`, + padding: '20px', +}); + +const desktopTabs = { + marginLeft: '-20px', + marginRight: '-20px', + marginBottom: '-20px', + marginTop: '30px', +}; + +const desktopAvatar = { + width: 30, + height: 30, +}; + +const desktopTooltip = { + overflow: 'hidden', + textOverflow: 'ellipsis', + display: '-webkit-box', + WebkitLineClamp: 2, + WebkitBoxOrient: 'vertical', +}; + +const desktop = { + title: desktopTitle, + paper: desktopPaper, + tabs: desktopTabs, + avatar: desktopAvatar, + tooltip: desktopTooltip, +}; + +// ========== Mobile ========== +const mobileTitle = { + fontWeight: 500, + fontSize: '25px', +}; + +const mobilePaper = classColor => ({ + width: '100%', + borderTop: `5px solid ${classColor}`, + padding: '10px', +}); + +const mobileTabs = { + marginLeft: '-10px', + marginRight: '-10px', + marginBottom: '-10px', + marginTop: '30px', +}; + +const mobileAvatar = { + width: 30, + height: 30, +}; + +const mobileTooltip = { + overflow: 'hidden', + textOverflow: 'ellipsis', + display: '-webkit-box', + WebkitLineClamp: 2, + WebkitBoxOrient: 'vertical', +}; + +const mobile = { + title: mobileTitle, + paper: mobilePaper, + tabs: mobileTabs, + avatar: mobileAvatar, + tooltip: mobileTooltip, +}; + +// ========== Unset ========== +const unset = { + title: null, + paper: null, + tabs: null, + avatar: null, + tooltip: null, +}; + +const styles = { desktop, mobile, unset }; +export default styles; diff --git a/src/screens/professor/Classroom/View.js b/src/screens/professor/Classroom/View.js new file mode 100644 index 0000000..f15bdd7 --- /dev/null +++ b/src/screens/professor/Classroom/View.js @@ -0,0 +1,39 @@ +import { Container } from '@mui/system'; +import AnnouncementsTab from './AnnouncementsTab'; +import AssignmentsTab from './AssignmentsTab'; +import Header from './Header'; +import styles from './styles'; + +function View({ + layoutType, + classroom, + selectedTabOption, + onSelectTabOption, + announcementsTabData, + assignmentsTabData, + isLoading, +}) { + const { container } = styles[layoutType]; + return ( + +
+ + + + ); +} + +export default View; diff --git a/src/screens/professor/Classroom/index.js b/src/screens/professor/Classroom/index.js new file mode 100644 index 0000000..9ada164 --- /dev/null +++ b/src/screens/professor/Classroom/index.js @@ -0,0 +1,101 @@ +import { useCallback, useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { useUser } from '../../../context/user'; +import useLayoutType from '../../../hooks/useLayoutType'; +import { TAB_OPTIONS } from './tabOptions'; +import View from './View'; + +function Classroom() { + const params = useParams(); + const layoutType = useLayoutType(); + const { userService } = useUser(); + const [classroom, setClassroom] = useState(null); + const [tabData, setTabData] = useState(null); + + const [selectedTabOption, setSelectedTabOption] = useState( + TAB_OPTIONS.announcements.value + ); + + const fetchAndPopulateAnnouncementsTabData = useCallback(async () => { + setTabData({ tab: 'announcements', state: 'loading' }); + const announcements = await userService.fetchClassroomAnnouncements( + params.id + ); + + setTabData({ + tab: 'announcements', + state: 'idle', + announcements: [...announcements.data], + }); + }, [userService, params.id]); + + const fetchAndPopulateAssignmentsTabData = useCallback(async () => { + setTabData({ tab: 'assignments', state: 'loading' }); + const assignments = await userService.fetchAssignmentsByClassId(params.id); + + setTabData({ + tab: 'assignments', + state: 'idle', + assignments: [...assignments.data], + }); + }, [userService, params.id]); + + useEffect(() => { + async function getClassroomById(classId) { + document.title = 'Carregando...'; + const result = await userService.fetchClassroomById(classId); + setClassroom(result.data); + } + + function updateDocumentTitle() { + if (classroom !== null) { + document.title = classroom.name; + } + } + + getClassroomById(params.id); + updateDocumentTitle(); + }, [userService, userService.fetchClassroomById, params, classroom]); + + useEffect(() => { + async function getSelectedTabData() { + switch (selectedTabOption) { + case TAB_OPTIONS.announcements.value: + fetchAndPopulateAnnouncementsTabData(); + break; + case TAB_OPTIONS.assignments.value: + fetchAndPopulateAssignmentsTabData(); + break; + case TAB_OPTIONS.people.value: + // TODO + break; + default: + console.log('Invalid tab option'); + } + } + getSelectedTabData(); + }, [ + selectedTabOption, + params, + fetchAndPopulateAnnouncementsTabData, + fetchAndPopulateAssignmentsTabData, + ]); + + return ( + setSelectedTabOption(value)} + announcementsTabData={ + tabData && tabData.tab === 'announcements' ? tabData : { state: 'gone' } + } + assignmentsTabData={ + tabData && tabData.tab === 'assignments' ? tabData : { state: 'gone' } + } + isLoading={tabData && tabData.state === 'loading'} + /> + ); +} + +export default Classroom; diff --git a/src/screens/professor/Classroom/styles.js b/src/screens/professor/Classroom/styles.js new file mode 100644 index 0000000..fe6018c --- /dev/null +++ b/src/screens/professor/Classroom/styles.js @@ -0,0 +1,34 @@ +// ========== Desktop ========== +const desktopContainer = { + width: '100%', + height: '100vh', + backgroundColor: '#red', + padding: 0, + margin: 0, + marginTop: '50px', +}; + +const desktop = { + container: desktopContainer, +}; + +// ========== Mobile ========== +const mobileContainer = { + width: '90%', + backgroundColor: '#red', + padding: 0, + marginTop: '30px', + paddingBottom: '100px', +}; + +const mobile = { + container: mobileContainer, +}; + +// ========== Unset ========== +const unset = { + container: null, +}; + +const styles = { desktop, mobile, unset }; +export default styles; diff --git a/src/screens/professor/Classroom/tabOptions.js b/src/screens/professor/Classroom/tabOptions.js new file mode 100644 index 0000000..fdc3e98 --- /dev/null +++ b/src/screens/professor/Classroom/tabOptions.js @@ -0,0 +1,20 @@ +const TAB_OPTIONS = { + announcements: { + value: 0, + lable: 'Comunicados', + }, + assignments: { + value: 1, + lable: 'Atividades', + }, + people: { + value: 2, + lable: 'Pessoas', + }, + grades: { + value: 3, + lable: 'Notas', + }, +}; + +export { TAB_OPTIONS }; diff --git a/src/services/professor.js b/src/services/professor.js index 3599f67..64da1f2 100644 --- a/src/services/professor.js +++ b/src/services/professor.js @@ -7,6 +7,14 @@ export default class ProfessorService { fetchClassrooms = () => ProfessorApi.getClassrooms(this.user.id); + fetchClassroomById = classId => ProfessorApi.getClassroomById(classId); + + fetchAssignmentsByClassId = classId => + ProfessorApi.getAssignmentsByClassId(classId); + fetchAssignmentsToReview = () => ProfessorApi.getAssignmentsToReview(this.user.id); + + fetchClassroomAnnouncements = classId => + ProfessorApi.getClassroomAnnouncementsById(classId); } diff --git a/src/utils/mocks/api.js b/src/utils/mocks/api.js index 08009c0..36c7407 100644 --- a/src/utils/mocks/api.js +++ b/src/utils/mocks/api.js @@ -41,6 +41,28 @@ const CommonApi = { window.localStorage.setItem('$USER', JSON.stringify(data)); return userData; }), + + getClassroomAnnouncementsById: classId => + sleep(300).then(() => { + console.log('Get classroon announcements by id ' + classId); + return { + data: allClassroomAnnouncements.filter(c => c.classroom.id === classId), + }; + }), + getClassroomById: classId => + sleep(300).then(() => { + console.log('Get classroom by id ' + classId); + return { + data: allClassrooms.filter(c => c.id === classId)[0], + }; + }), + getAssignmentsByClassId: classId => + sleep(300).then(() => { + console.log('Getting assignments by class id ' + classId); + return { + data: allAssignments.filter(a => a.classrooms[0].id === classId), + }; + }), }; const StudentApi = { @@ -53,22 +75,6 @@ const StudentApi = { }; }), - getClassroomById: classId => - sleep(300).then(() => { - console.log('Get classroom by id ' + classId); - return { - data: allClassrooms.filter(c => c.id === classId)[0], - }; - }), - - getClassroomAnnouncementsById: classId => - sleep(300).then(() => { - console.log('Get classroon announcements by id ' + classId); - return { - data: allClassroomAnnouncements.filter(c => c.classroom.id === classId), - }; - }), - getUpcomingAssignmentsByClassId: classId => sleep(300).then(() => { console.log('Getting upcoming assignments by class id ' + classId); @@ -95,14 +101,6 @@ const StudentApi = { }; }), - getAssignmentsByClassId: classId => - sleep(300).then(() => { - console.log('Getting assignments by class id ' + classId); - return { - data: allAssignments.filter(a => a.classrooms[0].id === classId), - }; - }), - getPeopleByClassId: classId => sleep(400).then(() => { console.log('Getting people by class id ' + classId); From 7a59fbc8369d397067994a75a8d316266a726944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Tue, 13 Dec 2022 21:56:23 -0300 Subject: [PATCH 14/27] Add professor classroom people tab placeholder --- src/screens/Classroom/index.js | 6 ++-- .../professor/Classroom/PeopleTab/index.js | 35 +++++++++++++++++++ .../professor/Classroom/PeopleTab/styles.js | 0 src/screens/professor/Classroom/View.js | 3 ++ src/screens/professor/Classroom/index.js | 17 ++++++++- src/services/professor.js | 2 ++ src/utils/mocks/api.js | 16 ++++----- 7 files changed, 67 insertions(+), 12 deletions(-) create mode 100644 src/screens/professor/Classroom/PeopleTab/index.js create mode 100644 src/screens/professor/Classroom/PeopleTab/styles.js diff --git a/src/screens/Classroom/index.js b/src/screens/Classroom/index.js index 10fc044..01eb4f3 100644 --- a/src/screens/Classroom/index.js +++ b/src/screens/Classroom/index.js @@ -42,7 +42,7 @@ function Classroom() { }); }, [userService, params.id]); - const fetchAndPopulatePoepleTabData = useCallback(async () => { + const fetchAndPopulatePeopleTabData = useCallback(async () => { setTabData({ tab: 'people', state: 'loading' }); const people = await userService.fetchPeopleByClassId(params.id); @@ -63,7 +63,7 @@ function Classroom() { fetchAndPopulateAssignmentsTabData(); break; case TAB_OPTIONS.people.value: - fetchAndPopulatePoepleTabData(); + fetchAndPopulatePeopleTabData(); break; default: console.log('Invalid tab option'); @@ -75,7 +75,7 @@ function Classroom() { params, fetchAndPopulateAnnouncementsTabData, fetchAndPopulateAssignmentsTabData, - fetchAndPopulatePoepleTabData, + fetchAndPopulatePeopleTabData, ]); useEffect(() => { diff --git a/src/screens/professor/Classroom/PeopleTab/index.js b/src/screens/professor/Classroom/PeopleTab/index.js new file mode 100644 index 0000000..3d54f36 --- /dev/null +++ b/src/screens/professor/Classroom/PeopleTab/index.js @@ -0,0 +1,35 @@ +function PeopleTab({ layoutType, peopleTabData }) { + const layoutResolver = (state, people, layoutType) => { + if (layoutType === 'desktop') { + switch (state) { + case 'loading': + return

Loading...

; + case 'idle': + return

People Tab

; + case 'gone': + return null; + default: + return null; + } + } else if (layoutType === 'mobile') { + switch (state) { + case 'loading': + return

Loading...

; + case 'idle': + return

People Tab

; + case 'gone': + return null; + default: + return null; + } + } + }; + + return layoutResolver( + peopleTabData && peopleTabData.state, + peopleTabData && peopleTabData.people, + layoutType + ); +} + +export default PeopleTab; diff --git a/src/screens/professor/Classroom/PeopleTab/styles.js b/src/screens/professor/Classroom/PeopleTab/styles.js new file mode 100644 index 0000000..e69de29 diff --git a/src/screens/professor/Classroom/View.js b/src/screens/professor/Classroom/View.js index f15bdd7..42c700f 100644 --- a/src/screens/professor/Classroom/View.js +++ b/src/screens/professor/Classroom/View.js @@ -2,6 +2,7 @@ import { Container } from '@mui/system'; import AnnouncementsTab from './AnnouncementsTab'; import AssignmentsTab from './AssignmentsTab'; import Header from './Header'; +import PeopleTab from './PeopleTab'; import styles from './styles'; function View({ @@ -11,6 +12,7 @@ function View({ onSelectTabOption, announcementsTabData, assignmentsTabData, + peopleTabData, isLoading, }) { const { container } = styles[layoutType]; @@ -32,6 +34,7 @@ function View({ layoutType={layoutType} assignmentsTabData={assignmentsTabData} /> + ); } diff --git a/src/screens/professor/Classroom/index.js b/src/screens/professor/Classroom/index.js index 9ada164..4613427 100644 --- a/src/screens/professor/Classroom/index.js +++ b/src/screens/professor/Classroom/index.js @@ -57,6 +57,17 @@ function Classroom() { updateDocumentTitle(); }, [userService, userService.fetchClassroomById, params, classroom]); + const fetchAndPopulatePeopleTabData = useCallback(async () => { + setTabData({ tab: 'people', state: 'loading' }); + const people = await userService.fetchPeopleByClassId(params.id); + + setTabData({ + tab: 'people', + state: 'idle', + people: [...people.data], + }); + }, [userService, params.id]); + useEffect(() => { async function getSelectedTabData() { switch (selectedTabOption) { @@ -67,7 +78,7 @@ function Classroom() { fetchAndPopulateAssignmentsTabData(); break; case TAB_OPTIONS.people.value: - // TODO + fetchAndPopulatePeopleTabData(); break; default: console.log('Invalid tab option'); @@ -79,6 +90,7 @@ function Classroom() { params, fetchAndPopulateAnnouncementsTabData, fetchAndPopulateAssignmentsTabData, + fetchAndPopulatePeopleTabData, ]); return ( @@ -93,6 +105,9 @@ function Classroom() { assignmentsTabData={ tabData && tabData.tab === 'assignments' ? tabData : { state: 'gone' } } + peopleTabData={ + tabData && tabData.tab === 'people' ? tabData : { state: 'gone' } + } isLoading={tabData && tabData.state === 'loading'} /> ); diff --git a/src/services/professor.js b/src/services/professor.js index 64da1f2..28e54af 100644 --- a/src/services/professor.js +++ b/src/services/professor.js @@ -17,4 +17,6 @@ export default class ProfessorService { fetchClassroomAnnouncements = classId => ProfessorApi.getClassroomAnnouncementsById(classId); + + fetchPeopleByClassId = classId => ProfessorApi.getPeopleByClassId(classId); } diff --git a/src/utils/mocks/api.js b/src/utils/mocks/api.js index 36c7407..4e1bdd7 100644 --- a/src/utils/mocks/api.js +++ b/src/utils/mocks/api.js @@ -63,6 +63,14 @@ const CommonApi = { data: allAssignments.filter(a => a.classrooms[0].id === classId), }; }), + + getPeopleByClassId: classId => + sleep(400).then(() => { + console.log('Getting people by class id ' + classId); + return { + data: allPeople.filter(p => p.classes[0].id === classId), + }; + }), }; const StudentApi = { @@ -101,14 +109,6 @@ const StudentApi = { }; }), - getPeopleByClassId: classId => - sleep(400).then(() => { - console.log('Getting people by class id ' + classId); - return { - data: allPeople.filter(p => p.classes[0].id === classId), - }; - }), - getFaq: () => sleep(300).then(() => { console.log('Fetching FAQ...'); From 0f3fe5494f5e976f539f1ea3683e826be012fc1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Wed, 14 Dec 2022 09:48:45 -0300 Subject: [PATCH 15/27] Add professor class grades placeholder screen --- .../professor/Classroom/GradesTab/index.js | 34 +++++++++++++++++++ .../professor/Classroom/GradesTab/styles.js | 0 src/screens/professor/Classroom/View.js | 3 ++ src/screens/professor/Classroom/index.js | 18 ++++++++++ src/services/professor.js | 2 ++ src/utils/mocks/api.js | 8 +++++ src/utils/mocks/responses.js | 8 +++++ 7 files changed, 73 insertions(+) create mode 100644 src/screens/professor/Classroom/GradesTab/index.js create mode 100644 src/screens/professor/Classroom/GradesTab/styles.js diff --git a/src/screens/professor/Classroom/GradesTab/index.js b/src/screens/professor/Classroom/GradesTab/index.js new file mode 100644 index 0000000..785bd50 --- /dev/null +++ b/src/screens/professor/Classroom/GradesTab/index.js @@ -0,0 +1,34 @@ +function GradesTab({ gradesTabData, layoutType }) { + const layoutResolver = (state, grades, layoutType) => { + if (layoutType === 'desktop') { + switch (state) { + case 'loading': + return

Loading...

; + case 'idle': + return

Grades Tab

; + case 'gone': + return null; + default: + return null; + } + } else if (layoutType === 'mobile') { + switch (state) { + case 'loading': + return

Loading...

; + case 'idle': + return

Grades Tab

; + case 'gone': + return null; + default: + return null; + } + } + }; + return layoutResolver( + gradesTabData && gradesTabData.state, + gradesTabData && gradesTabData.grades, + layoutType + ); +} + +export default GradesTab; diff --git a/src/screens/professor/Classroom/GradesTab/styles.js b/src/screens/professor/Classroom/GradesTab/styles.js new file mode 100644 index 0000000..e69de29 diff --git a/src/screens/professor/Classroom/View.js b/src/screens/professor/Classroom/View.js index 42c700f..66bdadb 100644 --- a/src/screens/professor/Classroom/View.js +++ b/src/screens/professor/Classroom/View.js @@ -1,6 +1,7 @@ import { Container } from '@mui/system'; import AnnouncementsTab from './AnnouncementsTab'; import AssignmentsTab from './AssignmentsTab'; +import GradesTab from './GradesTab'; import Header from './Header'; import PeopleTab from './PeopleTab'; import styles from './styles'; @@ -13,6 +14,7 @@ function View({ announcementsTabData, assignmentsTabData, peopleTabData, + gradesTabData, isLoading, }) { const { container } = styles[layoutType]; @@ -35,6 +37,7 @@ function View({ assignmentsTabData={assignmentsTabData} /> + ); } diff --git a/src/screens/professor/Classroom/index.js b/src/screens/professor/Classroom/index.js index 4613427..94e11f0 100644 --- a/src/screens/professor/Classroom/index.js +++ b/src/screens/professor/Classroom/index.js @@ -68,6 +68,17 @@ function Classroom() { }); }, [userService, params.id]); + const fetchAndPopulateGradesTabData = useCallback(async () => { + setTabData({ tab: 'people', state: 'loading' }); + const grades = await userService.fetchPeopleByClassId(params.id); + + setTabData({ + tab: 'grades', + state: 'idle', + grades: [...grades.data], + }); + }, [userService, params.id]); + useEffect(() => { async function getSelectedTabData() { switch (selectedTabOption) { @@ -80,6 +91,9 @@ function Classroom() { case TAB_OPTIONS.people.value: fetchAndPopulatePeopleTabData(); break; + case TAB_OPTIONS.grades.value: + fetchAndPopulateGradesTabData(); + break; default: console.log('Invalid tab option'); } @@ -91,6 +105,7 @@ function Classroom() { fetchAndPopulateAnnouncementsTabData, fetchAndPopulateAssignmentsTabData, fetchAndPopulatePeopleTabData, + fetchAndPopulateGradesTabData, ]); return ( @@ -108,6 +123,9 @@ function Classroom() { peopleTabData={ tabData && tabData.tab === 'people' ? tabData : { state: 'gone' } } + gradesTabData={ + tabData && tabData.tab === 'grades' ? tabData : { state: 'gone' } + } isLoading={tabData && tabData.state === 'loading'} /> ); diff --git a/src/services/professor.js b/src/services/professor.js index 28e54af..e36f268 100644 --- a/src/services/professor.js +++ b/src/services/professor.js @@ -19,4 +19,6 @@ export default class ProfessorService { ProfessorApi.getClassroomAnnouncementsById(classId); fetchPeopleByClassId = classId => ProfessorApi.getPeopleByClassId(classId); + + fetchGradesByClassId = classId => ProfessorApi.getGradesByClassId(classId); } diff --git a/src/utils/mocks/api.js b/src/utils/mocks/api.js index 4e1bdd7..2a2b66b 100644 --- a/src/utils/mocks/api.js +++ b/src/utils/mocks/api.js @@ -11,6 +11,7 @@ import { allPeople, professorClassrooms, assignmentsToReview, + grades, } from './responses'; const CommonApi = { @@ -134,6 +135,13 @@ const ProfessorApi = { data: assignmentsToReview, }; }), + getGradesByClassId: classId => + sleep(400).then(() => { + console.log('Getting grades' + classId); + return { + data: grades, + }; + }), }; export { StudentApi, ProfessorApi, CommonApi }; diff --git a/src/utils/mocks/responses.js b/src/utils/mocks/responses.js index 25eadd3..b3644af 100644 --- a/src/utils/mocks/responses.js +++ b/src/utils/mocks/responses.js @@ -643,6 +643,13 @@ const allPeople = [ }, ]; +// TODO: Mock correct data +const grades = [ + { + id: 'Some grade', + }, +]; + const studentUser = { id: '123', ra: '0021123', @@ -693,4 +700,5 @@ export { allUpcomingAssignments, professorClassrooms, assignmentsToReview, + grades, }; From 00ab43aaf54a6ca9228cd0388c78895f18e8e1ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Wed, 14 Dec 2022 12:03:12 -0300 Subject: [PATCH 16/27] Move home and classroom screen to student folder --- src/app/StudentRoutes.js | 5 +++-- .../{ => student}/Classroom/AnnouncementsTab/index.js | 6 +++--- .../{ => student}/Classroom/AnnouncementsTab/styles.js | 0 src/screens/{ => student}/Classroom/AssignmentsTab/index.js | 2 +- .../{ => student}/Classroom/AssignmentsTab/styles.js | 0 src/screens/{ => student}/Classroom/Header/index.js | 0 src/screens/{ => student}/Classroom/Header/styles.js | 0 src/screens/{ => student}/Classroom/PeopleTab/index.js | 2 +- src/screens/{ => student}/Classroom/PeopleTab/styles.js | 0 src/screens/{ => student}/Classroom/View.js | 0 src/screens/{ => student}/Classroom/index.js | 4 ++-- src/screens/{ => student}/Classroom/styles.js | 0 src/screens/{ => student}/Classroom/tabOptions.js | 0 src/screens/{ => student}/Home/View.js | 6 +++--- src/screens/{ => student}/Home/index.js | 6 +++--- src/screens/{ => student}/Home/styles.js | 0 16 files changed, 16 insertions(+), 15 deletions(-) rename src/screens/{ => student}/Classroom/AnnouncementsTab/index.js (98%) rename src/screens/{ => student}/Classroom/AnnouncementsTab/styles.js (100%) rename src/screens/{ => student}/Classroom/AssignmentsTab/index.js (99%) rename src/screens/{ => student}/Classroom/AssignmentsTab/styles.js (100%) rename src/screens/{ => student}/Classroom/Header/index.js (100%) rename src/screens/{ => student}/Classroom/Header/styles.js (100%) rename src/screens/{ => student}/Classroom/PeopleTab/index.js (99%) rename src/screens/{ => student}/Classroom/PeopleTab/styles.js (100%) rename src/screens/{ => student}/Classroom/View.js (100%) rename src/screens/{ => student}/Classroom/index.js (97%) rename src/screens/{ => student}/Classroom/styles.js (100%) rename src/screens/{ => student}/Classroom/tabOptions.js (100%) rename src/screens/{ => student}/Home/View.js (96%) rename src/screens/{ => student}/Home/index.js (87%) rename src/screens/{ => student}/Home/styles.js (100%) diff --git a/src/app/StudentRoutes.js b/src/app/StudentRoutes.js index b3d2961..1dc6b4a 100644 --- a/src/app/StudentRoutes.js +++ b/src/app/StudentRoutes.js @@ -1,9 +1,10 @@ import { Navigate, Route, Routes } from 'react-router-dom'; -import Home from '../screens/Home'; +import Home from '../screens/student/Home'; +import Classroom from '../screens/student/Classroom'; + import Information from '../screens/Information'; import Calendar from '../screens/Calendar'; -import Classroom from '../screens/Classroom'; import Assignment from '../screens/Assignment'; import Profile from '../screens/Profile'; diff --git a/src/screens/Classroom/AnnouncementsTab/index.js b/src/screens/student/Classroom/AnnouncementsTab/index.js similarity index 98% rename from src/screens/Classroom/AnnouncementsTab/index.js rename to src/screens/student/Classroom/AnnouncementsTab/index.js index 5540a94..90d169d 100644 --- a/src/screens/Classroom/AnnouncementsTab/index.js +++ b/src/screens/student/Classroom/AnnouncementsTab/index.js @@ -8,11 +8,11 @@ import { Stack, Typography, } from '@mui/material'; -import AnnouncementCard from '../../../components/AnnouncementCard'; -import { createArrayFrom1ToN } from '../../../utils/createArrayFrom1ToN'; +import AnnouncementCard from '../../../../components/AnnouncementCard'; +import { createArrayFrom1ToN } from '../../../../utils/createArrayFrom1ToN'; import styles from './styles'; -import jitsiLogo from '../../../assets/jitsi.svg'; +import jitsiLogo from '../../../../assets/jitsi.svg'; function AnnouncementsTab({ layoutType, announcementsTabData, classroom }) { const { container, emptyStateContainer } = styles[layoutType]; diff --git a/src/screens/Classroom/AnnouncementsTab/styles.js b/src/screens/student/Classroom/AnnouncementsTab/styles.js similarity index 100% rename from src/screens/Classroom/AnnouncementsTab/styles.js rename to src/screens/student/Classroom/AnnouncementsTab/styles.js diff --git a/src/screens/Classroom/AssignmentsTab/index.js b/src/screens/student/Classroom/AssignmentsTab/index.js similarity index 99% rename from src/screens/Classroom/AssignmentsTab/index.js rename to src/screens/student/Classroom/AssignmentsTab/index.js index bc089fe..19757b4 100644 --- a/src/screens/Classroom/AssignmentsTab/index.js +++ b/src/screens/student/Classroom/AssignmentsTab/index.js @@ -1,6 +1,6 @@ import { Container, Link, Skeleton, Stack, Typography } from '@mui/material'; import dayjs from 'dayjs'; -import { capitalizeFirstLetter } from '../../../utils/capitalizeFirstLetter'; +import { capitalizeFirstLetter } from '../../../../utils/capitalizeFirstLetter'; import styles from './styles'; function AssignmentsTab({ assignmentsTabData, layoutType }) { diff --git a/src/screens/Classroom/AssignmentsTab/styles.js b/src/screens/student/Classroom/AssignmentsTab/styles.js similarity index 100% rename from src/screens/Classroom/AssignmentsTab/styles.js rename to src/screens/student/Classroom/AssignmentsTab/styles.js diff --git a/src/screens/Classroom/Header/index.js b/src/screens/student/Classroom/Header/index.js similarity index 100% rename from src/screens/Classroom/Header/index.js rename to src/screens/student/Classroom/Header/index.js diff --git a/src/screens/Classroom/Header/styles.js b/src/screens/student/Classroom/Header/styles.js similarity index 100% rename from src/screens/Classroom/Header/styles.js rename to src/screens/student/Classroom/Header/styles.js diff --git a/src/screens/Classroom/PeopleTab/index.js b/src/screens/student/Classroom/PeopleTab/index.js similarity index 99% rename from src/screens/Classroom/PeopleTab/index.js rename to src/screens/student/Classroom/PeopleTab/index.js index 3080ee3..9dfde7b 100644 --- a/src/screens/Classroom/PeopleTab/index.js +++ b/src/screens/student/Classroom/PeopleTab/index.js @@ -1,5 +1,5 @@ import { Avatar, Container, Skeleton, Stack, Typography } from '@mui/material'; -import { createArrayFrom1ToN } from '../../../utils/createArrayFrom1ToN'; +import { createArrayFrom1ToN } from '../../../../utils/createArrayFrom1ToN'; import styles from './styles'; function PeopleTab({ layoutType, peopleTabData }) { diff --git a/src/screens/Classroom/PeopleTab/styles.js b/src/screens/student/Classroom/PeopleTab/styles.js similarity index 100% rename from src/screens/Classroom/PeopleTab/styles.js rename to src/screens/student/Classroom/PeopleTab/styles.js diff --git a/src/screens/Classroom/View.js b/src/screens/student/Classroom/View.js similarity index 100% rename from src/screens/Classroom/View.js rename to src/screens/student/Classroom/View.js diff --git a/src/screens/Classroom/index.js b/src/screens/student/Classroom/index.js similarity index 97% rename from src/screens/Classroom/index.js rename to src/screens/student/Classroom/index.js index 01eb4f3..5201b49 100644 --- a/src/screens/Classroom/index.js +++ b/src/screens/student/Classroom/index.js @@ -1,7 +1,7 @@ import { useCallback, useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; -import { useUser } from '../../context/user'; -import useLayoutType from '../../hooks/useLayoutType'; +import { useUser } from '../../../context/user'; +import useLayoutType from '../../../hooks/useLayoutType'; import { TAB_OPTIONS } from './tabOptions'; import View from './View'; diff --git a/src/screens/Classroom/styles.js b/src/screens/student/Classroom/styles.js similarity index 100% rename from src/screens/Classroom/styles.js rename to src/screens/student/Classroom/styles.js diff --git a/src/screens/Classroom/tabOptions.js b/src/screens/student/Classroom/tabOptions.js similarity index 100% rename from src/screens/Classroom/tabOptions.js rename to src/screens/student/Classroom/tabOptions.js diff --git a/src/screens/Home/View.js b/src/screens/student/Home/View.js similarity index 96% rename from src/screens/Home/View.js rename to src/screens/student/Home/View.js index 749049d..27b0f78 100644 --- a/src/screens/Home/View.js +++ b/src/screens/student/Home/View.js @@ -1,10 +1,10 @@ import { Container, Grid, Skeleton, Stack } from '@mui/material'; -import ClassCard from '../../components/ClassCard'; -import AssignmentCard from '../../components/AssignmentCard'; +import ClassCard from '../../../components/ClassCard'; +import AssignmentCard from '../../../components/AssignmentCard'; import styles from './styles'; -import { createArrayFrom1ToN } from '../../utils/createArrayFrom1ToN'; +import { createArrayFrom1ToN } from '../../../utils/createArrayFrom1ToN'; function View({ layoutType, diff --git a/src/screens/Home/index.js b/src/screens/student/Home/index.js similarity index 87% rename from src/screens/Home/index.js rename to src/screens/student/Home/index.js index 6e535c5..75d734e 100644 --- a/src/screens/Home/index.js +++ b/src/screens/student/Home/index.js @@ -1,8 +1,8 @@ import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { useUser } from '../../context/user'; -import { useDocumentTitle } from '../../hooks/useDocumentTitle'; -import useLayoutType from '../../hooks/useLayoutType'; +import { useUser } from '../../../context/user'; +import { useDocumentTitle } from '../../../hooks/useDocumentTitle'; +import useLayoutType from '../../../hooks/useLayoutType'; import View from './View'; function Home() { diff --git a/src/screens/Home/styles.js b/src/screens/student/Home/styles.js similarity index 100% rename from src/screens/Home/styles.js rename to src/screens/student/Home/styles.js From 30524bc1e023233fa8cae239a60cd0d2a481f61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Wed, 14 Dec 2022 18:43:26 -0300 Subject: [PATCH 17/27] Add Publish Announcement Card --- .../PublishAnnouncementCard/index.js | 64 +++++++++++++++++++ .../PublishAnnouncementCard/styles.js | 42 ++++++++++++ .../Classroom/AnnouncementsTab/index.js | 23 ++++++- .../Classroom/AnnouncementsTab/styles.js | 1 + src/screens/professor/Classroom/View.js | 2 + src/screens/professor/Classroom/index.js | 3 +- src/utils/mocks/responses.js | 5 +- 7 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 src/components/PublishAnnouncementCard/index.js create mode 100644 src/components/PublishAnnouncementCard/styles.js diff --git a/src/components/PublishAnnouncementCard/index.js b/src/components/PublishAnnouncementCard/index.js new file mode 100644 index 0000000..67501b2 --- /dev/null +++ b/src/components/PublishAnnouncementCard/index.js @@ -0,0 +1,64 @@ +import { + Avatar, + Button, + Card, + Stack, + TextField, + Typography, +} from '@mui/material'; +import { useState } from 'react'; +import styles from './styles'; + +function PublishAnnouncementCard({ layoutType, user, value, onChange }) { + const [isComposing, setIsComposing] = useState(false); + const { card, publishAnnouncement } = styles[layoutType]; + + return ( + + {isComposing ? ( + + + + + + + + + + ) : ( + setIsComposing(true)} + > + + + Escreva um comunicado para sua turma + + + )} + + ); +} + +export default PublishAnnouncementCard; diff --git a/src/components/PublishAnnouncementCard/styles.js b/src/components/PublishAnnouncementCard/styles.js new file mode 100644 index 0000000..282cd23 --- /dev/null +++ b/src/components/PublishAnnouncementCard/styles.js @@ -0,0 +1,42 @@ +// ========== Desktop ========== +const desktopCard = { + width: '100%', + padding: '20px', +}; + +const desktopPublishAnnouncement = { + cursor: 'pointer', + ':hover': { + color: '#32A041', + }, +}; + +const desktop = { + publishAnnouncement: desktopPublishAnnouncement, + card: desktopCard, +}; + +// ========== Mobile ========== +const mobilePublishAnnouncement = { + cursor: 'pointer', + ':hover': { + color: '#32A041', + }, +}; + +const mobileCard = {}; + +const mobile = { + publishAnnouncement: mobilePublishAnnouncement, + card: mobileCard, +}; + +// ========== Unset ========== + +const unset = { + publishAnnouncement: null, + card: null, +}; + +const styles = { desktop, mobile, unset }; +export default styles; diff --git a/src/screens/professor/Classroom/AnnouncementsTab/index.js b/src/screens/professor/Classroom/AnnouncementsTab/index.js index 4ca8c01..1154538 100644 --- a/src/screens/professor/Classroom/AnnouncementsTab/index.js +++ b/src/screens/professor/Classroom/AnnouncementsTab/index.js @@ -3,7 +3,6 @@ import { Card, Container, Grid, - Link, Skeleton, Stack, Typography, @@ -13,8 +12,16 @@ import AnnouncementCard from '../../../../components/AnnouncementCard'; import styles from './styles'; import jitsiLogo from '../../../../assets/jitsi.svg'; import { createArrayFrom1ToN } from '../../../../utils/createArrayFrom1ToN'; +import PublishAnnouncementCard from '../../../../components/PublishAnnouncementCard'; +import { useState } from 'react'; -function AnnouncementsTab({ layoutType, announcementsTabData, classroom }) { +function AnnouncementsTab({ + layoutType, + announcementsTabData, + classroom, + user, +}) { + const [composingTextValue, setComposingTextValue] = useState(''); const { container, emptyStateContainer } = styles[layoutType]; const layoutResolver = (state, layoutType) => { @@ -112,6 +119,12 @@ function AnnouncementsTab({ layoutType, announcementsTabData, classroom }) { direction="row" gap="30px" > + setComposingTextValue(e.target.value)} + /> {announcementsTabData.announcements.length !== 0 ? ( announcementsTabData.announcements.map(announcement => ( + setComposingTextValue(e.target.value)} + /> {announcementsTabData.announcements.length !== 0 ? ( announcementsTabData.announcements.map(announcement => ( ); diff --git a/src/utils/mocks/responses.js b/src/utils/mocks/responses.js index b3644af..d864fda 100644 --- a/src/utils/mocks/responses.js +++ b/src/utils/mocks/responses.js @@ -674,10 +674,11 @@ const professorUser = { email: 'carlos.junior@ifmg.edu.br', password: '#carlos1234', // TODO: Remove this! firstName: 'Carlos', - lastName: 'Severiano', + lastName: 'Alexandre Silva', token: 'xkhfb9458hnsdfsi9q8345bsdf9b834yr', phone: '31111111111', - avatar: 'https://i.pravatar.cc/300?img=61', + avatar: + 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=50&q=80', course: 0, termsAgreed: true, year: 2018, From 8e0922b047d9e0f1a64ad6c0ea7fc23c371ee364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Wed, 14 Dec 2022 18:52:24 -0300 Subject: [PATCH 18/27] Add fetch to upcoming assignments for professor --- .../Classroom/AnnouncementsTab/index.js | 34 ++++++++++++++++++- src/screens/professor/Classroom/index.js | 4 +++ src/services/professor.js | 3 ++ src/utils/mocks/api.js | 20 +++++------ 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/screens/professor/Classroom/AnnouncementsTab/index.js b/src/screens/professor/Classroom/AnnouncementsTab/index.js index 1154538..28d8311 100644 --- a/src/screens/professor/Classroom/AnnouncementsTab/index.js +++ b/src/screens/professor/Classroom/AnnouncementsTab/index.js @@ -1,8 +1,10 @@ +import { useState } from 'react'; import { Button, Card, Container, Grid, + Link, Skeleton, Stack, Typography, @@ -13,7 +15,6 @@ import styles from './styles'; import jitsiLogo from '../../../../assets/jitsi.svg'; import { createArrayFrom1ToN } from '../../../../utils/createArrayFrom1ToN'; import PublishAnnouncementCard from '../../../../components/PublishAnnouncementCard'; -import { useState } from 'react'; function AnnouncementsTab({ layoutType, @@ -90,6 +91,21 @@ function AnnouncementsTab({ >

Próximas Atividades

+ {announcementsTabData.upcomingAssignments.length !== 0 ? ( + announcementsTabData.upcomingAssignments.map(ua => ( + + {ua.title} + + )) + ) : ( + +

Nenhuma atividade encontrada!

+
+ )}

Próximas Atividades

+ + {announcementsTabData.upcomingAssignments.length !== 0 ? ( + announcementsTabData.upcomingAssignments.map(ua => ( + + {ua.title} + + )) + ) : ( + +

Nenhuma atividade encontrada!

+
+ )}
ProfessorApi.getPeopleByClassId(classId); fetchGradesByClassId = classId => ProfessorApi.getGradesByClassId(classId); + + fetchUpcomingAssignmentsByClassId = classId => + ProfessorApi.getUpcomingAssignmentsByClassId(classId); } diff --git a/src/utils/mocks/api.js b/src/utils/mocks/api.js index 2a2b66b..6076993 100644 --- a/src/utils/mocks/api.js +++ b/src/utils/mocks/api.js @@ -72,6 +72,16 @@ const CommonApi = { data: allPeople.filter(p => p.classes[0].id === classId), }; }), + + getUpcomingAssignmentsByClassId: classId => + sleep(300).then(() => { + console.log('Getting upcoming assignments by class id ' + classId); + return { + data: allUpcomingAssignments.filter( + a => a.classrooms.filter(c => c.id === classId)[0] + ), + }; + }), }; const StudentApi = { @@ -84,16 +94,6 @@ const StudentApi = { }; }), - getUpcomingAssignmentsByClassId: classId => - sleep(300).then(() => { - console.log('Getting upcoming assignments by class id ' + classId); - return { - data: allUpcomingAssignments.filter( - a => a.classrooms.filter(c => c.id === classId)[0] - ), - }; - }), - getAllAssignments: userId => sleep(400).then(() => { console.log('Getting all assignments ' + userId); From abd66a137e76e6e68451e89dc491043535ef05fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Mon, 19 Dec 2022 21:46:09 -0300 Subject: [PATCH 19/27] Make publish announcement card and tabs responsive --- src/components/PublishAnnouncementCard/styles.js | 7 ++++++- src/screens/professor/Classroom/Header/index.js | 2 +- src/screens/professor/Classroom/Header/styles.js | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/PublishAnnouncementCard/styles.js b/src/components/PublishAnnouncementCard/styles.js index 282cd23..4bc58fd 100644 --- a/src/components/PublishAnnouncementCard/styles.js +++ b/src/components/PublishAnnouncementCard/styles.js @@ -19,12 +19,17 @@ const desktop = { // ========== Mobile ========== const mobilePublishAnnouncement = { cursor: 'pointer', + padding: '10px', + width: '100%', ':hover': { color: '#32A041', }, }; -const mobileCard = {}; +const mobileCard = { + width: '100%', + padding: '10px ', +}; const mobile = { publishAnnouncement: mobilePublishAnnouncement, diff --git a/src/screens/professor/Classroom/Header/index.js b/src/screens/professor/Classroom/Header/index.js index 6a4a1a8..e077313 100644 --- a/src/screens/professor/Classroom/Header/index.js +++ b/src/screens/professor/Classroom/Header/index.js @@ -43,7 +43,7 @@ function Header({ value={selectedTabOption} onChange={onSelectTabOption} aria-label="Tabs para informações da disciplina" - variant="fullWidth" + variant={layoutType === 'mobile' ? 'scrollable' : 'fullWidth'} sx={tabs} > {Object.values(TAB_OPTIONS).map(option => ( diff --git a/src/screens/professor/Classroom/Header/styles.js b/src/screens/professor/Classroom/Header/styles.js index 58d19aa..babe6b3 100644 --- a/src/screens/professor/Classroom/Header/styles.js +++ b/src/screens/professor/Classroom/Header/styles.js @@ -6,14 +6,16 @@ const desktopTitle = { const desktopPaper = classColor => ({ width: '100%', borderTop: `5px solid ${classColor}`, - padding: '20px', + padding: '30px', }); const desktopTabs = { + display: 'flex', marginLeft: '-20px', marginRight: '-20px', marginBottom: '-20px', marginTop: '30px', + padding: '10px', }; const desktopAvatar = { From e17303453223ebb98e916174b4f89c859f6ab416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Mon, 19 Dec 2022 22:10:56 -0300 Subject: [PATCH 20/27] Include virtual room urls and update strings --- .../Classroom/AnnouncementsTab/index.js | 16 +++++++++------- src/screens/professor/Classroom/Header/styles.js | 4 +--- .../student/Classroom/AnnouncementsTab/index.js | 6 ++++-- src/utils/mocks/responses.js | 8 ++++++++ 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/screens/professor/Classroom/AnnouncementsTab/index.js b/src/screens/professor/Classroom/AnnouncementsTab/index.js index 28d8311..241d405 100644 --- a/src/screens/professor/Classroom/AnnouncementsTab/index.js +++ b/src/screens/professor/Classroom/AnnouncementsTab/index.js @@ -72,15 +72,18 @@ function AnnouncementsTab({ sx={{ display: 'flex', justifyContent: 'row' }} > Jitsi Meet -

Jitsi

+

+ Sala de aula virtual +

@@ -219,15 +222,14 @@ function AnnouncementsTab({ sx={{ display: 'flex', justifyContent: 'row' }} > Jitsi Meet -

Jitsi

+

Sala de aula virtual

- diff --git a/src/screens/professor/Classroom/Header/styles.js b/src/screens/professor/Classroom/Header/styles.js index babe6b3..3784cbd 100644 --- a/src/screens/professor/Classroom/Header/styles.js +++ b/src/screens/professor/Classroom/Header/styles.js @@ -10,12 +10,10 @@ const desktopPaper = classColor => ({ }); const desktopTabs = { - display: 'flex', marginLeft: '-20px', marginRight: '-20px', - marginBottom: '-20px', + marginBottom: '-30px', marginTop: '30px', - padding: '10px', }; const desktopAvatar = { diff --git a/src/screens/student/Classroom/AnnouncementsTab/index.js b/src/screens/student/Classroom/AnnouncementsTab/index.js index 90d169d..cded9cc 100644 --- a/src/screens/student/Classroom/AnnouncementsTab/index.js +++ b/src/screens/student/Classroom/AnnouncementsTab/index.js @@ -64,7 +64,9 @@ function AnnouncementsTab({ layoutType, announcementsTabData, classroom }) { sx={{ display: 'flex', justifyContent: 'row' }} > Jitsi Meet -

Jitsi

+

+ Sala de aula virtual +

+ + + + ); +} + +export default FormDialog; diff --git a/src/screens/professor/Classroom/AnnouncementsTab/index.js b/src/screens/professor/Classroom/AnnouncementsTab/index.js index c5af40b..74fbe3b 100644 --- a/src/screens/professor/Classroom/AnnouncementsTab/index.js +++ b/src/screens/professor/Classroom/AnnouncementsTab/index.js @@ -6,27 +6,50 @@ import { Grid, IconButton, Link, + Menu, + MenuItem, Skeleton, Stack, + TextField, + Tooltip, Typography, } from '@mui/material'; import MoreVertIcon from '@mui/icons-material/MoreVert'; import AnnouncementCard from '../../../../components/AnnouncementCard'; +import PublishAnnouncementCard from '../../../../components/PublishAnnouncementCard'; +import FormDialog from '../../../../components/FormDialog'; import styles from './styles'; import jitsiLogo from '../../../../assets/jitsi.svg'; import { createArrayFrom1ToN } from '../../../../utils/createArrayFrom1ToN'; -import PublishAnnouncementCard from '../../../../components/PublishAnnouncementCard'; function AnnouncementsTab({ layoutType, announcementsTabData, classroom, + onChangeEditInput, + onSaveEditChanges, user, }) { + const [anchorEl, setAnchorEl] = useState({ + virtualRoom: null, + appointmentSlots: null, + }); + const [dialogOpened, setDialogOpened] = useState(null); const [composingTextValue, setComposingTextValue] = useState(''); const { container, emptyStateContainer } = styles[layoutType]; + const onSaveEdit = anchorName => { + onSaveEditChanges(); + setDialogOpened(null); + setAnchorEl({ ...anchorEl, [anchorName]: null }); + }; + + const onDismissEdit = anchorName => { + setDialogOpened(null); + setAnchorEl({ ...anchorEl, [anchorName]: null }); + }; + const layoutResolver = (state, layoutType) => { if (layoutType === 'desktop') { switch (state) { @@ -82,9 +105,62 @@ function AnnouncementsTab({ Sala de aula virtual - - - + + + + setAnchorEl({ + ...anchorEl, + virtualRoom: e.currentTarget, + }) + } + aria-label="edit" + size="medium" + > + + + + + setAnchorEl({ ...anchorEl, virtualRoom: null }) + } + > + setDialogOpened('virtualRoom')} + > + Editar + + + , + ]} + onDismiss={() => onDismissEdit('virtualRoom')} + onSave={() => onSaveEdit('virtualRoom')} + />