Skip to content
This repository has been archived by the owner on May 24, 2022. It is now read-only.

Commit

Permalink
Merge pull request #461 from SELab-2/project_detail_page
Browse files Browse the repository at this point in the history
Project detail page (add students)
  • Loading branch information
Tiebe-Vercoutter authored May 21, 2022
2 parents 047dd8b + f4db862 commit 45547e4
Show file tree
Hide file tree
Showing 51 changed files with 2,912 additions and 1,393 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,6 @@ async def test_delete_pr_suggestion(database_session: AsyncSession, auth_client:
assert resp.status_code == status.HTTP_204_NO_CONTENT

response2 = await auth_client.get(f'/editions/{edition.name}/projects/{project.project_id}')
print(response2.json())
print((await database_session.execute(select(ProjectRole))).scalars().one().suggestions)
json = response2.json()
assert len(json['projectRoles']) == 1
assert len(json['projectRoles'][0]['suggestions']) == 0
Expand Down
2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@types/jest": "^27.0.1",
"@types/node": "^17.0.34",
"@types/react": "^17.0.20",
"@types/react-beautiful-dnd": "^13.1.2",
"@types/react-dom": "^17.0.9",
"@types/react-infinite-scroller": "^1.2.3",
"@types/react-router-bootstrap": "^0.24.5",
Expand All @@ -55,6 +56,7 @@
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-standard": "^5.0.0",
"react-beautiful-dnd": "^13.1.0",
"jest": "^28.1.0",
"prettier": "^2.6.2",
"typedoc": "^0.22.15"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default function Router() {
<Route path={"editions"} element={<PrivateRoute />}>
<Route path={""} element={<EditionsPage />} />
<Route path={"new"} element={<AdminRoute />}>
{/* TODO create edition page */}
{/* create edition page */}
<Route path={""} element={<CreateEditionPage />} />
</Route>
<Route path={":editionId"} element={<Outlet />}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { CreateButton } from "../../Common/Buttons";
import { FormControl } from "../../Common/Forms";
import { StyledModal, ModalHeader, ModalFooter, Button } from "./styles";
import FloatingLabel from "react-bootstrap/FloatingLabel";
import { useState } from "react";
import { AddStudentRole } from "../../../data/interfaces/projects";

export default function AddStudentModal({
visible,
handleClose,
handleConfirm,
result,
}: {
visible: boolean;
handleClose: () => void;
handleConfirm: (motivation: string, addStudentRole: AddStudentRole) => void;
result: AddStudentRole;
}) {
const [motivation, setMotivation] = useState("");
return (
<StyledModal show={visible} onHide={handleClose}>
<ModalHeader closeButton>
<StyledModal.Title>Suggest student for project</StyledModal.Title>
</ModalHeader>

<StyledModal.Body>
Please motivate your decision
<FloatingLabel label={"Motivation"} className={"mb-1"}>
<FormControl
placeholder={"Good fit!"}
value={motivation}
onChange={e => {
setMotivation(e.target.value);
}}
/>
</FloatingLabel>
</StyledModal.Body>

<ModalFooter>
<Button
onClick={() => {
handleClose();
setMotivation("");
}}
>
Cancel
</Button>
<CreateButton
label="Suggest"
onClick={() => {
handleConfirm(motivation, result);
setMotivation("");
}}
/>
</ModalFooter>
</StyledModal>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./AddStudentModal";
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import styled from "styled-components";
import Modal from "react-bootstrap/Modal";

export const StyledModal = styled(Modal)`
color: white;
background-color: #00000060;
margin-top: 5%;
.modal-content {
background-color: #272741;
border-radius: 5px;
border-color: var(--osoc_green);
}
`;

export const ModalHeader = styled(Modal.Header)`
border-bottom: 1px solid #131329;
`;
export const ModalFooter = styled(Modal.Footer)`
border-top: 1px solid #131329;
`;

export const Button = styled.button`
border-radius: 5px;
border: none;
padding: 5px 10px;
background-color: #131329;
color: white;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { Project } from "../../../data/interfaces";
import { User } from "../../../utils/api/users/users";

import { getCoaches } from "../../../utils/api/users/coaches";
import { Input, AddButton } from "../PartnerInput/styles";

export default function CoachInput({
project,
setProject,
}: {
project: Project;
setProject: (project: Project) => void;
}) {
const [coach, setCoach] = useState("");
const [availableCoaches, setAvailableCoaches] = useState<User[]>([]);

const params = useParams();
const editionId = params.editionId!;

useEffect(() => {
async function callCoaches() {
setAvailableCoaches((await getCoaches(editionId, coach, 0)).users);
}
callCoaches();
}, [coach, editionId]);

return (
<>
<Input
value={coach}
onChange={e => {
setCoach(e.target.value);
}}
list="coaches"
placeholder="Coach"
/>

<datalist id="coaches">
{availableCoaches.map((availableCoach, _index) => {
return <option key={_index} value={availableCoach.name} />;
})}
</datalist>

<AddButton
onClick={() => {
addToCoaches();
}}
>
Add Coach
</AddButton>
</>
);

function addToCoaches() {
let coachToAdd = null;
availableCoaches.forEach(availableCoach => {
if (availableCoach.name === coach) {
coachToAdd = availableCoach;
}
});
if (coachToAdd) {
if (!project.coaches.some(presentCoach => presentCoach.name === coach)) {
const newCoaches = [...project.coaches];
newCoaches.push(coachToAdd);
setProject({ ...project, coaches: newCoaches });
}
}
setCoach("");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./CoachInput";
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useState } from "react";
import { Project, Partner } from "../../../data/interfaces";
import { Input, AddButton } from "./styles";

export default function PartnerInput({
project,
setProject,
}: {
project: Project;
setProject: (project: Project) => void;
}) {
const [partner, setPartner] = useState("");

return (
<>
<Input
value={partner}
onChange={e => {
setPartner(e.target.value);
}}
placeholder="Partner"
/>
<AddButton
onClick={() => {
addToPartners();
}}
>
Add Partner
</AddButton>
</>
);

function addToPartners() {
if (!project.partners.some(presentPartner => presentPartner.name === partner)) {
const newPartner: Partner = { name: partner };
const newPartners = [...project.partners];
newPartners.push(newPartner);
const newProject: Project = { ...project, partners: newPartners };
setProject(newProject);
}
setPartner("");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./PartnerInput";
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import styled from "styled-components";

export const Input = styled.input`
margin-right: 5px;
padding: 5px 10px;
background-color: #131329;
color: white;
border: none;
border-radius: 5px;
width: 15%;
min-width: 100px;
`;

export const AddButton = styled.button`
padding: 0 10px;
background-color: #00bfff;
color: white;
border: none;
margin-right: 10px;
border-radius: 5px;
min-height: 34px;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { TiDeleteOutline } from "react-icons/ti";
import { CoachContainer, CoachesContainer, CoachText, RemoveButton } from "./styles";
import CoachInput from "../CoachInput";
import { Project } from "../../../data/interfaces";

export default function ProjectCoaches({
project,
editedProject,
setEditedProject,
editing,
}: {
project: Project;
editedProject: Project;
setEditedProject: (project: Project) => void;
editing: boolean;
}) {
return (
<CoachesContainer>
{editedProject.coaches.map((element, _index) => (
<CoachContainer key={_index}>
<CoachText>{element.name}</CoachText>
{editing && (
<RemoveButton
onClick={() => {
const newCoaches = [...editedProject.coaches];

newCoaches.splice(_index, 1);
const newProject: Project = {
...project,
coaches: newCoaches,
};
setEditedProject(newProject);
}}
>
<TiDeleteOutline size={"20px"} />
</RemoveButton>
)}
</CoachContainer>
))}
{editing && <CoachInput project={editedProject!} setProject={setEditedProject} />}
</CoachesContainer>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./ProjectCoaches";
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import styled from "styled-components";

export const CoachesContainer = styled.div`
display: flex;
align-items: center;
margin-top: 20px;
overflow-x: auto;
`;

export const CoachContainer = styled.div`
background-color: #1a1a36;
border-radius: 5px;
margin-right: 10px;
text-align: center;
padding: 7.5px 15px;
width: fit-content;
max-width: 20vw;
display: flex;
`;

export const CoachText = styled.div`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;

export const RemoveButton = styled.button`
padding: 0px 2.5px;
background-color: #f14a3b;
color: white;
border: none;
margin-left: 10px;
border-radius: 1px;
display: flex;
align-items: center;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { TiDeleteOutline } from "react-icons/ti";
import { Project } from "../../../data/interfaces";
import PartnerInput from "../PartnerInput";

import { ClientContainer, Client, RemoveButton } from "./styles";

export default function ProjectPartners({
project,
editedProject,
setEditedProject,
editing,
}: {
project: Project;
editedProject: Project;
setEditedProject: (project: Project) => void;
editing: boolean;
}) {
return (
<>
{editedProject.partners.map((element, _index) => (
<ClientContainer key={_index}>
<Client>{element.name}</Client>
{editing && (
<RemoveButton
onClick={() => {
const newPartners = [...editedProject.partners];
newPartners.splice(_index, 1);
const newProject: Project = {
...project,
partners: newPartners,
};
setEditedProject(newProject);
}}
>
<TiDeleteOutline size={"20px"} />
</RemoveButton>
)}
</ClientContainer>
))}
{editing && <PartnerInput project={editedProject!} setProject={setEditedProject} />}
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./ProjectPartners";
Loading

0 comments on commit 45547e4

Please sign in to comment.