From 32ef9c93bd6cce8d45541e09b4bd86e78537a2fa Mon Sep 17 00:00:00 2001 From: gerwoud Date: Tue, 21 May 2024 19:46:03 +0200 Subject: [PATCH] edit title and description functionality --- .../endpoints/projects/endpoint_parser.py | 4 +- frontend/public/locales/nl/translation.json | 244 +++++++++--------- .../pages/project/projectView/ProjectView.tsx | 105 +++++++- 3 files changed, 213 insertions(+), 140 deletions(-) diff --git a/backend/project/endpoints/projects/endpoint_parser.py b/backend/project/endpoints/projects/endpoint_parser.py index 8214c893..895658be 100644 --- a/backend/project/endpoints/projects/endpoint_parser.py +++ b/backend/project/endpoints/projects/endpoint_parser.py @@ -62,8 +62,8 @@ def parse_project_params(): ) result_dict[key] = new_deadlines elif "archived" == key: - result_dict[key] = True if value == "true" else False + result_dict[key] = value == "true" else: result_dict[key] = value - return result_dict \ No newline at end of file + return result_dict diff --git a/frontend/public/locales/nl/translation.json b/frontend/public/locales/nl/translation.json index d9de01c9..7715aecd 100644 --- a/frontend/public/locales/nl/translation.json +++ b/frontend/public/locales/nl/translation.json @@ -1,153 +1,145 @@ { "header": { - "myProjects": "My Projects", - "myCourses": "My Courses", - "projectCreate": "Create Project", - "login": "Login", + "myProjects": "Mijn Projecten", + "myCourses": "Mijn Vakken", + "login": "Aanmelden", "home": "Home", - "tag": "en", + "tag": "nl", "homepage": "Homepage", - "projectUploadForm": "Project upload form", - "logout": "Logout" + "projectUploadForm": "Project uploaden", + "logout": "Afmelden" }, "home": { "home": "Home", - "tag": "en", - "homepage": "Homepage", - "welcomeDescription": "Welcome to PeristerĂ³nas, the online submission platform of UGent", - "login": "Login" + "tag": "nl", + "homepage": "Homepagina", + "welcomeDescription": "Welkom bij PeristerĂ³nas, het online indieningsplatform van UGent", + "login": "Aanmelden" }, "courseDetailTeacher": { - "title": "Course Details", - "deleteCourse": "Delete Course", - "unauthorizedDelete": "You are unauthorized to delete this course", - "noCoursesFound": "No courses found", - "noProjects": "No projects", - "noStudents": "No students in this course", + "title": "Vak Details", + "noCoursesFound": "Geen vakken gevonden", + "unauthorizedDelete": "U heeft niet de juiste rechten om dit vak te verwijderen", + "noProjects": "Geen projecten", + "noStudents": "Geen studenten voor dit vak", + "deleteCourse": "Verwijder vak", "joinCodes": "Join Codes", - "forAdmins": "For Admins", - "forStudents": "For Students", - "noExpiryDate": "No expiry date", - "expiryDate": "Expiry Date", - "newJoinCode": "New Join Code", - "deleteSelected": "Delete Selected Students", - "projects": "Projects", - "newProject": "New Project", - "assistantList": "List of co-teachers/assistants", - "newTeacher": "New teacher", - "studentList": "List of students", - "newStudent": "New student(s)", + "forAdmins": "Voor Admins", + "forStudents": "Voor Studenten", + "noExpiryDate": "Geen vervaldatum", + "expiryDate": "Vervaldatum", + "newJoinCode": "Nieuwe Join Code", + "deleteSelected": "Verwijder geselecteerde studenten", + "projects": "Projecten", + "newProject": "Nieuw Project", + "assistantList": "Lijst co-leerkrachten/assistenten", + "newTeacher": "Nieuwe leerkracht", + "studentList": "Lijst studenten", + "newStudent": "Nieuwe student(en)", "deadline": "Deadline", - "teacher": "Teacher", - "view": "View", + "teacher": "Leerkracht", + "view": "Bekijk", "admins": "Admins", - "students": "Students" + "students": "Studenten" }, "allCoursesTeacher": { - "title": "All Courses", - "courseForm": "Course Form", - "courseName": "Course Name", - "submit": "Submit", - "emptyCourseNameError": "Course name should not be empty", - "cancel": "Cancel", - "create": "Create", - "activeCourses": "Active Courses", - "archivedCourses":"Archived Courses" + "title": "Alle Vakken", + "courseForm": "Vak Form", + "courseName": "Vak Naam", + "submit": "Opslaan", + "emptyCourseNameError": "Vak naam mag niet leeg zijn", + "cancel": "Annuleer", + "create": "Nieuw Vak", + "activeCourses": "Actieve Vakken", + "archivedCourses":"Gearchiveerde Vakken" }, "courseForm": { - "courseName": "Course Name", - "submit": "Submit", - "emptyCourseNameError": "Course name should not be empty" + "courseName": "Vak Naam", + "submit": "Opslaan", + "emptyCourseNameError": "Vak naam mag niet leeg zijn" + }, + "student": { + "myProjects": "Mijn Projecten", + "myCourses": "Mijn Vakken", + "deadlines": "Verlopen Deadlines", + "course": "Vak", + "last_submission": "Laatste indiening", + "SUCCESS": "Geslaagd", + "FAIL": "Gefaald", + "RUNNING": "Aan het lopen", + "LATE": "Te laat", + "deadlinesOnDay": "Deadlines op: ", + "noDeadline": "Geen deadlines op deze dag", + "no_submission_yet" : "Nog geen indiening", + "loading": "Laden...", + "no_projects": "Er zijn hier geen projecten, meld je aan voor een vak om projecten te zien" + + }, + "projectForm": { + "projectTitle": "Titel", + "projectDescription": "Beschrijving", + "projectCourse": "Vak", + "projectDeadline": "Project deadline", + "visibleForStudents": "Zichtbaar voor studenten", + "uploadFile": "Upload bestand", + "regex": "Voeg Regex toe", + "selectCourseText": "Selecteer een vak", + "testWarning": "Opgelet: Deze opgave bevat geen tests", + "helperText": "Selecteer een geldige deadline voor het project", + "uploadProject": "Upload project", + "regexStructure": "Regex structuur", + "uploadError": "Project is niet goed geformatteerd", + "noDeadlinesPlaceholder": "Nog geen opgegeven deadlines", + "noFilesPlaceholder": "Nog geen opgave bestanden geupload", + "noRegexPlaceholder": "Nog geen regex toegevoegd", + "unauthorized": "U heeft niet de juiste rechten om een project aan te maken voor dit vak", + "submissionError": "Er is een fout opgetreden bij het indienen van uw project, probeer het later opnieuw.", + "clearSelected": "Deselecteer keuze" }, "projectView": { - "submitNetworkError": "Failed to upload file, please try again.", - "selected": "Selected", - "submit": "Submit", - "previousSubmissions": "Previous Submissions", - "noFileSelected": "No file selected", + "submitNetworkError": "Er is iets mislopen bij het opslaan van uw indiening. Probeer het later opnieuw.", + "selected": "Geselecteerd", + "submit": "Indienen", + "previousSubmissions": "Vorige indieningen", + "noFileSelected": "Er is geen bestand geselecteerd", "submissionGrid": { - "late": "Late", - "fail": "Fail", - "success": "Success", - "running": "Running", - "submitTime": "Time submitted", + "late": "Te laat", + "fail": "Gefaald", + "success": "Succesvol", + "running": "Aan het uitvoeren", + "submitTime": "Indientijd", "status": "Status" }, - "projectOverview": "Overzicht" + "projectOverview": "Overzicht", + "archive": "Archiveer" }, "time": { - "yearsAgo": "years ago", - "monthsAgo": "months ago", - "daysAgo": "days ago", - "hoursAgo": "hours ago", - "minutesAgo": "minutes ago", - "justNow": "just now", - "yearsLater": "years later", - "monthsLater": "months later", - "daysLater": "days later", - "hoursLater": "hours later", - "minutesLater": "minutes later" - }, - "error": { - "pageNotFound": "Page Not Found", - "pageNotFoundMessage": "The requested page was not found.", - "forbidden": "Forbidden", - "forbiddenMessage": "You don't have access to this resource.", - "clientError": "Client Error", - "clientErrorMessage": "A client error has occured.", - "serverError": "Server Error", - "serverErrorMessage": "A server error has occured." - }, - "projectForm": { - "projectTitle": "Title", - "projectDescription": "Project description", - "projectCourse": "Course", - "projectDeadline": "Project deadline", - "visibleForStudents": "Visible for students", - "uploadFile": "Upload file", - "regex": "Add Regex", - "selectCourseText": "Select a course", - "testWarning": "Warning: This assignment doesn't contain tests", - "helperText": "Please fill in a valid deadline for the project", - "uploadProject": "Upload project", - "regexStructure": "Regex structure", - "uploadError": "Project isn't formatted appropriately", - "projectHeader": "Upload a project", - "deadline": "deadline", - "description": "Description", - "zipFile": "Zipfile", - "helperRegexText": "Regex can't be empty or already added", - "fileInfo": "The uploaded file must be a .zip file, if you want automatic tests you should include a Dockerfile or a run_tests.sh.\n For more info you should see", - "userDocs": "user guide", - "visibleForStudentsTooltip": "If this is checked the project will be visible to the students after upload", - "noDeadlinesPlaceholder": "No deadlines present yet", - "noFilesPlaceholder": "No assignment files given yet", - "noRegexPlaceholder": "No regex added yet", - "clearSelected": "Clear Selection", - "faultySubmission": "Some fields were left open or there is no valid runner/file combination", - "unauthorized": "You are unauthorized to upload a project for this course", - "submissionError": "Submission failed, please try again" - }, - "student" : { - "myProjects": "My Projects", - "myCourses": "My Courses", - "deadlines": "Past deadlines", - "last_submission" : "Last submission", - "course": "Course", - "SUCCESS": "Success", - "FAIL": "Fail", - "RUNNING": "Is running", - "LATE": "Late", - "deadlinesOnDay": "Deadlines on: ", - "noDeadline": "No deadlines on this day", - "no_submission_yet" : "No submission yet", - "loading": "Loading...", - "no_projects": "There are no projects here, sign up for a course to see projects" + "yearsAgo": "jaren geleden", + "monthsAgo": "maanden geleden", + "daysAgo": "dagen geleden", + "hoursAgo": "uur geleden", + "minutesAgo": "minuten geleden", + "justNow": "Zonet", + "yearsLater": "jaren later", + "monthsLater": "maanden later", + "daysLater": "dagen later", + "hoursLater": "uur later", + "minutesLater": "minuten later" }, "projectsOverview": { - "past_deadline": "Past Projects", - "future_deadline": "Upcoming Deadlines", - "no_projects": "There are no projects here.", - "new_project": "New Project" + "past_deadline": "Verlopen Projecten", + "future_deadline": "Opkomende Deadlines", + "no_projects": "Er zijn hier geen projecten.", + "new_project": "Nieuw Project" + }, + "error": { + "pageNotFound": "Pagina Niet Gevonden", + "pageNotFoundMessage": "De opgevraagde pagina werd niet gevonden.", + "forbidden": "Verboden", + "forbiddenMessage": "Je hebt geen toegang tot deze bron.", + "clientError": "Client Fout", + "clientErrorMessage": "Er is een client fout opgetreden.", + "serverError": "Server Fout", + "serverErrorMessage": "Er is een server fout opgetreden." } } \ No newline at end of file diff --git a/frontend/src/pages/project/projectView/ProjectView.tsx b/frontend/src/pages/project/projectView/ProjectView.tsx index a5dd7bfb..79497ca5 100644 --- a/frontend/src/pages/project/projectView/ProjectView.tsx +++ b/frontend/src/pages/project/projectView/ProjectView.tsx @@ -6,8 +6,9 @@ import { CardHeader, Container, Fade, - Grid, + Grid, IconButton, Stack, + TextField, Typography, } from "@mui/material"; import { useEffect, useState } from "react"; @@ -23,6 +24,9 @@ import {Me} from "../../../types/me.ts"; import {fetchMe} from "../../../utils/fetches/FetchMe.ts"; import DeadlineGrid from "../../../components/DeadlineView/DeadlineGrid.tsx"; import {Deadline} from "../../../types/deadline.ts"; +import EditIcon from '@mui/icons-material/Edit'; +import CheckIcon from '@mui/icons-material/Check'; +import CloseIcon from '@mui/icons-material/Close'; const API_URL = import.meta.env.VITE_APP_API_HOST; @@ -51,6 +55,9 @@ export default function ProjectView() { const [assignmentRawText, setAssignmentRawText] = useState(""); const [deadlines, setDeadlines] = useState([]); const [alertVisibility, setAlertVisibility] = useState(false) + const [edit, setEdit] = useState(false); + let [title, setTitle] = useState(""); + const [description, setDescription] = useState(""); const navigate = useNavigate() const deleteProject = () => { @@ -60,12 +67,43 @@ export default function ProjectView() { navigate('/projects'); } + const patchTitleAndDescription = async () => { + setEdit(false); + const formData = new FormData(); + formData.append('title', title); + formData.append('description', description); + + const response = await authenticatedFetch(`${API_URL}/projects/${projectId}`, { + method: "PATCH", + body: formData + }); + + // Check if the response is ok (status code 2xx) + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + updateProject(); + } + + const discardEditTitle = () => { + const title = projectData?.title; + setEdit(false); + if (title) + setTitle(title); + + if (projectData?.description) + setDescription(projectData?.description); + } + const updateProject = async () => { authenticatedFetch(`${API_URL}/projects/${projectId}`).then((response) => { if (response.ok) { response.json().then((data) => { const projectData = data["data"]; setProjectData(projectData); + setTitle(projectData.title); + setDescription(projectData.description); const transformedDeadlines = projectData.deadlines.map((deadlineArray: string[]): Deadline => ({ description: deadlineArray[0], @@ -75,7 +113,7 @@ export default function ProjectView() { setDeadlines(transformedDeadlines); authenticatedFetch( - `${API_URL}/courses/${projectData.course_id}` + `${API_URL}/courses/${projectData.course_id}` ).then((response) => { if (response.ok) { response.json().then((data) => { @@ -89,7 +127,7 @@ export default function ProjectView() { } const archiveProject = async () => { - const newArchived = !projectData.archived; + const newArchived = !projectData?.archived; const formData = new FormData(); formData.append('archived', newArchived.toString()); @@ -134,23 +172,66 @@ export default function ProjectView() { <CardHeader color="secondary" - title={projectData.title} + title={ + <Box + display="flex" + justifyContent="space-between" + alignItems="center" + > + { + !edit && <>{projectData.title}</> + } + { + edit && <><TextField id="edit-title" label="title" variant="outlined" size="small" defaultValue={title} onChange={(event) => setTitle(event.target.value)}/></> + } + {courseData && ( + <Button variant="outlined" type="link" href={`/${i18next.resolvedLanguage}/courses/${courseData.course_id}`}> + {courseData.name} + </Button> + )} + </Box> + } subheader={ - <> + <Box position="relative" height="100%" sx={{marginTop: "10px"}}> <Stack direction="row" spacing={2}> - <Typography>{projectData.description}</Typography> + { + !edit && <><Typography>{projectData.description}</Typography></> + } + { + edit && <><TextField id="edit-description" label="description" variant="outlined" size="small" defaultValue={description} onChange={(event) => setDescription(event.target.value)}/></> + } <Typography flex="1" /> - {courseData && ( - <Button variant="outlined" type="link" href={`/${i18next.resolvedLanguage}/courses/${courseData.course_id}`}> - {courseData.name} - </Button> - )} </Stack> - </> + </Box> } /> <CardContent> <Markdown>{assignmentRawText}</Markdown> + <Box + display="flex" + alignItems="flex-end" + justifyContent="end" + > + { + edit && ( + <> + <IconButton onClick={patchTitleAndDescription}> + <CheckIcon /> + </IconButton> + <IconButton onClick={discardEditTitle}> + <CloseIcon /> + </IconButton> + </> + ) + } + { + !edit && ( + <IconButton onClick={() => setEdit(true)}> + <EditIcon /> + </IconButton> + ) + } + </Box> </CardContent> </Card> )}