From 0dae859d864d6b204513d1d9924741f5bd58b1ec Mon Sep 17 00:00:00 2001 From: rdyselinck Date: Sun, 19 May 2024 21:38:15 +0200 Subject: [PATCH 1/8] Improved status on required files --- .../app/[locale]/components/StatusButton.tsx | 57 +++++++++++++++++++ .../components/general/RequiredFilesList.tsx | 40 ++++++++----- .../project_components/requiredFiles.tsx | 10 +++- .../[project_id]/edit/projectEditForm.tsx | 16 ++++-- 4 files changed, 103 insertions(+), 20 deletions(-) create mode 100644 frontend/app/[locale]/components/StatusButton.tsx diff --git a/frontend/app/[locale]/components/StatusButton.tsx b/frontend/app/[locale]/components/StatusButton.tsx new file mode 100644 index 00000000..c809017c --- /dev/null +++ b/frontend/app/[locale]/components/StatusButton.tsx @@ -0,0 +1,57 @@ +import {ClearIcon } from '@mui/x-date-pickers/icons'; +import React, { useState } from 'react'; +import CheckIcon from "@mui/icons-material/Check"; +import {Button, Typography} from "@mui/material"; +import HelpOutlineIcon from "@mui/icons-material/HelpOutline"; + +interface StatusButtonProps { + files: any[], + setFiles: (value: (((prevState: any[]) => any[]) | any[])) => void, + fileIndex: number, +} + +function StatusButton( + {files, setFiles, fileIndex}: StatusButtonProps, +) { + const [statusIndex, setStatusIndex] = useState(getStart(files[fileIndex])); + const statuses = [ + { icon: }, + { icon: }, + { icon: }, + ]; + const status_valeus = ['+', '~', '-']; + + const handleClick = () => { + const newStatusIndex = (statusIndex + 1) % statuses.length; + setStatusIndex(newStatusIndex); + const newFiles = [...files]; + newFiles[fileIndex] = status_valeus[newStatusIndex]; + setFiles(newFiles); + }; + + return ( + + ); +} + +function getStart(file: string) { + if (file[0] === '+') { + return 0; + } else if (file[0] === '~') { + return 1; + } else { + return 2; + } +} + +export default StatusButton; \ No newline at end of file diff --git a/frontend/app/[locale]/components/general/RequiredFilesList.tsx b/frontend/app/[locale]/components/general/RequiredFilesList.tsx index e2564ed1..8739bb8d 100644 --- a/frontend/app/[locale]/components/general/RequiredFilesList.tsx +++ b/frontend/app/[locale]/components/general/RequiredFilesList.tsx @@ -1,19 +1,30 @@ "use client" -import {IconButton, List, ListItem, ListItemText, TextField, Typography, Button} from "@mui/material"; +import {Button, IconButton, List, ListItem, ListItemText, TextField, Typography} from "@mui/material"; import DeleteIcon from "@mui/icons-material/Delete"; import React, {useState} from "react"; import Box from "@mui/material/Box"; +import StatusButton from "@app/[locale]/components/StatusButton"; interface ItemsListProps { items: string[], setItems: (value: (((prevState: any[]) => any[]) | any[])) => void, input_placeholder: string, - empty_list_placeholder:string, - button_text: string + empty_list_placeholder: string, + button_text: string, + items_status: string[], + setItemsStatus: (value: (((prevState: any[]) => any[]) | any[])) => void, } -const ItemsList = ({items, setItems, input_placeholder, empty_list_placeholder, button_text}: ItemsListProps) => { +const ItemsList = ({ + items, + setItems, + input_placeholder, + empty_list_placeholder, + button_text, + items_status, + setItemsStatus + }: ItemsListProps) => { const [newItem, setNewItem] = useState('') const [noInput, setNoInput] = useState(false) @@ -30,7 +41,6 @@ const ItemsList = ({items, setItems, input_placeholder, empty_list_placeholder, setItems(newItems); setNewItem(''); setNoInput(false); - console.log(items); } else { setNoInput(true); } @@ -39,7 +49,8 @@ const ItemsList = ({items, setItems, input_placeholder, empty_list_placeholder, return ( {items.length === 0 ? ( - {empty_list_placeholder} + {empty_list_placeholder} ) : ( handleDelete(index)} - > - - +
+ + handleDelete(index)} + > + + +
} > any[]) | any[])) => void, + file_status: any[], + setFileStatus: (value: (((prevState: any[]) => any[]) | any[])) => void, } function RequiredFiles( - {files, setFiles}: RequiredFilesProps + {files, setFiles, file_status, setFileStatus}: RequiredFilesProps ) { const {t} = useTranslation(); @@ -35,12 +37,14 @@ function RequiredFiles( - ; diff --git a/frontend/app/[locale]/project/[project_id]/edit/projectEditForm.tsx b/frontend/app/[locale]/project/[project_id]/edit/projectEditForm.tsx index 79cd926a..72d3497f 100644 --- a/frontend/app/[locale]/project/[project_id]/edit/projectEditForm.tsx +++ b/frontend/app/[locale]/project/[project_id]/edit/projectEditForm.tsx @@ -33,6 +33,7 @@ interface ProjectEditFormProps { function ProjectEditForm({project_id, add_course_id}: ProjectEditFormProps) { const [files, setFiles] = useState([]); + const [status_files, setStatusFiles] = useState([]); const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); const [groupAmount, setGroupAmount] = useState(1); @@ -70,8 +71,10 @@ function ProjectEditForm({project_id, add_course_id}: ProjectEditFormProps) { setDescription(project.description) if (project.file_structure !== null) { const file_structure = project.file_structure.split(",").map((item: string) => item.trim().replace(/"/g, '')); - setFiles(file_structure); - console.log(files); + const file_structure_status = file_structure.map((item: string) => item[0]); + const file_structure_name = file_structure.map((item: string) => item.substring(1)); + setFiles(file_structure_name); + setStatusFiles(file_structure_status); } setGroupSize(project["group_size"]) setTitle(project["name"]) @@ -152,13 +155,15 @@ function ProjectEditForm({project_id, add_course_id}: ProjectEditFormProps) { const zipFileBlob = await zip.generateAsync({type: "blob"}); const formData = new FormData(); const zipFile = new File([zipFileBlob], "test_files.zip"); + + const required_files = files.map((item, index) => status_files[index] + item); formData.append("test_files", zipFile); formData.append("name", title); formData.append("description", description); formData.append("max_score", score.toString()); formData.append("number_of_groups", groupAmount.toString()); formData.append("group_size", groupSize.toString()); - formData.append("file_structure", files.join(",")); + formData.append("file_structure", required_files.join(",")); formData.append("conditions", conditions.join(",")); formData.append("visible", visible.toString()); if (add_course_id < 0) { @@ -233,7 +238,10 @@ function ProjectEditForm({project_id, add_course_id}: ProjectEditFormProps) { description={description}/> + setFiles={setFiles} + file_status={status_files} + setFileStatus={setStatusFiles} + /> From 4f0ada0ca3e88389d4955d144c078ad8b96f2884 Mon Sep 17 00:00:00 2001 From: rdyselinck Date: Sun, 19 May 2024 21:44:49 +0200 Subject: [PATCH 2/8] Fix tests --- frontend/__test__/project/edit/Requiredfiles.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/__test__/project/edit/Requiredfiles.test.tsx b/frontend/__test__/project/edit/Requiredfiles.test.tsx index 8565ce27..8ba02667 100644 --- a/frontend/__test__/project/edit/Requiredfiles.test.tsx +++ b/frontend/__test__/project/edit/Requiredfiles.test.tsx @@ -15,7 +15,7 @@ describe('Requiredfiles', () => { files={["First", "Second"]} setFiles={jest.fn()} translations={translations.en} - /> + file_status={["+", "-"]} setFileStatus={jest.fn()}/> ); // check that the required files were rendered properly From 423656f477a86ef3b4ee718d309f8d5ca918ca5c Mon Sep 17 00:00:00 2001 From: rdyselinck Date: Sun, 19 May 2024 22:03:39 +0200 Subject: [PATCH 3/8] Fix locale on project/id/groups --- .../[locale]/project/[project_id]/edit/projectEditForm.tsx | 2 +- frontend/locales/en/common.json | 4 +++- frontend/locales/nl/common.json | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/app/[locale]/project/[project_id]/edit/projectEditForm.tsx b/frontend/app/[locale]/project/[project_id]/edit/projectEditForm.tsx index 72d3497f..3efaa934 100644 --- a/frontend/app/[locale]/project/[project_id]/edit/projectEditForm.tsx +++ b/frontend/app/[locale]/project/[project_id]/edit/projectEditForm.tsx @@ -118,7 +118,7 @@ function ProjectEditForm({project_id, add_course_id}: ProjectEditFormProps) { async function setTestFiles(project: Project) { const zip = new JSZip(); - console.log(project.test_files) + const test_files_zip = await getTestFiles(project.test_files); const zipData = await zip.loadAsync(test_files_zip); const testfiles_name: string[] = []; diff --git a/frontend/locales/en/common.json b/frontend/locales/en/common.json index a556edd0..edd2f80f 100644 --- a/frontend/locales/en/common.json +++ b/frontend/locales/en/common.json @@ -135,5 +135,7 @@ "group_number": "Group number", "submission_date": "Submission date", "status": "Status", - "join/leave": "Join/Leave" + "join/leave": "Join/Leave", + "group_nr": "Group nr", + "join_leave": "Join/Leave" } \ No newline at end of file diff --git a/frontend/locales/nl/common.json b/frontend/locales/nl/common.json index 5bee265d..0768cbfd 100644 --- a/frontend/locales/nl/common.json +++ b/frontend/locales/nl/common.json @@ -138,5 +138,7 @@ "group_number": "Groep nummer", "submission_date": "Inleverdatum", "status": "Status", - "join/leave": "Toetreden/Verlaten" + "join/leave": "Toetreden/Verlaten", + "group_nr": "Groep nr", + "join_leave": "Toetreden/Verlaten" } \ No newline at end of file From 27425ee4a8e0b344e2499101c392f141633ceaab Mon Sep 17 00:00:00 2001 From: rdyselinck Date: Sun, 19 May 2024 23:45:22 +0200 Subject: [PATCH 4/8] Join a group before uploading check --- .../components/ProjectDetailsPage.tsx | 28 ++++++++++++------- frontend/lib/api.ts | 5 ++++ frontend/locales/en/common.json | 3 +- frontend/locales/nl/common.json | 3 +- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/frontend/app/[locale]/components/ProjectDetailsPage.tsx b/frontend/app/[locale]/components/ProjectDetailsPage.tsx index cbd9aba1..5b132880 100644 --- a/frontend/app/[locale]/components/ProjectDetailsPage.tsx +++ b/frontend/app/[locale]/components/ProjectDetailsPage.tsx @@ -1,6 +1,6 @@ 'use client' import React, { useEffect, useState } from "react"; -import { getProject, getUserData, Project, UserData } from "@lib/api"; +import {checkGroup, getGroup, getProject, getUserData, Project, UserData} from "@lib/api"; import { useTranslation } from "react-i18next"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; @@ -36,6 +36,7 @@ const ProjectDetailsPage: React.FC = ({ const [loadingProject, setLoadingProject] = useState(true); const [user, setUser] = useState(null); const [isExpanded, setIsExpanded] = useState(false); + const [isInGroup, setIsInGroup] = useState(false); const previewLength = 300; const deadlineColorType = project?.deadline ? checkDeadline(project.deadline) @@ -66,6 +67,7 @@ const ProjectDetailsPage: React.FC = ({ }; fetchProject().then(() => setLoadingProject(false)); + checkGroup(project_id).then((response) => setIsInGroup(response)); }, [project_id]); if (loadingProject) { @@ -197,15 +199,21 @@ const ProjectDetailsPage: React.FC = ({ {user?.role === 3 ? ( - + isInGroup ? ( + + ) : ( + + {t("not_in_group")} + + ) ) : null} diff --git a/frontend/lib/api.ts b/frontend/lib/api.ts index 92e61922..fe2f4c18 100644 --- a/frontend/lib/api.ts +++ b/frontend/lib/api.ts @@ -418,6 +418,11 @@ export async function getGroup(id: number): Promise { return (await getRequest(`/groups/${id}`)); } +export async function checkGroup(id: number) { + let response = await axios.get(backend_url + "/projects/" + id + "/get_group/", {withCredentials: true}); + return response.status !== 404; +} + export async function getGroups(): Promise { return (await getListRequest('/groups')); } diff --git a/frontend/locales/en/common.json b/frontend/locales/en/common.json index edd2f80f..4730716c 100644 --- a/frontend/locales/en/common.json +++ b/frontend/locales/en/common.json @@ -137,5 +137,6 @@ "status": "Status", "join/leave": "Join/Leave", "group_nr": "Group nr", - "join_leave": "Join/Leave" + "join_leave": "Join/Leave", + "not_in_group": "Join a group to submit" } \ No newline at end of file diff --git a/frontend/locales/nl/common.json b/frontend/locales/nl/common.json index 0768cbfd..16a66b81 100644 --- a/frontend/locales/nl/common.json +++ b/frontend/locales/nl/common.json @@ -140,5 +140,6 @@ "status": "Status", "join/leave": "Toetreden/Verlaten", "group_nr": "Groep nr", - "join_leave": "Toetreden/Verlaten" + "join_leave": "Toetreden/Verlaten", + "not_in_group": "Je kan niet indienen zonder in een groep te zitten" } \ No newline at end of file From 50d671b5b23a06d2b2ba5bcda39525b77a654334 Mon Sep 17 00:00:00 2001 From: rdyselinck Date: Mon, 20 May 2024 00:05:44 +0200 Subject: [PATCH 5/8] Submit button disabled when submitting empty file --- frontend/app/[locale]/components/SubmitDetailsPage.tsx | 10 +++++++++- scripts/opdracht_bestand_leeg/leeg | 0 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 scripts/opdracht_bestand_leeg/leeg diff --git a/frontend/app/[locale]/components/SubmitDetailsPage.tsx b/frontend/app/[locale]/components/SubmitDetailsPage.tsx index fc3fdb1b..f8ffab64 100644 --- a/frontend/app/[locale]/components/SubmitDetailsPage.tsx +++ b/frontend/app/[locale]/components/SubmitDetailsPage.tsx @@ -46,6 +46,7 @@ const SubmitDetailsPage: React.FC = ({ const [submitted, setSubmitted] = useState('no'); const [loadingProject, setLoadingProject] = useState(true); const [isExpanded, setIsExpanded] = useState(false); + const [disabled, setDisabled] = useState(true); const previewLength = 300; const toggleDescription = () => { @@ -89,6 +90,7 @@ const SubmitDetailsPage: React.FC = ({ result = [...filepaths, ...newpaths]; } setPaths(result); + setDisabled(newpaths.length === 0) } if (loadingProject) { @@ -187,7 +189,13 @@ const SubmitDetailsPage: React.FC = ({
)} {submitted !== 'ok' && ( - )} diff --git a/scripts/opdracht_bestand_leeg/leeg b/scripts/opdracht_bestand_leeg/leeg new file mode 100644 index 00000000..e69de29b From 3ebadabd8be64fac55b31e69947eb8650af22f0d Mon Sep 17 00:00:00 2001 From: rdyselinck Date: Mon, 20 May 2024 00:18:24 +0200 Subject: [PATCH 6/8] only admins can see site-users --- frontend/app/[locale]/components/CourseControls.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/frontend/app/[locale]/components/CourseControls.tsx b/frontend/app/[locale]/components/CourseControls.tsx index cd86fe99..a7d097a9 100644 --- a/frontend/app/[locale]/components/CourseControls.tsx +++ b/frontend/app/[locale]/components/CourseControls.tsx @@ -73,11 +73,14 @@ const CourseControls = ({selectedYear, onYearChange}) => { {t("view_archive")} - - - + {user?.role === 1 ? ( + + + + ) : null + }