Major refactor

This commit is contained in:
Maciek Głowacki 2020-08-17 23:56:34 +02:00
parent 55bb7945c9
commit 74ad2e835c
14 changed files with 220 additions and 259 deletions

3
.env
View File

@ -1,2 +1 @@
REACT_APP_API_URL=http://localhost:5000/api
REACT_APP_API_HOST=http://localhost:5000
REACT_APP_API_URL=http://localhost:1285

View File

@ -2,7 +2,7 @@ import React, { useState, useContext } from 'react';
import { Topbar } from './Topbar';
import { Transfer } from './Transfer/Transfer';
import { Scheduler } from './Scheduler';
import RightBar from './Rightbar';
import { Rightbar } from './Rightbar';
import { CASContext } from '../contexts/CASProvider';
import styled from 'styled-components';
@ -13,7 +13,6 @@ const Wrapper = styled.div`
export const App = () => {
const [isOpenTransfer, setOpenTransfer] = useState(false);
const { logout } = useContext(CASContext)!;
const handleTransfer = () => {
setOpenTransfer(!isOpenTransfer);
@ -21,11 +20,11 @@ export const App = () => {
return (
<>
<Topbar handleTransfer={handleTransfer} handleLogout={logout} />
<Topbar handleTransfer={handleTransfer} />
<Transfer isOpen={isOpenTransfer} handleClose={handleTransfer} />
<Wrapper>
<Scheduler />
<RightBar />
<Rightbar />
</Wrapper>
</>
);

View File

@ -1,4 +1,4 @@
import React, { useContext } from 'react';
import React, { useContext, MouseEvent } from 'react';
import Collapse from '@material-ui/core/Collapse';
import ExpandIcon from '../assets/expand.png';
import { Course, Group } from '../types/index';
@ -10,7 +10,7 @@ interface ClassExandIconProps {
isSelected: boolean;
}
const ClassStyled = styled.div`
const CourseStyled = styled.div`
display: flex;
min-height: 50px;
background-color: rgb(100, 181, 246) !important;
@ -25,7 +25,7 @@ const ClassStyled = styled.div`
align-items: stretch;
`;
const ClassNameStyled = styled.div`
const CourseNameStyled = styled.div`
padding-top: 10px;
padding-bottom: 10px;
`;
@ -68,39 +68,38 @@ const useStyles = makeStyles({
});
interface CourseCardProps {
onCardClick: (e: React.MouseEvent) => void;
onCardClick: (event: MouseEvent) => void;
course: Course;
id: string;
isSelected: boolean;
}
export function CourseCard({ onCardClick, course, id, isSelected }: CourseCardProps) {
export const CourseCard = ({ onCardClick, course, id, isSelected }: CourseCardProps) => {
const classes = useStyles();
const { addGroup, courses } = useContext(coursesContext)!;
function onGroupClick(group: Group) {
addGroup(group);
}
const { addChoosenGroup, choosenCourses } = useContext(coursesContext)!;
const onGroupClick = (group: Group) => addChoosenGroup(group);
return (
<ClassStyled onClick={onCardClick} id={id}>
<ClassNameStyled>{course.name}</ClassNameStyled>
<CourseStyled onClick={onCardClick} id={id}>
<CourseNameStyled>{course.name}</CourseNameStyled>
<Collapse className={classes.expanded} in={isSelected} timeout="auto" unmountOnExit>
{courses.map((course, index) => (
<>
{choosenCourses.map((course) => (
<div key={id}>
{course.groups.map((group, index) => (
<ClassGroupStyled key={index} onClick={() => onGroupClick(group)}>
<p>
{group.time} {group.room} <br></br> {group.lecturer}
</p>{' '}
</p>
</ClassGroupStyled>
))}
</>
</div>
))}
</Collapse>
<div onClick={onCardClick} id={id}>
<ClassExandIconStyled isSelected={isSelected} alt="expand" src={ExpandIcon} />
</div>
</ClassStyled>
</CourseStyled>
);
}
};

110
src/components/Dropdown.tsx Normal file
View File

@ -0,0 +1,110 @@
import React, { useState, useContext, useEffect, MouseEvent } from 'react';
import axios from 'axios';
import { Input } from '@material-ui/core';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import { coursesContext } from '../contexts/CoursesProvider';
import { Course } from '../types';
import styled from 'styled-components';
import { makeStyles } from '@material-ui/core/styles';
const CourseStyled = styled.div`
position: relative;
z-index: 10;
padding: 5px;
padding-left: 20px;
background-color: #e6c759;
font-size: 18px;
font-family: Lato;
:hover {
background-color: #d4b851;
cursor: pointer;
}
`;
const DropdownStyled = styled.div`
max-height: 400px;
overflow-y: auto;
::-webkit-scrollbar {
display: none;
}
`;
const useStyles = makeStyles({
topbarInput: {
marginTop: '8px',
width: '100%',
},
});
export const Dropdown = () => {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const [input, setInput] = useState<string>('');
//courses - choosenCourses
const [filteredCourses, setFilteredCourses] = useState<Array<Course>>([]);
const { courses, choosenCourses, addChoosenCourse } = useContext(coursesContext)!;
useEffect(() => {
const filterCourses = (input: string) => {
const choosenCoursesNames = choosenCourses.map(({ name }) => name.trim());
const filteredCourses = courses.filter(
({ name }) => name.toLowerCase().includes(input.toLowerCase()) && !choosenCoursesNames.includes(name),
);
setFilteredCourses(filteredCourses);
};
filterCourses(input);
}, [input, open, choosenCourses]);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setInput(event.target.value);
const handleClick = () => setOpen(true);
const handleClickAway = () => setOpen(false);
const onCourseClick = async (event: MouseEvent) => {
const target = event.currentTarget;
if (target.id && target.textContent) {
const id = target.id;
const name = target.textContent;
const { data } = await axios.get(`${process.env.REACT_APP_API_URL}/getCourseGroups?id=${id}`);
//porozmawiać z Filipem, żeby odrobinę przerobił endpoint
const course: Course = {
name: name,
id: parseInt(id),
groups: data,
};
addChoosenCourse(course);
setOpen(false);
}
};
return (
<ClickAwayListener onClickAway={handleClickAway}>
<div>
<Input
placeholder="Wyszukaj..."
inputProps={{ 'aria-label': 'description' }}
className={classes.topbarInput}
onChange={handleChange}
onClick={handleClick}
value={input}
/>
{open && (
<DropdownStyled>
{filteredCourses.map(({ name, id }, index) => (
<CourseStyled key={index} id={id.toString()} onClick={onCourseClick}>
<p>{name} </p>
</CourseStyled>
))}
</DropdownStyled>
)}
</div>
</ClickAwayListener>
);
};

View File

@ -1,31 +1,20 @@
import { Menu, MenuItem } from '@material-ui/core';
import React, { FC } from 'react';
import React, { useContext } from 'react';
import { CASContext } from '../contexts/CASProvider';
interface ProfileProps {
anchorEl: HTMLElement | null;
handleClose: () => void;
handleLogout: () => void;
}
export const Profile: FC<ProfileProps> = ({ anchorEl, handleClose, handleLogout, ...restProps }) => {
export const Profile = ({ anchorEl, handleClose }: ProfileProps) => {
const { logout } = useContext(CASContext)!;
return (
<Menu
className="top-bar__menu"
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
<MenuItem>Profile</MenuItem>
<MenuItem>My account</MenuItem>
<MenuItem
onClick={() => {
handleLogout();
}}
>
Logout
</MenuItem>
<MenuItem onClick={logout}>Logout</MenuItem>
</Menu>
);
};

View File

@ -1,147 +0,0 @@
import React, { useState, useContext, useEffect } from 'react';
import axios from 'axios';
import { Input } from '@material-ui/core';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import { coursesContext } from '../contexts/CoursesProvider';
import { Course } from '../types';
import styled from 'styled-components';
import { makeStyles } from '@material-ui/core/styles';
interface courseData {
name: string;
id: number;
}
const CourseStyled = styled.div`
position: relative;
z-index: 10;
padding: 5px;
padding-left: 20px;
background-color: #e6c759;
font-size: 18px;
font-family: Lato;
:hover {
background-color: #d4b851;
cursor: pointer;
}
`;
const Dropdown = styled.div`
max-height: 400px;
overflow-y: auto;
::-webkit-scrollbar {
display: none;
}
`;
const useStyles = makeStyles({
topbarInput: {
marginTop: '8px',
width: '100%',
},
});
export const Results: React.FC = () => {
const classes = useStyles();
const [input, setInput] = useState<string>('');
const [coursesData, setcoursesData] = useState<Array<courseData>>([]);
const [filteredcoursesData, setFilteredcoursesData] = useState<Array<courseData>>([]);
const [open, setOpen] = React.useState(false);
const { courses, addCourse } = useContext(coursesContext)!;
useEffect(() => {
const fetchData = async () => {
const results = await axios.get(`http://localhost:1285/getCourses`);
const coursesData = results.data.map((result: { id: number; name: string }) => ({
id: result.id,
name: result.name,
}));
setcoursesData(coursesData);
};
fetchData();
}, []);
useEffect(() => {
const names = courses.map((course) => course.name);
const filtercourses = (value: string) => {
let filteredcourses = coursesData.filter(
(course) => course.name.toLowerCase().includes(value.toLowerCase()) && !names.includes(course.name),
);
setFilteredcoursesData(filteredcourses);
};
filtercourses(input);
}, [input, open]);
const getGroupsByCourseId = async (id: string) => {
const { data } = await axios.get(`http://localhost:1285/getCourseGroups?id=${id}`);
return data;
};
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setInput(event.target.value);
};
const handleClick = () => {
setOpen(true);
};
const handleClickAway = () => {
setOpen(false);
};
const onCourseClick = async (e: React.MouseEvent) => {
const target = e.currentTarget as HTMLElement;
if (target.id && target.textContent) {
const id = target.id;
const name = target.textContent;
const groups = await getGroupsByCourseId(id);
const course: Course = {
name: name,
id: parseInt(id),
groups: groups,
};
addCourse(course);
setOpen(false);
}
// let groups: Array<Group> = [];
// let course = { groups: groups } as course;
// course.id = result[0].course.id;
// course.name = result[0].course.name;
// for (let i = 0; i < result.length; i++) {
// let group = {} as Group;
// group.id = result[i].id;
// group.day = result[i].day;
// group.time = result[i].time;
// group.courser = result[i].courser.title + ' ' + result[i].courser.name + ' ' + result[i].courser.surname;
// group.room = result[i].room.trim();
// course.groups.push(group);
// }
};
return (
<ClickAwayListener onClickAway={handleClickAway}>
<div>
<Input
placeholder="Wyszukaj..."
inputProps={{ 'aria-label': 'description' }}
className={classes.topbarInput}
onChange={handleChange}
onClick={handleClick}
value={input}
/>
{open ? (
<Dropdown>
{filteredcoursesData.map((course, index) => (
<CourseStyled key={index} id={String(course.id)} onClick={onCourseClick}>
<p>{course.name} </p>
</CourseStyled>
))}
</Dropdown>
) : null}
</div>
</ClickAwayListener>
);
};

View File

@ -1,4 +1,4 @@
import React, { useState, useContext } from 'react';
import React, { useState, useContext, MouseEvent } from 'react';
import { CourseCard } from './CourseCard';
import { coursesContext } from '../contexts/CoursesProvider';
import styled from 'styled-components';
@ -30,23 +30,25 @@ const RightbarTextStyled = styled.div`
border-bottom: 1px solid;
`;
export default function Rightbar() {
export const Rightbar = () => {
const [selectedCardId, setSelectedCardId] = useState<string | null>(null);
const { courses } = useContext(coursesContext)!;
const { choosenCourses } = useContext(coursesContext)!;
const onCardClick = (e: React.MouseEvent) => {
const target = e.currentTarget as HTMLElement;
//działa clunky
const onCardClick = (event: MouseEvent) => {
const target = event.currentTarget;
selectedCardId === target.id ? setSelectedCardId(null) : setSelectedCardId(target.id);
};
//need to insert student name from db and course maybe based on current time or from db too
return (
<RightbarStyled>
<RightbarTextStyled>
Hubert Wrzesiński<br></br>
Semestr zimowy 2020/2021
</RightbarTextStyled>
{courses.map((course, index) => (
{choosenCourses.map((course, index) => (
<CourseCard
course={course}
key={index}
@ -57,4 +59,4 @@ export default function Rightbar() {
))}
</RightbarStyled>
);
}
};

View File

@ -1,8 +1,8 @@
import React, { useEffect, useRef } from "react";
import { useState } from "react";
import { SchedulerEvents } from "./SchedulerEvents";
import { days, hours } from "../constants/index";
import styled from "styled-components";
import React, { useEffect, useRef } from 'react';
import { useState } from 'react';
import { SchedulerEvents } from './SchedulerEvents';
import { days, hours } from '../constants/index';
import styled from 'styled-components';
const SchedulerWrapper = styled.div`
flex-grow: 3;
@ -55,7 +55,7 @@ export const Scheduler = () => {
}
};
handleResize();
window.addEventListener("resize", handleResize);
window.addEventListener('resize', handleResize);
}, []);
useEffect(() => {
@ -63,7 +63,7 @@ export const Scheduler = () => {
currentEventsIds.map((eventId: string) => {
const event = document.getElementById(eventId);
if (event) {
event.style.display = "block";
event.style.display = 'block';
}
});
};
@ -90,15 +90,12 @@ export const Scheduler = () => {
<TableBody>
{hours.map((hour, indexRow) => (
<TableRow key={indexRow}>
{[hour, "", "", "", "", ""].map((value, indexCell) =>
{[hour, '', '', '', '', ''].map((value, indexCell) =>
indexRow === 0 && indexCell === 1 ? (
<TableCell
key={`${indexRow}${indexCell}`}
ref={cellRef}
></TableCell>
<TableCell key={`${indexRow}${indexCell}`} ref={cellRef}></TableCell>
) : (
<TableCell key={`${indexRow}${indexCell}`}>{value}</TableCell>
)
),
)}
</TableRow>
))}

View File

@ -13,14 +13,10 @@ export const SchedulerEvents = ({ cellTop, cellWidth }: SchedulerEventsProps) =>
const [groupsMappedToEvents, setGroupsMappedToEvents] = useState<any>([]);
// const groups: Array<Group> = [{ id: "5", day: "4", time: "11.45", courser: "dr Dorota Blinkiewicz", room: "A2-3" },
// { id: "28", day: "1", time: "13.45", courser: "dr Barbara Kołodziejczak", room: "D-3" },
// { id: "69", day: "4", time: "15.30", courser: "dr Karol Gierszewski", room: "A2-3" }];
interface GroupTimeToEventRowMapping {
[time: string]: number;
}
//delete later additional mappings
const groupTimeToEventRowMapping: GroupTimeToEventRowMapping = {
'08.15': 0,
'10.00': 1,
@ -28,6 +24,8 @@ export const SchedulerEvents = ({ cellTop, cellWidth }: SchedulerEventsProps) =>
'13.45': 3,
'15.30': 4,
'17.15': 5,
'10.17': 0,
'13.55': 1,
};
useEffect(() => {
@ -45,23 +43,20 @@ export const SchedulerEvents = ({ cellTop, cellWidth }: SchedulerEventsProps) =>
};
setGroupsMappedToEvents((groupsMappedToEvents: any) => [...groupsMappedToEvents, groupMappedToEvent]);
}
function alternative(groups: Array<Group>) {
const groupsMapped = choosenGroups.map(({ id, day, lecturer, room, time }) => ({
id,
day,
lecturer,
room,
eventRow: groupTimeToEventRowMapping[time],
}));
setGroupsMappedToEvents(groupsMapped);
}
}
mapGroupTimeToEventRow(choosenGroups);
function alternative(choosenGroups: Array<Group>) {
const groupsMapped = choosenGroups.map(({ id, day, lecturer, room, time }) => ({
id,
day,
lecturer,
room,
eventRow: groupTimeToEventRowMapping[time],
}));
setGroupsMappedToEvents(groupsMapped);
}
alternative(choosenGroups);
}, [choosenGroups]);
useEffect(() => {
}, [groupsMappedToEvents]);
return (
<div>
{[...Array(6)].map((_, index) => (

View File

@ -26,8 +26,8 @@ interface SchedulerRowProps {
}
export const SchedulerRow = ({ groups, indexRow, cellTop, cellWidth }: SchedulerRowProps) => {
console.log(`You passed me these of a groupzzz`);
console.log(groups)
// console.log(`You passed me these of a groupzzz`);
// console.log(groups)
return (
<>

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, MouseEvent } from 'react';
import Transfer from '../assets/transfer.png';
import Search from '../assets/search.svg';
import UK from '../assets/UK.png';
@ -6,7 +6,7 @@ import PL from '../assets/PL.png';
import User from '../assets/user.png';
import CloseIcon from '../assets/close.svg';
import { Profile } from './Profile';
import { Results } from './Results';
import { Dropdown } from './Dropdown';
import styled from 'styled-components';
const TopbarTextStyled = styled.div`
@ -26,14 +26,14 @@ const TopbarStyled = styled.div`
justify-content: space-between;
`;
const TopbarLogoStyled = styled.div`
const TopbarLogoWrapperStyled = styled.div`
display: flex;
align-items: center;
flex-grow: 0.5;
justify-content: flex-start;
`;
const TopbarLogoImageStyled = styled.img`
const TopbarLogoStyled = styled.img`
width: 80px;
height: 80px;
@media only screen and (max-width: 670px) {
@ -42,7 +42,7 @@ const TopbarLogoImageStyled = styled.img`
}
`;
const TopbarInputDivStyled = styled.div`
const TopbarInputStyled = styled.div`
width: 70%;
display: flex;
flex-grow: 3;
@ -76,38 +76,37 @@ const TopbarIconBox = styled.div`
`;
interface TopbarProps {
handleTransfer: (e: React.MouseEvent) => void;
handleLogout: () => void;
handleTransfer: (e: MouseEvent) => void;
}
export const Topbar = ({ handleTransfer, handleLogout }: TopbarProps) => {
export const Topbar = ({ handleTransfer }: TopbarProps) => {
const [isPolish, setIsPolish] = useState(false);
const [anchorEl, setAnchorEl] = useState<HTMLImageElement | null>(null);
const onLangChange = (event: React.MouseEvent) => setIsPolish(!isPolish);
const onLangChange = () => setIsPolish(!isPolish);
const handleProfile = (event: React.MouseEvent) => setAnchorEl(event.currentTarget as HTMLImageElement);
const handleProfile = (event: MouseEvent<HTMLImageElement>) => setAnchorEl(event.currentTarget);
const handleClose = () => setAnchorEl(null);
return (
<TopbarStyled>
<TopbarLogoStyled>
<TopbarLogoImageStyled alt="logo" src="https://plannaplan.pl/img/logo.svg" />
<TopbarLogoWrapperStyled>
<TopbarLogoStyled alt="logo" src="https://plannaplan.pl/img/logo.svg" />
<TopbarTextStyled> plan na plan </TopbarTextStyled>
</TopbarLogoStyled>
<TopbarInputDivStyled>
</TopbarLogoWrapperStyled>
<TopbarInputStyled>
<TopbarInputIconStyled alt="search" src={Search} />
<TopbarInputFieldStyled>
<Results />
<Dropdown />
</TopbarInputFieldStyled>
<TopbarInputIconStyled alt="close" src={CloseIcon} />
</TopbarInputDivStyled>
</TopbarInputStyled>
<TopbarIconBox>
<TopbarIcon alt="transfer" src={Transfer} onClick={handleTransfer} />
<TopbarIcon alt="change_language" src={isPolish ? UK : PL} onClick={onLangChange} />
<TopbarIcon alt="profile" src={User} onClick={handleProfile} />
<Profile anchorEl={anchorEl} handleClose={handleClose} handleLogout={handleLogout} />
<Profile anchorEl={anchorEl} handleClose={handleClose} />
</TopbarIconBox>
</TopbarStyled>
);

View File

@ -27,7 +27,6 @@ export const CASProvider = ({ children }: CASProviderProps) => {
redirectToCASLoginService();
}
if (ticket) {
console.log(`Ticket is: ${ticket}`);
setUser({ ...user, ticket: ticket });
}
}

View File

@ -1,13 +1,14 @@
import React, { useState, createContext, useEffect } from 'react';
import { Course, Group } from '../types';
interface courseContext {
import axios from 'axios';
interface CourseContext {
courses: Array<Course>;
choosenCourses: Array<Course>;
choosenGroups: Array<Group>;
addCourse: (courses: Course) => void;
addGroup: (group: Group) => void;
addChoosenCourse: (courses: Course) => void;
addChoosenGroup: (group: Group) => void;
}
export const coursesContext = createContext<courseContext | null>(null);
export const coursesContext = createContext<CourseContext | null>(null);
interface CoursesProviderProps {
children: React.ReactNode;
@ -15,17 +16,36 @@ interface CoursesProviderProps {
export const CoursesProvider = ({ children }: CoursesProviderProps) => {
const [courses, setCourses] = useState<Array<Course>>([]);
const [choosenCourses, setChoosenCourses] = useState<Array<Course>>([]);
const [choosenGroups, setChoosenGroups] = useState<Array<Group>>([]);
const addCourse = (course: Course) => {
setCourses([...courses, course]);
const addChoosenCourse = (choosenCourse: Course) => {
console.log('adding course');
setChoosenCourses([...choosenCourses, choosenCourse]);
};
const addGroup = (group: Group) => {
setChoosenGroups([...choosenGroups, group]);
const addChoosenGroup = (choosenGroup: Group) => {
setChoosenGroups([...choosenGroups, choosenGroup]);
};
useEffect(() => {
console.log('All courses');
console.log(courses);
}, [courses]);
useEffect(() => {
console.log('Choosen courses');
console.log(choosenCourses);
}, [choosenCourses]);
useEffect(() => {
const fetchData = async () => {
const { data } = await axios.get(`${process.env.REACT_APP_API_URL}/getCourses`);
setCourses(data);
};
fetchData();
}, []);
return (
<coursesContext.Provider value={{ courses, choosenGroups, addCourse, addGroup }}>
<coursesContext.Provider value={{ courses, choosenGroups, choosenCourses, addChoosenCourse, addChoosenGroup }}>
{children}
</coursesContext.Provider>
);

View File

@ -1,6 +1,6 @@
export enum Types {
addCourse = 'ADD_COURSE',
addChoosenCourse = 'ADD_COURSE',
removecourse = 'REMOVE_COURSE',
addGroup = 'ADD_GROUP',
addChoosenGroup = 'ADD_GROUP',
removeGroup = 'REMOVE_GROUP',
}