Merge pull request #20 from leomurca/feature/professor_classroom
Feature/professor classroom
This commit is contained in:
commit
8eca8b79ce
51 changed files with 2962 additions and 313 deletions
|
@ -1,22 +1,20 @@
|
|||
import { Navigate, Route, Routes, useNavigate } from 'react-router-dom';
|
||||
import { lazy } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Container } from '@mui/system';
|
||||
import { useUser } from '../context/user';
|
||||
import { useAuthState } from '../context/auth';
|
||||
import { useUser } from '../context/user';
|
||||
|
||||
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 StudentRoutes = lazy(() => import('./StudentRoutes'));
|
||||
const ProfessorRoutes = lazy(() => import('./ProfessorRoutes'));
|
||||
|
||||
function AuthenticatedApp() {
|
||||
const navigate = useNavigate();
|
||||
const { state } = useUser();
|
||||
|
@ -24,6 +22,17 @@ function AuthenticatedApp() {
|
|||
const layoutType = useLayoutType();
|
||||
const { container, toolbar } = styles[layoutType];
|
||||
|
||||
const routeResolver = role => {
|
||||
switch (role) {
|
||||
case 'STUDENT':
|
||||
return <StudentRoutes />;
|
||||
case 'PROFESSOR':
|
||||
return <ProfessorRoutes />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
state &&
|
||||
state.user && (
|
||||
|
@ -43,21 +52,7 @@ function AuthenticatedApp() {
|
|||
options={menuOptions(state.pathname)}
|
||||
layoutType={layoutType}
|
||||
/>
|
||||
<Routes>
|
||||
<Route path="/home" element={<Home />} />
|
||||
<Route path="/info" element={<Information />} />
|
||||
<Route path="/calendar" element={<Calendar />} />
|
||||
<Route path="/profile" element={<Profile />} />
|
||||
<Route path="/class">
|
||||
<Route path=":id" element={<Classroom />} />
|
||||
</Route>
|
||||
<Route path="/assignment">
|
||||
<Route path=":id" element={<Assignment />} />
|
||||
</Route>
|
||||
<Route path="/login" element={<Navigate to="/home" />} />
|
||||
<Route path="/register" element={<Navigate to="/home" />} />
|
||||
<Route path="/" element={<Navigate to="/home" />} />
|
||||
</Routes>
|
||||
{routeResolver(state.user.role)}
|
||||
</Container>
|
||||
</>
|
||||
)
|
||||
|
|
22
src/app/ProfessorRoutes.js
Normal file
22
src/app/ProfessorRoutes.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
import Classroom from '../screens/professor/Classroom';
|
||||
import Home from '../screens/professor/Home';
|
||||
|
||||
function ProfessorRoutes() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/calendar" element={<h1>Calendar</h1>} />
|
||||
<Route path="/profile" element={<h1>Profile</h1>} />
|
||||
<Route path="/class">
|
||||
<Route path=":id" element={<Classroom />} />
|
||||
</Route>
|
||||
<Route path="/info" element={<h1>Information</h1>} />
|
||||
<Route path="/home" element={<Home />} />
|
||||
<Route path="/login" element={<Navigate to="/home" />} />
|
||||
<Route path="/register" element={<Navigate to="/home" />} />
|
||||
<Route path="/" element={<Navigate to="/home" />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProfessorRoutes;
|
31
src/app/StudentRoutes.js
Normal file
31
src/app/StudentRoutes.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
|
||||
import Home from '../screens/student/Home';
|
||||
import Classroom from '../screens/student/Classroom';
|
||||
|
||||
import Information from '../screens/Information';
|
||||
import Calendar from '../screens/Calendar';
|
||||
import Assignment from '../screens/Assignment';
|
||||
import Profile from '../screens/Profile';
|
||||
|
||||
function StudentRoutes() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/home" element={<Home />} />
|
||||
<Route path="/info" element={<Information />} />
|
||||
<Route path="/calendar" element={<Calendar />} />
|
||||
<Route path="/profile" element={<Profile />} />
|
||||
<Route path="/class">
|
||||
<Route path=":id" element={<Classroom />} />
|
||||
</Route>
|
||||
<Route path="/assignment">
|
||||
<Route path=":id" element={<Assignment />} />
|
||||
</Route>
|
||||
<Route path="/login" element={<Navigate to="/home" />} />
|
||||
<Route path="/register" element={<Navigate to="/home" />} />
|
||||
<Route path="/" element={<Navigate to="/home" />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
|
||||
export default StudentRoutes;
|
|
@ -28,6 +28,7 @@ const menuOptions = activePath => [
|
|||
isActive:
|
||||
activePath === '/home' ||
|
||||
activePath === '/login' ||
|
||||
activePath === '/register' ||
|
||||
activePath === '/profile' ||
|
||||
activePath === '/' ||
|
||||
activePath.indexOf('class') !== -1 ||
|
||||
|
|
|
@ -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(', ')}
|
||||
</Typography>
|
||||
<Divider sx={dividerMiddle} />
|
||||
|
||||
<Typography sx={typographyDueDate} variant="p" component="div">
|
||||
<strong>Data de entrega: </strong>{' '}
|
||||
{capitalizeFirstLetter(
|
||||
dayjs(dueDate).format('dddd, DD/MM | HH:mm[h]')
|
||||
)}
|
||||
</Typography>
|
||||
{deliveredByStudents >= 0 && total && (
|
||||
<Typography variant="p" component="div">
|
||||
<strong>Entregues: </strong>{' '}
|
||||
{`${deliveredByStudents} de ${total}`}
|
||||
</Typography>
|
||||
)}
|
||||
{reviewed >= 0 && total && (
|
||||
<Typography variant="p" component="div">
|
||||
<strong>Corrigidas: </strong> {`${reviewed} de ${total}`}
|
||||
</Typography>
|
||||
)}
|
||||
{!isAssignmentToReview && (
|
||||
<Typography variant="p" component="div">
|
||||
<strong>Valor: </strong>
|
||||
{scores.map(s => s.value).join(', ')} pts
|
||||
</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
|
@ -110,10 +128,23 @@ function AssignmentCard({
|
|||
dayjs(dueDate).format('dddd, DD/MM | HH:mm[h]')
|
||||
)}
|
||||
</Typography>
|
||||
{deliveredByStudents >= 0 && total && (
|
||||
<Typography variant="p" component="div">
|
||||
<strong>Entregues: </strong>{' '}
|
||||
{`${deliveredByStudents} de ${total}`}
|
||||
</Typography>
|
||||
)}
|
||||
{reviewed >= 0 && total && (
|
||||
<Typography variant="p" component="div">
|
||||
<strong>Corrigidas: </strong> {`${reviewed} de ${total}`}
|
||||
</Typography>
|
||||
)}
|
||||
{!isAssignmentToReview && (
|
||||
<Typography variant="p" component="div">
|
||||
<strong>Valor: </strong>
|
||||
{scores.map(s => s.value).join(', ')} pts
|
||||
</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
|
|
|
@ -17,6 +17,7 @@ function ClassCard({
|
|||
title,
|
||||
color,
|
||||
teachers,
|
||||
course,
|
||||
layoutType,
|
||||
onClick,
|
||||
}) {
|
||||
|
@ -45,6 +46,7 @@ function ClassCard({
|
|||
>
|
||||
{title}
|
||||
</Typography>
|
||||
{teachers && (
|
||||
<Stack alignItems="center" direction="row" spacing={1}>
|
||||
<AvatarGroup total={teachers.length}>
|
||||
{teachers.map(t => (
|
||||
|
@ -57,11 +59,17 @@ function ClassCard({
|
|||
))}
|
||||
</AvatarGroup>
|
||||
<Tooltip title={teachers.map(t => t.name).join(', ')}>
|
||||
<Typography sx={tooltip} variant="body3" color="text.secondary">
|
||||
<Typography
|
||||
sx={tooltip}
|
||||
variant="body3"
|
||||
color="text.secondary"
|
||||
>
|
||||
{teachers.map(t => t.name).join(', ')}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
)}
|
||||
{course && <Typography variant="body2">{course}</Typography>}
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
|
@ -82,6 +90,7 @@ function ClassCard({
|
|||
>
|
||||
{title}
|
||||
</Typography>
|
||||
{teachers && (
|
||||
<Stack alignItems="center" direction="row" spacing={1}>
|
||||
<AvatarGroup total={teachers.length}>
|
||||
{teachers.map(t => (
|
||||
|
@ -97,6 +106,8 @@ function ClassCard({
|
|||
{teachers.map(t => t.name).join(', ')}
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
{course && <Typography variant="body2">{course}</Typography>}
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
|
|
33
src/components/FormDialog/index.js
Normal file
33
src/components/FormDialog/index.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
} from '@mui/material';
|
||||
|
||||
function FormDialog({
|
||||
isOpened,
|
||||
title,
|
||||
contentText,
|
||||
inputs,
|
||||
onDismiss,
|
||||
onSave,
|
||||
}) {
|
||||
return (
|
||||
<Dialog open={isOpened} onClose={onDismiss}>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>{contentText}</DialogContentText>
|
||||
{[...inputs]}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onDismiss}>Cancelar</Button>
|
||||
<Button onClick={onSave}>Salvar</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default FormDialog;
|
64
src/components/PublishAnnouncementCard/index.js
Normal file
64
src/components/PublishAnnouncementCard/index.js
Normal file
|
@ -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 (
|
||||
<Card sx={card} elevation={4} variant="elevation">
|
||||
{isComposing ? (
|
||||
<Stack
|
||||
sx={publishAnnouncement}
|
||||
alignItems="end"
|
||||
direction="column"
|
||||
spacing={2}
|
||||
>
|
||||
<TextField
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
sx={{ width: '100%' }}
|
||||
id="outlined-multiline-static"
|
||||
label="Escreva um comunicado para sua turma"
|
||||
autoFocus
|
||||
multiline
|
||||
minRows={4}
|
||||
/>
|
||||
|
||||
<Stack direction="row" spacing={4}>
|
||||
<Button onClick={() => setIsComposing(false)} variant="text">
|
||||
Cancelar
|
||||
</Button>
|
||||
|
||||
<Button onClick={() => console.log('clicked')} variant="contained">
|
||||
Postar
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack
|
||||
sx={publishAnnouncement}
|
||||
alignItems="center"
|
||||
direction="row"
|
||||
spacing={2}
|
||||
onClick={() => setIsComposing(true)}
|
||||
>
|
||||
<Avatar alt={user.firstName} src={user.avatar} />
|
||||
<Typography id="outlined-multiline-static" sx={{ width: '100%' }}>
|
||||
Escreva um comunicado para sua turma
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default PublishAnnouncementCard;
|
47
src/components/PublishAnnouncementCard/styles.js
Normal file
47
src/components/PublishAnnouncementCard/styles.js
Normal file
|
@ -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;
|
|
@ -1,5 +1,5 @@
|
|||
import { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { getUser, registerUser } from '../services/user-service';
|
||||
import { CommonApi } from '../utils/mocks/api';
|
||||
|
||||
const AuthContext = createContext();
|
||||
|
||||
|
@ -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 CommonApi.registerUser(data).then(data => {
|
||||
if (data.message) {
|
||||
return setState({ status: 'error', user: null, error: data });
|
||||
} else {
|
||||
return setState({ status: 'success', user: data, error: null });
|
||||
|
@ -35,10 +34,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 CommonApi.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 });
|
||||
|
|
|
@ -1,23 +1,14 @@
|
|||
import { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { UserServiceProvider } from '../services/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 (
|
||||
<UserContext.Provider
|
||||
value={{
|
||||
state,
|
||||
fetchClassrooms,
|
||||
fetchAllAssignments,
|
||||
fetchAssignmentById,
|
||||
fetchAssignmentsByClassId,
|
||||
fetchClassroomById,
|
||||
fetchFAQ,
|
||||
fetchClassroomAnnouncements,
|
||||
fetchUpcomingAssignmentsByClassId,
|
||||
fetchPeopleByClassId,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
return <UserContext.Provider value={{ state, userService }} {...props} />;
|
||||
}
|
||||
|
||||
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 };
|
||||
|
|
|
@ -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 (
|
||||
<View assignment={assignment} dropzone={dropzone} layoutType={layoutType} />
|
||||
|
|
|
@ -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 <View faq={faq} sectors={sectors} layoutType={layoutType} />;
|
||||
}
|
||||
|
|
525
src/screens/professor/Classroom/AnnouncementsTab/index.js
Normal file
525
src/screens/professor/Classroom/AnnouncementsTab/index.js
Normal file
|
@ -0,0 +1,525 @@
|
|||
import { useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Container,
|
||||
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';
|
||||
|
||||
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) {
|
||||
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', paddingTop: '10px' }}
|
||||
elevation={4}
|
||||
variant="elevation"
|
||||
>
|
||||
<Stack justifyContent="flex-start" spacing={1}>
|
||||
<Container
|
||||
disableGutters
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<Stack direction="row">
|
||||
<img src={jitsiLogo} alt="Jitsi Meet" />
|
||||
<h3 style={{ fontWeight: 500 }}>
|
||||
Sala de aula virtual
|
||||
</h3>
|
||||
</Stack>
|
||||
|
||||
<Tooltip title="Opcoes">
|
||||
<IconButton
|
||||
onClick={e =>
|
||||
setAnchorEl({
|
||||
...anchorEl,
|
||||
virtualRoom: e.currentTarget,
|
||||
})
|
||||
}
|
||||
aria-label="edit"
|
||||
size="medium"
|
||||
>
|
||||
<MoreVertIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
id="menu-appbar-virtual-room"
|
||||
anchorEl={anchorEl.virtualRoom}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
open={Boolean(anchorEl.virtualRoom)}
|
||||
onClose={() =>
|
||||
setAnchorEl({ ...anchorEl, virtualRoom: null })
|
||||
}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => setDialogOpened('virtualRoom')}
|
||||
>
|
||||
<Typography textAlign="center">Editar</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<FormDialog
|
||||
isOpened={dialogOpened === 'virtualRoom'}
|
||||
title="Alterar url da sala de aula virtual"
|
||||
contentText="Edite o campo abaixo para alterar a url da sua sala de aula virtual."
|
||||
inputs={[
|
||||
<TextField
|
||||
autoFocus
|
||||
margin="dense"
|
||||
name="virtualRoom"
|
||||
type="text"
|
||||
value={classroom.virtualRoom}
|
||||
onChange={onChangeEditInput}
|
||||
fullWidth
|
||||
variant="standard"
|
||||
/>,
|
||||
]}
|
||||
onDismiss={() => onDismissEdit('virtualRoom')}
|
||||
onSave={() => onSaveEdit('virtualRoom')}
|
||||
/>
|
||||
</Container>
|
||||
|
||||
<Button
|
||||
sx={{ marginTop: '15px' }}
|
||||
variant="contained"
|
||||
href={classroom.virtualRoom}
|
||||
target="__blank"
|
||||
>
|
||||
Iniciar aula
|
||||
</Button>
|
||||
</Stack>
|
||||
</Card>
|
||||
<Card
|
||||
sx={{ width: '100%', padding: '20px', paddingTop: '10px' }}
|
||||
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', paddingTop: '10px' }}
|
||||
elevation={4}
|
||||
variant="elevation"
|
||||
>
|
||||
<Stack justifyContent="flex-start" spacing={1}>
|
||||
<Container
|
||||
disableGutters
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<h3 style={{ fontWeight: 500 }}>
|
||||
Horários de Atendimento
|
||||
</h3>
|
||||
<Tooltip title="Opcoes">
|
||||
<IconButton
|
||||
onClick={e =>
|
||||
setAnchorEl({
|
||||
...anchorEl,
|
||||
appointmentSlots: e.currentTarget,
|
||||
})
|
||||
}
|
||||
aria-label="edit"
|
||||
size="medium"
|
||||
>
|
||||
<MoreVertIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
id="menu-appbar-appointment-slots"
|
||||
anchorEl={anchorEl.appointmentSlots}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
open={Boolean(anchorEl.appointmentSlots)}
|
||||
onClose={() =>
|
||||
setAnchorEl({ ...anchorEl, appointmentSlots: null })
|
||||
}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => setDialogOpened('appointmentSlots')}
|
||||
>
|
||||
<Typography textAlign="center">Editar</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<FormDialog
|
||||
isOpened={dialogOpened === 'appointmentSlots'}
|
||||
title="Alterar horarios de atendimento"
|
||||
contentText="Edite os campos abaixo para alterar os horarios de atendimento da disciplina."
|
||||
inputs={[
|
||||
classroom.appointmentSlots.map((appts, index) => (
|
||||
<TextField
|
||||
key={index}
|
||||
autoFocus
|
||||
margin="dense"
|
||||
name={index}
|
||||
type="text"
|
||||
value={`${appts.weekDay}, ${appts.start}h - ${appts.end}h`}
|
||||
onChange={onChangeEditInput}
|
||||
fullWidth
|
||||
variant="standard"
|
||||
/>
|
||||
)),
|
||||
]}
|
||||
onDismiss={() => onDismissEdit('appointmentSlots')}
|
||||
onSave={() => onSaveEdit('appointmentSlots')}
|
||||
/>
|
||||
</Container>
|
||||
{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"
|
||||
>
|
||||
<PublishAnnouncementCard
|
||||
layoutType={layoutType}
|
||||
user={user}
|
||||
value={composingTextValue}
|
||||
onChange={e => setComposingTextValue(e.target.value)}
|
||||
/>
|
||||
{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', paddingTop: '10px' }}
|
||||
elevation={4}
|
||||
variant="elevation"
|
||||
>
|
||||
<Stack justifyContent="flex-start" spacing={1}>
|
||||
<Container
|
||||
disableGutters
|
||||
sx={{ display: 'flex', justifyContent: 'space-between' }}
|
||||
>
|
||||
<Stack direction="row">
|
||||
<img src={jitsiLogo} alt="Jitsi Meet" />
|
||||
<h3 style={{ fontWeight: 500 }}>
|
||||
Sala de aula virtual
|
||||
</h3>
|
||||
</Stack>
|
||||
<Tooltip title="Opcoes">
|
||||
<IconButton
|
||||
onClick={e => setAnchorEl(e.currentTarget)}
|
||||
aria-label="edit"
|
||||
size="medium"
|
||||
>
|
||||
<MoreVertIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
id="menu-appbar"
|
||||
anchorEl={anchorEl}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={() => setAnchorEl(null)}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => setDialogOpened('virtualRoom')}
|
||||
>
|
||||
<Typography textAlign="center">Editar</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<FormDialog
|
||||
isOpened={dialogOpened === 'virtualRoom'}
|
||||
title="Alterar url da sala de aula virtual"
|
||||
contentText="Edite o campo abaixo para alterar a url da sua sala de aula virtual."
|
||||
inputs={[
|
||||
<TextField
|
||||
autoFocus
|
||||
margin="dense"
|
||||
name="virtualRoom"
|
||||
type="text"
|
||||
value={classroom.virtualRoom}
|
||||
onChange={onChangeEditInput}
|
||||
fullWidth
|
||||
variant="standard"
|
||||
/>,
|
||||
]}
|
||||
onDismiss={() => onDismissEdit('virtualRoom')}
|
||||
onSave={() => onSaveEdit('virtualRoom')}
|
||||
/>
|
||||
</Container>
|
||||
<Button
|
||||
variant="contained"
|
||||
href={classroom.virtualRoom}
|
||||
target="__blank"
|
||||
>
|
||||
Iniciar aula
|
||||
</Button>
|
||||
</Stack>
|
||||
</Card>
|
||||
<Card
|
||||
sx={{ width: '100%', padding: '20px', paddingTop: '10px' }}
|
||||
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', paddingTop: '10px' }}
|
||||
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"
|
||||
>
|
||||
<PublishAnnouncementCard
|
||||
layoutType={layoutType}
|
||||
user={user}
|
||||
value={composingTextValue}
|
||||
onChange={e => setComposingTextValue(e.target.value)}
|
||||
/>
|
||||
{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;
|
49
src/screens/professor/Classroom/AnnouncementsTab/styles.js
Normal file
49
src/screens/professor/Classroom/AnnouncementsTab/styles.js
Normal file
|
@ -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;
|
409
src/screens/professor/Classroom/AssignmentsTab/index.js
Normal file
409
src/screens/professor/Classroom/AssignmentsTab/index.js
Normal file
|
@ -0,0 +1,409 @@
|
|||
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 (
|
||||
<Container
|
||||
sx={{
|
||||
...externalContainer,
|
||||
display: 'block',
|
||||
}}
|
||||
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>
|
||||
<Fab
|
||||
sx={{ width: 'fit-content', marginRight: '5%' }}
|
||||
color="primary"
|
||||
aria-label="add"
|
||||
variant="extended"
|
||||
>
|
||||
<AddIcon />
|
||||
Criar atividade
|
||||
</Fab>
|
||||
<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;
|
||||
}
|
||||
} 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>
|
||||
<Fab
|
||||
sx={{ width: '100%' }}
|
||||
color="primary"
|
||||
aria-label="add"
|
||||
variant="extended"
|
||||
>
|
||||
<AddIcon />
|
||||
Criar atividade
|
||||
</Fab>
|
||||
<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;
|
138
src/screens/professor/Classroom/AssignmentsTab/styles.js
Normal file
138
src/screens/professor/Classroom/AssignmentsTab/styles.js
Normal file
|
@ -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;
|
34
src/screens/professor/Classroom/GradesTab/index.js
Normal file
34
src/screens/professor/Classroom/GradesTab/index.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
function GradesTab({ gradesTabData, layoutType }) {
|
||||
const layoutResolver = (state, grades, layoutType) => {
|
||||
if (layoutType === 'desktop') {
|
||||
switch (state) {
|
||||
case 'loading':
|
||||
return <h1>Loading...</h1>;
|
||||
case 'idle':
|
||||
return <h1>Grades Tab</h1>;
|
||||
case 'gone':
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
} else if (layoutType === 'mobile') {
|
||||
switch (state) {
|
||||
case 'loading':
|
||||
return <h1>Loading...</h1>;
|
||||
case 'idle':
|
||||
return <h1>Grades Tab</h1>;
|
||||
case 'gone':
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
return layoutResolver(
|
||||
gradesTabData && gradesTabData.state,
|
||||
gradesTabData && gradesTabData.grades,
|
||||
layoutType
|
||||
);
|
||||
}
|
||||
|
||||
export default GradesTab;
|
0
src/screens/professor/Classroom/GradesTab/styles.js
Normal file
0
src/screens/professor/Classroom/GradesTab/styles.js
Normal file
62
src/screens/professor/Classroom/Header/index.js
Normal file
62
src/screens/professor/Classroom/Header/index.js
Normal file
|
@ -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={layoutType === 'mobile' ? 'scrollable' : '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;
|
90
src/screens/professor/Classroom/Header/styles.js
Normal file
90
src/screens/professor/Classroom/Header/styles.js
Normal file
|
@ -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;
|
|
@ -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 }) {
|
51
src/screens/professor/Classroom/View.js
Normal file
51
src/screens/professor/Classroom/View.js
Normal file
|
@ -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 (
|
||||
<Container disableGutters sx={container}>
|
||||
<Header
|
||||
layoutType={layoutType}
|
||||
classroom={classroom && classroom}
|
||||
selectedTabOption={selectedTabOption}
|
||||
onSelectTabOption={onSelectTabOption}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<AnnouncementsTab
|
||||
layoutType={layoutType}
|
||||
announcementsTabData={announcementsTabData}
|
||||
classroom={classroom && classroom}
|
||||
user={user && user}
|
||||
onChangeEditInput={onChangeEditInput}
|
||||
onSaveEditChanges={onSaveEditChanges}
|
||||
/>
|
||||
<AssignmentsTab
|
||||
layoutType={layoutType}
|
||||
assignmentsTabData={assignmentsTabData}
|
||||
/>
|
||||
<PeopleTab layoutType={layoutType} peopleTabData={peopleTabData} />
|
||||
<GradesTab layoutType={layoutType} gradesTabData={gradesTabData} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export default View;
|
156
src/screens/professor/Classroom/index.js
Normal file
156
src/screens/professor/Classroom/index.js
Normal file
|
@ -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 (
|
||||
<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' }
|
||||
}
|
||||
gradesTabData={
|
||||
tabData && tabData.tab === 'grades' ? tabData : { state: 'gone' }
|
||||
}
|
||||
user={state && state.user}
|
||||
onChangeEditInput={onChangeEditInput}
|
||||
onSaveEditChanges={onSaveEditChanges}
|
||||
isLoading={tabData && tabData.state === 'loading'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Classroom;
|
20
src/screens/professor/Classroom/tabOptions.js
Normal file
20
src/screens/professor/Classroom/tabOptions.js
Normal file
|
@ -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 };
|
200
src/screens/professor/Home/View.js
Normal file
200
src/screens/professor/Home/View.js
Normal file
|
@ -0,0 +1,200 @@
|
|||
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,
|
||||
assignmentsToReview,
|
||||
onClickClassCard,
|
||||
}) {
|
||||
const { container, divider, assignmentsStack, onClickAssignmentCard } =
|
||||
styles[layoutType];
|
||||
|
||||
if (layoutType === 'desktop') {
|
||||
return (
|
||||
<Grid sx={container} container spacing={2}>
|
||||
<Grid item xs={8}>
|
||||
<h1>Minhas Turmas</h1>
|
||||
<Stack alignItems="center" flexWrap="wrap" direction="row" gap="30px">
|
||||
{classrooms === null ? (
|
||||
createArrayFrom1ToN(6).map(i => (
|
||||
<Skeleton
|
||||
key={i}
|
||||
variant="rectangular"
|
||||
width={390}
|
||||
height={145}
|
||||
/>
|
||||
))
|
||||
) : classrooms.length !== 0 ? (
|
||||
classrooms.map(classroom => (
|
||||
<ClassCard
|
||||
key={classroom.name}
|
||||
abbreviation={classroom.abbreviation}
|
||||
title={classroom.name}
|
||||
color={classroom.color}
|
||||
teachers={classroom.teachers}
|
||||
course={classroom.course}
|
||||
layoutType={layoutType}
|
||||
onClick={() => onClickClassCard(classroom.id)}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Container
|
||||
sx={{
|
||||
height: '100vh',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
disableGutters
|
||||
>
|
||||
<p>Nenhuma sala de aula encontrada!</p>
|
||||
</Container>
|
||||
)}
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid sx={divider} item xs={4}>
|
||||
<h1>Atividades para corrigir</h1>
|
||||
<Stack
|
||||
sx={assignmentsStack}
|
||||
alignItems="end"
|
||||
flexWrap="wrap"
|
||||
direction="row"
|
||||
gap="30px"
|
||||
>
|
||||
{assignmentsToReview === null ? (
|
||||
createArrayFrom1ToN(6).map(i => (
|
||||
<Skeleton
|
||||
key={i}
|
||||
variant="rectangular"
|
||||
width="35em"
|
||||
height={145}
|
||||
/>
|
||||
))
|
||||
) : assignmentsToReview.length !== 0 ? (
|
||||
assignmentsToReview.map(assignment => (
|
||||
<AssignmentCard
|
||||
key={assignment.title}
|
||||
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)}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Container
|
||||
sx={{
|
||||
height: '100vh',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
disableGutters
|
||||
>
|
||||
<p>Nenhuma atividade encontrada!</p>
|
||||
</Container>
|
||||
)}
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
} else if (layoutType === 'mobile') {
|
||||
return (
|
||||
<Stack sx={container}>
|
||||
<h1>Minhas Turmas</h1>
|
||||
<Stack
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
flexWrap="wrap"
|
||||
direction="row"
|
||||
gap="30px"
|
||||
>
|
||||
{classrooms === null ? (
|
||||
createArrayFrom1ToN(6).map(i => (
|
||||
<Skeleton
|
||||
key={i}
|
||||
variant="rectangular"
|
||||
width="100%"
|
||||
height={245}
|
||||
/>
|
||||
))
|
||||
) : classrooms.length !== 0 ? (
|
||||
classrooms.map(classroom => (
|
||||
<ClassCard
|
||||
key={classroom.name}
|
||||
abbreviation={classroom.abbreviation}
|
||||
title={classroom.name}
|
||||
color={classroom.color}
|
||||
teachers={classroom.teachers}
|
||||
course={classroom.course}
|
||||
layoutType={layoutType}
|
||||
onClick={() => onClickClassCard(classroom.id)}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Container disableGutters>
|
||||
<p>Nenhuma sala de aula encontrada!</p>
|
||||
</Container>
|
||||
)}
|
||||
</Stack>
|
||||
<h1 style={divider}>Atividades para corrigir</h1>
|
||||
<Stack
|
||||
sx={assignmentsStack}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
flexWrap="wrap"
|
||||
direction="row"
|
||||
gap="30px"
|
||||
>
|
||||
{assignmentsToReview === null ? (
|
||||
createArrayFrom1ToN(6).map(i => (
|
||||
<Skeleton
|
||||
key={i}
|
||||
variant="rectangular"
|
||||
width="35em"
|
||||
height={145}
|
||||
/>
|
||||
))
|
||||
) : assignmentsToReview.length !== 0 ? (
|
||||
assignmentsToReview.map(assignment => (
|
||||
<AssignmentCard
|
||||
key={assignment.title}
|
||||
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)}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Container
|
||||
sx={{
|
||||
height: '100vh',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
disableGutters
|
||||
>
|
||||
<p>Nenhuma atividade encontrada!</p>
|
||||
</Container>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default View;
|
51
src/screens/professor/Home/index.js
Normal file
51
src/screens/professor/Home/index.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
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 { userService } = useUser();
|
||||
const [classrooms, setClassrooms] = useState(null);
|
||||
const [assignmentsToReview, setAssignmentsToReview] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function getClassrooms() {
|
||||
const result = await userService.fetchClassrooms();
|
||||
setClassrooms(result.data);
|
||||
}
|
||||
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}`);
|
||||
};
|
||||
|
||||
const onClickAssignmentCard = id => {
|
||||
navigate(`/assignment/${id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
layoutType={layoutType}
|
||||
classrooms={classrooms}
|
||||
assignmentsToReview={assignmentsToReview}
|
||||
onClickClassCard={onClickClassCard}
|
||||
onClickAssignmentCard={onClickAssignmentCard}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
42
src/screens/professor/Home/styles.js
Normal file
42
src/screens/professor/Home/styles.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
// ========== Desktop ==========
|
||||
const desktopContainer = {
|
||||
height: '100vh',
|
||||
margin: 0,
|
||||
};
|
||||
|
||||
const desktopDivider = {
|
||||
borderLeft: '4px solid #CFCFCF',
|
||||
};
|
||||
|
||||
const desktop = {
|
||||
container: desktopContainer,
|
||||
divider: desktopDivider,
|
||||
};
|
||||
|
||||
// ========== Mobile ==========
|
||||
const mobileContainer = {
|
||||
height: 'inherit',
|
||||
width: '100%',
|
||||
padding: '10px 20px ',
|
||||
margin: 0,
|
||||
};
|
||||
|
||||
const mobileDivider = {
|
||||
borderTop: '2px solid #CFCFCF',
|
||||
paddingTop: '15px',
|
||||
};
|
||||
|
||||
const mobile = {
|
||||
container: mobileContainer,
|
||||
divider: mobileDivider,
|
||||
};
|
||||
|
||||
// ========== Unset ==========
|
||||
const unset = {
|
||||
container: null,
|
||||
divider: null,
|
||||
assignmentsStack: null,
|
||||
};
|
||||
|
||||
const styles = { desktop, mobile, unset };
|
||||
export default styles;
|
|
@ -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' }}
|
||||
>
|
||||
<img src={jitsiLogo} alt="Jitsi Meet" />
|
||||
<h3 style={{ fontWeight: 500 }}>Jitsi</h3>
|
||||
<h3 style={{ fontWeight: 500 }}>
|
||||
Sala de aula virtual
|
||||
</h3>
|
||||
</Container>
|
||||
|
||||
<Button
|
||||
|
@ -205,7 +207,7 @@ function AnnouncementsTab({ layoutType, announcementsTabData, classroom }) {
|
|||
sx={{ display: 'flex', justifyContent: 'row' }}
|
||||
>
|
||||
<img src={jitsiLogo} alt="Jitsi Meet" />
|
||||
<h3 style={{ fontWeight: 500 }}>Jitsi</h3>
|
||||
<h3 style={{ fontWeight: 500 }}>Sala de aula virtual</h3>
|
||||
</Container>
|
||||
|
||||
<Button
|
|
@ -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 }) {
|
244
src/screens/student/Classroom/PeopleTab/index.js
Normal file
244
src/screens/student/Classroom/PeopleTab/index.js
Normal file
|
@ -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;
|
116
src/screens/student/Classroom/PeopleTab/styles.js
Normal file
116
src/screens/student/Classroom/PeopleTab/styles.js
Normal file
|
@ -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;
|
|
@ -1,20 +1,14 @@
|
|||
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';
|
||||
|
||||
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 () => {
|
||||
const fetchAndPopulatePeopleTabData = 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() {
|
||||
|
@ -72,7 +63,7 @@ function Classroom() {
|
|||
fetchAndPopulateAssignmentsTabData();
|
||||
break;
|
||||
case TAB_OPTIONS.people.value:
|
||||
fetchAndPopulatePoepleTabData();
|
||||
fetchAndPopulatePeopleTabData();
|
||||
break;
|
||||
default:
|
||||
console.log('Invalid tab option');
|
||||
|
@ -84,13 +75,13 @@ function Classroom() {
|
|||
params,
|
||||
fetchAndPopulateAnnouncementsTabData,
|
||||
fetchAndPopulateAssignmentsTabData,
|
||||
fetchAndPopulatePoepleTabData,
|
||||
fetchAndPopulatePeopleTabData,
|
||||
]);
|
||||
|
||||
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 (
|
||||
<View
|
34
src/screens/student/Classroom/styles.js
Normal file
34
src/screens/student/Classroom/styles.js
Normal file
|
@ -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;
|
|
@ -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,
|
|
@ -1,33 +1,33 @@
|
|||
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() {
|
||||
useDocumentTitle('Página Inicial');
|
||||
const navigate = useNavigate();
|
||||
const layoutType = useLayoutType();
|
||||
const { fetchClassrooms, fetchAllAssignments } = useUser();
|
||||
const { userService } = useUser();
|
||||
const [classrooms, setClassrooms] = useState(null);
|
||||
const [assignments, setAssignments] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
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}`);
|
27
src/services/professor.js
Normal file
27
src/services/professor.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { ProfessorApi } from '../utils/mocks/api';
|
||||
|
||||
export default class ProfessorService {
|
||||
constructor(user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
fetchPeopleByClassId = classId => ProfessorApi.getPeopleByClassId(classId);
|
||||
|
||||
fetchGradesByClassId = classId => ProfessorApi.getGradesByClassId(classId);
|
||||
|
||||
fetchUpcomingAssignmentsByClassId = classId =>
|
||||
ProfessorApi.getUpcomingAssignmentsByClassId(classId);
|
||||
}
|
31
src/services/provider.js
Normal file
31
src/services/provider.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
export const UserServiceProvider = (function () {
|
||||
let instance;
|
||||
|
||||
async function createInstance(user) {
|
||||
switch (user.role) {
|
||||
case 'STUDENT':
|
||||
const studentService = await import('./student');
|
||||
if (studentService) {
|
||||
return new studentService.default(user);
|
||||
}
|
||||
break;
|
||||
case 'PROFESSOR':
|
||||
const professorService = await import('./professor');
|
||||
if (professorService) {
|
||||
return new professorService.default(user);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid Role!');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getInstance: async function (user) {
|
||||
if (!instance) {
|
||||
instance = await createInstance(user);
|
||||
}
|
||||
return instance;
|
||||
},
|
||||
};
|
||||
})();
|
29
src/services/student.js
Normal file
29
src/services/student.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { StudentApi } from '../utils/mocks/api';
|
||||
|
||||
export default class StudentService {
|
||||
constructor(user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
fetchClassrooms = () => StudentApi.getClassrooms(this.user.id);
|
||||
|
||||
fetchAllAssignments = () => StudentApi.getAllAssignments(this.user.id);
|
||||
|
||||
fetchAssignmentById = assignmentId =>
|
||||
StudentApi.getAssignmentById(assignmentId);
|
||||
|
||||
fetchAssignmentsByClassId = classId =>
|
||||
StudentApi.getAssignmentsByClassId(classId);
|
||||
|
||||
fetchClassroomById = classId => StudentApi.getClassroomById(classId);
|
||||
|
||||
fetchFAQ = () => StudentApi.getFaq();
|
||||
|
||||
fetchClassroomAnnouncements = classId =>
|
||||
StudentApi.getClassroomAnnouncementsById(classId);
|
||||
|
||||
fetchUpcomingAssignmentsByClassId = classId =>
|
||||
StudentApi.getUpcomingAssignmentsByClassId(classId);
|
||||
|
||||
fetchPeopleByClassId = classId => StudentApi.getPeopleByClassId(classId);
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
import { sleep } from '../utils/sleep';
|
||||
import {
|
||||
allClassrooms,
|
||||
allAssignments,
|
||||
faq,
|
||||
user,
|
||||
authFailure,
|
||||
allClassroomAnnouncements,
|
||||
allUpcomingAssignments,
|
||||
allPeople,
|
||||
} from './mocks';
|
||||
|
||||
const getClassrooms = userId =>
|
||||
sleep(300).then(() => {
|
||||
console.log('Get classrooms ' + userId);
|
||||
return {
|
||||
data: allClassrooms,
|
||||
};
|
||||
});
|
||||
|
||||
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 = shouldFail =>
|
||||
sleep(300).then(() => {
|
||||
if (shouldFail) {
|
||||
return authFailure;
|
||||
} else {
|
||||
window.localStorage.setItem('$USER', JSON.stringify(user));
|
||||
return user;
|
||||
}
|
||||
});
|
||||
|
||||
const registerUser = (data, shouldFail) =>
|
||||
sleep(300).then(() => {
|
||||
if (shouldFail) {
|
||||
return authFailure;
|
||||
} else {
|
||||
console.log(data);
|
||||
window.localStorage.setItem('$USER', JSON.stringify(data));
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
export {
|
||||
getClassrooms,
|
||||
getClassroomById,
|
||||
getAllAssignments,
|
||||
getAssignmentById,
|
||||
getAssignmentsByClassId,
|
||||
getClassroomAnnouncementsById,
|
||||
getUpcomingAssignmentsByClassId,
|
||||
getPeopleByClassId,
|
||||
getFaq,
|
||||
getUser,
|
||||
registerUser,
|
||||
};
|
147
src/utils/mocks/api.js
Normal file
147
src/utils/mocks/api.js
Normal file
|
@ -0,0 +1,147 @@
|
|||
import { sleep } from '../sleep';
|
||||
import {
|
||||
allClassrooms,
|
||||
allAssignments,
|
||||
faq,
|
||||
studentUser,
|
||||
professorUser,
|
||||
authFailure,
|
||||
allClassroomAnnouncements,
|
||||
allUpcomingAssignments,
|
||||
allPeople,
|
||||
professorClassrooms,
|
||||
assignmentsToReview,
|
||||
grades,
|
||||
} from './responses';
|
||||
|
||||
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;
|
||||
}),
|
||||
|
||||
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;
|
||||
}),
|
||||
|
||||
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),
|
||||
};
|
||||
}),
|
||||
|
||||
getPeopleByClassId: classId =>
|
||||
sleep(400).then(() => {
|
||||
console.log('Getting people by class id ' + classId);
|
||||
return {
|
||||
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 = {
|
||||
...CommonApi,
|
||||
getClassrooms: userId =>
|
||||
sleep(300).then(() => {
|
||||
console.log('Get classrooms ' + userId);
|
||||
return {
|
||||
data: allClassrooms,
|
||||
};
|
||||
}),
|
||||
|
||||
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],
|
||||
};
|
||||
}),
|
||||
|
||||
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: professorClassrooms,
|
||||
};
|
||||
}),
|
||||
getAssignmentsToReview: userId =>
|
||||
sleep(400).then(() => {
|
||||
console.log('Getting assignments to review' + userId);
|
||||
return {
|
||||
data: assignmentsToReview,
|
||||
};
|
||||
}),
|
||||
getGradesByClassId: classId =>
|
||||
sleep(400).then(() => {
|
||||
console.log('Getting grades' + classId);
|
||||
return {
|
||||
data: grades,
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
export { StudentApi, ProfessorApi, CommonApi };
|
|
@ -4,6 +4,7 @@ const allClassrooms = [
|
|||
name: 'Introdução à Ciência de Dados',
|
||||
abbreviation: 'ICD',
|
||||
color: '#006FF2',
|
||||
virtualRoom: 'https://meet.jit.si/ifmg-icd-321',
|
||||
teachers: [
|
||||
{
|
||||
id: '2342',
|
||||
|
@ -22,6 +23,7 @@ const allClassrooms = [
|
|||
name: 'Gestão de Projetos',
|
||||
abbreviation: 'GP',
|
||||
color: '#7900F2',
|
||||
virtualRoom: 'https://meet.jit.si/ifmg-gp-123',
|
||||
teachers: [
|
||||
{
|
||||
id: '1234',
|
||||
|
@ -45,6 +47,7 @@ const allClassrooms = [
|
|||
name: 'Banco de Dados II',
|
||||
abbreviation: 'BDII',
|
||||
color: '#FF7A00',
|
||||
virtualRoom: 'https://meet.jit.si/ifmg-bdii-666',
|
||||
teachers: [
|
||||
{
|
||||
id: '6781',
|
||||
|
@ -63,6 +66,7 @@ const allClassrooms = [
|
|||
name: 'Contabilidade Básica',
|
||||
abbreviation: 'CB',
|
||||
color: '#BB0000',
|
||||
virtualRoom: 'https://meet.jit.si/ifmg-cb-765',
|
||||
teachers: [
|
||||
{
|
||||
id: '4321',
|
||||
|
@ -80,6 +84,7 @@ const allClassrooms = [
|
|||
name: 'Linguagens de Programação',
|
||||
abbreviation: 'LP',
|
||||
color: '#039200',
|
||||
virtualRoom: 'https://meet.jit.si/ifmg-lp-333',
|
||||
teachers: [
|
||||
{
|
||||
id: '9999',
|
||||
|
@ -95,6 +100,44 @@ const allClassrooms = [
|
|||
},
|
||||
];
|
||||
|
||||
const professorClassrooms = [
|
||||
{
|
||||
id: '321',
|
||||
name: 'Introdução à Ciência de Dados',
|
||||
abbreviation: 'ICD',
|
||||
color: '#006FF2',
|
||||
virtualRoom: 'https://meet.jit.si/ifmg-icd-321',
|
||||
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',
|
||||
virtualRoom: 'https://meet.jit.si/ifmg-tdg-123',
|
||||
course: 'BSI 2018',
|
||||
appointmentSlots: [
|
||||
{ weekDay: 'Quarta-feira', start: '11:00', end: '12:00' },
|
||||
{ weekDay: 'Segunda-feira', start: '10:00', end: '11:40' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '666',
|
||||
name: 'Matemática Discreta',
|
||||
abbreviation: 'MD',
|
||||
color: '#149b00',
|
||||
virtualRoom: 'https://meet.jit.si/ifmg-md-666',
|
||||
course: 'BSI 2020',
|
||||
appointmentSlots: [
|
||||
{ weekDay: 'Quarta-feira', start: '9:00', end: '10:00' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const allAssignments = [
|
||||
{
|
||||
id: '5435',
|
||||
|
@ -309,6 +352,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',
|
||||
|
@ -534,7 +651,14 @@ const allPeople = [
|
|||
},
|
||||
];
|
||||
|
||||
const user = {
|
||||
// TODO: Mock correct data
|
||||
const grades = [
|
||||
{
|
||||
id: 'Some grade',
|
||||
},
|
||||
];
|
||||
|
||||
const studentUser = {
|
||||
id: '123',
|
||||
ra: '0021123',
|
||||
username: 'ronaldosilva',
|
||||
|
@ -548,6 +672,25 @@ const user = {
|
|||
course: 0,
|
||||
termsAgreed: true,
|
||||
year: 2018,
|
||||
role: 'STUDENT',
|
||||
};
|
||||
|
||||
const professorUser = {
|
||||
id: '321',
|
||||
ra: '0021123',
|
||||
username: 'cazalbe',
|
||||
email: 'carlos.junior@ifmg.edu.br',
|
||||
password: '#carlos1234', // TODO: Remove this!
|
||||
firstName: 'Carlos',
|
||||
lastName: 'Alexandre Silva',
|
||||
token: 'xkhfb9458hnsdfsi9q8345bsdf9b834yr',
|
||||
phone: '31111111111',
|
||||
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,
|
||||
role: 'PROFESSOR',
|
||||
};
|
||||
|
||||
const authFailure = {
|
||||
|
@ -560,7 +703,11 @@ export {
|
|||
allClassroomAnnouncements,
|
||||
allPeople,
|
||||
faq,
|
||||
user,
|
||||
studentUser,
|
||||
professorUser,
|
||||
authFailure,
|
||||
allUpcomingAssignments,
|
||||
professorClassrooms,
|
||||
assignmentsToReview,
|
||||
grades,
|
||||
};
|
Loading…
Reference in a new issue