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 9bbc975c7..000000000
Binary files a/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
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