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/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/components/FormDialog/index.js b/src/components/FormDialog/index.js new file mode 100644 index 0000000..0b8ca30 --- /dev/null +++ b/src/components/FormDialog/index.js @@ -0,0 +1,33 @@ +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, +} from '@mui/material'; + +function FormDialog({ + isOpened, + title, + contentText, + inputs, + onDismiss, + onSave, +}) { + return ( + + {title} + + {contentText} + {[...inputs]} + + + + + + + ); +} + +export default FormDialog; 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..4bc58fd --- /dev/null +++ b/src/components/PublishAnnouncementCard/styles.js @@ -0,0 +1,47 @@ +// ========== 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', + padding: '10px', + width: '100%', + ':hover': { + color: '#32A041', + }, +}; + +const mobileCard = { + width: '100%', + padding: '10px ', +}; + +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/components/AssignmentCard/index.js b/src/components/professor/AssignmentCard/index.js similarity index 65% rename from src/components/AssignmentCard/index.js rename to src/components/professor/AssignmentCard/index.js index fc5758b..6e954f3 100644 --- a/src/components/AssignmentCard/index.js +++ b/src/components/professor/AssignmentCard/index.js @@ -9,19 +9,18 @@ import { } from '@mui/material'; import dayjs from 'dayjs'; -import { capitalizeFirstLetter } from '../../utils/capitalizeFirstLetter'; +import { capitalizeFirstLetter } from '../../../utils/capitalizeFirstLetter'; import styles from './styles'; +import { AssignmentTurnedIn, PendingActions } from '@mui/icons-material'; function AssignmentCard({ title, classrooms, dueDate, - scores, deliveredByStudents, reviewed, total, - isAssignmentToReview, layoutType, onClick, }) { @@ -64,29 +63,33 @@ function AssignmentCard({ > {classrooms.map(c => c.name).join(', ')} - - - Data de entrega: {' '} - {capitalizeFirstLetter( - dayjs(dueDate).format('dddd, DD/MM | HH:mm[h]') - )} + Data de entrega:{' '} + {capitalizeFirstLetter(dayjs(dueDate).format('dddd, DD/MM'))} + {deliveredByStudents >= 0 && total && ( - - Entregues: {' '} - {`${deliveredByStudents} de ${total}`} + + + {`${reviewed}`} corrigidas. )} {reviewed >= 0 && total && ( - - Corrigidas: {`${reviewed} de ${total}`} - - )} - {!isAssignmentToReview && ( - - Valor: - {scores.map(s => s.value).join(', ')} pts + + {' '} + {`${total - reviewed}`} pendentes. )} @@ -121,28 +124,34 @@ function AssignmentCard({ > {classrooms.map(c => c.name).join(', ')} - + - Data de entrega: - {capitalizeFirstLetter( - dayjs(dueDate).format('dddd, DD/MM | HH:mm[h]') - )} + Data de entrega:{' '} + {capitalizeFirstLetter(dayjs(dueDate).format('dddd, DD/MM'))} + {deliveredByStudents >= 0 && total && ( - - Entregues: {' '} - {`${deliveredByStudents} de ${total}`} + + + {`${reviewed}`} corrigidas. )} {reviewed >= 0 && total && ( - - Corrigidas: {`${reviewed} de ${total}`} - - )} - {!isAssignmentToReview && ( - - Valor: - {scores.map(s => s.value).join(', ')} pts + + {' '} + {`${total - reviewed}`} pendentes. )} diff --git a/src/components/professor/AssignmentCard/styles.js b/src/components/professor/AssignmentCard/styles.js new file mode 100644 index 0000000..1f513a6 --- /dev/null +++ b/src/components/professor/AssignmentCard/styles.js @@ -0,0 +1,143 @@ +// ========== Desktop ========== +const desktopCardContainer = classrooms => ({ + position: 'relative', + width: '35em', + borderLeft: `5px solid ${classrooms[0].color}`, +}); + +const desktopClassroomLinesIndicator = classroom => ({ + position: 'absolute', + height: '100%', + borderLeft: `5px solid ${classroom.color}`, + left: 0, +}); + +const desktopCardActionArea = { + display: 'flex', + flexDirection: 'row', + justifyContent: 'start', +}; + +const desktopCardContent = { + width: '100%', +}; + +const desktopTypographyTitle = { + overflow: 'hidden', + textOverflow: 'ellipsis', + display: '-webkit-box', + WebkitLineClamp: 2, + WebkitBoxOrient: 'vertical', + fontSize: '17px', +}; + +const desktopStackClassroomNames = { + width: '100%', +}; + +const desktopTypographyClassroomNames = { + fontStyle: 'italic', + fontWeight: 'bold', +}; + +const desktopDividerMiddle = { + marginTop: '10px', +}; + +const desktopTypographyDueDate = { + marginTop: '5px', + display: 'flex', + alignItems: 'center', + fontStyle: 'italic', +}; + +const desktop = { + cardContainer: desktopCardContainer, + classroomLinesIndicator: desktopClassroomLinesIndicator, + cardActionArea: desktopCardActionArea, + cardContent: desktopCardContent, + typographyTitle: desktopTypographyTitle, + stackClassroomNames: desktopStackClassroomNames, + typographyClassroomNames: desktopTypographyClassroomNames, + dividerMiddle: desktopDividerMiddle, + typographyDueDate: desktopTypographyDueDate, +}; + +// ========== Mobile ========== +const mobileCardContainer = classrooms => ({ + width: '100%', + borderTop: `5px solid ${classrooms[0].color}`, +}); + +const mobileClassroomLinesIndicator = classroom => ({ + position: 'absolute', + width: '100%', + borderTop: `5px solid ${classroom.color}`, + top: 0, +}); + +const mobileCardActionArea = { + position: 'relative', + display: 'flex', + flexDirection: 'column', +}; + +const mobileCardContent = { + width: '100%', +}; + +const mobileTypographyTitle = { + overflow: 'hidden', + textOverflow: 'ellipsis', + display: '-webkit-box', + WebkitLineClamp: 2, + WebkitBoxOrient: 'vertical', + fontSize: '17px', +}; + +const mobileStackClassroomNames = { + width: '100%', +}; + +const mobileTypographyClassroomNames = { + fontWeight: 'bold', +}; + +const mobileDividerMiddle = { + marginTop: '10px', +}; + +const mobileTypographyDueDate = { + marginTop: '5px', + display: 'flex', + alignItems: 'center', + fontStyle: 'italic', +}; + +const mobile = { + cardContainer: mobileCardContainer, + classroomLinesIndicator: mobileClassroomLinesIndicator, + cardActionArea: mobileCardActionArea, + cardContent: mobileCardContent, + typographyTitle: mobileTypographyTitle, + stackClassroomNames: mobileStackClassroomNames, + typographyClassroomNames: mobileTypographyClassroomNames, + dividerMiddle: mobileDividerMiddle, + typographyDueDate: mobileTypographyDueDate, +}; + +// ========== Unset ========== +const unset = { + cardContainer: null, + classroomLinesIndicator: null, + cardActionArea: null, + cardContent: null, + typographyTitle: null, + stackClassroomNames: null, + typographyClassroomNames: null, + dividerMiddle: null, + typographyDueDate: null, +}; + +const styles = { desktop, mobile, unset }; +export default styles; diff --git a/src/components/student/AssignmentCard/index.js b/src/components/student/AssignmentCard/index.js new file mode 100644 index 0000000..a78c155 --- /dev/null +++ b/src/components/student/AssignmentCard/index.js @@ -0,0 +1,151 @@ +import { + Card, + CardContent, + Typography, + CardActionArea, + Stack, + Tooltip, + Divider, +} from '@mui/material'; +import dayjs from 'dayjs'; + +import { capitalizeFirstLetter } from '../../../utils/capitalizeFirstLetter'; + +import styles from './styles'; +import { Assessment, Today } from '@mui/icons-material'; + +function AssignmentCard({ + title, + classrooms, + dueDate, + scores, + layoutType, + onClick, +}) { + const { + cardContainer, + classroomLinesIndicator, + cardActionArea, + cardContent, + typographyTitle, + stackClassroomNames, + typographyClassroomNames, + dividerMiddle, + typographyDueDate, + } = styles[layoutType]; + + if (layoutType === 'desktop') { + return ( + + {classrooms.length > 1 && + classrooms + .filter((_, i) => i > 0) + .map(c =>
)} + onClick()} sx={cardActionArea}> + + + + {title} + + + + + {classrooms.map(c => c.name).join(', ')} + + + + + + {capitalizeFirstLetter( + dayjs(dueDate).format('dddd, DD/MM, HH:mm[h]') + )} + + + + {scores.map(s => s.value).join(', ')} pontos + + + + + + ); + } else if (layoutType === 'mobile') { + return ( + + onClick()} sx={cardActionArea}> + {classrooms.length > 1 && + classrooms + .filter((_, i) => i > 0) + .map(c =>
)} + + + + {title} + + + + + {classrooms.map(c => c.name).join(', ')} + + + + + {capitalizeFirstLetter( + dayjs(dueDate).format('dddd, DD/MM, HH:mm[h]') + )} + + + + {scores.map(s => s.value).join(', ')} pontos + + + + + + ); + } +} + +export default AssignmentCard; diff --git a/src/components/AssignmentCard/styles.js b/src/components/student/AssignmentCard/styles.js similarity index 100% rename from src/components/AssignmentCard/styles.js rename to src/components/student/AssignmentCard/styles.js diff --git a/src/screens/Login/View.js b/src/screens/Login/View.js index b991509..260c971 100644 --- a/src/screens/Login/View.js +++ b/src/screens/Login/View.js @@ -63,6 +63,7 @@ function View({ type="password" value={password} onChange={e => onChangePassword(e.target.value)} + onKeyDown={e => (e.key === 'Enter' ? onTryLogin() : null)} /> + + + + +

Próximas Atividades

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

Nenhuma atividade encontrada!

+
+ )} +
+
+ + + +

+ Horários de Atendimento +

+ + + setAnchorEl({ + ...anchorEl, + appointmentSlots: e.currentTarget, + }) + } + aria-label="edit" + size="medium" + > + + + + + setAnchorEl({ ...anchorEl, appointmentSlots: null }) + } + > + setDialogOpened('appointmentSlots')} + > + Editar + + + ( + + )), + ]} + onDismiss={() => onDismissEdit('appointmentSlots')} + onSave={() => onSaveEdit('appointmentSlots')} + /> +
+ {classroom.appointmentSlots.map((appts, index) => ( + + {appts.weekDay}, {appts.start}h - {appts.end}h + + ))} +
+
+ + + + + setComposingTextValue(e.target.value)} + /> + {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 +

+ Sala de aula virtual +

+
+ + setAnchorEl(e.currentTarget)} + aria-label="edit" + size="medium" + > + + + + setAnchorEl(null)} + > + setDialogOpened('virtualRoom')} + > + Editar + + + , + ]} + onDismiss={() => onDismissEdit('virtualRoom')} + onSave={() => onSaveEdit('virtualRoom')} + /> +
+ +
+
+ + +

Próximas Atividades

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

Nenhuma atividade encontrada!

+
+ )} +
+
+ + +

Horários de Atendimento

+ {classroom.appointmentSlots.map((appts, index) => ( + + {appts.weekDay}, {appts.start}h - {appts.end}h + + ))} +
+
+
+ + setComposingTextValue(e.target.value)} + /> + {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..46b3813 --- /dev/null +++ b/src/screens/professor/Classroom/AnnouncementsTab/styles.js @@ -0,0 +1,49 @@ +// ========== 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, + emptyStateContainer: 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..33ee9fa --- /dev/null +++ b/src/screens/professor/Classroom/AssignmentsTab/index.js @@ -0,0 +1,413 @@ +import { + Container, + Fab, + Link, + Skeleton, + Stack, + Typography, +} from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import dayjs from 'dayjs'; +import { capitalizeFirstLetter } from '../../../../utils/capitalizeFirstLetter'; +import styles from './styles'; + +function AssignmentsTab({ assignmentsTabData, layoutType }) { + const layoutResolver = (state, assignments, layoutType) => { + const { + externalContainer, + innerContainer, + sectionTitle, + assignmentContainer, + assignmentTypography, + assignmentLink, + assignmentDueDate, + assignmentScores, + emptyStateContainer, + } = styles[layoutType]; + if (layoutType === 'desktop') { + switch (state) { + case 'loading': + return ( + + + + + + + + + + + + + + + + + + + + + + + + + ); + case 'idle': + const assesments = assignments.filter(a => a.type === 'assessment'); + const projects = assignments.filter(a => a.type === 'project'); + + return ( + + + + Criar atividade + + + + Provas + + + {assesments.length !== 0 ? ( + assesments.map(a => ( + + + + {a.title} + + + + Data de entrega: {' '} + {capitalizeFirstLetter( + dayjs(a.dueDate).format('dddd, DD/MM | HH:mm[h]') + )} + + + Valor: + {a.scores.map(s => s.value).join(', ')} pts + + + )) + ) : ( + +

Nenhuma prova encontrada!

+
+ )} +
+
+ + + + Trabalhos + + + {projects.length !== 0 ? ( + projects.map(a => ( + + + + {a.title} + + + + Data de entrega: {' '} + {capitalizeFirstLetter( + dayjs(a.dueDate).format('dddd, DD/MM | HH:mm[h]') + )} + + + Valor: + {a.scores.map(s => s.value).join(', ')} pts + + + )) + ) : ( + +

Nenhum trabalho encontrado!

+
+ )} +
+
+
+ ); + case 'gone': + return null; + default: + return null; + } + } else if (layoutType === 'mobile') { + switch (state) { + case 'loading': + return ( + + + + + + + + + + + + ); + case 'idle': + const assesments = assignments.filter(a => a.type === 'assessment'); + const projects = assignments.filter(a => a.type === 'project'); + + return ( + + + + Criar atividade + + + + Provas + + + {assesments.length !== 0 ? ( + assesments.map(a => ( + + + + {a.title} + + + + Data de entrega: {' '} + {capitalizeFirstLetter( + dayjs(a.dueDate).format('dddd, DD/MM | HH:mm[h]') + )} + + + Valor: + {a.scores.map(s => s.value).join(', ')} pts + + + )) + ) : ( + +

Nenhuma prova encontrada!

+
+ )} +
+
+ + + + Trabalhos + + + {projects.length !== 0 ? ( + projects.map(a => ( + + + + {a.title} + + + + Data de entrega: {' '} + {capitalizeFirstLetter( + dayjs(a.dueDate).format('dddd, DD/MM | HH:mm[h]') + )} + + + Valor: + {a.scores.map(s => s.value).join(', ')} pts + + + )) + ) : ( + +

Nenhum trabalho encontrado!

+
+ )} +
+
+
+ ); + 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..0493789 --- /dev/null +++ b/src/screens/professor/Classroom/AssignmentsTab/styles.js @@ -0,0 +1,138 @@ +// ========== Desktop ========== +const desktopExternalContainer = { + display: 'flex', + flexDirection: 'column', + marginTop: '50px', + height: '100vh', + alignItems: 'flex-end', +}; + +const desktopInnerContainer = { + width: '90%', + marginBottom: '30px', +}; + +const desktopSectionTitle = { + padding: '10px', + borderBottom: '2px solid #00420D', + color: '#00420D', +}; + +const desktopAssignmentContainer = { + width: '95%', + padding: '20px', + borderBottom: '2px solid #BCBCBC', +}; + +const desktopAssignmentTypography = {}; + +const desktopAssignmentLink = { + color: 'black', + textDecoration: 'underline #000000', +}; + +const desktopAssignmentDueDate = { + marginTop: '15px', + fontSize: '15px', +}; + +const desktopAssignmentScores = { + fontSize: '15px', +}; + +const desktopEmptyStateContainer = { + display: 'flex', + justifyContent: 'center', + marginTop: '30px', +}; + +const desktop = { + externalContainer: desktopExternalContainer, + innerContainer: desktopInnerContainer, + sectionTitle: desktopSectionTitle, + assignmentContainer: desktopAssignmentContainer, + assignmentTypography: desktopAssignmentTypography, + assignmentLink: desktopAssignmentLink, + assignmentDueDate: desktopAssignmentDueDate, + assignmentScores: desktopAssignmentScores, + emptyStateContainer: desktopEmptyStateContainer, +}; + +// ========== Mobile ========== +const mobileExternalContainer = { + marginTop: '50px', + height: '100vh', +}; + +const mobileInnerContainer = { + width: '100%', + marginBottom: '30px', + marginTop: '30px', +}; + +const mobileSectionTitle = { + padding: '10px', + borderBottom: '2px solid #00420D', + color: '#00420D', +}; + +const mobileAssignmentContainer = { + width: '100%', + padding: '20px', + borderBottom: '2px solid #BCBCBC', +}; + +const mobileAssignmentTypography = { + overflow: 'hidden', + textOverflow: 'ellipsis', + display: '-webkit-box', + WebkitLineClamp: 2, + WebkitBoxOrient: 'vertical', +}; + +const mobileAssignmentLink = { + color: 'black', + textDecoration: 'underline #000000', +}; + +const mobileAssignmentDueDate = { + marginTop: '10px', + fontSize: '12px', +}; + +const mobileAssignmentScores = { + fontSize: '12px', +}; + +const mobileEmptyStateContainer = { + display: 'flex', + justifyContent: 'center', + marginTop: '30px', +}; + +const mobile = { + externalContainer: mobileExternalContainer, + innerContainer: mobileInnerContainer, + sectionTitle: mobileSectionTitle, + assignmentContainer: mobileAssignmentContainer, + assignmentTypography: mobileAssignmentTypography, + assignmentLink: mobileAssignmentLink, + assignmentDueDate: mobileAssignmentDueDate, + assignmentScores: mobileAssignmentScores, + emptyStateContainer: mobileEmptyStateContainer, +}; + +// ========== Unset ========== +const unset = { + externalContainer: null, + innerContainer: null, + sectionTitle: null, + assignmentContainer: null, + assignmentTypography: null, + assignmentLink: null, + assignmentDueDate: null, + assignmentScores: null, +}; + +const styles = { desktop, mobile, unset }; +export default styles; 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/Header/index.js b/src/screens/professor/Classroom/Header/index.js new file mode 100644 index 0000000..e077313 --- /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..03ba4ab --- /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: '30px', +}); + +const desktopTabs = { + marginLeft: '-20px', + marginRight: '-20px', + marginBottom: '-30px', + 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: '20px', +}); + +const mobileTabs = { + marginLeft: '-10px', + marginRight: '-10px', + marginBottom: '-20px', + 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/Classroom/PeopleTab/index.js b/src/screens/professor/Classroom/PeopleTab/index.js similarity index 99% rename from src/screens/Classroom/PeopleTab/index.js rename to src/screens/professor/Classroom/PeopleTab/index.js index 3080ee3..9dfde7b 100644 --- a/src/screens/Classroom/PeopleTab/index.js +++ b/src/screens/professor/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/professor/Classroom/PeopleTab/styles.js similarity index 96% rename from src/screens/Classroom/PeopleTab/styles.js rename to src/screens/professor/Classroom/PeopleTab/styles.js index 30668db..d49b5de 100644 --- a/src/screens/Classroom/PeopleTab/styles.js +++ b/src/screens/professor/Classroom/PeopleTab/styles.js @@ -24,12 +24,14 @@ const desktopPersonContainer = { }; const desktopPersonAvatar = { - width: '60px', - height: '60px', + width: '50px', + height: '50px', marginRight: '15px', }; -const desktopPersonName = {}; +const desktopPersonName = { + fontSize: '1.3em', +}; const desktopEmptyStateContainer = { display: 'flex', diff --git a/src/screens/professor/Classroom/View.js b/src/screens/professor/Classroom/View.js new file mode 100644 index 0000000..59dbd92 --- /dev/null +++ b/src/screens/professor/Classroom/View.js @@ -0,0 +1,51 @@ +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'; + +function View({ + layoutType, + classroom, + selectedTabOption, + onSelectTabOption, + announcementsTabData, + assignmentsTabData, + peopleTabData, + gradesTabData, + user, + onChangeEditInput, + onSaveEditChanges, + 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..fa86816 --- /dev/null +++ b/src/screens/professor/Classroom/index.js @@ -0,0 +1,156 @@ +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, state } = useUser(); + const [classroom, setClassroom] = useState(null); + const [tabData, setTabData] = useState(null); + + const [selectedTabOption, setSelectedTabOption] = useState( + TAB_OPTIONS.announcements.value + ); + + const onChangeEditInput = e => { + const name = e.target.name; + const value = e.target.value; + + setClassroom(prev => ({ ...prev, [name]: value })); + }; + + const onSaveEditChanges = () => { + console.log('Saving edit changes...'); + console.log(classroom); + }; + + const fetchAndPopulateAnnouncementsTabData = useCallback(async () => { + setTabData({ tab: 'announcements', state: 'loading' }); + const announcements = await userService.fetchClassroomAnnouncements( + params.id + ); + + const upcomingAssignments = + await userService.fetchUpcomingAssignmentsByClassId(params.id); + + setTabData({ + tab: 'announcements', + state: 'idle', + announcements: [...announcements.data], + upcomingAssignments: [...upcomingAssignments.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; + } + } + + if (!classroom) { + getClassroomById(params.id); + } + + 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]); + + 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) { + case TAB_OPTIONS.announcements.value: + fetchAndPopulateAnnouncementsTabData(); + break; + case TAB_OPTIONS.assignments.value: + fetchAndPopulateAssignmentsTabData(); + break; + case TAB_OPTIONS.people.value: + fetchAndPopulatePeopleTabData(); + break; + case TAB_OPTIONS.grades.value: + fetchAndPopulateGradesTabData(); + break; + default: + console.log('Invalid tab option'); + } + } + getSelectedTabData(); + }, [ + selectedTabOption, + params, + fetchAndPopulateAnnouncementsTabData, + fetchAndPopulateAssignmentsTabData, + fetchAndPopulatePeopleTabData, + fetchAndPopulateGradesTabData, + ]); + + return ( + setSelectedTabOption(value)} + announcementsTabData={ + tabData && tabData.tab === 'announcements' ? tabData : { state: 'gone' } + } + assignmentsTabData={ + tabData && tabData.tab === 'assignments' ? tabData : { state: 'gone' } + } + peopleTabData={ + tabData && tabData.tab === 'people' ? tabData : { state: 'gone' } + } + gradesTabData={ + tabData && tabData.tab === 'grades' ? tabData : { state: 'gone' } + } + user={state && state.user} + onChangeEditInput={onChangeEditInput} + onSaveEditChanges={onSaveEditChanges} + isLoading={tabData && tabData.state === 'loading'} + /> + ); +} + +export default Classroom; diff --git a/src/screens/Classroom/styles.js b/src/screens/professor/Classroom/styles.js similarity index 100% rename from src/screens/Classroom/styles.js rename to src/screens/professor/Classroom/styles.js 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/screens/professor/Home/View.js b/src/screens/professor/Home/View.js index 0651aea..2bed30d 100644 --- a/src/screens/professor/Home/View.js +++ b/src/screens/professor/Home/View.js @@ -1,8 +1,11 @@ import { Grid, Skeleton, Stack } from '@mui/material'; import { Container } from '@mui/system'; -import AssignmentCard from '../../../components/AssignmentCard'; + +import AssignmentCard from '../../../components/professor/AssignmentCard'; import ClassCard from '../../../components/ClassCard'; + import { createArrayFrom1ToN } from '../../../utils/createArrayFrom1ToN'; + import styles from './styles'; function View({ @@ -81,11 +84,9 @@ function View({ title={assignment.title} classrooms={assignment.classrooms} dueDate={assignment.dueDate} - scores={assignment.scores} layoutType={layoutType} deliveredByStudents={assignment.deliveredByStudents} reviewed={assignment.reviewed} - isAssignmentToReview={assignment.status !== null} total={assignment.total} onClick={() => onClickAssignmentCard(assignment.id)} /> @@ -170,11 +171,9 @@ function View({ title={assignment.title} classrooms={assignment.classrooms} dueDate={assignment.dueDate} - scores={assignment.scores} layoutType={layoutType} deliveredByStudents={assignment.deliveredByStudents} reviewed={assignment.reviewed} - isAssignmentToReview={assignment.status !== null} total={assignment.total} onClick={() => onClickAssignmentCard(assignment.id)} /> diff --git a/src/screens/Classroom/AnnouncementsTab/index.js b/src/screens/student/Classroom/AnnouncementsTab/index.js similarity index 95% rename from src/screens/Classroom/AnnouncementsTab/index.js rename to src/screens/student/Classroom/AnnouncementsTab/index.js index 5540a94..cded9cc 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]; @@ -64,7 +64,9 @@ function AnnouncementsTab({ layoutType, announcementsTabData, classroom }) { sx={{ display: 'flex', justifyContent: 'row' }} > Jitsi Meet -

Jitsi

+

+ Sala de aula virtual +