Merge pull request 'Admin' (#49) from Admin into master

Reviewed-on: http://git.plannaplan.pl/y0rune/frontend/pulls/49
Reviewed-by: wrzesinski-hubert <wrzesinski.hubert@gmail.com>
This commit is contained in:
wrzesinski-hubert 2020-12-29 17:31:40 +01:00
commit 72782880c0
8 changed files with 78 additions and 39 deletions

View File

@ -7,7 +7,6 @@ import styled, { css } from 'styled-components';
import { makeStyles } from '@material-ui/core/styles'; import { makeStyles } from '@material-ui/core/styles';
import DeleteIcon from '@material-ui/icons/Delete'; import DeleteIcon from '@material-ui/icons/Delete';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { createClassTime } from '../utils';
import { dayMapping } from '../constants'; import { dayMapping } from '../constants';
const CourseCardWrapper = styled.div` const CourseCardWrapper = styled.div`
@ -219,12 +218,9 @@ export const CourseCard = ({ course }: CourseCardProps) => {
</FlexItem> </FlexItem>
)} )}
<FlexItem style={{ justifyContent: 'center', flexDirection: 'column' }}> <FlexItem style={{ justifyContent: 'center', flexDirection: 'column' }}>
{/* <span>
{dayMapping[group.day]} {createClassTime(group.time)[0]} - {createClassTime(group.time)[1]}
</span> */}
<div>{dayMapping[group.day]}</div> <div>{dayMapping[group.day]}</div>
<div> <div>
{createClassTime(group.time)[0]} - {createClassTime(group.time)[1]} {group.time} - {group.endTime}
</div> </div>
</FlexItem> </FlexItem>
</FlexboxWrapper> </FlexboxWrapper>

View File

@ -77,7 +77,7 @@ const StyledSchedulerEvent = styled.div<SchedulerEventProps>`
`} `}
transition: background-color ease-out 0.4s; transition: background-color ease-out 0.4s;
box-shadow: 3px 3px 3px 0px rgba(0, 0, 0, 0.75); box-shadow: 3px 3px 3px 0px rgba(0, 0, 0, 0.75);
cursor:pointer; cursor: pointer;
`; `;
const threeStyles = () => { const threeStyles = () => {
@ -189,12 +189,16 @@ export const SchedulerRow = ({ groups, indexRow, rowTop, cellWidth, cellHeight }
<BoldParagraph isThree={groupsPerDay[group.day] >= 3}>{groups[index].name}</BoldParagraph> <BoldParagraph isThree={groupsPerDay[group.day] >= 3}>{groups[index].name}</BoldParagraph>
{groupsPerDay[group.day] < 3 ? ( {groupsPerDay[group.day] < 3 ? (
<TextWrapper> <TextWrapper>
<div>{`${groups[index].time[0]}-${groups[index].time[1]}`}</div> <div>{`${groups[index].time}-${groups[index].endTime}`}</div>
<div>3/{groups[index].capacity}</div> <div>
{groups[index].takenPlaces}/{groups[index].capacity}
</div>
</TextWrapper> </TextWrapper>
) : ( ) : (
<TextWrapper style={{ flexDirection: 'column' }}> <TextWrapper style={{ flexDirection: 'column' }}>
<div style={{ alignSelf: 'flex-end' }}>3/{groups[index].capacity}</div> <div style={{ alignSelf: 'flex-end' }}>
{groups[index].takenPlaces}/{groups[index].capacity}
</div>
</TextWrapper> </TextWrapper>
)} )}
</ClassWrap> </ClassWrap>

View File

@ -6,6 +6,7 @@ export interface CASContext {
user: LoggedUser | undefined; user: LoggedUser | undefined;
logout: () => void; logout: () => void;
token: string | undefined; token: string | undefined;
refreshToken: string | undefined;
} }
export const CASContext = createContext<CASContext | undefined>(undefined); export const CASContext = createContext<CASContext | undefined>(undefined);
@ -17,6 +18,7 @@ export interface CASProviderProps {
export const CASProvider = ({ children }: CASProviderProps) => { export const CASProvider = ({ children }: CASProviderProps) => {
const [user, setUser] = useState<LoggedUser>(); const [user, setUser] = useState<LoggedUser>();
const [token, setToken] = useState<string | undefined>(); const [token, setToken] = useState<string | undefined>();
const [refreshToken, setRefreshToken] = useState<string | undefined>();
useEffect(() => { useEffect(() => {
const login = async () => { const login = async () => {
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
@ -26,15 +28,19 @@ export const CASProvider = ({ children }: CASProviderProps) => {
} }
try { try {
if (!localStorage.getItem('userToken')) { if (!localStorage.getItem('userToken')) {
const { data: user } = await axiosInstance.get<LoggedUser & { token: string }>( const { data: user } = await axiosInstance.get<LoggedUser & { token: string; refreshToken: string }>(
`${process.env.REACT_APP_API_URL}/token?ticket=${ticket}`, `${process.env.REACT_APP_API_URL}/token?ticket=${ticket}`,
); );
console.log('token response: ', user);
setUser({ authorityRole: user.authorityRole, email: user.email, id: user.id }); setUser({ authorityRole: user.authorityRole, email: user.email, id: user.id });
localStorage.setItem('userToken', user.token); localStorage.setItem('userToken', user.token);
localStorage.setItem('userPrivilige', user.authorityRole); localStorage.setItem('userPrivilige', user.authorityRole);
localStorage.setItem('refreshToken', user.refreshToken);
} }
const token = localStorage.getItem('userToken'); const token = localStorage.getItem('userToken');
const refreshToken = localStorage.getItem('refreshToken');
token && setToken(token); token && setToken(token);
refreshToken && setRefreshToken(refreshToken);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
@ -44,6 +50,7 @@ export const CASProvider = ({ children }: CASProviderProps) => {
function logout() { function logout() {
localStorage.removeItem('userToken'); localStorage.removeItem('userToken');
localStorage.removeItem('refreshToken');
localStorage.removeItem('userPrivilige'); localStorage.removeItem('userPrivilige');
redirectToCASLogoutService(); redirectToCASLogoutService();
} }
@ -56,5 +63,5 @@ export const CASProvider = ({ children }: CASProviderProps) => {
window.location.replace(`https://cas.amu.edu.pl/cas/login?service=${window.origin}&locale=pl`); window.location.replace(`https://cas.amu.edu.pl/cas/login?service=${window.origin}&locale=pl`);
} }
return <CASContext.Provider value={{ user, token, logout }}>{children}</CASContext.Provider>; return <CASContext.Provider value={{ user, token, refreshToken, logout }}>{children}</CASContext.Provider>;
}; };

View File

@ -1,7 +1,6 @@
import React, { useState, createContext, useEffect, ReactNode } from 'react'; import React, { useState, createContext, useEffect, ReactNode } from 'react';
import { Course, Group, Basket, GroupType, SchedulerEvent } from '../types'; import { Course, Group, Basket, GroupType, SchedulerEvent } from '../types';
import { useSnackbar } from 'notistack'; import { useSnackbar } from 'notistack';
import { createClassTime } from '../utils';
import { axiosInstance } from '../utils/axiosInstance'; import { axiosInstance } from '../utils/axiosInstance';
import CloseIcon from '@material-ui/icons/Close'; import CloseIcon from '@material-ui/icons/Close';
import styled from 'styled-components'; import styled from 'styled-components';
@ -71,12 +70,13 @@ export const CoursesProvider = ({ children }: CoursesProviderProps) => {
return basket.reduce((res, el) => { return basket.reduce((res, el) => {
const { name } = el; const { name } = el;
if (el.classes) { if (el.classes) {
const { time } = el.classes; console.log('element kurwa is: ', el);
res.push({ ...el.classes, name, time: createClassTime(time) }); res.push({ ...el.classes, name });
} }
if (el.lecture) { if (el.lecture) {
const { time } = el.lecture; console.log('element kurwa is: ', el);
res.push({ ...el.lecture, name, time: createClassTime(time) });
res.push({ ...el.lecture, name });
} }
return res; return res;
}, [] as Array<SchedulerEvent>); }, [] as Array<SchedulerEvent>);
@ -175,8 +175,12 @@ export const CoursesProvider = ({ children }: CoursesProviderProps) => {
const getNewestTimetable = async () => { const getNewestTimetable = async () => {
try { try {
const { data } = await axiosInstance.get(`${process.env.REACT_APP_API_URL}/api/v1/commisions/user/schedule`); const { data } = await axiosInstance.get<Array<Basket> | ''>(
`${process.env.REACT_APP_API_URL}/api/v1/commisions/user/schedule`,
);
const basket = data === '' ? [] : data; const basket = data === '' ? [] : data;
console.log('basket is: ', basket);
console.log('mordo weź');
setBasket(basket); setBasket(basket);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
@ -189,6 +193,7 @@ export const CoursesProvider = ({ children }: CoursesProviderProps) => {
`${process.env.REACT_APP_API_URL}/api/v1/commisions/user/${studentId}/schedule`, `${process.env.REACT_APP_API_URL}/api/v1/commisions/user/${studentId}/schedule`,
); );
const basket = data === '' ? [] : data; const basket = data === '' ? [] : data;
setBasket(basket); setBasket(basket);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
@ -198,13 +203,14 @@ export const CoursesProvider = ({ children }: CoursesProviderProps) => {
const fetchCourses = async () => { const fetchCourses = async () => {
try { try {
const { data: courses } = await axiosInstance.get<Array<Course>>( const { data: courses } = await axiosInstance.get<Array<Course>>(
`${process.env.REACT_APP_API_URL}/api/v1/courses/all?groups=true`, `${process.env.REACT_APP_API_URL}/api/v1/courses/all?groups=true&takenPlaces=true`,
); );
const sortedCourses = courses.sort((a, b) => (a.name > b.name ? 1 : -1)); const sortedCourses = courses.sort((a, b) => (a.name > b.name ? 1 : -1));
console.log('sortedCourses: ', sortedCourses);
setCourses(sortedCourses); setCourses(sortedCourses);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
}; };
useEffect(() => { useEffect(() => {

View File

@ -20,8 +20,6 @@ export const StudentsProvider = ({ children }: StudentsProviderProps) => {
const [selectedStudent, setSelectedStudent] = useState<Student | null>(null); const [selectedStudent, setSelectedStudent] = useState<Student | null>(null);
//not working currently //not working currently
const userPrivilige = localStorage.getItem('userPrivilige');
const { user } = useContext(CASContext)!;
const getStudents = async () => { const getStudents = async () => {
try { try {
@ -41,8 +39,9 @@ export const StudentsProvider = ({ children }: StudentsProviderProps) => {
useEffect(() => { useEffect(() => {
setTimeout(() => { setTimeout(() => {
// user?.authorityRole === 'DEANERY' && const userPrivilige = localStorage.getItem('userPrivilige');
getStudents(); console.log('mordo privilidż: ', userPrivilige);
userPrivilige === 'DEANERY' && getStudents();
}, 500); }, 500);
}, []); }, []);

View File

@ -14,10 +14,12 @@ export interface Group {
id: number; id: number;
day: number; day: number;
time: string; time: string;
endTime: string;
lecturer: string; lecturer: string;
room: string; room: string;
type: GroupType; type: GroupType;
capacity?: number; capacity?: number;
takenPlaces: number;
} }
export interface Course { export interface Course {
@ -43,10 +45,12 @@ export interface Student {
export interface SchedulerEvent { export interface SchedulerEvent {
id: number; id: number;
day: number; day: number;
time: [string, string]; time: string;
endTime: string;
lecturer: string; lecturer: string;
room: string; room: string;
type: GroupType; type: GroupType;
capacity?: number; capacity?: number;
takenPlaces: number;
name: string; name: string;
} }

View File

@ -2,15 +2,47 @@ import axios from 'axios';
export const axiosInstance = axios.create(); export const axiosInstance = axios.create();
//getting new tokens
const getNewTokens = async () => {
try {
const refreshToken = localStorage.getItem('refreshToken');
const { data } = await axiosInstance.get(
`${process.env.REACT_APP_API_URL}/token/refresh?refreshToken=${refreshToken}`,
);
localStorage.setItem('userToken', data.token);
localStorage.setItem('refreshToken', data.refreshToken);
return data.token;
} catch (e) {
console.log(e);
}
};
axiosInstance.interceptors.request.use( axiosInstance.interceptors.request.use(
(config) => { (request) => {
const token = localStorage.getItem('userToken'); const token = localStorage.getItem('userToken');
config.headers['Content-Type'] = 'application/json'; request.headers['Content-Type'] = 'application/json';
config.headers['Authorization'] = token ? `Bearer ${token}` : ''; request.headers['Authorization'] = token ? `Bearer ${token}` : '';
return config; return request;
}, },
(error) => { (error) => {
Promise.reject(error); Promise.reject(error);
}, },
); );
// Response interceptor for API calls
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
const originalRequest = error.config;
if (error.response.status === 403 && !originalRequest._retry) {
originalRequest._retry = true;
const access_token = await getNewTokens();
axios.defaults.headers.common['Authorization'] = `Bearer ${access_token}`;
return axiosInstance(originalRequest);
}
return Promise.reject(error);
},
);

View File

@ -1,21 +1,12 @@
import { courseStartTimeToEventRow } from '../constants/index'; import { courseStartTimeToEventRow } from '../constants/index';
import { SchedulerEvent } from '../types'; import { SchedulerEvent } from '../types';
export const createClassTime = (startTime: string): [string, string] => {
const startTimeMapped = courseStartTimeToEventRow[startTime];
const endTime = Object.keys(courseStartTimeToEventRow).find(
(key) => courseStartTimeToEventRow[key] === startTimeMapped + 1,
)!;
return [startTime, endTime];
};
export const selectGroupsToShow = (schedulerEvents: Array<SchedulerEvent>, index: number) => { export const selectGroupsToShow = (schedulerEvents: Array<SchedulerEvent>, index: number) => {
return schedulerEvents.filter(({ time }: { time: [string, string] }) => courseStartTimeToEventRow[time[0]] === index); return schedulerEvents.filter((schedulerEvent) => courseStartTimeToEventRow[schedulerEvent.time] === index);
}; };
//debounce declaration and implementation //debounce declaration and implementation
type Procedure = (...args: any[]) => any; type Procedure = (...args: any[]) => any;