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);