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

Commit

Permalink
Merge branch 'develop' into create_project_skills
Browse files Browse the repository at this point in the history
  • Loading branch information
Tiebe-Vercoutter committed May 21, 2022
2 parents c4c2350 + 9689c45 commit a132908
Show file tree
Hide file tree
Showing 15 changed files with 374 additions and 331 deletions.
16 changes: 0 additions & 16 deletions frontend/src/components/AdminsComponents/AdminList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import { User } from "../../utils/api/users/users";
import { AdminsTable } from "./styles";
import React from "react";
import { AdminListItem } from "./index";
import LoadSpinner from "../Common/LoadSpinner";
import { ListDiv } from "../Common/Users/styles";
import { SpinnerContainer } from "../Common/LoadSpinner/styles";
import { RemoveTh } from "../Common/Tables/styles";

/**
Expand All @@ -21,20 +19,6 @@ export default function AdminList(props: {
gotData: boolean;
removeAdmin: (user: User) => void;
}) {
if (props.loading) {
return (
<SpinnerContainer>
<LoadSpinner show={true} />
</SpinnerContainer>
);
} else if (props.admins.length === 0) {
if (props.gotData) {
return <div>No admins</div>;
} else {
return null;
}
}

return (
<ListDiv>
<AdminsTable>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default function Coach({

useEffect(() => {
async function callCoaches() {
setAvailableCoaches((await getCoaches(editionId, coach, 0)).users);
setAvailableCoaches((await getCoaches(editionId, coach, 0))!.users);
}
callCoaches();
}, [coach, editionId]);
Expand All @@ -49,7 +49,7 @@ export default function Coach({

<AddButton onClick={addCoach}>Add</AddButton>
<WarningContainer>
<BadCoachAlert show={showAlert} setShow={setShowAlert}></BadCoachAlert>
<BadCoachAlert show={showAlert} setShow={setShowAlert} />
</WarningContainer>
</div>
);
Expand Down
11 changes: 6 additions & 5 deletions frontend/src/components/ProjectsComponents/ProjectTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@ import LoadSpinner from "../Common/LoadSpinner";
* A table of [[ProjectCard]]s.
* @param props.projects A list of projects which needs to be shown.
* @param props.loading Data is not available yet.
* @param props.gotData All data is received.
* @param props.getMoreProjects A function to load more projects.
* @param props.moreProjectsAvailable More unfetched projects available.
* @param props.removeProject A function which will be called when a project is removed.
*/
export default function ProjectTable(props: {
projects: Project[];
loading: boolean;
gotData: boolean;
getMoreProjects: () => void;
getMoreProjects: (page: number, reset: boolean) => void;
moreProjectsAvailable: boolean;
removeProject: (project: Project) => void;
}) {
if (props.gotData && props.projects.length === 0) {
if (props.projects.length === 0) {
if (props.loading) {
return <LoadSpinner show={true} />;
}
return (
<MessageDiv>
<div>No projects found.</div>
Expand All @@ -32,7 +33,7 @@ export default function ProjectTable(props: {

return (
<InfiniteScroll
loadMore={props.getMoreProjects}
loadMore={page => props.getMoreProjects(page, false)}
hasMore={props.moreProjectsAvailable}
loader={<LoadSpinner show={true} key="Spinner" />}
initialLoad={true}
Expand Down
22 changes: 16 additions & 6 deletions frontend/src/components/UsersComponents/Coaches/Coaches.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ import { User } from "../../../utils/api/users/users";
import { CoachList, AddCoach } from "./CoachesComponents";
import { SearchBar } from "../../Common/Forms";
import { SearchFieldDiv, TableDiv } from "../../Common/Users/styles";
import LoadSpinner from "../../Common/LoadSpinner";

/**
* List of coaches of the given edition.
* This includes a searchfield and the option to remove and add coaches.
* @param props.edition The edition of which coaches are shown.
* @param props.coaches The list of coaches which need to be shown.
* @param props.loading Data is being loaded
* @param props.getMoreCoaches A function to load more coaches.
* @param props.searchCoaches A function to set the filter for coaches' username.
* @param props.gotData All data is received.
* @param props.setPage Set the next page to fetch
* @param props.moreCoachesAvailable More unfetched coaches available.
* @param props.searchTerm Current filter for coaches' names.
* @param props.refreshCoaches A function which will be called when a coach is added.
Expand All @@ -21,17 +23,22 @@ import { SearchFieldDiv, TableDiv } from "../../Common/Users/styles";
export default function Coaches(props: {
edition: string;
coaches: User[];
getMoreCoaches: () => void;
loading: boolean;
getMoreCoaches: (page: number) => void;
searchCoaches: (word: string) => void;
gotData: boolean;
setPage: (page: number) => void;
moreCoachesAvailable: boolean;
searchTerm: string;
refreshCoaches: () => void;
removeCoach: (user: User) => void;
}) {
let table;
if (props.gotData && props.coaches.length === 0) {
table = <div>No coaches found</div>;
if (props.coaches.length === 0) {
if (props.loading) {
table = <LoadSpinner show={true} />;
} else {
table = <div>No coaches found</div>;
}
} else {
table = (
<CoachList
Expand All @@ -49,7 +56,10 @@ export default function Coaches(props: {
<CoachesTitle>Coaches</CoachesTitle>
<SearchFieldDiv>
<SearchBar
onChange={e => props.searchCoaches(e.target.value)}
onChange={e => {
props.searchCoaches(e.target.value);
props.setPage(0);
}}
value={props.searchTerm}
placeholder="Search name..."
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@ export default function CoachList(props: {
coaches: User[];
edition: string;
removeCoach: (coach: User) => void;
getMoreCoaches: () => void;
getMoreCoaches: (page: number) => void;
moreCoachesAvailable: boolean;
}) {
return (
<ListDiv>
<InfiniteScroll
pageStart={0}
loadMore={props.getMoreCoaches}
hasMore={props.moreCoachesAvailable}
loader={<LoadSpinner show={true} key="spinner" />}
Expand Down
100 changes: 49 additions & 51 deletions frontend/src/components/UsersComponents/Requests/Requests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { RequestList, RequestsHeader } from "./RequestsComponents";
import SearchBar from "../../Common/Forms/SearchBar";
import { SearchFieldDiv } from "../../Common/Users/styles";
import { toast } from "react-toastify";
import { LoadSpinner } from "../../Common";

/**
* A collapsible component which contains all coach requests for a given edition.
Expand All @@ -18,7 +19,7 @@ export default function Requests(props: { edition: string; refreshCoaches: () =>
const [requests, setRequests] = useState<Request[]>([]); // All requests after filter
const [loading, setLoading] = useState(false); // Waiting for data
const [searchTerm, setSearchTerm] = useState(""); // The word set in the filter
const [gotData, setGotData] = useState(false); // Received data
const [requestedEdition, setRequestedEdition] = useState(props.edition);
const [open, setOpen] = useState(false); // Collapsible is open
const [moreRequestsAvailable, setMoreRequestsAvailable] = useState(true); // Endpoint has more requests available
const [allRequestsFetched, setAllRequestsFetched] = useState(false);
Expand Down Expand Up @@ -52,13 +53,15 @@ export default function Requests(props: { edition: string; refreshCoaches: () =>
* Request the next page from the list of requests.
* The set searchterm will be used.
*/
async function getData() {
if (loading) {
async function getData(requested: number, reset: boolean) {
const filterChanged = requested === -1;
const requestedPage = requested === -1 ? 0 : page;

if (loading && !filterChanged) {
return;
}

if (allRequestsFetched) {
setGotData(true);
if (allRequestsFetched && !reset) {
setRequests(
allRequests.filter(request =>
request.user.name.toUpperCase().includes(searchTerm.toUpperCase())
Expand All @@ -77,67 +80,61 @@ export default function Requests(props: { edition: string; refreshCoaches: () =>
setController(newController);

const response = await toast.promise(
getRequests(props.edition, searchTerm, page, newController),
getRequests(props.edition, searchTerm, requestedPage, newController),
{
error: "Failed to retrieve requests",
}
);

if (response.requests.length === 0) {
setMoreRequestsAvailable(false);
}
if (page === 0) {
setRequests(response.requests);
} else {
setRequests(requests.concat(response.requests));
}

if (searchTerm === "") {
if (response.requests.length === 0) {
setAllRequestsFetched(true);
if (response !== null) {
if (response.requests.length === 0 && !filterChanged) {
setMoreRequestsAvailable(false);
}
if (page === 0) {
setAllRequests(response.requests);
if (requestedPage === 0 || filterChanged) {
setRequests(response.requests);
} else {
setAllRequests(allRequests.concat(response.requests));
setRequests(requests.concat(response.requests));
}
}

setPage(page + 1);
if (searchTerm === "") {
if (response.requests.length === 0 && !filterChanged) {
setAllRequestsFetched(true);
}
if (requestedPage === 0) {
setAllRequests(response.requests);
} else {
setAllRequests(allRequests.concat(response.requests));
}
}

setGotData(true);
setPage(requestedPage + 1);
} else {
setMoreRequestsAvailable(false);
}
setLoading(false);
}

/**
* update the requests when the edition changes
*/
useEffect(() => {
refreshRequests();
}, [props.edition]);

/**
* Reset the list of requests and get the first page.
* Used when the edition is changed.
*/
function refreshRequests() {
setRequests([]);
setPage(0);
setAllRequestsFetched(false);
setGotData(false);
setMoreRequestsAvailable(true);
}

function filter(searchTerm: string) {
setPage(0);
setGotData(false);
setMoreRequestsAvailable(true);
setSearchTerm(searchTerm);
setRequests([]);
}
if (props.edition !== requestedEdition) {
setRequests([]);
setPage(0);
setAllRequestsFetched(false);
setMoreRequestsAvailable(true);
getData(-1, true);
setRequestedEdition(props.edition);
} else {
setPage(0);
setMoreRequestsAvailable(true);
getData(-1, false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchTerm, props.edition]);

let list;
if (gotData && requests.length === 0) {
if (requests.length === 0) {
if (loading) {
list = <LoadSpinner show={true} />;
}
list = <div>No requests found</div>;
} else {
list = (
Expand All @@ -160,7 +157,8 @@ export default function Requests(props: { edition: string; refreshCoaches: () =>
<SearchFieldDiv>
<SearchBar
onChange={e => {
filter(e.target.value);
setPage(0);
setSearchTerm(e.target.value);
}}
value={searchTerm}
placeholder="Search name..."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@ export default function RequestList(props: {
requests: Request[];
removeRequest: (coachAdded: boolean, request: Request) => void;
moreRequestsAvailable: boolean;
getMoreRequests: () => void;
getMoreRequests: (page: number, reset: boolean) => void;
}) {
return (
<ListDiv>
<InfiniteScroll
pageStart={0}
loadMore={props.getMoreRequests}
loadMore={(page: number) => props.getMoreRequests(page, false)}
hasMore={props.moreRequestsAvailable}
loader={<LoadSpinner show={true} key="spinner" />}
useWindow={false}
Expand Down
29 changes: 19 additions & 10 deletions frontend/src/utils/api/mail_overview.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Email, Student } from "../../data/interfaces";
import { EmailType } from "../../data/enums";
import { axiosInstance } from "./api";
import axios from "axios";

/**
* A student together with its email history
Expand All @@ -26,17 +27,25 @@ export async function getMailOverview(
name: string,
filters: EmailType[],
controller: AbortController
): Promise<StudentEmails> {
const FormatFilters: string[] = filters.map(filter => {
return `&email_status=${Object.values(EmailType).indexOf(filter)}`;
});
const concatted: string = FormatFilters.join("");
): Promise<StudentEmails | null> {
try {
const FormatFilters: string[] = filters.map(filter => {
return `&email_status=${Object.values(EmailType).indexOf(filter)}`;
});
const concatted: string = FormatFilters.join("");

const response = await axiosInstance.get(
`/editions/${edition}/students/emails?page=${page}&name=${name}${concatted}`,
{ signal: controller.signal }
);
return response.data as StudentEmails;
const response = await axiosInstance.get(
`/editions/${edition}/students/emails?page=${page}&name=${name}${concatted}`,
{ signal: controller.signal }
);
return response.data as StudentEmails;
} catch (error) {
if (axios.isAxiosError(error) && error.code === "ERR_CANCELED") {
return null;
} else {
throw error;
}
}
}

/**
Expand Down
Loading

0 comments on commit a132908

Please sign in to comment.