-
-
-
-
-
-
-
- {project?.name}
-
-
- {user?.role !== 3 && (
-
-
-
- )}
-
-
-
-
-
-
- {t("assignment")}
-
-
- {project?.description && project?.description.length > previewLength && !isExpanded
- ? `${project?.description.substring(0, previewLength)}...`
- : project?.description
- }
-
- {project?.description && project?.description.length > previewLength && (
-
- {isExpanded ? : }
-
- )}
-
- {t("required_files")}
-
-
- {project?.file_structure}
-
-
- {t("conditions")}
-
-
- {project?.conditions}
-
-
- Max score:
- {project?.max_score}
-
-
- Number of groups:
- {project?.number_of_groups}
-
-
- Group size:
- {project?.group_size}
-
- {user?.role !== 3 && (
-
- }
- >
- {t("test_files")}
-
-
- )}
-
-
-
- {user?.role === 3 ? (
-
-
-
- {t("submissions")}
-
-
-
- {project?.deadline ? formatDate(project.deadline) : "No Deadline"}
-
-
-
-
-
- ) : (
-
-
-
- {t("submissions")}
-
-
-
- {project?.deadline ? formatDate(project.deadline) : "No Deadline"}
-
-
-
-
- )}
+ fetchProject().then(() => setLoadingProject(false));
+ }, [project_id]);
+
+ if (loadingProject) {
+ return
;
+ }
+
+ const toggleDescription = () => {
+ setIsExpanded(!isExpanded);
+ };
+
+ function formatDate(isoString: string): string {
+ const options: Intl.DateTimeFormatOptions = {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ hour: "2-digit",
+ minute: "2-digit",
+ hour12: false,
+ };
+ const date = new Date(isoString);
+ return date.toLocaleString(locale, options);
+ }
+
+ function checkDeadline(deadline) {
+ const now = new Date();
+ const deadlineDate = new Date(deadline);
+ return now < deadlineDate ? "success" : "failure";
+ }
+
+ return (
+
+
+
+
+ }
+ href={`/${locale}/course/${project?.course_id}`}
+ >
+ {t("return_course")}
+
+
+
+ {project?.name}
+
+ {user?.role !== 3 && (
+
+ }
+ href={`/${locale}/project/${project_id}/edit`}
+ sx={{ fontSize: "0.75rem", py: 1 }}
+ >
+ {t("edit_project")}
+
+
+ )}
+
+ }
+ href={`/${locale}/project/${project_id}/groups`}
+ sx={{ fontSize: "0.75rem", py: 1 }}
+ >
+ {t("groups")}
+
+
+
+
+ {t("assignment")}
+
+ {project?.description &&
+ project?.description.length > previewLength &&
+ !isExpanded
+ ? `${project?.description.substring(0, previewLength)}...`
+ : project?.description}
+
+ {project?.description && project?.description.length > previewLength && (
+
+ {isExpanded ? : }
+
+ )}
+ {t("required_files")}
+ {project?.file_structure}
+ {t("conditions")}
+ {project?.conditions}
+
+ {t("max_score")}:
+ {project?.max_score}
+
+
+ {t("number_of_groups")}:
+ {project?.number_of_groups}
+
+
+ {t("group_size")}:
+ {project?.group_size}
+
+ {user?.role !== 3 && (
+ }
+ sx={{ my: 1 }}
+ >
+ {t("test_files")}
+
+ )}
+
+
+ {t("submissions")}
+
+
+
+ {project?.deadline ? formatDate(project.deadline) : "No Deadline"}
+
-
- )
-}
+ {user?.role === 3 ? (
+
}
+ href={`/${locale}/project/${project_id}/submit`}
+ sx={{ my: 1 }}
+ >
+ {t("add_submission")}
+
+ ) : null}
+
+
+ {user?.role === 3 ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ );
+};
export default ProjectDetailsPage;
diff --git a/frontend/app/[locale]/components/SubmissionDetailsPage.tsx b/frontend/app/[locale]/components/SubmissionDetailsPage.tsx
index 7fdd08ab..a8b1a1d1 100644
--- a/frontend/app/[locale]/components/SubmissionDetailsPage.tsx
+++ b/frontend/app/[locale]/components/SubmissionDetailsPage.tsx
@@ -12,7 +12,6 @@ import DownloadIcon from "@mui/icons-material/CloudDownload";
const backend_url = process.env['NEXT_PUBLIC_BACKEND_URL'];
-
interface ProjectDetailsPageProps {
locale: any,
submission_id: number;
@@ -66,23 +65,23 @@ const ProjectDetailsPage: React.FC
= ({ locale, submiss
return (
-
-
-
+
+
+
-
-
+
+
-
+
{`${t("submission")} #${submission?.submission_nr}`}
-
-
-
-
+
+
+
+
{`${t("evaluation")} status`}
-
+
{submission?.output_test !== "" ? (
) : (
@@ -98,8 +97,8 @@ const ProjectDetailsPage: React.FC
= ({ locale, submiss
-
-
+
+
{t("uploaded_files")}
diff --git a/frontend/app/[locale]/components/SubmitDetailsPage.tsx b/frontend/app/[locale]/components/SubmitDetailsPage.tsx
index e6dc178e..fc3fdb1b 100644
--- a/frontend/app/[locale]/components/SubmitDetailsPage.tsx
+++ b/frontend/app/[locale]/components/SubmitDetailsPage.tsx
@@ -1,245 +1,203 @@
-"use client"
-
-import React, {useEffect, useState} from 'react';
-import {useTranslation} from "react-i18next";
+'use client';
+import React, { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import {
+ Box,
+ Button,
+ Card,
+ CardContent,
+ Divider,
+ Grid,
+ IconButton,
+ Input,
+ LinearProgress,
+ ThemeProvider,
+ Typography,
+} from '@mui/material';
import {
- Box,
- Button,
- Card,
- CardContent,
- Divider,
- Grid,
- IconButton,
- Input,
- LinearProgress,
- ThemeProvider,
- Typography
-} from "@mui/material";
-import {getProject, Project, uploadSubmissionFile} from '@lib/api';
-import baseTheme from "@styles/theme";
-import ProjectReturnButton from "@app/[locale]/components/ProjectReturnButton";
+ getProject,
+ Project,
+ uploadSubmissionFile,
+} from '@lib/api';
+import baseTheme from '@styles/theme';
+import ProjectReturnButton from '@app/[locale]/components/ProjectReturnButton';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import PublishIcon from '@mui/icons-material/Publish';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import ErrorIcon from '@mui/icons-material/Error';
-import Tree from "@app/[locale]/components/Tree";
+import Tree from '@app/[locale]/components/Tree';
interface SubmitDetailsPageProps {
- locale: any;
- project_id: number;
+ locale: any;
+ project_id: number;
}
-const SubmitDetailsPage: React.FC = ({locale, project_id}) => {
- const {t} = useTranslation();
-
- const [projectData, setProjectData] = useState()
- const [paths, setPaths] = useState([]);
- const [filepaths, setFilePaths] = useState([]);
- const [folderpaths, setFolderPaths] = useState([]);
- const [submitted, setSubmitted] = useState("no");
- const [loadingProject, setLoadingProject] = useState(true);
- const [isExpanded, setIsExpanded] = useState(false);
- const previewLength = 300;
-
-
- const toggleDescription = () => {
- setIsExpanded(!isExpanded);
- }
-
- const handleSubmit = async (e) => {
- setSubmitted(await uploadSubmissionFile(e, project_id));
- }
-
- useEffect(() => {
- const fetchProject = async () => {
- try {
- const project: Project = await getProject(+project_id);
- setProjectData(project)
- } catch (e) {
- console.error(e)
- }
- }
- fetchProject().then(() => setLoadingProject(false));
- }, [project_id]);
-
- function folderAdded(event: any) {
- console.log(event.target.id)
- let newpaths: string[] = []
- let result: string[] = []
- if(event.target.id === "filepicker2"){
- for(const file of event.target.files){
- newpaths.push(file.name)
- }
- setFilePaths(newpaths);
- result = [...folderpaths, ...newpaths]
- }else{
- for (const file of event.target.files) {
- let text: string = file.webkitRelativePath;
- if (text.includes("/")) {
- text = text.substring((text.indexOf("/") ?? 0) + 1, text.length);
- }
- newpaths.push(text);
- }
- setFolderPaths(newpaths);
- result = [...filepaths, ...newpaths]
+const SubmitDetailsPage: React.FC = ({
+ locale,
+ project_id,
+}) => {
+ const { t } = useTranslation();
+
+ const [projectData, setProjectData] = useState();
+ const [paths, setPaths] = useState([]);
+ const [filepaths, setFilePaths] = useState([]);
+ const [folderpaths, setFolderPaths] = useState([]);
+ const [submitted, setSubmitted] = useState('no');
+ const [loadingProject, setLoadingProject] = useState(true);
+ const [isExpanded, setIsExpanded] = useState(false);
+ const previewLength = 300;
+
+ const toggleDescription = () => {
+ setIsExpanded(!isExpanded);
+ };
+
+ const handleSubmit = async (e) => {
+ setSubmitted(await uploadSubmissionFile(e, project_id));
+ };
+
+ useEffect(() => {
+ const fetchProject = async () => {
+ try {
+ const project: Project = await getProject(+project_id);
+ setProjectData(project);
+ } catch (e) {
+ console.error(e);
+ }
+ };
+ fetchProject().then(() => setLoadingProject(false));
+ }, [project_id]);
+
+ function folderAdded(event: any) {
+ let newpaths: string[] = [];
+ let result: string[] = [];
+ if (event.target.id === 'filepicker2') {
+ for (const file of event.target.files) {
+ newpaths.push(file.name);
+ }
+ setFilePaths(newpaths);
+ result = [...folderpaths, ...newpaths];
+ } else {
+ for (const file of event.target.files) {
+ let text: string = file.webkitRelativePath;
+ if (text.includes('/')) {
+ text = text.substring((text.indexOf('/') ?? 0) + 1, text.length);
}
- console.log(result)
- setPaths(result)
- }
-
- if (loadingProject) {
- return ;
+ newpaths.push(text);
+ }
+ setFolderPaths(newpaths);
+ result = [...filepaths, ...newpaths];
}
-
- return (
-
-
-
-
-
-
-
-
-
- {projectData?.name}
-
-
-
- {projectData?.description && projectData?.description.length > previewLength && !isExpanded
- ? `${projectData?.description.substring(0, previewLength)}...`
- : projectData?.description
- }
-
- {projectData?.description && projectData?.description.length > previewLength && (
-
- {isExpanded ? : }
-
- )}
-
- {t('upload_folders')}
-
-
-
-
-
-
-
-
- {t('files')}
-
-
-
-
-
-
-
-
-
- {submitted['result'] === 'ok' && (
-
-
-
- {t('submitted')}
-
-
- )}
- {submitted['result'] === 'error' && (
-
-
-
- {t('submission_error')}: {t(submitted['errorcode'])}
-
-
- )}
- {submitted['result'] !== 'ok' && (
-
- )}
-
-
-
-
-
-
- );
-}
+ setPaths(result);
+ }
+
+ if (loadingProject) {
+ return ;
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+ {projectData?.name}
+
+
+
+ {projectData?.description && projectData?.description.length > previewLength && !isExpanded
+ ? `${projectData?.description.substring(0, previewLength)}...`
+ : projectData?.description}
+
+ {projectData?.description && projectData?.description.length > previewLength && (
+
+ {isExpanded ? : }
+
+ )}
+
+ {t('upload_folders')}
+
+
+
+
+
+
+ {t('files')}
+
+
+
+
+
+
+ {submitted === 'ok' && (
+
+
+
+
+ {t('submitted')}
+
+
+ )}
+ {submitted === 'error' && (
+
+
+
+ {t('submission_error')}: {t(submitted['errorcode'])}
+
+
+ )}
+ {submitted !== 'ok' && (
+ } type="submit">
+ {t('submit')}
+
+ )}
+
+
+
+
+
+
+ );
+};
export default SubmitDetailsPage;
diff --git a/frontend/app/[locale]/course/[course_id]/page.tsx b/frontend/app/[locale]/course/[course_id]/page.tsx
index 112a31fe..e32f4230 100644
--- a/frontend/app/[locale]/course/[course_id]/page.tsx
+++ b/frontend/app/[locale]/course/[course_id]/page.tsx
@@ -1,6 +1,6 @@
import initTranslations from "@app/i18n";
import TranslationsProvider from "@app/[locale]/components/TranslationsProvider";
-import {Box, Typography} from "@mui/material";
+import { Box, Typography, Grid } from "@mui/material";
import NavBar from "@app/[locale]/components/NavBar";
import CourseBanner from "@app/[locale]/components/CourseBanner";
import CourseDetails from "@app/[locale]/components/CourseDetails";
@@ -30,37 +30,38 @@ export default async function Course({params: {locale, course_id}, searchParams:
locale={locale}
namespaces={i18nNamespaces}
>
-
-
+
+
-
-
+
+
+
+
+
+ {t('projects')}
+
+
+
+
+
+
+
+
-
- {t('projects')}
-
-
-
-
-
-
+
-
)
-}
\ No newline at end of file
+}
diff --git a/frontend/app/[locale]/project/[project_id]/edit/project_styles.css b/frontend/app/[locale]/project/[project_id]/edit/project_styles.css
index f33b4c54..c6f08ad4 100644
--- a/frontend/app/[locale]/project/[project_id]/edit/project_styles.css
+++ b/frontend/app/[locale]/project/[project_id]/edit/project_styles.css
@@ -1,75 +1,109 @@
-.typographyStyle, .MuiTypography-h5.typographyStyle {
- font-weight: bold !important;
- margin: 5px 0 0 0 !important;
-}
-
-.pageBoxLeft {
- margin-top: 20px !important;
- padding: 50px 50px 50px 100px !important;
+/* Existing styles */
+.typographyStyle,
+.MuiTypography-h5.typographyStyle {
+ font-weight: bold!important;
+ margin: 5px 0 0 0!important;
}
+.pageBoxLeft,
.pageBoxRight {
- margin-top: 64px !important;
- padding: 50px 50px 50px 100px !important;
+ margin-top: 20px!important;
+ padding: 50px 50px 50px 100px!important;
}
-.conditionsText .MuiTypography-body1.ConditionsText {}
+@media (max-width: 768px) {
+ .container {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .pageBoxLeft,
+ .pageBoxRight {
+ width: 100%; /* Ensure full width on mobile */
+ margin-top: 0; /* Remove unnecessary top margin */
+ }
+ }
+
+.conditionsText.MuiTypography-body1.ConditionsText {}
.conditionsHelp {
- font-size: large !important;
- margin-left: 5px !important;
+ font-size: large!important;
+ margin-left: 5px!important;
}
.conditionsBox {
- display: flex !important;
- flex-direction: column !important;
+ display: flex!important;
+ flex-direction: column!important;
}
.conditionsSummation {
- width: 100% !important;
- margin-bottom: 10px !important;
+ width: 100%!important;
+ margin-bottom: 10px!important;
}
.buttonsGrid {
- padding: 10px !important;
+ padding: 10px!important;
}
.titleGrids {
- margin: 0 !important;
- width: 100% !important;
+ margin: 0!important;
+ width: 100%!important;
}
.assignmentTextField {
- width: 100% !important;
+ width: 100%!important;
}
.uploadInput {
- display: none !important;
+ display: none!important;
}
.dialogPadding {
- padding: 10px !important;
+ padding: 10px!important;
}
.dialogRemove {
- background-color: #D0E4FF !important;
- padding: 15px 30px !important;
- margin-left: 15px !important;
- border-radius: 20px !important;
- border: none !important;
- cursor: pointer !important;
- font-weight: bold !important;
- font-size: 1.2em !important;
+ background-color: #D0E4FF!important;
+ padding: 15px 30px!important;
+ margin-left: 15px!important;
+ border-radius: 20px!important;
+ border: none!important;
+ cursor: pointer!important;
+ font-weight: bold!important;
+ font-size: 1.2em!important;
}
.dialogCancel {
- background-color: #D0E4FF !important;
- padding: 15px 30px !important;
- margin-left: 30px !important;
- margin-right: 15px !important;
- border-radius: 20px !important;
- border: none !important;
- cursor: pointer !important;
- font-weight: bold !important;
- font-size: 1.2em !important;
-}
\ No newline at end of file
+ background-color: #D0E4FF!important;
+ padding: 15px 30px!important;
+ margin-left: 30px!important;
+ margin-right: 15px!important;
+ border-radius: 20px!important;
+ border: none!important;
+ cursor: pointer!important;
+ font-weight: bold!important;
+ font-size: 1.2em!important;
+}
+
+/* Mobile-friendly adjustments */
+@media (max-width: 768px) {
+ .pageBoxLeft,
+ .pageBoxRight {
+ padding: 20px!important; /* Adjust padding for smaller screens */
+ margin-top: 10px!important; /* Reduce top margin */
+ }
+
+ .conditionsBox {
+ flex-direction: column!important; /* Stack elements vertically */
+ }
+
+ .conditionsHelp {
+ margin: 5px 0!important; /* Adjust margin */
+ }
+
+ .dialogRemove,
+ .dialogCancel {
+ margin-left: 0!important;
+ margin-right: 0!important;
+ }
+}