From 0f65e4fc5e0a1cd01ce2e7ffc8c2ce68768d029d Mon Sep 17 00:00:00 2001 From: Michael <50985676+mpeters4@users.noreply.github.com> Date: Wed, 14 Aug 2024 15:18:31 +0200 Subject: [PATCH] Dungeon: add support for editing and managing of dsl files ("task manager") (#1611) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Task Manager ist im Rahmen der Bachelorarbeit von @mpeters4 entstanden und wird als Kotlin-Projekt in diesem PR in das Dungeon-Projekt integriert. Mit dem Task Manager können DSL-Dateien komfortabel außerhalb einer IDE bearbeitet und geladen werden, es steht sogar ein Syntaxhighlighting zur Verfügung. Im Gegensatz zum [LSP-Support](dsl-ide-support-with-lsp/) (#1601) sollte dieses Sub-Projekt mittelfristig fest in den Dungeon-DSL-Starter integriert werden, damit informatikfremde Nutzende einfacher mit den Aufgabendefinition in den DSL-Dateien umgehen können. (Der LSP-Support sollte als separates Sub-Projekt weitergeführt werden, da es hier um die IDE-Unterstützung geht.) --- Todo vor dem Merge: - [x] Anpassen des Haupt-Readme (Lizenzen) Todo nach dem Merge: - [ ] ~~Einbinden als Gradle-Sub-Projekt im Haupt-Dungeon-Projekt~~ (#1615) - [ ] ~~Anpassen der Ordnerstruktur und Hilfs-/Konfig-Dateien an die anderen Gradle-Sub-Projekte~~ (#1616) - [ ] ~~Anpassen des Haupt-Readme (Erwähnung des Projekts)~~ (#1617) - [ ] ~~Ausschließen von Checkstyle und Spotless (Kotlin-Code)~~ (#1618) Mittelfristig: - [ ] ~~Umbauen auf Java (?) => https://codeconverter.io/convert/kotlin-to-java (?) https://stackoverflow.com/questions/34957430/how-to-convert-a-kotlin-source-file-to-a-java-source-file (?)~~ (#1619) - [ ] ~~Bereitstellen im JAR des Dungeon-DSL-Starters: Beim Starten des Dungeons kommt nicht der Auswahldialog für die DSL-Dateien, sondern @mpeters4's schicke GUI und von dort kann man dann Dateien laden, editieren, speichern, ...~~ (#1620) --------- Co-authored-by: Carsten Gips --- LICENSE-ICONS.md | 9 + README.md | 107 +++-- dungeon-task-manager/.gitattributes | 2 + dungeon-task-manager/.gitignore | 11 + dungeon-task-manager/LICENSE.md | 21 + dungeon-task-manager/Readme.md | 7 + .../Task_Management_Dungeon/.gitignore | 42 ++ .../Task_Management_Dungeon/build.gradle.kts | 88 ++++ .../Task_Management_Dungeon/gradle.properties | 4 + .../gradle/wrapper/gradle-wrapper.properties | 7 + .../settings.gradle.kts | 13 + .../src/main/kotlin/Main.kt | 55 +++ .../src/main/kotlin/classes/Answer.kt | 6 + .../src/main/kotlin/classes/AssignQuestion.kt | 21 + .../src/main/kotlin/classes/Assignment.kt | 11 + .../src/main/kotlin/classes/Dependency.kt | 23 + .../src/main/kotlin/classes/DependencyType.kt | 5 + .../kotlin/classes/MultipleChoiceQuestion.kt | 24 + .../src/main/kotlin/classes/Project.kt | 16 + .../src/main/kotlin/classes/Question.kt | 14 + .../src/main/kotlin/classes/QuestionType.kt | 5 + .../kotlin/classes/SingleChoiceQuestion.kt | 24 + .../composable/ClickableIconCardExit.kt | 50 ++ .../composable/ClickableIconCardNavBack.kt | 53 +++ .../composable/ClickableIconCardNavigate.kt | 55 +++ .../kotlin/composable/CreateAssignment.kt | 127 +++++ .../kotlin/composable/CreateStringList.kt | 102 ++++ .../main/kotlin/composable/DependencyCard.kt | 55 +++ .../main/kotlin/composable/DisplayQuestion.kt | 194 ++++++++ .../kotlin/composable/DropdownSelection.kt | 65 +++ .../main/kotlin/composable/ExpandableItem.kt | 123 +++++ .../src/main/kotlin/composable/NumberField.kt | 40 ++ .../src/main/kotlin/composable/TextField.kt | 36 ++ .../src/main/kotlin/composable/TextForBody.kt | 29 ++ .../src/main/kotlin/composable/Title.kt | 27 ++ .../main/kotlin/composable/checkBoxFilter.kt | 42 ++ .../src/main/kotlin/data/AnswerDataSource.kt | 21 + .../main/kotlin/data/AnswerDataSourceImpl.kt | 50 ++ .../main/kotlin/data/AssignmentDataSource.kt | 16 + .../kotlin/data/AssignmentDataSourceImpl.kt | 40 ++ .../kotlin/data/CorrectAnswerDataSource.kt | 12 + .../data/CorrectAnswerDataSourceImpl.kt | 28 ++ .../data/CorrectAssignmentDataSource.kt | 12 + .../data/CorrectAssignmentDataSourceImpl.kt | 28 ++ .../main/kotlin/data/DependencyDataSource.kt | 16 + .../kotlin/data/DependencyDataSourceImpl.kt | 41 ++ .../src/main/kotlin/data/ProjectDataSource.kt | 19 + .../main/kotlin/data/ProjectDataSourceImpl.kt | 41 ++ .../kotlin/data/ProjectQuestionDataSource.kt | 13 + .../data/ProjectQuestionDataSourceImpl.kt | 29 ++ .../main/kotlin/data/QuestionDataSource.kt | 17 + .../kotlin/data/QuestionDataSourceImpl.kt | 51 ++ .../main/kotlin/data/QuestionTagDataSource.kt | 14 + .../kotlin/data/QuestionTagDataSourceImpl.kt | 29 ++ .../src/main/kotlin/data/TagDataSource.kt | 19 + .../src/main/kotlin/data/TagDataSourceImpl.kt | 44 ++ .../DataBaseCommunication.kt | 136 ++++++ .../main/kotlin/databaseInteraction/Driver.kt | 24 + .../kotlin/databaseInteraction/Provider.kt | 45 ++ .../databaseInteraction/TaskFileWriter.kt | 169 +++++++ .../src/main/kotlin/icon/Add.kt | 60 +++ .../src/main/kotlin/icon/AddFile.kt | 87 ++++ .../src/main/kotlin/icon/Assign_Task.kt | 82 ++++ .../src/main/kotlin/icon/Back.kt | 60 +++ .../src/main/kotlin/icon/Delete.kt | 90 ++++ .../src/main/kotlin/icon/Edit.kt | 66 +++ .../src/main/kotlin/icon/Exit.kt | 81 ++++ .../src/main/kotlin/icon/Multiple_Choice.kt | 88 ++++ .../src/main/kotlin/icon/Project.kt | 125 +++++ .../src/main/kotlin/icon/RectangleList.kt | 123 +++++ .../src/main/kotlin/icon/Single_Choice.kt | 52 ++ .../src/main/kotlin/icon/SquarePlus.kt | 92 ++++ .../src/main/kotlin/icon/Walking.kt | 80 ++++ .../kotlin/screen/CheckAssignTaskScreen.kt | 120 +++++ .../CheckMultipleChoiceQuestionScreen.kt | 134 ++++++ .../screen/CheckSingleChoiceQuestionScreen.kt | 153 ++++++ .../main/kotlin/screen/CreateAssignScreen.kt | 164 +++++++ .../kotlin/screen/CreateDependencyScreen.kt | 135 ++++++ .../screen/CreateMultipleChoiceScreen.kt | 174 +++++++ .../kotlin/screen/CreateSingleChoiceScreen.kt | 169 +++++++ .../kotlin/screen/CreateTaskFileScreen.kt | 147 ++++++ .../main/kotlin/screen/EditProjectScreen.kt | 116 +++++ .../src/main/kotlin/screen/HomeScreen.kt | 100 ++++ .../MultipleChoiceChooseAnswerIndexScreen.kt | 109 +++++ .../src/main/kotlin/screen/ProjectScreen.kt | 147 ++++++ .../kotlin/screen/QuestionBChooserScreen.kt | 331 +++++++++++++ .../kotlin/screen/QuestionChoiceScreen.kt | 83 ++++ .../kotlin/screen/QuestionChooserScreen.kt | 331 +++++++++++++ .../kotlin/screen/QuestionOverviewScreen.kt | 327 +++++++++++++ .../SingleChoiceChooseAnswerIndexScreen.kt | 106 +++++ .../src/main/kotlin/ui/theme/Color.kt | 67 +++ .../src/main/kotlin/ui/theme/Theme.kt | 90 ++++ .../src/main/sqldelight/db/Answer.sq | 40 ++ .../src/main/sqldelight/db/Assignment.sq | 30 ++ .../src/main/sqldelight/db/CorrectAnswer.sq | 18 + .../main/sqldelight/db/CorrectAssignment.sq | 18 + .../src/main/sqldelight/db/Dependency.sq | 27 ++ .../src/main/sqldelight/db/Project.sq | 27 ++ .../src/main/sqldelight/db/ProjectQuestion.sq | 18 + .../src/main/sqldelight/db/Question.sq | 31 ++ .../src/main/sqldelight/db/QuestionTag.sq | 19 + .../src/main/sqldelight/db/Tag.sq | 32 ++ .../szenario_definitions | 444 ++++++++++++++++++ .../gradle/wrapper/gradle-wrapper.properties | 5 + dungeon-task-manager/szenario_definitions | 444 ++++++++++++++++++ 105 files changed, 7621 insertions(+), 48 deletions(-) create mode 100644 LICENSE-ICONS.md create mode 100644 dungeon-task-manager/.gitattributes create mode 100644 dungeon-task-manager/.gitignore create mode 100644 dungeon-task-manager/LICENSE.md create mode 100644 dungeon-task-manager/Readme.md create mode 100644 dungeon-task-manager/Task_Management_Dungeon/.gitignore create mode 100644 dungeon-task-manager/Task_Management_Dungeon/build.gradle.kts create mode 100644 dungeon-task-manager/Task_Management_Dungeon/gradle.properties create mode 100644 dungeon-task-manager/Task_Management_Dungeon/gradle/wrapper/gradle-wrapper.properties create mode 100644 dungeon-task-manager/Task_Management_Dungeon/settings.gradle.kts create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/Main.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Answer.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/AssignQuestion.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Assignment.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Dependency.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/DependencyType.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/MultipleChoiceQuestion.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Project.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Question.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/QuestionType.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/SingleChoiceQuestion.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ClickableIconCardExit.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ClickableIconCardNavBack.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ClickableIconCardNavigate.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/CreateAssignment.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/CreateStringList.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/DependencyCard.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/DisplayQuestion.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/DropdownSelection.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ExpandableItem.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/NumberField.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/TextField.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/TextForBody.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/Title.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/checkBoxFilter.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AnswerDataSource.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AnswerDataSourceImpl.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AssignmentDataSource.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AssignmentDataSourceImpl.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAnswerDataSource.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAnswerDataSourceImpl.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAssignmentDataSource.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAssignmentDataSourceImpl.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/DependencyDataSource.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/DependencyDataSourceImpl.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectDataSource.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectDataSourceImpl.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectQuestionDataSource.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectQuestionDataSourceImpl.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionDataSource.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionDataSourceImpl.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionTagDataSource.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionTagDataSourceImpl.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/TagDataSource.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/TagDataSourceImpl.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/DataBaseCommunication.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/Driver.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/Provider.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/TaskFileWriter.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Add.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/AddFile.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Assign_Task.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Back.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Delete.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Edit.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Exit.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Multiple_Choice.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Project.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/RectangleList.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Single_Choice.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/SquarePlus.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Walking.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CheckAssignTaskScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CheckMultipleChoiceQuestionScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CheckSingleChoiceQuestionScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateAssignScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateDependencyScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateMultipleChoiceScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateSingleChoiceScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateTaskFileScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/EditProjectScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/HomeScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/MultipleChoiceChooseAnswerIndexScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/ProjectScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionBChooserScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionChoiceScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionChooserScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionOverviewScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/SingleChoiceChooseAnswerIndexScreen.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/ui/theme/Color.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/ui/theme/Theme.kt create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Answer.sq create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Assignment.sq create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/CorrectAnswer.sq create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/CorrectAssignment.sq create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Dependency.sq create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Project.sq create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/ProjectQuestion.sq create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Question.sq create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/QuestionTag.sq create mode 100644 dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Tag.sq create mode 100644 dungeon-task-manager/Task_Management_Dungeon/szenario_definitions create mode 100644 dungeon-task-manager/gradle/wrapper/gradle-wrapper.properties create mode 100644 dungeon-task-manager/szenario_definitions diff --git a/LICENSE-ICONS.md b/LICENSE-ICONS.md new file mode 100644 index 000000000..5a0d27451 --- /dev/null +++ b/LICENSE-ICONS.md @@ -0,0 +1,9 @@ +Apache License 2.0 + +======================================================================= + +Copyright (c) 2023 Composable Horizons LLC. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/README.md b/README.md index 2319d6b63..ae57d5fc3 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,9 @@ problems or suggestions, please feel free to contact us in English or German. This project was funded by [Stiftung für Innovation in der Hochschullehre] (["Freiraum 2022"]). -The assets in [`game/assets/`], [`dungeon/assets/`] and [`devDungeon/assets/`] are a mix from free and self created resources: +The assets in [`game/assets/`], [`dungeon/assets/`], [`devDungeon/assets/`], and +[`dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/`] are a mix from free and +self created resources: - Textures and animations: - https://0x72.itch.io/16x16-dungeon-tileset (CC0 1.0) @@ -91,13 +93,16 @@ The assets in [`game/assets/`], [`dungeon/assets/`] and [`devDungeon/assets/`] a - https://opengameart.org/content/hurt-death-sound-effect-for-character (CC0 1.0) - https://opengameart.org/content/80-cc0-creture-sfx-2 (CC0 1.0) - https://freesound.org/s/578488/ (CC0 1.0) +- Icons in [`dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/`] + - https://www.composables.com/icons (Apache 2.0) - Adapted and modified by [\@Flamtky][]: - - Files (except [Health Potion]) in [`dungeon/assets/items/potion/`] (originating from @dkirshner) + - Files (except [Health Potion]) in [`dungeon/assets/items/potion/`] (originating from + [\@dkirshner]) - Files in [`game/assets/dungeon/*/floor`][]: each `floor_damaged.png` (originating from [\@dkirshner]) - [`game/assets/dungeon/fire/floor/floor_1.png`] (originating from [\@dkirshner]) -- Self created by [\@Flamtky][]: - - Files in [`devDungeon/assets/objects/spawner/`] +- Self created by [\@Flamtky][]: + - Files in [`devDungeon/assets/objects/spawner/`] ## Licenses @@ -105,50 +110,56 @@ This [work] by [André Matutat], [Malte Reinsch], and [contributors] is licensed All files in [`doc/publication/`] are licensed under [CC BY-SA 4.0]. -All files in [`game/assets/`](game/assets/), [`dungeon/assets/`](dungeon/assets/) and [`devDungeon/assets/`](devDungeon/assets/) are licensed under [CC0 1.0]. +All files in [`game/assets/`], [`dungeon/assets/`] and [`devDungeon/assets/`] are licensed +under [CC0 1.0]. + +All files in [`dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/`] are +licensed under [Apache 2.0].

Banner

-["Game"]: #game-dungeon-platform -["Dungeon"]: #dungeon-learning-by-questing -["Blockly"]: #blockly-low-code-dungeon -[interesting report]: https://www.hsbi.de/presse/pressemitteilungen/informatik-studierende-am-campus-minden-entwickeln-2d-rollenspiel-zum-lehren-und-lernen -[`game`]: game -[libGDX]: https://github.com/libgdx/libgdx -[rogue-like 2D role-playing games]: https://en.wikipedia.org/wiki/Roguelike -[Java]: https://jdk.java.net/ -[ECS architecture pattern]: https://en.wikipedia.org/wiki/Entity_component_system -[Quickstart]: game/doc/quickstart.md -[Documentation]: game/doc/ -[1]: game/doc/img/monster.gif -[`dungeon`]: dungeon -[2]: dungeon/doc/quickstart.md -[3]: dungeon/doc/ -[Dungeon: StarterKit]: https://github.com/Dungeon-CampusMinden/Dungeon-StarterKit -[4]: dungeon/doc/dsl/img/quickstart_select_config_level.png -[5]: dungeon/doc/dsl/img/quickstart_answer_menu.png -[`blockly`]: blockly -[Google's Blockly]: https://github.com/google/blockly -[6]: blockly/doc/ -[7]: blockly/doc/img/examples/komplexes_beispiel.png -[Java SE Development Kit 21 LTS]: https://jdk.java.net/21/ -[Stiftung für Innovation in der Hochschullehre]: https://stiftung-hochschullehre.de -["Freiraum 2022"]: https://stiftung-hochschullehre.de/foerderung/freiraum2022/ -[`game/assets/`]: game/assets/ -[`dungeon/assets/`]: dungeon/assets/ -[`devDungeon/assets/`]: devDungeon/assets/ -[\@Flamtky]: https://github.com/Flamtky -[Health Potion]: dungeon/assets/items/potion/health_potion.png -[`dungeon/assets/items/potion/`]: dungeon/assets/items/potion/ -[`game/assets/dungeon/*/floor`]: game/assets/dungeon/ -[\@dkirshner]: https://github.com/dkirshner -[`game/assets/dungeon/fire/floor/floor_1.png`]: game/assets/dungeon/fire/floor/floor_1.png -[`devDungeon/assets/objects/spawner/`]: devDungeon/assets/objects/spawner/ -[work]: https://github.com/Dungeon-CampusMinden/Dungeon -[André Matutat]: https://github.com/AMatutat -[Malte Reinsch]: https://github.com/malt-r -[contributors]: https://github.com/Dungeon-CampusMinden/Dungeon/graphs/contributors -[MIT]: LICENSE.md -[`doc/publication/`]: doc/publication/ -[CC BY-SA 4.0]: LICENSE-PAPER.md -[CC0 1.0]: LICENSE-ASSETS.md + ["Game"]: #game-dungeon-platform + ["Dungeon"]: #dungeon-learning-by-questing + ["Blockly"]: #blockly-low-code-dungeon + [interesting report]: https://www.hsbi.de/presse/pressemitteilungen/informatik-studierende-am-campus-minden-entwickeln-2d-rollenspiel-zum-lehren-und-lernen + [`game`]: game + [libGDX]: https://github.com/libgdx/libgdx + [rogue-like 2D role-playing games]: https://en.wikipedia.org/wiki/Roguelike + [Java]: https://jdk.java.net/ + [ECS architecture pattern]: https://en.wikipedia.org/wiki/Entity_component_system + [Quickstart]: game/doc/quickstart.md + [Documentation]: game/doc/ + [1]: game/doc/img/monster.gif + [`dungeon`]: dungeon + [2]: dungeon/doc/quickstart.md + [3]: dungeon/doc/ + [Dungeon: StarterKit]: https://github.com/Dungeon-CampusMinden/Dungeon-StarterKit + [4]: dungeon/doc/dsl/img/quickstart_select_config_level.png + [5]: dungeon/doc/dsl/img/quickstart_answer_menu.png + [`blockly`]: blockly + [Google's Blockly]: https://github.com/google/blockly + [6]: blockly/doc/ + [7]: blockly/doc/img/examples/komplexes_beispiel.png + [Java SE Development Kit 21 LTS]: https://jdk.java.net/21/ + [Stiftung für Innovation in der Hochschullehre]: https://stiftung-hochschullehre.de + ["Freiraum 2022"]: https://stiftung-hochschullehre.de/foerderung/freiraum2022/ + [`game/assets/`]: game/assets/ + [`dungeon/assets/`]: dungeon/assets/ + [`devDungeon/assets/`]: devDungeon/assets/ + [`dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/`]: dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/ + [\@Flamtky]: https://github.com/Flamtky + [Health Potion]: dungeon/assets/items/potion/health_potion.png + [`dungeon/assets/items/potion/`]: dungeon/assets/items/potion/ + [`game/assets/dungeon/*/floor`]: game/assets/dungeon/ + [\@dkirshner]: https://github.com/dkirshner + [`game/assets/dungeon/fire/floor/floor_1.png`]: game/assets/dungeon/fire/floor/floor_1.png + [`devDungeon/assets/objects/spawner/`]: devDungeon/assets/objects/spawner/ + [work]: https://github.com/Dungeon-CampusMinden/Dungeon + [André Matutat]: https://github.com/AMatutat + [Malte Reinsch]: https://github.com/malt-r + [contributors]: https://github.com/Dungeon-CampusMinden/Dungeon/graphs/contributors + [MIT]: LICENSE.md + [`doc/publication/`]: doc/publication/ + [CC BY-SA 4.0]: LICENSE-PAPER.md + [CC0 1.0]: LICENSE-ASSETS.md + [Apache 2.0]: LICENSE-ICONS.md diff --git a/dungeon-task-manager/.gitattributes b/dungeon-task-manager/.gitattributes new file mode 100644 index 000000000..dfe077042 --- /dev/null +++ b/dungeon-task-manager/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/dungeon-task-manager/.gitignore b/dungeon-task-manager/.gitignore new file mode 100644 index 000000000..2148f5641 --- /dev/null +++ b/dungeon-task-manager/.gitignore @@ -0,0 +1,11 @@ + +Task_Management_Dungeon/.idea/uiDesigner.xml +*.xml +Task_Management_Dungeon/.idea/gradle.xml +Task_Management_Dungeon/.idea/gradle.xml +Task_Management_Dungeon/gradle/wrapper/gradle-wrapper.jar +Task_Management_Dungeon/gradle/wrapper/gradle-wrapper.jar +Task_Management_Dungeon/gradle/wrapper/gradle-wrapper.jar +Task_Management_Dungeon/task_manager.db +Task_Management_Dungeon/Dungeon_Files/Test.dng +*.dng diff --git a/dungeon-task-manager/LICENSE.md b/dungeon-task-manager/LICENSE.md new file mode 100644 index 000000000..bdc7c2c7a --- /dev/null +++ b/dungeon-task-manager/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Michael Peters + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dungeon-task-manager/Readme.md b/dungeon-task-manager/Readme.md new file mode 100644 index 000000000..015aced5c --- /dev/null +++ b/dungeon-task-manager/Readme.md @@ -0,0 +1,7 @@ +Dies is eine Software zur Verwaltung von Aufgaben im Dungeon. +Die Erstellung erfolgt im Rahmen meiner Bachelorarbeit. + +Folgende externe Bibliotheken stehen unter einer anderen Lizenz (Apache 2.0): + +[Material Symbols for Jetpack Compose (Ordner: Task_Management_Dungeon\src\main\kotlin\icon)](https://www.composables.com/icons) + diff --git a/dungeon-task-manager/Task_Management_Dungeon/.gitignore b/dungeon-task-manager/Task_Management_Dungeon/.gitignore new file mode 100644 index 000000000..b63da4551 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/build.gradle.kts b/dungeon-task-manager/Task_Management_Dungeon/build.gradle.kts new file mode 100644 index 000000000..cccebc2cd --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/build.gradle.kts @@ -0,0 +1,88 @@ +import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType + +plugins { + kotlin("jvm") + id("org.jetbrains.compose") + id("app.cash.sqldelight") version "2.0.1" +} + +group = "org.example" +version = "1.0-SNAPSHOT" + +//Set Compiler to Java 21.01 +compose { + kotlinCompilerPlugin.set("androidx.compose.compiler:compiler:1.5.7") +} + +kotlin{ + + sourceSets{ + main{ + dependencies{ + // implementation("app.cash.sqldelight:native-driver:2.0.1") + } + } + } +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + google() +} + + +dependencies { + val voyagerVersion = "1.0.0" + // Note, if you develop a library, you should use compose.desktop.common. + // compose.desktop.currentOs should be used in launcher-sourceSet + // (in a separate module for demo project and in testMain). + // With compose.desktop.common you will also lose @Preview functionality + implementation(compose.desktop.currentOs) + + // Navigator Copyright (c) 2021 Adriel Café + implementation("cafe.adriel.voyager:voyager-navigator:$voyagerVersion") + + // Screen Model Copyright (c) 2021 Adriel Café + implementation("cafe.adriel.voyager:voyager-screenmodel:$voyagerVersion") + + // BottomSheetNavigator Copyright (c) 2021 Adriel Café + implementation("cafe.adriel.voyager:voyager-bottom-sheet-navigator:$voyagerVersion") + + // TabNavigator Copyright (c) 2021 Adriel Café + implementation("cafe.adriel.voyager:voyager-tab-navigator:$voyagerVersion") + + // Transitions Copyright (c) 2021 Adriel Café + implementation("cafe.adriel.voyager:voyager-transitions:$voyagerVersion") + + //Material 3 + implementation("org.jetbrains.compose.material3:material3-desktop:1.6.0") + //SQLdelight + implementation("app.cash.sqldelight:sqlite-driver:2.0.1") + implementation("app.cash.sqldelight:coroutines-extensions:2.0.1") + + implementation("org.slf4j:slf4j-nop:1.7.25") + +} + +//Database +sqldelight { + databases { + create("Database") { + packageName.set("Task_Management_Dungeon") + } + } +} + +compose.desktop { + application { + mainClass = "MainKt" + + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "Task_Management_Dungeon" + packageVersion = "1.0.0" + } + } +} diff --git a/dungeon-task-manager/Task_Management_Dungeon/gradle.properties b/dungeon-task-manager/Task_Management_Dungeon/gradle.properties new file mode 100644 index 000000000..618b17faa --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +kotlin.code.style=official +kotlin.version=1.9.20 +compose.version=1.5.10 diff --git a/dungeon-task-manager/Task_Management_Dungeon/gradle/wrapper/gradle-wrapper.properties b/dungeon-task-manager/Task_Management_Dungeon/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..1af9e0930 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/dungeon-task-manager/Task_Management_Dungeon/settings.gradle.kts b/dungeon-task-manager/Task_Management_Dungeon/settings.gradle.kts new file mode 100644 index 000000000..67d56d3e4 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/settings.gradle.kts @@ -0,0 +1,13 @@ +pluginManagement { + repositories { + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + } + + plugins { + kotlin("jvm").version(extra["kotlin.version"] as String) + id("org.jetbrains.compose").version(extra["compose.version"] as String) + } +} + +rootProject.name = "Task_Management_Dungeon" diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/Main.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/Main.kt new file mode 100644 index 000000000..d31e67f34 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/Main.kt @@ -0,0 +1,55 @@ +import Task_Management_Dungeon.Database +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.material3.Button +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver +import cafe.adriel.voyager.navigator.Navigator +import cafe.adriel.voyager.transitions.SlideTransition +import databaseInteraction.Driver +import screen.HomeScreen +import java.awt.Dimension +import java.io.File + + +const val MIN_HEIGHT = 300 +const val MIN_WIDTH = 400 + +@Composable +@Preview +fun App() { + var text by remember { mutableStateOf("Hello, World!") } + + MaterialTheme { + Button(onClick = { + text = "Hello, Desktop!" + }) { + Text(text) + } + + } +} + +fun main() = application { + //val driver = Driver.createDriver() + Window( + onCloseRequest = ::exitApplication, + resizable = true, + title = "Dungeon Task Manager", + ) { + window.minimumSize = Dimension(500, 500) + Navigator(HomeScreen()) { navigator -> + SlideTransition(navigator) + } + } +} diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Answer.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Answer.kt new file mode 100644 index 000000000..869a50159 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Answer.kt @@ -0,0 +1,6 @@ +package classes + +data class Answer( + val id : Int, + val answer: String +) \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/AssignQuestion.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/AssignQuestion.kt new file mode 100644 index 000000000..a82a1efa0 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/AssignQuestion.kt @@ -0,0 +1,21 @@ +package classes + +import androidx.compose.runtime.mutableStateListOf + +class AssignQuestion( + id: Long = 0, + description: String = "", + points: Int = 0, + pointsToPass: Int = 0, + explanation: String = "", + tags: List = mutableStateListOf(), + val assignments: List = mutableStateListOf() +): Question( + id = id, + description = description, + points = points, + pointsToPass = pointsToPass, + explanation = explanation, + tags = tags, + type = QuestionType.Assign +) \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Assignment.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Assignment.kt new file mode 100644 index 000000000..2aac2b0e0 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Assignment.kt @@ -0,0 +1,11 @@ +package classes + +/** + * Assignments use a Term and a Definition as solutions for assign tasks + * @param termA term A of the assignment. If there is no solution to it, leave it as default + * @param termB term B of the assignment. If there is no solution to it, leave it as default + */ +data class Assignment( + val termA: String = "_", + val termB: String = "_" +) \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Dependency.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Dependency.kt new file mode 100644 index 000000000..c168e14e9 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Dependency.kt @@ -0,0 +1,23 @@ +package classes + +class Dependency( + val projectId: Long + +) { + var questionA: Question? = null + get() = field + set(value) { + field = value + } + var questionB: Question? = null + get() = field + set(value) { + field = value + } + var dependency: String = "" + get() = field + set(value) { + field = value + } + +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/DependencyType.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/DependencyType.kt new file mode 100644 index 000000000..53a0c3458 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/DependencyType.kt @@ -0,0 +1,5 @@ +package classes + +enum class DependencyType { + SUBTASK_MANDATORY, SUBTASK_OPTIONAL, SEQUENCE, CONDITONAL_FALSE, CONDITIONAL_CORRECT +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/MultipleChoiceQuestion.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/MultipleChoiceQuestion.kt new file mode 100644 index 000000000..9213bfb90 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/MultipleChoiceQuestion.kt @@ -0,0 +1,24 @@ +package classes + +import androidx.compose.runtime.mutableStateListOf + +class MultipleChoiceQuestion( + id: Long = 0, + description: String = "", + points: Int = 0, + pointsToPass: Int = 0, + explanation: String = "", + tags: List = mutableStateListOf(), + val answers: List = mutableStateListOf(), + var correctAnswerIndices: List = mutableStateListOf() +) : Question( + id = id, + description = description, + points = points, + pointsToPass = pointsToPass, + explanation = explanation, + tags = tags, + type = QuestionType.MultipleChoice +) { + +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Project.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Project.kt new file mode 100644 index 000000000..8e0eeeef6 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Project.kt @@ -0,0 +1,16 @@ +package classes + +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember + +data class Project( + val name: String, +) { + var questions: List = mutableListOf() + val dependencies = mutableStateListOf() + + fun addDependency(dependency: Dependency){ + dependencies.add(dependency) + } + +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Question.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Question.kt new file mode 100644 index 000000000..60b26467f --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/Question.kt @@ -0,0 +1,14 @@ +package classes + +import androidx.compose.runtime.mutableStateListOf + +open class Question( + val id: Long = 0, + var description: String = "", + var points: Int = 0, + var pointsToPass: Int = 0, + var explanation: String = "", + val tags: List = mutableStateListOf(), + val type : QuestionType +) { +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/QuestionType.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/QuestionType.kt new file mode 100644 index 000000000..ca5df804b --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/QuestionType.kt @@ -0,0 +1,5 @@ +package classes + +enum class QuestionType { + SingleChoice, MultipleChoice, Assign +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/SingleChoiceQuestion.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/SingleChoiceQuestion.kt new file mode 100644 index 000000000..5922e8e42 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/classes/SingleChoiceQuestion.kt @@ -0,0 +1,24 @@ +package classes + +import androidx.compose.runtime.mutableStateListOf + +class SingleChoiceQuestion( + id: Long = 0, + description: String = "", + points: Int = 0, + pointsToPass: Int = 0, + explanation: String = "", + val answers: List = mutableStateListOf(), + tags: List = mutableStateListOf(), + var correctAnswerIndex: Int = -1 +) : Question( + id= id, + description = description, + points = points, + pointsToPass = pointsToPass, + explanation = explanation, + tags = tags, + type = QuestionType.SingleChoice +) { + +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ClickableIconCardExit.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ClickableIconCardExit.kt new file mode 100644 index 000000000..f04b648af --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ClickableIconCardExit.kt @@ -0,0 +1,50 @@ +package composable + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import kotlin.system.exitProcess + +/** + * Card to exitIcon the Programm + * @param icon ImageVector of the Icon that is shown in the card + * @param text Text that is shown in the Card + */ +@Composable +fun clickableIconCardExit( + icon: ImageVector, + text: String, + modifier: Modifier +) { + androidx.compose.material3.Card( + modifier = modifier.clickable { exitProcess(0) }, + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.background + ), + ) { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + + ) { + Image(icon, "Icon", Modifier.padding(10.dp)) + Text( + text, + style = MaterialTheme.typography.titleLarge, + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ClickableIconCardNavBack.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ClickableIconCardNavBack.kt new file mode 100644 index 000000000..a99887526 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ClickableIconCardNavBack.kt @@ -0,0 +1,53 @@ +package composable + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.navigator.Navigator + +/** + * Clickable Card to novigate to the previous Screen + * @param icon ImageVector of the Icon that is shown in the card + * @param text Text that is shown in the Card + * @param navigator Navigator that is used to navigate to another screen + */ +@Composable +fun clickableIconCardNavBack( + icon: ImageVector, + text: String, + modifier: Modifier, + navigator: Navigator, +) { + Card( + modifier = modifier.clickable { navigator.pop() }, + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.background + ), + ) { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image(icon, "Icon", Modifier.padding(10.dp)) + Text( + text, + style = MaterialTheme.typography.titleLarge, + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } +} + diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ClickableIconCardNavigate.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ClickableIconCardNavigate.kt new file mode 100644 index 000000000..696cb9769 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ClickableIconCardNavigate.kt @@ -0,0 +1,55 @@ +package composable + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.Navigator + +/** + * Clickable Card to novigate to a Screen + * @param icon ImageVector of the Icon that is shown in the card + * @param text Text that is shown in the Card + * @param navigator Navigator that is used to navigate to another screen + */ +@Composable +fun clickableIconCardNavigate( + icon: ImageVector, + text: String, + modifier: Modifier, + navigator: Navigator, + nextLocation: Screen +) { + Card( + modifier = modifier.clickable { navigator.push(nextLocation) }, + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.background + ), + ) { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image(icon, "Icon", Modifier.padding(10.dp)) + Text( + text, + style = MaterialTheme.typography.titleLarge, + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } +} + diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/CreateAssignment.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/CreateAssignment.kt new file mode 100644 index 000000000..96695767a --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/CreateAssignment.kt @@ -0,0 +1,127 @@ +package composable + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import classes.Assignment +import icon.addIcon +import icon.deleteIcon + +/** + * Composable Function to create an Assignment used in Assignment Tasks + * @param modifier Modify the used fields + * @param onValueChange Function that runs when the addIcon is clicked + * @param taskLabel Textfield Label + * @param outputLabel is shown above the output + * @param minAmount TextField shows error if there are fewer assignments than minAmount + */ +@Composable +fun createAssignment( + modifier: Modifier, + onValueChange: (SnapshotStateList) -> Unit, + taskLabel: String, + outputLabel: String, + minAmount: Int +) { + var termB by rememberSaveable { mutableStateOf("") } + var termA by rememberSaveable { mutableStateOf("") } + val assignments = remember { mutableStateListOf() } + Column { + bodyText(taskLabel) + Row(content = { + Image( + addIcon(MaterialTheme.colorScheme.onBackground), + "Add", + Modifier.padding(4.dp, top = 26.dp).clickable { + if (termA.isNotEmpty()||termB.isNotEmpty()) { + if(termA.isEmpty()){ + assignments.add(Assignment(termB = termB)) + } + else if (termB.isEmpty()){ + assignments.add(Assignment(termA = termA)) + }else{ + assignments.add(Assignment(termA,termB)) + } + } + termA = "" + termB = "" + }) + OutlinedTextField( + modifier = modifier.padding(8.dp).fillMaxWidth().weight(4f), + value = termA, + onValueChange = { termA = it }, + label = { Text("Term A") }, + isError = assignments.size < minAmount + ) + OutlinedTextField( + modifier = modifier.padding(8.dp).fillMaxWidth().weight(4f), + value = termB, + onValueChange = { termB = it }, + label = { Text("Term B") }, + isError = assignments.size < minAmount + ) + }) + LazyColumn( + Modifier.fillMaxWidth().size(300.dp).clip(shape = RoundedCornerShape(10.dp)) + .background(MaterialTheme.colorScheme.onSecondary), + ) { + item { + Text( + text = outputLabel, + style = MaterialTheme.typography.titleLarge, + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = modifier.padding(bottom = 10.dp, start = 8.dp, top = 8.dp) + ) + } + + items(items = assignments) { assignment -> + Card( + modifier = modifier.fillMaxWidth().padding(20.dp, end = 16.dp, bottom = 10.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.background + ) + ) { + Box { + Row( + modifier.align(Alignment.Center), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + deleteIcon(MaterialTheme.colorScheme.onSurfaceVariant), + "Remove Item", + Modifier.padding(10.dp).clickable { assignments.remove(assignment) }) + Text( + "Term A: ${assignment.termA} ", + style = MaterialTheme.typography.bodyMedium, + fontSize = 24.sp, + lineHeight = 25.sp, + modifier = modifier.clickable {}) + Text( + " Term B: ${assignment.termB}", + style = MaterialTheme.typography.bodyMedium, + fontSize = 24.sp, + lineHeight = 25.sp, + modifier = modifier.clickable {}) + } + } + } + } + onValueChange(assignments) + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/CreateStringList.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/CreateStringList.kt new file mode 100644 index 000000000..398b9ff7b --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/CreateStringList.kt @@ -0,0 +1,102 @@ +package composable + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import icon.addIcon +import icon.deleteIcon + +/** + * Composable function to create a Stringlist + * @param modifier Modify the used fields + * @param onValueChange Function that runs when the addIcon is clicked. Uses String Value + * @param taskLabel Textfield Label + * @param outputLabel is shown above the output + * @param minAmount TextField shows error if there are fewer assignments than minAmount + */ +@Composable +fun createStringList( + modifier: Modifier, + onValueChange: (SnapshotStateList) -> Unit, + taskLabel: String, + textFieldLabel: String, + outputLabel: String, + minAmount: Int +) { + var text by rememberSaveable { mutableStateOf("") } + val strings = remember { mutableStateListOf() } + Column { + bodyText(taskLabel) + Row(content = { + Image( + addIcon(MaterialTheme.colorScheme.onBackground), + "Add", + Modifier.padding(4.dp, top = 26.dp).clickable { + if (text.isNotEmpty()) { + strings.add(text) + } + text = "" + }) + OutlinedTextField( + modifier = modifier.padding(8.dp).fillMaxWidth(), + value = text, + onValueChange = { text = it }, + label = { Text(textFieldLabel) }, + isError = strings.size < minAmount + ) + }) + LazyColumn(Modifier.fillMaxWidth().size(200.dp).clip(shape = RoundedCornerShape(10.dp)).background(MaterialTheme.colorScheme.onSecondary)) { + item { + Text( + text = outputLabel, + style = MaterialTheme.typography.titleLarge, + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = modifier.padding(bottom = 10.dp, start = 8.dp, top = 8.dp) + ) + } + + items(items = strings) { answer -> + Card( + modifier = modifier.fillMaxWidth().padding(20.dp, end = 16.dp, bottom = 10.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.background + ) + ) { + Box { + Row( + modifier.align(Alignment.Center), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + deleteIcon(MaterialTheme.colorScheme.onSurfaceVariant), + "Remove Item", + Modifier.padding(10.dp).clickable { strings.remove(answer) }) + Text( + "Antwort: $answer", + style = MaterialTheme.typography.bodyMedium, + fontSize = 24.sp, + lineHeight = 25.sp, + modifier = modifier.clickable {}) + } + } + } + } + onValueChange(strings) + } + } +} diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/DependencyCard.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/DependencyCard.kt new file mode 100644 index 000000000..986ea3480 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/DependencyCard.kt @@ -0,0 +1,55 @@ +package composable + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Card +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import databaseInteraction.DataBaseCommunication +import databaseInteraction.Driver +import databaseInteraction.Provider +import icon.deleteIcon +import kotlinx.coroutines.runBlocking + +/** + * Card with a dependency view + * @param dependency dependency to show + * @param action function that is called when the remove icon is clicked + */ +@Composable +fun dependencyCard( + dependency: db.Dependency, + action: () -> Unit +){ + val questionData = Provider.provideQuestionDataSource(Driver.createDriver()) + val dependencyData = Provider.provideDependencyDataSource((Driver.createDriver())) + val questionA = runBlocking {DataBaseCommunication.getQuestionAsClass(questionData.getQuestionById(dependency.questionAID)!!) } + val questionB = runBlocking {DataBaseCommunication.getQuestionAsClass(questionData.getQuestionById(dependency.questionBID)!!) } + + Card(modifier = Modifier.padding(16.dp)) { + Row(Modifier.align(Alignment.CenterHorizontally)) { + if (questionA != null) { + expandableItem(questionA,{}, mode = 3, modifier = Modifier.weight(3f)) + } + if (questionB != null) { + expandableItem(questionB,{}, mode = 3, modifier = Modifier.weight(3f)) + } + Box(modifier = Modifier.width(200.dp)) { bodyText(dependency.dependency, modifier = Modifier.align(Alignment.Center).padding(top = 24.dp)) } + Image( + deleteIcon(MaterialTheme.colorScheme.onSurfaceVariant), + "Remove Item", + Modifier.padding(10.dp, top = 30.dp).clickable { + runBlocking { dependencyData.deleteDependencyById(dependency.dependencyID)} + action() + }.width(56.dp) + ) + } + } +} diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/DisplayQuestion.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/DisplayQuestion.kt new file mode 100644 index 000000000..62c1d355a --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/DisplayQuestion.kt @@ -0,0 +1,194 @@ +package composable + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Divider +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import classes.AssignQuestion +import classes.MultipleChoiceQuestion +import classes.SingleChoiceQuestion + +/** + * Composable Function to display an overview of the question + * @param question Single choice question to display + * @param modifier Modifier to manipulate the composable + * @param showQuestion defines if the question.description is shown + */ +@Composable +fun QuestionDisplay(question: SingleChoiceQuestion, modifier: Modifier = Modifier, showQuestion : Boolean= true){ + Box (modifier.fillMaxSize().clip(shape = RoundedCornerShape(10.dp)).background(MaterialTheme.colorScheme.onSecondary)){ + Column(modifier = modifier.fillMaxWidth().padding(16.dp)) { + if(showQuestion){ + Row { + bodyText("Frage: ", modifier = Modifier.weight(1f)) + bodyText(question.description, modifier = Modifier.weight(4f)) + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + } + Row { + bodyText("Antworten:", modifier = Modifier.weight(1f)) + Column(modifier = Modifier.weight(4f)) { + question.answers.forEachIndexed { index, answer -> + Row { + bodyText("Antwort ${index+1}: ") + bodyText(answer) + } + } + } + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + Row { + bodyText("Korrekte Antwort:", modifier = Modifier.weight(1f)) + bodyText("Antwort ${(question.correctAnswerIndex + 1)}", modifier = Modifier.weight(4f)) + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + Row { + bodyText("Erklärung:", modifier = Modifier.weight(1f)) + bodyText(question.explanation, modifier = Modifier.weight(4f)) + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + Row { + bodyText("Punkte:", modifier = Modifier.weight(1f)) + bodyText("${question.points} (Punkte zum Bestehen: ${question.pointsToPass})", modifier = Modifier.weight(4f)) + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + Row { + bodyText("Tags:", 20, Modifier.weight(1f)) + if(question.tags.isNotEmpty()){ + Column(Modifier.weight(4f)) { + question.tags.forEach { tag -> + bodyText(tag) + } + } + }else{ + bodyText("Keine Tags vorhanden", modifier = Modifier.weight(4f)) + } + } + } + } +} + +/** + * Composable Function to display an overview of the question + * @param question multiple choice question to display + * @param modifier Modifier to manipulate the composable + * @param showQuestion defines if the question.description is shown + */ +@Composable +fun QuestionDisplay(question: MultipleChoiceQuestion, modifier: Modifier = Modifier, showQuestion : Boolean= true){ + Box (modifier.fillMaxSize().clip(shape = RoundedCornerShape(10.dp)).background(MaterialTheme.colorScheme.onSecondary)){ + Column(modifier = modifier.fillMaxWidth().padding(16.dp)) { + if(showQuestion){ + Row { + bodyText("Frage: ", modifier = Modifier.weight(1f)) + bodyText(question.description, modifier = Modifier.weight(4f)) + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + } + Row { + bodyText("Antworten:", modifier = Modifier.weight(1f)) + Column(modifier = Modifier.weight(4f)) { + question.answers.forEachIndexed { index, answer -> + Row { + bodyText("Antwort ${index+1}: ") + bodyText(answer) + } + } + } + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + Row { + bodyText("Korrekte Antworten:", modifier = Modifier.weight(1f)) + Column(modifier = Modifier.weight(4f)) { + question.correctAnswerIndices.forEach{ index -> + bodyText("Antwort ${index+1} ") + } + } + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + Row { + bodyText("Erklärung:", modifier = Modifier.weight(1f)) + bodyText(question.explanation, modifier = Modifier.weight(4f)) + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + Row { + bodyText("Punkte:", modifier = Modifier.weight(1f)) + bodyText("${question.points} (Punkte zum Bestehen: ${question.pointsToPass})", modifier = Modifier.weight(4f)) + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + Row { + bodyText("Tags:", 20, Modifier.weight(1f)) + if(question.tags.isNotEmpty()){ + Column(Modifier.weight(4f)) { + question.tags.forEach { tag -> + bodyText(tag) + } + } + }else{ + bodyText("Keine Tags vorhanden", modifier = Modifier.weight(4f)) + } + } + } + } +}/** + * Composable Function to display an overview of the question + * @param question Assign question to display + * @param modifier Modifier to manipulate the composable + * @param showQuestion defines if the question.description is shown + */ + + +@Composable +fun QuestionDisplay(question: AssignQuestion, modifier: Modifier = Modifier, showQuestion : Boolean= true){ + Box (modifier.fillMaxSize().clip(shape = RoundedCornerShape(10.dp)).background(MaterialTheme.colorScheme.onSecondary)){ + Column(modifier = modifier.fillMaxWidth().padding(16.dp)) { + if(showQuestion){ + Row { + bodyText("Frage: ", modifier = Modifier.weight(1f)) + bodyText(question.description, modifier = Modifier.weight(4f)) + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + Row { + bodyText("Antworten:", modifier = Modifier.weight(1f)) + Column(modifier = Modifier.weight(4f)) { + question.assignments.forEachIndexed { index, assignment -> + Row { + bodyText("Lösung ${index+1}: ") + bodyText("A: ${assignment.termA}, B: ${assignment.termB}") + } + } + } + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + Row { + bodyText("Erklärung:", modifier = Modifier.weight(1f)) + bodyText(question.explanation, modifier = Modifier.weight(4f)) + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + Row { + bodyText("Punkte:", modifier = Modifier.weight(1f)) + bodyText("${question.points} (Punkte zum Bestehen: ${question.pointsToPass})", modifier = Modifier.weight(4f)) + } + Divider (color = MaterialTheme.colorScheme.background, modifier = Modifier.height(2.dp).fillMaxWidth()) + Row { + bodyText("Tags:", 20, Modifier.weight(1f)) + if(question.tags.isNotEmpty()){ + Column(Modifier.weight(4f)) { + question.tags.forEach { tag -> + bodyText(tag) + } + } + }else{ + bodyText("Keine Tags vorhanden", modifier = Modifier.weight(4f)) + } + } + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/DropdownSelection.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/DropdownSelection.kt new file mode 100644 index 000000000..9bbb90403 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/DropdownSelection.kt @@ -0,0 +1,65 @@ + +package composable + +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier + +/** + * Composable Function to show a selection in a dropdown menu + * @param value Chosen value in the selection + * @param itemList List of values to choose + * @param modifier Modify appearance of the menu + * @param onItemClick Function that runs if an item is clicked + */ +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3Api::class) +@Composable +fun dropdownSelection( + value: String, + itemList: List, + modifier: Modifier, + onItemClick: (String) -> Unit +) { + var showDropdown by rememberSaveable { mutableStateOf(false) } + var selectedIndex by rememberSaveable { mutableStateOf(0) } + var copyValue by rememberSaveable { mutableStateOf(value) } + ExposedDropdownMenuBox( + expanded = showDropdown, + onExpandedChange = {showDropdown = it } + ){ + TextField( + value = copyValue, + modifier = modifier + .menuAnchor(), + readOnly = true, + onValueChange = {}, + label = {}, + trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = showDropdown)}, + ) + ExposedDropdownMenu( + expanded = showDropdown, + onDismissRequest = {showDropdown = false} + ){ + itemList.forEachIndexed{index, item -> + DropdownMenuItem( + text = { Text(item) }, + onClick = { + copyValue = item + onItemClick(item) + selectedIndex = index + showDropdown = false + }, + contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding + ) + } + onItemClick(copyValue) + } + + } +} + + diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ExpandableItem.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ExpandableItem.kt new file mode 100644 index 000000000..458c084c9 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/ExpandableItem.kt @@ -0,0 +1,123 @@ +package composable + +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.LinearOutSlowInEasing +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material3.* +import androidx.compose.material3.AlertDialogDefaults.shape +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import classes.AssignQuestion +import classes.MultipleChoiceQuestion +import classes.Question +import classes.SingleChoiceQuestion +import icon.addIcon +import icon.deleteIcon +import icon.editIcon + + +@Composable +fun expandableItem(question: Question, action: (Question) -> Unit, modifier: Modifier = Modifier, mode: Int = 0) { + var expandedState by remember { mutableStateOf(false) } + val rotationState by animateFloatAsState( + targetValue = if (expandedState) 180f else 0f + ) + Card( + modifier = modifier + .animateContentSize( + animationSpec = tween( + durationMillis = 300, + easing = LinearOutSlowInEasing + ) + ).padding(8.dp), + shape = shape, + onClick = { + expandedState = !expandedState + } + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text( + modifier = Modifier + .weight(6f), + text = question.description, + fontSize = 16.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + IconButton( + modifier = Modifier + .weight(1f) + .alpha(0.2f) + .rotate(rotationState), + onClick = { + expandedState = !expandedState + }) { + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = "Drop-Down Arrow" + ) + } + } + if (expandedState) { + when (question) { + is SingleChoiceQuestion -> { + QuestionDisplay(question, showQuestion = false) + } + + is MultipleChoiceQuestion -> { + QuestionDisplay(question, showQuestion = false) + } + + is AssignQuestion -> { + QuestionDisplay(question, showQuestion = false) + } + } + when (mode) { + 0 -> { + Image( + deleteIcon(MaterialTheme.colorScheme.onSurfaceVariant), + "Remove Item", + Modifier.padding(10.dp).align(Alignment.End).clickable { action(question) }) + } + 1 -> { + Image( + addIcon(MaterialTheme.colorScheme.onSurfaceVariant), + "add Item", + Modifier.padding(10.dp).align(Alignment.End).clickable { action(question) }) + } + 2 -> { + Image( + editIcon(MaterialTheme.colorScheme.onSurfaceVariant), + "add Item", + Modifier.padding(10.dp).align(Alignment.End).clickable { action(question) }) + } + 3 -> {} + } + + } + + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/NumberField.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/NumberField.kt new file mode 100644 index 000000000..0f0b93368 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/NumberField.kt @@ -0,0 +1,40 @@ +package composable + +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp + +/** + * @param modifier Modify appearance of + * @param value Value inside the textfield + * @param onValueChange Function that runs when the input value changes + * @param label Label of the textfield + */ +@Composable +fun inputNumberField( + modifier: Modifier, + value: String, + onValueChange: (String) -> Unit, + label : String +) { + var text by remember { mutableStateOf(value) } + + OutlinedTextField( + modifier = modifier.padding(8.dp), + value = text, + onValueChange = { + text = it.filter { symbol -> + symbol.isDigit() + } + onValueChange(it)}, + label = { Text(label) }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + isError = text.isEmpty() + ) +} + diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/TextField.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/TextField.kt new file mode 100644 index 000000000..174f003e7 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/TextField.kt @@ -0,0 +1,36 @@ +package composable + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +/** + * Composable function for a textfield + * @param modifier Modify appearance of + * @param value Value inside the textfield + * @param onValueChange Function that runs when the input value changes + * @param label Label of the textfield + * @param isError Statement to define the error. True if you don't want any error + */ +@Composable +fun inputTextField( + modifier: Modifier, + value: String, + onValueChange: (String) -> Unit, + label : String, + isError: Boolean +) { + var text by remember { mutableStateOf(value) } + OutlinedTextField( + modifier = modifier.fillMaxWidth().padding(8.dp), + value = text, + onValueChange = { text = it + onValueChange(it)}, + label = { Text(label) }, + isError = isError + ) +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/TextForBody.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/TextForBody.kt new file mode 100644 index 000000000..15cc572d5 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/TextForBody.kt @@ -0,0 +1,29 @@ +package composable + +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +/** + * Composable predefined text for this application + * @param text Shown message + * @param size Text size + * @param modifier Modifier to modify text appearance + */ +@Composable +fun bodyText(text : String, size: Int = 16, modifier: Modifier = Modifier){ + Text( + text = text, + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Start, + fontSize = size.sp, + lineHeight = size.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = modifier.padding(16.dp) + ) +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/Title.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/Title.kt new file mode 100644 index 000000000..884781558 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/Title.kt @@ -0,0 +1,27 @@ +package composable + +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +/** + * Composable function to use predefined title text + * @param title Title message + */ +@Composable +fun title(title : String){ + Text( + text = title, + style = MaterialTheme.typography.titleLarge, + textAlign = TextAlign.Start, + fontSize = 40.sp, + lineHeight = 40.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(bottom = 16.dp) + ) +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/checkBoxFilter.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/checkBoxFilter.kt new file mode 100644 index 000000000..6a2ab2a62 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/composable/checkBoxFilter.kt @@ -0,0 +1,42 @@ +package composable + +import androidx.compose.foundation.layout.Row +import androidx.compose.material3.Checkbox +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.ui.Alignment + +/** + * Composable checkbox to filter Items + * @param text Text next to the checkbox + * @param onCheckedTrue Function that runs if the checkbox is ticked + * @param onCheckedFalse Function that runs if the checkbox is not ticked + */ +@Composable +fun checkBoxFilter( + text : String, + onCheckedTrue: (SnapshotStateList) -> Unit, + onCheckedFalse: (SnapshotStateList) -> Unit +){ + val isChecked = remember { mutableStateOf(false) } + val tagList = remember { mutableStateListOf() } + + + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox( + checked = isChecked.value, + onCheckedChange = { checked -> isChecked.value = checked + if (isChecked.value) { + onCheckedTrue(tagList) + }else{ + onCheckedFalse(tagList) + } + }, + ) + Text(text) + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AnswerDataSource.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AnswerDataSource.kt new file mode 100644 index 000000000..1379d92a7 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AnswerDataSource.kt @@ -0,0 +1,21 @@ +package data + +import db.Answer +import kotlinx.coroutines.flow.Flow + +interface AnswerDataSource { + + suspend fun getAnswerById(id: Long): Answer? + + suspend fun getAnswerId(questionId: Long,answer: String): Long? + + fun getAnswersByQuestionId(id: Long): Flow> + + fun getCorrectAnswersByQuestionId(questionId: Long): Flow> + + suspend fun setCorrectAnswer(id: Long) + + suspend fun insertAnswer(questionId: Long, answer: String, id: Long? = null) + + suspend fun deleteAnswerById(id: Long) +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AnswerDataSourceImpl.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AnswerDataSourceImpl.kt new file mode 100644 index 000000000..928897c26 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AnswerDataSourceImpl.kt @@ -0,0 +1,50 @@ +package data + +import Task_Management_Dungeon.Database +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import db.Answer +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.withContext + +class AnswerDataSourceImpl(db: Database): AnswerDataSource { + private val queries = db.answerQueries + override suspend fun getAnswerById(id: Long): Answer? { + return withContext(Dispatchers.IO){ + queries.getAnswerById(id).executeAsOneOrNull() + } + } + + override suspend fun getAnswerId(questionId: Long, answer: String): Long? { + return withContext(Dispatchers.IO){ + queries.getAnswerId(questionId,answer).executeAsOneOrNull() + } + } + + override fun getAnswersByQuestionId(id: Long): Flow> { + return queries.getAnswersByQuestionId(id).asFlow().mapToList(Dispatchers.IO) + } + + override fun getCorrectAnswersByQuestionId(questionId: Long): Flow> { + return queries.getCorrectAnswersByQuestionId(questionId).asFlow().mapToList(Dispatchers.IO) + } + + override suspend fun setCorrectAnswer(id: Long) { + return withContext(Dispatchers.IO){ + queries.setCorrectAnswer(id) + } + } + + override suspend fun insertAnswer(questionId: Long, answer: String, id: Long?) { + return withContext(Dispatchers.IO){ + queries.insertAnswer(questionID = questionId, answer = answer, id = id, correct = 0) + } + } + + override suspend fun deleteAnswerById(id: Long) { + return withContext(Dispatchers.IO){ + queries.deleteAnswerById(id) + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AssignmentDataSource.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AssignmentDataSource.kt new file mode 100644 index 000000000..fd194cae5 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AssignmentDataSource.kt @@ -0,0 +1,16 @@ +package data + +import db.Assignment +import kotlinx.coroutines.flow.Flow + +interface AssignmentDataSource { + suspend fun getAssignmentById(id: Long): Assignment? + + suspend fun getAssignmentId(questionId: Long,termA: String, termB: String): Long? + + fun getAssignmentsByQuestionId(id: Long): Flow> + + suspend fun insertAssignment(questionId: Long, termA: String, termB: String, id: Long? = null) + + suspend fun deleteAssignmentById(id: Long) +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AssignmentDataSourceImpl.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AssignmentDataSourceImpl.kt new file mode 100644 index 000000000..f11c017e4 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/AssignmentDataSourceImpl.kt @@ -0,0 +1,40 @@ +package data + +import Task_Management_Dungeon.Database +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import db.Assignment +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.withContext + +class AssignmentDataSourceImpl(db:Database): AssignmentDataSource { + private val queries = db.assignmentQueries + override suspend fun getAssignmentById(id: Long): Assignment? { + return withContext(Dispatchers.IO){ + queries.getAssignmentById(id).executeAsOneOrNull() + } + } + + override suspend fun getAssignmentId(questionId: Long, termA: String, termB: String): Long? { + return withContext(Dispatchers.IO){ + queries.getAssignmentId(questionId, termB = termB, termA = termA ).executeAsOneOrNull() + } + } + + override fun getAssignmentsByQuestionId(id: Long): Flow> { + return queries.getAssignmentByQuestionId(id).asFlow().mapToList(Dispatchers.IO) + } + + override suspend fun insertAssignment(questionId: Long, termA: String, termB: String, id: Long?) { + return withContext(Dispatchers.IO){ + queries.insertAssignment(questionID = questionId, termA = termA, termB = termB, id = id) + } + } + + override suspend fun deleteAssignmentById(id: Long) { + return withContext(Dispatchers.IO){ + queries.deleteAssignmentById(id) + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAnswerDataSource.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAnswerDataSource.kt new file mode 100644 index 000000000..71011196d --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAnswerDataSource.kt @@ -0,0 +1,12 @@ +package data + +import db.Answer + +interface CorrectAnswerDataSource { + suspend fun getCorrectAnswerByQuestionId(questionId: Long): Answer? + + suspend fun insertCorrectAnswer(questionId: Long, answerId:Long) + + suspend fun deleteCorrectAnswerByAnswerId(answerId: Long, questinId: Long) + +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAnswerDataSourceImpl.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAnswerDataSourceImpl.kt new file mode 100644 index 000000000..20effb885 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAnswerDataSourceImpl.kt @@ -0,0 +1,28 @@ +package data + +import Task_Management_Dungeon.Database +import db.Answer +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class CorrectAnswerDataSourceImpl(db:Database): CorrectAnswerDataSource { + private val queries = db.correctAnswerQueries + override suspend fun getCorrectAnswerByQuestionId(questionId: Long): Answer? { + /* return withContext(Dispatchers.IO){ + queries.getCorrectAnswerByQuestionId(questionId).executeAsOneOrNull() + }*/ + TODO() + } + + override suspend fun insertCorrectAnswer(questionId: Long, answerId: Long) { + return withContext(Dispatchers.IO){ + queries.insertCorrectAnswer(questionID = questionId, answerID = answerId) + } + } + + override suspend fun deleteCorrectAnswerByAnswerId(answerId: Long, questinId: Long) { + return withContext(Dispatchers.IO){ + queries.deleteCorrectAnswerByAnswerId(answerId = answerId, questionId = questinId) + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAssignmentDataSource.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAssignmentDataSource.kt new file mode 100644 index 000000000..59b74147b --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAssignmentDataSource.kt @@ -0,0 +1,12 @@ +package data + +import db.Answer +import db.Assignment + +interface CorrectAssignmentDataSource { + suspend fun getCorrectAssignmentByQuestionId(questionId: Long): Assignment? + + suspend fun insertCorrectAssignment(questionId: Long, assignmentId:Long) + + suspend fun deleteCorrectAssignmentByAssignmentId(assignmentId: Long, questinId: Long) +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAssignmentDataSourceImpl.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAssignmentDataSourceImpl.kt new file mode 100644 index 000000000..624096f21 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/CorrectAssignmentDataSourceImpl.kt @@ -0,0 +1,28 @@ +package data + +import Task_Management_Dungeon.Database +import db.Assignment +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class CorrectAssignmentDataSourceImpl(db:Database): CorrectAssignmentDataSource { + private val queries = db.correctAssignmentQueries + override suspend fun getCorrectAssignmentByQuestionId(questionId: Long): Assignment? { + /*return withContext(Dispatchers.IO){ + queries.getCorrectAssignmentByQuestionId(questionId).executeAsOneOrNull() + }*/TODO() + } + + override suspend fun insertCorrectAssignment(questionId: Long, assignmentId: Long) { + return withContext(Dispatchers.IO){ + queries.insertCorrectAssignment(questionID = questionId, assignmentID =assignmentId) + } + } + + override suspend fun deleteCorrectAssignmentByAssignmentId(assignmentId: Long, questinId: Long) { + return withContext(Dispatchers.IO){ + queries.deleteCorrectAssignmentByAssignmentId(assignmentId = assignmentId, questionId = questinId) + } + } + +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/DependencyDataSource.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/DependencyDataSource.kt new file mode 100644 index 000000000..0240397fb --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/DependencyDataSource.kt @@ -0,0 +1,16 @@ +package data + +import db.Assignment +import db.Dependency +import kotlinx.coroutines.flow.Flow + +interface DependencyDataSource { + + suspend fun getDependencyById(id: Long): Dependency? + + fun getAllDependenciesByProjectId(projectId: Long ): Flow> + + suspend fun insertDependency(questionAId: Long, questionBId: Long, projectId: Long, position: Long, dependency: String,id: Long? = null) + + suspend fun deleteDependencyById(id: Long) +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/DependencyDataSourceImpl.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/DependencyDataSourceImpl.kt new file mode 100644 index 000000000..1c0dd5fcf --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/DependencyDataSourceImpl.kt @@ -0,0 +1,41 @@ +package data + +import Task_Management_Dungeon.Database +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import db.Dependency +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.withContext + +class DependencyDataSourceImpl(db: Database): DependencyDataSource { + private val queries = db.dependencyQueries + override suspend fun getDependencyById(id: Long): Dependency? { + return withContext(Dispatchers.IO){ + queries.getDependencyById(id).executeAsOneOrNull() + } + } + + override fun getAllDependenciesByProjectId(projectId: Long): Flow> { + return queries.getAllDependenciesByProjectId(projectId).asFlow().mapToList(Dispatchers.IO) + } + + override suspend fun insertDependency( + questionAId: Long, + questionBId: Long, + projectId: Long, + position: Long, + dependency: String, + id: Long? + ) { + return withContext(Dispatchers.IO){ + queries.insertDependency(id,questionAId,questionBId,projectId,position,dependency) + } + } + + override suspend fun deleteDependencyById(id: Long) { + return withContext(Dispatchers.IO){ + queries.deleteDependencyById(id) + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectDataSource.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectDataSource.kt new file mode 100644 index 000000000..80dd0a1fa --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectDataSource.kt @@ -0,0 +1,19 @@ +package data + +import db.Project +import db.Question +import kotlinx.coroutines.flow.Flow + +interface ProjectDataSource { + + suspend fun getProjectById(id: Long): Project? + + fun getAllProjects(): Flow> + + suspend fun getProjectId(name : String): Long? + + suspend fun insertProject(name: String, id: Long? = null) + + suspend fun deleteProjectById(id: Long) + +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectDataSourceImpl.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectDataSourceImpl.kt new file mode 100644 index 000000000..863f8a5eb --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectDataSourceImpl.kt @@ -0,0 +1,41 @@ +package data + +import Task_Management_Dungeon.Database +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import db.Project +import db.Question +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.withContext + +class ProjectDataSourceImpl(db: Database): ProjectDataSource { + private val queries = db.projectQueries + override suspend fun getProjectById(id: Long): Project? { + return withContext(Dispatchers.IO){ + queries.getProjectById(id).executeAsOneOrNull() + } + } + + override fun getAllProjects(): Flow> { + return queries.getAllProjects().asFlow().mapToList(Dispatchers.IO) + } + + override suspend fun getProjectId(name: String): Long? { + return withContext(Dispatchers.IO){ + queries.getProjectId(name).executeAsOneOrNull() + } + } + + override suspend fun insertProject(name: String, id: Long?) { + return withContext(Dispatchers.IO){ + queries.insertProject(id,name) + } + } + + override suspend fun deleteProjectById(id: Long) { + return withContext(Dispatchers.IO){ + queries.deleteProjectById(id) + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectQuestionDataSource.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectQuestionDataSource.kt new file mode 100644 index 000000000..ead4da40e --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectQuestionDataSource.kt @@ -0,0 +1,13 @@ +package data + +import db.Question +import kotlinx.coroutines.flow.Flow + +interface ProjectQuestionDataSource { + fun getAllQuestionsByProjectId(projectId: Long): Flow> + + + suspend fun insertProjectQuestion(projectId: Long, questionId: Long) + + suspend fun deleteProjectQuestionByQuestionId(projectId: Long, questionId:Long) +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectQuestionDataSourceImpl.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectQuestionDataSourceImpl.kt new file mode 100644 index 000000000..4a9847d8e --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/ProjectQuestionDataSourceImpl.kt @@ -0,0 +1,29 @@ +package data + +import Task_Management_Dungeon.Database +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import db.Question +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.withContext + + +class ProjectQuestionDataSourceImpl(db: Database): ProjectQuestionDataSource { + private val queries = db.projectQuestionQueries + override fun getAllQuestionsByProjectId(projectId: Long): Flow> { + return queries.getAllQuestionsByProjectId(projectId).asFlow().mapToList(Dispatchers.IO) + } + + override suspend fun insertProjectQuestion(projectId: Long, questionId: Long) { + return withContext(Dispatchers.IO){ + queries.insertProjectQuestion(projectId,questionId) + } + } + + override suspend fun deleteProjectQuestionByQuestionId(projectId: Long, questionId: Long) { + return withContext(Dispatchers.IO){ + queries.deleteProjectQuestionByQuestionId(questionId, projectId) + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionDataSource.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionDataSource.kt new file mode 100644 index 000000000..6e99a3a67 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionDataSource.kt @@ -0,0 +1,17 @@ +package data + +import db.Question +import kotlinx.coroutines.flow.Flow + +interface QuestionDataSource { + + suspend fun getQuestionById(id: Long): Question? + + fun getAllQuestions(): Flow> + + suspend fun getQuestionId(description: String,explanation: String,points: Long,pointsToPass: Long): Long? + suspend fun deleteQuestionById(id: Long) + + suspend fun insertQuestion(description: String, explanation: String, points: Long, pointsToPass: Long, type: String, id: Long? = null) + +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionDataSourceImpl.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionDataSourceImpl.kt new file mode 100644 index 000000000..4856e0d42 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionDataSourceImpl.kt @@ -0,0 +1,51 @@ +package data + +import Task_Management_Dungeon.Database +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import db.Question +import db.QuestionQueries +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.withContext + +class QuestionDataSourceImpl(db : Database) : QuestionDataSource { + private val queries = db.questionQueries + override suspend fun getQuestionById(id: Long): Question? { + return withContext(Dispatchers.IO){ + queries.getQuestionById(id).executeAsOneOrNull() + } + } + + override fun getAllQuestions(): Flow> { + return queries.getAllQuestions().asFlow().mapToList(Dispatchers.IO) + } + + override suspend fun getQuestionId(description: String, explanation: String, points: Long, pointsToPass: Long): Long? { + return withContext(Dispatchers.IO){ + queries.getQuestionId(description,explanation,points,pointsToPass).executeAsOneOrNull() + } + + } + + + override suspend fun deleteQuestionById(id: Long) { + return withContext(Dispatchers.IO){ + queries.deleteQuestionById(id) + } + } + + override suspend fun insertQuestion( + description: String, + explanation: String, + points: Long, + pointsToPass: Long, + type: String, + id: Long? + ) { + return withContext(Dispatchers.IO){ + queries.insertQuestion(id,description,explanation,points,pointsToPass,type) + } + + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionTagDataSource.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionTagDataSource.kt new file mode 100644 index 000000000..6d9953a77 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionTagDataSource.kt @@ -0,0 +1,14 @@ +package data + +import db.Question +import db.Tag +import kotlinx.coroutines.flow.Flow + +interface QuestionTagDataSource { + + fun getTagsByQuestionId(questionId: Long): Flow> + + suspend fun insertQuestionTag(questionId: Long, tagId: Long) + + suspend fun deleteQuestionTag(questionId: Long, tagId: Long) +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionTagDataSourceImpl.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionTagDataSourceImpl.kt new file mode 100644 index 000000000..a0f0181b4 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/QuestionTagDataSourceImpl.kt @@ -0,0 +1,29 @@ +package data + +import Task_Management_Dungeon.Database +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import db.Question +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.withContext + +class QuestionTagDataSourceImpl(db : Database): QuestionTagDataSource { + private val queries = db.questionTagQueries + override fun getTagsByQuestionId(questionId: Long): Flow> { + return queries.getTagsByQuestionId(questionId).asFlow().mapToList(Dispatchers.IO) + } + + + override suspend fun insertQuestionTag(questionId: Long, tagId: Long) { + return withContext(Dispatchers.IO){ + queries.insertQuestionTag(questionID = questionId, tagID = tagId ) + } + } + + override suspend fun deleteQuestionTag(questionId: Long, tagId: Long) { + return withContext(Dispatchers.IO){ + queries.deleteQuestionTag(questionId = questionId, tagId = tagId ) + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/TagDataSource.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/TagDataSource.kt new file mode 100644 index 000000000..b835fdaf6 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/TagDataSource.kt @@ -0,0 +1,19 @@ +package data + +import db.Question +import db.Tag +import kotlinx.coroutines.flow.Flow + +interface TagDataSource { + + suspend fun getTagById(id: Long): Tag? + + fun getTagsByQuestionId(questionId: Long): Flow> + suspend fun getTagByName(name: String) : Long? + + fun getAllTags(): Flow> + + suspend fun insertTag(tag: String, id: Long? = null) + + suspend fun deleteTagById(id: Long) +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/TagDataSourceImpl.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/TagDataSourceImpl.kt new file mode 100644 index 000000000..8c8488552 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/data/TagDataSourceImpl.kt @@ -0,0 +1,44 @@ +package data + +import Task_Management_Dungeon.Database +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import db.Tag +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.withContext + +class TagDataSourceImpl(db : Database): TagDataSource { + private val queries = db.tagQueries + override suspend fun getTagById(id: Long): Tag? { + return withContext(Dispatchers.IO){ + queries.getTagById(id).executeAsOneOrNull() + } + } + + override fun getTagsByQuestionId(questionId: Long): Flow> { + return queries.getTagsByQuestionId(questionId).asFlow().mapToList(Dispatchers.IO) + } + + override suspend fun getTagByName(name: String): Long? { + return withContext(Dispatchers.IO){ + queries.getTagByName(name).executeAsOneOrNull() + } + } + + override fun getAllTags(): Flow> { + return queries.getAllTags().asFlow().mapToList(Dispatchers.IO) + } + + override suspend fun insertTag(tag: String, id: Long?) { + return withContext(Dispatchers.IO){ + queries.insertTag(tag = tag, id = id ) + } + } + + override suspend fun deleteTagById(id: Long) { + return withContext(Dispatchers.IO){ + queries.deleteTagById(id) + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/DataBaseCommunication.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/DataBaseCommunication.kt new file mode 100644 index 000000000..478475fcd --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/DataBaseCommunication.kt @@ -0,0 +1,136 @@ +package databaseInteraction + +import classes.* +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking + +/** + * Functions to interact with the database + */ +object DataBaseCommunication { + /** + * returns answers from the db + * @param questionId questionID to get the answers for + */ + private suspend fun getAnswersToQuestionId(questionId: Long): List { + val answerData = Provider.provideAnswerDataSource(Driver.createDriver()) + val answerDataList = answerData.getAnswersByQuestionId(questionId).firstOrNull() + val answers = mutableListOf() + answerDataList!!.forEach{answer -> + answers.add(answer.answer) + } + return answers + } + + /** + * return the correct answers to a question + * @param questionId ID of the question + */ + private suspend fun getCorrectAnswersByQuestionId(questionId: Long): List { + val answerData = Provider.provideAnswerDataSource(Driver.createDriver()) + val answerList = answerData.getCorrectAnswersByQuestionId(questionId).firstOrNull() + val answers = mutableListOf() + answerList!!.forEach{answer -> + answers.add(answer.answer) + } + return answers + } + + /** + * returns the assignments to a question + * @param questionId ID of the question + */ + private fun getAssignmentsToQuestionId(questionId: Long): List { + val assignmentData = Provider.provideAssignmentDataSource(Driver.createDriver()) + val assignmentList = runBlocking { assignmentData.getAssignmentsByQuestionId(questionId).firstOrNull() } + val assignments = mutableListOf() + assignmentList?.forEach{ + assignments.add(Assignment(it.termA!!,it.termB!!)) + } + return assignments + } + + /** + * returns a list of questions from a dependency (removes doubles) + * @param dependencies List of dependencies + */ + fun getQuestionsFromDependencyList(dependencies:List?): List{ + val questions = mutableListOf() + val questionData = runBlocking { Provider.provideQuestionDataSource(Driver.createDriver()) } + val addedId = mutableListOf() + dependencies?.forEach{ dep -> + runBlocking { + if(!addedId.contains(dep.questionAID)){ + questions.add(getQuestionAsClass(questionData.getQuestionById(dep.questionAID)!!)!!) + addedId.add(dep.questionAID) + } + if(!addedId.contains(dep.questionBID)){ + questions.add(getQuestionAsClass(questionData.getQuestionById(dep.questionBID)!!)!!) + addedId.add(dep.questionBID) + } + } + } + return questions + } + + /** + * transforms a question from the db to a question class + * @param question question to transform + */ + suspend fun getQuestionAsClass(question: db.Question): Question? { + when (question.type) { + "SINGLE_CHOICE_QUESTION" -> { + val answers = getAnswersToQuestionId(question.id) + val correctAnswers = getCorrectAnswersByQuestionId(question.id) + val correctAnswerIndices= mutableListOf () + answers.forEachIndexed { index, answer -> + if (correctAnswers.contains(answer)) { + correctAnswerIndices.add(index) + } + } + return SingleChoiceQuestion( + id = question.id, + description = question.description, + explanation = question.explanation, + points = question.points.toInt(), + pointsToPass = question.pointsToPass.toInt(), + answers = answers, + correctAnswerIndex =correctAnswerIndices[0] + ) + } + + "MULTIPLE_CHOICE_QUESTION" -> { + val answers = getAnswersToQuestionId(question.id) + val correctAnswers = getCorrectAnswersByQuestionId(question.id) + val correctAnswerIndices= mutableListOf () + answers.forEachIndexed { index, answer -> + if (correctAnswers.contains(answer)) { + correctAnswerIndices.add(index) + } + } + return MultipleChoiceQuestion( + id = question.id, + description = question.description, + explanation = question.explanation, + points = question.points.toInt(), + pointsToPass = question.pointsToPass.toInt(), + answers = answers, + correctAnswerIndices =correctAnswerIndices + ) + } + + "ASSIGN_QUESTION" -> { + return AssignQuestion( + id = question.id, + description = question.description, + explanation = question.explanation, + points = question.points.toInt(), + pointsToPass = question.pointsToPass.toInt(), + assignments = getAssignmentsToQuestionId(questionId = question.id) + ) + } + + } + return null + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/Driver.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/Driver.kt new file mode 100644 index 000000000..d42d93390 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/Driver.kt @@ -0,0 +1,24 @@ +package databaseInteraction + +import Task_Management_Dungeon.Database +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver +import java.io.File + +object Driver { + fun createDriver(): SqlDriver { + val driver: SqlDriver = JdbcSqliteDriver("jdbc:sqlite:task_manager.db") + if (!File("task_manager.db").exists()) { + Database.Schema.create(driver) + } + return driver + } + + fun provideDriver(): SqlDriver{ + return JdbcSqliteDriver( + "jdbc:sqlite:task_manager.db", + schema = Database.Schema) + } + + +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/Provider.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/Provider.kt new file mode 100644 index 000000000..0d1ee5eda --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/Provider.kt @@ -0,0 +1,45 @@ +package databaseInteraction + +import Task_Management_Dungeon.Database +import app.cash.sqldelight.db.SqlDriver +import data.* +/** + * Provides data Sources to interact with the Database + */ +object Provider { + + fun provideQuestionDataSource(driver : SqlDriver): QuestionDataSource { + return QuestionDataSourceImpl(Database(driver)) + } + + fun provideAnswerDataSource(driver : SqlDriver): AnswerDataSource { + return AnswerDataSourceImpl(Database(driver)) + } + + fun provideAssignmentDataSource(driver : SqlDriver): AssignmentDataSource { + return AssignmentDataSourceImpl(Database(driver)) + } + fun provideCorrectAnswerDataSource(driver : SqlDriver): CorrectAnswerDataSource { + return CorrectAnswerDataSourceImpl(Database(driver)) + } + fun provideCorrectAssignmentDataSource(driver : SqlDriver): CorrectAssignmentDataSource{ + return CorrectAssignmentDataSourceImpl(Database(driver)) + } + fun provideDependencyDataSource(driver : SqlDriver): DependencyDataSource { + return DependencyDataSourceImpl(Database(driver)) + } + fun provideProjectQuestionDataSource(driver : SqlDriver): ProjectQuestionDataSource { + return ProjectQuestionDataSourceImpl(Database(driver)) + } + fun provideProjectDataSource(driver : SqlDriver): ProjectDataSource { + return ProjectDataSourceImpl(Database(driver)) + } + fun provideQuestionTagDataSource(driver : SqlDriver): QuestionTagDataSource { + return QuestionTagDataSourceImpl(Database(driver)) + } + fun provideTagDataSource(driver : SqlDriver): TagDataSource { + return TagDataSourceImpl(Database(driver)) + } + + +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/TaskFileWriter.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/TaskFileWriter.kt new file mode 100644 index 000000000..eb5fe53da --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/databaseInteraction/TaskFileWriter.kt @@ -0,0 +1,169 @@ +package databaseInteraction + +import classes.* +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import java.io.File + + +/** + * Writes a Taskfile + */ +object TaskFileWriter { + + /** + * Writes a taskfile from a projectId + * @param projectId ID of the project + * @param filename name of the File + */ + fun writeProjectToFile(projectId: Long, filename: String) { + val projectData = Provider.provideProjectDataSource(Driver.createDriver()) + val dependencyData = Provider.provideDependencyDataSource(Driver.createDriver()) + val project = runBlocking { projectData.getProjectById(projectId) } + val dependencies = runBlocking { dependencyData.getAllDependenciesByProjectId(projectId).firstOrNull() } + // create Questionlist + val questions = DataBaseCommunication.getQuestionsFromDependencyList(dependencies) + File("$filename.dng").createNewFile() + //ProjektAnleitung zum Schreiben + try { + File("$filename.dng").appendText("// $project") + } catch (e: Exception) { + println("An error occurred: ${e.message}") + } + questions.forEach { + writeQuestion(it, filename) + } + writeGraph(dependencies!!, filename, project!!.name) + writeSzenarioDefinitions(filename) + } + + private fun writeQuestion(question: Question, filename: String) { + try { + when (question) { + is SingleChoiceQuestion -> { + File("$filename.dng").appendText( + "\nsingle_choice_task t${question.id} {" + + "\n\tdescription: \"${question.description}\"," + + "\n\texplanation: \"${question.explanation}\"," + + "\n\tpoints: ${question.points}," + + "\n\tpoints_to_pass: ${question.pointsToPass}," + + "\n\tanswers: [" + ) + question.answers.forEachIndexed { index, answer -> + if (index == 0) { + File("$filename.dng").appendText("\n\t\"${answer.trim()}\"") + } else { + File("$filename.dng").appendText(",\n\t\"${answer.trim()}\"") + } + } + File("$filename.dng").appendText("],\n\tcorrect_answer_index: ${question.correctAnswerIndex}\n}\n") + } + + is MultipleChoiceQuestion -> { + File("$filename.dng").appendText( + "\nmultiple_choice_task t${question.id} {" + + "\n\tdescription: \"${question.description}\"," + + "\n\texplanation: \"${question.explanation}\"," + + "\n\tpoints: ${question.points}," + + "\n\tpoints_to_pass: ${question.pointsToPass}," + + "\n\tanswers: [" + ) + question.answers.forEachIndexed { index, answer -> + if (index == 0) { + File("$filename.dng").appendText("\n\t\"${answer.trim()}\"") + } else { + File("$filename.dng").appendText("\n\t\"${answer.trim()}\"") + } + } + File("$filename.dng").appendText("],\n\tcorrect_answer_index: [") + question.correctAnswerIndices.forEachIndexed { index, correctAnswer -> + if (index == 0) { + File("$filename.dng").appendText("$correctAnswer") + } else { + File("$filename.dng").appendText(", $correctAnswer") + } + } + File("$filename.dng").appendText("] \n}\n") + } + + is AssignQuestion -> { + File("$filename.dng").appendText( + "\nassign_task t${question.id} {" + + "\n\tdescription: \"${question.description}\"," + + "\n\texplanation: \"${question.explanation}\"," + + "\n\tpoints: ${question.points}," + + "\n\tpoints_to_pass: ${question.pointsToPass}," + + "\n\tsolution: <" + ) + question.assignments.forEachIndexed { index, assignment -> + if (index == 0) { + if (assignment.termA == "_") { + File("$filename.dng").appendText("\n\t\t[_, \"${assignment.termB.trim()}\"]") + } else { + if (assignment.termB == "_") { + File("$filename.dng").appendText("\n\t\t[\"${assignment.termA.trim()}\", _]") + } else { + File("$filename.dng").appendText("\n\t\t[\"${assignment.termA.trim()}\", \"${assignment.termB.trim()}\"]") + } + } + } else { + if (assignment.termA == "_") { + File("$filename.dng").appendText(",\n\t\t[_, \"${assignment.termB.trim()}\"]") + } else { + if (assignment.termB == "_") { + File("$filename.dng").appendText(",\n\t\t[\"${assignment.termA.trim()}\", _]") + } else { + File("$filename.dng").appendText(",\n\t\t[\"${assignment.termA.trim()}\", \"${assignment.termB.trim()}\"]") + } + } + } + } + File("$filename.dng").appendText("\n\t>\n}\n") + } + } + } catch (e: Exception) { + println("An error occurred: ${e.message}") + } + + } + + private fun writeGraph(dependencies: List, filename: String, projectName :String) { + File("$filename.dng").appendText("\ngraph p${dependencies[0].projectID}_graph {") + + dependencies.forEach { dependency -> + File("$filename.dng").appendText("\n\tt${dependency.questionAID} -> t${dependency.questionBID}") + when (dependency.dependency) { + "Sequenz" -> { + File("$filename.dng").appendText(" [type=seq];") + } + + "Pflicht Unteraufgabe" -> { + File("$filename.dng").appendText(" [type=st_m];") + } + + "Optionale Unteraufgabe" -> { + File("$filename.dng").appendText(" [type=st_o];") + } + + "Bei falscher Antwort" -> { + File("$filename.dng").appendText(" [type=c_f];") + } + + "Bei richtiger Antwort" -> { + File("$filename.dng").appendText(" [type=c_c];") + } + } + } + File("$filename.dng").appendText( + "\n}\n\ndungeon_config $projectName {" + + "\n\t\tdependency_graph: p${dependencies[0].projectID}_graph" + + "\n}\n\n" + ) + } + + private fun writeSzenarioDefinitions(filename: String){ + File("szenario_definitions").forEachLine { line -> + File("$filename.dng").appendText( line +"\n") + } + } +} diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Add.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Add.kt new file mode 100644 index 000000000..2f2047011 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Add.kt @@ -0,0 +1,60 @@ +package icon + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + +@Composable +fun addIcon(c : Color): ImageVector { + return remember { + ImageVector.Builder( + name = "add", + defaultWidth = 40.0.dp, + defaultHeight = 40.0.dp, + viewportWidth = 40.0f, + viewportHeight = 40.0f + ).apply { + path( + fill = SolidColor(c), + fillAlpha = 1f, + stroke = null, + strokeAlpha = 1f, + strokeLineWidth = 1.0f, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 1f, + pathFillType = PathFillType.NonZero + ) { + moveTo(20f, 31.458f) + quadToRelative(-0.542f, 0f, -0.917f, -0.396f) + quadToRelative(-0.375f, -0.395f, -0.375f, -0.937f) + verticalLineToRelative(-8.833f) + horizontalLineTo(9.875f) + quadToRelative(-0.583f, 0f, -0.958f, -0.375f) + reflectiveQuadTo(8.542f, 20f) + quadToRelative(0f, -0.583f, 0.375f, -0.958f) + reflectiveQuadToRelative(0.958f, -0.375f) + horizontalLineToRelative(8.833f) + verticalLineTo(9.833f) + quadToRelative(0f, -0.541f, 0.375f, -0.916f) + reflectiveQuadTo(20f, 8.542f) + quadToRelative(0.542f, 0f, 0.938f, 0.375f) + quadToRelative(0.395f, 0.375f, 0.395f, 0.916f) + verticalLineToRelative(8.834f) + horizontalLineToRelative(8.792f) + quadToRelative(0.583f, 0f, 0.958f, 0.395f) + quadToRelative(0.375f, 0.396f, 0.375f, 0.938f) + quadToRelative(0f, 0.542f, -0.375f, 0.917f) + reflectiveQuadToRelative(-0.958f, 0.375f) + horizontalLineToRelative(-8.792f) + verticalLineToRelative(8.833f) + quadToRelative(0f, 0.542f, -0.395f, 0.937f) + quadToRelative(-0.396f, 0.396f, -0.938f, 0.396f) + close() + } + }.build() + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/AddFile.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/AddFile.kt new file mode 100644 index 000000000..50e2f3911 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/AddFile.kt @@ -0,0 +1,87 @@ +package icon + + +import androidx.compose.ui.graphics.Color +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + + +@Composable +fun addFileIcon(c: Color): ImageVector { + return remember { + ImageVector.Builder( + name = "file_open", + defaultWidth = 128.dp, + defaultHeight = 128.dp, + viewportWidth = 40.0f, + viewportHeight = 40.0f + ).apply { + path( + fill = SolidColor(c), + fillAlpha = 1f, + stroke = null, + strokeAlpha = 1f, + strokeLineWidth = 1.0f, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 1f, + pathFillType = PathFillType.NonZero + ) { + moveTo(9.542f, 36.375f) + quadToRelative(-1.042f, 0f, -1.834f, -0.771f) + quadToRelative(-0.791f, -0.771f, -0.791f, -1.854f) + verticalLineTo(6.25f) + quadToRelative(0f, -1.083f, 0.791f, -1.854f) + quadToRelative(0.792f, -0.771f, 1.834f, -0.771f) + horizontalLineTo(22.25f) + quadToRelative(0.542f, 0f, 1.042f, 0.208f) + quadToRelative(0.5f, 0.209f, 0.875f, 0.584f) + lineToRelative(8.125f, 8.125f) + quadToRelative(0.375f, 0.375f, 0.583f, 0.854f) + quadToRelative(0.208f, 0.479f, 0.208f, 1.021f) + verticalLineToRelative(9.958f) + horizontalLineToRelative(-2.625f) + verticalLineToRelative(-9.417f) + horizontalLineToRelative(-7.416f) + quadToRelative(-0.542f, 0f, -0.917f, -0.375f) + reflectiveQuadToRelative(-0.375f, -0.958f) + verticalLineTo(6.25f) + horizontalLineTo(9.542f) + verticalLineToRelative(27.5f) + horizontalLineToRelative(16.5f) + verticalLineToRelative(2.625f) + close() + moveToRelative(26.083f, -0.542f) + lineToRelative(-4.292f, -4.291f) + verticalLineToRelative(3.416f) + quadToRelative(0f, 0.584f, -0.395f, 0.959f) + quadToRelative(-0.396f, 0.375f, -0.938f, 0.375f) + quadToRelative(-0.542f, 0f, -0.917f, -0.375f) + reflectiveQuadToRelative(-0.375f, -0.959f) + verticalLineToRelative(-6.666f) + quadToRelative(0f, -0.542f, 0.354f, -0.917f) + quadTo(29.417f, 27f, 30f, 27f) + horizontalLineToRelative(6.625f) + quadToRelative(0.583f, 0f, 0.958f, 0.396f) + reflectiveQuadToRelative(0.375f, 0.937f) + quadToRelative(0f, 0.542f, -0.375f, 0.917f) + reflectiveQuadToRelative(-0.958f, 0.375f) + horizontalLineToRelative(-3.458f) + lineTo(37.5f, 34f) + quadToRelative(0.417f, 0.417f, 0.417f, 0.938f) + quadToRelative(0f, 0.52f, -0.375f, 0.895f) + quadToRelative(-0.417f, 0.417f, -0.959f, 0.417f) + quadToRelative(-0.541f, 0f, -0.958f, -0.417f) + close() + moveTo(9.542f, 33.75f) + verticalLineTo(6.25f) + verticalLineToRelative(27.5f) + close() + } + }.build() + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Assign_Task.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Assign_Task.kt new file mode 100644 index 000000000..6bdb968fa --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Assign_Task.kt @@ -0,0 +1,82 @@ +package icon + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + +@Composable +fun assignIcon(c : Color): ImageVector { + return remember { + ImageVector.Builder( + name = "compare_arrows", + defaultWidth = 128.0.dp, + defaultHeight = 128.0.dp, + viewportWidth = 40.0f, + viewportHeight = 40.0f + ).apply { + path( + fill = SolidColor(c), + fillAlpha = 1f, + stroke = null, + strokeAlpha = 1f, + strokeLineWidth = 1.0f, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 1f, + pathFillType = PathFillType.NonZero + ) { + moveTo(25.792f, 21.875f) + lineToRelative(-6.167f, -6.125f) + quadToRelative(-0.208f, -0.208f, -0.292f, -0.437f) + quadToRelative(-0.083f, -0.23f, -0.083f, -0.48f) + quadToRelative(0f, -0.25f, 0.083f, -0.479f) + quadToRelative(0.084f, -0.229f, 0.292f, -0.437f) + lineToRelative(6.167f, -6.167f) + quadToRelative(0.375f, -0.375f, 0.896f, -0.375f) + quadToRelative(0.52f, 0f, 0.937f, 0.417f) + quadToRelative(0.375f, 0.375f, 0.375f, 0.916f) + quadToRelative(0f, 0.542f, -0.375f, 0.959f) + lineToRelative(-3.875f, 3.875f) + horizontalLineToRelative(11.417f) + quadToRelative(0.541f, 0f, 0.916f, 0.375f) + reflectiveQuadToRelative(0.375f, 0.916f) + quadToRelative(0f, 0.584f, -0.375f, 0.959f) + reflectiveQuadToRelative(-0.916f, 0.375f) + horizontalLineTo(23.75f) + lineToRelative(3.917f, 3.916f) + quadToRelative(0.375f, 0.417f, 0.375f, 0.917f) + reflectiveQuadToRelative(-0.417f, 0.917f) + quadToRelative(-0.417f, 0.375f, -0.937f, 0.375f) + quadToRelative(-0.521f, 0f, -0.896f, -0.417f) + close() + moveTo(12.417f, 32.208f) + quadToRelative(0.375f, 0.375f, 0.916f, 0.396f) + quadToRelative(0.542f, 0.021f, 0.917f, -0.396f) + lineToRelative(6.167f, -6.166f) + quadToRelative(0.208f, -0.209f, 0.291f, -0.417f) + quadToRelative(0.084f, -0.208f, 0.084f, -0.5f) + quadToRelative(0f, -0.25f, -0.084f, -0.479f) + quadToRelative(-0.083f, -0.229f, -0.291f, -0.438f) + lineToRelative(-6.167f, -6.166f) + quadToRelative(-0.375f, -0.375f, -0.917f, -0.354f) + quadToRelative(-0.541f, 0.02f, -0.916f, 0.395f) + reflectiveQuadToRelative(-0.375f, 0.917f) + quadToRelative(0f, 0.542f, 0.375f, 0.917f) + lineToRelative(3.875f, 3.916f) + horizontalLineTo(4.875f) + quadToRelative(-0.542f, 0f, -0.937f, 0.375f) + quadToRelative(-0.396f, 0.375f, -0.396f, 0.917f) + quadToRelative(0f, 0.583f, 0.396f, 0.958f) + quadToRelative(0.395f, 0.375f, 0.937f, 0.375f) + horizontalLineToRelative(11.417f) + lineToRelative(-3.917f, 3.917f) + quadToRelative(-0.375f, 0.375f, -0.375f, 0.896f) + reflectiveQuadToRelative(0.417f, 0.937f) + close() + } + }.build() + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Back.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Back.kt new file mode 100644 index 000000000..54ba3122c --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Back.kt @@ -0,0 +1,60 @@ +package icon + +import androidx.compose.ui.graphics.Color +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + + +@Composable +fun backIcon(c: Color): ImageVector { + return remember { + ImageVector.Builder( + name = "arrow_back", + defaultWidth = 128.dp, + defaultHeight = 128.dp, + viewportWidth = 40.0f, + viewportHeight = 40.0f + ).apply { + path( + fill = SolidColor(c), + fillAlpha = 1f, + stroke = null, + strokeAlpha = 1f, + strokeLineWidth = 1.0f, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 1f, + pathFillType = PathFillType.NonZero + ) { + moveTo(18.542f, 32.208f) + lineTo(7.25f, 20.917f) + quadToRelative(-0.208f, -0.209f, -0.292f, -0.438f) + quadToRelative(-0.083f, -0.229f, -0.083f, -0.479f) + quadToRelative(0f, -0.25f, 0.083f, -0.479f) + quadToRelative(0.084f, -0.229f, 0.292f, -0.438f) + lineTo(18.583f, 7.75f) + quadToRelative(0.375f, -0.333f, 0.896f, -0.333f) + reflectiveQuadToRelative(0.938f, 0.375f) + quadToRelative(0.375f, 0.416f, 0.375f, 0.937f) + quadToRelative(0f, 0.521f, -0.375f, 0.938f) + lineToRelative(-9.042f, 9f) + horizontalLineToRelative(19.917f) + quadToRelative(0.541f, 0f, 0.916f, 0.395f) + quadToRelative(0.375f, 0.396f, 0.375f, 0.938f) + quadToRelative(0f, 0.542f, -0.375f, 0.917f) + reflectiveQuadToRelative(-0.916f, 0.375f) + horizontalLineTo(11.375f) + lineToRelative(9.083f, 9.083f) + quadToRelative(0.334f, 0.375f, 0.334f, 0.896f) + reflectiveQuadToRelative(-0.375f, 0.937f) + quadToRelative(-0.417f, 0.375f, -0.938f, 0.375f) + quadToRelative(-0.521f, 0f, -0.937f, -0.375f) + close() + } + }.build() + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Delete.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Delete.kt new file mode 100644 index 000000000..3ab7b4d89 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Delete.kt @@ -0,0 +1,90 @@ +package icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + + +@Composable +fun deleteIcon(c: Color): ImageVector { + return remember { + ImageVector.Builder( + name = "delete", + defaultWidth = 40.0.dp, + defaultHeight = 40.0.dp, + viewportWidth = 40.0f, + viewportHeight = 40.0f + ).apply { + path( + fill = SolidColor(c), + fillAlpha = 1f, + stroke = null, + strokeAlpha = 1f, + strokeLineWidth = 1.0f, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 1f, + pathFillType = PathFillType.NonZero + ) { + moveTo(11.208f, 34.708f) + quadToRelative(-1.041f, 0f, -1.833f, -0.77f) + quadToRelative(-0.792f, -0.771f, -0.792f, -1.855f) + verticalLineTo(9.25f) + horizontalLineTo(8.25f) + quadToRelative(-0.583f, 0f, -0.958f, -0.396f) + reflectiveQuadToRelative(-0.375f, -0.937f) + quadToRelative(0f, -0.542f, 0.375f, -0.917f) + reflectiveQuadToRelative(0.958f, -0.375f) + horizontalLineToRelative(6.458f) + quadToRelative(0f, -0.583f, 0.375f, -0.958f) + reflectiveQuadToRelative(0.959f, -0.375f) + horizontalLineTo(24f) + quadToRelative(0.583f, 0f, 0.938f, 0.375f) + quadToRelative(0.354f, 0.375f, 0.354f, 0.958f) + horizontalLineToRelative(6.5f) + quadToRelative(0.541f, 0f, 0.937f, 0.375f) + reflectiveQuadToRelative(0.396f, 0.917f) + quadToRelative(0f, 0.583f, -0.396f, 0.958f) + reflectiveQuadToRelative(-0.937f, 0.375f) + horizontalLineToRelative(-0.334f) + verticalLineToRelative(22.833f) + quadToRelative(0f, 1.084f, -0.791f, 1.855f) + quadToRelative(-0.792f, 0.77f, -1.875f, 0.77f) + close() + moveToRelative(0f, -25.458f) + verticalLineToRelative(22.833f) + horizontalLineToRelative(17.584f) + verticalLineTo(9.25f) + close() + moveToRelative(4.125f, 18.042f) + quadToRelative(0f, 0.583f, 0.396f, 0.958f) + reflectiveQuadToRelative(0.938f, 0.375f) + quadToRelative(0.541f, 0f, 0.916f, -0.375f) + reflectiveQuadToRelative(0.375f, -0.958f) + verticalLineTo(14f) + quadToRelative(0f, -0.542f, -0.375f, -0.937f) + quadToRelative(-0.375f, -0.396f, -0.916f, -0.396f) + quadToRelative(-0.584f, 0f, -0.959f, 0.396f) + quadToRelative(-0.375f, 0.395f, -0.375f, 0.937f) + close() + moveToRelative(6.709f, 0f) + quadToRelative(0f, 0.583f, 0.396f, 0.958f) + quadToRelative(0.395f, 0.375f, 0.937f, 0.375f) + reflectiveQuadToRelative(0.937f, -0.375f) + quadToRelative(0.396f, -0.375f, 0.396f, -0.958f) + verticalLineTo(14f) + quadToRelative(0f, -0.542f, -0.396f, -0.937f) + quadToRelative(-0.395f, -0.396f, -0.937f, -0.396f) + reflectiveQuadToRelative(-0.937f, 0.396f) + quadToRelative(-0.396f, 0.395f, -0.396f, 0.937f) + close() + moveTo(11.208f, 9.25f) + verticalLineToRelative(22.833f) + verticalLineTo(9.25f) + close() + } + }.build() + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Edit.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Edit.kt new file mode 100644 index 000000000..91798198f --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Edit.kt @@ -0,0 +1,66 @@ +package icon + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + + +@Composable +fun editIcon(c: Color): ImageVector { + return remember { + ImageVector.Builder( + name = "edit", + defaultWidth = 40.0.dp, + defaultHeight = 40.0.dp, + viewportWidth = 40.0f, + viewportHeight = 40.0f + ).apply { + path( + fill = SolidColor(c), + fillAlpha = 1f, + stroke = null, + strokeAlpha = 1f, + strokeLineWidth = 1.0f, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 1f, + pathFillType = PathFillType.NonZero + ) { + moveTo(7.958f, 32.042f) + horizontalLineToRelative(1.875f) + lineTo(27f, 14.875f) + lineTo(25.083f, 13f) + lineTo(7.958f, 30.167f) + close() + moveTo(32.583f, 13f) + lineToRelative(-5.625f, -5.583f) + lineToRelative(1.875f, -1.875f) + quadToRelative(0.75f, -0.75f, 1.875f, -0.75f) + reflectiveQuadToRelative(1.875f, 0.791f) + lineToRelative(1.875f, 1.875f) + quadToRelative(0.75f, 0.75f, 0.75f, 1.855f) + quadToRelative(0f, 1.104f, -0.75f, 1.854f) + close() + moveTo(6.625f, 34.667f) + quadToRelative(-0.583f, 0f, -0.958f, -0.375f) + reflectiveQuadToRelative(-0.375f, -0.917f) + verticalLineToRelative(-3.792f) + quadToRelative(0f, -0.25f, 0.104f, -0.479f) + quadToRelative(0.104f, -0.229f, 0.312f, -0.437f) + lineTo(25.125f, 9.25f) + lineToRelative(5.625f, 5.625f) + lineToRelative(-19.458f, 19.417f) + quadToRelative(-0.167f, 0.208f, -0.417f, 0.291f) + quadToRelative(-0.25f, 0.084f, -0.5f, 0.084f) + close() + moveToRelative(19.417f, -20.75f) + lineTo(25.083f, 13f) + lineTo(27f, 14.875f) + close() + } + }.build() + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Exit.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Exit.kt new file mode 100644 index 000000000..d4a97dba4 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Exit.kt @@ -0,0 +1,81 @@ +package icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.StrokeJoin +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.group +import androidx.compose.ui.graphics.PathFillType +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + +@Composable +fun exitIcon(c: Color): ImageVector { + return remember { + ImageVector.Builder( + name = "end", + defaultWidth = 128.0.dp, + defaultHeight = 128.0.dp, + viewportWidth = 40.0f, + viewportHeight = 40.0f + ).apply { + path( + fill = SolidColor(c), + fillAlpha = 1f, + stroke = null, + strokeAlpha = 1f, + strokeLineWidth = 1.0f, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 1f, + pathFillType = PathFillType.NonZero + ) { + moveTo(26.417f, 26.5f) + quadToRelative(-0.375f, -0.417f, -0.375f, -0.958f) + quadToRelative(0f, -0.542f, 0.375f, -0.917f) + lineToRelative(3.25f, -3.25f) + horizontalLineTo(16.833f) + quadToRelative(-0.541f, 0f, -0.937f, -0.375f) + reflectiveQuadToRelative(-0.396f, -0.917f) + quadToRelative(0f, -0.583f, 0.396f, -0.958f) + reflectiveQuadToRelative(0.937f, -0.375f) + horizontalLineToRelative(12.792f) + lineToRelative(-3.292f, -3.292f) + quadToRelative(-0.375f, -0.333f, -0.354f, -0.875f) + quadToRelative(0.021f, -0.541f, 0.396f, -0.958f) + quadToRelative(0.375f, -0.375f, 0.937f, -0.375f) + quadToRelative(0.563f, 0f, 0.98f, 0.375f) + lineToRelative(5.5f, 5.542f) + quadToRelative(0.208f, 0.208f, 0.312f, 0.437f) + quadToRelative(0.104f, 0.229f, 0.104f, 0.479f) + quadToRelative(0f, 0.292f, -0.104f, 0.5f) + quadToRelative(-0.104f, 0.209f, -0.312f, 0.417f) + lineToRelative(-5.5f, 5.542f) + quadToRelative(-0.375f, 0.375f, -0.917f, 0.354f) + quadToRelative(-0.542f, -0.021f, -0.958f, -0.396f) + close() + moveToRelative(-18.5f, 8.375f) + quadToRelative(-1.084f, 0f, -1.855f, -0.792f) + quadToRelative(-0.77f, -0.791f, -0.77f, -1.875f) + verticalLineTo(7.917f) + quadToRelative(0f, -1.084f, 0.77f, -1.854f) + quadToRelative(0.771f, -0.771f, 1.855f, -0.771f) + horizontalLineToRelative(10.541f) + quadToRelative(0.542f, 0f, 0.938f, 0.375f) + quadToRelative(0.396f, 0.375f, 0.396f, 0.916f) + quadToRelative(0f, 0.584f, -0.396f, 0.959f) + reflectiveQuadToRelative(-0.938f, 0.375f) + horizontalLineTo(7.917f) + verticalLineToRelative(24.291f) + horizontalLineToRelative(10.541f) + quadToRelative(0.542f, 0f, 0.938f, 0.396f) + quadToRelative(0.396f, 0.396f, 0.396f, 0.938f) + quadToRelative(0f, 0.541f, -0.396f, 0.937f) + reflectiveQuadToRelative(-0.938f, 0.396f) + close() + } + }.build() + } + } \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Multiple_Choice.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Multiple_Choice.kt new file mode 100644 index 000000000..e5827809a --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Multiple_Choice.kt @@ -0,0 +1,88 @@ +package icon + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + +@Composable +fun multipleChopiceIcon(c: Color): ImageVector { + return remember { + ImageVector.Builder( + name = "checklist_rtl", + defaultWidth = 128.0.dp, + defaultHeight = 128.0.dp, + viewportWidth = 40.0f, + viewportHeight = 40.0f + ).apply { + path( + fill = SolidColor(c), + fillAlpha = 1f, + stroke = null, + strokeAlpha = 1f, + strokeLineWidth = 1.0f, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 1f, + pathFillType = PathFillType.NonZero + ) { + moveTo(4.75f, 14.625f) + quadToRelative(-0.583f, 0f, -0.958f, -0.375f) + reflectiveQuadToRelative(-0.375f, -0.917f) + quadToRelative(0f, -0.541f, 0.375f, -0.937f) + reflectiveQuadTo(4.75f, 12f) + horizontalLineToRelative(12.208f) + quadToRelative(0.542f, 0f, 0.938f, 0.396f) + quadToRelative(0.396f, 0.396f, 0.396f, 0.937f) + quadToRelative(0f, 0.542f, -0.396f, 0.917f) + reflectiveQuadToRelative(-0.938f, 0.375f) + close() + moveToRelative(0f, 13.333f) + quadToRelative(-0.583f, 0f, -0.958f, -0.375f) + reflectiveQuadToRelative(-0.375f, -0.916f) + quadToRelative(0f, -0.542f, 0.375f, -0.938f) + quadToRelative(0.375f, -0.396f, 0.958f, -0.396f) + horizontalLineToRelative(12.208f) + quadToRelative(0.542f, 0f, 0.938f, 0.396f) + quadToRelative(0.396f, 0.396f, 0.396f, 0.938f) + quadToRelative(0f, 0.541f, -0.396f, 0.916f) + reflectiveQuadToRelative(-0.938f, 0.375f) + close() + moveTo(26.5f, 16.917f) + lineToRelative(-4f, -3.959f) + quadToRelative(-0.375f, -0.416f, -0.375f, -0.937f) + quadToRelative(0f, -0.521f, 0.417f, -0.938f) + quadToRelative(0.375f, -0.375f, 0.896f, -0.375f) + quadToRelative(0.52f, 0f, 0.937f, 0.375f) + lineToRelative(3.042f, 3.042f) + lineToRelative(6.375f, -6.417f) + quadToRelative(0.416f, -0.416f, 0.958f, -0.395f) + quadToRelative(0.542f, 0.02f, 0.917f, 0.395f) + quadToRelative(0.375f, 0.417f, 0.395f, 0.959f) + quadToRelative(0.021f, 0.541f, -0.395f, 0.916f) + lineToRelative(-7.334f, 7.334f) + quadToRelative(-0.375f, 0.375f, -0.916f, 0.375f) + quadToRelative(-0.542f, 0f, -0.917f, -0.375f) + close() + moveToRelative(0f, 13.333f) + lineToRelative(-4f, -3.958f) + quadToRelative(-0.375f, -0.417f, -0.375f, -0.938f) + quadToRelative(0f, -0.521f, 0.417f, -0.937f) + quadToRelative(0.375f, -0.375f, 0.896f, -0.375f) + quadToRelative(0.52f, 0f, 0.937f, 0.375f) + lineToRelative(3.042f, 3.041f) + lineToRelative(6.375f, -6.416f) + quadToRelative(0.416f, -0.417f, 0.958f, -0.396f) + quadToRelative(0.542f, 0.021f, 0.917f, 0.396f) + quadToRelative(0.375f, 0.416f, 0.395f, 0.958f) + quadToRelative(0.021f, 0.542f, -0.395f, 0.917f) + lineToRelative(-7.334f, 7.333f) + quadToRelative(-0.375f, 0.375f, -0.916f, 0.375f) + quadToRelative(-0.542f, 0f, -0.917f, -0.375f) + close() + } + }.build() + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Project.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Project.kt new file mode 100644 index 000000000..432d6b806 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Project.kt @@ -0,0 +1,125 @@ +package icon + + +import androidx.compose.ui.graphics.Color +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + + +@Composable +fun projectIcon(c : Color): ImageVector { + return remember { + ImageVector.Builder( + name = "project", + defaultWidth = 128.0.dp, + defaultHeight = 128.0.dp, + viewportWidth = 40.0f, + viewportHeight = 40.0f + ).apply { + path( + fill = SolidColor(c), + fillAlpha = 1f, + stroke = null, + strokeAlpha = 1f, + strokeLineWidth = 1.0f, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 1f, + pathFillType = PathFillType.NonZero + ) { + moveTo(7.208f, 34.917f) + quadToRelative(-1.583f, 0f, -2.687f, -1.105f) + quadToRelative(-1.104f, -1.104f, -1.104f, -2.687f) + quadToRelative(0f, -1.625f, 1.104f, -2.729f) + reflectiveQuadToRelative(2.687f, -1.104f) + quadToRelative(0.25f, 0f, 0.604f, 0.041f) + quadToRelative(0.355f, 0.042f, 0.688f, 0.167f) + lineToRelative(7.333f, -11.375f) + quadToRelative(-1f, -0.833f, -1.562f, -2.062f) + quadToRelative(-0.563f, -1.23f, -0.563f, -2.646f) + quadToRelative(0f, -2.667f, 1.834f, -4.521f) + quadTo(17.375f, 5.042f, 20f, 5.042f) + reflectiveQuadToRelative(4.479f, 1.854f) + quadToRelative(1.854f, 1.854f, 1.854f, 4.479f) + quadToRelative(0f, 1.458f, -0.583f, 2.688f) + quadToRelative(-0.583f, 1.229f, -1.583f, 2.062f) + lineTo(31.5f, 27.5f) + quadToRelative(0.333f, -0.125f, 0.688f, -0.167f) + quadToRelative(0.354f, -0.041f, 0.604f, -0.041f) + quadToRelative(1.583f, 0f, 2.708f, 1.104f) + quadToRelative(1.125f, 1.104f, 1.125f, 2.729f) + quadToRelative(0f, 1.583f, -1.125f, 2.687f) + quadToRelative(-1.125f, 1.105f, -2.708f, 1.105f) + quadToRelative(-1.584f, 0f, -2.709f, -1.105f) + quadToRelative(-1.125f, -1.104f, -1.125f, -2.687f) + quadToRelative(0f, -0.708f, 0.271f, -1.354f) + quadToRelative(0.271f, -0.646f, 0.771f, -1.271f) + lineToRelative(-7.333f, -11.375f) + quadToRelative(-0.417f, 0.208f, -0.855f, 0.333f) + quadToRelative(-0.437f, 0.125f, -0.895f, 0.209f) + verticalLineToRelative(9.75f) + quadToRelative(1.25f, 0.416f, 2.083f, 1.416f) + quadToRelative(0.833f, 1f, 0.833f, 2.292f) + quadToRelative(0f, 1.583f, -1.125f, 2.687f) + quadToRelative(-1.125f, 1.105f, -2.708f, 1.105f) + quadToRelative(-1.583f, 0f, -2.688f, -1.105f) + quadToRelative(-1.104f, -1.104f, -1.104f, -2.687f) + quadToRelative(0f, -1.292f, 0.813f, -2.313f) + quadToRelative(0.812f, -1.02f, 2.104f, -1.395f) + verticalLineToRelative(-9.75f) + quadToRelative(-0.5f, -0.084f, -0.937f, -0.209f) + quadToRelative(-0.438f, -0.125f, -0.855f, -0.333f) + lineTo(10f, 28.5f) + quadToRelative(0.5f, 0.625f, 0.771f, 1.271f) + quadToRelative(0.271f, 0.646f, 0.271f, 1.354f) + quadToRelative(0f, 1.583f, -1.104f, 2.687f) + quadToRelative(-1.105f, 1.105f, -2.73f, 1.105f) + close() + moveToRelative(0f, -2.625f) + quadToRelative(0.5f, 0f, 0.854f, -0.354f) + quadToRelative(0.355f, -0.355f, 0.355f, -0.813f) + quadToRelative(0f, -0.5f, -0.355f, -0.854f) + quadToRelative(-0.354f, -0.354f, -0.854f, -0.354f) + quadToRelative(-0.458f, 0f, -0.812f, 0.354f) + quadToRelative(-0.354f, 0.354f, -0.354f, 0.854f) + quadToRelative(0f, 0.458f, 0.354f, 0.813f) + quadToRelative(0.354f, 0.354f, 0.812f, 0.354f) + close() + moveTo(20f, 15.042f) + quadToRelative(1.542f, 0f, 2.625f, -1.063f) + quadToRelative(1.083f, -1.062f, 1.083f, -2.604f) + reflectiveQuadToRelative(-1.083f, -2.604f) + quadTo(21.542f, 7.708f, 20f, 7.708f) + quadToRelative(-1.5f, 0f, -2.583f, 1.063f) + quadToRelative(-1.084f, 1.062f, -1.084f, 2.604f) + reflectiveQuadToRelative(1.084f, 2.604f) + quadTo(18.5f, 15.042f, 20f, 15.042f) + close() + moveToRelative(0f, 17.25f) + quadToRelative(0.5f, 0f, 0.854f, -0.354f) + quadToRelative(0.354f, -0.355f, 0.354f, -0.813f) + quadToRelative(0f, -0.5f, -0.354f, -0.854f) + reflectiveQuadTo(20f, 29.917f) + quadToRelative(-0.5f, 0f, -0.833f, 0.354f) + quadToRelative(-0.334f, 0.354f, -0.334f, 0.854f) + quadToRelative(0f, 0.458f, 0.334f, 0.813f) + quadToRelative(0.333f, 0.354f, 0.833f, 0.354f) + close() + moveToRelative(12.792f, 0f) + quadToRelative(0.5f, 0f, 0.833f, -0.354f) + quadToRelative(0.333f, -0.355f, 0.333f, -0.813f) + quadToRelative(0f, -0.5f, -0.333f, -0.854f) + reflectiveQuadToRelative(-0.833f, -0.354f) + quadToRelative(-0.5f, 0f, -0.834f, 0.354f) + quadToRelative(-0.333f, 0.354f, -0.333f, 0.854f) + quadToRelative(0f, 0.458f, 0.333f, 0.813f) + quadToRelative(0.334f, 0.354f, 0.834f, 0.354f) + close() + } + }.build() + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/RectangleList.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/RectangleList.kt new file mode 100644 index 000000000..9db001598 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/RectangleList.kt @@ -0,0 +1,123 @@ +package icon + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + +@Composable +fun rectangleListIcon(c: Color): ImageVector { + return remember { + ImageVector.Builder( + name = "list", + defaultWidth = 128.0.dp, + defaultHeight = 128.0.dp, + viewportWidth = 40.0f, + viewportHeight = 40.0f + ).apply { + path( + fill = SolidColor(c), + fillAlpha = 1f, + stroke = null, + strokeAlpha = 1f, + strokeLineWidth = 1.0f, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 1f, + pathFillType = PathFillType.NonZero + ) { + moveTo(7.875f, 34.75f) + quadToRelative(-1.042f, 0f, -1.833f, -0.792f) + quadToRelative(-0.792f, -0.791f, -0.792f, -1.833f) + verticalLineTo(7.875f) + quadToRelative(0f, -1.042f, 0.792f, -1.833f) + quadToRelative(0.791f, -0.792f, 1.833f, -0.792f) + horizontalLineToRelative(24.25f) + quadToRelative(1.042f, 0f, 1.833f, 0.792f) + quadToRelative(0.792f, 0.791f, 0.792f, 1.833f) + verticalLineToRelative(24.25f) + quadToRelative(0f, 1.042f, -0.792f, 1.833f) + quadToRelative(-0.791f, 0.792f, -1.833f, 0.792f) + close() + moveToRelative(0f, -2.625f) + horizontalLineToRelative(24.25f) + verticalLineTo(7.875f) + horizontalLineTo(7.875f) + verticalLineToRelative(24.25f) + close() + moveToRelative(5f, -4.167f) + quadToRelative(0.5f, 0f, 0.896f, -0.375f) + reflectiveQuadToRelative(0.396f, -0.916f) + quadToRelative(0f, -0.542f, -0.396f, -0.938f) + quadToRelative(-0.396f, -0.396f, -0.896f, -0.396f) + quadToRelative(-0.542f, 0f, -0.937f, 0.396f) + quadToRelative(-0.396f, 0.396f, -0.396f, 0.938f) + quadToRelative(0f, 0.541f, 0.396f, 0.916f) + quadToRelative(0.395f, 0.375f, 0.937f, 0.375f) + close() + moveToRelative(0f, -6.666f) + quadToRelative(0.5f, 0f, 0.896f, -0.375f) + reflectiveQuadToRelative(0.396f, -0.917f) + quadToRelative(0f, -0.542f, -0.396f, -0.938f) + quadToRelative(-0.396f, -0.395f, -0.896f, -0.395f) + quadToRelative(-0.542f, 0f, -0.937f, 0.395f) + quadToRelative(-0.396f, 0.396f, -0.396f, 0.938f) + quadToRelative(0f, 0.542f, 0.396f, 0.917f) + quadToRelative(0.395f, 0.375f, 0.937f, 0.375f) + close() + moveToRelative(0f, -6.667f) + quadToRelative(0.5f, 0f, 0.896f, -0.396f) + reflectiveQuadToRelative(0.396f, -0.937f) + quadToRelative(0f, -0.542f, -0.396f, -0.917f) + reflectiveQuadTo(12.875f, 12f) + quadToRelative(-0.542f, 0f, -0.937f, 0.375f) + quadToRelative(-0.396f, 0.375f, -0.396f, 0.917f) + quadToRelative(0f, 0.541f, 0.396f, 0.937f) + quadToRelative(0.395f, 0.396f, 0.937f, 0.396f) + close() + moveTo(19.458f, 28f) + horizontalLineToRelative(7.334f) + quadToRelative(0.5f, 0f, 0.896f, -0.396f) + quadToRelative(0.395f, -0.396f, 0.395f, -0.937f) + quadToRelative(0f, -0.542f, -0.395f, -0.917f) + quadToRelative(-0.396f, -0.375f, -0.896f, -0.375f) + horizontalLineToRelative(-7.334f) + quadToRelative(-0.541f, 0f, -0.937f, 0.375f) + reflectiveQuadToRelative(-0.396f, 0.917f) + quadToRelative(0f, 0.583f, 0.396f, 0.958f) + reflectiveQuadToRelative(0.937f, 0.375f) + close() + moveToRelative(0f, -6.708f) + horizontalLineToRelative(7.334f) + quadToRelative(0.5f, 0f, 0.896f, -0.375f) + quadToRelative(0.395f, -0.375f, 0.395f, -0.917f) + quadToRelative(0f, -0.583f, -0.395f, -0.958f) + quadToRelative(-0.396f, -0.375f, -0.896f, -0.375f) + horizontalLineToRelative(-7.334f) + quadToRelative(-0.541f, 0f, -0.937f, 0.395f) + quadToRelative(-0.396f, 0.396f, -0.396f, 0.938f) + quadToRelative(0f, 0.542f, 0.396f, 0.917f) + reflectiveQuadToRelative(0.937f, 0.375f) + close() + moveToRelative(0f, -6.667f) + horizontalLineToRelative(7.334f) + quadToRelative(0.5f, 0f, 0.896f, -0.375f) + quadToRelative(0.395f, -0.375f, 0.395f, -0.917f) + quadToRelative(0f, -0.583f, -0.395f, -0.958f) + quadToRelative(-0.396f, -0.375f, -0.896f, -0.375f) + horizontalLineToRelative(-7.334f) + quadToRelative(-0.541f, 0f, -0.937f, 0.396f) + reflectiveQuadToRelative(-0.396f, 0.937f) + quadToRelative(0f, 0.542f, 0.396f, 0.917f) + reflectiveQuadToRelative(0.937f, 0.375f) + close() + moveToRelative(-11.583f, 17.5f) + verticalLineTo(7.875f) + verticalLineToRelative(24.25f) + close() + } + }.build() + } + } \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Single_Choice.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Single_Choice.kt new file mode 100644 index 000000000..ea7c95e49 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Single_Choice.kt @@ -0,0 +1,52 @@ +package icon + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + +@Composable +fun singleChoiceIcon(c : Color): ImageVector { + return remember { + ImageVector.Builder( + name = "check", + defaultWidth = 128.0.dp, + defaultHeight = 128.0.dp, + viewportWidth = 40.0f, + viewportHeight = 40.0f + ).apply { + path( + fill = SolidColor(c), + fillAlpha = 1f, + stroke = null, + strokeAlpha = 1f, + strokeLineWidth = 1.0f, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 1f, + pathFillType = PathFillType.NonZero + ) { + moveTo(15.833f, 29.125f) + quadToRelative(-0.25f, 0f, -0.479f, -0.083f) + quadToRelative(-0.229f, -0.084f, -0.437f, -0.292f) + lineToRelative(-7.334f, -7.333f) + quadToRelative(-0.375f, -0.375f, -0.375f, -0.938f) + quadToRelative(0f, -0.562f, 0.375f, -0.979f) + quadToRelative(0.375f, -0.375f, 0.938f, -0.375f) + quadToRelative(0.562f, 0f, 0.937f, 0.375f) + lineToRelative(6.375f, 6.375f) + lineTo(30.5f, 11.208f) + quadToRelative(0.375f, -0.375f, 0.938f, -0.375f) + quadToRelative(0.562f, 0f, 0.979f, 0.375f) + quadToRelative(0.375f, 0.375f, 0.375f, 0.938f) + quadToRelative(0f, 0.562f, -0.375f, 0.937f) + lineTo(16.75f, 28.75f) + quadToRelative(-0.208f, 0.208f, -0.438f, 0.292f) + quadToRelative(-0.229f, 0.083f, -0.479f, 0.083f) + close() + } + }.build() + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/SquarePlus.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/SquarePlus.kt new file mode 100644 index 000000000..65a18d1c3 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/SquarePlus.kt @@ -0,0 +1,92 @@ +package icon + +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + +@Composable +@Preview +fun squarePlusIcon(c: Color): ImageVector { + return remember { + ImageVector.Builder( + name = "add_circle", + defaultWidth = 128.0.dp, + defaultHeight = 128.0.dp, + viewportWidth = 40.0f, + viewportHeight = 40.0f + ).apply { + path( + fill = SolidColor(c), + fillAlpha = 1f, + stroke = null, + strokeAlpha = 1f, + strokeLineWidth = 1.0f, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 1f, + pathFillType = PathFillType.NonZero + ) { + moveTo(20.125f, 28.208f) + quadToRelative(0.542f, 0f, 0.917f, -0.396f) + quadToRelative(0.375f, -0.395f, 0.375f, -0.937f) + verticalLineToRelative(-5.417f) + horizontalLineToRelative(5.5f) + quadToRelative(0.541f, 0f, 0.916f, -0.396f) + quadToRelative(0.375f, -0.395f, 0.375f, -0.937f) + reflectiveQuadToRelative(-0.396f, -0.937f) + quadToRelative(-0.395f, -0.396f, -0.937f, -0.396f) + horizontalLineToRelative(-5.458f) + verticalLineToRelative(-5.709f) + quadToRelative(0f, -0.541f, -0.375f, -0.916f) + reflectiveQuadToRelative(-0.959f, -0.375f) + quadToRelative(-0.541f, 0f, -0.916f, 0.396f) + quadToRelative(-0.375f, 0.395f, -0.375f, 0.937f) + verticalLineToRelative(5.667f) + horizontalLineToRelative(-5.709f) + quadToRelative(-0.541f, 0f, -0.916f, 0.396f) + quadToRelative(-0.375f, 0.395f, -0.375f, 0.937f) + reflectiveQuadToRelative(0.396f, 0.937f) + quadToRelative(0.395f, 0.396f, 0.937f, 0.396f) + horizontalLineToRelative(5.667f) + verticalLineToRelative(5.459f) + quadToRelative(0f, 0.541f, 0.375f, 0.916f) + reflectiveQuadToRelative(0.958f, 0.375f) + close() + moveTo(20f, 36.375f) + quadToRelative(-3.458f, 0f, -6.458f, -1.25f) + reflectiveQuadToRelative(-5.209f, -3.458f) + quadToRelative(-2.208f, -2.209f, -3.458f, -5.209f) + quadToRelative(-1.25f, -3f, -1.25f, -6.458f) + reflectiveQuadToRelative(1.25f, -6.437f) + quadToRelative(1.25f, -2.98f, 3.458f, -5.188f) + quadToRelative(2.209f, -2.208f, 5.209f, -3.479f) + quadToRelative(3f, -1.271f, 6.458f, -1.271f) + reflectiveQuadToRelative(6.438f, 1.271f) + quadToRelative(2.979f, 1.271f, 5.187f, 3.479f) + reflectiveQuadToRelative(3.479f, 5.188f) + quadToRelative(1.271f, 2.979f, 1.271f, 6.437f) + reflectiveQuadToRelative(-1.271f, 6.458f) + quadToRelative(-1.271f, 3f, -3.479f, 5.209f) + quadToRelative(-2.208f, 2.208f, -5.187f, 3.458f) + quadToRelative(-2.98f, 1.25f, -6.438f, 1.25f) + close() + moveTo(20f, 20f) + close() + moveToRelative(0f, 13.75f) + quadToRelative(5.667f, 0f, 9.708f, -4.042f) + quadTo(33.75f, 25.667f, 33.75f, 20f) + reflectiveQuadToRelative(-4.042f, -9.708f) + quadTo(25.667f, 6.25f, 20f, 6.25f) + reflectiveQuadToRelative(-9.708f, 4.042f) + quadTo(6.25f, 14.333f, 6.25f, 20f) + reflectiveQuadToRelative(4.042f, 9.708f) + quadTo(14.333f, 33.75f, 20f, 33.75f) + close() + } + }.build() + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Walking.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Walking.kt new file mode 100644 index 000000000..4baf77ce5 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/icon/Walking.kt @@ -0,0 +1,80 @@ +package icon + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + +@Composable +fun walkingIcon(c: Color): ImageVector { + return remember { + ImageVector.Builder( + name = "Walking", + defaultWidth = 128.dp, + defaultHeight = 128.dp, + viewportWidth = 24f, + viewportHeight = 24f + ).apply { + path( + fill = SolidColor(c), + fillAlpha = 1.0f, + stroke = null, + strokeAlpha = 1.0f, + strokeLineWidth = 1.0f, + strokeLineCap = StrokeCap.Butt, + strokeLineJoin = StrokeJoin.Miter, + strokeLineMiter = 1.0f, + pathFillType = PathFillType.NonZero + ) { + moveTo(10f, 2.5f) + curveToRelative(0f, -1.381f, 1.119f, -2.5f, 2.5f, -2.5f) + reflectiveCurveToRelative(2.5f, 1.119f, 2.5f, 2.5f) + reflectiveCurveToRelative(-1.119f, 2.5f, -2.5f, 2.5f) + reflectiveCurveToRelative(-2.5f, -1.119f, -2.5f, -2.5f) + close() + moveToRelative(4.621f, 8.038f) + lineToRelative(-1.408f, 5.296f) + lineToRelative(2.788f, 1.584f) + verticalLineToRelative(6.582f) + horizontalLineToRelative(-2f) + verticalLineToRelative(-5.418f) + lineToRelative(-4.482f, -2.546f) + curveToRelative(-1.148f, -0.652f, -1.74f, -2.006f, -1.439f, -3.292f) + lineToRelative(1.037f, -4.434f) + lineToRelative(-3.119f, 1.559f) + lineToRelative(-1.03f, 3.887f) + lineToRelative(-1.934f, -0.513f) + lineToRelative(1.262f, -4.759f) + lineToRelative(4.969f, -2.484f) + horizontalLineToRelative(3.236f) + curveToRelative(0.865f, 0f, 1.687f, 0.374f, 2.258f, 1.024f) + lineToRelative(2.347f, 3.706f) + lineToRelative(3.75f, 1.875f) + lineToRelative(-0.895f, 1.789f) + lineToRelative(-4.25f, -2.125f) + lineToRelative(-1.089f, -1.731f) + close() + moveToRelative(-3.164f, 4.298f) + lineToRelative(1.721f, -6.572f) + curveToRelative(-0.184f, -0.169f, -0.425f, -0.264f, -0.677f, -0.264f) + horizontalLineToRelative(-1.258f) + lineToRelative(-1.216f, 5.2f) + curveToRelative(-0.1f, 0.428f, 0.097f, 0.879f, 0.48f, 1.097f) + lineToRelative(0.951f, 0.54f) + close() + moveToRelative(-3.434f, 2.296f) + lineToRelative(-0.463f, 1.996f) + lineToRelative(-2.272f, 4.872f) + horizontalLineToRelative(2.199f) + lineToRelative(1.923f, -4.086f) + lineToRelative(0.388f, -1.688f) + lineToRelative(-1.029f, -0.585f) + curveToRelative(-0.259f, -0.147f, -0.508f, -0.317f, -0.747f, -0.509f) + close() + } + }.build() + } +} + diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CheckAssignTaskScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CheckAssignTaskScreen.kt new file mode 100644 index 000000000..15d9c1a31 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CheckAssignTaskScreen.kt @@ -0,0 +1,120 @@ +package screen + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.AssignQuestion +import com.example.compose.AppTheme +import composable.QuestionDisplay +import composable.title +import databaseInteraction.Driver +import databaseInteraction.Provider +import kotlinx.coroutines.runBlocking + +/** + * Screen to check assign question before saving + * @param question Question to check + */ +class CheckAssignTaskScreen(val question : AssignQuestion) : Screen{ + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold { + LazyColumn( + Modifier.padding( + start = 24.dp, + top = 24.dp, + end = 24.dp + ) + ) { + item { + title("Bitte kontrollieren Sie die Angaben") + } + item { + QuestionDisplay(question, Modifier.fillMaxWidth()) + } + item { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + //ADD QUESTION TO DATABASE + addAssignQuestion(question) + navigator.popUntilRoot() + }) { + Text("Speichern") + } + } + } + } + } + } + } + } + + private suspend fun addTags(questionId: Long, newTags: List) { + val tagData = Provider.provideTagDataSource(Driver.createDriver()) + val tagQuestionData = Provider.provideQuestionTagDataSource(Driver.createDriver()) + newTags.forEach{newTag -> + if (tagData.getTagByName(newTag)!= null){ + tagQuestionData.insertQuestionTag(questionId = questionId,tagData.getTagByName(newTag)!!) + }else{ + tagData.insertTag(newTag) + tagQuestionData.insertQuestionTag(questionId, tagData.getTagByName(newTag)!!) + } + } + } + + private fun addAssignQuestion(question : AssignQuestion) { + val questionData = Provider.provideQuestionDataSource(Driver.createDriver()) + val answerData = Provider.provideAssignmentDataSource(Driver.createDriver()) + runBlocking { + //Frage einfügen + questionData.insertQuestion( + question.description, + question.explanation, + question.points.toLong(), + question.pointsToPass.toLong(), + "ASSIGN_QUESTION", + ) + //Antworten einfügen + question.assignments.forEach { assignment -> + answerData.insertAssignment( + termA = assignment.termA, + termB = assignment.termB, + questionId = questionData.getQuestionId( + question.description, + question.explanation, + question.points.toLong(), + question.pointsToPass.toLong() + )!! + ) + //TAGS einfügen + // addTags(questionId = questionData.getQuestionId(question.description, question.explanation, question.points.toLong(), question.pointsToPass.toLong())!!,question.tags) + } + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CheckMultipleChoiceQuestionScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CheckMultipleChoiceQuestionScreen.kt new file mode 100644 index 000000000..d9cad3199 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CheckMultipleChoiceQuestionScreen.kt @@ -0,0 +1,134 @@ +package screen + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.MultipleChoiceQuestion +import com.example.compose.AppTheme +import composable.QuestionDisplay +import composable.title +import databaseInteraction.Driver +import databaseInteraction.Provider +import kotlinx.coroutines.runBlocking + +/** + * Screen to check multiple question before saving + * @param question Question to check + */ +class CheckMultipleChoiceQuestionScreen(val question: MultipleChoiceQuestion) : Screen { + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold { + LazyColumn( + Modifier.padding( + start = 24.dp, + top = 24.dp, + end = 24.dp + ) + ) { + item { + title("Bitte kontrollieren Sie die Angaben") + } + item { + QuestionDisplay(question, Modifier.fillMaxWidth()) + } + item { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + //ADD QUESTION TO DATABASE + addMultipleChoiceQuestion(question) + navigator.popUntilRoot() + }) { + Text("Speichern") + } + } + } + } + } + } + } + + } + + private suspend fun addTags(questionId: Long, newTags: List) { + val tagData = Provider.provideTagDataSource(Driver.createDriver()) + val tagQuestionData = Provider.provideQuestionTagDataSource(Driver.createDriver()) + newTags.forEach{newTag -> + if (tagData.getTagByName(newTag)!= null){ + tagQuestionData.insertQuestionTag(questionId = questionId,tagData.getTagByName(newTag)!!) + }else{ + tagData.insertTag(newTag) + tagQuestionData.insertQuestionTag(questionId, tagData.getTagByName(newTag)!!) + } + } + } + + private fun addMultipleChoiceQuestion(question: MultipleChoiceQuestion) { + val questionData = Provider.provideQuestionDataSource(Driver.createDriver()) + val answerData = Provider.provideAnswerDataSource(Driver.createDriver()) + + runBlocking { + //Frage einfügen + questionData.insertQuestion( + question.description, + question.explanation, + question.points.toLong(), + question.pointsToPass.toLong(), + "MULTIPLE_CHOICE_QUESTION", + ) + //Antworten einfügen + question.answers.forEachIndexed { index, answer -> + answerData.insertAnswer( + answer = answer, + questionId = questionData.getQuestionId( + question.description, + question.explanation, + question.points.toLong(), + question.pointsToPass.toLong() + )!! + ) + //Korrekte Anworten anfügen + if (question.correctAnswerIndices.contains(index)) { + answerData.setCorrectAnswer( + answerData.getAnswerId( + answer = answer, + questionId = questionData.getQuestionId( + question.description, + question.explanation, + question.points.toLong(), + question.pointsToPass.toLong() + )!! + )!! + ) + } + //addTags(questionId = questionData.getQuestionId(question.description, question.explanation, question.points.toLong(), question.pointsToPass.toLong())!!, question.tags) + } + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CheckSingleChoiceQuestionScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CheckSingleChoiceQuestionScreen.kt new file mode 100644 index 000000000..a67c3ff1a --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CheckSingleChoiceQuestionScreen.kt @@ -0,0 +1,153 @@ +package screen + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.SingleChoiceQuestion +import com.example.compose.AppTheme +import composable.QuestionDisplay +import composable.title +import databaseInteraction.Driver +import databaseInteraction.Provider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking + +/** + * Screen to check the Question before saving it to the Database + * @param question Single Choice Question created in Screen before + */ +class CheckSingleChoiceQuestionScreen(val question: SingleChoiceQuestion) : Screen { + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold { + LazyColumn( + Modifier.padding( + start = 24.dp, + top = 24.dp, + end = 24.dp + ) + ) { + item { + title("Bitte kontrollieren Sie die Angaben") + } + item { + QuestionDisplay(question, Modifier.fillMaxWidth()) + } + item { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + //ADD QUESTION TO DATABASE + addSingleChoiceQuestion(question) + navigator.popUntilRoot() + }) { + Text("Speichern") + } + } + } + } + } + } + } + } + + private suspend fun addTags(questionId: Long, newTags: List) { + /* Fehlerhafte Lösung + val tagData = Provider.provideTagDataSource(Driver.createDriver()) + val tagQuestionData = Provider.provideQuestionTagDataSource(Driver.createDriver()) + var tagsFromDB = tagData.getAllTags().firstOrNull() + var tagsToAdd = mutableListOf() + if (tagsFromDB != null) { + newTags.forEach { tag -> + tagsToAdd.add(tag) + } + tagsFromDB.forEach { tagDB -> + if (tagsToAdd.contains(tagDB.tag)) { + tagQuestionData.insertQuestionTag(questionId = questionId, tagData.getTagByName(tagDB.tag+"1")!!) + tagsToAdd.remove(tagDB.tag) + } + } + while (tagsToAdd != null){ + tagsToAdd[0] + tagData.insertTag(tagsToAdd[0]) + tagQuestionData.insertQuestionTag(questionId, tagData.getTagByName(tagsToAdd.get(0))!!) + tagsToAdd.remove(tagsToAdd[0]) + } + } else { + while (tagsToAdd.isNotEmpty()){ + tagsToAdd.get(0) + tagData.insertTag(tagsToAdd[0]) + tagQuestionData.insertQuestionTag(questionId, tagData.getTagByName(tagsToAdd[0])!!) + tagsToAdd.remove(tagsToAdd[0]) + } + }*/ + } + + private fun addSingleChoiceQuestion(question: SingleChoiceQuestion) { + val questionData = Provider.provideQuestionDataSource(Driver.createDriver()) + val answerData = Provider.provideAnswerDataSource(Driver.createDriver()) + runBlocking { + //Frage einfügen + questionData.insertQuestion( + question.description, + question.explanation, + question.points.toLong(), + question.pointsToPass.toLong(), + "SINGLE_CHOICE_QUESTION", + ) + //Antworten einfügen + question.answers.forEachIndexed { index, answer -> + answerData.insertAnswer( + answer = answer, + questionId = questionData.getQuestionId( + question.description, + question.explanation, + question.points.toLong(), + question.pointsToPass.toLong() + )!! + ) + //Korrekte Anworten anfügen + if (question.correctAnswerIndex == index) { + answerData.setCorrectAnswer( + answerData.getAnswerId( + answer = answer, + questionId = questionData.getQuestionId( + question.description, + question.explanation, + question.points.toLong(), + question.pointsToPass.toLong() + )!! + )!! + ) + } + //addTags(questionId = questionData.getQuestionId(question.description, question.explanation, question.points.toLong(),question.pointsToPass.toLong())!!, question.tags) + } + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateAssignScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateAssignScreen.kt new file mode 100644 index 000000000..cc3c9fa4a --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateAssignScreen.kt @@ -0,0 +1,164 @@ +package screen + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.AssignQuestion +import classes.Assignment +import com.example.compose.AppTheme +import composable.* +import kotlinx.coroutines.launch + +/** + * Screen to create an assign question and pass it the next Screen (Creates everything but the correctAnswerIndex) + */ +class CreateAssignScreen : Screen { + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + //Variablen für Single Choice Frage + var questionText by rememberSaveable { mutableStateOf("") } + var assignments = remember { mutableStateListOf() } + var points by rememberSaveable { mutableStateOf("") } + var pointsToPass by rememberSaveable { mutableStateOf("") } + var explanation by rememberSaveable { mutableStateOf("") } + var tags = remember { mutableStateListOf() } + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold( + snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, + bottomBar = { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + if (questionText.isNotEmpty() && points.isNotEmpty() && explanation.isNotEmpty() && pointsToPass.isNotEmpty()) { + navigator.push( + CheckAssignTaskScreen( + AssignQuestion( + 0, + questionText, + points.toInt(), + pointsToPass.toInt(), + explanation, + tags, + assignments + ) + ) + ) + } else if (assignments.size < 2) { + scope.launch { + snackbarHostState.showSnackbar( + message = "Bitte geben Sie mindestens 2 Antwortmöglichkeiten an", + withDismissAction = true + ) + } + } else { + scope.launch { + snackbarHostState.showSnackbar( + message = "Bitte füllen Sie alle Felder aus", + withDismissAction = true + ) + } + } + }) { + Text("Weiter") + } + } + } + ) { + LazyColumn( + Modifier.padding( + start = 48.dp, + top = 20.dp, + end = 48.dp, + bottom = 76.dp + ) + ) { + item { + title("Zuordnungsaufgabe erstellen") + } + //Multiple_Choice_Frage erstellen + item { + inputTextField( + Modifier, + questionText, + onValueChange = { questionText = it }, + "Frage eingeben", + questionText.isEmpty() + ) + } + item { + createAssignment( + Modifier, + onValueChange = { assignments = it }, + taskLabel = "Bitte mindestens 2 Lösungen angeben", + outputLabel = "Lösungen:", + minAmount = 2 + ) + } + item { + createStringList( + Modifier, + onValueChange = { tags = it }, + taskLabel = "Bitte Tags angeben", + outputLabel = "Tags:", + textFieldLabel = "Tag angeben", + minAmount = 0 + ) + } + item { + inputTextField( + Modifier, + explanation, + onValueChange = { explanation = it }, + "Erklärung angeben", + explanation.isEmpty() + ) + } + item { + Row { + inputNumberField( + Modifier.width(300.dp), + points, + onValueChange = { points = it }, + "Punkte" + ) + inputNumberField( + Modifier.width(300.dp), + points, + onValueChange = { pointsToPass = it }, + "Punkte zum bestehen" + ) + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateDependencyScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateDependencyScreen.kt new file mode 100644 index 000000000..86837207b --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateDependencyScreen.kt @@ -0,0 +1,135 @@ +package screen + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.Dependency +import com.example.compose.AppTheme +import composable.bodyText +import composable.title +import databaseInteraction.Driver +import databaseInteraction.Provider +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + +/** + * Screen to choose the dependency type + * @param dependency dependency to add + */ +class CreateDependencyScreen(private var dependency: Dependency) : Screen { + @Composable + override fun Content() { + val dependencyData = Provider.provideDependencyDataSource(Driver.createDriver()) + val questionData = Provider.provideQuestionDataSource(Driver.createDriver()) + val navigator = LocalNavigator.currentOrThrow + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + var selectedIndex by remember { mutableStateOf(-1) } + val dependencyList = mutableListOf( + "Sequenz", + "Pflicht Unteraufgabe", + "Optionale Unteraufgabe", + "Bei falscher Antwort", + "Bei richtiger Antwort" + ) + + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold( + snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, + ) { + LazyColumn( + Modifier.padding( + start = 24.dp, + top = 24.dp, + end = 24.dp + ).fillMaxSize() + ) { + item { + title("Bitte wählen Sie die Abhängigkeit aus") + } + itemsIndexed(items = dependencyList) { index, answer -> + bodyText(answer, modifier = Modifier.selectable( + selected = selectedIndex == index, + onClick = { + dependency.dependency = dependencyList[index] + selectedIndex = index + } + ).clip(shape = RoundedCornerShape(10.dp)).background( + if (selectedIndex == index) { + MaterialTheme.colorScheme.onSecondary + } else MaterialTheme.colorScheme.background + ).fillParentMaxWidth().padding(start = 16.dp, end = 16.dp, top = 16.dp)) + Spacer(modifier = Modifier.padding(8.dp)) + } + item { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + if (dependencyList.contains(dependency.dependency)) { + runBlocking { + dependencyData.insertDependency( + questionAId = questionData.getQuestionId( + dependency.questionA!!.description, + dependency.questionA!!.explanation, + dependency.questionA!!.points.toLong(), + dependency.questionA!!.pointsToPass.toLong() + )!!, + questionData.getQuestionId( + dependency.questionB!!.description, + dependency.questionB!!.explanation, + dependency.questionB!!.points.toLong(), + dependency.questionB!!.pointsToPass.toLong() + )!!, + projectId = dependency.projectId, + 0, + dependencyList[selectedIndex] + ) + } + navigator.popUntil {it is EditProjectScreen } + } else { + scope.launch { + snackbarHostState.showSnackbar( + message = "Bitte wählen Sie eine Bedingung aus", + withDismissAction = true + ) + } + } + }) { + Text("Speichern") + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateMultipleChoiceScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateMultipleChoiceScreen.kt new file mode 100644 index 000000000..2d3f13e8a --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateMultipleChoiceScreen.kt @@ -0,0 +1,174 @@ +package screen + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.MultipleChoiceQuestion +import com.example.compose.AppTheme +import composable.createStringList +import composable.inputNumberField +import composable.inputTextField +import composable.title +import kotlinx.coroutines.launch + +/** + * Screen to create a multiple choice question and pass it Screen (Creates everything but the correctAnswerIndex) + */ +class CreateMultipleChoiceScreen : Screen { + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + //Variablen für Single Choice Frage + var questionText by rememberSaveable { mutableStateOf("") } + var answers = remember { mutableStateListOf() } + var points by rememberSaveable { mutableStateOf("") } + var pointsToPass by rememberSaveable { mutableStateOf("") } + var explanation by rememberSaveable { mutableStateOf("") } + var tags = remember { mutableStateListOf() } + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold( + snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, + bottomBar = { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + if (questionText.isNotEmpty() && points.isNotEmpty() && explanation.isNotEmpty() && pointsToPass.isNotEmpty()) { + navigator.push( + MultipleChoiceChooseAnswerIndexScreen( + MultipleChoiceQuestion( + 0, + questionText, + points.toInt(), + pointsToPass.toInt(), + explanation, + answers = answers, + tags = tags + ) + ) + ) + } else if (answers.size < 2) { + scope.launch { + snackbarHostState.showSnackbar( + message = "Bitte geben Sie mindestens 2 Antwortmöglichkeiten an", + withDismissAction = true + ) + } + } else if (points < pointsToPass) { + scope.launch { + snackbarHostState.showSnackbar( + message = "Ungültige Punktzahl. Weniger Punkte möglich als zum bestehen nötig ", + withDismissAction = true + ) + } + } else { + scope.launch { + snackbarHostState.showSnackbar( + message = "Bitte füllen Sie alle Felder aus", + withDismissAction = true + ) + } + } + }) { + Text("Weiter") + } + } + } + ) { + LazyColumn( + Modifier.padding( + start = 24.dp, + top = 20.dp, + end = 24.dp, + bottom = 76.dp + ) + ) { + item { + title("Multiple Choice Frage erstellen") + } + //Multiple_Choice_Frage erstellen + item { + inputTextField( + Modifier, + questionText, + onValueChange = { questionText = it }, + "Frage eingeben", + questionText.isEmpty() + ) + } + item { + createStringList( + Modifier, + onValueChange = { answers = it }, + taskLabel = "Bitte mindestens 2 Antworten angeben", + outputLabel = "Antworten:", + textFieldLabel = "Antwort angeben", + minAmount = 2 + ) + } + item { + createStringList( + Modifier, + onValueChange = { tags = it }, + taskLabel = "Bitte Tags angeben", + outputLabel = "Tags:", + textFieldLabel = "Tag angeben", + minAmount = 0 + ) + } + item { + inputTextField( + Modifier, + explanation, + onValueChange = { explanation = it }, + "Erklärung angeben", + explanation.isEmpty() + ) + } + item { + Row { + inputNumberField( + Modifier.width(300.dp), + points, + onValueChange = { points = it }, + "Punkte" + ) + inputNumberField( + Modifier.width(300.dp), + points, + onValueChange = { pointsToPass = it }, + "Punkte zum bestehen" + ) + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateSingleChoiceScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateSingleChoiceScreen.kt new file mode 100644 index 000000000..9234754c8 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateSingleChoiceScreen.kt @@ -0,0 +1,169 @@ +package screen + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.SingleChoiceQuestion +import com.example.compose.AppTheme +import composable.createStringList +import composable.inputNumberField +import composable.inputTextField +import composable.title +import kotlinx.coroutines.launch + +/** + * Screen to Create a Single Choice Question and pass it further to the next Screen (Creates everything but the correctAnswerIndex) + */ +class CreateSingleChoiceScreen : Screen { + + @Composable + override fun Content() { + + val navigator = LocalNavigator.currentOrThrow + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + //Variablen für Single Choice Frage + var questionText by rememberSaveable { mutableStateOf("") } + var answers = remember { mutableStateListOf() } + var points by rememberSaveable { mutableStateOf("") } + var pointsToPass by rememberSaveable { mutableStateOf("") } + var explanation by rememberSaveable { mutableStateOf("") } + var tags = remember { mutableStateListOf() } + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold( + snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, + bottomBar = { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + if (questionText.isNotEmpty() && points.isNotEmpty() && explanation.isNotEmpty() && pointsToPass.isNotEmpty()) { + navigator.push( + SingleChoiceChooseAnswerIndexScreen( + SingleChoiceQuestion( + 0, + questionText, + points.toInt(), + pointsToPass.toInt(), + explanation, + answers, + tags + ) + ) + ) + } else if (answers.size < 2) { + scope.launch { + snackbarHostState.showSnackbar( + message = "Bitte geben Sie mindestens 2 Antwortmöglichkeiten an", + withDismissAction = true + ) + } + } else { + scope.launch { + snackbarHostState.showSnackbar( + message = "Bitte füllen Sie alle Felder aus", + withDismissAction = true + ) + } + } + }) { + Text("Weiter") + } + } + } + ) { + LazyColumn( + Modifier.padding( + start = 48.dp, + top = 20.dp, + end = 48.dp, + bottom = 76.dp + ) + ) { + item { + title("Single Choice Frage erstellen") + } + //Single_Choice_Frage erstellen + item { + inputTextField( + Modifier, + questionText, + onValueChange = { questionText = it }, + "Frage eingeben", + questionText.isEmpty() + ) + } + item { + createStringList( + Modifier, + onValueChange = { answers = it }, + taskLabel = "Bitte mindestens 2 Antworten angeben", + outputLabel = "Antworten:", + textFieldLabel = "Antwort angeben", + minAmount = 2 + ) + } + item { + createStringList( + Modifier, + onValueChange = { tags = it }, + taskLabel = "Bitte Tags angeben", + outputLabel = "Tags:", + textFieldLabel = "Tag angeben", + minAmount = 0 + ) + } + item { + inputTextField( + Modifier, + explanation, + onValueChange = { explanation = it }, + "Erklärung angeben", + explanation.isEmpty() + ) + } + item { + Row { + inputNumberField( + Modifier.width(300.dp), + points, + onValueChange = { points = it }, + "Punkte" + ) + inputNumberField( + Modifier.width(300.dp), + points, + onValueChange = { pointsToPass = it }, + "Punkte zum bestehen" + ) + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateTaskFileScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateTaskFileScreen.kt new file mode 100644 index 000000000..274d0afe4 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/CreateTaskFileScreen.kt @@ -0,0 +1,147 @@ +package screen + +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import com.example.compose.AppTheme +import composable.bodyText +import composable.inputTextField +import composable.title +import databaseInteraction.Driver +import databaseInteraction.Provider +import databaseInteraction.TaskFileWriter +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + +/** + * Screen to create the dungeon task file + */ +class CreateTaskFileScreen : Screen { + @Composable + @Preview + override fun Content() { + val projectData = Provider.provideProjectDataSource(Driver.createDriver()) + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + val navigator = LocalNavigator.currentOrThrow + var filename by rememberSaveable { mutableStateOf("") } + val projects = projectData.getAllProjects().collectAsState(initial = emptyList()).value + val selectedIndices = remember { mutableStateListOf() } + val selectedProjectIds = remember { mutableStateListOf() } + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold( + snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, + topBar = { + Column { + title("Aufgabendatei erstellen") + bodyText("Bitte wählen Sie die Projekte, die in die Aufgabendatei aufgenommen werden sollen und bestätigen Sie mit weiter") + } + }, + bottomBar = { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Start + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + if (selectedIndices.isEmpty()) { + scope.launch { + snackbarHostState.showSnackbar( + message = "Bitte wählen Sie mindestens ein Projekt aus", + withDismissAction = true + ) + } + } else if (filename.isEmpty()){ + scope.launch { + snackbarHostState.showSnackbar( + message = "Bitte geben Sie einen Dateinamen an", + withDismissAction = true + ) + } + } else { + selectedProjectIds.forEach { + TaskFileWriter.writeProjectToFile(it, filename) + } + while (selectedIndices.isNotEmpty()){ + selectedIndices.removeLast() + filename= "" + } + scope.launch { + snackbarHostState.showSnackbar( + message = "Datei wurde erstellt!", + withDismissAction = true + ) + } + } + }) { + Text("Datei Speichern") + } + } + } + ) { + LazyColumn( + Modifier.padding(it).fillMaxSize() + ) { + item { + inputTextField( + label = "Dateinamen angeben", + modifier = Modifier.padding(start = 24.dp, end = 24.dp), + value = filename, + onValueChange = { filename = it }, + isError = filename.isEmpty() + ) + } + itemsIndexed(items = projects) { index, project -> + bodyText(project.name, modifier = Modifier.selectable( + selected = selectedIndices.contains(index), + onClick = { + if (!selectedIndices.contains(index)) { + selectedIndices.add(index) + selectedProjectIds.add(project.id) + } else { + selectedIndices.remove(index) + selectedProjectIds.remove(project.id) + } + } + ).clip(shape = RoundedCornerShape(10.dp)).background( + if (selectedIndices.contains(index)) { + MaterialTheme.colorScheme.onSecondary + } else MaterialTheme.colorScheme.background + ).fillParentMaxWidth().padding(start = 16.dp, end = 16.dp, top = 16.dp)) + Spacer(modifier = Modifier.padding(8.dp)) + } + + } + } + + } + } + } +} diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/EditProjectScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/EditProjectScreen.kt new file mode 100644 index 000000000..f7cf6ad6c --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/EditProjectScreen.kt @@ -0,0 +1,116 @@ +package screen + +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.* +import com.example.compose.AppTheme +import composable.* +import databaseInteraction.Driver +import databaseInteraction.Provider +import icon.addIcon +import kotlinx.coroutines.runBlocking + +/** + * Screen to edit a project + * @param projectId Project to edit + */ +class EditProjectScreen(private val projectId: Long) : Screen { + @Composable + @Preview + override fun Content() { + val projectData = Provider.provideProjectDataSource(Driver.createDriver()) + val dependencyData = Provider.provideDependencyDataSource(Driver.createDriver()) + val project = runBlocking { projectData.getProjectById(projectId) } + var projectDependencies = dependencyData.getAllDependenciesByProjectId(projectId).collectAsState(initial = emptyList()).value + val dependencyCopyList = SnapshotStateList() + projectDependencies.forEach{dependencyCopyList.add(it)} + val navigator = LocalNavigator.currentOrThrow + val snackbarHostState = remember { SnackbarHostState() } + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold( + snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, + topBar = { + if (project != null) { + title(project.name) + } else { + title("FEHLER! Bitte gehen Sie zurück ins Hauptmenü") + } + + }, + bottomBar = { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Start + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.popUntilRoot() + }) { + Text("Fertig") + } + } + } + ) { + LazyColumn(Modifier.padding(it)) { + item { + title("Spielablauf:") + bodyText("Bitte wählen Sie Fragen aus, die in das Spiel integriert werden sollen. Jede Frage muss in Abhängigkeit zu einer anderen Frage stehen. Die Art der Abhängigkeiten kann dabei frei gewählt werden. Hat eine Frage mehrere Abhängigkeiten, muss die Frage mehrmals ausgewählt und zugeordnet werden.") + + } + items(items = dependencyCopyList) { dependency -> + dependencyCard(dependency, action = {dependencyCopyList.remove(dependency) }) + } + item { + Card( + Modifier.padding(top = 16.dp, start = 128.dp, end = 128.dp).clickable { + navigator.push(QuestionChooserScreen(Dependency(projectId))) + } + ) { + Row( + Modifier.padding(start = 48.dp, end = 48.dp).fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Image( + addIcon(MaterialTheme.colorScheme.onBackground), + "Add", + Modifier.padding(4.dp) + ) + Text("Neuen Aufgabenteil hinzufügen", textAlign = TextAlign.Center) + } + } + } + } + } + } + } + } +} diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/HomeScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/HomeScreen.kt new file mode 100644 index 000000000..832bccb70 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/HomeScreen.kt @@ -0,0 +1,100 @@ +@file:OptIn(ExperimentalLayoutApi::class) + +package screen + +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import com.example.compose.AppTheme +import composable.clickableIconCardExit +import composable.clickableIconCardNavigate +import composable.title +import icon.* + +/** + * Homescreen of the Application. Allows to navigate to all the functionalities. + */ +class HomeScreen : Screen { + @Composable + @Preview + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + Box ( + Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background).padding(8.dp) + .verticalScroll(rememberScrollState()) + ) { + title("Willkommen im Dungeon Task Manager") + Box( + Modifier.fillMaxSize().padding(top = 30.dp).align(Alignment.Center) + ) { + FlowRow( + Modifier.height(intrinsicSize = IntrinsicSize.Max) + .width(intrinsicSize = IntrinsicSize.Max) + .align(Alignment.Center).padding(top = 60.dp), + maxItemsInEachRow = 3, + ) { + clickableIconCardNavigate( + projectIcon(MaterialTheme.colorScheme.onSurfaceVariant), + "Projekte", + Modifier.padding(8.dp), + navigator, + ProjectScreen() + ) + clickableIconCardNavigate( + addFileIcon(MaterialTheme.colorScheme.onSurfaceVariant), + "Aufgabendatei\ngenerieren", + Modifier.padding(8.dp), + navigator, + CreateTaskFileScreen() + ) + clickableIconCardNavigate( + squarePlusIcon(MaterialTheme.colorScheme.onSurfaceVariant), + "Neue Aufgabe", + Modifier.padding(8.dp), + navigator, + QuestionChoiceScreen() + ) + /* + clickableIconCardNavigate( + walkingIcon(MaterialTheme.colorScheme.onSurfaceVariant), + "Simulieren", + Modifier.padding(8.dp), + navigator, + HomeScreen() + )*/ + clickableIconCardNavigate( + rectangleListIcon(MaterialTheme.colorScheme.onSurfaceVariant), + "Fragenliste", + Modifier.padding(8.dp), + navigator, + QuestionOverviewScreen() + ) + clickableIconCardExit( + exitIcon(MaterialTheme.colorScheme.onSurfaceVariant), + "Beenden", + Modifier.padding(8.dp) + ) + } + } + + } + } + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/MultipleChoiceChooseAnswerIndexScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/MultipleChoiceChooseAnswerIndexScreen.kt new file mode 100644 index 000000000..f7f367430 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/MultipleChoiceChooseAnswerIndexScreen.kt @@ -0,0 +1,109 @@ +package screen + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.MultipleChoiceQuestion +import com.example.compose.AppTheme +import composable.bodyText +import composable.title +import kotlinx.coroutines.launch + +/** + * Screen to choose the correct answers of a multiple choice question + * @param question Multiple choice question to choose the answers from + */ +class MultipleChoiceChooseAnswerIndexScreen(private val question: MultipleChoiceQuestion) : Screen { + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + val selectedIndices = remember { mutableStateListOf() } + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold( + snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, + ) { + LazyColumn( + Modifier.padding( + start = 24.dp, + top = 24.dp, + end = 24.dp + ).fillMaxSize() + ) { + item { + title("Bitte wählen Sie die korrekte Antwort aus") + } + itemsIndexed(items = question.answers) { index, answer -> + bodyText(answer, modifier = Modifier.selectable( + selected = selectedIndices.contains(index), + onClick = { + if(!selectedIndices.contains(index)){ + selectedIndices.add(index) + }else{ + selectedIndices.remove(index) + } + } + ).clip(shape = RoundedCornerShape(10.dp)).background( + if (selectedIndices.contains(index)) { + MaterialTheme.colorScheme.onSecondary + } else MaterialTheme.colorScheme.background + ).fillParentMaxWidth().padding(start = 16.dp, end = 16.dp, top = 16.dp)) + Spacer(modifier = Modifier.padding(8.dp)) + } + item { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + if (selectedIndices.isNotEmpty()) { + question.correctAnswerIndices = selectedIndices + navigator.push( + CheckMultipleChoiceQuestionScreen(question) + ) + } else { + scope.launch { + snackbarHostState.showSnackbar( + message = "Bitte wählen Sie mindestens eine korrekte Antwort aus", + withDismissAction = true + ) + } + } + }) { + Text("Weiter") + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/ProjectScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/ProjectScreen.kt new file mode 100644 index 000000000..399c08313 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/ProjectScreen.kt @@ -0,0 +1,147 @@ +package screen + +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.* +import com.example.compose.AppTheme +import composable.bodyText +import composable.inputTextField +import composable.title +import databaseInteraction.Driver +import databaseInteraction.Provider +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + +/** + * Welcome project screen. Passes the chosen project to the next screen + */ +class ProjectScreen : Screen { + @Composable + @Preview + override fun Content() { + val projectData = Provider.provideProjectDataSource(Driver.createDriver()) + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + val navigator = LocalNavigator.currentOrThrow + var newProjectName by rememberSaveable { mutableStateOf("") } + var selectedIndex by rememberSaveable { mutableStateOf(-1) } + val projects = projectData.getAllProjects().collectAsState(initial = emptyList()).value + + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold( + snackbarHost = { SnackbarHost(hostState = snackbarHostState) } , + topBar = { + Column { + title("Projekte") + bodyText( + "Erstellen Sie ein neues Projekt erstellen oder bearbeiten Sie ein vorhandenes", + modifier = Modifier.padding(start = 24.dp, end = 24.dp) + ) + } + }, + bottomBar = { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + scope.launch { + if (selectedIndex == -1 && newProjectName.isEmpty()){ + snackbarHostState.showSnackbar( + message = "Bitte wählen Sie ein Projekt aus oder tragen Sie einen Namen für das neue Projekt ein", + withDismissAction = true + ) + }else if (selectedIndex != -1 && newProjectName.isNotEmpty()){ + snackbarHostState.showSnackbar( + message = "Bitte wählen Sie ein Projekt aus oder tragen Sie einen Namen für das neue Projekt ein.\nBeides ist nicht zulässig!", + withDismissAction = true + ) + }else if(selectedIndex != -1){ + navigator.push(EditProjectScreen(projects.get(selectedIndex).id)) + }else{ + projectData.insertProject(newProjectName) + navigator.push(EditProjectScreen(projectData.getProjectId(newProjectName)!!)) + } + } + }) { + Text("Weiter") + } + + } + } + ) { + Column(modifier = Modifier.padding(it)) { + inputTextField( + label = "Neues Projekt anlegen", + modifier = Modifier.padding(start = 24.dp, end = 24.dp), + value = newProjectName, + onValueChange = { newProjectName = it }, + isError = false + ) + LazyColumn( + Modifier.padding( + start = 24.dp, + top = 24.dp, + end = 24.dp + ).fillMaxSize() + ) { + item { + title("Vorhandene Projekte") + } + itemsIndexed(items = projects) { index, project -> + bodyText(project.name, modifier = Modifier.selectable( + selected = selectedIndex == index, + onClick = { + selectedIndex = if(selectedIndex != index) { + index + } else { + -1 + } + } + ).clip(shape = RoundedCornerShape(10.dp)).background( + if (index == selectedIndex) { + MaterialTheme.colorScheme.onSecondary + } else MaterialTheme.colorScheme.background + ).fillParentMaxWidth().padding(start = 16.dp, end = 16.dp)) + Spacer(modifier = Modifier.padding(8.dp)) + } + + + } + + } + } + + } + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionBChooserScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionBChooserScreen.kt new file mode 100644 index 000000000..71f76146a --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionBChooserScreen.kt @@ -0,0 +1,331 @@ +package screen + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.* +import com.example.compose.AppTheme +import composable.* +import databaseInteraction.Driver +import databaseInteraction.Provider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + +/** + * Screen to choose a Question. + */ +class QuestionBChooserScreen(var dependency: Dependency) : Screen { + private fun filterSearchbar(searchBar: String, item: Question): Boolean { + if (item.description.lowercase().contains(searchBar.lowercase())) { + return true + } else if (item.explanation.lowercase().contains(searchBar.lowercase())) { + return true + } + return false + } + + private suspend fun getAnswersToQuestionId(questionId: Long): List { + val answerData = Provider.provideAnswerDataSource(Driver.createDriver()) + val answerDataList = answerData.getAnswersByQuestionId(questionId).firstOrNull() + //sval aaa = answerDataList.forEach { } + val answers = mutableListOf() + answerDataList!!.forEach{answer -> + answers.add(answer.answer) + } + //LOAD ANSWER + return answers + } + + private suspend fun getCorrectAnswersByQuestionId(questionId: Long): List { + val answerData = Provider.provideAnswerDataSource(Driver.createDriver()) + val answerList = answerData.getCorrectAnswersByQuestionId(questionId).firstOrNull() + //LOAD ANSWER + val answers = mutableListOf() + answerList!!.forEach{answer -> + answers.add(answer.answer) + } + //LOAD ANSWER + return answers + } + + + private fun getAssignmentsToQuestionId(): List { + Provider.provideAssignmentDataSource(Driver.createDriver()) + val assignmentList = mutableStateListOf() + assignmentList.add(Assignment()) + assignmentList.add(Assignment("TERMa", "TermB")) + assignmentList.add(Assignment(termB = "TermB")) + assignmentList.add(Assignment("TERMa")) + //LOAD ANSWER + return assignmentList + } + + private suspend fun getTagsToQuestionId(questionId: Long): List { + val tagData = Provider.provideTagDataSource(Driver.createDriver()) + val tagDataList = tagData.getTagsByQuestionId(questionId).firstOrNull() + //LOAD ANSWER + val tags = mutableListOf() + tagDataList!!.forEach{tag -> + tags.add(tag) + } + //LOAD TAGS + return tags + } + + private suspend fun getAllQuestionsAsClasses(questions: List): List { + val questionList = mutableStateListOf() + var tags: List + var answers: List + var correctAnswers: List + var correctAnswerIndices= mutableListOf() + var assignments: List + //LOAD QuestionsDATA + //FOR EACH QUESTION -> + questions.forEach { question -> + //GET ANSWERS + tags = getTagsToQuestionId(question.id) + when (question.type) { + "SINGLE_CHOICE_QUESTION" -> { + answers = getAnswersToQuestionId(question.id) + correctAnswers = getCorrectAnswersByQuestionId(question.id) + answers.forEachIndexed { index, answer -> + if (correctAnswers.contains(answer)) { + correctAnswerIndices.add(index) + } + } + questionList.add( + SingleChoiceQuestion( + 0, + question.description, + question.points.toInt(), + question.pointsToPass.toInt(), + question.explanation, + answers, + tags, + correctAnswerIndices[0] + ) + ) + correctAnswerIndices = mutableStateListOf() + } + "MULTIPLE_CHOICE_QUESTION" -> { + answers = getAnswersToQuestionId(question.id) + correctAnswers = getCorrectAnswersByQuestionId(question.id) + answers.forEachIndexed { index, answer -> + if (correctAnswers.contains(answer)) { + correctAnswerIndices.add(index) + } + } + questionList.add( + MultipleChoiceQuestion( + 0, + question.description, + question.points.toInt(), + question.pointsToPass.toInt(), + question.explanation, + answers = answers, + tags = tags, + correctAnswerIndices = correctAnswerIndices + ) + ) + correctAnswerIndices = mutableStateListOf() + } + "ASSIGN_QUESTION" -> { + tags = getTagsToQuestionId(question.id) + assignments = getAssignmentsToQuestionId() + questionList.add( + AssignQuestion( + 0, + question.description, + question.points.toInt(), + question.pointsToPass.toInt(), + question.explanation, + assignments = assignments + ) + ) + } + else -> { + } + } + // GET TAGS + } + return questionList + } + + + + + @Composable + override fun Content() { + val tagFilterList = remember { mutableStateListOf() } + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + //Get all questions from DB + val questionData = Provider.provideQuestionDataSource(Driver.createDriver()) + val questionDataList = questionData.getAllQuestions().collectAsState(initial = emptyList()).value + //Turn all Questions to full Question classes and connect them to Answers and Tags + val questionList = runBlocking { + getAllQuestionsAsClasses(questionDataList) + } + var searchBar by rememberSaveable { mutableStateOf("") } + var chosenQuestion: Question? = null + val navigator = LocalNavigator.currentOrThrow + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold( + snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, + topBar = { + Column { + inputTextField(Modifier, searchBar, onValueChange = { searchBar = it }, "Suche", false) + if(dependency.questionA == null){ + title("Frage 1 zum hinzufügen auswählen") + }else{ + title("Frage 2 zum hinzufügen auswählen") + } + + } + }, + bottomBar = { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Start + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + if(chosenQuestion != null){ + if (dependency.questionA == chosenQuestion){ + scope.launch { + snackbarHostState.showSnackbar( + message = "Frage 1 kann nicht gleich Frage 2 sein!", + withDismissAction = true + ) + } + }else{ + if (dependency.questionA !=null) { + dependency.questionB = chosenQuestion + navigator.push(CreateDependencyScreen(dependency = dependency)) + }else{ + dependency.questionA = chosenQuestion + navigator.push(QuestionChooserScreen(dependency = dependency)) + } + } + }else{ + scope.launch { + snackbarHostState.showSnackbar( + message = "Bitte wählen Sie eine Frage aus!", + withDismissAction = true + ) + } + } + }) { + Text("Weiter") + } + } + } + ) { + LazyColumn( + Modifier.padding( + it + ) + ) { + item { bodyText("Ausgewählte Frage:") } + item { + if (chosenQuestion != null) { + expandableItem( + question = chosenQuestion!!, + action = {}, + modifier = Modifier.fillMaxWidth(), + mode = 0 + ) + } else { + bodyText("Bitte wählen Sie eine Frage zum hinzufügen aus.\n\nEine Frage wählen Sie mithilfe des hinzufügen Symbols(+) ganz unten in jeder Ausgeklappten Frage aus.", size = 20) + } + } + item { + HorizontalDivider( + Modifier.padding(12.dp), + color = MaterialTheme.colorScheme.onSecondary, + thickness = 10.dp + ) + } + + items(items = questionList) { item -> + if (tagFilterList.isNotEmpty() && searchBar.isNotEmpty()) { + tagFilterList.forEach { + if (item.tags.contains(it)) { + if (filterSearchbar(it, item)) { + if (filterSearchbar(searchBar, item)) { + expandableItem( + question = item, + action = {}, + modifier = Modifier.fillMaxWidth(), + mode = 1 + ) + } + } + } + } + } else if (searchBar.isEmpty() && tagFilterList.isNotEmpty()) { + tagFilterList.forEach { + if (item.tags.contains(it)) { + if (filterSearchbar(it, item)) { + expandableItem( + question = item, + action = { chosenQuestion = item }, + modifier = Modifier.fillMaxWidth(), + mode = 1 + ) + } + } + } + } else if (searchBar.isNotEmpty() && tagFilterList.isEmpty()) { + if (filterSearchbar(searchBar, item)) { + expandableItem( + question = item, + action = { chosenQuestion = item }, + modifier = Modifier.fillMaxWidth(), + mode = 1 + ) + } + + } + if (searchBar.isEmpty() && tagFilterList.isEmpty()) { + expandableItem( + question = item, + action = { chosenQuestion = item }, + modifier = Modifier.fillMaxWidth(), + mode = 1 + ) + } + } + } + } + } + + + } + + } +} + diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionChoiceScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionChoiceScreen.kt new file mode 100644 index 000000000..7a83f11e8 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionChoiceScreen.kt @@ -0,0 +1,83 @@ +@file:OptIn(ExperimentalLayoutApi::class) + +package screen + +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import com.example.compose.AppTheme +import composable.clickableIconCardNavBack +import composable.clickableIconCardNavigate +import composable.title +import icon.* + +/** + * Screen to navigate to the different question creation screens + */ +class QuestionChoiceScreen : Screen { + @Composable + @Preview + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + Box{ + title("Bitte wählen Sie den Fragetypen") + Box( + Modifier.fillMaxSize().padding(top = 100.dp).verticalScroll(rememberScrollState()), + contentAlignment = Alignment.Center, + ) { + FlowRow( + Modifier.height(intrinsicSize = IntrinsicSize.Max) + .width(intrinsicSize = IntrinsicSize.Max) + .align(Alignment.Center), + horizontalArrangement = Arrangement.Center, + maxItemsInEachRow = 3 + ) { + clickableIconCardNavigate( + singleChoiceIcon(MaterialTheme.colorScheme.onBackground), + "Single-Choice", + Modifier.padding(8.dp), + navigator, + CreateSingleChoiceScreen() + ) + clickableIconCardNavigate( + multipleChopiceIcon(MaterialTheme.colorScheme.onBackground), + "Multiple-Choice", + Modifier.padding(8.dp), + navigator, + CreateMultipleChoiceScreen() + ) + clickableIconCardNavigate( + assignIcon(MaterialTheme.colorScheme.onBackground), + "Zuordnungs-\naufgabe", + Modifier.padding(8.dp), + navigator, + CreateAssignScreen() + ) + clickableIconCardNavBack( + backIcon(MaterialTheme.colorScheme.onBackground), + "Zurück", + Modifier.padding(8.dp), + navigator, + ) + } + } + } + } + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionChooserScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionChooserScreen.kt new file mode 100644 index 000000000..f363a91ef --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionChooserScreen.kt @@ -0,0 +1,331 @@ +package screen + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.* +import com.example.compose.AppTheme +import composable.* +import databaseInteraction.Driver +import databaseInteraction.Provider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + +/** + * Screen to choose a Question. + */ +class QuestionChooserScreen(var dependency: Dependency) : Screen { + private fun filterSearchbar(searchBar: String, item: Question): Boolean { + if (item.description.lowercase().contains(searchBar.lowercase())) { + return true + } else if (item.explanation.lowercase().contains(searchBar.lowercase())) { + return true + } + return false + } + + private suspend fun getAnswersToQuestionId(questionId: Long): List { + val answerData = Provider.provideAnswerDataSource(Driver.createDriver()) + val answerDataList = answerData.getAnswersByQuestionId(questionId).firstOrNull() + //sval aaa = answerDataList.forEach { } + var answers = mutableListOf() + answerDataList!!.forEach(){answer -> + answers.add(answer.answer) + } + //LOAD ANSWER + return answers + } + + private suspend fun getCorrectAnswersByQuestionId(questionId: Long): List { + val answerData = Provider.provideAnswerDataSource(Driver.createDriver()) + val answerList = answerData.getCorrectAnswersByQuestionId(questionId).firstOrNull() + //LOAD ANSWER + var answers = mutableListOf() + answerList!!.forEach(){answer -> + answers.add(answer.answer) + } + //LOAD ANSWER + return answers + } + + + private suspend fun getAssignmentsToQuestionId(questionId: Long): List { + val assignmentData = Provider.provideAssignmentDataSource(Driver.createDriver()) + val assignmentList = mutableStateListOf() + assignmentList.add(Assignment()) + assignmentList.add(Assignment("TERMa", "TermB")) + assignmentList.add(Assignment(termB = "TermB")) + assignmentList.add(Assignment("TERMa")) + //LOAD ANSWER + return assignmentList + } + + private suspend fun getTagsToQuestionId(questionId: Long): List { + val tagData = Provider.provideTagDataSource(Driver.createDriver()) + val tagQuestionData = Provider.provideQuestionTagDataSource(Driver.createDriver()) + val tagDataList = tagData.getTagsByQuestionId(questionId).firstOrNull() + //LOAD ANSWER + var tags = mutableListOf() + tagDataList!!.forEach(){tag -> + tags.add(tag) + } + //LOAD TAGS + return tags + } + + private suspend fun getQuestions(): List { + val questionData = Provider.provideQuestionDataSource(Driver.createDriver()) + val tagQuestionData = Provider.provideQuestionTagDataSource(Driver.createDriver()) + val questionList = mutableStateListOf() + return questionList + } + + + private suspend fun getAllQuestionsAsClasses(questions: List): List { + val questionData = Provider.provideQuestionDataSource(Driver.createDriver()) + //val answerData = Provider.provideAnswerDataSource(Driver.createDriver()) + val tagData = Provider.provideTagDataSource(Driver.createDriver()) + val questionList = mutableStateListOf() + var tags: List = mutableStateListOf() + var answers: List = mutableStateListOf() + var correctAnswers: List = mutableStateListOf() + var correctAnswerIndices = mutableListOf() + var assignments: List = mutableStateListOf() + //LOAD QuestionsDATA + val questionDataList = getQuestions() + //FOR EACH QUESTION -> + questions.forEach { question -> + //GET ANSWERS + tags = getTagsToQuestionId(question.id) + if (question.type == "SINGLE_CHOICE_QUESTION") { + answers = getAnswersToQuestionId(question.id) + correctAnswers = getCorrectAnswersByQuestionId(question.id) + answers.forEachIndexed() { index, answer -> + if (correctAnswers.contains(answer)) { + correctAnswerIndices.add(index) + } + } + questionList.add( + SingleChoiceQuestion( + 0, + question.description, + question.points.toInt(), + question.pointsToPass.toInt(), + question.explanation, + answers, + tags, + correctAnswerIndices[0] + ) + ) + correctAnswerIndices = mutableStateListOf() + } else if (question.type == "MULTIPLE_CHOICE_QUESTION") { + answers = getAnswersToQuestionId(question.id) + correctAnswers = getCorrectAnswersByQuestionId(question.id) + answers.forEachIndexed() { index, answer -> + if (correctAnswers.contains(answer)) { + correctAnswerIndices.add(index) + } + } + questionList.add( + MultipleChoiceQuestion( + 0, + question.description, + question.points.toInt(), + question.pointsToPass.toInt(), + question.explanation, + answers = answers, + tags = tags, + correctAnswerIndices = correctAnswerIndices + ) + ) + correctAnswerIndices = mutableStateListOf() + } else if (question.type == "ASSIGN_QUESTION") { + tags = getTagsToQuestionId(question.id) + assignments = getAssignmentsToQuestionId(questionId = question.id) + questionList.add( + AssignQuestion( + 0, + question.description, + question.points.toInt(), + question.pointsToPass.toInt(), + question.explanation, + assignments = assignments + ) + ) + } else { + } + // GET TAGS + } + return questionList + } + + + @Composable + override fun Content() { + val tagFilterList = remember { mutableStateListOf() } + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + val tagData = Provider.provideTagDataSource(Driver.createDriver()) + val tagList = tagData.getAllTags().collectAsState(initial = emptyList()).value + + //Get all questions from DB + val questionData = Provider.provideQuestionDataSource(Driver.createDriver()) + val questionDataList = questionData.getAllQuestions().collectAsState(initial = emptyList()).value + //Turn all Questions to full Question classes and connect them to Answers and Tags + var questionList = runBlocking { + getAllQuestionsAsClasses(questionDataList) + } + var searchBar by rememberSaveable { mutableStateOf("") } + var chosenQuestion: Question? = null + val navigator = LocalNavigator.currentOrThrow + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold( + snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, + topBar = { + Column { + inputTextField(Modifier, searchBar, onValueChange = { searchBar = it }, "Suche", false) + if(dependency.questionA == null){ + title("Frage 1 zum hinzufügen auswählen") + }else{ + title("Frage 2 zum hinzufügen auswählen") + } + + } + }, + bottomBar = { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Start + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + if(chosenQuestion != null){ + if (dependency.questionA !=null){ + dependency.questionB = chosenQuestion + navigator.push(CreateDependencyScreen(dependency = dependency)) + }else{ + dependency.questionA = chosenQuestion + navigator.push(QuestionBChooserScreen(dependency = dependency)) + } + }else{ + scope.launch { + snackbarHostState.showSnackbar( + message = "Bitte wählen Sie eine Frage aus!", + withDismissAction = true + ) + } + } + }) { + Text("Weiter") + } + } + } + ) { + LazyColumn( + Modifier.padding( + it + ) + ) { + item { bodyText("Ausgewählte Frage:") } + item { + if (chosenQuestion != null) { + expandableItem( + question = chosenQuestion!!, + action = {}, + modifier = Modifier.fillMaxWidth(), + mode = 3 + ) + } else { + bodyText("Bitte wählen Sie eine Frage zum hinzufügen aus.\n\nEine Frage wählen Sie mithilfe des hinzufügen Symbols(+) ganz unten in jeder Ausgeklappten Frage aus.", size = 20) + } + } + item { + HorizontalDivider( + Modifier.padding(12.dp), + color = MaterialTheme.colorScheme.onSecondary, + thickness = 10.dp + ) + } + + items(items = questionList) { item -> + if (tagFilterList.isNotEmpty() && searchBar.isNotEmpty()) { + tagFilterList.forEach { + if (item.tags.contains(it)) { + if (filterSearchbar(it, item)) { + if (filterSearchbar(searchBar, item)) { + expandableItem( + question = item, + action = {}, + modifier = Modifier.fillMaxWidth(), + mode = 1 + ) + } + } + } + } + } else if (searchBar.isEmpty() && tagFilterList.isNotEmpty()) { + tagFilterList.forEach { + if (item.tags.contains(it)) { + if (filterSearchbar(it, item)) { + expandableItem( + question = item, + action = { chosenQuestion = item }, + modifier = Modifier.fillMaxWidth(), + mode = 1 + ) + } + } + } + } else if (searchBar.isNotEmpty() && tagFilterList.isEmpty()) { + if (filterSearchbar(searchBar, item)) { + expandableItem( + question = item, + action = { chosenQuestion = item }, + modifier = Modifier.fillMaxWidth(), + mode = 1 + ) + } + + } + if (searchBar.isEmpty() && tagFilterList.isEmpty()) { + expandableItem( + question = item, + action = { chosenQuestion = item }, + modifier = Modifier.fillMaxWidth(), + mode = 1 + ) + } + } + } + } + } + + + } + + } +} + diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionOverviewScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionOverviewScreen.kt new file mode 100644 index 000000000..ae2e3a845 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/QuestionOverviewScreen.kt @@ -0,0 +1,327 @@ +package screen + +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.* +import com.example.compose.AppTheme +import composable.checkBoxFilter +import composable.expandableItem +import composable.inputTextField +import databaseInteraction.Driver +import databaseInteraction.Provider +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.runBlocking + +/** + * Scrren to get an overview of all Questions + */ +class QuestionOverviewScreen : Screen { + private fun filterSearchbar(searchBar: String, item: Question): Boolean { + if (item.description.lowercase().contains(searchBar.lowercase())) { + return true + } else if (item.explanation.lowercase().contains(searchBar.lowercase())) { + return true + } + return false + } + + + private suspend fun getAnswersToQuestionId(questionId: Long): List { + val answerData = Provider.provideAnswerDataSource(Driver.createDriver()) + val answerDataList = answerData.getAnswersByQuestionId(questionId).firstOrNull() + //sval aaa = answerDataList.forEach { } + val answers = mutableListOf() + answerDataList!!.forEach(){answer -> + answers.add(answer.answer) + } + //LOAD ANSWER + return answers + } + + private suspend fun getCorrectAnswersByQuestionId(questionId: Long): List { + val answerData = Provider.provideAnswerDataSource(Driver.createDriver()) + val answerList = answerData.getCorrectAnswersByQuestionId(questionId).firstOrNull() + //LOAD ANSWER + var answers = mutableListOf() + answerList!!.forEach(){answer -> + answers.add(answer.answer) + } + //LOAD ANSWER + return answers + } + + + private suspend fun getAssignmentsToQuestionId(questionId: Long): List { + val assignmentData = Provider.provideAssignmentDataSource(Driver.createDriver()) + val assignmentList = assignmentData.getAssignmentsByQuestionId(questionId).firstOrNull() + val assignments = mutableListOf() + if (assignmentList!= null){ + assignmentList.forEach{ + assignments.add(Assignment(it.termA!!,it.termB!!)) + } + } + return assignments + } + + private suspend fun getTagsToQuestionId(questionId: Long): List { + val tagData = Provider.provideTagDataSource(Driver.createDriver()) + val tagQuestionData = Provider.provideQuestionTagDataSource(Driver.createDriver()) + val tagDataList = tagData.getTagsByQuestionId(questionId).firstOrNull() + //LOAD ANSWER + var tags = mutableListOf() + tagDataList!!.forEach(){tag -> + tags.add(tag) + } + //LOAD TAGS + return tags + } + + private suspend fun getQuestions(): List { + val questionData = Provider.provideQuestionDataSource(Driver.createDriver()) + val tagQuestionData = Provider.provideQuestionTagDataSource(Driver.createDriver()) + val questionList = mutableStateListOf() + return questionList + } + + + private suspend fun getAllQuestionsAsClasses(questions: List): List { + val questionData = Provider.provideQuestionDataSource(Driver.createDriver()) + //val answerData = Provider.provideAnswerDataSource(Driver.createDriver()) + val tagData = Provider.provideTagDataSource(Driver.createDriver()) + val questionList = mutableStateListOf() + var tags: List = mutableStateListOf() + var answers: List = mutableStateListOf() + var correctAnswers: List = mutableStateListOf() + var correctAnswerIndices = mutableListOf() + var assignments: List = mutableStateListOf() + //LOAD QuestionsDATA + val questionDataList = getQuestions() + //FOR EACH QUESTION -> + questions.forEach { question -> + //GET ANSWERS + tags = getTagsToQuestionId(question.id) + if (question.type == "SINGLE_CHOICE_QUESTION") { + answers = getAnswersToQuestionId(question.id) + correctAnswers = getCorrectAnswersByQuestionId(question.id) + answers.forEachIndexed() { index, answer -> + if (correctAnswers.contains(answer)) { + correctAnswerIndices.add(index) + } + } + questionList.add( + SingleChoiceQuestion( + 0, + question.description, + question.points.toInt(), + question.pointsToPass.toInt(), + question.explanation, + answers, + tags, + correctAnswerIndices[0] + ) + ) + correctAnswerIndices = mutableStateListOf() + } else if (question.type == "MULTIPLE_CHOICE_QUESTION") { + answers = getAnswersToQuestionId(question.id) + correctAnswers = getCorrectAnswersByQuestionId(question.id) + answers.forEachIndexed() { index, answer -> + if (correctAnswers.contains(answer)) { + correctAnswerIndices.add(index) + } + } + questionList.add( + MultipleChoiceQuestion( + 0, + question.description, + question.points.toInt(), + question.pointsToPass.toInt(), + question.explanation, + answers = answers, + tags = tags, + correctAnswerIndices = correctAnswerIndices + ) + ) + correctAnswerIndices = mutableStateListOf() + } else if (question.type == "ASSIGN_QUESTION") { + tags = getTagsToQuestionId(question.id) + assignments = getAssignmentsToQuestionId(questionId = question.id) + questionList.add( + AssignQuestion( + 0, + question.description, + question.points.toInt(), + question.pointsToPass.toInt(), + question.explanation, + assignments = assignments + ) + ) + } else { + } + // GET TAGS + } + return questionList + } + + @Composable + @Preview + override fun Content() { + val tagData = Provider.provideTagDataSource(Driver.createDriver()) + val tagList = tagData.getAllTags().collectAsState(initial = emptyList()).value + val tagFilterList = remember { mutableStateListOf() } + //Get all questions from DB + val questionData = Provider.provideQuestionDataSource(Driver.createDriver()) + val questionDataList = questionData.getAllQuestions().collectAsState(initial = emptyList()).value + //Turn all Questions to full Question classes and connect them to Answers and Tags + var questionList = runBlocking { + getAllQuestionsAsClasses(questionDataList) + } + var searchBar by rememberSaveable { mutableStateOf("") } + val navigator = LocalNavigator.currentOrThrow + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold( + topBar = { + inputTextField(Modifier, searchBar, onValueChange = { searchBar = it }, "Suche", false) + }, + bottomBar = { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Start + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + } + } + ) { + Row { + Column( + Modifier.padding( + it + ).weight(1f) + ) { + tagList.forEach { tag -> + checkBoxFilter(tag.tag, onCheckedTrue = { + tagFilterList.add(tag.tag) + }, + onCheckedFalse = { + tagFilterList.remove(tag.tag) + }) + } + } + LazyColumn( + Modifier.padding( + it + ).weight(5f) + ) { + items(items = questionList) { item -> + if (tagFilterList.isNotEmpty() && searchBar.isNotEmpty()) { + tagFilterList.forEach { + if (item.tags.contains(it)) { + if (filterSearchbar(it, item)) { + if (filterSearchbar(searchBar, item)) { + expandableItem(question = item, action = { + runBlocking { + questionData.deleteQuestionById( + questionData.getQuestionId( + item.description, + item.explanation, + item.points.toLong(), + item.pointsToPass.toLong() + )!! + ) + getAllQuestionsAsClasses(questionDataList) + } + + }, modifier = Modifier.fillMaxWidth()) + } + } + } + } + } else if (searchBar.isEmpty() && tagFilterList.isNotEmpty()) { + tagFilterList.forEach { + if (item.tags.contains(it)) { + if (filterSearchbar(it, item)) { + expandableItem( + question = item, + action = { runBlocking { + questionData.deleteQuestionById( + questionData.getQuestionId( + item.description, + item.explanation, + item.points.toLong(), + item.pointsToPass.toLong() + )!! + ) + getAllQuestionsAsClasses(questionDataList) + }}, + modifier = Modifier.fillMaxWidth() + ) + } + } + } + } else if (searchBar.isNotEmpty() && tagFilterList.isEmpty()) { + if (filterSearchbar(searchBar, item)) { + expandableItem( + question = item, + action = { runBlocking { + questionData.deleteQuestionById( + questionData.getQuestionId( + item.description, + item.explanation, + item.points.toLong(), + item.pointsToPass.toLong() + )!! + ) + getAllQuestionsAsClasses(questionDataList) + }}, + modifier = Modifier.fillMaxWidth() + ) + } + + } + if (searchBar.isEmpty() && tagFilterList.isEmpty()) { + expandableItem( + question = item, + action = { runBlocking { + questionData.deleteQuestionById( + questionData.getQuestionId( + item.description, + item.explanation, + item.points.toLong(), + item.pointsToPass.toLong() + )!! + ) + getAllQuestionsAsClasses(questionDataList) + }}, + modifier = Modifier.fillMaxWidth() + ) + } + } + } + } + } + + } + } + + } +} diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/SingleChoiceChooseAnswerIndexScreen.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/SingleChoiceChooseAnswerIndexScreen.kt new file mode 100644 index 000000000..592acb93a --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/screen/SingleChoiceChooseAnswerIndexScreen.kt @@ -0,0 +1,106 @@ +package screen + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import classes.SingleChoiceQuestion +import com.example.compose.AppTheme +import composable.bodyText +import composable.title +import kotlinx.coroutines.launch + +/** + * Screen to choose the Correct Answer of the created Question and pass the Question further to the next Screen + * @param question Single Choice Question created in Screen before + */ +class SingleChoiceChooseAnswerIndexScreen(private val question: SingleChoiceQuestion) : Screen { + + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + var selectedIndex by remember { mutableStateOf(-1) } + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background, + ) { + Scaffold ( + snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, + ){ + LazyColumn( + Modifier.padding( + start = 24.dp, + top = 24.dp, + end = 24.dp + ).fillMaxSize() + ) { + item { + title("Bitte wählen Sie die korrekte Antwort aus") + } + itemsIndexed(items = question.answers) { index, answer -> + bodyText(answer, modifier = Modifier.selectable( + selected = selectedIndex == index, + onClick = { + question.correctAnswerIndex = index + selectedIndex = index + } + ).clip(shape = RoundedCornerShape(10.dp)).background( + if (index == question.correctAnswerIndex){ + MaterialTheme.colorScheme.onSecondary + }else MaterialTheme.colorScheme.background + ).fillParentMaxWidth().padding(start = 16.dp, end = 16.dp)) + Spacer(modifier = Modifier.padding(8.dp)) + } + item { + Row(//verticalAlignment = Alignment.Bottom, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + navigator.pop() + }) { + Text("Zurück") + } + Button( + modifier = Modifier.padding(16.dp), + colors = ButtonDefaults.buttonColors(), + onClick = { + if (question.correctAnswerIndex !=-1) { + navigator.push( + CheckSingleChoiceQuestionScreen(question = question) + ) + } else { + scope.launch { + snackbarHostState.showSnackbar( + message = "Bitte wählen Sie die korrekte Antwort aus", + withDismissAction = true + ) + } + } + }) { + Text("Weiter") + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/ui/theme/Color.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/ui/theme/Color.kt new file mode 100644 index 000000000..f9c29b7b9 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/ui/theme/Color.kt @@ -0,0 +1,67 @@ +package com.example.compose +import androidx.compose.ui.graphics.Color + +val md_theme_light_primary = Color(0xFF006874) +val md_theme_light_onPrimary = Color(0xFFFFFFFF) +val md_theme_light_primaryContainer = Color(0xFF97F0FF) +val md_theme_light_onPrimaryContainer = Color(0xFF001F24) +val md_theme_light_secondary = Color(0xFF4A6267) +val md_theme_light_onSecondary = Color(0xFFFFFFFF) +val md_theme_light_secondaryContainer = Color(0xFFCDE7EC) +val md_theme_light_onSecondaryContainer = Color(0xFF051F23) +val md_theme_light_tertiary = Color(0xFF525E7D) +val md_theme_light_onTertiary = Color(0xFFFFFFFF) +val md_theme_light_tertiaryContainer = Color(0xFFDAE2FF) +val md_theme_light_onTertiaryContainer = Color(0xFF0E1B37) +val md_theme_light_error = Color(0xFFBA1A1A) +val md_theme_light_errorContainer = Color(0xFFFFDAD6) +val md_theme_light_onError = Color(0xFFFFFFFF) +val md_theme_light_onErrorContainer = Color(0xFF410002) +val md_theme_light_background = Color(0xFFFAFDFD) +val md_theme_light_onBackground = Color(0xFF191C1D) +val md_theme_light_surface = Color(0xFFFAFDFD) +val md_theme_light_onSurface = Color(0xFF191C1D) +val md_theme_light_surfaceVariant = Color(0xFFDBE4E6) +val md_theme_light_onSurfaceVariant = Color(0xFF3F484A) +val md_theme_light_outline = Color(0xFF6F797A) +val md_theme_light_inverseOnSurface = Color(0xFFEFF1F1) +val md_theme_light_inverseSurface = Color(0xFF2E3132) +val md_theme_light_inversePrimary = Color(0xFF4FD8EB) +val md_theme_light_shadow = Color(0xFF000000) +val md_theme_light_surfaceTint = Color(0xFF006874) +val md_theme_light_outlineVariant = Color(0xFFBFC8CA) +val md_theme_light_scrim = Color(0xFF000000) + +val md_theme_dark_primary = Color(0xFFB1CBD0) +val md_theme_dark_onPrimary = Color(0xFF1C3438) +val md_theme_dark_primaryContainer = Color(0xFF334B4F) +val md_theme_dark_onPrimaryContainer = Color(0xFFCDE7EC) +val md_theme_dark_secondary = Color(0xFFB1CBD0) +val md_theme_dark_onSecondary = Color(0xFF1C3438) +val md_theme_dark_secondaryContainer = Color(0xFF334B4F) +val md_theme_dark_onSecondaryContainer = Color(0xFFCDE7EC) +val md_theme_dark_tertiary = Color(0xFFBAC6EA) +val md_theme_dark_onTertiary = Color(0xFF24304D) +val md_theme_dark_tertiaryContainer = Color(0xFF3B4664) +val md_theme_dark_onTertiaryContainer = Color(0xFFDAE2FF) +val md_theme_dark_error = Color(0xFFFFB4AB) +val md_theme_dark_errorContainer = Color(0xFF93000A) +val md_theme_dark_onError = Color(0xFF690005) +val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) +val md_theme_dark_background = Color(0xFF191C1D) +val md_theme_dark_onBackground = Color(0xFFE1E3E3) +val md_theme_dark_surface = Color(0xFF191C1D) +val md_theme_dark_onSurface = Color(0xFFE1E3E3) +val md_theme_dark_surfaceVariant = Color(0xFF3F484A) +val md_theme_dark_onSurfaceVariant = Color(0xFFBFC8CA) +val md_theme_dark_outline = Color(0xFF899294) +val md_theme_dark_inverseOnSurface = Color(0xFF191C1D) +val md_theme_dark_inverseSurface = Color(0xFFE1E3E3) +val md_theme_dark_inversePrimary = Color(0xFF006874) +val md_theme_dark_shadow = Color(0xFF000000) +val md_theme_dark_surfaceTint = Color(0xFF4FD8EB) +val md_theme_dark_outlineVariant = Color(0xFF3F484A) +val md_theme_dark_scrim = Color(0xFF000000) + + +val seed = Color(0xFFFFFFFF) diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/ui/theme/Theme.kt b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/ui/theme/Theme.kt new file mode 100644 index 000000000..fd0889204 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/kotlin/ui/theme/Theme.kt @@ -0,0 +1,90 @@ +package com.example.compose + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.runtime.Composable + + +private val LightColors = lightColorScheme( + primary = md_theme_light_primary, + onPrimary = md_theme_light_onPrimary, + primaryContainer = md_theme_light_primaryContainer, + onPrimaryContainer = md_theme_light_onPrimaryContainer, + secondary = md_theme_light_secondary, + onSecondary = md_theme_light_onSecondary, + secondaryContainer = md_theme_light_secondaryContainer, + onSecondaryContainer = md_theme_light_onSecondaryContainer, + tertiary = md_theme_light_tertiary, + onTertiary = md_theme_light_onTertiary, + tertiaryContainer = md_theme_light_tertiaryContainer, + onTertiaryContainer = md_theme_light_onTertiaryContainer, + error = md_theme_light_error, + errorContainer = md_theme_light_errorContainer, + onError = md_theme_light_onError, + onErrorContainer = md_theme_light_onErrorContainer, + background = md_theme_light_background, + onBackground = md_theme_light_onBackground, + surface = md_theme_light_surface, + onSurface = md_theme_light_onSurface, + surfaceVariant = md_theme_light_surfaceVariant, + onSurfaceVariant = md_theme_light_onSurfaceVariant, + outline = md_theme_light_outline, + inverseOnSurface = md_theme_light_inverseOnSurface, + inverseSurface = md_theme_light_inverseSurface, + inversePrimary = md_theme_light_inversePrimary, + surfaceTint = md_theme_light_surfaceTint, + outlineVariant = md_theme_light_outlineVariant, + scrim = md_theme_light_scrim, +) + + +private val DarkColors = darkColorScheme( + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + primaryContainer = md_theme_dark_primaryContainer, + onPrimaryContainer = md_theme_dark_onPrimaryContainer, + secondary = md_theme_dark_secondary, + onSecondary = md_theme_dark_onSecondary, + secondaryContainer = md_theme_dark_secondaryContainer, + onSecondaryContainer = md_theme_dark_onSecondaryContainer, + tertiary = md_theme_dark_tertiary, + onTertiary = md_theme_dark_onTertiary, + tertiaryContainer = md_theme_dark_tertiaryContainer, + onTertiaryContainer = md_theme_dark_onTertiaryContainer, + error = md_theme_dark_error, + errorContainer = md_theme_dark_errorContainer, + onError = md_theme_dark_onError, + onErrorContainer = md_theme_dark_onErrorContainer, + background = md_theme_dark_background, + onBackground = md_theme_dark_onBackground, + surface = md_theme_dark_surface, + onSurface = md_theme_dark_onSurface, + surfaceVariant = md_theme_dark_surfaceVariant, + onSurfaceVariant = md_theme_dark_onSurfaceVariant, + outline = md_theme_dark_outline, + inverseOnSurface = md_theme_dark_inverseOnSurface, + inverseSurface = md_theme_dark_inverseSurface, + inversePrimary = md_theme_dark_inversePrimary, + surfaceTint = md_theme_dark_surfaceTint, + outlineVariant = md_theme_dark_outlineVariant, + scrim = md_theme_dark_scrim, +) + +@Composable +fun AppTheme( + useDarkTheme: Boolean = true, + content: @Composable() () -> Unit +) { + val colors = if (!useDarkTheme) { + LightColors + } else { + DarkColors + } + + MaterialTheme( + colorScheme = colors, + content = content + ) +} \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Answer.sq b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Answer.sq new file mode 100644 index 000000000..93c3a2ef3 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Answer.sq @@ -0,0 +1,40 @@ +CREATE TABLE Answer ( + id INTEGER NOT NULL PRIMARY KEY, + questionID INTEGER NOT NULL, + answer TEXT NOT NULL, + correct INTEGER DEFAULT 0 +); + +getAnswerById: +SELECT * +FROM Answer +WHERE id = :id; + +getAnswerId: +SELECT id +FROM Answer +WHERE questionID = :questionId AND answer = :answer; + +getCorrectAnswersByQuestionId: +SELECT * +FROM Answer +WHERE questionID = :questionId AND correct = 1; + +getAnswersByQuestionId: +SELECT * +FROM Answer +WHERE questionID = :id; + +insertAnswer: +INSERT OR REPLACE +INTO Answer +VALUES (?,?,?,?); + +setCorrectAnswer: +UPDATE Answer +SET correct = 1 +WHERE id = :id; + +deleteAnswerById: +DELETE FROM Answer +WHERE id = :id; \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Assignment.sq b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Assignment.sq new file mode 100644 index 000000000..8f6b29ee3 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Assignment.sq @@ -0,0 +1,30 @@ +CREATE TABLE Assignment ( + id INTEGER NOT NULL PRIMARY KEY, + questionID INTEGER NOT NULL, + termA TEXT DEFAULT "_", + termB TEXT DEFAULT "_" +); + +getAssignmentById: +SELECT * +FROM Assignment +WHERE id = :id; + +getAssignmentId: +SELECT id +FROM Assignment +WHERE questionID = :questionId AND termB = :termB AND termA = :termA; + +getAssignmentByQuestionId: +SELECT * +FROM Assignment +WHERE questionID = :id; + +insertAssignment: +INSERT OR REPLACE +INTO Assignment +VALUES (?,?,?,?); + +deleteAssignmentById: +DELETE FROM Assignment +WHERE id = :id; \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/CorrectAnswer.sq b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/CorrectAnswer.sq new file mode 100644 index 000000000..6ce6d2076 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/CorrectAnswer.sq @@ -0,0 +1,18 @@ +CREATE TABLE CorrectAnswer ( + questionID INTEGER NOT NULL, + answerID INTEGER NOT NULL +); + +getCorrectAnswerByQuestionId: +SELECT Answer.id,Answer.questionID, Answer.answer +FROM Answer JOIN CorrectAnswer +WHERE CorrectAnswer.questionID = :questionID AND Answer.id = answerID; + +insertCorrectAnswer: +INSERT OR REPLACE +INTO CorrectAnswer +VALUES (?,?); + +deleteCorrectAnswerByAnswerId: +DELETE FROM CorrectAnswer +WHERE answerID = :answerId AND questionID = :questionId; \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/CorrectAssignment.sq b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/CorrectAssignment.sq new file mode 100644 index 000000000..a57b353dc --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/CorrectAssignment.sq @@ -0,0 +1,18 @@ +CREATE TABLE CorrectAssignment ( + questionID INTEGER NOT NULL, + assignmentID INTEGER NOT NULL +); + +getCorrectAssignmentByQuestionId: +SELECT Assignment.id, Assignment.questionID,Assignment.termA, Assignment.termB +FROM Assignment JOIN CorrectAssignment +WHERE CorrectAssignment.questionID = :questionID AND Assignment.id = assignmentID; + +insertCorrectAssignment: +INSERT OR REPLACE +INTO CorrectAssignment +VALUES (?,?); + +deleteCorrectAssignmentByAssignmentId: +DELETE FROM CorrectAssignment +WHERE assignmentID = :assignmentId AND questionID = :questionId; \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Dependency.sq b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Dependency.sq new file mode 100644 index 000000000..28c1cf48c --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Dependency.sq @@ -0,0 +1,27 @@ +CREATE TABLE Dependency ( + dependencyID INTEGER NOT NULL PRIMARY KEY, + questionAID INTEGER NOT NULL, + questionBID INTEGER NOT NULL, + projectID INTEGER NOT NULL, + position INTEGER, + dependency TEXT NOT NULL CHECK (dependency IN ("Sequenz", "Pflicht Unteraufgabe", "Optionale Unteraufgabe", "Bei falscher Antwort", "Bei richtiger Antwort")) +); + +getDependencyById: +SELECT * +FROM Dependency +WHERE dependencyID = :id; + +getAllDependenciesByProjectId: +SELECT * +FROM Dependency +WHERE projectID= :projectId; + +insertDependency: +INSERT OR REPLACE +INTO Dependency +VALUES (?,?,?,?,?,?); + +deleteDependencyById: +DELETE FROM Dependency +WHERE dependencyID = :id; \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Project.sq b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Project.sq new file mode 100644 index 000000000..6cb3bdaab --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Project.sq @@ -0,0 +1,27 @@ +CREATE TABLE Project ( + id INTEGER NOT NULL PRIMARY KEY, + name TEXT NOT NULL +); + +getProjectById: +SELECT * +FROM Project +WHERE id = :id; + +getAllProjects: +SELECT * +FROM Project; + +getProjectId: +SELECT id +FROM Project +WHERE name = :name; + +insertProject: +INSERT OR REPLACE +INTO Project +VALUES (?,?); + +deleteProjectById: +DELETE FROM Question +WHERE id = :id; \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/ProjectQuestion.sq b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/ProjectQuestion.sq new file mode 100644 index 000000000..88b2ab1ba --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/ProjectQuestion.sq @@ -0,0 +1,18 @@ +CREATE TABLE ProjectQuestion ( + projectID INTEGER, + questionID INTEGER +); + +getAllQuestionsByProjectId: +SELECT Question.id,Question.description, Question.explanation, Question.points, Question.pointsToPass, Question.type +FROM Question JOIN ProjectQuestion +WHERE ProjectQuestion.projectID = :projectID AND Question.id = questionID; + +insertProjectQuestion: +INSERT OR REPLACE +INTO ProjectQuestion +VALUES (?,?); + +deleteProjectQuestionByQuestionId: +DELETE FROM ProjectQuestion +WHERE questionID = :id AND ProjectQuestion.projectID = :projectId; \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Question.sq b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Question.sq new file mode 100644 index 000000000..8d0422d2d --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Question.sq @@ -0,0 +1,31 @@ +CREATE TABLE Question ( + id INTEGER NOT NULL PRIMARY KEY, + description TEXT NOT NULL, + explanation TEXT NOT NULL, + points INTEGER NOT NULL, + pointsToPass INTEGER NOT NULL, + type TEXT NOT NULL CHECK (type IN ("SINGLE_CHOICE_QUESTION","MULTIPLE_CHOICE_QUESTION","ASSIGN_QUESTION")) +); + +getQuestionById: +SELECT * +FROM Question +WHERE id = :id; + +getQuestionId: +SELECT id +FROM Question +WHERE description = :description AND explanation = :explanation AND points = :points AND pointsToPass = :pointsToPass; + +getAllQuestions: +SELECT * +FROM Question; + +insertQuestion: +INSERT OR REPLACE +INTO Question +VALUES (?,?,?,?,?,?); + +deleteQuestionById: +DELETE FROM Question +WHERE id = :id; \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/QuestionTag.sq b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/QuestionTag.sq new file mode 100644 index 000000000..4dc7112bf --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/QuestionTag.sq @@ -0,0 +1,19 @@ +CREATE TABLE QuestionTag ( + questionID INTEGER NOT NULL, + tagID INTEGER NOT NULL +); + +getTagsByQuestionId: +SELECT tag +FROM Tag JOIN QuestionTag +WHERE QuestionTag.questionID = :questionID AND Tag.id = tagID; + + +insertQuestionTag: +INSERT OR REPLACE +INTO QuestionTag +VALUES (?,?); + +deleteQuestionTag: +DELETE FROM QuestionTag +WHERE tagID = :tagId AND questionID = :questionId; \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Tag.sq b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Tag.sq new file mode 100644 index 000000000..f91869b89 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/src/main/sqldelight/db/Tag.sq @@ -0,0 +1,32 @@ +CREATE TABLE Tag ( + id INTEGER NOT NULL PRIMARY KEY, + tag TEXT NOT NULL +); + +getTagById: +SELECT * +FROM Tag +WHERE id = :id; + +getTagByName: +SELECT Tag.id +FROM Tag +WHERE tag = :name; + +getTagsByQuestionId: +SELECT Tag.tag +FROM Tag JOIN QuestionTag +WHERE tagID = Tag.id AND questionID =:questionId; + +getAllTags: +SELECT * +FROM Tag; + +insertTag: +INSERT OR REPLACE +INTO Tag +VALUES (?,?); + +deleteTagById: +DELETE FROM Tag +WHERE id = :id; \ No newline at end of file diff --git a/dungeon-task-manager/Task_Management_Dungeon/szenario_definitions b/dungeon-task-manager/Task_Management_Dungeon/szenario_definitions new file mode 100644 index 000000000..614fe1b92 --- /dev/null +++ b/dungeon-task-manager/Task_Management_Dungeon/szenario_definitions @@ -0,0 +1,444 @@ +// ++++++++++++++++++++ SZENARIO DEFINITIONEN +++++++++++++++ + +entity_type monster_type { + inventory_component {}, + health_component { + max_health: 10, + start_health: 10, + on_death: drop_items + }, + position_component {}, + draw_component { + path: "character/monster/chort" + }, + velocity_component { + x_velocity: 4.0, + y_velocity: 4.0 + }, + hitbox_component {}, + ai_component{} + // TODO: spiky component + // TODO: add sound component +} + +entity_type chest_type { + inventory_component {}, + draw_component { + path: "objects/treasurechest" + }, + hitbox_component {}, + position_component{}, + interaction_component{ + radius: 1.5, + on_interaction: open_container + }, + task_content_component{} +} + +entity_type knight_type { + draw_component { + path: "character/knight" + }, + hitbox_component {}, + position_component{}, + interaction_component{ + radius: 1.5 + }, + task_component{} +} + +entity_type wizard_type { + draw_component { + path: "character/wizard" + }, + hitbox_component {}, + position_component{}, + interaction_component{ + radius: 1.5 + }, + task_component{} +} + +fn drop_items(entity me) { + me.inventory_component.drop_items(); +} + +fn open_container(entity chest, entity who) { + chest.inventory_component.open(who); +} + +fn ask_task_finished(entity knight, entity who) { + var my_task : task; + my_task = knight.task_component.task; + if my_task.is_active() { + ask_task_yes_no(my_task); + } else { + show_info("Du hast die Aufgabe schon bearbeitet."); + } +} + +fn ask_task_on_ui(entity knight, entity who) { + var my_task : task; + my_task = knight.task_component.task; + if my_task.is_active() { + show_task_on_ui(my_task); + } else { + show_info("Du hast die Aufgabe schon bearbeitet."); + } +} + +item_type scroll_type { + display_name: "Eine Schriftrolle", + description: "Lies mich", + texture_path: "items/book/wisdom_scroll.png" +} + +item_type mushroom_type { + display_name: "Ein Pilz", + description: "Iss mich (nicht)", + texture_path: "items/resource/toadstool.png" +} + +entity_type kettle_type { + inventory_component {}, + draw_component { + path: "objects/magic_kettle" + }, + hitbox_component {}, + position_component{}, + interaction_component{ + radius: 1.5, + on_interaction: open_container + }, + task_content_component{} +} + +fn build_task_single_ui(single_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + // quest giver knight + var knight : entity; + knight = instantiate_named(knight_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_on_ui; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_single_kettle(single_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + for task_content content in t.get_content() { + var item : entity; + item = build_quest_item(mushroom_type, content); + place_quest_item(item, room_set); + } + + var chest : entity; + chest = instantiate(kettle_type); + //chest.add_named_task_content("This is a quest chest", t); + chest.mark_as_task_container(t, "Quest-Zauberkessel"); + + room_set.add(chest); + t.set_scenario_text("Platziere die richtigen Pilze in dem Quest-Zauberkessel"); + t.set_answer_picker_function(answer_picker_single_chest); + + // quest giver knight + var knight : entity; + knight = instantiate_named(wizard_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_finished; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_single_chest(single_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + for task_content content in t.get_content() { + var item : entity; + item = build_quest_item(scroll_type, content); + place_quest_item(item, room_set); + } + + var chest : entity; + chest = instantiate(chest_type); + //chest.add_named_task_content("This is a quest chest", t); + chest.mark_as_task_container(t, "Quest-Truhe"); + + room_set.add(chest); + t.set_scenario_text("Platziere die richtige Schriftrolle in der Quest-Truhe"); + t.set_answer_picker_function(answer_picker_single_chest); + + // quest giver knight + var knight : entity; + knight = instantiate_named(knight_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_finished; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_single_chest_with_monster(single_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + for task_content content in t.get_content() { + var item : quest_item; + item = build_quest_item(scroll_type, content); + + var monster: entity; + monster = instantiate(monster_type); + monster.inventory_component.add_item(item); + room_set.add(monster); + } + + var chest : entity; + chest = instantiate(chest_type); + chest.mark_as_task_container(t, "Quest-Truhe"); + + room_set.add(chest); + t.set_scenario_text("Hilfe! Monster haben die Schriftrollen geklaut! Platziere die richtige Schriftrolle in der Quest-Truhe!"); + t.set_answer_picker_function(answer_picker_single_chest); + + // quest giver knight + var knight : entity; + knight = instantiate_named(knight_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_finished; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + + return return_set; +} + +fn build_task_multi_kettle(multiple_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + // build items from content + for task_content content in t.get_content() { + var item : entity; + item = build_quest_item(mushroom_type, content); + place_quest_item(item, room_set); + } + + // build answer chest + var chest : entity; + chest = instantiate(kettle_type); + chest.mark_as_task_container(t, "Quest-Zauberkessel"); + room_set.add(chest); + + // setup task + t.set_scenario_text("Platziere die richtigen Pilze in dem Quest-Zauberkessel"); + t.set_answer_picker_function(answer_picker_single_chest); + + // knight + var knight : entity; + knight = instantiate_named(wizard_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_finished; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_multi_chest(multiple_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + // build items from content + for task_content content in t.get_content() { + var item : entity; + item = build_quest_item(scroll_type, content); + place_quest_item(item, room_set); + } + + // build answer chest + var chest : entity; + chest = instantiate(chest_type); + chest.mark_as_task_container(t, "Quest-Truhe"); + room_set.add(chest); + + // setup task + t.set_scenario_text("Platziere die richtigen Schriftrollen in der Quest-Truhe"); + t.set_answer_picker_function(answer_picker_single_chest); + + // knight + var knight : entity; + knight = instantiate_named(knight_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_finished; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_multi_ui(multiple_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + // setup task + //t.set_scenario_text("Platziere die richtigen Schriftrollen in der Quest-Truhe"); + //t.set_answer_picker_function(answer_picker_single_chest); + + // knight + var knight : entity; + knight = instantiate_named(knight_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_on_ui; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_assign(assign_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + var solution_map : [element -> element<>]; + solution_map = t.get_solution(); + + t.set_scenario_text("Platziere die richtige Schriftrolle in der richtigen Quest-Truhe"); + t.set_answer_picker_function(answer_picker_multi_chest); + + // instantiate chests + for element key in solution_map.get_keys() { + if key.is_empty() { + // skip + } else { + // if this variable is declared outside of the for-loop, + // it is not correctly placed in the set, because the internal + // Value will be still the same Object (with the same HashCode!!) + var chest : entity; + chest = instantiate(chest_type); + chest.mark_as_task_container_with_element(t, key); + room_set.add(chest); + } + } + + var item : quest_item; + // instantiate all answer elements as scrolls + for element<> element_set in solution_map.get_elements() { + for element element in element_set { + if element.is_empty() { + // skip + } else { + print(element); + item = build_quest_item(scroll_type, element); + place_quest_item(item, room_set); + } + } + } + + // quest giver knight + var knight : entity; + knight = instantiate_named(knight_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_finished; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_assign_kettle_mushroom(assign_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + var solution_map : [element -> element<>]; + solution_map = t.get_solution(); + + t.set_scenario_text("Platziere die richtigen Pilze in den richtigen Kesseln"); + t.set_answer_picker_function(answer_picker_multi_chest); + + // instantiate chests + for element key in solution_map.get_keys() { + if key.is_empty() { + // skip + } else { + // if this variable is declared outside of the for-loop, + // it is not correctly placed in the set, because the internal + // Value will be still the same Object (with the same HashCode!!) + var chest : entity; + chest = instantiate(kettle_type); + chest.mark_as_task_container_with_element(t, key); + room_set.add(chest); + } + } + + var item : quest_item; + // instantiate all answer elements as scrolls + for element<> element_set in solution_map.get_elements() { + for element element in element_set { + if element.is_empty() { + // skip + } else { + print(element); + item = build_quest_item(mushroom_type, element); + place_quest_item(item, room_set); + } + } + } + + // quest giver wizard + var wizard : entity; + wizard = instantiate_named(wizard_type, "Questgeber"); + wizard.task_component.task = t; + wizard.interaction_component.on_interaction = ask_task_finished; + room_set.add(wizard); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +// +++++++++++++++ ENDE SZENARIO DEFINITIONEN +++++++++++++++ \ No newline at end of file diff --git a/dungeon-task-manager/gradle/wrapper/gradle-wrapper.properties b/dungeon-task-manager/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..edc0bb11c --- /dev/null +++ b/dungeon-task-manager/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/dungeon-task-manager/szenario_definitions b/dungeon-task-manager/szenario_definitions new file mode 100644 index 000000000..614fe1b92 --- /dev/null +++ b/dungeon-task-manager/szenario_definitions @@ -0,0 +1,444 @@ +// ++++++++++++++++++++ SZENARIO DEFINITIONEN +++++++++++++++ + +entity_type monster_type { + inventory_component {}, + health_component { + max_health: 10, + start_health: 10, + on_death: drop_items + }, + position_component {}, + draw_component { + path: "character/monster/chort" + }, + velocity_component { + x_velocity: 4.0, + y_velocity: 4.0 + }, + hitbox_component {}, + ai_component{} + // TODO: spiky component + // TODO: add sound component +} + +entity_type chest_type { + inventory_component {}, + draw_component { + path: "objects/treasurechest" + }, + hitbox_component {}, + position_component{}, + interaction_component{ + radius: 1.5, + on_interaction: open_container + }, + task_content_component{} +} + +entity_type knight_type { + draw_component { + path: "character/knight" + }, + hitbox_component {}, + position_component{}, + interaction_component{ + radius: 1.5 + }, + task_component{} +} + +entity_type wizard_type { + draw_component { + path: "character/wizard" + }, + hitbox_component {}, + position_component{}, + interaction_component{ + radius: 1.5 + }, + task_component{} +} + +fn drop_items(entity me) { + me.inventory_component.drop_items(); +} + +fn open_container(entity chest, entity who) { + chest.inventory_component.open(who); +} + +fn ask_task_finished(entity knight, entity who) { + var my_task : task; + my_task = knight.task_component.task; + if my_task.is_active() { + ask_task_yes_no(my_task); + } else { + show_info("Du hast die Aufgabe schon bearbeitet."); + } +} + +fn ask_task_on_ui(entity knight, entity who) { + var my_task : task; + my_task = knight.task_component.task; + if my_task.is_active() { + show_task_on_ui(my_task); + } else { + show_info("Du hast die Aufgabe schon bearbeitet."); + } +} + +item_type scroll_type { + display_name: "Eine Schriftrolle", + description: "Lies mich", + texture_path: "items/book/wisdom_scroll.png" +} + +item_type mushroom_type { + display_name: "Ein Pilz", + description: "Iss mich (nicht)", + texture_path: "items/resource/toadstool.png" +} + +entity_type kettle_type { + inventory_component {}, + draw_component { + path: "objects/magic_kettle" + }, + hitbox_component {}, + position_component{}, + interaction_component{ + radius: 1.5, + on_interaction: open_container + }, + task_content_component{} +} + +fn build_task_single_ui(single_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + // quest giver knight + var knight : entity; + knight = instantiate_named(knight_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_on_ui; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_single_kettle(single_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + for task_content content in t.get_content() { + var item : entity; + item = build_quest_item(mushroom_type, content); + place_quest_item(item, room_set); + } + + var chest : entity; + chest = instantiate(kettle_type); + //chest.add_named_task_content("This is a quest chest", t); + chest.mark_as_task_container(t, "Quest-Zauberkessel"); + + room_set.add(chest); + t.set_scenario_text("Platziere die richtigen Pilze in dem Quest-Zauberkessel"); + t.set_answer_picker_function(answer_picker_single_chest); + + // quest giver knight + var knight : entity; + knight = instantiate_named(wizard_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_finished; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_single_chest(single_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + for task_content content in t.get_content() { + var item : entity; + item = build_quest_item(scroll_type, content); + place_quest_item(item, room_set); + } + + var chest : entity; + chest = instantiate(chest_type); + //chest.add_named_task_content("This is a quest chest", t); + chest.mark_as_task_container(t, "Quest-Truhe"); + + room_set.add(chest); + t.set_scenario_text("Platziere die richtige Schriftrolle in der Quest-Truhe"); + t.set_answer_picker_function(answer_picker_single_chest); + + // quest giver knight + var knight : entity; + knight = instantiate_named(knight_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_finished; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_single_chest_with_monster(single_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + for task_content content in t.get_content() { + var item : quest_item; + item = build_quest_item(scroll_type, content); + + var monster: entity; + monster = instantiate(monster_type); + monster.inventory_component.add_item(item); + room_set.add(monster); + } + + var chest : entity; + chest = instantiate(chest_type); + chest.mark_as_task_container(t, "Quest-Truhe"); + + room_set.add(chest); + t.set_scenario_text("Hilfe! Monster haben die Schriftrollen geklaut! Platziere die richtige Schriftrolle in der Quest-Truhe!"); + t.set_answer_picker_function(answer_picker_single_chest); + + // quest giver knight + var knight : entity; + knight = instantiate_named(knight_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_finished; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + + return return_set; +} + +fn build_task_multi_kettle(multiple_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + // build items from content + for task_content content in t.get_content() { + var item : entity; + item = build_quest_item(mushroom_type, content); + place_quest_item(item, room_set); + } + + // build answer chest + var chest : entity; + chest = instantiate(kettle_type); + chest.mark_as_task_container(t, "Quest-Zauberkessel"); + room_set.add(chest); + + // setup task + t.set_scenario_text("Platziere die richtigen Pilze in dem Quest-Zauberkessel"); + t.set_answer_picker_function(answer_picker_single_chest); + + // knight + var knight : entity; + knight = instantiate_named(wizard_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_finished; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_multi_chest(multiple_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + // build items from content + for task_content content in t.get_content() { + var item : entity; + item = build_quest_item(scroll_type, content); + place_quest_item(item, room_set); + } + + // build answer chest + var chest : entity; + chest = instantiate(chest_type); + chest.mark_as_task_container(t, "Quest-Truhe"); + room_set.add(chest); + + // setup task + t.set_scenario_text("Platziere die richtigen Schriftrollen in der Quest-Truhe"); + t.set_answer_picker_function(answer_picker_single_chest); + + // knight + var knight : entity; + knight = instantiate_named(knight_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_finished; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_multi_ui(multiple_choice_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + // setup task + //t.set_scenario_text("Platziere die richtigen Schriftrollen in der Quest-Truhe"); + //t.set_answer_picker_function(answer_picker_single_chest); + + // knight + var knight : entity; + knight = instantiate_named(knight_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_on_ui; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_assign(assign_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + var solution_map : [element -> element<>]; + solution_map = t.get_solution(); + + t.set_scenario_text("Platziere die richtige Schriftrolle in der richtigen Quest-Truhe"); + t.set_answer_picker_function(answer_picker_multi_chest); + + // instantiate chests + for element key in solution_map.get_keys() { + if key.is_empty() { + // skip + } else { + // if this variable is declared outside of the for-loop, + // it is not correctly placed in the set, because the internal + // Value will be still the same Object (with the same HashCode!!) + var chest : entity; + chest = instantiate(chest_type); + chest.mark_as_task_container_with_element(t, key); + room_set.add(chest); + } + } + + var item : quest_item; + // instantiate all answer elements as scrolls + for element<> element_set in solution_map.get_elements() { + for element element in element_set { + if element.is_empty() { + // skip + } else { + print(element); + item = build_quest_item(scroll_type, element); + place_quest_item(item, room_set); + } + } + } + + // quest giver knight + var knight : entity; + knight = instantiate_named(knight_type, "Questgeber"); + knight.task_component.task = t; + knight.interaction_component.on_interaction = ask_task_finished; + room_set.add(knight); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +fn build_task_assign_kettle_mushroom(assign_task t) -> entity<><> { + var return_set : entity<><>; + var room_set : entity<>; + + var solution_map : [element -> element<>]; + solution_map = t.get_solution(); + + t.set_scenario_text("Platziere die richtigen Pilze in den richtigen Kesseln"); + t.set_answer_picker_function(answer_picker_multi_chest); + + // instantiate chests + for element key in solution_map.get_keys() { + if key.is_empty() { + // skip + } else { + // if this variable is declared outside of the for-loop, + // it is not correctly placed in the set, because the internal + // Value will be still the same Object (with the same HashCode!!) + var chest : entity; + chest = instantiate(kettle_type); + chest.mark_as_task_container_with_element(t, key); + room_set.add(chest); + } + } + + var item : quest_item; + // instantiate all answer elements as scrolls + for element<> element_set in solution_map.get_elements() { + for element element in element_set { + if element.is_empty() { + // skip + } else { + print(element); + item = build_quest_item(mushroom_type, element); + place_quest_item(item, room_set); + } + } + } + + // quest giver wizard + var wizard : entity; + wizard = instantiate_named(wizard_type, "Questgeber"); + wizard.task_component.task = t; + wizard.interaction_component.on_interaction = ask_task_finished; + room_set.add(wizard); + + var random_entity : entity; + random_entity = get_random_content(); + room_set.add(random_entity); + + return_set.add(room_set); + return return_set; +} + +// +++++++++++++++ ENDE SZENARIO DEFINITIONEN +++++++++++++++ \ No newline at end of file