summaryrefslogtreecommitdiff
path: root/src/screens/student/Classroom
diff options
context:
space:
mode:
Diffstat (limited to 'src/screens/student/Classroom')
-rw-r--r--src/screens/student/Classroom/AnnouncementsTab/index.js300
-rw-r--r--src/screens/student/Classroom/AnnouncementsTab/styles.js48
-rw-r--r--src/screens/student/Classroom/AssignmentsTab/index.js384
-rw-r--r--src/screens/student/Classroom/AssignmentsTab/styles.js134
-rw-r--r--src/screens/student/Classroom/Header/index.js62
-rw-r--r--src/screens/student/Classroom/Header/styles.js90
-rw-r--r--src/screens/student/Classroom/PeopleTab/index.js244
-rw-r--r--src/screens/student/Classroom/PeopleTab/styles.js116
-rw-r--r--src/screens/student/Classroom/View.js44
-rw-r--r--src/screens/student/Classroom/index.js118
-rw-r--r--src/screens/student/Classroom/styles.js34
-rw-r--r--src/screens/student/Classroom/tabOptions.js16
12 files changed, 1590 insertions, 0 deletions
diff --git a/src/screens/student/Classroom/AnnouncementsTab/index.js b/src/screens/student/Classroom/AnnouncementsTab/index.js
new file mode 100644
index 0000000..cded9cc
--- /dev/null
+++ b/src/screens/student/Classroom/AnnouncementsTab/index.js
@@ -0,0 +1,300 @@
+import {
+ Button,
+ Card,
+ Container,
+ Grid,
+ Link,
+ Skeleton,
+ Stack,
+ Typography,
+} from '@mui/material';
+import AnnouncementCard from '../../../../components/AnnouncementCard';
+import { createArrayFrom1ToN } from '../../../../utils/createArrayFrom1ToN';
+
+import styles from './styles';
+import jitsiLogo from '../../../../assets/jitsi.svg';
+
+function AnnouncementsTab({ layoutType, announcementsTabData, classroom }) {
+ const { container, emptyStateContainer } = styles[layoutType];
+
+ const layoutResolver = (state, layoutType) => {
+ if (layoutType === 'desktop') {
+ switch (state) {
+ case 'loading':
+ return (
+ <Grid sx={container} container spacing={2}>
+ <Grid sx={{ padding: '0 !important' }} item xs={4}>
+ {createArrayFrom1ToN(3).map(i => (
+ <Skeleton
+ key={i}
+ variant="rectangular"
+ width="100%"
+ height={200}
+ sx={{ marginBottom: '30px' }}
+ />
+ ))}
+ </Grid>
+ <Grid sx={{ paddingTop: '0 !important' }} item xs={8}>
+ {createArrayFrom1ToN(4).map(i => (
+ <Skeleton
+ key={i}
+ variant="rectangular"
+ width="100%"
+ height={250}
+ sx={{ marginBottom: '30px' }}
+ />
+ ))}
+ </Grid>
+ </Grid>
+ );
+
+ case 'idle':
+ return (
+ <Grid sx={container} container spacing={2}>
+ <Grid sx={{ padding: '0 !important' }} item xs={4}>
+ <Stack gap="30px">
+ <Card
+ sx={{ width: '100%', padding: '20px' }}
+ elevation={4}
+ variant="elevation"
+ >
+ <Stack justifyContent="flex-start" spacing={1}>
+ <Container
+ disableGutters
+ sx={{ display: 'flex', justifyContent: 'row' }}
+ >
+ <img src={jitsiLogo} alt="Jitsi Meet" />
+ <h3 style={{ fontWeight: 500 }}>
+ Sala de aula virtual
+ </h3>
+ </Container>
+
+ <Button
+ variant="contained"
+ href="https://google.com"
+ target="__blank"
+ >
+ Entrar
+ </Button>
+ </Stack>
+ </Card>
+ <Card
+ sx={{ width: '100%', padding: '20px' }}
+ elevation={4}
+ variant="elevation"
+ >
+ <Stack justifyContent="flex-start" spacing={1}>
+ <h3 style={{ fontWeight: 500 }}>Próximas Atividades</h3>
+ {announcementsTabData.upcomingAssignments.length !== 0 ? (
+ announcementsTabData.upcomingAssignments.map(ua => (
+ <Link
+ href={`/assignment/${ua.id}`}
+ sx={{ fontSize: '15px' }}
+ key={ua.id}
+ >
+ {ua.title}
+ </Link>
+ ))
+ ) : (
+ <Container disableGutters>
+ <p>Nenhuma atividade encontrada!</p>
+ </Container>
+ )}
+ </Stack>
+ </Card>
+ <Card
+ sx={{ width: '100%', padding: '20px' }}
+ elevation={4}
+ variant="elevation"
+ >
+ <Stack justifyContent="flex-start" spacing={1}>
+ <h3 style={{ fontWeight: 500 }}>
+ Horários de Atendimento
+ </h3>
+ {classroom.appointmentSlots.map((appts, index) => (
+ <Typography key={index} variant="body1">
+ {appts.weekDay}, {appts.start}h - {appts.end}h
+ </Typography>
+ ))}
+ </Stack>
+ </Card>
+ </Stack>
+ </Grid>
+ <Grid sx={{ paddingTop: '0 !important' }} item xs={8}>
+ <Stack
+ sx={{ width: '100%', paddingTop: 0 }}
+ alignItems="center"
+ justifyContent="center"
+ flexWrap="wrap"
+ direction="row"
+ gap="30px"
+ >
+ {announcementsTabData.announcements.length !== 0 ? (
+ announcementsTabData.announcements.map(announcement => (
+ <AnnouncementCard
+ key={announcement.id}
+ announcement={announcement}
+ />
+ ))
+ ) : (
+ <Container sx={emptyStateContainer} disableGutters>
+ <p>Nenhum comunicado encontrado!</p>
+ </Container>
+ )}
+ </Stack>
+ </Grid>
+ </Grid>
+ );
+
+ case 'gone':
+ return null;
+
+ default:
+ return null;
+ }
+ } else if (layoutType === 'mobile') {
+ switch (state) {
+ case 'loading':
+ return (
+ <Stack
+ alignItems="center"
+ justifyContent="center"
+ flexWrap="wrap"
+ direction="row"
+ gap="30px"
+ sx={{ marginTop: '30px' }}
+ >
+ {createArrayFrom1ToN(3).map(i => (
+ <Skeleton
+ key={i}
+ variant="rectangular"
+ width="100%"
+ height={200}
+ sx={{ marginBottom: '30px' }}
+ />
+ ))}
+ {createArrayFrom1ToN(4).map(i => (
+ <Skeleton
+ key={i}
+ variant="rectangular"
+ width="100%"
+ height={250}
+ sx={{ marginBottom: '30px' }}
+ />
+ ))}
+ </Stack>
+ );
+
+ case 'idle':
+ return (
+ <Stack
+ alignItems="center"
+ justifyContent="center"
+ flexWrap="wrap"
+ direction="row"
+ gap="30px"
+ sx={{ marginTop: '30px' }}
+ >
+ <Stack gap="30px" sx={{ width: '100%' }}>
+ <Card
+ sx={{ width: '100%', padding: '20px' }}
+ elevation={4}
+ variant="elevation"
+ >
+ <Stack justifyContent="flex-start" spacing={1}>
+ <Container
+ disableGutters
+ sx={{ display: 'flex', justifyContent: 'row' }}
+ >
+ <img src={jitsiLogo} alt="Jitsi Meet" />
+ <h3 style={{ fontWeight: 500 }}>Sala de aula virtual</h3>
+ </Container>
+
+ <Button
+ variant="contained"
+ href="https://google.com"
+ target="__blank"
+ >
+ Entrar
+ </Button>
+ </Stack>
+ </Card>
+ <Card
+ sx={{ width: '100%', padding: '20px' }}
+ elevation={4}
+ variant="elevation"
+ >
+ <Stack justifyContent="flex-start" spacing={1}>
+ <h3 style={{ fontWeight: 500 }}>Próximas Atividades</h3>
+ {announcementsTabData.upcomingAssignments.length !== 0 ? (
+ announcementsTabData.upcomingAssignments.map(ua => (
+ <Link
+ href={`/assignment/${ua.id}`}
+ sx={{ fontSize: '15px' }}
+ key={ua.id}
+ >
+ {ua.title}
+ </Link>
+ ))
+ ) : (
+ <Container disableGutters>
+ <p>Nenhuma atividade encontrada!</p>
+ </Container>
+ )}
+ </Stack>
+ </Card>
+ <Card
+ sx={{ width: '100%', padding: '20px' }}
+ elevation={4}
+ variant="elevation"
+ >
+ <Stack justifyContent="flex-start" spacing={1}>
+ <h3 style={{ fontWeight: 500 }}>Horários de Atendimento</h3>
+ {classroom.appointmentSlots.map((appts, index) => (
+ <Typography key={index} variant="body1">
+ {appts.weekDay}, {appts.start}h - {appts.end}h
+ </Typography>
+ ))}
+ </Stack>
+ </Card>
+ </Stack>
+ <Stack
+ sx={{ width: '100%' }}
+ alignItems="center"
+ justifyContent="center"
+ flexWrap="wrap"
+ direction="row"
+ gap="30px"
+ >
+ {announcementsTabData.announcements.length !== 0 ? (
+ announcementsTabData.announcements.map(announcement => (
+ <AnnouncementCard
+ key={announcement.id}
+ announcement={announcement}
+ />
+ ))
+ ) : (
+ <Container sx={emptyStateContainer} disableGutters>
+ <p>Nenhum comunicado encontrado!</p>
+ </Container>
+ )}
+ </Stack>
+ </Stack>
+ );
+
+ case 'gone':
+ return null;
+
+ default:
+ return null;
+ }
+ }
+ };
+
+ return layoutResolver(
+ announcementsTabData && announcementsTabData.state,
+ layoutType
+ );
+}
+
+export default AnnouncementsTab;
diff --git a/src/screens/student/Classroom/AnnouncementsTab/styles.js b/src/screens/student/Classroom/AnnouncementsTab/styles.js
new file mode 100644
index 0000000..d7d218a
--- /dev/null
+++ b/src/screens/student/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/student/Classroom/AssignmentsTab/index.js b/src/screens/student/Classroom/AssignmentsTab/index.js
new file mode 100644
index 0000000..19757b4
--- /dev/null
+++ b/src/screens/student/Classroom/AssignmentsTab/index.js
@@ -0,0 +1,384 @@
+import { Container, Link, Skeleton, Stack, Typography } from '@mui/material';
+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 (
+ <Container sx={externalContainer} disableGutters>
+ <Stack alignItems="center">
+ <Skeleton
+ variant="rectangular"
+ width="90%"
+ height={70}
+ sx={{ marginBottom: '30px' }}
+ />
+ <Stack alignItems="flex-start" sx={{ width: '90%' }}>
+ <Skeleton variant="rectangular" height={50} width="95%" />
+ <Skeleton
+ variant="rectangular"
+ height={20}
+ width={450}
+ sx={{ marginTop: '25px' }}
+ />
+ <Skeleton
+ variant="rectangular"
+ height={20}
+ width={300}
+ sx={{ marginTop: '15px' }}
+ />
+ </Stack>
+ <Stack
+ alignItems="flex-start"
+ sx={{ width: '90%', marginTop: '30px' }}
+ >
+ <Skeleton variant="rectangular" height={50} width="95%" />
+ <Skeleton
+ variant="rectangular"
+ height={20}
+ width={450}
+ sx={{ marginTop: '25px' }}
+ />
+ <Skeleton
+ variant="rectangular"
+ height={20}
+ width={300}
+ sx={{ marginTop: '15px' }}
+ />
+ </Stack>
+ </Stack>
+
+ <Stack sx={{ marginTop: '50px' }} alignItems="center">
+ <Skeleton
+ variant="rectangular"
+ width="90%"
+ height={70}
+ sx={{ marginBottom: '30px' }}
+ />
+ <Stack alignItems="flex-start" sx={{ width: '90%' }}>
+ <Skeleton variant="rectangular" height={50} width="95%" />
+ <Skeleton
+ variant="rectangular"
+ height={20}
+ width={450}
+ sx={{ marginTop: '25px' }}
+ />
+ <Skeleton
+ variant="rectangular"
+ height={20}
+ width={300}
+ sx={{ marginTop: '15px' }}
+ />
+ </Stack>
+ </Stack>
+ </Container>
+ );
+
+ case 'idle':
+ const assesments = assignments.filter(a => a.type === 'assessment');
+ const projects = assignments.filter(a => a.type === 'project');
+
+ return (
+ <Container sx={externalContainer} disableGutters>
+ <Container sx={innerContainer} disableGutters>
+ <Typography sx={sectionTitle} variant="h4">
+ Provas
+ </Typography>
+ <Stack alignItems="center">
+ {assesments.length !== 0 ? (
+ assesments.map(a => (
+ <Container
+ key={a.id}
+ sx={assignmentContainer}
+ disableGutters
+ >
+ <Typography sx={assignmentTypography} variant="h5">
+ <Link
+ sx={assignmentLink}
+ href={`/assignment/${a.id}`}
+ >
+ {a.title}
+ </Link>
+ </Typography>
+ <Typography
+ sx={assignmentDueDate}
+ variant="p"
+ component="div"
+ >
+ <strong>Data de entrega: </strong>{' '}
+ {capitalizeFirstLetter(
+ dayjs(a.dueDate).format('dddd, DD/MM | HH:mm[h]')
+ )}
+ </Typography>
+ <Typography
+ sx={assignmentScores}
+ variant="p"
+ component="div"
+ >
+ <strong>Valor: </strong>
+ {a.scores.map(s => s.value).join(', ')} pts
+ </Typography>
+ </Container>
+ ))
+ ) : (
+ <Container sx={emptyStateContainer} disableGutters>
+ <p>Nenhuma prova encontrada!</p>
+ </Container>
+ )}
+ </Stack>
+ </Container>
+
+ <Container sx={innerContainer} disableGutters>
+ <Typography sx={sectionTitle} variant="h4">
+ Trabalhos
+ </Typography>
+ <Stack alignItems="center">
+ {projects.length !== 0 ? (
+ projects.map(a => (
+ <Container
+ key={a.id}
+ sx={assignmentContainer}
+ disableGutters
+ >
+ <Typography variant="h5">
+ <Link
+ sx={assignmentLink}
+ href={`/assignment/${a.id}`}
+ >
+ {a.title}
+ </Link>
+ </Typography>
+ <Typography
+ sx={assignmentDueDate}
+ variant="p"
+ component="div"
+ >
+ <strong>Data de entrega: </strong>{' '}
+ {capitalizeFirstLetter(
+ dayjs(a.dueDate).format('dddd, DD/MM | HH:mm[h]')
+ )}
+ </Typography>
+ <Typography
+ sx={assignmentScores}
+ variant="p"
+ component="div"
+ >
+ <strong>Valor: </strong>
+ {a.scores.map(s => s.value).join(', ')} pts
+ </Typography>
+ </Container>
+ ))
+ ) : (
+ <Container sx={emptyStateContainer} disableGutters>
+ <p>Nenhum trabalho encontrado!</p>
+ </Container>
+ )}
+ </Stack>
+ </Container>
+ </Container>
+ );
+
+ case 'gone':
+ return null;
+
+ default:
+ return null;
+ }
+ } else if (layoutType === 'mobile') {
+ switch (state) {
+ case 'loading':
+ return (
+ <Stack
+ alignItems="center"
+ flexWrap="wrap"
+ direction="row"
+ sx={{ marginTop: '30px' }}
+ >
+ <Skeleton
+ variant="rectangular"
+ width="100%"
+ height={70}
+ sx={{ marginTop: '30px' }}
+ />
+ <Skeleton
+ variant="rectangular"
+ width="100%"
+ height={30}
+ sx={{ marginTop: '20px' }}
+ />
+ <Skeleton
+ variant="rectangular"
+ width="100%"
+ height={15}
+ sx={{ marginTop: '20px' }}
+ />
+ <Skeleton
+ variant="rectangular"
+ width={250}
+ height={15}
+ sx={{ marginTop: '10px' }}
+ />
+
+ <Skeleton
+ variant="rectangular"
+ width="100%"
+ height={70}
+ sx={{ marginTop: '50px' }}
+ />
+ <Skeleton
+ variant="rectangular"
+ width="100%"
+ height={30}
+ sx={{ marginTop: '20px' }}
+ />
+ <Skeleton
+ variant="rectangular"
+ width="100%"
+ height={15}
+ sx={{ marginTop: '20px' }}
+ />
+ <Skeleton
+ variant="rectangular"
+ width={250}
+ height={15}
+ sx={{ marginTop: '10px' }}
+ />
+ </Stack>
+ );
+
+ case 'idle':
+ const assesments = assignments.filter(a => a.type === 'assessment');
+ const projects = assignments.filter(a => a.type === 'project');
+
+ return (
+ <Container sx={externalContainer} disableGutters>
+ <Container sx={innerContainer} disableGutters>
+ <Typography sx={sectionTitle} variant="h4">
+ Provas
+ </Typography>
+ <Stack alignItems="center">
+ {assesments.length !== 0 ? (
+ assesments.map(a => (
+ <Container
+ key={a.id}
+ sx={assignmentContainer}
+ disableGutters
+ >
+ <Typography variant="body1" sx={assignmentTypography}>
+ <Link
+ sx={assignmentLink}
+ href={`/assignment/${a.id}`}
+ >
+ {a.title}
+ </Link>
+ </Typography>
+ <Typography
+ sx={assignmentDueDate}
+ variant="p"
+ component="div"
+ >
+ <strong>Data de entrega: </strong>{' '}
+ {capitalizeFirstLetter(
+ dayjs(a.dueDate).format('dddd, DD/MM | HH:mm[h]')
+ )}
+ </Typography>
+ <Typography
+ sx={assignmentScores}
+ variant="p"
+ component="div"
+ >
+ <strong>Valor: </strong>
+ {a.scores.map(s => s.value).join(', ')} pts
+ </Typography>
+ </Container>
+ ))
+ ) : (
+ <Container sx={emptyStateContainer} disableGutters>
+ <p>Nenhuma prova encontrada!</p>
+ </Container>
+ )}
+ </Stack>
+ </Container>
+
+ <Container sx={innerContainer} disableGutters>
+ <Typography sx={sectionTitle} variant="h4">
+ Trabalhos
+ </Typography>
+ <Stack alignItems="center">
+ {projects.length !== 0 ? (
+ projects.map(a => (
+ <Container
+ key={a.id}
+ sx={assignmentContainer}
+ disableGutters
+ >
+ <Typography variant="body1" sx={assignmentTypography}>
+ <Link
+ sx={assignmentLink}
+ href={`/assignment/${a.id}`}
+ >
+ {a.title}
+ </Link>
+ </Typography>
+ <Typography
+ sx={assignmentDueDate}
+ variant="p"
+ component="div"
+ >
+ <strong>Data de entrega: </strong>{' '}
+ {capitalizeFirstLetter(
+ dayjs(a.dueDate).format('dddd, DD/MM | HH:mm[h]')
+ )}
+ </Typography>
+ <Typography
+ sx={assignmentScores}
+ variant="p"
+ component="div"
+ >
+ <strong>Valor: </strong>
+ {a.scores.map(s => s.value).join(', ')} pts
+ </Typography>
+ </Container>
+ ))
+ ) : (
+ <Container sx={emptyStateContainer} disableGutters>
+ <p>Nenhum trabalho encontrado!</p>
+ </Container>
+ )}
+ </Stack>
+ </Container>
+ </Container>
+ );
+
+ case 'gone':
+ return null;
+
+ default:
+ return null;
+ }
+ }
+ };
+
+ return layoutResolver(
+ assignmentsTabData && assignmentsTabData.state,
+ assignmentsTabData && assignmentsTabData.assignments,
+ layoutType
+ );
+}
+
+export default AssignmentsTab;
diff --git a/src/screens/student/Classroom/AssignmentsTab/styles.js b/src/screens/student/Classroom/AssignmentsTab/styles.js
new file mode 100644
index 0000000..8c97bd3
--- /dev/null
+++ b/src/screens/student/Classroom/AssignmentsTab/styles.js
@@ -0,0 +1,134 @@
+// ========== Desktop ==========
+const desktopExternalContainer = {
+ marginTop: '50px',
+ height: '100vh',
+};
+
+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',
+};
+
+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/student/Classroom/Header/index.js b/src/screens/student/Classroom/Header/index.js
new file mode 100644
index 0000000..6a4a1a8
--- /dev/null
+++ b/src/screens/student/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 ? (
+ <Skeleton variant="rectangular" width="100%" height={240} />
+ ) : (
+ <Container disableGutters>
+ <Paper sx={paper(classroom.color)} elevation={4} variant="elevation">
+ <h1 style={title}>{classroom.name}</h1>
+ <Stack alignItems="center" direction="row" spacing={1}>
+ <AvatarGroup total={classroom.teachers.length}>
+ {classroom.teachers.map(t => (
+ <Avatar key={t.name} alt={t.name} src={t.avatar} sx={avatar} />
+ ))}
+ </AvatarGroup>
+ <Tooltip title={classroom.teachers.map(t => t.name).join(', ')}>
+ <Typography sx={tooltip} variant="body3" color="text.secondary">
+ {classroom.teachers.map(t => t.name).join(', ')}
+ </Typography>
+ </Tooltip>
+ </Stack>
+ <Tabs
+ value={selectedTabOption}
+ onChange={onSelectTabOption}
+ aria-label="Tabs para informações da disciplina"
+ variant="fullWidth"
+ sx={tabs}
+ >
+ {Object.values(TAB_OPTIONS).map(option => (
+ <Tab
+ key={option.value}
+ label={option.lable}
+ disabled={isLoading && option.value !== selectedTabOption}
+ />
+ ))}
+ </Tabs>
+ </Paper>
+ </Container>
+ );
+}
+
+export default Header;
diff --git a/src/screens/student/Classroom/Header/styles.js b/src/screens/student/Classroom/Header/styles.js
new file mode 100644
index 0000000..58d19aa
--- /dev/null
+++ b/src/screens/student/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/student/Classroom/PeopleTab/index.js b/src/screens/student/Classroom/PeopleTab/index.js
new file mode 100644
index 0000000..9dfde7b
--- /dev/null
+++ b/src/screens/student/Classroom/PeopleTab/index.js
@@ -0,0 +1,244 @@
+import { Avatar, Container, Skeleton, Stack, Typography } from '@mui/material';
+import { createArrayFrom1ToN } from '../../../../utils/createArrayFrom1ToN';
+import styles from './styles';
+
+function PeopleTab({ layoutType, peopleTabData }) {
+ const layoutResolver = (state, people, layoutType) => {
+ const {
+ externalContainer,
+ sectionContainer,
+ sectionTitle,
+ personContainer,
+ personAvatar,
+ personName,
+ emptyStateContainer,
+ } = styles[layoutType];
+ if (layoutType === 'desktop') {
+ switch (state) {
+ case 'loading':
+ return (
+ <Container sx={externalContainer} disableGutters>
+ <Stack alignItems="center">
+ <Skeleton
+ variant="rectangular"
+ width="90%"
+ height={70}
+ sx={{ marginBottom: '30px' }}
+ />
+ <Stack
+ flexDirection="row"
+ alignItems="center"
+ sx={{ width: '90%', marginLeft: '20px' }}
+ >
+ <Skeleton variant="circular" width={60} height={60} />
+ <Skeleton
+ variant="rectangular"
+ width="70%"
+ height={40}
+ sx={{ marginLeft: '15px' }}
+ />
+ </Stack>
+ </Stack>
+
+ <Stack alignItems="center">
+ <Skeleton
+ variant="rectangular"
+ width="90%"
+ height={70}
+ sx={{ marginBottom: '30px', marginTop: '50px' }}
+ />
+ {createArrayFrom1ToN(5).map(i => (
+ <Stack
+ key={i}
+ flexDirection="row"
+ alignItems="center"
+ sx={{ width: '90%', marginLeft: '20px', marginTop: '25px' }}
+ >
+ <Skeleton variant="circular" width={60} height={60} />
+ <Skeleton
+ variant="rectangular"
+ width="70%"
+ height={40}
+ sx={{ marginLeft: '15px' }}
+ />
+ </Stack>
+ ))}
+ </Stack>
+ </Container>
+ );
+ case 'idle':
+ const professors = people.filter(p => p.role === 'PROFESSOR');
+ const students = people.filter(p => p.role === 'STUDENT');
+
+ return (
+ <Container sx={externalContainer} disableGutters>
+ <Container sx={sectionContainer} disableGutters>
+ <Typography sx={sectionTitle} variant="h4">
+ Docentes
+ </Typography>
+ <Stack alignItems="center">
+ {professors.length !== 0 ? (
+ professors.map(p => (
+ <Container key={p.id} sx={personContainer} disableGutters>
+ <Avatar alt={p.name} src={p.avatar} sx={personAvatar} />
+ <Typography sx={personName} variant="h5">
+ {p.name}
+ </Typography>
+ </Container>
+ ))
+ ) : (
+ <Container sx={emptyStateContainer} disableGutters>
+ <p>Nenhum professor encontrado!</p>
+ </Container>
+ )}
+ </Stack>
+ </Container>
+
+ <Container sx={sectionContainer} disableGutters>
+ <Typography sx={sectionTitle} variant="h4">
+ Discentes
+ </Typography>
+ <Stack alignItems="center">
+ {students.length !== 0 ? (
+ students.map(p => (
+ <Container key={p.id} sx={personContainer} disableGutters>
+ <Avatar alt={p.name} src={p.avatar} sx={personAvatar} />
+ <Typography sx={personName} variant="h5">
+ {p.name}
+ </Typography>
+ </Container>
+ ))
+ ) : (
+ <Container sx={emptyStateContainer} disableGutters>
+ <p>Nenhum estudante encontrado!</p>
+ </Container>
+ )}
+ </Stack>
+ </Container>
+ </Container>
+ );
+ case 'gone':
+ return null;
+ default:
+ return null;
+ }
+ } else if (layoutType === 'mobile') {
+ switch (state) {
+ case 'loading':
+ return (
+ <Container sx={externalContainer} disableGutters>
+ <Stack alignItems="center">
+ <Skeleton
+ variant="rectangular"
+ width="90%"
+ height={50}
+ sx={{ marginBottom: '30px' }}
+ />
+ <Stack
+ flexDirection="row"
+ alignItems="center"
+ sx={{ width: '90%', marginLeft: '20px' }}
+ >
+ <Skeleton variant="circular" width={40} height={40} />
+ <Skeleton
+ variant="rectangular"
+ width="80%"
+ height={30}
+ sx={{ marginLeft: '15px' }}
+ />
+ </Stack>
+ </Stack>
+
+ <Stack alignItems="center">
+ <Skeleton
+ variant="rectangular"
+ width="90%"
+ height={50}
+ sx={{ marginBottom: '30px', marginTop: '50px' }}
+ />
+ {createArrayFrom1ToN(5).map(i => (
+ <Stack
+ key={i}
+ flexDirection="row"
+ alignItems="center"
+ sx={{ width: '90%', marginLeft: '20px', marginTop: '25px' }}
+ >
+ <Skeleton variant="circular" width={40} height={40} />
+ <Skeleton
+ variant="rectangular"
+ width="80%"
+ height={30}
+ sx={{ marginLeft: '15px' }}
+ />
+ </Stack>
+ ))}
+ </Stack>
+ </Container>
+ );
+ case 'idle':
+ const professors = people.filter(p => p.role === 'PROFESSOR');
+ const students = people.filter(p => p.role === 'STUDENT');
+
+ return (
+ <Container sx={externalContainer} disableGutters>
+ <Container sx={sectionContainer} disableGutters>
+ <Typography sx={sectionTitle} variant="h5">
+ Docentes
+ </Typography>
+ <Stack alignItems="center">
+ {professors.length !== 0 ? (
+ professors.map(p => (
+ <Container key={p.id} sx={personContainer} disableGutters>
+ <Avatar alt={p.name} src={p.avatar} sx={personAvatar} />
+ <Typography sx={personName} variant="body1">
+ {p.name}
+ </Typography>
+ </Container>
+ ))
+ ) : (
+ <Container sx={emptyStateContainer} disableGutters>
+ <p>Nenhum professor encontrado!</p>
+ </Container>
+ )}
+ </Stack>
+ </Container>
+
+ <Container sx={sectionContainer} disableGutters>
+ <Typography sx={sectionTitle} variant="h5">
+ Discentes
+ </Typography>
+ <Stack alignItems="center">
+ {students.length !== 0 ? (
+ students.map(p => (
+ <Container key={p.id} sx={personContainer} disableGutters>
+ <Avatar alt={p.name} src={p.avatar} sx={personAvatar} />
+ <Typography sx={personName} variant="body1">
+ {p.name}
+ </Typography>
+ </Container>
+ ))
+ ) : (
+ <Container sx={emptyStateContainer} disableGutters>
+ <p>Nenhum estudante encontrado!</p>
+ </Container>
+ )}
+ </Stack>
+ </Container>
+ </Container>
+ );
+ case 'gone':
+ return null;
+ default:
+ return null;
+ }
+ }
+ };
+
+ return layoutResolver(
+ peopleTabData && peopleTabData.state,
+ peopleTabData && peopleTabData.people,
+ layoutType
+ );
+}
+
+export default PeopleTab;
diff --git a/src/screens/student/Classroom/PeopleTab/styles.js b/src/screens/student/Classroom/PeopleTab/styles.js
new file mode 100644
index 0000000..30668db
--- /dev/null
+++ b/src/screens/student/Classroom/PeopleTab/styles.js
@@ -0,0 +1,116 @@
+// ========== Desktop ==========
+const desktopExternalContainer = {
+ marginTop: '50px',
+ height: '100vh',
+};
+
+const desktopSectionContainer = {
+ width: '90%',
+ marginBottom: '30px',
+};
+
+const desktopSectionTitle = {
+ padding: '10px',
+ borderBottom: '2px solid #00420D',
+ color: '#00420D',
+};
+
+const desktopPersonContainer = {
+ width: '95%',
+ padding: '20px',
+ borderBottom: '2px solid #BCBCBC',
+ display: 'flex',
+ alignItems: 'center',
+};
+
+const desktopPersonAvatar = {
+ width: '60px',
+ height: '60px',
+ marginRight: '15px',
+};
+
+const desktopPersonName = {};
+
+const desktopEmptyStateContainer = {
+ display: 'flex',
+ justifyContent: 'center',
+ marginTop: '30px',
+};
+
+const desktop = {
+ externalContainer: desktopExternalContainer,
+ sectionContainer: desktopSectionContainer,
+ sectionTitle: desktopSectionTitle,
+ personContainer: desktopPersonContainer,
+ personAvatar: desktopPersonAvatar,
+ personName: desktopPersonName,
+ emptyStateContainer: desktopEmptyStateContainer,
+};
+
+// ========== Mobile ==========
+const mobileExternalContainer = {
+ marginTop: '50px',
+ height: '100vh',
+};
+
+const mobileSectionContainer = {
+ width: '90%',
+ marginBottom: '30px',
+};
+
+const mobileSectionTitle = {
+ padding: '10px',
+ borderBottom: '2px solid #00420D',
+ color: '#00420D',
+};
+
+const mobilePersonContainer = {
+ width: '95%',
+ padding: '20px',
+ borderBottom: '2px solid #BCBCBC',
+ display: 'flex',
+ alignItems: 'center',
+};
+
+const mobilePersonAvatar = {
+ width: '40px',
+ height: '40px',
+ marginRight: '15px',
+};
+
+const mobilePersonName = {
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ display: '-webkit-box',
+ WebkitLineClamp: 1,
+ WebkitBoxOrient: 'vertical',
+};
+
+const mobileEmptyStateContainer = {
+ display: 'flex',
+ justifyContent: 'center',
+ marginTop: '30px',
+};
+
+const mobile = {
+ externalContainer: mobileExternalContainer,
+ sectionContainer: mobileSectionContainer,
+ sectionTitle: mobileSectionTitle,
+ personContainer: mobilePersonContainer,
+ personAvatar: mobilePersonAvatar,
+ personName: mobilePersonName,
+ emptyStateContainer: mobileEmptyStateContainer,
+};
+
+// ========== Unset ==========
+const unset = {
+ externalContainer: null,
+ sectionContainer: null,
+ sectionTitle: null,
+ personContainer: null,
+ personAvatar: null,
+ personName: null,
+};
+
+const styles = { desktop, mobile, unset };
+export default styles;
diff --git a/src/screens/student/Classroom/View.js b/src/screens/student/Classroom/View.js
new file mode 100644
index 0000000..675e697
--- /dev/null
+++ b/src/screens/student/Classroom/View.js
@@ -0,0 +1,44 @@
+import { Container } from '@mui/material';
+
+import Header from './Header';
+import AnnouncementsTab from './AnnouncementsTab';
+import AssignmentsTab from './AssignmentsTab';
+import PeopleTab from './PeopleTab';
+
+import styles from './styles';
+
+function View({
+ layoutType,
+ classroom,
+ selectedTabOption,
+ onSelectTabOption,
+ announcementsTabData,
+ assignmentsTabData,
+ peopleTabData,
+ isLoading,
+}) {
+ const { container } = styles[layoutType];
+ return (
+ <Container disableGutters sx={container}>
+ <Header
+ layoutType={layoutType}
+ classroom={classroom}
+ selectedTabOption={selectedTabOption}
+ onSelectTabOption={onSelectTabOption}
+ isLoading={isLoading}
+ />
+ <AnnouncementsTab
+ layoutType={layoutType}
+ announcementsTabData={announcementsTabData}
+ classroom={classroom && classroom}
+ />
+ <AssignmentsTab
+ layoutType={layoutType}
+ assignmentsTabData={assignmentsTabData}
+ />
+ <PeopleTab layoutType={layoutType} peopleTabData={peopleTabData} />
+ </Container>
+ );
+}
+
+export default View;
diff --git a/src/screens/student/Classroom/index.js b/src/screens/student/Classroom/index.js
new file mode 100644
index 0000000..5201b49
--- /dev/null
+++ b/src/screens/student/Classroom/index.js
@@ -0,0 +1,118 @@
+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
+ );
+ 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]);
+
+ 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) {
+ case TAB_OPTIONS.announcements.value:
+ fetchAndPopulateAnnouncementsTabData();
+ break;
+ case TAB_OPTIONS.assignments.value:
+ fetchAndPopulateAssignmentsTabData();
+ break;
+ case TAB_OPTIONS.people.value:
+ fetchAndPopulatePeopleTabData();
+ break;
+ default:
+ console.log('Invalid tab option');
+ }
+ }
+ getSelectedTabData();
+ }, [
+ selectedTabOption,
+ params,
+ fetchAndPopulateAnnouncementsTabData,
+ fetchAndPopulateAssignmentsTabData,
+ fetchAndPopulatePeopleTabData,
+ ]);
+
+ 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]);
+
+ return (
+ <View
+ layoutType={layoutType}
+ classroom={classroom}
+ selectedTabOption={selectedTabOption}
+ onSelectTabOption={(_, value) => 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' }
+ }
+ isLoading={tabData && tabData.state === 'loading'}
+ />
+ );
+}
+
+export default Classroom;
diff --git a/src/screens/student/Classroom/styles.js b/src/screens/student/Classroom/styles.js
new file mode 100644
index 0000000..fe6018c
--- /dev/null
+++ b/src/screens/student/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/student/Classroom/tabOptions.js b/src/screens/student/Classroom/tabOptions.js
new file mode 100644
index 0000000..48fb518
--- /dev/null
+++ b/src/screens/student/Classroom/tabOptions.js
@@ -0,0 +1,16 @@
+const TAB_OPTIONS = {
+ announcements: {
+ value: 0,
+ lable: 'Comunicados',
+ },
+ assignments: {
+ value: 1,
+ lable: 'Atividades',
+ },
+ people: {
+ value: 2,
+ lable: 'Pessoas',
+ },
+};
+
+export { TAB_OPTIONS };