From e57932f39d347f6ba3555a79a5c559ab95c37dbd Mon Sep 17 00:00:00 2001
From: Xera <ddosmukhamedovali@gmail.com>
Date: Tue, 5 Nov 2024 18:22:33 +0100
Subject: [PATCH] Add finishTask() and getFinishedTasks()

Added whole finished tasks collection functional. Now you can click tasks to finish them
---
 .../commonMain/kotlin/todo/model/TodoItem.kt  |   9 -
 site/src/jsMain/kotlin/todo/data/ApiCalls.kt  |  45 +++-
 site/src/jsMain/kotlin/todo/pages/Index.kt    | 200 ++++++++++++++----
 site/src/jvmMain/kotlin/todo/api/Tasks.kt     |  26 +++
 site/src/jvmMain/kotlin/todo/data/MongoDb.kt  |   2 +
 .../jvmMain/kotlin/todo/data/MongoDbImp.kt    |  24 ++-
 6 files changed, 248 insertions(+), 58 deletions(-)
 delete mode 100644 site/src/commonMain/kotlin/todo/model/TodoItem.kt

diff --git a/site/src/commonMain/kotlin/todo/model/TodoItem.kt b/site/src/commonMain/kotlin/todo/model/TodoItem.kt
deleted file mode 100644
index 1f49cf2..0000000
--- a/site/src/commonMain/kotlin/todo/model/TodoItem.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package todo.model
-
-import kotlinx.serialization.Serializable
-
-@Serializable
-class TodoItem(
-    val id: String,
-    val text: String,
-)
\ No newline at end of file
diff --git a/site/src/jsMain/kotlin/todo/data/ApiCalls.kt b/site/src/jsMain/kotlin/todo/data/ApiCalls.kt
index 8d727fe..d2c851c 100644
--- a/site/src/jsMain/kotlin/todo/data/ApiCalls.kt
+++ b/site/src/jsMain/kotlin/todo/data/ApiCalls.kt
@@ -8,14 +8,6 @@ import kotlinx.serialization.encodeToString
 import kotlinx.serialization.json.Json
 import todo.model.ApiListResponse
 
-
-suspend fun addTask(task: Task): String {
-    return window.api.tryPost(
-        apiPath = "addtask",
-        body = Json.encodeToString(task).encodeToByteArray()
-    )?.decodeToString().toString()
-}
-
 suspend fun getAllTasks(
     onSuccess: (ApiListResponse) -> Unit,
     onError: (Exception) -> Unit
@@ -36,9 +28,46 @@ suspend fun getAllTasks(
     }
 }
 
+suspend fun getFinishedTasks(
+    onSuccess: (ApiListResponse) -> Unit,
+    onError: (Exception) -> Unit
+) {
+    try {
+        val result = window.api.tryGet(
+            apiPath = "getfinishedtasks"
+        )?.decodeToString()
+        if (result != null) {
+            onSuccess(Json.decodeFromString(result))
+        } else {
+            onError(Exception("Something went wrong"))
+        }
+    }catch (e: Exception){
+        console.log("It's here")
+        println(e)
+        onError(e)
+    }
+}
+
+suspend fun addTask(task: Task): String {
+    return window.api.tryPost(
+        apiPath = "addtask",
+        body = Json.encodeToString(task).encodeToByteArray()
+    )?.decodeToString().toString()
+}
+
+
 suspend fun deleteTask(task: Task): String {
     return window.api.tryPost(
         apiPath = "deletetask",
         body = Json.encodeToString(task).encodeToByteArray()
     )?.decodeToString().toString()
 }
+
+suspend fun finishTask(task: Task): String {
+    return window.api.tryPost(
+        apiPath = "finishtask",
+        body = Json.encodeToString(task).encodeToByteArray()
+    )?.decodeToString().toString()
+}
+
+
diff --git a/site/src/jsMain/kotlin/todo/pages/Index.kt b/site/src/jsMain/kotlin/todo/pages/Index.kt
index fdcab7f..bdfa246 100644
--- a/site/src/jsMain/kotlin/todo/pages/Index.kt
+++ b/site/src/jsMain/kotlin/todo/pages/Index.kt
@@ -40,26 +40,25 @@ import org.jetbrains.compose.web.dom.Span
 import org.jetbrains.compose.web.dom.Text
 import org.w3c.dom.HTMLInputElement
 import todo.components.widgets.HeaderLayout
-import todo.data.addTask
-import todo.data.deleteTask
-import todo.data.getAllTasks
+import todo.data.*
 import todo.model.ApiListResponse
 import todo.model.Task
-import todo.model.TodoItem
 
 
 // ====== STYLES ======
 
 val HeaderStyle = CssStyle.base {
     Modifier
-        .backgroundColor(if (colorMode.isLight) Colors.Grey else Colors.MediumPurple)
+        .backgroundColor(if (colorMode.isLight) Colors.Azure else Colors.MediumPurple)
         .whiteSpace(WhiteSpace.PreWrap)
         .padding(20.px)
-        .fontSize(80.px)
+        .fontSize(60.px)
         .fontWeight(FontWeight.Bold)
         .fillMaxWidth()
-        .margin(bottom = 50.px)
-
+        .padding(24.px)
+        .margin(right = 50.px)
+        .outline(width = 0.px)
+        .border(width = 0.px)
 }
 
 val ButtonStyle = CssStyle {
@@ -68,6 +67,8 @@ val ButtonStyle = CssStyle {
             .backgroundColor(if (colorMode.isLight) Colors.White else Colors.Black)
             .color(if (colorMode.isLight) Colors.Black else Colors.White)
             .padding(10.px)
+            .borderRadius(50.px)
+            .border(0.px)
     }
     hover {
         Modifier
@@ -77,13 +78,15 @@ val ButtonStyle = CssStyle {
 
 val TaskStyle = CssStyle.base {
     Modifier
-        .cursor(Cursor.Pointer)
-        .padding(20.px)
+        .padding(15.px)
         .background(Colors.WhiteSmoke)
-        .fontSize(36.px)
+        .fontSize(24.px)
         .fillMaxWidth()
         .margin(5.px)
         .maxWidth(800.px)
+        .borderRadius(500.px)
+        .backgroundColor(if (colorMode.isLight) Colors.WhiteSmoke else Colors.DarkMagenta)
+        .color(if (colorMode.isLight) Colors.Black else Colors.White)
 }
 
 
@@ -91,15 +94,12 @@ val TaskStyle = CssStyle.base {
 
 @Composable
 fun HomePage() {
-    val context = rememberPageContext()
     val scope = rememberCoroutineScope()
-    var id by remember { mutableStateOf("") }
-    var loadingCount by remember { mutableStateOf(1) } // How many API requests are occurring at the same time
-    val todos = remember { mutableStateListOf<TodoItem>() }
-    val coroutineScope = rememberCoroutineScope()
+
     var colorMode by ColorMode.currentState
 
-    var response by remember { mutableStateOf<ApiListResponse>(ApiListResponse.Loading) }
+    var myTasksResponse by remember { mutableStateOf<ApiListResponse>(ApiListResponse.Loading) }
+    var finishedTasksResponse by remember { mutableStateOf<ApiListResponse>(ApiListResponse.Loading) }
 
 
 
@@ -110,12 +110,24 @@ fun HomePage() {
         getAllTasks(
             onSuccess = {
                 if (it is ApiListResponse.Success) {
-                    response = it
+                    myTasksResponse = it
+                }
+            },
+            onError = {
+                println("Error")
+                myTasksResponse = ApiListResponse.Error(it.message.toString())
+            }
+        )
+
+        getFinishedTasks(
+            onSuccess = {
+                if (it is ApiListResponse.Success) {
+                    finishedTasksResponse = it
                 }
             },
             onError = {
                 println("Error")
-                response = ApiListResponse.Error(it.message.toString())
+                finishedTasksResponse = ApiListResponse.Error(it.message.toString())
             }
         )
     }
@@ -126,19 +138,18 @@ fun HomePage() {
         verticalArrangement = Arrangement.Top,
     ) {
 
-        HeaderLayout(context = context) {}
+        //HeaderLayout(context = context) {}
 
 
 
-// ========  H E A D E R  ========
-        /*Header(
-
-            HeaderStyle.toModifier().toAttrs()
+        // ========  H E A D E R  ========
+        Box(
+            modifier = HeaderStyle.toModifier()
+                .margin(bottom = 25.px)
         ) {
             Row {
                 Span(
                     HeaderStyle.toModifier()
-                        //.justifySelf(JustifySelf.Left)
                         .toAttrs(),
                 ) {
                     Text("To do list")
@@ -150,11 +161,11 @@ fun HomePage() {
                     Text(value = "Change theme")
                 }
             }
-        }*/
-        // ===== Input form =====
-
+        }
+        // ===============================
 
 
+        // =======  INPUT  FORM  =======
         Row(
             verticalAlignment = Alignment.CenterVertically,
             horizontalArrangement = Arrangement.Center
@@ -173,6 +184,7 @@ fun HomePage() {
                     .outline(width = 0.px)
                     .border(width = 0.px)
                     .fontStyle(FontStyle.Italic)
+
                     .toAttrs {
                         attr("placeholder", "Enter a todo")
                     }
@@ -187,7 +199,20 @@ fun HomePage() {
                                 Task(
                                     task = todo
                                 )
+
+                            )
+                            getAllTasks(
+                                onSuccess = {
+                                    if (it is ApiListResponse.Success) {
+                                        myTasksResponse = it
+                                    }
+                                },
+                                onError = {
+                                    println("Error")
+                                    myTasksResponse = ApiListResponse.Error(it.message.toString())
+                                }
                             )
+                            (document.getElementById("todoInput") as HTMLInputElement).value = ""
                         }
                     }
                     .height(54.px)
@@ -196,21 +221,106 @@ fun HomePage() {
                     .cursor(Cursor.Pointer)
                     .toAttrs()
             ) {
-
-
-                    SpanText(
+                SpanText(
                         modifier = Modifier
                             .fontSize(18.px)
                             .fontWeight(FontWeight.Medium)
                             .color(Colors.Black),
                         text = "Create"
                     )
+                }
+            }
+        // =============================
 
+        // =======  TASKS  COLUMN  =======
+        Column(
+            modifier = Modifier
+                .margin(40.px)
+                .fillMaxWidth(),
+            verticalArrangement = Arrangement.Top,
+            horizontalAlignment = Alignment.CenterHorizontally
+        ) {
+            when (myTasksResponse) {
+                is ApiListResponse.Success -> {
+                    (myTasksResponse as ApiListResponse.Success).data.forEach { task ->
 
+                        // ===== Task Row =====
+                        Row(
+                            verticalAlignment = Alignment.CenterVertically,
+                            horizontalArrangement = Arrangement.Center,
+                            modifier = TaskStyle.toModifier()
+                        ) {
+                            SpanText(
+                                text = task.task,
+                                modifier = TaskStyle.toModifier()
+                                    .cursor(Cursor.Pointer)
+                                    .onClick { scope.launch {
+                                        finishTask(task)
+                                        getFinishedTasks(
+                                            onSuccess = {
+                                                if (it is ApiListResponse.Success) {
+                                                    finishedTasksResponse = it
+                                                }
+                                            },
+                                            onError = {
+                                                println("Error")
+                                                finishedTasksResponse = ApiListResponse.Error(it.message.toString())
+                                            }
+                                        )
+                                        getAllTasks(
+                                            onSuccess = {
+                                                if (it is ApiListResponse.Success) {
+                                                    myTasksResponse = it
+                                                }
+                                            },
+                                            onError = {
+                                                println("Error")
+                                                myTasksResponse = ApiListResponse.Error(it.message.toString())
+                                            }
+                                        )
+                                    } }
+                            )
 
+                            // ===== Delete Button =====
+                            Button(ButtonStyle.toModifier()
+                                .onClick {
+                                    scope.launch {
+                                        deleteTask(task)
+                                        getAllTasks(
+                                            onSuccess = {
+                                                if (it is ApiListResponse.Success) {
+                                                    myTasksResponse = it
+                                                }
+                                            },
+                                            onError = {
+                                                println("Error")
+                                                myTasksResponse = ApiListResponse.Error(it.message.toString())
+                                            }
+                                        )
+                                    }
+                                }
+                                .toAttrs()
+                            ) {
+                                Text(value = "Delete")
+                            }
+                        }
+                    }
+                }
+                else -> {
+                    console.log("Nothing to display")
                 }
+
             }
+        }
 
+        SpanText(text = "Finished tasks",
+            modifier = Modifier
+                .fontSize(36.px)
+                .color(Colors.Grey)
+                )
+
+
+        // ======= FINISHED  TASKS  COLUMN  =======
         Column(
             modifier = Modifier
                 .margin(40.px)
@@ -218,9 +328,11 @@ fun HomePage() {
             verticalArrangement = Arrangement.Top,
             horizontalAlignment = Alignment.CenterHorizontally
         ) {
-            when (response) {
+            when (finishedTasksResponse) {
                 is ApiListResponse.Success -> {
-                    (response as ApiListResponse.Success).data.forEach { task ->
+                    (finishedTasksResponse as ApiListResponse.Success).data.forEach { task ->
+
+                        // ===== Finished Task Row =====
                         Row(
                             verticalAlignment = Alignment.CenterVertically,
                             horizontalArrangement = Arrangement.Center,
@@ -229,12 +341,25 @@ fun HomePage() {
                             SpanText(
                                 text = task.task,
                                 modifier = TaskStyle.toModifier()
-                            )
+                                )
+
+                            // ===== Delete Button =====
                             Button(ButtonStyle.toModifier()
+
                                 .onClick {
                                     scope.launch {
                                         deleteTask(task)
-
+                                        getFinishedTasks(
+                                            onSuccess = {
+                                                if (it is ApiListResponse.Success) {
+                                                    finishedTasksResponse = it
+                                                }
+                                            },
+                                            onError = {
+                                                println("Error")
+                                                finishedTasksResponse = ApiListResponse.Error(it.message.toString())
+                                            }
+                                        )
                                     }
                                 }
                                 .toAttrs()
@@ -244,7 +369,6 @@ fun HomePage() {
                         }
                     }
                 }
-
                 else -> {
                     console.log("Nothing to display")
                 }
@@ -252,7 +376,9 @@ fun HomePage() {
             }
         }
 
-        }
+
+
+    }
         Spacer()
 
 
diff --git a/site/src/jvmMain/kotlin/todo/api/Tasks.kt b/site/src/jvmMain/kotlin/todo/api/Tasks.kt
index e716c75..39f17bb 100644
--- a/site/src/jvmMain/kotlin/todo/api/Tasks.kt
+++ b/site/src/jvmMain/kotlin/todo/api/Tasks.kt
@@ -35,6 +35,17 @@ suspend fun getAllTasks(context: ApiContext) {
     }
 }
 
+@Api(routeOverride = "getfinishedtasks")
+suspend fun getFinishedTasks(context: ApiContext) {
+    try {
+        val myTasks = context.data.getValue<MongoDbImp>().getFinishedTasks()
+        context.res.setBodyText(Json.encodeToString(ApiListResponse.Success(data = myTasks)))
+    }catch (e: Exception){
+        context.res.setBodyText(Json.encodeToString(ApiListResponse.Error(message = e.message.toString())))
+    }
+}
+
+
 @Api(routeOverride = "deletetask")
 suspend fun deleteTask(context: ApiContext) {
     try {
@@ -45,3 +56,18 @@ suspend fun deleteTask(context: ApiContext) {
         context.res.setBodyText(Json.encodeToString(e))
     }
 }
+
+@Api(routeOverride = "finishtask")
+suspend fun finishTask(context: ApiContext) {
+    try {
+        val task = context.req.body?.decodeToString()?.let { Json.decodeFromString<Task>(it) }
+        val responseBody = Json.encodeToString(task?.let { context.data.getValue<MongoDbImp>().finishTask(it) })
+        context.res.setBodyText(responseBody)
+    } catch (e: Exception) {
+        context.res.setBodyText(Json.encodeToString(e))
+    }
+}
+
+
+
+
diff --git a/site/src/jvmMain/kotlin/todo/data/MongoDb.kt b/site/src/jvmMain/kotlin/todo/data/MongoDb.kt
index 4b0c27e..e5b2747 100644
--- a/site/src/jvmMain/kotlin/todo/data/MongoDb.kt
+++ b/site/src/jvmMain/kotlin/todo/data/MongoDb.kt
@@ -4,6 +4,8 @@ import todo.model.Task
 
 interface MongoDb {
     suspend fun getAllTasks(): List<Task>
+    suspend fun getFinishedTasks(): List<Task>
     suspend fun addTask(task: Task): Boolean
     suspend fun deleteTask(task: Task): Boolean
+    suspend fun finishTask(task: Task): Boolean
 }
\ No newline at end of file
diff --git a/site/src/jvmMain/kotlin/todo/data/MongoDbImp.kt b/site/src/jvmMain/kotlin/todo/data/MongoDbImp.kt
index 45ca396..3633fd0 100644
--- a/site/src/jvmMain/kotlin/todo/data/MongoDbImp.kt
+++ b/site/src/jvmMain/kotlin/todo/data/MongoDbImp.kt
@@ -6,7 +6,6 @@ import com.varabyte.kobweb.api.data.add
 import com.varabyte.kobweb.api.init.InitApi
 import com.varabyte.kobweb.api.init.InitApiContext
 import kotlinx.coroutines.flow.toList
-import org.bson.types.ObjectId
 import todo.model.Task
 
 @InitApi
@@ -24,14 +23,17 @@ class MongoDbImp(): MongoDb {
     private val client = MongoClient.create()
     private val database = client.getDatabase("todolist")
     private val tasksCollection = database.getCollection<Task>("tasks")
-
-
+    private val finishedTasksCollection = database.getCollection<Task>("finishedTasks")
 
 
     override suspend fun getAllTasks(): List<Task> {
         return tasksCollection.find().toList()
     }
 
+    override suspend fun getFinishedTasks(): List<Task> {
+        return finishedTasksCollection.find().toList()
+    }
+
     override suspend fun addTask(task: Task): Boolean {
         return tasksCollection.insertOne(task).wasAcknowledged()
     }
@@ -39,6 +41,20 @@ class MongoDbImp(): MongoDb {
     override suspend fun deleteTask(task: Task): Boolean {
         val filter = Filters.eq("_id", task._id)
 
-        return tasksCollection.deleteOne(filter).wasAcknowledged()
+        return (tasksCollection.deleteOne(filter).wasAcknowledged() or finishedTasksCollection.deleteOne(filter)
+            .wasAcknowledged())
+    }
+
+    override suspend fun finishTask(task: Task): Boolean {
+        try {
+
+            val filter = Filters.eq("_id", task._id)
+
+            return (tasksCollection.deleteOne(filter).wasAcknowledged() and finishedTasksCollection.insertOne(task)
+                .wasAcknowledged())
+        } catch (e: Exception) {
+            return false
+        }
+
     }
 }
\ No newline at end of file
-- 
GitLab