From 8e0f1f7f626932db1a8abd694ada20ddae69e189 Mon Sep 17 00:00:00 2001 From: Tristan Verbeken Date: Thu, 23 May 2024 19:02:04 +0200 Subject: [PATCH] added container menu dropdown --- frontend/public/docker_langauges/custom.svg | 1 + .../forms/projectFormTabs/DockerFormTab.tsx | 105 +++++++++++------- frontend/src/i18n/en/translation.json | 2 +- frontend/src/i18n/nl/translation.json | 4 +- .../src/pages/editProject/EditProject.tsx | 11 +- 5 files changed, 79 insertions(+), 44 deletions(-) create mode 100644 frontend/public/docker_langauges/custom.svg diff --git a/frontend/public/docker_langauges/custom.svg b/frontend/public/docker_langauges/custom.svg new file mode 100644 index 00000000..39ead495 --- /dev/null +++ b/frontend/public/docker_langauges/custom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/components/forms/projectFormTabs/DockerFormTab.tsx b/frontend/src/components/forms/projectFormTabs/DockerFormTab.tsx index f8f3e4e8..24c14d26 100644 --- a/frontend/src/components/forms/projectFormTabs/DockerFormTab.tsx +++ b/frontend/src/components/forms/projectFormTabs/DockerFormTab.tsx @@ -11,6 +11,12 @@ import { classicNameResolver } from "typescript" import MarkdownTextfield from "../../input/MarkdownTextfield" import TextArea from "antd/es/input/TextArea"; +import BashIcon from "../../../../public/docker_langauges/bash.svg" +import PythonIcon from "../../../../public/docker_langauges/python.svg" +import NodeIcon from "../../../../public/docker_langauges/node-js.svg" +import HaskellIcon from "../../../../public/docker_langauges/haskell.svg" +import Custom from "../../../../public/docker_langauges/custom.svg" + const UploadBtn: React.FC<{ form: FormInstance; fieldName: string; textFieldProps?: TextAreaProps; disabled?: boolean }> = ({ form, fieldName, disabled }) => { const handleFileUpload = (file: File) => { const reader = new FileReader() @@ -45,27 +51,41 @@ const UploadBtn: React.FC<{ form: FormInstance; fieldName: string; textFieldProp interface Script { displayName: string; - scriptGenerator: (script: string) => string; - image?: string; + image: string; + icon: string; } interface ScriptCollection { [key: string]: Script; } +export const languageOptions: ScriptCollection = { + "bash": { displayName: "Bash", image: "fedora", icon: BashIcon }, + "python": { displayName: "Python", image: "python", icon: PythonIcon }, + "javascript": { displayName: "Javascript (node)", image: "node", icon: NodeIcon }, + "haskell": { displayName: "Haskell", image: "haskell", icon: HaskellIcon }, + "custom": { displayName: "Custom", image: "", icon: Custom } +} + +export function imageToLanguage(script: string): string { + for(const language in languageOptions) { + if(script === languageOptions[language].image) { + return language + } + } + return "" +} + + const DockerFormTab: FC<{ form: FormInstance }> = ({ form }) => { const { t } = useTranslation() const {message} = useAppApi() const [withTemplate, setWithTemplate] = useState(true) + const [selectedLanguage, setSelectedLanguage] = useState("bash") const dockerImage = Form.useWatch("dockerImage", form) const dockerDisabled = !dockerImage?.length - const languageOptions:ScriptCollection = { - "bash": {displayName:"Bash", scriptGenerator: (script: string) => script, image: "fedora"}, - "python": {displayName:"Python", scriptGenerator: (script: string) => `python -c '${script}'`, image: "python"}, - "javascript": {displayName:"Javascript (node)", scriptGenerator: (script: string) => `node -e '${script}', image: "node"`}, - "haskell": {displayName:"Haskell", scriptGenerator: (script: string) => `runhaskell -e '${script}'`, image: "haskell"} - } + function isValidTemplate(template: string): string { if (!template?.length) return "" // Template is optional @@ -124,28 +144,46 @@ const DockerFormTab: FC<{ form: FormInstance }> = ({ form }) => { return ( <> - } - - name="dockerImage" + label={ + + } + name="dockerImage" > - { } + + + + + + + + - - - <> = ({ form }) => { - {/* */}
= ({ form }) => {
+ = ({ form }) => { style={{fontFamily: "monospace", whiteSpace: "pre", overflowX: "auto"}} placeholder={"@helloWorldTest\n>required\n>description=\"This is a test\"\nExpected output 1\n\n@helloUGent\n>optional\nExpected output 2\n"} /> - {/**/}
: } diff --git a/frontend/src/i18n/en/translation.json b/frontend/src/i18n/en/translation.json index da297bf2..6aaf520a 100644 --- a/frontend/src/i18n/en/translation.json +++ b/frontend/src/i18n/en/translation.json @@ -211,7 +211,7 @@ "simpleMode": "Without template", "templateMode": "With template", "fileStructureTooltip": "This templates specifies the file structure a submission has to follow.\nIt uses the following syntax:\n* Folders end on `'/'`\n* Use indents to specify files inside a folder\n* Regex can be used\n\t* `'.'` is still a normal `'.'`\n\t* `'\\.'` can be used as regex `'.'`\n* `'-'` at the start of a line specifies a file/folder that is not allowed", - "dockerImageTooltip": "Specify a valid Docker-container from [Docker Hub](https://hub.docker.com/) on which the test script will be run.", + "dockerImageTooltip": "Specify a valid Docker container from [Docker Hub](https://hub.docker.com/) on which the test script will be run. You can also choose a language with a preselected container.", "dockerScriptTooltip": "Bash-script that is executed.\n* The files of the student's submission can be found in `'/shared/input'`\n* Extra files uploaded below can be found in `'/shared/extra'`\n\n More information about the required output depends on the mode and can be found below.", "dockerTemplateTooltip": "To specify specific tests, you need to provide a template. First, enter the test name with '@{test}'. Below this, you can use '>' to provide options such as ('>required', '>optional', '>description'). Everything under these options until the next test or the end of the file is the expected output.", "dockerTestDirTooltip": "Upload additional files needed for the Docker test.\n\nThese files are available in the folder `'/shared/extra'`.", diff --git a/frontend/src/i18n/nl/translation.json b/frontend/src/i18n/nl/translation.json index 39820ff9..522fc6c5 100644 --- a/frontend/src/i18n/nl/translation.json +++ b/frontend/src/i18n/nl/translation.json @@ -213,7 +213,7 @@ "templateMode": "Met sjabloon", "fileStructurePreview": "Voorbeeld van bestandsstructuur", "fileStructureTooltip": "Dit sjabloon specificeert de bestandsstructuur die een indiening moet volgen.\nHet gebruikt de volgende syntax:\n* Mappen eindigen op `'/'`\n* Gebruik inspringing om bestanden binnen een map aan te geven\n* Regex kan worden gebruikt\n\t* `'.'` is nog steeds een normale `'.'`\n\t* `'\\.'` kan worden gebruikt als regex `'.'`\n* `'-'` aan het begin van een regel geeft aan dat een bestand/map niet is toegestaan", - "dockerImageTooltip": "Specificeer een geldige Docker-container van [Docker Hub](https://hub.docker.com/) waarop het testscript zal worden uitgevoerd.", + "dockerImageTooltip": "Specificeer een geldige Docker-container van [Docker Hub](https://hub.docker.com/) waarop het testscript zal worden uitgevoerd. Je kan ook kiezen voor een voorgeconfigureerde programmeertaal met bijhorende container.", "dockerScriptTooltip": "Bash-script dat wordt uitgevoerd.\n* De bestanden van de student zijn indieningen zijn te vinden in `'/shared/input'`\n* Extra bestanden die hieronder zijn geĆ¼pload, zijn te vinden in `'/shared/extra'`\n\nMeer informatie over de vereiste uitvoer is afhankelijk van de modus en is hieronder te vinden.", "dockerTemplateTooltip": "Om specifieke tests te definiĆ«ren, moet je een sjabloon invoeren. Geef eerst de naam van de test in met '@{test}'. Hieronder kun je met een '>' opties geven zoals ('>required', '>optional', '>description'). Alles onder de opties tot de volgende test of het einde van het bestand is de verwachte output.", "dockerTestDirTooltip": "Upload extra bestanden die nodig zijn voor de dockertest.\n\nDeze bestanden zijn beschikbaar in de map `'/shared/extra'`.", @@ -247,7 +247,7 @@ "getStarted": "Aan de slag", "docs": "Documentatie" }, - + "submission": { "submission": "Indiening", "submittedFiles": "Ingediende bestanden:", diff --git a/frontend/src/pages/editProject/EditProject.tsx b/frontend/src/pages/editProject/EditProject.tsx index c5bc4dc9..898e4517 100644 --- a/frontend/src/pages/editProject/EditProject.tsx +++ b/frontend/src/pages/editProject/EditProject.tsx @@ -13,6 +13,7 @@ import { AppRoutes } from "../../@types/routes" import { ProjectContext } from "../../router/ProjectRoutes" import useApi from "../../hooks/useApi" import saveDockerForm, { DockerFormData } from "../../components/common/saveDockerForm" +import {imageToLanguage} from "../../components/forms/projectFormTabs/DockerFormTab"; const EditProject: React.FC = () => { const [form] = Form.useForm() @@ -40,10 +41,9 @@ const EditProject: React.FC = () => { dockerScript: null, dockerImage: null, } + if (response.success) { const tests = response.response.data - console.log(tests) - if (tests.extraFilesName) { const downloadLink = AppRoutes.DOWNLOAD_PROJECT_TESTS.replace(":projectId", projectId).replace(":courseId", courseId!) @@ -64,10 +64,12 @@ const EditProject: React.FC = () => { dockerScript: tests.dockerScript ?? "", dockerImage: tests.dockerImage ?? "", } + const selectedLanguage = imageToLanguage(formVals.dockerImage ?? "") + formVals.dockerScript = selectedLanguage[1] // We only want the script, not the language + form.setFieldValue("languageSelect", selectedLanguage) } form.setFieldsValue(formVals) - setInitialDockerValues(formVals) } @@ -80,6 +82,9 @@ const EditProject: React.FC = () => { const handleCreation = async () => { const values: ProjectFormData & DockerFormData = form.getFieldsValue() + + console.log(values) + if (values.visible) { values.visibleAfter = null }