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