From 90f2a13c252c9ec0b0d3c941ce680c1a7c7e4759 Mon Sep 17 00:00:00 2001 From: juerg Date: Wed, 11 Mar 2026 20:11:00 +0100 Subject: [PATCH] Remove Android application files and related functionality - Deleted all application source files including view models, UI components, and data transfer objects. - Removed build configurations and dependencies from `build.gradle`. - Executed comprehensive cleanup removing application manifest and database configurations. - This change effectively retires the Android application component. --- android/app/build.gradle | 110 ------ android/app/src/main/AndroidManifest.xml | 27 -- .../angularai/android/AngularAIApplication.kt | 7 - .../goodone/angularai/android/MainActivity.kt | 367 ------------------ .../android/data/local/AppDatabase.kt | 21 - .../angularai/android/data/local/TaskDao.kt | 20 - .../android/data/local/entity/TaskEntity.kt | 15 - .../angularai/android/data/remote/AuthApi.kt | 22 -- .../android/data/remote/DashboardApi.kt | 9 - .../angularai/android/data/remote/LogApi.kt | 22 -- .../android/data/remote/SystemApi.kt | 9 - .../angularai/android/data/remote/TaskApi.kt | 22 -- .../angularai/android/data/remote/UserApi.kt | 28 -- .../android/data/remote/dto/ActionLogDTO.kt | 17 - .../android/data/remote/dto/DashboardDTO.kt | 27 -- .../android/data/remote/dto/SystemInfoDTO.kt | 8 - .../android/data/remote/dto/TaskDTO.kt | 12 - .../android/data/remote/dto/UserDTO.kt | 14 - .../android/data/repository/AuthRepository.kt | 106 ----- .../data/repository/DashboardRepository.kt | 15 - .../android/data/repository/LogRepository.kt | 30 -- .../data/repository/SystemRepository.kt | 15 - .../android/data/repository/TaskRepository.kt | 73 ---- .../android/data/repository/UserRepository.kt | 54 --- .../angularai/android/di/NetworkModule.kt | 144 ------- .../angularai/android/domain/model/Task.kt | 11 - .../android/domain/model/TaskStatus.kt | 8 - .../angularai/android/domain/model/User.kt | 12 - .../angularai/android/ui/SystemViewModel.kt | 30 -- .../android/ui/admin/AdminUserEditScreen.kt | 203 ---------- .../android/ui/admin/AdminUserListScreen.kt | 91 ----- .../android/ui/admin/AdminViewModel.kt | 72 ---- .../android/ui/auth/AuthViewModel.kt | 79 ---- .../angularai/android/ui/auth/LoginScreen.kt | 77 ---- .../android/ui/auth/RegisterScreen.kt | 134 ------- .../android/ui/dashboard/DashboardScreen.kt | 250 ------------ .../ui/dashboard/DashboardViewModel.kt | 42 -- .../angularai/android/ui/log/LogScreen.kt | 206 ---------- .../angularai/android/ui/log/LogViewModel.kt | 106 ----- .../android/ui/profile/ProfileScreen.kt | 76 ---- .../android/ui/profile/ProfileViewModel.kt | 54 --- .../android/ui/tasks/TaskEditScreen.kt | 216 ----------- .../android/ui/tasks/TaskListScreen.kt | 208 ---------- .../android/ui/tasks/TaskViewModel.kt | 114 ------ .../angularai/android/ui/theme/Theme.kt | 33 -- .../res/drawable/ic_launcher_background.xml | 10 - .../res/drawable/ic_launcher_foreground.xml | 17 - .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - android/app/src/main/res/values/strings.xml | 4 - android/app/src/main/res/values/themes.xml | 6 - .../angularai/android/DomainModelTest.kt | 54 --- .../goodone/angularai/android/DtoModelTest.kt | 91 ----- .../angularai/android/RepositoryTest.kt | 135 ------- .../org.mockito.plugins.MockMaker | 1 - android/build.gradle | 22 -- android/gradle.properties | 4 - android/gradle/wrapper/gradle-wrapper.jar | Bin 43705 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 8 - android/gradlew | 170 -------- android/gradlew.bat | 95 ----- android/local.properties | 8 - android/settings.gradle | 2 - .../github-code-scanning-alerts-enriched.json | 0 ...github-code-scanning-alerts-for-junie.json | 0 .../github-code-scanning-alerts-enriched.json | 0 ...github-code-scanning-alerts-for-junie.json | 0 .../github-code-scanning-alerts-raw.json | 0 .../.github-code-scanning-export/index.md | 0 .../V1/sonar-issues-enriched.json | 0 .../V1/sonar-issues-for-junie.json | 0 .../V2/sonar-issues-enriched.json | 0 .../V2/sonar-issues-for-junie.json | 0 .../V3/sonar-issues-enriched.json | 0 .../V3/sonar-issues-for-junie.json | 0 .../.sonar-export/sonar-issues-enriched.json | 0 .../.sonar-export/sonar-issues-for-junie.json | 0 .../.sonar-export/sonar-issues-full.json | 0 {sonar => deploy/sonar}/commit-message.md | 0 .../export-github-code-scanning-alerts.ps1 | 0 .../sonar}/export-sonar-issues.ps1 | 0 .../sonar}/github-code-scanning-loop.ps1 | 0 .../sonar}/junie-sonar-issue-fix.md | 0 {sonar => deploy/sonar}/next-batch-info.md | 0 {sonar => deploy/sonar}/sonar-junie-loop.ps1 | 0 .../doc/presentation-workflow.md | 0 .../presentation}/inspector.log | Bin .../presentation}/inspector_success.log | 0 {presentation => doc/presentation}/pom.xml | 0 .../Iteration-2/GovernanceFlow.png | Bin .../SoftwareEntwicklungAi-Level2-draft.pdf | Bin .../SoftwareEntwicklungAi-Level2.pptx | Bin .../Iteration-2/files/AiRaceFinal.png | Bin .../Iteration-2/files/AiRaceStart.png | Bin .../Iteration-2/files/ArchitectureBackup.png | Bin .../files/ArchitectureExplainArchitecture.png | Bin .../files/ArchitectureExplainSecurity.png | Bin .../Iteration-2/files/AuthSequence.png | Bin .../files/BuildReleasePipeline.png | Bin .../Iteration-2/files/DeploymentReview.png | Bin .../Iteration-2/files/GithubActions.png | Bin .../Iteration-2/files/GovernanceFlow.png | Bin .../Iteration-2/files/Graphana.png | Bin .../Iteration-2/files/IterationRetro.png | Bin .../Iteration-2/files/ObservabilityBackup.png | Bin .../Iteration-2/files/QuickAddWithAi.png | Bin .../Iteration-2/files/QuickAddWithAi2.png | Bin .../Iteration-2/files/RegistrationAfter.png | Bin .../Iteration-2/files/RegistrationBefore.png | Bin .../Iteration-2/files/ReleaseNotes.png | Bin .../Iteration-2/files/SecurityBackup.png | Bin .../Iteration-2/files/SecurityThreatModel.png | Bin .../Iteration-2/files/TaskExample.png | Bin .../Iteration-2/files/TaskManagementAfter.png | Bin .../files/TaskManagementBefore.png | Bin .../Iteration-2/files/TaskSetExample.png | Bin .../files/playwright-demo-screenshots.png | Bin .../files/playwright-test-failure-img.png | Bin .../files/playwright-test-failure-txt.png | Bin .../files/playwright-test-failure.png | Bin .../Iteration-2/generate-presentation.py | 0 .../Iteration-2/generate-slides.md | 0 .../Iteration-2/goodone-reveal-theme.css | 0 .../Iteration-2/invitation-2.txt | 0 .../old/presentation-content-proposal.md | 0 .../Iteration-2/presentation-2-workbook.md | 0 .../Iteration-2/slides-2-backup.md | 0 .../Iteration-2/slides-2-executive.md | 0 .../Iteration-2/slides-2-improvements.md | 0 .../Iteration-2/slides-2-non-tech.md | 0 .../Iteration-2/slides-2-task.md | 0 .../Iteration-2/slides-2-test.md | 0 .../presentations/Iteration-2/slides-2.html | 0 .../presentations/Iteration-2/slides-2.md | 0 .../Iteration-2/template-2-junie.md | 0 .../presentations/Iteration-2/template.pptx | Bin .../iteration-1/SoftwareEntwicklungAI.pptx | Bin .../presentations/iteration-1/auto_agenda.lua | 0 .../files/architecture_overivew.yaml | 0 .../files/generated/architecture_overview.png | Bin .../generated/architecture_overview_orig.png | Bin .../files/generated/er_diagram.png | Bin .../iteration-1/files/generated/erd.png | Bin .../files/generated/local_dev_setup.png | Bin .../generated/local_dev_setup_ai_dev_edu.png | Bin .../iteration-1/files/images/AiRace.png | Bin .../iteration-1/files/images/AiRaceFinal.png | Bin .../iteration-1/files/images/AndroidApp.png | Bin .../files/images/AndroidTaskMenu.png | Bin .../iteration-1/files/images/ChatGpt.png | Bin .../iteration-1/files/images/CypressTests.png | Bin .../files/images/DashboardBefore.png | Bin .../files/images/DashboardImplementation.png | Bin .../files/images/DashboardProposal.png | Bin .../files/images/GoodOne2020_Starting.png | Bin .../files/images/GoodOne2020_Users.png | Bin .../files/images/IdeIntegration.png | Bin .../iteration-1/files/images/IntellJ.png | Bin .../iteration-1/files/images/SonarSummary.png | Bin .../files/images/TaskManagementBefore.png | Bin .../files/images/TaskManagementProposal.png | Bin .../files/images/android_task_menu.png | Bin .../files/images/angular_task_menu.png | Bin .../files/images/angular_task_menu2.png | Bin .../title_slide_background_no_text_v2.png | Bin .../images/title_slide_goodone_ai_dech.png | Bin .../files/images/title_slide_goodone_v3.png | Bin .../files/input/architecture_overview.drawio | 0 .../input/architecture_overview.old.drawio | 0 .../input/architecture_overview_clean.drawio | 0 .../input/architecture_slide_variant.drawio | 0 .../iteration-1/files/input/er_diagram.mmd | 0 .../iteration-1/files/input/erd.puml | 0 .../files/input/local_dev_setup.drawio | 0 .../input/local_dev_setup_ai_dev_edu.drawio | 0 .../iteration-1/generate_presentation.py | 0 .../SoftwareEntwicklungAI_Python.pptx | Bin .../iteration-1/patch_autofit.py | 0 .../iteration-1/pptx_generation_plan.md | 0 .../presentations/iteration-1/reference.pptx | Bin .../presentations/iteration-1/slides-1.md | 0 .../iteration-1/template-company.pptx | Bin .../presentations/iteration-1/template.pptx | Bin .../presentations/iteration-3/QandA-3.md | 0 .../SoftwareEntwicklungAi-Level3.pptx | Bin .../iteration-3/files/AdrIndex.png | Bin .../iteration-3/files/AiRaceFinal.png | Bin .../iteration-3/files/AiRaceStart.png | Bin .../iteration-3/files/ArchitectureBackup.png | Bin .../files/ArchitectureExplainArchitecture.png | Bin .../files/ArchitectureExplainSecurity.png | Bin .../iteration-3/files/AuthSequence.png | Bin .../files/BuildReleasePipeline.png | Bin .../iteration-3/files/DeploymentReview.png | Bin .../iteration-3/files/GithubActions.png | Bin .../iteration-3/files/GovernanceFlow.png | Bin .../iteration-3/files/Graphana.png | Bin .../iteration-3/files/IterationRetro.png | Bin .../iteration-3/files/ObservabilityBackup.png | Bin .../iteration-3/files/QuickAddWithAi.png | Bin .../iteration-3/files/QuickAddWithAi2.png | Bin .../iteration-3/files/RegistrationAfter.png | Bin .../iteration-3/files/RegistrationBefore.png | Bin .../iteration-3/files/ReleaseNotes.png | Bin .../iteration-3/files/SecurityBackup.png | Bin .../iteration-3/files/SecurityThreatModel.png | Bin .../iteration-3/files/TaskExample.png | Bin .../iteration-3/files/TaskManagementAfter.png | Bin .../files/TaskManagementBefore.png | Bin .../iteration-3/files/TaskSetExample.png | Bin .../files/playwright-demo-screenshots.png | Bin .../files/playwright-test-failure-img.png | Bin .../files/playwright-test-failure-txt.png | Bin .../files/playwright-test-failure.png | Bin .../iteration-3/generate-slides.md | 0 .../presentations/iteration-3/invitation-3.md | 0 .../presentations/iteration-3/slides-3.md | 0 .../pandoc/pandoc_authoring_checklist.md | 0 .../pandoc/reference_template_guidelines.md | 0 .../presentation/CompanyStyleAligner.java | 0 .../presentation/DetailedInspector.java | 0 .../presentation/FinalResultFixer.java | 0 .../presentation/FinalTemplateFixer.java | 0 .../presentation/LayoutInspector.java | 0 .../presentation/TemplateCreator.java | 0 .../presentation/TemplateFixerV2.java | 0 226 files changed, 3853 deletions(-) delete mode 100644 android/app/build.gradle delete mode 100644 android/app/src/main/AndroidManifest.xml delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/AngularAIApplication.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/MainActivity.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/local/AppDatabase.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/local/TaskDao.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/local/entity/TaskEntity.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/remote/AuthApi.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/remote/DashboardApi.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/remote/LogApi.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/remote/SystemApi.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/remote/TaskApi.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/remote/UserApi.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/ActionLogDTO.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/DashboardDTO.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/SystemInfoDTO.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/TaskDTO.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/UserDTO.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/repository/AuthRepository.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/repository/DashboardRepository.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/repository/LogRepository.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/repository/SystemRepository.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/repository/TaskRepository.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/data/repository/UserRepository.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/di/NetworkModule.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/domain/model/Task.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/domain/model/TaskStatus.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/domain/model/User.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/SystemViewModel.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/admin/AdminUserEditScreen.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/admin/AdminUserListScreen.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/admin/AdminViewModel.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/auth/AuthViewModel.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/auth/LoginScreen.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/auth/RegisterScreen.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/dashboard/DashboardScreen.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/dashboard/DashboardViewModel.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/log/LogScreen.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/log/LogViewModel.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/profile/ProfileScreen.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/profile/ProfileViewModel.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/tasks/TaskEditScreen.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/tasks/TaskListScreen.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/tasks/TaskViewModel.kt delete mode 100644 android/app/src/main/java/ch/goodone/angularai/android/ui/theme/Theme.kt delete mode 100644 android/app/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 android/app/src/main/res/drawable/ic_launcher_foreground.xml delete mode 100644 android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 android/app/src/main/res/values/strings.xml delete mode 100644 android/app/src/main/res/values/themes.xml delete mode 100644 android/app/src/test/java/ch/goodone/angularai/android/DomainModelTest.kt delete mode 100644 android/app/src/test/java/ch/goodone/angularai/android/DtoModelTest.kt delete mode 100644 android/app/src/test/java/ch/goodone/angularai/android/RepositoryTest.kt delete mode 100644 android/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker delete mode 100644 android/build.gradle delete mode 100644 android/gradle.properties delete mode 100644 android/gradle/wrapper/gradle-wrapper.jar delete mode 100644 android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 android/gradlew delete mode 100644 android/gradlew.bat delete mode 100644 android/local.properties delete mode 100644 android/settings.gradle rename {sonar => deploy/sonar}/.github-code-scanning-export/V1/github-code-scanning-alerts-enriched.json (100%) rename {sonar => deploy/sonar}/.github-code-scanning-export/V1/github-code-scanning-alerts-for-junie.json (100%) rename {sonar => deploy/sonar}/.github-code-scanning-export/github-code-scanning-alerts-enriched.json (100%) rename {sonar => deploy/sonar}/.github-code-scanning-export/github-code-scanning-alerts-for-junie.json (100%) rename {sonar => deploy/sonar}/.github-code-scanning-export/github-code-scanning-alerts-raw.json (100%) rename {sonar => deploy/sonar}/.github-code-scanning-export/index.md (100%) rename {sonar => deploy/sonar}/.sonar-export/V1/sonar-issues-enriched.json (100%) rename {sonar => deploy/sonar}/.sonar-export/V1/sonar-issues-for-junie.json (100%) rename {sonar => deploy/sonar}/.sonar-export/V2/sonar-issues-enriched.json (100%) rename {sonar => deploy/sonar}/.sonar-export/V2/sonar-issues-for-junie.json (100%) rename {sonar => deploy/sonar}/.sonar-export/V3/sonar-issues-enriched.json (100%) rename {sonar => deploy/sonar}/.sonar-export/V3/sonar-issues-for-junie.json (100%) rename {sonar => deploy/sonar}/.sonar-export/sonar-issues-enriched.json (100%) rename {sonar => deploy/sonar}/.sonar-export/sonar-issues-for-junie.json (100%) rename {sonar => deploy/sonar}/.sonar-export/sonar-issues-full.json (100%) rename {sonar => deploy/sonar}/commit-message.md (100%) rename {sonar => deploy/sonar}/export-github-code-scanning-alerts.ps1 (100%) rename {sonar => deploy/sonar}/export-sonar-issues.ps1 (100%) rename {sonar => deploy/sonar}/github-code-scanning-loop.ps1 (100%) rename {sonar => deploy/sonar}/junie-sonar-issue-fix.md (100%) rename {sonar => deploy/sonar}/next-batch-info.md (100%) rename {sonar => deploy/sonar}/sonar-junie-loop.ps1 (100%) rename {presentation => doc/presentation}/doc/presentation-workflow.md (100%) rename {presentation => doc/presentation}/inspector.log (100%) rename {presentation => doc/presentation}/inspector_success.log (100%) rename {presentation => doc/presentation}/pom.xml (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/GovernanceFlow.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/SoftwareEntwicklungAi-Level2-draft.pdf (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/SoftwareEntwicklungAi-Level2.pptx (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/AiRaceFinal.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/AiRaceStart.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/ArchitectureBackup.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/ArchitectureExplainArchitecture.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/ArchitectureExplainSecurity.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/AuthSequence.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/BuildReleasePipeline.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/DeploymentReview.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/GithubActions.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/GovernanceFlow.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/Graphana.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/IterationRetro.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/ObservabilityBackup.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/QuickAddWithAi.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/QuickAddWithAi2.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/RegistrationAfter.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/RegistrationBefore.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/ReleaseNotes.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/SecurityBackup.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/SecurityThreatModel.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/TaskExample.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/TaskManagementAfter.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/TaskManagementBefore.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/TaskSetExample.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/playwright-demo-screenshots.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/playwright-test-failure-img.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/playwright-test-failure-txt.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/files/playwright-test-failure.png (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/generate-presentation.py (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/generate-slides.md (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/goodone-reveal-theme.css (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/invitation-2.txt (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/old/presentation-content-proposal.md (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/presentation-2-workbook.md (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/slides-2-backup.md (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/slides-2-executive.md (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/slides-2-improvements.md (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/slides-2-non-tech.md (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/slides-2-task.md (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/slides-2-test.md (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/slides-2.html (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/slides-2.md (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/template-2-junie.md (100%) rename {presentation => doc/presentation}/presentations/Iteration-2/template.pptx (100%) rename {presentation => doc/presentation}/presentations/iteration-1/SoftwareEntwicklungAI.pptx (100%) rename {presentation => doc/presentation}/presentations/iteration-1/auto_agenda.lua (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/architecture_overivew.yaml (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/generated/architecture_overview.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/generated/architecture_overview_orig.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/generated/er_diagram.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/generated/erd.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/generated/local_dev_setup.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/generated/local_dev_setup_ai_dev_edu.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/AiRace.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/AiRaceFinal.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/AndroidApp.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/AndroidTaskMenu.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/ChatGpt.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/CypressTests.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/DashboardBefore.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/DashboardImplementation.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/DashboardProposal.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/GoodOne2020_Starting.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/GoodOne2020_Users.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/IdeIntegration.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/IntellJ.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/SonarSummary.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/TaskManagementBefore.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/TaskManagementProposal.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/android_task_menu.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/angular_task_menu.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/angular_task_menu2.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/title_slide_background_no_text_v2.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/title_slide_goodone_ai_dech.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/images/title_slide_goodone_v3.png (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/input/architecture_overview.drawio (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/input/architecture_overview.old.drawio (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/input/architecture_overview_clean.drawio (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/input/architecture_slide_variant.drawio (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/input/er_diagram.mmd (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/input/erd.puml (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/input/local_dev_setup.drawio (100%) rename {presentation => doc/presentation}/presentations/iteration-1/files/input/local_dev_setup_ai_dev_edu.drawio (100%) rename {presentation => doc/presentation}/presentations/iteration-1/generate_presentation.py (100%) rename {presentation => doc/presentation}/presentations/iteration-1/generated/SoftwareEntwicklungAI_Python.pptx (100%) rename {presentation => doc/presentation}/presentations/iteration-1/patch_autofit.py (100%) rename {presentation => doc/presentation}/presentations/iteration-1/pptx_generation_plan.md (100%) rename {presentation => doc/presentation}/presentations/iteration-1/reference.pptx (100%) rename {presentation => doc/presentation}/presentations/iteration-1/slides-1.md (100%) rename {presentation => doc/presentation}/presentations/iteration-1/template-company.pptx (100%) rename {presentation => doc/presentation}/presentations/iteration-1/template.pptx (100%) rename {presentation => doc/presentation}/presentations/iteration-3/QandA-3.md (100%) rename {presentation => doc/presentation}/presentations/iteration-3/SoftwareEntwicklungAi-Level3.pptx (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/AdrIndex.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/AiRaceFinal.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/AiRaceStart.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/ArchitectureBackup.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/ArchitectureExplainArchitecture.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/ArchitectureExplainSecurity.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/AuthSequence.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/BuildReleasePipeline.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/DeploymentReview.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/GithubActions.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/GovernanceFlow.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/Graphana.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/IterationRetro.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/ObservabilityBackup.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/QuickAddWithAi.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/QuickAddWithAi2.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/RegistrationAfter.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/RegistrationBefore.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/ReleaseNotes.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/SecurityBackup.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/SecurityThreatModel.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/TaskExample.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/TaskManagementAfter.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/TaskManagementBefore.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/TaskSetExample.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/playwright-demo-screenshots.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/playwright-test-failure-img.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/playwright-test-failure-txt.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/files/playwright-test-failure.png (100%) rename {presentation => doc/presentation}/presentations/iteration-3/generate-slides.md (100%) rename {presentation => doc/presentation}/presentations/iteration-3/invitation-3.md (100%) rename {presentation => doc/presentation}/presentations/iteration-3/slides-3.md (100%) rename {presentation => doc/presentation}/presentations/pandoc/pandoc_authoring_checklist.md (100%) rename {presentation => doc/presentation}/presentations/pandoc/reference_template_guidelines.md (100%) rename {presentation => doc/presentation}/src/main/java/ch/goodone/angularai/presentation/CompanyStyleAligner.java (100%) rename {presentation => doc/presentation}/src/main/java/ch/goodone/angularai/presentation/DetailedInspector.java (100%) rename {presentation => doc/presentation}/src/main/java/ch/goodone/angularai/presentation/FinalResultFixer.java (100%) rename {presentation => doc/presentation}/src/main/java/ch/goodone/angularai/presentation/FinalTemplateFixer.java (100%) rename {presentation => doc/presentation}/src/main/java/ch/goodone/angularai/presentation/LayoutInspector.java (100%) rename {presentation => doc/presentation}/src/main/java/ch/goodone/angularai/presentation/TemplateCreator.java (100%) rename {presentation => doc/presentation}/src/main/java/ch/goodone/angularai/presentation/TemplateFixerV2.java (100%) diff --git a/android/app/build.gradle b/android/app/build.gradle deleted file mode 100644 index cbdbbd195..000000000 --- a/android/app/build.gradle +++ /dev/null @@ -1,110 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'com.google.dagger.hilt.android' - -android { - namespace 'ch.goodone.angularai.android' - compileSdk 34 - - defaultConfig { - applicationId "ch.goodone.angularai.android" - minSdk 26 - targetSdk 34 - versionCode 1 - versionName "1.1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - vectorDrawables { - useSupportLibrary true - } - } - - buildTypes { - debug { - testCoverageEnabled true - } - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - - testOptions { - unitTests { - all { - jacoco { - includeNoLocationClasses = true - } - jvmArgs "--add-opens", "java.base/jdk.internal.reflect=ALL-UNNAMED", - "--add-opens", "java.base/java.lang=ALL-UNNAMED", - "--add-opens", "java.base/java.util=ALL-UNNAMED", - "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", - "--add-opens", "java.base/java.io=ALL-UNNAMED", - "--add-opens", "java.base/sun.reflect.annotation=ALL-UNNAMED" - } - returnDefaultValues = true - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } - kotlinOptions { - jvmTarget = '17' - } - buildFeatures { - compose true - } - composeOptions { - kotlinCompilerExtensionVersion '1.5.8' - } - packagingOptions { - resources { - excludes += '/META-INF/{AL2.0,LGPL2.1}' - } - } -} - -dependencies { - implementation 'androidx.core:core-ktx:1.12.0' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0' - implementation 'androidx.activity:activity-compose:1.8.2' - implementation platform('androidx.compose:compose-bom:2023.10.01') - implementation 'androidx.compose.ui:ui' - implementation 'androidx.compose.ui:ui-graphics' - implementation 'androidx.compose.ui:ui-tooling-preview' - implementation 'androidx.compose.material3:material3' - implementation 'androidx.compose.material:material-icons-extended' - implementation 'androidx.navigation:navigation-compose:2.7.7' - - // Retrofit - implementation 'com.squareup.retrofit2:retrofit:2.9.0' - implementation 'com.squareup.retrofit2:converter-gson:2.9.0' - implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0' - - // Room - def room_version = "2.6.1" - implementation "androidx.room:room-runtime:$room_version" - implementation "androidx.room:room-ktx:$room_version" - kapt "androidx.room:room-compiler:$room_version" - - // Hilt - implementation "com.google.dagger:hilt-android:2.50" - kapt "com.google.dagger:hilt-compiler:2.50" - implementation 'androidx.hilt:hilt-navigation-compose:1.1.0' - - // DataStore - implementation "androidx.datastore:datastore-preferences:1.0.0" - - testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:5.15.2' - testImplementation 'org.mockito:mockito-inline:5.2.0' - testImplementation 'org.mockito.kotlin:mockito-kotlin:5.4.0' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - androidTestImplementation platform('androidx.compose:compose-bom:2023.10.01') - androidTestImplementation 'androidx.compose.ui:ui-test-junit4' - debugImplementation 'androidx.compose.ui:ui-tooling' - debugImplementation 'androidx.compose.ui:ui-test-manifest' -} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index df41966a7..000000000 --- a/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/android/app/src/main/java/ch/goodone/angularai/android/AngularAIApplication.kt b/android/app/src/main/java/ch/goodone/angularai/android/AngularAIApplication.kt deleted file mode 100644 index ce8c5769e..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/AngularAIApplication.kt +++ /dev/null @@ -1,7 +0,0 @@ -package ch.goodone.angularai.android - -import android.app.Application -import dagger.hilt.android.HiltAndroidApp - -@HiltAndroidApp -class AngularAIApplication : Application() diff --git a/android/app/src/main/java/ch/goodone/angularai/android/MainActivity.kt b/android/app/src/main/java/ch/goodone/angularai/android/MainActivity.kt deleted file mode 100644 index ded806986..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/MainActivity.kt +++ /dev/null @@ -1,367 +0,0 @@ -package ch.goodone.angularai.android - -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.* -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavType -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController -import androidx.navigation.navArgument -import ch.goodone.angularai.android.ui.SystemViewModel -import ch.goodone.angularai.android.domain.model.User -import ch.goodone.angularai.android.ui.admin.AdminUserEditScreen -import ch.goodone.angularai.android.ui.admin.AdminUserListScreen -import ch.goodone.angularai.android.ui.auth.AuthViewModel -import ch.goodone.angularai.android.ui.auth.LoginScreen -import ch.goodone.angularai.android.ui.auth.RegisterScreen -import ch.goodone.angularai.android.ui.dashboard.DashboardScreen -import ch.goodone.angularai.android.ui.log.LogScreen -import ch.goodone.angularai.android.ui.profile.ProfileScreen -import ch.goodone.angularai.android.ui.tasks.TaskEditScreen -import ch.goodone.angularai.android.ui.tasks.TaskListScreen -import ch.goodone.angularai.android.ui.theme.AngularAITheme -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch - -@AndroidEntryPoint -class MainActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContent { - AngularAITheme { - MainApp() - } - } - } -} - -@Composable -fun MainDrawerContent( - isLoggedIn: Boolean, - isAdmin: Boolean, - scope: kotlinx.coroutines.CoroutineScope, - drawerState: DrawerState, - navController: androidx.navigation.NavHostController, - authViewModel: AuthViewModel -) { - ModalDrawerSheet { - Box( - modifier = Modifier - .fillMaxWidth() - .background(Color(0xFF1A237E)) - .padding(16.dp) - ) { - Row(verticalAlignment = Alignment.CenterVertically) { - Icon( - imageVector = Icons.Default.Filter1, - contentDescription = null, - tint = Color(0xFFFF4081), - modifier = Modifier.size(32.dp) - ) - Spacer(modifier = Modifier.width(12.dp)) - Text( - "GoodOne", - color = Color.White, - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.Bold - ) - } - } - Divider() - if (isLoggedIn) { - NavigationDrawerItem( - label = { Text("Dashboard") }, - selected = false, - onClick = { scope.launch { drawerState.close() }; navController.navigate("dashboard") }, - icon = { Icon(Icons.Default.Dashboard, contentDescription = null) } - ) - NavigationDrawerItem( - label = { Text("Tasks") }, - selected = false, - onClick = { scope.launch { drawerState.close() }; navController.navigate("tasks") }, - icon = { Icon(Icons.Default.List, contentDescription = null) } - ) - NavigationDrawerItem( - label = { Text("Profile") }, - selected = false, - onClick = { scope.launch { drawerState.close() }; navController.navigate("profile") }, - icon = { Icon(Icons.Default.Person, contentDescription = null) } - ) - if (isAdmin) { - NavigationDrawerItem( - label = { Text("User Admin") }, - selected = false, - onClick = { scope.launch { drawerState.close() }; navController.navigate("admin") }, - icon = { Icon(Icons.Default.SupervisorAccount, contentDescription = null) } - ) - NavigationDrawerItem( - label = { Text("Logs") }, - selected = false, - onClick = { scope.launch { drawerState.close() }; navController.navigate("logs") }, - icon = { Icon(Icons.Default.History, contentDescription = null) } - ) - } - Divider() - NavigationDrawerItem( - label = { Text("Logout") }, - selected = false, - onClick = { - scope.launch { - drawerState.close() - authViewModel.onLogout() - navController.navigate("login") { - popUpTo(0) - } - } - }, - icon = { Icon(Icons.Default.Logout, contentDescription = null) } - ) - } else { - NavigationDrawerItem( - label = { Text("Login") }, - selected = false, - onClick = { scope.launch { drawerState.close() }; navController.navigate("login") }, - icon = { Icon(Icons.Default.Login, contentDescription = null) } - ) - NavigationDrawerItem( - label = { Text("Register") }, - selected = false, - onClick = { scope.launch { drawerState.close() }; navController.navigate("register") }, - icon = { Icon(Icons.Default.PersonAdd, contentDescription = null) } - ) - } - } -} - - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun MainApp( - authViewModel: AuthViewModel = hiltViewModel(), - systemViewModel: SystemViewModel = hiltViewModel() -) { - val navController = rememberNavController() - val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) - val scope = rememberCoroutineScope() - - val currentUser by authViewModel.currentUser.collectAsState() - val isLoggedIn = currentUser != null - val isAdmin = currentUser?.role == "ROLE_ADMIN" || currentUser?.role == "ROLE_ADMIN_READ" - - var showSettingsMenu by remember { mutableStateOf(false) } - var showHelpDialog by remember { mutableStateOf(false) } - - LaunchedEffect(isLoggedIn) { - if (isLoggedIn) { - systemViewModel.loadSystemInfo() - } - } - - if (showHelpDialog) { - AlertDialog( - onDismissRequest = { showHelpDialog = false }, - title = { Text("Application Help") }, - text = { - Column { - Text("This application allows you to manage tasks and user profiles.") - Spacer(modifier = Modifier.height(8.dp)) - Text("• Tasks: Create, edit, and delete your tasks.") - Text("• Profile: Manage your personal information.") - Text("• Admin: Administrators can manage all users.") - Spacer(modifier = Modifier.height(8.dp)) - Text("Note: This is a test application for AI code generation.", style = MaterialTheme.typography.bodySmall) - } - }, - confirmButton = { - TextButton(onClick = { showHelpDialog = false }) { Text("Close") } - } - ) - } - - ModalNavigationDrawer( - drawerState = drawerState, - drawerContent = { - MainDrawerContent( - isLoggedIn = isLoggedIn, - isAdmin = isAdmin, - scope = scope, - drawerState = drawerState, - navController = navController, - authViewModel = authViewModel - ) - }, - gesturesEnabled = true - ) { - Scaffold( - topBar = { - MainTopBar( - isLoggedIn = isLoggedIn, - scope = scope, - drawerState = drawerState, - onSettingsClick = { showSettingsMenu = true } - ) - } - ) { padding -> - MainNavHost( - navController = navController, - padding = padding, - isAdmin = isAdmin, - onSaveTask = { navController.popBackStack() }, - onBack = { navController.popBackStack() } - ) - } - } -} - -@Composable -fun MainNavHost( - navController: androidx.navigation.NavHostController, - padding: PaddingValues, - isAdmin: Boolean, - onSaveTask: () -> Unit, - onBack: () -> Unit -) { - NavHost( - navController = navController, - startDestination = "login", - modifier = Modifier.padding(padding) - ) { - composable("login") { - LoginScreen( - onLoginSuccess = { user -> - if (user.role == "ROLE_ADMIN" || user.role == "ROLE_ADMIN_READ") { - navController.navigate("dashboard") { popUpTo("login") { inclusive = true } } - } else { - navController.navigate("tasks") { popUpTo("login") { inclusive = true } } - } - }, - onNavigateToRegister = { navController.navigate("register") } - ) - } - composable("register") { - RegisterScreen( - onRegisterSuccess = { navController.navigate("login") }, - onNavigateToLogin = { navController.navigate("login") } - ) - } - composable("dashboard") { - DashboardScreen( - onNavigateToTasks = { navController.navigate("tasks") }, - onNavigateToLogs = { navController.navigate("logs") }, - onNavigateToUsers = { navController.navigate("admin") } - ) - } - composable("tasks") { - TaskListScreen( - onTaskClick = { task -> navController.navigate("task_edit/${task.id}") }, - onAddTask = { navController.navigate("task_add") } - ) - } - composable("task_add") { - TaskEditScreen( - taskId = null, - onSave = onSaveTask, - onBack = onBack - ) - } - composable( - "task_edit/{taskId}", - arguments = listOf(navArgument("taskId") { type = NavType.LongType }) - ) { backStackEntry -> - val taskId = backStackEntry.arguments?.getLong("taskId") - TaskEditScreen( - taskId = taskId, - onSave = onSaveTask, - onBack = onBack - ) - } - composable("profile") { - ProfileScreen() - } - composable("logs") { - if (isAdmin) { - LogScreen() - } - } - composable("admin") { - AdminUserListScreen( - onUserClick = { user -> navController.navigate("admin_user_edit/${user.id}") }, - onAddUser = { navController.navigate("admin_user_add") } - ) - } - composable("admin_user_add") { - AdminUserEditScreen( - userId = null, - onSave = { navController.popBackStack() }, - onBack = { navController.popBackStack() } - ) - } - composable( - "admin_user_edit/{userId}", - arguments = listOf(navArgument("userId") { type = NavType.LongType }) - ) { backStackEntry -> - val userId = backStackEntry.arguments?.getLong("userId") - AdminUserEditScreen( - userId = userId, - onSave = { navController.popBackStack() }, - onBack = { navController.popBackStack() } - ) - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun MainTopBar( - isLoggedIn: Boolean, - scope: kotlinx.coroutines.CoroutineScope, - drawerState: DrawerState, - onSettingsClick: () -> Unit -) { - TopAppBar( - title = { - Row(verticalAlignment = Alignment.CenterVertically) { - Icon( - imageVector = Icons.Default.Filter1, - contentDescription = null, - tint = Color(0xFFFF4081), - modifier = Modifier.size(24.dp) - ) - Spacer(modifier = Modifier.width(8.dp)) - Text("GoodOne") - } - }, - navigationIcon = { - IconButton(onClick = { scope.launch { drawerState.open() } }) { - Icon(Icons.Default.Menu, contentDescription = "Menu") - } - }, - actions = { - if (isLoggedIn) { - IconButton(onClick = onSettingsClick) { - Icon(Icons.Default.Settings, contentDescription = "Settings") - } - } - }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = Color(0xFF1A237E), - titleContentColor = Color.White, - navigationIconContentColor = Color.White, - actionIconContentColor = Color.White - ) - ) -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/local/AppDatabase.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/local/AppDatabase.kt deleted file mode 100644 index 064e99233..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/local/AppDatabase.kt +++ /dev/null @@ -1,21 +0,0 @@ -package ch.goodone.angularai.android.data.local - -import androidx.room.Database -import androidx.room.RoomDatabase -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase -import ch.goodone.angularai.android.data.local.entity.TaskEntity - -@Database(entities = [TaskEntity::class], version = 2, exportSchema = false) -abstract class AppDatabase : RoomDatabase() { - abstract val taskDao: TaskDao - - companion object { - val MIGRATION_1_2 = object : Migration(1, 2) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE tasks ADD COLUMN status TEXT NOT NULL DEFAULT 'OPEN'") - db.execSQL("ALTER TABLE tasks ADD COLUMN position INTEGER NOT NULL DEFAULT 0") - } - } - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/local/TaskDao.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/local/TaskDao.kt deleted file mode 100644 index a23b09293..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/local/TaskDao.kt +++ /dev/null @@ -1,20 +0,0 @@ -package ch.goodone.angularai.android.data.local - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import ch.goodone.angularai.android.data.local.entity.TaskEntity -import kotlinx.coroutines.flow.Flow - -@Dao -interface TaskDao { - @Query("SELECT * FROM tasks ORDER BY position ASC") - fun getAllTasks(): Flow> - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertTasks(tasks: List) - - @Query("DELETE FROM tasks") - suspend fun clearTasks() -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/local/entity/TaskEntity.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/local/entity/TaskEntity.kt deleted file mode 100644 index 7e5622ca0..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/local/entity/TaskEntity.kt +++ /dev/null @@ -1,15 +0,0 @@ -package ch.goodone.angularai.android.data.local.entity - -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity(tableName = "tasks") -data class TaskEntity( - @PrimaryKey val id: Long, - val title: String, - val description: String, - val dueDate: String?, - val priority: String, - val status: String, - val position: Int -) diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/AuthApi.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/remote/AuthApi.kt deleted file mode 100644 index bbe5cd2cb..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/AuthApi.kt +++ /dev/null @@ -1,22 +0,0 @@ -package ch.goodone.angularai.android.data.remote - -import ch.goodone.angularai.android.data.remote.dto.UserDTO -import retrofit2.Response -import retrofit2.http.Body -import retrofit2.http.Header -import retrofit2.http.POST - -interface AuthApi { - @POST("api/auth/login") - suspend fun login( - @Header("Authorization") authHeader: String - ): Response - - @POST("api/auth/register") - suspend fun register( - @Body user: UserDTO - ): Response - - @POST("api/auth/logout") - suspend fun logout(): Response -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/DashboardApi.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/remote/DashboardApi.kt deleted file mode 100644 index efc352acb..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/DashboardApi.kt +++ /dev/null @@ -1,9 +0,0 @@ -package ch.goodone.angularai.android.data.remote - -import ch.goodone.angularai.android.data.remote.dto.DashboardDTO -import retrofit2.http.GET - -interface DashboardApi { - @GET("api/dashboard") - suspend fun getDashboard(): DashboardDTO -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/LogApi.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/remote/LogApi.kt deleted file mode 100644 index 32b7d3700..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/LogApi.kt +++ /dev/null @@ -1,22 +0,0 @@ -package ch.goodone.angularai.android.data.remote - -import ch.goodone.angularai.android.data.remote.dto.LogResponseDTO -import retrofit2.Response -import retrofit2.http.DELETE -import retrofit2.http.GET -import retrofit2.http.Query - -interface LogApi { - @GET("api/admin/logs") - suspend fun getLogs( - @Query("page") page: Int = 0, - @Query("size") size: Int = 20, - @Query("actionType") actionType: String? = null, - @Query("startDate") startDate: String? = null, - @Query("endDate") endDate: String? = null, - @Query("sort") sort: String = "timestamp,desc" - ): LogResponseDTO - - @DELETE("api/admin/logs") - suspend fun clearLogs(): Response -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/SystemApi.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/remote/SystemApi.kt deleted file mode 100644 index 764773084..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/SystemApi.kt +++ /dev/null @@ -1,9 +0,0 @@ -package ch.goodone.angularai.android.data.remote - -import ch.goodone.angularai.android.data.remote.dto.SystemInfoDTO -import retrofit2.http.GET - -interface SystemApi { - @GET("api/system/info") - suspend fun getSystemInfo(): SystemInfoDTO -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/TaskApi.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/remote/TaskApi.kt deleted file mode 100644 index ea7244998..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/TaskApi.kt +++ /dev/null @@ -1,22 +0,0 @@ -package ch.goodone.angularai.android.data.remote - -import ch.goodone.angularai.android.data.remote.dto.TaskDTO -import retrofit2.Response -import retrofit2.http.* - -interface TaskApi { - @GET("api/tasks") - suspend fun getTasks(): List - - @POST("api/tasks") - suspend fun createTask(@Body task: TaskDTO): TaskDTO - - @PUT("api/tasks/{id}") - suspend fun updateTask(@Path("id") id: Long, @Body task: TaskDTO): TaskDTO - - @DELETE("api/tasks/{id}") - suspend fun deleteTask(@Path("id") id: Long): Response - - @PUT("api/tasks/reorder") - suspend fun reorderTasks(@Body taskIds: List): Response -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/UserApi.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/remote/UserApi.kt deleted file mode 100644 index 950ffac3d..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/UserApi.kt +++ /dev/null @@ -1,28 +0,0 @@ -package ch.goodone.angularai.android.data.remote - -import ch.goodone.angularai.android.data.remote.dto.UserDTO -import retrofit2.Response -import retrofit2.http.* - -interface UserApi { - @GET("api/system/info") - suspend fun getSystemInfo(): ch.goodone.angularai.android.data.remote.dto.SystemInfoDTO - - @GET("api/users/me") - suspend fun getCurrentUser(): UserDTO - - @PUT("api/users/me") - suspend fun updateCurrentUser(@Body user: UserDTO): UserDTO - - @GET("api/admin/users") - suspend fun getAllUsers(): List - - @POST("api/admin/users") - suspend fun createUser(@Body user: UserDTO): UserDTO - - @PUT("api/admin/users/{id}") - suspend fun updateUser(@Path("id") id: Long, @Body user: UserDTO): UserDTO - - @DELETE("api/admin/users/{id}") - suspend fun deleteUser(@Path("id") id: Long): Response -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/ActionLogDTO.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/ActionLogDTO.kt deleted file mode 100644 index 056312723..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/ActionLogDTO.kt +++ /dev/null @@ -1,17 +0,0 @@ -package ch.goodone.angularai.android.data.remote.dto - -data class ActionLogDTO( - val id: Long, - val timestamp: String, - val login: String, - val action: String, - val details: String -) - -data class LogResponseDTO( - val content: List, - val totalElements: Long, - val totalPages: Int, - val size: Int, - val number: Int -) diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/DashboardDTO.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/DashboardDTO.kt deleted file mode 100644 index 6c1430910..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/DashboardDTO.kt +++ /dev/null @@ -1,27 +0,0 @@ -package ch.goodone.angularai.android.data.remote.dto - -data class DashboardDTO( - val summary: SummaryStatsDTO, - val priorityTasks: List, - val recentActivity: List, - val recentUsers: List, - val taskDistribution: TaskStatusDistributionDTO -) - -data class SummaryStatsDTO( - val openTasks: Long, - val openTasksDelta: Long, - val activeUsers: Long, - val activeUsersDelta: Long, - val completedTasks: Long, - val completedTasksDelta: Long, - val todayLogs: Long, - val todayLogsDelta: Long -) - -data class TaskStatusDistributionDTO( - val open: Long, - val inProgress: Long, - val completed: Long, - val total: Long -) diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/SystemInfoDTO.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/SystemInfoDTO.kt deleted file mode 100644 index e91f9dcfa..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/SystemInfoDTO.kt +++ /dev/null @@ -1,8 +0,0 @@ -package ch.goodone.angularai.android.data.remote.dto - -data class SystemInfoDTO( - val backendVersion: String, - val frontendVersion: String, - val mode: String, - val landingMessage: String? = null -) diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/TaskDTO.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/TaskDTO.kt deleted file mode 100644 index d6236ec56..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/TaskDTO.kt +++ /dev/null @@ -1,12 +0,0 @@ -package ch.goodone.angularai.android.data.remote.dto - -data class TaskDTO( - val id: Long? = null, - val title: String, - val description: String, - val dueDate: String?, - val priority: String, - val status: String, - val position: Int, - val createdAt: String? = null -) diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/UserDTO.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/UserDTO.kt deleted file mode 100644 index f4cc68e4b..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/remote/dto/UserDTO.kt +++ /dev/null @@ -1,14 +0,0 @@ -package ch.goodone.angularai.android.data.remote.dto - -data class UserDTO( - val id: Long? = null, - val firstName: String? = null, - val lastName: String? = null, - val login: String? = null, - val email: String? = null, - val birthDate: String? = null, // yyyy-MM-dd - val address: String? = null, - val role: String? = null, - val password: String? = null, - val createdAt: String? = null -) diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/repository/AuthRepository.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/repository/AuthRepository.kt deleted file mode 100644 index 542f6c1f9..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/repository/AuthRepository.kt +++ /dev/null @@ -1,106 +0,0 @@ -package ch.goodone.angularai.android.data.repository - -import ch.goodone.angularai.android.data.remote.AuthApi -import ch.goodone.angularai.android.data.remote.dto.UserDTO -import ch.goodone.angularai.android.domain.model.User -import android.util.Base64 -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.stringPreferencesKey -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.firstOrNull -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AuthRepository @Inject constructor( - private val api: AuthApi, - private val userRepository: UserRepository, - private val dataStore: DataStore -) { - companion object { - private val AUTH_KEY = stringPreferencesKey("auth_token") - } - - private val _currentUser = MutableStateFlow(null) - val currentUser: StateFlow = _currentUser.asStateFlow() - - val authToken: Flow = dataStore.data.map { it[AUTH_KEY] } - - suspend fun init() { - authToken.firstOrNull()?.let { - try { - _currentUser.value = userRepository.getCurrentUser() - } catch (e: Exception) { - logout() - } - } - } - - suspend fun login(login: String, pass: String): Result { - return try { - val token = Base64.encodeToString("$login:$pass".toByteArray(), Base64.NO_WRAP) - val response = api.login("Basic $token") - if (response.isSuccessful) { - val userDto = response.body()!! - dataStore.edit { it[AUTH_KEY] = token } - val user = userDto.toDomain() - _currentUser.value = user - Result.success(user) - } else { - val errorBody = response.errorBody()?.string() ?: "Login failed: ${response.code()}" - Result.failure(Exception(errorBody)) - } - } catch (e: Exception) { - Result.failure(e) - } - } - - suspend fun register(user: User, pass: String): Result { - return try { - val dto = UserDTO( - firstName = user.firstName, - lastName = user.lastName, - login = user.login, - email = user.email, - birthDate = user.birthDate, - address = user.address, - password = pass - ) - val response = api.register(dto) - if (response.isSuccessful) { - Result.success(response.body()!!.toDomain()) - } else { - val errorBody = response.errorBody()?.string() ?: "Registration failed: ${response.code()}" - Result.failure(Exception(errorBody)) - } - } catch (e: Exception) { - Result.failure(e) - } - } - - suspend fun logout() { - try { - api.logout() - } finally { - dataStore.edit { it.remove(AUTH_KEY) } - _currentUser.value = null - } - } - - private fun UserDTO.toDomain() = User( - id = id, - firstName = firstName ?: "", - lastName = lastName ?: "", - login = login ?: "", - email = email ?: "", - birthDate = birthDate ?: "", - address = address ?: "", - role = role ?: "" - ) -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/repository/DashboardRepository.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/repository/DashboardRepository.kt deleted file mode 100644 index 760d8d112..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/repository/DashboardRepository.kt +++ /dev/null @@ -1,15 +0,0 @@ -package ch.goodone.angularai.android.data.repository - -import ch.goodone.angularai.android.data.remote.DashboardApi -import ch.goodone.angularai.android.data.remote.dto.DashboardDTO -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class DashboardRepository @Inject constructor( - private val dashboardApi: DashboardApi -) { - suspend fun getDashboard(): DashboardDTO { - return dashboardApi.getDashboard() - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/repository/LogRepository.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/repository/LogRepository.kt deleted file mode 100644 index 27c99a216..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/repository/LogRepository.kt +++ /dev/null @@ -1,30 +0,0 @@ -package ch.goodone.angularai.android.data.repository - -import ch.goodone.angularai.android.data.remote.LogApi -import ch.goodone.angularai.android.data.remote.dto.LogResponseDTO -import javax.inject.Inject - -class LogRepository @Inject constructor( - private val api: LogApi -) { - suspend fun getLogs( - page: Int, - size: Int, - actionType: String?, - startDate: String?, - endDate: String? - ): LogResponseDTO { - val typeParam = if (actionType == "all") null else actionType - return api.getLogs( - page = page, - size = size, - actionType = typeParam, - startDate = startDate, - endDate = endDate - ) - } - - suspend fun clearLogs() { - api.clearLogs() - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/repository/SystemRepository.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/repository/SystemRepository.kt deleted file mode 100644 index 93d50ffd7..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/repository/SystemRepository.kt +++ /dev/null @@ -1,15 +0,0 @@ -package ch.goodone.angularai.android.data.repository - -import ch.goodone.angularai.android.data.remote.SystemApi -import ch.goodone.angularai.android.data.remote.dto.SystemInfoDTO -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SystemRepository @Inject constructor( - private val api: SystemApi -) { - suspend fun getSystemInfo(): SystemInfoDTO { - return api.getSystemInfo() - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/repository/TaskRepository.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/repository/TaskRepository.kt deleted file mode 100644 index 099f81fff..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/repository/TaskRepository.kt +++ /dev/null @@ -1,73 +0,0 @@ -package ch.goodone.angularai.android.data.repository - -import ch.goodone.angularai.android.data.local.TaskDao -import ch.goodone.angularai.android.data.local.entity.TaskEntity -import ch.goodone.angularai.android.data.remote.TaskApi -import ch.goodone.angularai.android.data.remote.dto.TaskDTO -import ch.goodone.angularai.android.domain.model.Task -import ch.goodone.angularai.android.domain.model.TaskStatus -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import javax.inject.Inject - -class TaskRepository @Inject constructor( - private val api: TaskApi, - private val dao: TaskDao -) { - val tasks: Flow> = dao.getAllTasks().map { entities -> - entities.map { it.toDomain() } - } - - suspend fun refreshTasks() { - try { - val remoteTasks = api.getTasks() - dao.clearTasks() - dao.insertTasks(remoteTasks.map { it.toEntity() }) - } catch (e: Exception) { - // Handle error or use cache - } - } - - suspend fun createTask(task: Task) { - val dto = task.toDto() - val savedDto = api.createTask(dto) - dao.insertTasks(listOf(savedDto.toEntity())) - } - - suspend fun updateTask(task: Task) { - val dto = task.toDto() - val updatedDto = api.updateTask(task.id!!, dto) - dao.insertTasks(listOf(updatedDto.toEntity())) - } - - suspend fun deleteTask(id: Long) { - api.deleteTask(id) - // Ideally we should have a way to delete from local too, - // but for now refreshTasks will handle it or we can add a delete method to DAO. - refreshTasks() - } - - suspend fun reorderTasks(taskIds: List) { - try { - api.reorderTasks(taskIds) - refreshTasks() - } catch (e: Exception) { - // Handle error - } - } - - private fun TaskEntity.toDomain() = Task( - id, title, description, dueDate, priority, - TaskStatus.valueOf(status), position - ) - - private fun TaskDTO.toEntity() = TaskEntity( - id!!, title, description, dueDate, priority, - status, position - ) - - private fun Task.toDto() = TaskDTO( - id, title, description, dueDate, priority, - status.name, position - ) -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/data/repository/UserRepository.kt b/android/app/src/main/java/ch/goodone/angularai/android/data/repository/UserRepository.kt deleted file mode 100644 index 7f287f651..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/data/repository/UserRepository.kt +++ /dev/null @@ -1,54 +0,0 @@ -package ch.goodone.angularai.android.data.repository - -import ch.goodone.angularai.android.data.remote.UserApi -import ch.goodone.angularai.android.data.remote.dto.UserDTO -import ch.goodone.angularai.android.domain.model.User -import javax.inject.Inject - -class UserRepository @Inject constructor( - private val api: UserApi -) { - suspend fun getCurrentUser(): User = api.getCurrentUser().toDomain() - - suspend fun updateCurrentUser(user: User): User { - return api.updateCurrentUser(user.toDto()).toDomain() - } - - suspend fun getAllUsers(): List { - return api.getAllUsers().map { it.toDomain() } - } - - suspend fun createUser(user: User, pass: String): User { - return api.createUser(user.toDto().copy(password = pass)).toDomain() - } - - suspend fun updateUser(user: User): User { - return api.updateUser(user.id!!, user.toDto()).toDomain() - } - - suspend fun deleteUser(id: Long) { - api.deleteUser(id) - } - - private fun UserDTO.toDomain() = User( - id = id, - firstName = firstName ?: "", - lastName = lastName ?: "", - login = login ?: "", - email = email ?: "", - birthDate = birthDate ?: "", - address = address ?: "", - role = role ?: "" - ) - - private fun User.toDto() = UserDTO( - id = id, - firstName = firstName, - lastName = lastName, - login = login, - email = email, - birthDate = birthDate, - address = address, - role = role - ) -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/di/NetworkModule.kt b/android/app/src/main/java/ch/goodone/angularai/android/di/NetworkModule.kt deleted file mode 100644 index 9cd95cf8b..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/di/NetworkModule.kt +++ /dev/null @@ -1,144 +0,0 @@ -package ch.goodone.angularai.android.di - -import android.content.Context -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.PreferenceDataStoreFactory -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.preferencesDataStoreFile -import ch.goodone.angularai.android.data.local.AppDatabase -import ch.goodone.angularai.android.data.local.TaskDao -import ch.goodone.angularai.android.data.remote.AuthApi -import ch.goodone.angularai.android.data.remote.DashboardApi -import ch.goodone.angularai.android.data.remote.LogApi -import ch.goodone.angularai.android.data.remote.SystemApi -import ch.goodone.angularai.android.data.remote.TaskApi -import ch.goodone.angularai.android.data.remote.UserApi -import ch.goodone.angularai.android.data.repository.AuthRepository -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import okhttp3.Interceptor -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -object NetworkModule { - - @Provides - @Singleton - fun provideDataStore(@ApplicationContext context: Context): DataStore { - return PreferenceDataStoreFactory.create( - produceFile = { context.preferencesDataStoreFile("settings") } - ) - } - - @Provides - @Singleton - fun provideOkHttpClient(dataStore: DataStore): OkHttpClient { - val logging = HttpLoggingInterceptor().apply { - level = HttpLoggingInterceptor.Level.BODY - } - - val cookieManager = java.net.CookieManager().apply { - setCookiePolicy(java.net.CookiePolicy.ACCEPT_ALL) - } - - val authInterceptor = Interceptor { chain -> - val token = runBlocking { - dataStore.data.first()[ch.goodone.angularai.android.di.NetworkModule.AUTH_KEY] - } - val requestBuilder = chain.request().newBuilder() - - if (token != null) { - requestBuilder.addHeader("Authorization", "Basic $token") - } - - // Add CSRF token header if cookie is present - val cookies = cookieManager.cookieStore.cookies - cookies.find { it.name == "XSRF-TOKEN" }?.let { - requestBuilder.addHeader("X-XSRF-TOKEN", it.value) - } - - chain.proceed(requestBuilder.build()) - } - - return OkHttpClient.Builder() - .addInterceptor(logging) - .addInterceptor(authInterceptor) - .build() - } - - private val AUTH_KEY = androidx.datastore.preferences.core.stringPreferencesKey("auth_token") - - @Provides - @Singleton - fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { - return Retrofit.Builder() - .baseUrl("http://10.0.2.2:8080/") // Emulator address for localhost - .client(okHttpClient) - .addConverterFactory(GsonConverterFactory.create()) - .build() - } - - @Provides - @Singleton - fun provideAuthApi(retrofit: Retrofit): AuthApi { - return retrofit.create(AuthApi::class.java) - } - - @Provides - @Singleton - fun provideTaskApi(retrofit: Retrofit): TaskApi { - return retrofit.create(TaskApi::class.java) - } - - @Provides - @Singleton - fun provideUserApi(retrofit: Retrofit): UserApi { - return retrofit.create(UserApi::class.java) - } - - @Provides - @Singleton - fun provideSystemApi(retrofit: Retrofit): SystemApi { - return retrofit.create(SystemApi::class.java) - } - - @Provides - @Singleton - fun provideLogApi(retrofit: Retrofit): LogApi { - return retrofit.create(LogApi::class.java) - } - - @Provides - @Singleton - fun provideDashboardApi(retrofit: Retrofit): DashboardApi { - return retrofit.create(DashboardApi::class.java) - } - - @Provides - @Singleton - fun provideDatabase(@ApplicationContext context: Context): AppDatabase { - return androidx.room.Room.databaseBuilder( - context, - AppDatabase::class.java, - "angularai_db" - ) - .addMigrations(AppDatabase.MIGRATION_1_2) - .build() - } - - @Provides - @Singleton - fun provideTaskDao(db: AppDatabase): TaskDao { - return db.taskDao - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/domain/model/Task.kt b/android/app/src/main/java/ch/goodone/angularai/android/domain/model/Task.kt deleted file mode 100644 index 399258502..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/domain/model/Task.kt +++ /dev/null @@ -1,11 +0,0 @@ -package ch.goodone.angularai.android.domain.model - -data class Task( - val id: Long? = null, - val title: String, - val description: String, - val dueDate: String?, - val priority: String, - val status: TaskStatus = TaskStatus.OPEN, - val position: Int = 0 -) diff --git a/android/app/src/main/java/ch/goodone/angularai/android/domain/model/TaskStatus.kt b/android/app/src/main/java/ch/goodone/angularai/android/domain/model/TaskStatus.kt deleted file mode 100644 index 9a1cb8b9b..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/domain/model/TaskStatus.kt +++ /dev/null @@ -1,8 +0,0 @@ -package ch.goodone.angularai.android.domain.model - -enum class TaskStatus { - OPEN, - IN_PROGRESS, - COMPLETED, - CLOSED -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/domain/model/User.kt b/android/app/src/main/java/ch/goodone/angularai/android/domain/model/User.kt deleted file mode 100644 index b832a820c..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/domain/model/User.kt +++ /dev/null @@ -1,12 +0,0 @@ -package ch.goodone.angularai.android.domain.model - -data class User( - val id: Long? = null, - val firstName: String = "", - val lastName: String = "", - val login: String = "", - val email: String = "", - val birthDate: String = "", - val address: String = "", - val role: String = "" -) diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/SystemViewModel.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/SystemViewModel.kt deleted file mode 100644 index 8973ac07e..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/SystemViewModel.kt +++ /dev/null @@ -1,30 +0,0 @@ -package ch.goodone.angularai.android.ui - -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import ch.goodone.angularai.android.data.repository.SystemRepository -import ch.goodone.angularai.android.data.remote.dto.SystemInfoDTO -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class SystemViewModel @Inject constructor( - private val repository: SystemRepository -) : ViewModel() { - - private val _systemInfo = mutableStateOf(null) - val systemInfo: State = _systemInfo - - fun loadSystemInfo() { - viewModelScope.launch { - try { - _systemInfo.value = repository.getSystemInfo() - } catch (e: Exception) { - // Handle error - } - } - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/admin/AdminUserEditScreen.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/admin/AdminUserEditScreen.kt deleted file mode 100644 index 317e5140f..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/admin/AdminUserEditScreen.kt +++ /dev/null @@ -1,203 +0,0 @@ -package ch.goodone.angularai.android.ui.admin - -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.PressInteraction -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.DateRange -import androidx.compose.material3.* -import androidx.compose.runtime.* -import ch.goodone.angularai.android.ui.auth.AuthViewModel -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import ch.goodone.angularai.android.domain.model.User -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import java.time.Instant -import java.time.LocalDate -import java.time.ZoneId -import java.time.format.DateTimeFormatter - -@Composable -private fun calculateInitialDateMillis(showDatePicker: Boolean, birthDate: String): Long? { - return remember(showDatePicker) { - if (!showDatePicker) return@remember null - - try { - val localDate = if (birthDate.isNotBlank()) { - LocalDate.parse(birthDate, DateTimeFormatter.ISO_LOCAL_DATE) - } else { - LocalDate.now().minusYears(20) - } - localDate.atStartOfDay(ZoneId.systemDefault()) - .toInstant() - .toEpochMilli() - } catch (e: Exception) { - LocalDate.now().minusYears(20) - .atStartOfDay(ZoneId.systemDefault()) - .toInstant() - .toEpochMilli() - } - } -} - -@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) -@Composable -fun AdminUserEditScreen( - userId: Long?, - onSave: () -> Unit, - onBack: () -> Unit, - viewModel: AdminViewModel = hiltViewModel(), - authViewModel: AuthViewModel = hiltViewModel() -) { - val state = viewModel.state.value - val currentUser by authViewModel.currentUser.collectAsState() - val canEdit = currentUser?.role == "ROLE_ADMIN" - val user = remember(userId, state.users) { userId?.let { id -> state.users.find { it.id == id } } } - - var firstName by remember { mutableStateOf("") } - var lastName by remember { mutableStateOf("") } - var login by remember { mutableStateOf("") } - var email by remember { mutableStateOf("") } - var password by remember { mutableStateOf("") } - var birthDate by remember { mutableStateOf("") } - var address by remember { mutableStateOf("") } - var role by remember { mutableStateOf("ROLE_USER") } - - var showDatePicker by remember { mutableStateOf(false) } - val datePickerState = rememberDatePickerState( - initialSelectedDateMillis = calculateInitialDateMillis(showDatePicker, birthDate) - ) - - LaunchedEffect(user) { - user?.let { - firstName = it.firstName - lastName = it.lastName - login = it.login - email = it.email - birthDate = it.birthDate - address = it.address - role = it.role - } - } - - Column( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - .verticalScroll(rememberScrollState()) - ) { - Text(text = if (user == null) "Add User" else (if (canEdit) "Edit User" else "View User"), style = MaterialTheme.typography.headlineMedium) - Spacer(modifier = Modifier.height(16.dp)) - - TextField(value = firstName, onValueChange = { firstName = it }, label = { Text("First Name") }, modifier = Modifier.fillMaxWidth(), enabled = canEdit) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = lastName, onValueChange = { lastName = it }, label = { Text("Last Name") }, modifier = Modifier.fillMaxWidth(), enabled = canEdit) - Spacer(modifier = Modifier.height(8.dp)) - if (user == null) { - TextField(value = login, onValueChange = { login = it }, label = { Text("Login") }, modifier = Modifier.fillMaxWidth(), enabled = canEdit) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = password, onValueChange = { password = it }, label = { Text("Password") }, modifier = Modifier.fillMaxWidth(), enabled = canEdit) - Spacer(modifier = Modifier.height(8.dp)) - } - TextField(value = email, onValueChange = { email = it }, label = { Text("Email") }, modifier = Modifier.fillMaxWidth(), enabled = canEdit) - Spacer(modifier = Modifier.height(8.dp)) - - if (showDatePicker && canEdit) { - DatePickerDialog( - onDismissRequest = { showDatePicker = false }, - confirmButton = { - TextButton(onClick = { - datePickerState.selectedDateMillis?.let { millis -> - val date = Instant.ofEpochMilli(millis) - .atZone(ZoneId.systemDefault()) - .toLocalDate() - birthDate = date.format(DateTimeFormatter.ISO_LOCAL_DATE) - } - showDatePicker = false - }) { - Text("OK") - } - }, - dismissButton = { - TextButton(onClick = { showDatePicker = false }) { - Text("Cancel") - } - } - ) { - DatePicker(state = datePickerState) - } - } - - TextField( - value = birthDate, - onValueChange = { }, - label = { Text("Birth Date (yyyy-MM-dd)") }, - modifier = Modifier.fillMaxWidth(), - enabled = canEdit, - readOnly = true, - interactionSource = remember { MutableInteractionSource() }.also { interactionSource -> - LaunchedEffect(interactionSource) { - interactionSource.interactions.collect { interaction -> - if (interaction is PressInteraction.Release) { - showDatePicker = true - } - } - } - }, - trailingIcon = { - if (canEdit) { - IconButton(onClick = { showDatePicker = true }) { - Icon(Icons.Default.DateRange, contentDescription = "Select Date") - } - } - } - ) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = address, onValueChange = { address = it }, label = { Text("Address") }, modifier = Modifier.fillMaxWidth(), enabled = canEdit) - Spacer(modifier = Modifier.height(8.dp)) - - Text("Role") - FlowRow { - listOf("ROLE_USER", "ROLE_ADMIN", "ROLE_ADMIN_READ").forEach { r -> - Row(verticalAlignment = androidx.compose.ui.Alignment.CenterVertically) { - RadioButton( - selected = role == r, - onClick = { role = r }, - enabled = canEdit && (user == null || user.login != currentUser?.login) - ) - Text(text = r) - } - } - } - - Spacer(modifier = Modifier.height(16.dp)) - - if (canEdit) { - Button( - onClick = { - val newUser = (user ?: User()).copy( - firstName = firstName, - lastName = lastName, - login = if (user == null) login else user.login, - email = email, - birthDate = birthDate, - address = address, - role = role - ) - viewModel.onSaveUser(newUser, password.takeIf { it.isNotBlank() }) - onSave() - }, - modifier = Modifier.fillMaxWidth() - ) { - Text("Save") - } - } - TextButton(onClick = onBack, modifier = Modifier.fillMaxWidth()) { - Text(if (canEdit) "Cancel" else "Close") - } - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/admin/AdminUserListScreen.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/admin/AdminUserListScreen.kt deleted file mode 100644 index ade16d629..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/admin/AdminUserListScreen.kt +++ /dev/null @@ -1,91 +0,0 @@ -package ch.goodone.angularai.android.ui.admin - -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.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Delete -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import ch.goodone.angularai.android.domain.model.User -import ch.goodone.angularai.android.ui.auth.AuthViewModel -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue - -@Composable -fun AdminUserListScreen( - onUserClick: (User) -> Unit, - onAddUser: () -> Unit, - viewModel: AdminViewModel = hiltViewModel(), - authViewModel: AuthViewModel = hiltViewModel() -) { - val state = viewModel.state.value - val currentUser by authViewModel.currentUser.collectAsState() - val canEdit = currentUser?.role == "ROLE_ADMIN" - - Scaffold( - floatingActionButton = { - if (canEdit) { - FloatingActionButton(onClick = onAddUser) { - Icon(Icons.Default.Add, contentDescription = "Add User") - } - } - } - ) { padding -> - Column(modifier = Modifier.padding(padding)) { - if (state.isLoading && state.users.isEmpty()) { - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - CircularProgressIndicator() - } - } else { - LazyColumn(modifier = Modifier.fillMaxSize()) { - items(state.users) { user -> - UserItem( - user = user, - onClick = { onUserClick(user) }, - onDelete = { viewModel.onDeleteUser(user.id!!) }, - canDelete = canEdit && user.login != currentUser?.login - ) - } - } - } - } - } -} - -@Composable -fun UserItem( - user: User, - onClick: () -> Unit, - onDelete: () -> Unit, - canDelete: Boolean -) { - Card( - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - .clickable { onClick() } - ) { - Row( - modifier = Modifier.padding(16.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Column(modifier = Modifier.weight(1f)) { - Text(text = "${user.firstName} ${user.lastName}", style = MaterialTheme.typography.titleMedium) - Text(text = "Login: ${user.login}", style = MaterialTheme.typography.bodySmall) - Text(text = "Role: ${user.role}", style = MaterialTheme.typography.bodySmall) - } - if (canDelete) { - IconButton(onClick = onDelete) { - Icon(Icons.Default.Delete, contentDescription = "Delete User") - } - } - } - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/admin/AdminViewModel.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/admin/AdminViewModel.kt deleted file mode 100644 index 6bacd4bc1..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/admin/AdminViewModel.kt +++ /dev/null @@ -1,72 +0,0 @@ -package ch.goodone.angularai.android.ui.admin - -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import ch.goodone.angularai.android.data.repository.UserRepository -import ch.goodone.angularai.android.domain.model.User -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class AdminViewModel @Inject constructor( - private val repository: UserRepository -) : ViewModel() { - - private val _state = mutableStateOf(AdminUiState()) - val state: State = _state - - init { - loadUsers() - } - - fun loadUsers() { - viewModelScope.launch { - _state.value = _state.value.copy(isLoading = true) - try { - val users = repository.getAllUsers() - _state.value = _state.value.copy(users = users, isLoading = false) - } catch (e: Exception) { - _state.value = _state.value.copy(error = e.message, isLoading = false) - } - } - } - - fun onDeleteUser(id: Long) { - viewModelScope.launch { - try { - repository.deleteUser(id) - loadUsers() - } catch (e: Exception) { - _state.value = _state.value.copy(error = e.message) - } - } - } - - fun onSaveUser(user: User, pass: String?) { - viewModelScope.launch { - try { - if (user.id == null) { - repository.createUser(user, pass ?: "password123") - } else { - repository.updateUser(user) - } - loadUsers() - } catch (e: Exception) { - _state.value = _state.value.copy(error = e.message) - } - } - } - - fun getUser(id: Long): User? { - return _state.value.users.find { it.id == id } - } - - data class AdminUiState( - val users: List = emptyList(), - val isLoading: Boolean = false, - val error: String? = null - ) -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/auth/AuthViewModel.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/auth/AuthViewModel.kt deleted file mode 100644 index 9036a940c..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/auth/AuthViewModel.kt +++ /dev/null @@ -1,79 +0,0 @@ -package ch.goodone.angularai.android.ui.auth - -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import ch.goodone.angularai.android.data.repository.AuthRepository -import ch.goodone.angularai.android.domain.model.User -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class AuthViewModel @Inject constructor( - private val repository: AuthRepository -) : ViewModel() { - - private val _loginState = mutableStateOf(AuthUiState()) - val loginState: State = _loginState - - val currentUser = repository.currentUser - - private val _eventFlow = MutableSharedFlow() - val eventFlow = _eventFlow.asSharedFlow() - - init { - viewModelScope.launch { - repository.init() - } - } - - fun onLogin(login: String, pass: String) { - viewModelScope.launch { - _loginState.value = _loginState.value.copy(isLoading = true) - val result = repository.login(login, pass) - if (result.isSuccess) { - _eventFlow.emit(UiEvent.LoginSuccess(result.getOrThrow())) - } else { - _loginState.value = _loginState.value.copy( - isLoading = false, - error = result.exceptionOrNull()?.message ?: "Unknown error" - ) - } - } - } - - fun onRegister(user: User, pass: String) { - viewModelScope.launch { - _loginState.value = _loginState.value.copy(isLoading = true) - val result = repository.register(user, pass) - if (result.isSuccess) { - _eventFlow.emit(UiEvent.RegisterSuccess) - } else { - _loginState.value = _loginState.value.copy( - isLoading = false, - error = result.exceptionOrNull()?.message ?: "Unknown error" - ) - } - } - } - - fun onLogout() { - viewModelScope.launch { - repository.logout() - } - } - - data class AuthUiState( - val isLoading: Boolean = false, - val error: String? = null - ) - - sealed class UiEvent { - data class LoginSuccess(val user: User) : UiEvent() - object RegisterSuccess : UiEvent() - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/auth/LoginScreen.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/auth/LoginScreen.kt deleted file mode 100644 index d69e11327..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/auth/LoginScreen.kt +++ /dev/null @@ -1,77 +0,0 @@ -package ch.goodone.angularai.android.ui.auth - -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import kotlinx.coroutines.flow.collectLatest - -@Composable -fun LoginScreen( - onLoginSuccess: (ch.goodone.angularai.android.domain.model.User) -> Unit, - onNavigateToRegister: () -> Unit, - viewModel: AuthViewModel = hiltViewModel() -) { - var login by remember { mutableStateOf("") } - var password by remember { mutableStateOf("") } - val state = viewModel.loginState.value - - LaunchedEffect(key1 = true) { - viewModel.eventFlow.collectLatest { event -> - when (event) { - is AuthViewModel.UiEvent.LoginSuccess -> onLoginSuccess(event.user) - else -> Unit - } - } - } - - Column( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - Text(text = "Login", style = MaterialTheme.typography.headlineMedium) - Spacer(modifier = Modifier.height(16.dp)) - - TextField( - value = login, - onValueChange = { login = it }, - label = { Text("Login") }, - modifier = Modifier.fillMaxWidth() - ) - Spacer(modifier = Modifier.height(8.dp)) - - TextField( - value = password, - onValueChange = { password = it }, - label = { Text("Password") }, - visualTransformation = PasswordVisualTransformation(), - modifier = Modifier.fillMaxWidth() - ) - Spacer(modifier = Modifier.height(16.dp)) - - if (state.isLoading) { - CircularProgressIndicator() - } else { - Button( - onClick = { viewModel.onLogin(login, password) }, - modifier = Modifier.fillMaxWidth() - ) { - Text("Login") - } - TextButton(onClick = onNavigateToRegister) { - Text("Don't have an account? Register") - } - } - - state.error?.let { - Text(text = it, color = MaterialTheme.colorScheme.error) - } - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/auth/RegisterScreen.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/auth/RegisterScreen.kt deleted file mode 100644 index 3af26a79b..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/auth/RegisterScreen.kt +++ /dev/null @@ -1,134 +0,0 @@ -package ch.goodone.angularai.android.ui.auth - -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import ch.goodone.angularai.android.domain.model.User -import kotlinx.coroutines.flow.collectLatest - -@Composable -fun RegistrationForm( - firstName: String, onFirstNameChange: (String) -> Unit, - lastName: String, onLastNameChange: (String) -> Unit, - login: String, onLoginChange: (String) -> Unit, - email: String, onEmailChange: (String) -> Unit, - password: String, onPasswordChange: (String) -> Unit, - birthDate: String, onBirthDateChange: (String) -> Unit, - address: String, onAddressChange: (String) -> Unit -) { - TextField(value = firstName, onValueChange = onFirstNameChange, label = { Text("First Name") }, modifier = Modifier.fillMaxWidth()) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = lastName, onValueChange = onLastNameChange, label = { Text("Last Name") }, modifier = Modifier.fillMaxWidth()) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = login, onValueChange = onLoginChange, label = { Text("Login") }, modifier = Modifier.fillMaxWidth()) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = email, onValueChange = onEmailChange, label = { Text("Email") }, modifier = Modifier.fillMaxWidth()) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = password, onValueChange = onPasswordChange, label = { Text("Password") }, visualTransformation = PasswordVisualTransformation(), modifier = Modifier.fillMaxWidth()) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = birthDate, onValueChange = onBirthDateChange, label = { Text("Birth Date (yyyy-MM-dd)") }, modifier = Modifier.fillMaxWidth()) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = address, onValueChange = onAddressChange, label = { Text("Address") }, modifier = Modifier.fillMaxWidth()) -} - -@Composable -fun RegisterScreen( - onRegisterSuccess: () -> Unit, - onNavigateToLogin: () -> Unit, - viewModel: AuthViewModel = hiltViewModel() -) { - val snackbarHostState = remember { SnackbarHostState() } - - var firstName by remember { mutableStateOf("") } - var lastName by remember { mutableStateOf("") } - var login by remember { mutableStateOf("") } - var email by remember { mutableStateOf("") } - var password by remember { mutableStateOf("") } - var birthDate by remember { mutableStateOf("") } - var address by remember { mutableStateOf("") } - - var localError by remember { mutableStateOf(null) } - - val state = viewModel.loginState.value - - LaunchedEffect(key1 = true) { - viewModel.eventFlow.collectLatest { event -> - when (event) { - is AuthViewModel.UiEvent.RegisterSuccess -> { - snackbarHostState.showSnackbar("Registration successful! Please login.") - onRegisterSuccess() - } - else -> Unit - } - } - } - - Scaffold( - snackbarHost = { SnackbarHost(snackbarHostState) } - ) { padding -> - Column( - modifier = Modifier - .fillMaxSize() - .padding(padding) - .padding(16.dp) - .verticalScroll(rememberScrollState()), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - Text(text = "Register", style = MaterialTheme.typography.headlineMedium) - Spacer(modifier = Modifier.height(16.dp)) - - RegistrationForm( - firstName = firstName, onFirstNameChange = { firstName = it }, - lastName = lastName, onLastNameChange = { lastName = it }, - login = login, onLoginChange = { login = it }, - email = email, onEmailChange = { email = it }, - password = password, onPasswordChange = { password = it }, - birthDate = birthDate, onBirthDateChange = { birthDate = it }, - address = address, onAddressChange = { address = it } - ) - - Spacer(modifier = Modifier.height(16.dp)) - - if (state.isLoading) { - CircularProgressIndicator() - } else { - Button( - onClick = { - if (firstName.isBlank() || lastName.isBlank() || login.isBlank() || email.isBlank() || password.isBlank() || birthDate.isBlank()) { - localError = "Please fill in all required fields" - return@Button - } - if (!email.matches(Regex("^[A-Za-z0-9+_.-]+@(.+)$"))) { - localError = "Invalid email format" - return@Button - } - localError = null - val user = User(firstName = firstName, lastName = lastName, login = login, email = email, birthDate = birthDate, address = address) - viewModel.onRegister(user, password) - }, - modifier = Modifier.fillMaxWidth() - ) { - Text("Register") - } - TextButton(onClick = onNavigateToLogin) { - Text("Already have an account? Login") - } - } - - localError?.let { - Text(text = it, color = MaterialTheme.colorScheme.error) - } - - state.error?.let { - Text(text = it, color = MaterialTheme.colorScheme.error) - } - } -}} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/dashboard/DashboardScreen.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/dashboard/DashboardScreen.kt deleted file mode 100644 index db4290b06..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/dashboard/DashboardScreen.kt +++ /dev/null @@ -1,250 +0,0 @@ -package ch.goodone.angularai.android.ui.dashboard - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.PriorityHigh -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel -import ch.goodone.angularai.android.data.remote.dto.DashboardDTO -import ch.goodone.angularai.android.data.remote.dto.SummaryStatsDTO -import androidx.compose.material3.ExperimentalMaterial3Api - -@Composable -fun DashboardScreen( - onNavigateToTasks: () -> Unit, - onNavigateToLogs: () -> Unit, - onNavigateToUsers: () -> Unit, - viewModel: DashboardViewModel = hiltViewModel() -) { - val state = viewModel.state.value - - Box(modifier = Modifier.fillMaxSize()) { - if (state.isLoading) { - CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) - } else if (state.error != null) { - Text(text = state.error, color = MaterialTheme.colorScheme.error, modifier = Modifier.align(Alignment.Center)) - } else { - state.dashboard?.let { dashboard -> - DashboardContent(dashboard, onNavigateToTasks, onNavigateToLogs, onNavigateToUsers) - } - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun RecentActivitySection( - recentActivity: List, - onNavigateToLogs: () -> Unit -) { - ElevatedCard( - modifier = Modifier.fillMaxWidth(), - shape = MaterialTheme.shapes.large, - elevation = CardDefaults.elevatedCardElevation(defaultElevation = 4.dp), - onClick = onNavigateToLogs - ) { - Column(modifier = Modifier.padding(16.dp)) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Text("Recent Activity", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold) - TextButton(onClick = onNavigateToLogs) { - Text("Show All") - } - } - Spacer(modifier = Modifier.height(8.dp)) - recentActivity.forEachIndexed { index, log -> - val bgColor = if (index % 2 != 0) Color(0xFFFAFAFA) else Color.Transparent - Column(modifier = Modifier - .fillMaxWidth() - .background(bgColor) - .padding(vertical = 8.dp, horizontal = 4.dp)) { - Text(text = log.timestamp, fontSize = 12.sp, color = Color.Gray) - Text(text = "${log.login}: ${log.action}", fontWeight = FontWeight.Medium) - } - if (index < recentActivity.size - 1) { - Divider(color = Color(0xFFEEEEEE)) - } - } - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun PriorityTasksSection( - priorityTasks: List, - onNavigateToTasks: () -> Unit -) { - ElevatedCard( - modifier = Modifier.fillMaxWidth(), - shape = MaterialTheme.shapes.large, - elevation = CardDefaults.elevatedCardElevation(defaultElevation = 4.dp), - onClick = onNavigateToTasks - ) { - Column(modifier = Modifier.padding(16.dp)) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Text("Priority Tasks", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold) - TextButton(onClick = onNavigateToTasks) { - Text("Show All") - } - } - Spacer(modifier = Modifier.height(8.dp)) - priorityTasks.forEachIndexed { index, task -> - val bgColor = if (index % 2 != 0) Color(0xFFFAFAFA) else Color.Transparent - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .background(bgColor) - .padding(vertical = 8.dp, horizontal = 4.dp) - ) { - Icon(Icons.Default.PriorityHigh, contentDescription = null, tint = Color(0xFFD32F2F), modifier = Modifier.size(16.dp)) - Spacer(modifier = Modifier.width(8.dp)) - Column { - Text(text = task.title, fontWeight = FontWeight.SemiBold) - Text(text = "Due: ${task.dueDate ?: "N/A"}", fontSize = 12.sp, color = Color.Gray) - } - } - if (index < priorityTasks.size - 1) { - Divider(color = Color(0xFFEEEEEE)) - } - } - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun DashboardContent( - dashboard: DashboardDTO, - onNavigateToTasks: () -> Unit, - onNavigateToLogs: () -> Unit, - onNavigateToUsers: () -> Unit -) { - Column( - modifier = Modifier - .fillMaxSize() - .background(Color(0xFFF8F9FA)) - .padding(16.dp) - .verticalScroll(rememberScrollState()) - ) { - Text("Dashboard", style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold) - Spacer(modifier = Modifier.height(16.dp)) - - // Summary Stats (Horizontal scroll) - SummaryStatsRow(dashboard.summary, onNavigateToTasks, onNavigateToUsers, onNavigateToLogs) - Spacer(modifier = Modifier.height(24.dp)) - - // Task Overview - ElevatedCard( - modifier = Modifier.fillMaxWidth(), - shape = MaterialTheme.shapes.large, - elevation = CardDefaults.elevatedCardElevation(defaultElevation = 4.dp), - onClick = onNavigateToTasks - ) { - Column(modifier = Modifier.padding(16.dp)) { - Text("Task Overview", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold) - Spacer(modifier = Modifier.height(16.dp)) - TaskOverviewChart(dashboard.taskDistribution) - } - } - Spacer(modifier = Modifier.height(16.dp)) - - // Recent Activity - RecentActivitySection(dashboard.recentActivity, onNavigateToLogs) - Spacer(modifier = Modifier.height(16.dp)) - - // Priority Tasks - PriorityTasksSection(dashboard.priorityTasks, onNavigateToTasks) - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun SummaryStatsRow( - summary: SummaryStatsDTO, - onNavigateToTasks: () -> Unit, - onNavigateToUsers: () -> Unit, - onNavigateToLogs: () -> Unit -) { - LazyRow(horizontalArrangement = Arrangement.spacedBy(16.dp)) { - item { StatCard("Open Tasks", summary.openTasks.toString(), "+${summary.openTasksDelta}", Color(0xFF3F51B5), onNavigateToTasks) } - item { StatCard("Active Users", summary.activeUsers.toString(), "+${summary.activeUsersDelta}", Color(0xFF4CAF50), onNavigateToUsers) } - item { StatCard("Completed", summary.completedTasks.toString(), "+${summary.completedTasksDelta}", Color(0xFF2196F3), onNavigateToTasks) } - item { StatCard("Today Logs", summary.todayLogs.toString(), "+${summary.todayLogsDelta}", Color(0xFFFF9800), onNavigateToLogs) } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun StatCard(label: String, value: String, delta: String, accentColor: Color, onClick: () -> Unit) { - ElevatedCard( - modifier = Modifier.width(150.dp), - elevation = CardDefaults.elevatedCardElevation(defaultElevation = 2.dp), - shape = MaterialTheme.shapes.medium, - onClick = onClick - ) { - Column(modifier = Modifier.fillMaxWidth()) { - Box(modifier = Modifier - .fillMaxWidth() - .height(4.dp) - .background(accentColor)) - Column(modifier = Modifier.padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally) { - Text(label, fontSize = 12.sp, fontWeight = FontWeight.Medium, color = Color.Gray) - Text(value, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold) - Text(delta, color = Color(0xFF2E7D32), fontSize = 12.sp, fontWeight = FontWeight.Bold) - } - } - } -} - -@Composable -fun TaskOverviewChart(distribution: ch.goodone.angularai.android.data.remote.dto.TaskStatusDistributionDTO) { - Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceAround, modifier = Modifier.fillMaxWidth()) { - // Simple circle to represent donut - Box(contentAlignment = Alignment.Center, modifier = Modifier - .size(110.dp) - .background(Color(0xFFF1F1F1), CircleShape) - .padding(4.dp) - .background(Color(0xFF3F51B5), CircleShape)) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text(distribution.total.toString(), color = Color.White, fontWeight = FontWeight.ExtraBold, fontSize = 24.sp) - Text("Total", color = Color.White, fontSize = 10.sp, fontWeight = FontWeight.Bold) - } - } - - Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { - LegendItem(Color(0xFF3F51B5), "Open: ${distribution.open}") - LegendItem(Color(0xFF2196F3), "In Progress: ${distribution.inProgress}") - LegendItem(Color(0xFF4CAF50), "Completed: ${distribution.completed}") - } - } -} - -@Composable -fun LegendItem(color: Color, text: String) { - Row(verticalAlignment = Alignment.CenterVertically) { - Box(modifier = Modifier.size(8.dp).background(color, CircleShape)) - Spacer(modifier = Modifier.width(8.dp)) - Text(text, fontSize = 12.sp) - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/dashboard/DashboardViewModel.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/dashboard/DashboardViewModel.kt deleted file mode 100644 index 9d466f9f0..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/dashboard/DashboardViewModel.kt +++ /dev/null @@ -1,42 +0,0 @@ -package ch.goodone.angularai.android.ui.dashboard - -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import ch.goodone.angularai.android.data.remote.dto.DashboardDTO -import ch.goodone.angularai.android.data.repository.DashboardRepository -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import javax.inject.Inject - -data class DashboardState( - val dashboard: DashboardDTO? = null, - val isLoading: Boolean = false, - val error: String? = null -) - -@HiltViewModel -class DashboardViewModel @Inject constructor( - private val repository: DashboardRepository -) : ViewModel() { - - private val _state = mutableStateOf(DashboardState()) - val state: State = _state - - init { - loadDashboard() - } - - fun loadDashboard() { - viewModelScope.launch { - _state.value = _state.value.copy(isLoading = true, error = null) - try { - val dashboard = repository.getDashboard() - _state.value = _state.value.copy(dashboard = dashboard, isLoading = false) - } catch (e: Exception) { - _state.value = _state.value.copy(isLoading = false, error = e.message ?: "Unknown error") - } - } - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/log/LogScreen.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/log/LogScreen.kt deleted file mode 100644 index dfa7f0db5..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/log/LogScreen.kt +++ /dev/null @@ -1,206 +0,0 @@ -package ch.goodone.angularai.android.ui.log - -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.DateRange -import androidx.compose.material.icons.filled.Delete -import androidx.compose.material.icons.filled.FilterList -import androidx.compose.material.icons.filled.Close -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel -import ch.goodone.angularai.android.data.remote.dto.ActionLogDTO - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun LogScreen( - viewModel: LogViewModel = hiltViewModel() -) { - val state = viewModel.state.value - var showDatePicker by remember { mutableStateOf(false) } - var showClearConfirm by remember { mutableStateOf(false) } - var showTypeMenu by remember { mutableStateOf(false) } - - val dateRangePickerState = rememberDateRangePickerState() - - if (showDatePicker) { - DatePickerDialog( - onDismissRequest = { showDatePicker = false }, - confirmButton = { - TextButton(onClick = { - viewModel.onDateRangeChange( - dateRangePickerState.selectedStartDateMillis, - dateRangePickerState.selectedEndDateMillis - ) - showDatePicker = false - }) { - Text("OK") - } - }, - dismissButton = { - TextButton(onClick = { showDatePicker = false }) { - Text("Cancel") - } - } - ) { - DateRangePicker( - state = dateRangePickerState, - modifier = Modifier.height(400.dp) - ) - } - } - - if (showClearConfirm) { - AlertDialog( - onDismissRequest = { showClearConfirm = false }, - title = { Text("Confirm Clear Log") }, - text = { Text("Are you sure you want to delete all log entries? This action cannot be undone.") }, - confirmButton = { - TextButton(onClick = { - viewModel.onClearLogs() - showClearConfirm = false - }, colors = ButtonDefaults.textButtonColors(contentColor = Color.Red)) { - Text("Clear") - } - }, - dismissButton = { - TextButton(onClick = { showClearConfirm = false }) { - Text("Cancel") - } - } - ) - } - - Scaffold( - topBar = { - Column { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(8.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Box { - OutlinedButton(onClick = { showTypeMenu = true }) { - Icon(Icons.Default.FilterList, contentDescription = null) - Spacer(Modifier.width(4.dp)) - Text(state.actionType.replaceFirstChar { it.uppercase() }) - } - DropdownMenu( - expanded = showTypeMenu, - onDismissRequest = { showTypeMenu = false } - ) { - listOf("all", "login", "task", "user admin").forEach { type -> - DropdownMenuItem( - text = { Text(type.replaceFirstChar { it.uppercase() }) }, - onClick = { - viewModel.onActionTypeChange(type) - showTypeMenu = false - } - ) - } - } - } - - Row { - IconButton(onClick = { showDatePicker = true }) { - Icon(Icons.Default.DateRange, contentDescription = "Select Dates", tint = if (state.startDate != null) MaterialTheme.colorScheme.primary else LocalContentColor.current) - } - IconButton(onClick = { viewModel.onClearFilter() }) { - Icon(Icons.Default.Close, contentDescription = "Clear Filters") - } - IconButton(onClick = { showClearConfirm = true }) { - Icon(Icons.Default.Delete, contentDescription = "Clear Logs", tint = Color.Red) - } - } - } - if (state.startDate != null || state.endDate != null) { - Text( - text = "Range: ${state.startDate ?: "..."} - ${state.endDate ?: "..."}", - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp) - ) - } - } - } - ) { padding -> - Box(modifier = Modifier.padding(padding).fillMaxSize()) { - if (state.isLoading && state.logs.isEmpty()) { - CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) - } else if (state.logs.isEmpty()) { - Text("No logs found.", modifier = Modifier.align(Alignment.Center)) - } else { - Column { - LazyColumn(modifier = Modifier.weight(1f)) { - items(state.logs) { log -> - LogItem(log) - Divider() - } - } - - // Paging Controls - Row( - modifier = Modifier.fillMaxWidth().padding(8.dp), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically - ) { - IconButton( - onClick = { viewModel.onPageChange(state.currentPage - 1) }, - enabled = state.currentPage > 0 - ) { - Text("<") - } - Text("Page ${state.currentPage + 1} of ${state.totalPages}") - IconButton( - onClick = { viewModel.onPageChange(state.currentPage + 1) }, - enabled = state.currentPage < state.totalPages - 1 - ) { - Text(">") - } - } - } - } - } - } -} - -@Composable -fun LogItem(log: ActionLogDTO) { - Column(modifier = Modifier.fillMaxWidth().padding(12.dp)) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - text = log.timestamp, - style = MaterialTheme.typography.bodySmall, - color = Color.Gray - ) - Text( - text = log.login, - style = MaterialTheme.typography.bodySmall, - fontWeight = FontWeight.Bold - ) - } - Spacer(modifier = Modifier.height(4.dp)) - Text( - text = log.action, - style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.primary - ) - Text( - text = log.details, - style = MaterialTheme.typography.bodyMedium, - fontSize = 13.sp - ) - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/log/LogViewModel.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/log/LogViewModel.kt deleted file mode 100644 index 9bf62bb45..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/log/LogViewModel.kt +++ /dev/null @@ -1,106 +0,0 @@ -package ch.goodone.angularai.android.ui.log - -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import ch.goodone.angularai.android.data.remote.dto.ActionLogDTO -import ch.goodone.angularai.android.data.repository.LogRepository -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import java.time.Instant -import java.time.ZoneId -import java.time.format.DateTimeFormatter -import javax.inject.Inject - -@HiltViewModel -class LogViewModel @Inject constructor( - private val repository: LogRepository -) : ViewModel() { - - private val _state = mutableStateOf(LogUiState()) - val state: State = _state - - init { - loadLogs() - } - - fun loadLogs() { - viewModelScope.launch { - _state.value = _state.value.copy(isLoading = true) - try { - val response = repository.getLogs( - page = _state.value.currentPage, - size = _state.value.pageSize, - actionType = _state.value.actionType, - startDate = _state.value.startDate, - endDate = _state.value.endDate - ) - _state.value = _state.value.copy( - logs = response.content, - totalElements = response.totalElements, - totalPages = response.totalPages, - isLoading = false - ) - } catch (e: Exception) { - _state.value = _state.value.copy(isLoading = false, error = e.message) - } - } - } - - fun onActionTypeChange(actionType: String) { - _state.value = _state.value.copy(actionType = actionType, currentPage = 0) - loadLogs() - } - - fun onDateRangeChange(start: Long?, end: Long?) { - val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") - val startDateStr = start?.let { - Instant.ofEpochMilli(it).atZone(ZoneId.systemDefault()).toLocalDate().format(formatter) - } - val endDateStr = end?.let { - Instant.ofEpochMilli(it).atZone(ZoneId.systemDefault()).toLocalDate().format(formatter) - } - _state.value = _state.value.copy(startDate = startDateStr, endDate = endDateStr, currentPage = 0) - loadLogs() - } - - fun onClearFilter() { - _state.value = _state.value.copy( - actionType = "all", - startDate = null, - endDate = null, - currentPage = 0 - ) - loadLogs() - } - - fun onClearLogs() { - viewModelScope.launch { - try { - repository.clearLogs() - loadLogs() - } catch (e: Exception) { - _state.value = _state.value.copy(error = e.message) - } - } - } - - fun onPageChange(page: Int) { - _state.value = _state.value.copy(currentPage = page) - loadLogs() - } - - data class LogUiState( - val logs: List = emptyList(), - val isLoading: Boolean = false, - val error: String? = null, - val actionType: String = "all", - val startDate: String? = null, - val endDate: String? = null, - val currentPage: Int = 0, - val pageSize: Int = 20, - val totalElements: Long = 0, - val totalPages: Int = 0 - ) -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/profile/ProfileScreen.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/profile/ProfileScreen.kt deleted file mode 100644 index f208ccb7e..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/profile/ProfileScreen.kt +++ /dev/null @@ -1,76 +0,0 @@ -package ch.goodone.angularai.android.ui.profile - -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import ch.goodone.angularai.android.domain.model.User - -@Composable -fun ProfileScreen( - viewModel: ProfileViewModel = hiltViewModel() -) { - val state = viewModel.state.value - var firstName by remember { mutableStateOf("") } - var lastName by remember { mutableStateOf("") } - var email by remember { mutableStateOf("") } - var birthDate by remember { mutableStateOf("") } - var address by remember { mutableStateOf("") } - - LaunchedEffect(state.user) { - state.user?.let { - firstName = it.firstName - lastName = it.lastName - email = it.email - birthDate = it.birthDate - address = it.address - } - } - - Column( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - ) { - Text(text = "My Profile", style = MaterialTheme.typography.headlineMedium) - Spacer(modifier = Modifier.height(16.dp)) - - if (state.isLoading) { - CircularProgressIndicator() - } else { - TextField(value = firstName, onValueChange = { firstName = it }, label = { Text("First Name") }, modifier = Modifier.fillMaxWidth()) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = lastName, onValueChange = { lastName = it }, label = { Text("Last Name") }, modifier = Modifier.fillMaxWidth()) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = email, onValueChange = { email = it }, label = { Text("Email") }, modifier = Modifier.fillMaxWidth()) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = birthDate, onValueChange = { birthDate = it }, label = { Text("Birth Date (yyyy-MM-dd)") }, modifier = Modifier.fillMaxWidth()) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = address, onValueChange = { address = it }, label = { Text("Address") }, modifier = Modifier.fillMaxWidth()) - - Spacer(modifier = Modifier.height(16.dp)) - - Button( - onClick = { - val updatedUser = state.user?.copy( - firstName = firstName, - lastName = lastName, - email = email, - birthDate = birthDate, - address = address - ) - updatedUser?.let { viewModel.onUpdateProfile(it) } - }, - modifier = Modifier.fillMaxWidth() - ) { - Text("Update Profile") - } - } - - state.error?.let { - Text(text = it, color = MaterialTheme.colorScheme.error) - } - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/profile/ProfileViewModel.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/profile/ProfileViewModel.kt deleted file mode 100644 index 6e3cb798b..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/profile/ProfileViewModel.kt +++ /dev/null @@ -1,54 +0,0 @@ -package ch.goodone.angularai.android.ui.profile - -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import ch.goodone.angularai.android.data.repository.UserRepository -import ch.goodone.angularai.android.domain.model.User -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class ProfileViewModel @Inject constructor( - private val repository: UserRepository -) : ViewModel() { - - private val _state = mutableStateOf(ProfileUiState()) - val state: State = _state - - init { - loadProfile() - } - - fun loadProfile() { - viewModelScope.launch { - _state.value = _state.value.copy(isLoading = true) - try { - val user = repository.getCurrentUser() - _state.value = _state.value.copy(user = user, isLoading = false) - } catch (e: Exception) { - _state.value = _state.value.copy(error = e.message, isLoading = false) - } - } - } - - fun onUpdateProfile(user: User) { - viewModelScope.launch { - _state.value = _state.value.copy(isLoading = true) - try { - val updatedUser = repository.updateCurrentUser(user) - _state.value = _state.value.copy(user = updatedUser, isLoading = false) - } catch (e: Exception) { - _state.value = _state.value.copy(error = e.message, isLoading = false) - } - } - } - - data class ProfileUiState( - val user: User? = null, - val isLoading: Boolean = false, - val error: String? = null - ) -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/tasks/TaskEditScreen.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/tasks/TaskEditScreen.kt deleted file mode 100644 index 6db1628df..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/tasks/TaskEditScreen.kt +++ /dev/null @@ -1,216 +0,0 @@ -package ch.goodone.angularai.android.ui.tasks - -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.PressInteraction -import androidx.compose.foundation.layout.* -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.DateRange -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import ch.goodone.angularai.android.domain.model.Task -import ch.goodone.angularai.android.domain.model.TaskStatus -import java.time.Instant -import java.time.LocalDate -import java.time.ZoneId -import java.time.format.DateTimeFormatter - -@Composable -private fun calculateInitialDateMillis(showDatePicker: Boolean, dueDate: String): Long? { - return remember(showDatePicker) { - if (!showDatePicker) return@remember null - - try { - val localDate = if (dueDate.isNotBlank()) { - LocalDate.parse(dueDate, DateTimeFormatter.ISO_LOCAL_DATE) - } else { - LocalDate.now() - } - localDate.atStartOfDay(ZoneId.systemDefault()) - .toInstant() - .toEpochMilli() - } catch (e: Exception) { - LocalDate.now() - .atStartOfDay(ZoneId.systemDefault()) - .toInstant() - .toEpochMilli() - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun TaskEditScreen( - taskId: Long?, - onSave: () -> Unit, - onBack: () -> Unit, - viewModel: TaskViewModel = hiltViewModel() -) { - val state = viewModel.state.value - val task = remember(taskId, state.tasks) { taskId?.let { id -> state.tasks.find { it.id == id } } } - - var title by remember { mutableStateOf("") } - var description by remember { mutableStateOf("") } - var dueDate by remember { mutableStateOf("") } - var priority by remember { mutableStateOf("MEDIUM") } - var status by remember { mutableStateOf(TaskStatus.OPEN) } - - var showDatePicker by remember { mutableStateOf(false) } - val datePickerState = rememberDatePickerState( - initialSelectedDateMillis = calculateInitialDateMillis(showDatePicker, dueDate) - ) - - val isDueDateValid = remember(dueDate) { - dueDate.isBlank() || dueDate.matches(Regex("^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$")) - } - - LaunchedEffect(task) { - task?.let { - title = it.title - description = it.description - dueDate = it.dueDate ?: "" - priority = it.priority - status = it.status - } - } - - Column( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - ) { - Text(text = if (task == null) "Add Task" else "Edit Task", style = MaterialTheme.typography.headlineMedium) - Spacer(modifier = Modifier.height(16.dp)) - - TextField( - value = title, - onValueChange = { title = it }, - label = { Text("Title") }, - modifier = Modifier.fillMaxWidth(), - isError = title.isBlank() - ) - Spacer(modifier = Modifier.height(8.dp)) - TextField(value = description, onValueChange = { description = it }, label = { Text("Description") }, modifier = Modifier.fillMaxWidth()) - Spacer(modifier = Modifier.height(8.dp)) - - if (showDatePicker) { - DatePickerDialog( - onDismissRequest = { showDatePicker = false }, - confirmButton = { - TextButton(onClick = { - datePickerState.selectedDateMillis?.let { millis -> - val date = Instant.ofEpochMilli(millis) - .atZone(ZoneId.systemDefault()) - .toLocalDate() - dueDate = date.format(DateTimeFormatter.ISO_LOCAL_DATE) - } - showDatePicker = false - }) { - Text("OK") - } - }, - dismissButton = { - TextButton(onClick = { showDatePicker = false }) { - Text("Cancel") - } - } - ) { - DatePicker(state = datePickerState) - } - } - - TextField( - value = dueDate, - onValueChange = { }, - label = { Text("Due Date (yyyy-MM-dd)") }, - modifier = Modifier.fillMaxWidth(), - placeholder = { Text("e.g. 2026-01-18") }, - readOnly = true, - interactionSource = remember { MutableInteractionSource() }.also { interactionSource -> - LaunchedEffect(interactionSource) { - interactionSource.interactions.collect { interaction -> - if (interaction is PressInteraction.Release) { - showDatePicker = true - } - } - } - }, - trailingIcon = { - IconButton(onClick = { showDatePicker = true }) { - Icon(Icons.Default.DateRange, contentDescription = "Select Date") - } - }, - isError = !isDueDateValid, - supportingText = { - if (!isDueDateValid) { - Text("Invalid format. Use yyyy-MM-dd") - } - } - ) - Spacer(modifier = Modifier.height(8.dp)) - - if (state.error != null) { - Text( - text = state.error, - color = MaterialTheme.colorScheme.error, - modifier = Modifier.padding(vertical = 8.dp) - ) - } - - Text("Priority") - Row { - listOf("LOW", "MEDIUM", "HIGH", "CRITICAL").forEach { p -> - Row(verticalAlignment = androidx.compose.ui.Alignment.CenterVertically) { - RadioButton(selected = priority == p, onClick = { priority = p }) - Text(text = p) - } - } - } - - Spacer(modifier = Modifier.height(8.dp)) - Text("Status") - Row { - TaskStatus.values().forEach { s -> - Row(verticalAlignment = androidx.compose.ui.Alignment.CenterVertically) { - RadioButton(selected = status == s, onClick = { status = s }) - Text(text = s.name.lowercase().replace("_", " ").split(" ").joinToString(" ") { it.replaceFirstChar { char -> char.uppercase() } }) - } - } - } - - Spacer(modifier = Modifier.height(16.dp)) - - Button( - onClick = { - if (title.isBlank()) return@Button - val newTask = Task( - id = task?.id, - title = title, - description = description, - dueDate = dueDate.trim().takeIf { it.isNotBlank() }, - priority = priority, - status = status, - position = task?.position ?: 0 - ) - viewModel.onSaveTask(newTask, onSave) - }, - modifier = Modifier.fillMaxWidth(), - enabled = !state.isLoading && title.isNotBlank() && isDueDateValid - ) { - if (state.isLoading) { - CircularProgressIndicator( - modifier = Modifier.size(24.dp), - color = MaterialTheme.colorScheme.onPrimary - ) - } else { - Text("Save") - } - } - TextButton(onClick = onBack, modifier = Modifier.fillMaxWidth()) { - Text("Cancel") - } - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/tasks/TaskListScreen.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/tasks/TaskListScreen.kt deleted file mode 100644 index 623802001..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/tasks/TaskListScreen.kt +++ /dev/null @@ -1,208 +0,0 @@ -package ch.goodone.angularai.android.ui.tasks - -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.material.icons.Icons -import androidx.compose.material.icons.filled.* -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import ch.goodone.angularai.android.domain.model.Task -import ch.goodone.angularai.android.domain.model.TaskStatus -import androidx.compose.foundation.background -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.ui.draw.clip -import androidx.compose.ui.text.font.FontWeight - -@Composable -fun TaskListHeader( - statusFilter: TaskStatus?, - onStatusFilterChange: (TaskStatus?) -> Unit, - onResetSorting: () -> Unit -) { - var showFilterMenu by remember { mutableStateOf(false) } - - Row( - modifier = Modifier - .fillMaxWidth() - .padding(8.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Box { - TextButton(onClick = { showFilterMenu = true }) { - Icon(Icons.Default.FilterList, contentDescription = null) - Spacer(Modifier.width(4.dp)) - Text(statusFilter?.name?.lowercase()?.replace("_", " ")?.split(" ")?.joinToString(" ") { it.replaceFirstChar { char -> char.uppercase() } } ?: "All Status") - } - DropdownMenu( - expanded = showFilterMenu, - onDismissRequest = { showFilterMenu = false } - ) { - DropdownMenuItem( - text = { Text("All Status") }, - onClick = { onStatusFilterChange(null); showFilterMenu = false } - ) - TaskStatus.values().forEach { status -> - DropdownMenuItem( - text = { Text(status.name.lowercase().replace("_", " ").split(" ").joinToString(" ") { it.replaceFirstChar { char -> char.uppercase() } }) }, - onClick = { onStatusFilterChange(status); showFilterMenu = false } - ) - } - } - } - - TextButton(onClick = onResetSorting) { - Icon(Icons.Default.Sort, contentDescription = null) - Spacer(Modifier.width(4.dp)) - Text("Reset Sorting") - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun TaskListScreen( - onTaskClick: (Task) -> Unit, - onAddTask: () -> Unit, - viewModel: TaskViewModel = hiltViewModel() -) { - val state = viewModel.state.value - val statusFilter by viewModel.statusFilter.collectAsState() - - Scaffold( - topBar = { - TaskListHeader( - statusFilter = statusFilter, - onStatusFilterChange = { viewModel.onStatusFilterChange(it) }, - onResetSorting = { viewModel.onResetSorting() } - ) - }, - floatingActionButton = { - FloatingActionButton(onClick = onAddTask) { - Icon(Icons.Default.Add, contentDescription = "Add Task") - } - } - ) { padding -> - Column( - modifier = Modifier - .padding(padding) - .fillMaxSize() - ) { - if (state.isLoading && state.tasks.isEmpty()) { - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - CircularProgressIndicator() - } - } else if (state.tasks.isEmpty()) { - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Text(text = "No tasks found.", style = MaterialTheme.typography.bodyLarge) - } - } else { - LazyColumn(modifier = Modifier.fillMaxSize()) { - itemsIndexed(state.tasks) { index, task -> - // Reordering logic simplified for this environment without external libs - // In a real app, use a reorderable list library. - TaskItem( - task = task, - onClick = { onTaskClick(task) }, - onDelete = { viewModel.onDeleteTask(task.id!!) }, - onMoveUp = if (index > 0 && statusFilter == null) { { viewModel.onReorderTasks(index, index - 1) } } else null, - onMoveDown = if (index < state.tasks.size - 1 && statusFilter == null) { { viewModel.onReorderTasks(index, index + 1) } } else null - ) - } - } - } - } - } -} - -@Composable -fun TaskItem( - task: Task, - onClick: () -> Unit, - onDelete: () -> Unit, - onMoveUp: (() -> Unit)? = null, - onMoveDown: (() -> Unit)? = null -) { - Card( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 8.dp, vertical = 4.dp) - .clickable { onClick() } - ) { - Row( - modifier = Modifier.padding(12.dp), - verticalAlignment = Alignment.CenterVertically - ) { - if (onMoveUp != null || onMoveDown != null) { - Column { - IconButton(onClick = onMoveUp ?: {}, enabled = onMoveUp != null, modifier = Modifier.size(24.dp)) { - Icon(Icons.Default.KeyboardArrowUp, contentDescription = "Move Up") - } - IconButton(onClick = onMoveDown ?: {}, enabled = onMoveDown != null, modifier = Modifier.size(24.dp)) { - Icon(Icons.Default.KeyboardArrowDown, contentDescription = "Move Down") - } - } - Spacer(modifier = Modifier.width(8.dp)) - } - - Column(modifier = Modifier.weight(1f)) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text(text = task.title, style = MaterialTheme.typography.titleMedium) - Spacer(modifier = Modifier.width(8.dp)) - StatusChip(status = task.status) - } - Text( - text = "Priority: ${task.priority}", - style = MaterialTheme.typography.bodySmall, - color = when(task.priority) { - "HIGH", "CRITICAL" -> Color.Red - "MEDIUM" -> Color(0xFFFFA500) - else -> Color.Gray - } - ) - task.dueDate?.let { - Text(text = "Due: $it", style = MaterialTheme.typography.bodySmall) - } - } - IconButton(onClick = onDelete) { - Icon(Icons.Default.Delete, contentDescription = "Delete Task") - } - } - } -} - -@Composable -fun StatusChip(status: TaskStatus) { - val backgroundColor = when(status) { - TaskStatus.OPEN -> Color.LightGray - TaskStatus.IN_PROGRESS -> Color(0xFFBBDEFB) - TaskStatus.COMPLETED, TaskStatus.CLOSED -> Color(0xFFC8E6C9) - } - val textColor = when(status) { - TaskStatus.OPEN -> Color.DarkGray - TaskStatus.IN_PROGRESS -> Color(0xFF1976D2) - TaskStatus.COMPLETED, TaskStatus.CLOSED -> Color(0xFF388E3C) - } - - Box( - modifier = Modifier - .clip(RoundedCornerShape(12.dp)) - .background(backgroundColor) - .padding(horizontal = 8.dp, vertical = 2.dp) - ) { - Text( - text = status.name.lowercase().replace("_", " ").split(" ").joinToString(" ") { it.replaceFirstChar { char -> char.uppercase() } }, - style = MaterialTheme.typography.labelSmall, - color = textColor, - fontWeight = FontWeight.Bold - ) - } -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/tasks/TaskViewModel.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/tasks/TaskViewModel.kt deleted file mode 100644 index ee7cba53d..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/tasks/TaskViewModel.kt +++ /dev/null @@ -1,114 +0,0 @@ -package ch.goodone.angularai.android.ui.tasks - -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import ch.goodone.angularai.android.data.repository.TaskRepository -import ch.goodone.angularai.android.domain.model.Task -import ch.goodone.angularai.android.domain.model.TaskStatus -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class TaskViewModel @Inject constructor( - private val repository: TaskRepository -) : ViewModel() { - - private val _statusFilter = MutableStateFlow(null) - val statusFilter: StateFlow = _statusFilter.asStateFlow() - - private val _state = mutableStateOf(TaskUiState()) - val state: State = _state - - init { - combine(repository.tasks, _statusFilter) { tasks, filter -> - val filteredTasks = if (filter == null) tasks else tasks.filter { it.status == filter } - _state.value = _state.value.copy(tasks = filteredTasks) - }.launchIn(viewModelScope) - refresh() - } - - fun onStatusFilterChange(status: TaskStatus?) { - _statusFilter.value = status - } - - fun onReorderTasks(fromIndex: Int, toIndex: Int) { - val currentTasks = _state.value.tasks.toMutableList() - if (fromIndex !in currentTasks.indices || toIndex !in currentTasks.indices) return - - val task = currentTasks.removeAt(fromIndex) - currentTasks.add(toIndex, task) - - // Update local state immediately for smooth UI - _state.value = _state.value.copy(tasks = currentTasks) - - viewModelScope.launch { - repository.reorderTasks(currentTasks.mapNotNull { it.id }) - } - } - - fun onResetSorting() { - viewModelScope.launch { - // Reordering by priority: HIGH, MEDIUM, LOW - val sortedTasks = _state.value.tasks.sortedWith(compareBy { - when (it.priority) { - "CRITICAL" -> 0 - "HIGH" -> 1 - "MEDIUM" -> 2 - "LOW" -> 3 - else -> 4 - } - }) - repository.reorderTasks(sortedTasks.mapNotNull { it.id }) - } - } - - fun refresh() { - viewModelScope.launch { - _state.value = _state.value.copy(isLoading = true) - repository.refreshTasks() - _state.value = _state.value.copy(isLoading = false) - } - } - - fun onDeleteTask(id: Long) { - viewModelScope.launch { - repository.deleteTask(id) - } - } - - fun onSaveTask(task: Task, onSuccess: () -> Unit) { - viewModelScope.launch { - try { - _state.value = _state.value.copy(isLoading = true, error = null) - if (task.id == null) { - repository.createTask(task) - } else { - repository.updateTask(task) - } - onSuccess() - } catch (e: Exception) { - _state.value = _state.value.copy(error = e.message ?: "Unknown error occurred") - } finally { - _state.value = _state.value.copy(isLoading = false) - } - } - } - - fun getTask(id: Long): Task? { - return _state.value.tasks.find { it.id == id } - } - - data class TaskUiState( - val tasks: List = emptyList(), - val isLoading: Boolean = false, - val error: String? = null - ) -} diff --git a/android/app/src/main/java/ch/goodone/angularai/android/ui/theme/Theme.kt b/android/app/src/main/java/ch/goodone/angularai/android/ui/theme/Theme.kt deleted file mode 100644 index 7d2d15268..000000000 --- a/android/app/src/main/java/ch/goodone/angularai/android/ui/theme/Theme.kt +++ /dev/null @@ -1,33 +0,0 @@ -package ch.goodone.angularai.android.ui.theme - -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.lightColorScheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color - -private val DarkColorScheme = darkColorScheme( - primary = Color(0xFF3F51B5), // Indigo - secondary = Color(0xFFFF4081), // Pink - tertiary = Color(0xFF03A9F4) -) - -private val LightColorScheme = lightColorScheme( - primary = Color(0xFF3F51B5), - secondary = Color(0xFFFF4081), - tertiary = Color(0xFF03A9F4) -) - -@Composable -fun AngularAITheme( - darkTheme: Boolean = isSystemInDarkTheme(), - content: @Composable () -> Unit -) { - val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme - - MaterialTheme( - colorScheme = colorScheme, - content = content - ) -} diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 6ffbb3fc2..000000000 --- a/android/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/android/app/src/main/res/drawable/ic_launcher_foreground.xml b/android/app/src/main/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index bfcb09440..000000000 --- a/android/app/src/main/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 6b78462d6..000000000 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 6b78462d6..000000000 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml deleted file mode 100644 index 9b7d69fbd..000000000 --- a/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - AngularAI - diff --git a/android/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml deleted file mode 100644 index c476209a7..000000000 --- a/android/app/src/main/res/values/themes.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/android/app/src/test/java/ch/goodone/angularai/android/DomainModelTest.kt b/android/app/src/test/java/ch/goodone/angularai/android/DomainModelTest.kt deleted file mode 100644 index 231162a8f..000000000 --- a/android/app/src/test/java/ch/goodone/angularai/android/DomainModelTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -package ch.goodone.angularai.android - -import ch.goodone.angularai.android.domain.model.User -import ch.goodone.angularai.android.domain.model.Task -import ch.goodone.angularai.android.domain.model.TaskStatus -import org.junit.Test -import org.junit.Assert.* - -class DomainModelTest { - @Test - fun userModel_shouldStoreData() { - val user = User(id = 1, firstName = "Test", lastName = "User", login = "test", email = "test@example.com", address = "Addr", role = "USER") - assertEquals("Test", user.firstName) - assertEquals("User", user.lastName) - assertEquals("test", user.login) - assertEquals("test@example.com", user.email) - assertEquals("Addr", user.address) - assertEquals("USER", user.role) - } - - @Test - fun taskModel_shouldStoreData() { - val task = Task(id = 1, title = "Test Task", description = "Desc", dueDate = "2024-01-01", priority = "HIGH", status = TaskStatus.OPEN, position = 0) - assertEquals("Test Task", task.title) - assertEquals("Desc", task.description) - assertEquals("2024-01-01", task.dueDate) - assertEquals("HIGH", task.priority) - assertEquals(TaskStatus.OPEN, task.status) - assertEquals(0, task.position) - } - - @Test - fun taskStatus_shouldHaveCorrectValues() { - assertEquals(4, TaskStatus.values().size) - assertTrue(TaskStatus.valueOf("OPEN") == TaskStatus.OPEN) - assertTrue(TaskStatus.valueOf("IN_PROGRESS") == TaskStatus.IN_PROGRESS) - assertTrue(TaskStatus.valueOf("COMPLETED") == TaskStatus.COMPLETED) - assertTrue(TaskStatus.valueOf("CLOSED") == TaskStatus.CLOSED) - } - @Test - fun taskStatus_shouldFormatCorrectly() { - val testCases = mapOf( - TaskStatus.OPEN to "Open", - TaskStatus.IN_PROGRESS to "In Progress", - TaskStatus.COMPLETED to "Completed", - TaskStatus.CLOSED to "Closed" - ) - - testCases.forEach { (status, expected) -> - val formatted = status.name.lowercase().replace("_", " ").split(" ").joinToString(" ") { it.replaceFirstChar { char -> char.uppercase() } } - assertEquals(expected, formatted) - } - } -} diff --git a/android/app/src/test/java/ch/goodone/angularai/android/DtoModelTest.kt b/android/app/src/test/java/ch/goodone/angularai/android/DtoModelTest.kt deleted file mode 100644 index 12ad00dc9..000000000 --- a/android/app/src/test/java/ch/goodone/angularai/android/DtoModelTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -package ch.goodone.angularai.android - -import ch.goodone.angularai.android.data.remote.dto.* -import ch.goodone.angularai.android.data.local.entity.TaskEntity -import org.junit.Test -import org.junit.Assert.* - -class DtoModelTest { - @Test - fun actionLogDTO_shouldStoreData() { - val dto = ActionLogDTO(1L, "2024-01-01", "user", "ACTION", "Details") - assertEquals(1L, dto.id) - assertEquals("2024-01-01", dto.timestamp) - assertEquals("user", dto.login) - assertEquals("ACTION", dto.action) - assertEquals("Details", dto.details) - } - - @Test - fun logResponseDTO_shouldStoreData() { - val log = ActionLogDTO(1L, "2024-01-01", "user", "ACTION", "Details") - val dto = LogResponseDTO(listOf(log), 1L, 1, 10, 0) - assertEquals(1, dto.content.size) - assertEquals(1L, dto.totalElements) - assertEquals(1, dto.totalPages) - assertEquals(10, dto.size) - assertEquals(0, dto.number) - } - - @Test - fun dashboardDTO_shouldStoreData() { - val summary = SummaryStatsDTO(1, 1, 1, 1, 1, 1, 1, 1) - val distribution = TaskStatusDistributionDTO(1, 1, 1, 3) - val dto = DashboardDTO(summary, emptyList(), emptyList(), emptyList(), distribution) - - assertEquals(summary, dto.summary) - assertEquals(distribution, dto.taskDistribution) - assertTrue(dto.priorityTasks.isEmpty()) - assertTrue(dto.recentActivity.isEmpty()) - assertTrue(dto.recentUsers.isEmpty()) - } - - @Test - fun systemInfoDTO_shouldStoreData() { - val dto = SystemInfoDTO("1.0", "1.0", "Dev", "Message") - assertEquals("1.0", dto.backendVersion) - assertEquals("1.0", dto.frontendVersion) - assertEquals("Dev", dto.mode) - assertEquals("Message", dto.landingMessage) - } - - @Test - fun taskDTO_shouldStoreData() { - val dto = TaskDTO(1L, "Title", "Desc", "2024-01-01", "HIGH", "OPEN", 0, "2024-01-01") - assertEquals(1L, dto.id) - assertEquals("Title", dto.title) - assertEquals("Desc", dto.description) - assertEquals("2024-01-01", dto.dueDate) - assertEquals("HIGH", dto.priority) - assertEquals("OPEN", dto.status) - assertEquals(0, dto.position) - assertEquals("2024-01-01", dto.createdAt) - } - - @Test - fun userDTO_shouldStoreData() { - val dto = UserDTO(1L, "First", "Last", "login", "email", "2000-01-01", "Addr", "USER", "pass", "2024-01-01") - assertEquals(1L, dto.id) - assertEquals("First", dto.firstName) - assertEquals("Last", dto.lastName) - assertEquals("login", dto.login) - assertEquals("email", dto.email) - assertEquals("2000-01-01", dto.birthDate) - assertEquals("Addr", dto.address) - assertEquals("USER", dto.role) - assertEquals("pass", dto.password) - assertEquals("2024-01-01", dto.createdAt) - } - - @Test - fun taskEntity_shouldStoreData() { - val entity = TaskEntity(1L, "Title", "Desc", "2024-01-01", "HIGH", "OPEN", 0) - assertEquals(1L, entity.id) - assertEquals("Title", entity.title) - assertEquals("Desc", entity.description) - assertEquals("2024-01-01", entity.dueDate) - assertEquals("HIGH", entity.priority) - assertEquals("OPEN", entity.status) - assertEquals(0, entity.position) - } -} diff --git a/android/app/src/test/java/ch/goodone/angularai/android/RepositoryTest.kt b/android/app/src/test/java/ch/goodone/angularai/android/RepositoryTest.kt deleted file mode 100644 index 0f689f656..000000000 --- a/android/app/src/test/java/ch/goodone/angularai/android/RepositoryTest.kt +++ /dev/null @@ -1,135 +0,0 @@ -package ch.goodone.angularai.android - -import ch.goodone.angularai.android.data.local.TaskDao -import ch.goodone.angularai.android.data.remote.AuthApi -import ch.goodone.angularai.android.data.remote.TaskApi -import ch.goodone.angularai.android.data.remote.UserApi -import ch.goodone.angularai.android.data.remote.dto.TaskDTO -import ch.goodone.angularai.android.data.remote.dto.UserDTO -import ch.goodone.angularai.android.data.repository.AuthRepository -import ch.goodone.angularai.android.data.repository.TaskRepository -import ch.goodone.angularai.android.data.repository.UserRepository -import ch.goodone.angularai.android.domain.model.Task -import ch.goodone.angularai.android.domain.model.TaskStatus -import ch.goodone.angularai.android.domain.model.User -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.runBlocking -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.mockito.Mock -import org.mockito.MockitoAnnotations -import org.mockito.kotlin.* -import retrofit2.Response - -class RepositoryTest { - - @Mock - lateinit var taskApi: TaskApi - - @Mock - lateinit var taskDao: TaskDao - - @Mock - lateinit var authApi: AuthApi - - @Mock - lateinit var userApi: UserApi - - @Mock - lateinit var dataStore: androidx.datastore.core.DataStore - - lateinit var taskRepository: TaskRepository - lateinit var userRepository: UserRepository - lateinit var authRepository: AuthRepository - - @Before - fun setUp() { - MockitoAnnotations.openMocks(this) - taskRepository = TaskRepository(taskApi, taskDao) - userRepository = UserRepository(userApi) - authRepository = AuthRepository(authApi, userRepository, dataStore) - } - - @Test - fun refreshTasks_shouldFetchFromApiAndSaveToDao() = runBlocking { - val remoteTasks = listOf(TaskDTO(1L, "Title", "Desc", "2024-01-01", "HIGH", "OPEN", 0)) - whenever(taskApi.getTasks()).thenReturn(remoteTasks) - - taskRepository.refreshTasks() - - verify(taskApi).getTasks() - verify(taskDao).clearTasks() - verify(taskDao).insertTasks(any()) - } - - @Test - fun createTask_shouldCallApiAndSaveToDao() = runBlocking { - val task = Task(null, "New Task", "Desc", "2024-01-01", "MEDIUM") - val savedDto = TaskDTO(1L, "New Task", "Desc", "2024-01-01", "MEDIUM", "OPEN", 0) - whenever(taskApi.createTask(any())).thenReturn(savedDto) - - taskRepository.createTask(task) - - verify(taskApi).createTask(any()) - verify(taskDao).insertTasks(any()) - } - - @Test - fun updateTask_shouldCallApiAndSaveToDao() = runBlocking { - val task = Task(1L, "Updated Task", "Desc", "2024-01-01", "MEDIUM", TaskStatus.IN_PROGRESS, 1) - val updatedDto = TaskDTO(1L, "Updated Task", "Desc", "2024-01-01", "MEDIUM", "IN_PROGRESS", 1) - whenever(taskApi.updateTask(eq(1L), any())).thenReturn(updatedDto) - - taskRepository.updateTask(task) - - verify(taskApi).updateTask(eq(1L), any()) - verify(taskDao).insertTasks(any()) - } - - @Test - fun deleteTask_shouldCallApiAndRefresh() = runBlocking { - whenever(taskApi.getTasks()).thenReturn(emptyList()) - - taskRepository.deleteTask(1L) - - verify(taskApi).deleteTask(1L) - verify(taskApi).getTasks() // from refreshTasks - } - - @Test - fun register_shouldCallApiAndReturnResult() = runBlocking { - val user = User(null, "New", "User", "newuser", "new@e.c") - val responseDto = UserDTO(1L, "New", "User", "newuser", "new@e.c", null, null, "ROLE_USER") - whenever(authApi.register(any())).thenReturn(Response.success(responseDto)) - - val result = authRepository.register(user, "password") - - assertTrue(result.isSuccess) - assertEquals(1L, result.getOrNull()?.id) - verify(authApi).register(any()) - } - - @Test - fun getCurrentUser_shouldCallApi() = runBlocking { - val dto = UserDTO(1L, "First", "Last", "login", "email") - whenever(userApi.getCurrentUser()).thenReturn(dto) - - val result = userRepository.getCurrentUser() - - assertEquals("First", result.firstName) - verify(userApi).getCurrentUser() - } - - @Test - fun getAllUsers_shouldCallApi() = runBlocking { - val dtos = listOf(UserDTO(1L, "First", "Last", "login", "email")) - whenever(userApi.getAllUsers()).thenReturn(dtos) - - val result = userRepository.getAllUsers() - - assertEquals(1, result.size) - verify(userApi).getAllUsers() - } -} diff --git a/android/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/android/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker deleted file mode 100644 index 1f0955d45..000000000 --- a/android/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker +++ /dev/null @@ -1 +0,0 @@ -mock-maker-inline diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index a6f20ebf6..000000000 --- a/android/build.gradle +++ /dev/null @@ -1,22 +0,0 @@ -buildscript { - repositories { - google() - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:8.2.2' - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22' - classpath 'com.google.dagger:hilt-android-gradle-plugin:2.50' - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/android/gradle.properties b/android/gradle.properties deleted file mode 100644 index 9650bbc4b..000000000 --- a/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -android.useAndroidX=true -android.enableJetifier=true -kotlin.code.style=official diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 9bbc975c742b298b441bfb90dbc124400a3751b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43705 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 DHx0+F diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index f718ae4c8..000000000 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,8 +0,0 @@ -#Sun Jan 04 18:15:47 CET 2026 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew deleted file mode 100644 index 9383fc1ff..000000000 --- a/android/gradlew +++ /dev/null @@ -1,170 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# 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 -# -# https://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. -# - -############################################################################## -# -# Gradle start up script for POSIX -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for albumfs -while [ -h "$app_path" ] ; do - ls=`ls -ld "$app_path"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null ; then - app_path="$link" - else - app_path=`dirname "$app_path"`/"$link" - fi -done - -APP_HOME=`dirname "$app_path"` -APP_HOME=`cd "$APP_HOME" > /dev/null; pwd` - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MSYS* | MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/bin/java" ] ; then - # Now, organized in the same order as in JAVA_HOME/bin/java - JAVACMD="$JAVA_HOME/bin/java" - else - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now use the pattern with cygpath to convert any paths to Win32 format - # in the argument list. - for arg in "$@" ; do - CHECK=`echo "$arg" | egrep "$OURCYGPATTERN"` - if [ "$CHECK" != "" ] ; then - arg=`cygpath --mixed "$arg"` - fi - set -- "$@" "$arg" - shift - done -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` - -# Collect all arguments for the java command, -# making sure not to use JAVACMD as a variable -# name in the process as it's already used above. -eval set -- $DEFAULT_JVM_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat deleted file mode 100644 index 3e0c8a49d..000000000 --- a/android/gradlew.bat +++ /dev/null @@ -1,95 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:winNT_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:winNT_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1 -shift -goto winNT_args_slurp - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -:win9xME_args_slurp -if "x%1" == "x" goto execute - -set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1 -shift -goto win9xME_args_slurp - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the check of return code instead of exiting. -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/android/local.properties b/android/local.properties deleted file mode 100644 index 402f1b6f6..000000000 --- a/android/local.properties +++ /dev/null @@ -1,8 +0,0 @@ -## This file must *NOT* be checked into Version Control Systems, -# as it contains information specific to your local configuration. -# -# Location of the SDK. This is only used by Gradle. -# For customization when using a Version Control System, please read the -# header note. -#Sun Jan 04 17:26:26 CET 2026 -sdk.dir=C\:\\Users\\sub\\AppData\\Local\\Android\\Sdk diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index 62b7fb8b4..000000000 --- a/android/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = "AngularAI" -include ':app' diff --git a/sonar/.github-code-scanning-export/V1/github-code-scanning-alerts-enriched.json b/deploy/sonar/.github-code-scanning-export/V1/github-code-scanning-alerts-enriched.json similarity index 100% rename from sonar/.github-code-scanning-export/V1/github-code-scanning-alerts-enriched.json rename to deploy/sonar/.github-code-scanning-export/V1/github-code-scanning-alerts-enriched.json diff --git a/sonar/.github-code-scanning-export/V1/github-code-scanning-alerts-for-junie.json b/deploy/sonar/.github-code-scanning-export/V1/github-code-scanning-alerts-for-junie.json similarity index 100% rename from sonar/.github-code-scanning-export/V1/github-code-scanning-alerts-for-junie.json rename to deploy/sonar/.github-code-scanning-export/V1/github-code-scanning-alerts-for-junie.json diff --git a/sonar/.github-code-scanning-export/github-code-scanning-alerts-enriched.json b/deploy/sonar/.github-code-scanning-export/github-code-scanning-alerts-enriched.json similarity index 100% rename from sonar/.github-code-scanning-export/github-code-scanning-alerts-enriched.json rename to deploy/sonar/.github-code-scanning-export/github-code-scanning-alerts-enriched.json diff --git a/sonar/.github-code-scanning-export/github-code-scanning-alerts-for-junie.json b/deploy/sonar/.github-code-scanning-export/github-code-scanning-alerts-for-junie.json similarity index 100% rename from sonar/.github-code-scanning-export/github-code-scanning-alerts-for-junie.json rename to deploy/sonar/.github-code-scanning-export/github-code-scanning-alerts-for-junie.json diff --git a/sonar/.github-code-scanning-export/github-code-scanning-alerts-raw.json b/deploy/sonar/.github-code-scanning-export/github-code-scanning-alerts-raw.json similarity index 100% rename from sonar/.github-code-scanning-export/github-code-scanning-alerts-raw.json rename to deploy/sonar/.github-code-scanning-export/github-code-scanning-alerts-raw.json diff --git a/sonar/.github-code-scanning-export/index.md b/deploy/sonar/.github-code-scanning-export/index.md similarity index 100% rename from sonar/.github-code-scanning-export/index.md rename to deploy/sonar/.github-code-scanning-export/index.md diff --git a/sonar/.sonar-export/V1/sonar-issues-enriched.json b/deploy/sonar/.sonar-export/V1/sonar-issues-enriched.json similarity index 100% rename from sonar/.sonar-export/V1/sonar-issues-enriched.json rename to deploy/sonar/.sonar-export/V1/sonar-issues-enriched.json diff --git a/sonar/.sonar-export/V1/sonar-issues-for-junie.json b/deploy/sonar/.sonar-export/V1/sonar-issues-for-junie.json similarity index 100% rename from sonar/.sonar-export/V1/sonar-issues-for-junie.json rename to deploy/sonar/.sonar-export/V1/sonar-issues-for-junie.json diff --git a/sonar/.sonar-export/V2/sonar-issues-enriched.json b/deploy/sonar/.sonar-export/V2/sonar-issues-enriched.json similarity index 100% rename from sonar/.sonar-export/V2/sonar-issues-enriched.json rename to deploy/sonar/.sonar-export/V2/sonar-issues-enriched.json diff --git a/sonar/.sonar-export/V2/sonar-issues-for-junie.json b/deploy/sonar/.sonar-export/V2/sonar-issues-for-junie.json similarity index 100% rename from sonar/.sonar-export/V2/sonar-issues-for-junie.json rename to deploy/sonar/.sonar-export/V2/sonar-issues-for-junie.json diff --git a/sonar/.sonar-export/V3/sonar-issues-enriched.json b/deploy/sonar/.sonar-export/V3/sonar-issues-enriched.json similarity index 100% rename from sonar/.sonar-export/V3/sonar-issues-enriched.json rename to deploy/sonar/.sonar-export/V3/sonar-issues-enriched.json diff --git a/sonar/.sonar-export/V3/sonar-issues-for-junie.json b/deploy/sonar/.sonar-export/V3/sonar-issues-for-junie.json similarity index 100% rename from sonar/.sonar-export/V3/sonar-issues-for-junie.json rename to deploy/sonar/.sonar-export/V3/sonar-issues-for-junie.json diff --git a/sonar/.sonar-export/sonar-issues-enriched.json b/deploy/sonar/.sonar-export/sonar-issues-enriched.json similarity index 100% rename from sonar/.sonar-export/sonar-issues-enriched.json rename to deploy/sonar/.sonar-export/sonar-issues-enriched.json diff --git a/sonar/.sonar-export/sonar-issues-for-junie.json b/deploy/sonar/.sonar-export/sonar-issues-for-junie.json similarity index 100% rename from sonar/.sonar-export/sonar-issues-for-junie.json rename to deploy/sonar/.sonar-export/sonar-issues-for-junie.json diff --git a/sonar/.sonar-export/sonar-issues-full.json b/deploy/sonar/.sonar-export/sonar-issues-full.json similarity index 100% rename from sonar/.sonar-export/sonar-issues-full.json rename to deploy/sonar/.sonar-export/sonar-issues-full.json diff --git a/sonar/commit-message.md b/deploy/sonar/commit-message.md similarity index 100% rename from sonar/commit-message.md rename to deploy/sonar/commit-message.md diff --git a/sonar/export-github-code-scanning-alerts.ps1 b/deploy/sonar/export-github-code-scanning-alerts.ps1 similarity index 100% rename from sonar/export-github-code-scanning-alerts.ps1 rename to deploy/sonar/export-github-code-scanning-alerts.ps1 diff --git a/sonar/export-sonar-issues.ps1 b/deploy/sonar/export-sonar-issues.ps1 similarity index 100% rename from sonar/export-sonar-issues.ps1 rename to deploy/sonar/export-sonar-issues.ps1 diff --git a/sonar/github-code-scanning-loop.ps1 b/deploy/sonar/github-code-scanning-loop.ps1 similarity index 100% rename from sonar/github-code-scanning-loop.ps1 rename to deploy/sonar/github-code-scanning-loop.ps1 diff --git a/sonar/junie-sonar-issue-fix.md b/deploy/sonar/junie-sonar-issue-fix.md similarity index 100% rename from sonar/junie-sonar-issue-fix.md rename to deploy/sonar/junie-sonar-issue-fix.md diff --git a/sonar/next-batch-info.md b/deploy/sonar/next-batch-info.md similarity index 100% rename from sonar/next-batch-info.md rename to deploy/sonar/next-batch-info.md diff --git a/sonar/sonar-junie-loop.ps1 b/deploy/sonar/sonar-junie-loop.ps1 similarity index 100% rename from sonar/sonar-junie-loop.ps1 rename to deploy/sonar/sonar-junie-loop.ps1 diff --git a/presentation/doc/presentation-workflow.md b/doc/presentation/doc/presentation-workflow.md similarity index 100% rename from presentation/doc/presentation-workflow.md rename to doc/presentation/doc/presentation-workflow.md diff --git a/presentation/inspector.log b/doc/presentation/inspector.log similarity index 100% rename from presentation/inspector.log rename to doc/presentation/inspector.log diff --git a/presentation/inspector_success.log b/doc/presentation/inspector_success.log similarity index 100% rename from presentation/inspector_success.log rename to doc/presentation/inspector_success.log diff --git a/presentation/pom.xml b/doc/presentation/pom.xml similarity index 100% rename from presentation/pom.xml rename to doc/presentation/pom.xml diff --git a/presentation/presentations/Iteration-2/GovernanceFlow.png b/doc/presentation/presentations/Iteration-2/GovernanceFlow.png similarity index 100% rename from presentation/presentations/Iteration-2/GovernanceFlow.png rename to doc/presentation/presentations/Iteration-2/GovernanceFlow.png diff --git a/presentation/presentations/Iteration-2/SoftwareEntwicklungAi-Level2-draft.pdf b/doc/presentation/presentations/Iteration-2/SoftwareEntwicklungAi-Level2-draft.pdf similarity index 100% rename from presentation/presentations/Iteration-2/SoftwareEntwicklungAi-Level2-draft.pdf rename to doc/presentation/presentations/Iteration-2/SoftwareEntwicklungAi-Level2-draft.pdf diff --git a/presentation/presentations/Iteration-2/SoftwareEntwicklungAi-Level2.pptx b/doc/presentation/presentations/Iteration-2/SoftwareEntwicklungAi-Level2.pptx similarity index 100% rename from presentation/presentations/Iteration-2/SoftwareEntwicklungAi-Level2.pptx rename to doc/presentation/presentations/Iteration-2/SoftwareEntwicklungAi-Level2.pptx diff --git a/presentation/presentations/Iteration-2/files/AiRaceFinal.png b/doc/presentation/presentations/Iteration-2/files/AiRaceFinal.png similarity index 100% rename from presentation/presentations/Iteration-2/files/AiRaceFinal.png rename to doc/presentation/presentations/Iteration-2/files/AiRaceFinal.png diff --git a/presentation/presentations/Iteration-2/files/AiRaceStart.png b/doc/presentation/presentations/Iteration-2/files/AiRaceStart.png similarity index 100% rename from presentation/presentations/Iteration-2/files/AiRaceStart.png rename to doc/presentation/presentations/Iteration-2/files/AiRaceStart.png diff --git a/presentation/presentations/Iteration-2/files/ArchitectureBackup.png b/doc/presentation/presentations/Iteration-2/files/ArchitectureBackup.png similarity index 100% rename from presentation/presentations/Iteration-2/files/ArchitectureBackup.png rename to doc/presentation/presentations/Iteration-2/files/ArchitectureBackup.png diff --git a/presentation/presentations/Iteration-2/files/ArchitectureExplainArchitecture.png b/doc/presentation/presentations/Iteration-2/files/ArchitectureExplainArchitecture.png similarity index 100% rename from presentation/presentations/Iteration-2/files/ArchitectureExplainArchitecture.png rename to doc/presentation/presentations/Iteration-2/files/ArchitectureExplainArchitecture.png diff --git a/presentation/presentations/Iteration-2/files/ArchitectureExplainSecurity.png b/doc/presentation/presentations/Iteration-2/files/ArchitectureExplainSecurity.png similarity index 100% rename from presentation/presentations/Iteration-2/files/ArchitectureExplainSecurity.png rename to doc/presentation/presentations/Iteration-2/files/ArchitectureExplainSecurity.png diff --git a/presentation/presentations/Iteration-2/files/AuthSequence.png b/doc/presentation/presentations/Iteration-2/files/AuthSequence.png similarity index 100% rename from presentation/presentations/Iteration-2/files/AuthSequence.png rename to doc/presentation/presentations/Iteration-2/files/AuthSequence.png diff --git a/presentation/presentations/Iteration-2/files/BuildReleasePipeline.png b/doc/presentation/presentations/Iteration-2/files/BuildReleasePipeline.png similarity index 100% rename from presentation/presentations/Iteration-2/files/BuildReleasePipeline.png rename to doc/presentation/presentations/Iteration-2/files/BuildReleasePipeline.png diff --git a/presentation/presentations/Iteration-2/files/DeploymentReview.png b/doc/presentation/presentations/Iteration-2/files/DeploymentReview.png similarity index 100% rename from presentation/presentations/Iteration-2/files/DeploymentReview.png rename to doc/presentation/presentations/Iteration-2/files/DeploymentReview.png diff --git a/presentation/presentations/Iteration-2/files/GithubActions.png b/doc/presentation/presentations/Iteration-2/files/GithubActions.png similarity index 100% rename from presentation/presentations/Iteration-2/files/GithubActions.png rename to doc/presentation/presentations/Iteration-2/files/GithubActions.png diff --git a/presentation/presentations/Iteration-2/files/GovernanceFlow.png b/doc/presentation/presentations/Iteration-2/files/GovernanceFlow.png similarity index 100% rename from presentation/presentations/Iteration-2/files/GovernanceFlow.png rename to doc/presentation/presentations/Iteration-2/files/GovernanceFlow.png diff --git a/presentation/presentations/Iteration-2/files/Graphana.png b/doc/presentation/presentations/Iteration-2/files/Graphana.png similarity index 100% rename from presentation/presentations/Iteration-2/files/Graphana.png rename to doc/presentation/presentations/Iteration-2/files/Graphana.png diff --git a/presentation/presentations/Iteration-2/files/IterationRetro.png b/doc/presentation/presentations/Iteration-2/files/IterationRetro.png similarity index 100% rename from presentation/presentations/Iteration-2/files/IterationRetro.png rename to doc/presentation/presentations/Iteration-2/files/IterationRetro.png diff --git a/presentation/presentations/Iteration-2/files/ObservabilityBackup.png b/doc/presentation/presentations/Iteration-2/files/ObservabilityBackup.png similarity index 100% rename from presentation/presentations/Iteration-2/files/ObservabilityBackup.png rename to doc/presentation/presentations/Iteration-2/files/ObservabilityBackup.png diff --git a/presentation/presentations/Iteration-2/files/QuickAddWithAi.png b/doc/presentation/presentations/Iteration-2/files/QuickAddWithAi.png similarity index 100% rename from presentation/presentations/Iteration-2/files/QuickAddWithAi.png rename to doc/presentation/presentations/Iteration-2/files/QuickAddWithAi.png diff --git a/presentation/presentations/Iteration-2/files/QuickAddWithAi2.png b/doc/presentation/presentations/Iteration-2/files/QuickAddWithAi2.png similarity index 100% rename from presentation/presentations/Iteration-2/files/QuickAddWithAi2.png rename to doc/presentation/presentations/Iteration-2/files/QuickAddWithAi2.png diff --git a/presentation/presentations/Iteration-2/files/RegistrationAfter.png b/doc/presentation/presentations/Iteration-2/files/RegistrationAfter.png similarity index 100% rename from presentation/presentations/Iteration-2/files/RegistrationAfter.png rename to doc/presentation/presentations/Iteration-2/files/RegistrationAfter.png diff --git a/presentation/presentations/Iteration-2/files/RegistrationBefore.png b/doc/presentation/presentations/Iteration-2/files/RegistrationBefore.png similarity index 100% rename from presentation/presentations/Iteration-2/files/RegistrationBefore.png rename to doc/presentation/presentations/Iteration-2/files/RegistrationBefore.png diff --git a/presentation/presentations/Iteration-2/files/ReleaseNotes.png b/doc/presentation/presentations/Iteration-2/files/ReleaseNotes.png similarity index 100% rename from presentation/presentations/Iteration-2/files/ReleaseNotes.png rename to doc/presentation/presentations/Iteration-2/files/ReleaseNotes.png diff --git a/presentation/presentations/Iteration-2/files/SecurityBackup.png b/doc/presentation/presentations/Iteration-2/files/SecurityBackup.png similarity index 100% rename from presentation/presentations/Iteration-2/files/SecurityBackup.png rename to doc/presentation/presentations/Iteration-2/files/SecurityBackup.png diff --git a/presentation/presentations/Iteration-2/files/SecurityThreatModel.png b/doc/presentation/presentations/Iteration-2/files/SecurityThreatModel.png similarity index 100% rename from presentation/presentations/Iteration-2/files/SecurityThreatModel.png rename to doc/presentation/presentations/Iteration-2/files/SecurityThreatModel.png diff --git a/presentation/presentations/Iteration-2/files/TaskExample.png b/doc/presentation/presentations/Iteration-2/files/TaskExample.png similarity index 100% rename from presentation/presentations/Iteration-2/files/TaskExample.png rename to doc/presentation/presentations/Iteration-2/files/TaskExample.png diff --git a/presentation/presentations/Iteration-2/files/TaskManagementAfter.png b/doc/presentation/presentations/Iteration-2/files/TaskManagementAfter.png similarity index 100% rename from presentation/presentations/Iteration-2/files/TaskManagementAfter.png rename to doc/presentation/presentations/Iteration-2/files/TaskManagementAfter.png diff --git a/presentation/presentations/Iteration-2/files/TaskManagementBefore.png b/doc/presentation/presentations/Iteration-2/files/TaskManagementBefore.png similarity index 100% rename from presentation/presentations/Iteration-2/files/TaskManagementBefore.png rename to doc/presentation/presentations/Iteration-2/files/TaskManagementBefore.png diff --git a/presentation/presentations/Iteration-2/files/TaskSetExample.png b/doc/presentation/presentations/Iteration-2/files/TaskSetExample.png similarity index 100% rename from presentation/presentations/Iteration-2/files/TaskSetExample.png rename to doc/presentation/presentations/Iteration-2/files/TaskSetExample.png diff --git a/presentation/presentations/Iteration-2/files/playwright-demo-screenshots.png b/doc/presentation/presentations/Iteration-2/files/playwright-demo-screenshots.png similarity index 100% rename from presentation/presentations/Iteration-2/files/playwright-demo-screenshots.png rename to doc/presentation/presentations/Iteration-2/files/playwright-demo-screenshots.png diff --git a/presentation/presentations/Iteration-2/files/playwright-test-failure-img.png b/doc/presentation/presentations/Iteration-2/files/playwright-test-failure-img.png similarity index 100% rename from presentation/presentations/Iteration-2/files/playwright-test-failure-img.png rename to doc/presentation/presentations/Iteration-2/files/playwright-test-failure-img.png diff --git a/presentation/presentations/Iteration-2/files/playwright-test-failure-txt.png b/doc/presentation/presentations/Iteration-2/files/playwright-test-failure-txt.png similarity index 100% rename from presentation/presentations/Iteration-2/files/playwright-test-failure-txt.png rename to doc/presentation/presentations/Iteration-2/files/playwright-test-failure-txt.png diff --git a/presentation/presentations/Iteration-2/files/playwright-test-failure.png b/doc/presentation/presentations/Iteration-2/files/playwright-test-failure.png similarity index 100% rename from presentation/presentations/Iteration-2/files/playwright-test-failure.png rename to doc/presentation/presentations/Iteration-2/files/playwright-test-failure.png diff --git a/presentation/presentations/Iteration-2/generate-presentation.py b/doc/presentation/presentations/Iteration-2/generate-presentation.py similarity index 100% rename from presentation/presentations/Iteration-2/generate-presentation.py rename to doc/presentation/presentations/Iteration-2/generate-presentation.py diff --git a/presentation/presentations/Iteration-2/generate-slides.md b/doc/presentation/presentations/Iteration-2/generate-slides.md similarity index 100% rename from presentation/presentations/Iteration-2/generate-slides.md rename to doc/presentation/presentations/Iteration-2/generate-slides.md diff --git a/presentation/presentations/Iteration-2/goodone-reveal-theme.css b/doc/presentation/presentations/Iteration-2/goodone-reveal-theme.css similarity index 100% rename from presentation/presentations/Iteration-2/goodone-reveal-theme.css rename to doc/presentation/presentations/Iteration-2/goodone-reveal-theme.css diff --git a/presentation/presentations/Iteration-2/invitation-2.txt b/doc/presentation/presentations/Iteration-2/invitation-2.txt similarity index 100% rename from presentation/presentations/Iteration-2/invitation-2.txt rename to doc/presentation/presentations/Iteration-2/invitation-2.txt diff --git a/presentation/presentations/Iteration-2/old/presentation-content-proposal.md b/doc/presentation/presentations/Iteration-2/old/presentation-content-proposal.md similarity index 100% rename from presentation/presentations/Iteration-2/old/presentation-content-proposal.md rename to doc/presentation/presentations/Iteration-2/old/presentation-content-proposal.md diff --git a/presentation/presentations/Iteration-2/presentation-2-workbook.md b/doc/presentation/presentations/Iteration-2/presentation-2-workbook.md similarity index 100% rename from presentation/presentations/Iteration-2/presentation-2-workbook.md rename to doc/presentation/presentations/Iteration-2/presentation-2-workbook.md diff --git a/presentation/presentations/Iteration-2/slides-2-backup.md b/doc/presentation/presentations/Iteration-2/slides-2-backup.md similarity index 100% rename from presentation/presentations/Iteration-2/slides-2-backup.md rename to doc/presentation/presentations/Iteration-2/slides-2-backup.md diff --git a/presentation/presentations/Iteration-2/slides-2-executive.md b/doc/presentation/presentations/Iteration-2/slides-2-executive.md similarity index 100% rename from presentation/presentations/Iteration-2/slides-2-executive.md rename to doc/presentation/presentations/Iteration-2/slides-2-executive.md diff --git a/presentation/presentations/Iteration-2/slides-2-improvements.md b/doc/presentation/presentations/Iteration-2/slides-2-improvements.md similarity index 100% rename from presentation/presentations/Iteration-2/slides-2-improvements.md rename to doc/presentation/presentations/Iteration-2/slides-2-improvements.md diff --git a/presentation/presentations/Iteration-2/slides-2-non-tech.md b/doc/presentation/presentations/Iteration-2/slides-2-non-tech.md similarity index 100% rename from presentation/presentations/Iteration-2/slides-2-non-tech.md rename to doc/presentation/presentations/Iteration-2/slides-2-non-tech.md diff --git a/presentation/presentations/Iteration-2/slides-2-task.md b/doc/presentation/presentations/Iteration-2/slides-2-task.md similarity index 100% rename from presentation/presentations/Iteration-2/slides-2-task.md rename to doc/presentation/presentations/Iteration-2/slides-2-task.md diff --git a/presentation/presentations/Iteration-2/slides-2-test.md b/doc/presentation/presentations/Iteration-2/slides-2-test.md similarity index 100% rename from presentation/presentations/Iteration-2/slides-2-test.md rename to doc/presentation/presentations/Iteration-2/slides-2-test.md diff --git a/presentation/presentations/Iteration-2/slides-2.html b/doc/presentation/presentations/Iteration-2/slides-2.html similarity index 100% rename from presentation/presentations/Iteration-2/slides-2.html rename to doc/presentation/presentations/Iteration-2/slides-2.html diff --git a/presentation/presentations/Iteration-2/slides-2.md b/doc/presentation/presentations/Iteration-2/slides-2.md similarity index 100% rename from presentation/presentations/Iteration-2/slides-2.md rename to doc/presentation/presentations/Iteration-2/slides-2.md diff --git a/presentation/presentations/Iteration-2/template-2-junie.md b/doc/presentation/presentations/Iteration-2/template-2-junie.md similarity index 100% rename from presentation/presentations/Iteration-2/template-2-junie.md rename to doc/presentation/presentations/Iteration-2/template-2-junie.md diff --git a/presentation/presentations/Iteration-2/template.pptx b/doc/presentation/presentations/Iteration-2/template.pptx similarity index 100% rename from presentation/presentations/Iteration-2/template.pptx rename to doc/presentation/presentations/Iteration-2/template.pptx diff --git a/presentation/presentations/iteration-1/SoftwareEntwicklungAI.pptx b/doc/presentation/presentations/iteration-1/SoftwareEntwicklungAI.pptx similarity index 100% rename from presentation/presentations/iteration-1/SoftwareEntwicklungAI.pptx rename to doc/presentation/presentations/iteration-1/SoftwareEntwicklungAI.pptx diff --git a/presentation/presentations/iteration-1/auto_agenda.lua b/doc/presentation/presentations/iteration-1/auto_agenda.lua similarity index 100% rename from presentation/presentations/iteration-1/auto_agenda.lua rename to doc/presentation/presentations/iteration-1/auto_agenda.lua diff --git a/presentation/presentations/iteration-1/files/architecture_overivew.yaml b/doc/presentation/presentations/iteration-1/files/architecture_overivew.yaml similarity index 100% rename from presentation/presentations/iteration-1/files/architecture_overivew.yaml rename to doc/presentation/presentations/iteration-1/files/architecture_overivew.yaml diff --git a/presentation/presentations/iteration-1/files/generated/architecture_overview.png b/doc/presentation/presentations/iteration-1/files/generated/architecture_overview.png similarity index 100% rename from presentation/presentations/iteration-1/files/generated/architecture_overview.png rename to doc/presentation/presentations/iteration-1/files/generated/architecture_overview.png diff --git a/presentation/presentations/iteration-1/files/generated/architecture_overview_orig.png b/doc/presentation/presentations/iteration-1/files/generated/architecture_overview_orig.png similarity index 100% rename from presentation/presentations/iteration-1/files/generated/architecture_overview_orig.png rename to doc/presentation/presentations/iteration-1/files/generated/architecture_overview_orig.png diff --git a/presentation/presentations/iteration-1/files/generated/er_diagram.png b/doc/presentation/presentations/iteration-1/files/generated/er_diagram.png similarity index 100% rename from presentation/presentations/iteration-1/files/generated/er_diagram.png rename to doc/presentation/presentations/iteration-1/files/generated/er_diagram.png diff --git a/presentation/presentations/iteration-1/files/generated/erd.png b/doc/presentation/presentations/iteration-1/files/generated/erd.png similarity index 100% rename from presentation/presentations/iteration-1/files/generated/erd.png rename to doc/presentation/presentations/iteration-1/files/generated/erd.png diff --git a/presentation/presentations/iteration-1/files/generated/local_dev_setup.png b/doc/presentation/presentations/iteration-1/files/generated/local_dev_setup.png similarity index 100% rename from presentation/presentations/iteration-1/files/generated/local_dev_setup.png rename to doc/presentation/presentations/iteration-1/files/generated/local_dev_setup.png diff --git a/presentation/presentations/iteration-1/files/generated/local_dev_setup_ai_dev_edu.png b/doc/presentation/presentations/iteration-1/files/generated/local_dev_setup_ai_dev_edu.png similarity index 100% rename from presentation/presentations/iteration-1/files/generated/local_dev_setup_ai_dev_edu.png rename to doc/presentation/presentations/iteration-1/files/generated/local_dev_setup_ai_dev_edu.png diff --git a/presentation/presentations/iteration-1/files/images/AiRace.png b/doc/presentation/presentations/iteration-1/files/images/AiRace.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/AiRace.png rename to doc/presentation/presentations/iteration-1/files/images/AiRace.png diff --git a/presentation/presentations/iteration-1/files/images/AiRaceFinal.png b/doc/presentation/presentations/iteration-1/files/images/AiRaceFinal.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/AiRaceFinal.png rename to doc/presentation/presentations/iteration-1/files/images/AiRaceFinal.png diff --git a/presentation/presentations/iteration-1/files/images/AndroidApp.png b/doc/presentation/presentations/iteration-1/files/images/AndroidApp.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/AndroidApp.png rename to doc/presentation/presentations/iteration-1/files/images/AndroidApp.png diff --git a/presentation/presentations/iteration-1/files/images/AndroidTaskMenu.png b/doc/presentation/presentations/iteration-1/files/images/AndroidTaskMenu.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/AndroidTaskMenu.png rename to doc/presentation/presentations/iteration-1/files/images/AndroidTaskMenu.png diff --git a/presentation/presentations/iteration-1/files/images/ChatGpt.png b/doc/presentation/presentations/iteration-1/files/images/ChatGpt.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/ChatGpt.png rename to doc/presentation/presentations/iteration-1/files/images/ChatGpt.png diff --git a/presentation/presentations/iteration-1/files/images/CypressTests.png b/doc/presentation/presentations/iteration-1/files/images/CypressTests.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/CypressTests.png rename to doc/presentation/presentations/iteration-1/files/images/CypressTests.png diff --git a/presentation/presentations/iteration-1/files/images/DashboardBefore.png b/doc/presentation/presentations/iteration-1/files/images/DashboardBefore.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/DashboardBefore.png rename to doc/presentation/presentations/iteration-1/files/images/DashboardBefore.png diff --git a/presentation/presentations/iteration-1/files/images/DashboardImplementation.png b/doc/presentation/presentations/iteration-1/files/images/DashboardImplementation.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/DashboardImplementation.png rename to doc/presentation/presentations/iteration-1/files/images/DashboardImplementation.png diff --git a/presentation/presentations/iteration-1/files/images/DashboardProposal.png b/doc/presentation/presentations/iteration-1/files/images/DashboardProposal.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/DashboardProposal.png rename to doc/presentation/presentations/iteration-1/files/images/DashboardProposal.png diff --git a/presentation/presentations/iteration-1/files/images/GoodOne2020_Starting.png b/doc/presentation/presentations/iteration-1/files/images/GoodOne2020_Starting.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/GoodOne2020_Starting.png rename to doc/presentation/presentations/iteration-1/files/images/GoodOne2020_Starting.png diff --git a/presentation/presentations/iteration-1/files/images/GoodOne2020_Users.png b/doc/presentation/presentations/iteration-1/files/images/GoodOne2020_Users.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/GoodOne2020_Users.png rename to doc/presentation/presentations/iteration-1/files/images/GoodOne2020_Users.png diff --git a/presentation/presentations/iteration-1/files/images/IdeIntegration.png b/doc/presentation/presentations/iteration-1/files/images/IdeIntegration.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/IdeIntegration.png rename to doc/presentation/presentations/iteration-1/files/images/IdeIntegration.png diff --git a/presentation/presentations/iteration-1/files/images/IntellJ.png b/doc/presentation/presentations/iteration-1/files/images/IntellJ.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/IntellJ.png rename to doc/presentation/presentations/iteration-1/files/images/IntellJ.png diff --git a/presentation/presentations/iteration-1/files/images/SonarSummary.png b/doc/presentation/presentations/iteration-1/files/images/SonarSummary.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/SonarSummary.png rename to doc/presentation/presentations/iteration-1/files/images/SonarSummary.png diff --git a/presentation/presentations/iteration-1/files/images/TaskManagementBefore.png b/doc/presentation/presentations/iteration-1/files/images/TaskManagementBefore.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/TaskManagementBefore.png rename to doc/presentation/presentations/iteration-1/files/images/TaskManagementBefore.png diff --git a/presentation/presentations/iteration-1/files/images/TaskManagementProposal.png b/doc/presentation/presentations/iteration-1/files/images/TaskManagementProposal.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/TaskManagementProposal.png rename to doc/presentation/presentations/iteration-1/files/images/TaskManagementProposal.png diff --git a/presentation/presentations/iteration-1/files/images/android_task_menu.png b/doc/presentation/presentations/iteration-1/files/images/android_task_menu.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/android_task_menu.png rename to doc/presentation/presentations/iteration-1/files/images/android_task_menu.png diff --git a/presentation/presentations/iteration-1/files/images/angular_task_menu.png b/doc/presentation/presentations/iteration-1/files/images/angular_task_menu.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/angular_task_menu.png rename to doc/presentation/presentations/iteration-1/files/images/angular_task_menu.png diff --git a/presentation/presentations/iteration-1/files/images/angular_task_menu2.png b/doc/presentation/presentations/iteration-1/files/images/angular_task_menu2.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/angular_task_menu2.png rename to doc/presentation/presentations/iteration-1/files/images/angular_task_menu2.png diff --git a/presentation/presentations/iteration-1/files/images/title_slide_background_no_text_v2.png b/doc/presentation/presentations/iteration-1/files/images/title_slide_background_no_text_v2.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/title_slide_background_no_text_v2.png rename to doc/presentation/presentations/iteration-1/files/images/title_slide_background_no_text_v2.png diff --git a/presentation/presentations/iteration-1/files/images/title_slide_goodone_ai_dech.png b/doc/presentation/presentations/iteration-1/files/images/title_slide_goodone_ai_dech.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/title_slide_goodone_ai_dech.png rename to doc/presentation/presentations/iteration-1/files/images/title_slide_goodone_ai_dech.png diff --git a/presentation/presentations/iteration-1/files/images/title_slide_goodone_v3.png b/doc/presentation/presentations/iteration-1/files/images/title_slide_goodone_v3.png similarity index 100% rename from presentation/presentations/iteration-1/files/images/title_slide_goodone_v3.png rename to doc/presentation/presentations/iteration-1/files/images/title_slide_goodone_v3.png diff --git a/presentation/presentations/iteration-1/files/input/architecture_overview.drawio b/doc/presentation/presentations/iteration-1/files/input/architecture_overview.drawio similarity index 100% rename from presentation/presentations/iteration-1/files/input/architecture_overview.drawio rename to doc/presentation/presentations/iteration-1/files/input/architecture_overview.drawio diff --git a/presentation/presentations/iteration-1/files/input/architecture_overview.old.drawio b/doc/presentation/presentations/iteration-1/files/input/architecture_overview.old.drawio similarity index 100% rename from presentation/presentations/iteration-1/files/input/architecture_overview.old.drawio rename to doc/presentation/presentations/iteration-1/files/input/architecture_overview.old.drawio diff --git a/presentation/presentations/iteration-1/files/input/architecture_overview_clean.drawio b/doc/presentation/presentations/iteration-1/files/input/architecture_overview_clean.drawio similarity index 100% rename from presentation/presentations/iteration-1/files/input/architecture_overview_clean.drawio rename to doc/presentation/presentations/iteration-1/files/input/architecture_overview_clean.drawio diff --git a/presentation/presentations/iteration-1/files/input/architecture_slide_variant.drawio b/doc/presentation/presentations/iteration-1/files/input/architecture_slide_variant.drawio similarity index 100% rename from presentation/presentations/iteration-1/files/input/architecture_slide_variant.drawio rename to doc/presentation/presentations/iteration-1/files/input/architecture_slide_variant.drawio diff --git a/presentation/presentations/iteration-1/files/input/er_diagram.mmd b/doc/presentation/presentations/iteration-1/files/input/er_diagram.mmd similarity index 100% rename from presentation/presentations/iteration-1/files/input/er_diagram.mmd rename to doc/presentation/presentations/iteration-1/files/input/er_diagram.mmd diff --git a/presentation/presentations/iteration-1/files/input/erd.puml b/doc/presentation/presentations/iteration-1/files/input/erd.puml similarity index 100% rename from presentation/presentations/iteration-1/files/input/erd.puml rename to doc/presentation/presentations/iteration-1/files/input/erd.puml diff --git a/presentation/presentations/iteration-1/files/input/local_dev_setup.drawio b/doc/presentation/presentations/iteration-1/files/input/local_dev_setup.drawio similarity index 100% rename from presentation/presentations/iteration-1/files/input/local_dev_setup.drawio rename to doc/presentation/presentations/iteration-1/files/input/local_dev_setup.drawio diff --git a/presentation/presentations/iteration-1/files/input/local_dev_setup_ai_dev_edu.drawio b/doc/presentation/presentations/iteration-1/files/input/local_dev_setup_ai_dev_edu.drawio similarity index 100% rename from presentation/presentations/iteration-1/files/input/local_dev_setup_ai_dev_edu.drawio rename to doc/presentation/presentations/iteration-1/files/input/local_dev_setup_ai_dev_edu.drawio diff --git a/presentation/presentations/iteration-1/generate_presentation.py b/doc/presentation/presentations/iteration-1/generate_presentation.py similarity index 100% rename from presentation/presentations/iteration-1/generate_presentation.py rename to doc/presentation/presentations/iteration-1/generate_presentation.py diff --git a/presentation/presentations/iteration-1/generated/SoftwareEntwicklungAI_Python.pptx b/doc/presentation/presentations/iteration-1/generated/SoftwareEntwicklungAI_Python.pptx similarity index 100% rename from presentation/presentations/iteration-1/generated/SoftwareEntwicklungAI_Python.pptx rename to doc/presentation/presentations/iteration-1/generated/SoftwareEntwicklungAI_Python.pptx diff --git a/presentation/presentations/iteration-1/patch_autofit.py b/doc/presentation/presentations/iteration-1/patch_autofit.py similarity index 100% rename from presentation/presentations/iteration-1/patch_autofit.py rename to doc/presentation/presentations/iteration-1/patch_autofit.py diff --git a/presentation/presentations/iteration-1/pptx_generation_plan.md b/doc/presentation/presentations/iteration-1/pptx_generation_plan.md similarity index 100% rename from presentation/presentations/iteration-1/pptx_generation_plan.md rename to doc/presentation/presentations/iteration-1/pptx_generation_plan.md diff --git a/presentation/presentations/iteration-1/reference.pptx b/doc/presentation/presentations/iteration-1/reference.pptx similarity index 100% rename from presentation/presentations/iteration-1/reference.pptx rename to doc/presentation/presentations/iteration-1/reference.pptx diff --git a/presentation/presentations/iteration-1/slides-1.md b/doc/presentation/presentations/iteration-1/slides-1.md similarity index 100% rename from presentation/presentations/iteration-1/slides-1.md rename to doc/presentation/presentations/iteration-1/slides-1.md diff --git a/presentation/presentations/iteration-1/template-company.pptx b/doc/presentation/presentations/iteration-1/template-company.pptx similarity index 100% rename from presentation/presentations/iteration-1/template-company.pptx rename to doc/presentation/presentations/iteration-1/template-company.pptx diff --git a/presentation/presentations/iteration-1/template.pptx b/doc/presentation/presentations/iteration-1/template.pptx similarity index 100% rename from presentation/presentations/iteration-1/template.pptx rename to doc/presentation/presentations/iteration-1/template.pptx diff --git a/presentation/presentations/iteration-3/QandA-3.md b/doc/presentation/presentations/iteration-3/QandA-3.md similarity index 100% rename from presentation/presentations/iteration-3/QandA-3.md rename to doc/presentation/presentations/iteration-3/QandA-3.md diff --git a/presentation/presentations/iteration-3/SoftwareEntwicklungAi-Level3.pptx b/doc/presentation/presentations/iteration-3/SoftwareEntwicklungAi-Level3.pptx similarity index 100% rename from presentation/presentations/iteration-3/SoftwareEntwicklungAi-Level3.pptx rename to doc/presentation/presentations/iteration-3/SoftwareEntwicklungAi-Level3.pptx diff --git a/presentation/presentations/iteration-3/files/AdrIndex.png b/doc/presentation/presentations/iteration-3/files/AdrIndex.png similarity index 100% rename from presentation/presentations/iteration-3/files/AdrIndex.png rename to doc/presentation/presentations/iteration-3/files/AdrIndex.png diff --git a/presentation/presentations/iteration-3/files/AiRaceFinal.png b/doc/presentation/presentations/iteration-3/files/AiRaceFinal.png similarity index 100% rename from presentation/presentations/iteration-3/files/AiRaceFinal.png rename to doc/presentation/presentations/iteration-3/files/AiRaceFinal.png diff --git a/presentation/presentations/iteration-3/files/AiRaceStart.png b/doc/presentation/presentations/iteration-3/files/AiRaceStart.png similarity index 100% rename from presentation/presentations/iteration-3/files/AiRaceStart.png rename to doc/presentation/presentations/iteration-3/files/AiRaceStart.png diff --git a/presentation/presentations/iteration-3/files/ArchitectureBackup.png b/doc/presentation/presentations/iteration-3/files/ArchitectureBackup.png similarity index 100% rename from presentation/presentations/iteration-3/files/ArchitectureBackup.png rename to doc/presentation/presentations/iteration-3/files/ArchitectureBackup.png diff --git a/presentation/presentations/iteration-3/files/ArchitectureExplainArchitecture.png b/doc/presentation/presentations/iteration-3/files/ArchitectureExplainArchitecture.png similarity index 100% rename from presentation/presentations/iteration-3/files/ArchitectureExplainArchitecture.png rename to doc/presentation/presentations/iteration-3/files/ArchitectureExplainArchitecture.png diff --git a/presentation/presentations/iteration-3/files/ArchitectureExplainSecurity.png b/doc/presentation/presentations/iteration-3/files/ArchitectureExplainSecurity.png similarity index 100% rename from presentation/presentations/iteration-3/files/ArchitectureExplainSecurity.png rename to doc/presentation/presentations/iteration-3/files/ArchitectureExplainSecurity.png diff --git a/presentation/presentations/iteration-3/files/AuthSequence.png b/doc/presentation/presentations/iteration-3/files/AuthSequence.png similarity index 100% rename from presentation/presentations/iteration-3/files/AuthSequence.png rename to doc/presentation/presentations/iteration-3/files/AuthSequence.png diff --git a/presentation/presentations/iteration-3/files/BuildReleasePipeline.png b/doc/presentation/presentations/iteration-3/files/BuildReleasePipeline.png similarity index 100% rename from presentation/presentations/iteration-3/files/BuildReleasePipeline.png rename to doc/presentation/presentations/iteration-3/files/BuildReleasePipeline.png diff --git a/presentation/presentations/iteration-3/files/DeploymentReview.png b/doc/presentation/presentations/iteration-3/files/DeploymentReview.png similarity index 100% rename from presentation/presentations/iteration-3/files/DeploymentReview.png rename to doc/presentation/presentations/iteration-3/files/DeploymentReview.png diff --git a/presentation/presentations/iteration-3/files/GithubActions.png b/doc/presentation/presentations/iteration-3/files/GithubActions.png similarity index 100% rename from presentation/presentations/iteration-3/files/GithubActions.png rename to doc/presentation/presentations/iteration-3/files/GithubActions.png diff --git a/presentation/presentations/iteration-3/files/GovernanceFlow.png b/doc/presentation/presentations/iteration-3/files/GovernanceFlow.png similarity index 100% rename from presentation/presentations/iteration-3/files/GovernanceFlow.png rename to doc/presentation/presentations/iteration-3/files/GovernanceFlow.png diff --git a/presentation/presentations/iteration-3/files/Graphana.png b/doc/presentation/presentations/iteration-3/files/Graphana.png similarity index 100% rename from presentation/presentations/iteration-3/files/Graphana.png rename to doc/presentation/presentations/iteration-3/files/Graphana.png diff --git a/presentation/presentations/iteration-3/files/IterationRetro.png b/doc/presentation/presentations/iteration-3/files/IterationRetro.png similarity index 100% rename from presentation/presentations/iteration-3/files/IterationRetro.png rename to doc/presentation/presentations/iteration-3/files/IterationRetro.png diff --git a/presentation/presentations/iteration-3/files/ObservabilityBackup.png b/doc/presentation/presentations/iteration-3/files/ObservabilityBackup.png similarity index 100% rename from presentation/presentations/iteration-3/files/ObservabilityBackup.png rename to doc/presentation/presentations/iteration-3/files/ObservabilityBackup.png diff --git a/presentation/presentations/iteration-3/files/QuickAddWithAi.png b/doc/presentation/presentations/iteration-3/files/QuickAddWithAi.png similarity index 100% rename from presentation/presentations/iteration-3/files/QuickAddWithAi.png rename to doc/presentation/presentations/iteration-3/files/QuickAddWithAi.png diff --git a/presentation/presentations/iteration-3/files/QuickAddWithAi2.png b/doc/presentation/presentations/iteration-3/files/QuickAddWithAi2.png similarity index 100% rename from presentation/presentations/iteration-3/files/QuickAddWithAi2.png rename to doc/presentation/presentations/iteration-3/files/QuickAddWithAi2.png diff --git a/presentation/presentations/iteration-3/files/RegistrationAfter.png b/doc/presentation/presentations/iteration-3/files/RegistrationAfter.png similarity index 100% rename from presentation/presentations/iteration-3/files/RegistrationAfter.png rename to doc/presentation/presentations/iteration-3/files/RegistrationAfter.png diff --git a/presentation/presentations/iteration-3/files/RegistrationBefore.png b/doc/presentation/presentations/iteration-3/files/RegistrationBefore.png similarity index 100% rename from presentation/presentations/iteration-3/files/RegistrationBefore.png rename to doc/presentation/presentations/iteration-3/files/RegistrationBefore.png diff --git a/presentation/presentations/iteration-3/files/ReleaseNotes.png b/doc/presentation/presentations/iteration-3/files/ReleaseNotes.png similarity index 100% rename from presentation/presentations/iteration-3/files/ReleaseNotes.png rename to doc/presentation/presentations/iteration-3/files/ReleaseNotes.png diff --git a/presentation/presentations/iteration-3/files/SecurityBackup.png b/doc/presentation/presentations/iteration-3/files/SecurityBackup.png similarity index 100% rename from presentation/presentations/iteration-3/files/SecurityBackup.png rename to doc/presentation/presentations/iteration-3/files/SecurityBackup.png diff --git a/presentation/presentations/iteration-3/files/SecurityThreatModel.png b/doc/presentation/presentations/iteration-3/files/SecurityThreatModel.png similarity index 100% rename from presentation/presentations/iteration-3/files/SecurityThreatModel.png rename to doc/presentation/presentations/iteration-3/files/SecurityThreatModel.png diff --git a/presentation/presentations/iteration-3/files/TaskExample.png b/doc/presentation/presentations/iteration-3/files/TaskExample.png similarity index 100% rename from presentation/presentations/iteration-3/files/TaskExample.png rename to doc/presentation/presentations/iteration-3/files/TaskExample.png diff --git a/presentation/presentations/iteration-3/files/TaskManagementAfter.png b/doc/presentation/presentations/iteration-3/files/TaskManagementAfter.png similarity index 100% rename from presentation/presentations/iteration-3/files/TaskManagementAfter.png rename to doc/presentation/presentations/iteration-3/files/TaskManagementAfter.png diff --git a/presentation/presentations/iteration-3/files/TaskManagementBefore.png b/doc/presentation/presentations/iteration-3/files/TaskManagementBefore.png similarity index 100% rename from presentation/presentations/iteration-3/files/TaskManagementBefore.png rename to doc/presentation/presentations/iteration-3/files/TaskManagementBefore.png diff --git a/presentation/presentations/iteration-3/files/TaskSetExample.png b/doc/presentation/presentations/iteration-3/files/TaskSetExample.png similarity index 100% rename from presentation/presentations/iteration-3/files/TaskSetExample.png rename to doc/presentation/presentations/iteration-3/files/TaskSetExample.png diff --git a/presentation/presentations/iteration-3/files/playwright-demo-screenshots.png b/doc/presentation/presentations/iteration-3/files/playwright-demo-screenshots.png similarity index 100% rename from presentation/presentations/iteration-3/files/playwright-demo-screenshots.png rename to doc/presentation/presentations/iteration-3/files/playwright-demo-screenshots.png diff --git a/presentation/presentations/iteration-3/files/playwright-test-failure-img.png b/doc/presentation/presentations/iteration-3/files/playwright-test-failure-img.png similarity index 100% rename from presentation/presentations/iteration-3/files/playwright-test-failure-img.png rename to doc/presentation/presentations/iteration-3/files/playwright-test-failure-img.png diff --git a/presentation/presentations/iteration-3/files/playwright-test-failure-txt.png b/doc/presentation/presentations/iteration-3/files/playwright-test-failure-txt.png similarity index 100% rename from presentation/presentations/iteration-3/files/playwright-test-failure-txt.png rename to doc/presentation/presentations/iteration-3/files/playwright-test-failure-txt.png diff --git a/presentation/presentations/iteration-3/files/playwright-test-failure.png b/doc/presentation/presentations/iteration-3/files/playwright-test-failure.png similarity index 100% rename from presentation/presentations/iteration-3/files/playwright-test-failure.png rename to doc/presentation/presentations/iteration-3/files/playwright-test-failure.png diff --git a/presentation/presentations/iteration-3/generate-slides.md b/doc/presentation/presentations/iteration-3/generate-slides.md similarity index 100% rename from presentation/presentations/iteration-3/generate-slides.md rename to doc/presentation/presentations/iteration-3/generate-slides.md diff --git a/presentation/presentations/iteration-3/invitation-3.md b/doc/presentation/presentations/iteration-3/invitation-3.md similarity index 100% rename from presentation/presentations/iteration-3/invitation-3.md rename to doc/presentation/presentations/iteration-3/invitation-3.md diff --git a/presentation/presentations/iteration-3/slides-3.md b/doc/presentation/presentations/iteration-3/slides-3.md similarity index 100% rename from presentation/presentations/iteration-3/slides-3.md rename to doc/presentation/presentations/iteration-3/slides-3.md diff --git a/presentation/presentations/pandoc/pandoc_authoring_checklist.md b/doc/presentation/presentations/pandoc/pandoc_authoring_checklist.md similarity index 100% rename from presentation/presentations/pandoc/pandoc_authoring_checklist.md rename to doc/presentation/presentations/pandoc/pandoc_authoring_checklist.md diff --git a/presentation/presentations/pandoc/reference_template_guidelines.md b/doc/presentation/presentations/pandoc/reference_template_guidelines.md similarity index 100% rename from presentation/presentations/pandoc/reference_template_guidelines.md rename to doc/presentation/presentations/pandoc/reference_template_guidelines.md diff --git a/presentation/src/main/java/ch/goodone/angularai/presentation/CompanyStyleAligner.java b/doc/presentation/src/main/java/ch/goodone/angularai/presentation/CompanyStyleAligner.java similarity index 100% rename from presentation/src/main/java/ch/goodone/angularai/presentation/CompanyStyleAligner.java rename to doc/presentation/src/main/java/ch/goodone/angularai/presentation/CompanyStyleAligner.java diff --git a/presentation/src/main/java/ch/goodone/angularai/presentation/DetailedInspector.java b/doc/presentation/src/main/java/ch/goodone/angularai/presentation/DetailedInspector.java similarity index 100% rename from presentation/src/main/java/ch/goodone/angularai/presentation/DetailedInspector.java rename to doc/presentation/src/main/java/ch/goodone/angularai/presentation/DetailedInspector.java diff --git a/presentation/src/main/java/ch/goodone/angularai/presentation/FinalResultFixer.java b/doc/presentation/src/main/java/ch/goodone/angularai/presentation/FinalResultFixer.java similarity index 100% rename from presentation/src/main/java/ch/goodone/angularai/presentation/FinalResultFixer.java rename to doc/presentation/src/main/java/ch/goodone/angularai/presentation/FinalResultFixer.java diff --git a/presentation/src/main/java/ch/goodone/angularai/presentation/FinalTemplateFixer.java b/doc/presentation/src/main/java/ch/goodone/angularai/presentation/FinalTemplateFixer.java similarity index 100% rename from presentation/src/main/java/ch/goodone/angularai/presentation/FinalTemplateFixer.java rename to doc/presentation/src/main/java/ch/goodone/angularai/presentation/FinalTemplateFixer.java diff --git a/presentation/src/main/java/ch/goodone/angularai/presentation/LayoutInspector.java b/doc/presentation/src/main/java/ch/goodone/angularai/presentation/LayoutInspector.java similarity index 100% rename from presentation/src/main/java/ch/goodone/angularai/presentation/LayoutInspector.java rename to doc/presentation/src/main/java/ch/goodone/angularai/presentation/LayoutInspector.java diff --git a/presentation/src/main/java/ch/goodone/angularai/presentation/TemplateCreator.java b/doc/presentation/src/main/java/ch/goodone/angularai/presentation/TemplateCreator.java similarity index 100% rename from presentation/src/main/java/ch/goodone/angularai/presentation/TemplateCreator.java rename to doc/presentation/src/main/java/ch/goodone/angularai/presentation/TemplateCreator.java diff --git a/presentation/src/main/java/ch/goodone/angularai/presentation/TemplateFixerV2.java b/doc/presentation/src/main/java/ch/goodone/angularai/presentation/TemplateFixerV2.java similarity index 100% rename from presentation/src/main/java/ch/goodone/angularai/presentation/TemplateFixerV2.java rename to doc/presentation/src/main/java/ch/goodone/angularai/presentation/TemplateFixerV2.java