From 28010f7091edf33b42eac50328cb087a3372047c Mon Sep 17 00:00:00 2001 From: Xera <ddosmukhamedovali@gmail.com> Date: Tue, 5 Nov 2024 00:30:40 +0100 Subject: [PATCH] Added connection to mongoDB and API endpoints, tried adding deleteTask() function, the program does not work now :c --- .idea/uiDesigner.xml | 124 ++++++++++ .../kotlin/todo/model/ApiListResponse.kt | 32 +++ site/src/jsMain/kotlin/todo/AppEntry.kt | 17 -- .../todo/components/widgets/HeaderLayout.kt | 68 ++++++ .../todo/components/widgets/LoadingSpinner.kt | 7 - .../todo/components/widgets/TodoCard.kt | 25 -- .../todo/components/widgets/TodoForm.kt | 37 --- .../todo/components/widgets/TodoStyles.kt | 72 ------ site/src/jsMain/kotlin/todo/data/ApiCalls.kt | 44 ++++ .../jsMain/kotlin/todo/navigation/Screen.kt | 7 + site/src/jsMain/kotlin/todo/pages/Index.kt | 225 +++++++++++------- site/src/jvmMain/kotlin/todo/api/Add.kt | 21 -- site/src/jvmMain/kotlin/todo/api/Id.kt | 17 -- site/src/jvmMain/kotlin/todo/api/List.kt | 19 -- site/src/jvmMain/kotlin/todo/api/Remove.kt | 21 -- site/src/jvmMain/kotlin/todo/api/Tasks.kt | 51 ++++ site/src/jvmMain/kotlin/todo/api/tasks | 0 site/src/jvmMain/kotlin/todo/api/tasks.kt | 10 - site/src/jvmMain/kotlin/todo/data/MongoDb.kt | 1 + ...MongoDbImplementation.kt => MongoDbImp.kt} | 16 +- 20 files changed, 476 insertions(+), 338 deletions(-) create mode 100644 .idea/uiDesigner.xml create mode 100644 site/src/commonMain/kotlin/todo/model/ApiListResponse.kt create mode 100644 site/src/jsMain/kotlin/todo/components/widgets/HeaderLayout.kt delete mode 100644 site/src/jsMain/kotlin/todo/components/widgets/LoadingSpinner.kt delete mode 100644 site/src/jsMain/kotlin/todo/components/widgets/TodoCard.kt delete mode 100644 site/src/jsMain/kotlin/todo/components/widgets/TodoForm.kt delete mode 100644 site/src/jsMain/kotlin/todo/components/widgets/TodoStyles.kt create mode 100644 site/src/jsMain/kotlin/todo/data/ApiCalls.kt create mode 100644 site/src/jsMain/kotlin/todo/navigation/Screen.kt delete mode 100644 site/src/jvmMain/kotlin/todo/api/Add.kt delete mode 100644 site/src/jvmMain/kotlin/todo/api/Id.kt delete mode 100644 site/src/jvmMain/kotlin/todo/api/List.kt delete mode 100644 site/src/jvmMain/kotlin/todo/api/Remove.kt create mode 100644 site/src/jvmMain/kotlin/todo/api/Tasks.kt delete mode 100644 site/src/jvmMain/kotlin/todo/api/tasks delete mode 100644 site/src/jvmMain/kotlin/todo/api/tasks.kt rename site/src/jvmMain/kotlin/todo/data/{MongoDbImplementation.kt => MongoDbImp.kt} (66%) diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="Palette2"> + <group name="Swing"> + <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> + </item> + <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true"> + <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> + <initial-values> + <property name="text" value="Button" /> + </initial-values> + </item> + <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="RadioButton" /> + </initial-values> + </item> + <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="CheckBox" /> + </initial-values> + </item> + <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="Label" /> + </initial-values> + </item> + <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> + <preferred-size width="-1" height="20" /> + </default-constraints> + </item> + <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> + </item> + </group> + </component> +</project> \ No newline at end of file diff --git a/site/src/commonMain/kotlin/todo/model/ApiListResponse.kt b/site/src/commonMain/kotlin/todo/model/ApiListResponse.kt new file mode 100644 index 0000000..82c1a95 --- /dev/null +++ b/site/src/commonMain/kotlin/todo/model/ApiListResponse.kt @@ -0,0 +1,32 @@ +package todo.model + +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonContentPolymorphicSerializer +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.jsonObject + +@Serializable(ListResponseSerializer::class) +sealed class ApiListResponse { + @Serializable + @SerialName("loading") + object Loading: ApiListResponse() + + @Serializable + @SerialName("success") + data class Success(val data: List<Task>) : ApiListResponse() + + @Serializable + @SerialName("error") + data class Error(val message: String): ApiListResponse() +} + + +object ListResponseSerializer: JsonContentPolymorphicSerializer<ApiListResponse>(ApiListResponse::class){ + override fun selectDeserializer(element: JsonElement) = when { + "data" in element.jsonObject -> ApiListResponse.Success.serializer() + "message" in element.jsonObject -> ApiListResponse.Error.serializer() + else -> ApiListResponse.Loading.serializer() + } +} \ No newline at end of file diff --git a/site/src/jsMain/kotlin/todo/AppEntry.kt b/site/src/jsMain/kotlin/todo/AppEntry.kt index c176351..a3d7032 100644 --- a/site/src/jsMain/kotlin/todo/AppEntry.kt +++ b/site/src/jsMain/kotlin/todo/AppEntry.kt @@ -36,23 +36,6 @@ fun initSiteStyles(ctx: InitSilkContext) = ctx.stylesheet.apply { .justifyContent(JustifyContent.Center) .alignItems(AlignItems.Center) } - - /*ctx.theme.palettes.light.background = Colors.White - ctx.theme.palettes.light.color = Colors.Black - ctx.theme.palettes.light.link.default = Colors.Red - ctx.theme.palettes.light.link.visited = Colors.DarkRed - ctx.theme.palettes.light.button.default = Colors.WhiteSmoke - ctx.theme.palettes.light.button.hover = Colors.Gold - - ctx.theme.palettes.dark.background = Colors.Black - ctx.theme.palettes.dark.color = Colors.White - ctx.theme.palettes.dark.link.default = Colors.Purple - ctx.theme.palettes.dark.link.visited = Colors.BlueViolet - ctx.theme.palettes.dark.button.default = Colors.Black - ctx.theme.palettes.dark.button.hover = Colors.Gold*/ - - - } diff --git a/site/src/jsMain/kotlin/todo/components/widgets/HeaderLayout.kt b/site/src/jsMain/kotlin/todo/components/widgets/HeaderLayout.kt new file mode 100644 index 0000000..c9c45e6 --- /dev/null +++ b/site/src/jsMain/kotlin/todo/components/widgets/HeaderLayout.kt @@ -0,0 +1,68 @@ +package todo.components.widgets + +import androidx.compose.runtime.Composable +import androidx.compose.web.events.SyntheticMouseEvent +import todo.navigation.Screen +import com.varabyte.kobweb.compose.css.Cursor +import com.varabyte.kobweb.compose.foundation.layout.Arrangement +import com.varabyte.kobweb.compose.foundation.layout.Box +import com.varabyte.kobweb.compose.foundation.layout.Column +import com.varabyte.kobweb.compose.foundation.layout.Row +import com.varabyte.kobweb.compose.ui.Alignment +import com.varabyte.kobweb.compose.ui.Modifier +import com.varabyte.kobweb.compose.ui.graphics.Colors +import com.varabyte.kobweb.compose.ui.modifiers.* +import com.varabyte.kobweb.core.PageContext +import com.varabyte.kobweb.silk.components.text.SpanText +import kotlinx.serialization.json.JsonNull.content +import org.jetbrains.compose.web.css.px + +@Composable +fun HeaderLayout( + context: PageContext, + content: @Composable () -> Unit +) { + Box( + modifier = Modifier.fillMaxSize() + ) { + Row( + modifier = Modifier + .background(Colors.Navy) + .padding(20.px) + .fillMaxWidth() + .height(150.px), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start + ) { + SpanText( + text = "Home Page", + modifier = Modifier + .color(Colors.White) + .cursor(Cursor.Pointer) + .margin(40.px) + .fontSize(24.px) + .onClick { (context.router.navigateTo(Screen.HomePage.route)) } + ) + SpanText( + text = "Task List", + modifier = Modifier + .color(Colors.White) + .cursor(Cursor.Pointer) + .margin(40.px) + .fontSize(24.px) + .onClick { (context.router.navigateTo(Screen.HomePage.route)) } + ) + + + } + Column( + modifier = Modifier + .fillMaxSize() + .margin(150.px), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + content() + } + } +} \ No newline at end of file diff --git a/site/src/jsMain/kotlin/todo/components/widgets/LoadingSpinner.kt b/site/src/jsMain/kotlin/todo/components/widgets/LoadingSpinner.kt deleted file mode 100644 index 6b6c199..0000000 --- a/site/src/jsMain/kotlin/todo/components/widgets/LoadingSpinner.kt +++ /dev/null @@ -1,7 +0,0 @@ -package todo.components.widgets - -import androidx.compose.runtime.Composable -import org.jetbrains.compose.web.dom.Img - -@Composable -fun LoadingSpinner() = Img("/loader.gif") diff --git a/site/src/jsMain/kotlin/todo/components/widgets/TodoCard.kt b/site/src/jsMain/kotlin/todo/components/widgets/TodoCard.kt deleted file mode 100644 index 36b1029..0000000 --- a/site/src/jsMain/kotlin/todo/components/widgets/TodoCard.kt +++ /dev/null @@ -1,25 +0,0 @@ -package todo.components.widgets - -import androidx.compose.runtime.Composable -import com.varabyte.kobweb.silk.style.toAttrs -import org.jetbrains.compose.web.dom.A - -@Composable -fun TodoCard(onClick: (() -> Unit)? = null, content: @Composable () -> Unit) { - val styles = mutableListOf(TodoStyle, TodoContainerStyle, TodoTextStyle) - if (onClick != null) { - styles.add(TodoClickableStyle) - } - - // Use "A" so the item supports a11y (you can tab on it and press enter to click it) - A(href = "#", attrs = styles.toAttrs { - tabIndex(0) // Make this item tabbable - - onClick { evt -> - evt.preventDefault() // We are using "A" for its a11y side effects but don't want its actual behavior - onClick?.invoke() - } - }) { - content() - } -} diff --git a/site/src/jsMain/kotlin/todo/components/widgets/TodoForm.kt b/site/src/jsMain/kotlin/todo/components/widgets/TodoForm.kt deleted file mode 100644 index 45dee42..0000000 --- a/site/src/jsMain/kotlin/todo/components/widgets/TodoForm.kt +++ /dev/null @@ -1,37 +0,0 @@ -package todo.components.widgets - -import androidx.compose.runtime.* -import com.varabyte.kobweb.silk.style.toAttrs -import org.jetbrains.compose.web.attributes.InputType -import org.jetbrains.compose.web.attributes.name -import org.jetbrains.compose.web.attributes.onSubmit -import org.jetbrains.compose.web.attributes.placeholder -import org.jetbrains.compose.web.dom.Form -import org.jetbrains.compose.web.dom.Input - -@Composable -fun TodoForm(placeholder: String, loading: Boolean, submitTodo: (String) -> Unit) { - if (loading) { - TodoCard { - LoadingSpinner() - } - } else { - var todo by remember { mutableStateOf("") } - Form(attrs = listOf(TodoStyle, TodoContainerStyle).toAttrs { - onSubmit { evt -> - evt.preventDefault() - submitTodo(todo) - } - }) { - Input( - InputType.Text, - attrs = listOf(TodoStyle, TodoTextStyle, TodoInputStyle) - .toAttrs { - placeholder(placeholder) - name("todo") - onChange { todo = it.value } - } - ) - } - } -} \ No newline at end of file diff --git a/site/src/jsMain/kotlin/todo/components/widgets/TodoStyles.kt b/site/src/jsMain/kotlin/todo/components/widgets/TodoStyles.kt deleted file mode 100644 index 5666822..0000000 --- a/site/src/jsMain/kotlin/todo/components/widgets/TodoStyles.kt +++ /dev/null @@ -1,72 +0,0 @@ -package todo.components.widgets - -import com.varabyte.kobweb.compose.css.* -import com.varabyte.kobweb.compose.css.AlignItems -import com.varabyte.kobweb.compose.css.Transition -import com.varabyte.kobweb.compose.ui.Modifier -import com.varabyte.kobweb.compose.ui.graphics.Color -import com.varabyte.kobweb.compose.ui.graphics.Colors -import com.varabyte.kobweb.compose.ui.modifiers.* -import com.varabyte.kobweb.silk.style.CssStyle -import com.varabyte.kobweb.silk.style.base -import com.varabyte.kobweb.silk.style.selectors.hover -import com.varabyte.kobweb.silk.style.selectors.placeholderShown -import org.jetbrains.compose.web.css.* -import todo.BORDER_COLOR - -private val INTERACT_COLOR = Color.rgb(0x00, 0x70, 0xf3) - -/** Common styles for all todo widgets */ -val TodoStyle = CssStyle.base { - Modifier - .width(85.percent) - .height(5.cssRem) - .border(1.px, LineStyle.Solid, BORDER_COLOR) - .borderRadius(10.px) - .transition(Transition.group(listOf("color", "border-color"), 0.15.s, TransitionTimingFunction.Ease)) - .textDecorationLine(TextDecorationLine.None) -} - -/** Styles for the bordered, outer container (the form component has an inner and outer layer) */ -val TodoContainerStyle = CssStyle.base { - Modifier - .margin(0.5.cssRem) - .border(1.px, LineStyle.Solid, BORDER_COLOR) - .display(DisplayStyle.Flex) - .textAlign(TextAlign.Left) - .alignItems(AlignItems.Center) -} - -/** Styles for the text parts of todo widgets */ -val TodoTextStyle = CssStyle.base { - Modifier - .padding(1.5.cssRem) - .fontSize(1.25.cssRem) - // We use "A" tags for accessibility, but we want our colors to come from our container - .color(Color("inherit")) -} - -/** Styles for the input element which handles user input */ -val TodoInputStyle = CssStyle { - base { - Modifier - .fillMaxWidth() - .backgroundColor(Colors.Transparent) - .border(0.px) - } - - placeholderShown { - Modifier.fontStyle(FontStyle.Italic) - } -} - -/** Styles for mouse interaction with todo widgets */ -val TodoClickableStyle = CssStyle { - hover { - Modifier - .color(INTERACT_COLOR) - .cursor(Cursor.Pointer) - .border { color(INTERACT_COLOR) } - .textDecorationLine(TextDecorationLine.LineThrough) - } -} diff --git a/site/src/jsMain/kotlin/todo/data/ApiCalls.kt b/site/src/jsMain/kotlin/todo/data/ApiCalls.kt new file mode 100644 index 0000000..6d09609 --- /dev/null +++ b/site/src/jsMain/kotlin/todo/data/ApiCalls.kt @@ -0,0 +1,44 @@ +package todo.data + +import todo.model.Task +import com.varabyte.kobweb.browser.api +import kotlinx.browser.window +import kotlinx.serialization.decodeFromString +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 +) { + try { + val result = window.api.tryGet( + apiPath = "getalltasks" + )?.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 deleteTask(taskID: String): String { + return window.api.tryPost( + apiPath = "deletetask", + body = Json.encodeToString(taskID).encodeToByteArray() + )?.decodeToString().toString() +}*/ diff --git a/site/src/jsMain/kotlin/todo/navigation/Screen.kt b/site/src/jsMain/kotlin/todo/navigation/Screen.kt new file mode 100644 index 0000000..803583b --- /dev/null +++ b/site/src/jsMain/kotlin/todo/navigation/Screen.kt @@ -0,0 +1,7 @@ +package todo.navigation + + +sealed class Screen(val route: String) { + object HomePage: Screen(route = "/") + object TasksListPage: Screen(route = "/tasks") +} diff --git a/site/src/jsMain/kotlin/todo/pages/Index.kt b/site/src/jsMain/kotlin/todo/pages/Index.kt index 3c16547..2a93ffe 100644 --- a/site/src/jsMain/kotlin/todo/pages/Index.kt +++ b/site/src/jsMain/kotlin/todo/pages/Index.kt @@ -17,6 +17,7 @@ import com.varabyte.kobweb.compose.ui.modifiers.alignItems import com.varabyte.kobweb.compose.ui.modifiers.verticalAlign import com.varabyte.kobweb.compose.ui.toAttrs import com.varabyte.kobweb.core.Page +import com.varabyte.kobweb.core.rememberPageContext import com.varabyte.kobweb.silk.components.navigation.Link import com.varabyte.kobweb.silk.components.text.SpanText import com.varabyte.kobweb.silk.style.CssStyle @@ -24,13 +25,13 @@ import com.varabyte.kobweb.silk.style.base import com.varabyte.kobweb.silk.style.selectors.hover import com.varabyte.kobweb.silk.style.toModifier import com.varabyte.kobweb.silk.theme.colors.ColorMode -import com.varabyte.kobweb.silk.theme.colors.ColorSchemes -import com.varabyte.kobweb.silk.theme.colors.palette.link -import com.varabyte.kobweb.silk.theme.colors.palette.toPalette + +import kotlinx.browser.document import kotlinx.browser.window import kotlinx.coroutines.launch import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json +import org.jetbrains.compose.web.attributes.InputType import org.jetbrains.compose.web.css.* import org.jetbrains.compose.web.css.AlignContent import org.jetbrains.compose.web.css.AlignSelf @@ -38,24 +39,14 @@ import org.jetbrains.compose.web.dom.Footer import org.jetbrains.compose.web.dom.* import org.jetbrains.compose.web.dom.Span import org.jetbrains.compose.web.dom.Text -import todo.components.widgets.LoadingSpinner -import todo.components.widgets.TodoCard -import todo.components.widgets.TodoForm +import org.w3c.dom.HTMLInputElement +import todo.components.widgets.HeaderLayout +import todo.data.addTask +import todo.data.getAllTasks +import todo.model.ApiListResponse +import todo.model.Task import todo.model.TodoItem -private suspend fun loadAndReplaceTodos(id: String, todos: SnapshotStateList<TodoItem>) { - return window.api.get("list?owner=$id").let { listBytes -> - Snapshot.withMutableSnapshot { - todos.clear() - todos.addAll(Json.decodeFromString(listBytes.decodeToString())) - } - } -} - -/*val TitleStyle = CssStyle.base { - Modifier - -}*/ // ====== STYLES ====== @@ -67,6 +58,7 @@ val HeaderStyle = CssStyle.base { .fontSize(80.px) .fontWeight(FontWeight.Bold) .fillMaxWidth() + .margin(bottom = 50.px) } @@ -83,31 +75,46 @@ val ButtonStyle = CssStyle { } } +val TaskStyle = CssStyle.base { + Modifier + .cursor(Cursor.Pointer) + .padding(20.px) + .background(Colors.WhiteSmoke) + .fontSize(36.px) + .fillMaxWidth() + .margin(5.px) + .maxWidth(800.px) +} + @Page @Composable fun HomePage() { + val context = rememberPageContext() + val scope = rememberCoroutineScope() var id by remember { mutableStateOf("") } - var ready by remember { mutableStateOf(false) } 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) } LaunchedEffect(Unit) { - check(!ready && loadingCount == 1) - id = window.localStorage.getItem("id") ?: run { - window.api.get("id").decodeToString().also { - window.localStorage.setItem("id", it) - } - } - loadAndReplaceTodos(id, todos) - loadingCount-- - ready = true + getAllTasks( + onSuccess = { + if (it is ApiListResponse.Success) { + response = it + } + }, + onError = { + println("Error") + response = ApiListResponse.Error(it.message.toString()) + } + ) } Column( @@ -115,99 +122,139 @@ fun HomePage() { horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Top, ) { - if (!ready) { - Box(Modifier.fillMaxWidth().margin(top = 2.cssRem), contentAlignment = Alignment.TopCenter) { - LoadingSpinner() - } - return@Column - } - // ======== H E A D E R ======== - Header(HeaderStyle.toModifier().toAttrs() - ) { + + +// ======== H E A D E R ======== + Header( + + HeaderStyle.toModifier().toAttrs() + ) { Row { Span( HeaderStyle.toModifier() //.justifySelf(JustifySelf.Left) .toAttrs(), ) { - Text("To do:") + Text("To do list") } Button(ButtonStyle.toModifier() .onClick { colorMode = colorMode.opposite } - //.justifySelf(JustifySelf.Right) .toAttrs() ) { Text(value = "Change theme") } } } + // ===== Input form ===== - Column(Modifier.fillMaxSize()) { - Column( - Modifier.fillMaxWidth().maxWidth(800.px).align(Alignment.CenterHorizontally), - horizontalAlignment = Alignment.CenterHorizontally - ) { - TodoForm("Type a TODO and press ENTER", loadingCount > 0) { todo -> - coroutineScope.launch { - loadingCount++ - window.api.post("add?owner=$id&todo=$todo") - loadAndReplaceTodos(id, todos) - loadingCount-- + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Input( + type = InputType.Text, + attrs = Modifier + .id("todoInput") + .width(320.px) + .height(50.px) + .color(Color.navi) + .backgroundColor(Colors.LightGray) + .padding(24.px) + .fontSize(24.px) + .margin(right = 50.px) + .outline(width = 0.px) + .border(width = 0.px) + .fontStyle(FontStyle.Italic) + .toAttrs { + attr("placeholder", "Enter a todo") } - } + ) - todos.forEachIndexed { i, todo -> - TodoCard(onClick = { - coroutineScope.launch { - loadingCount++ - todos.removeAt(i) - window.api.post("remove?owner=$id&todo=${todo.id}") - loadAndReplaceTodos(id, todos) - loadingCount-- + Button( + attrs = Modifier + .onClick { + val todo: String = (document.getElementById("todoInput") as HTMLInputElement).value + scope.launch { + addTask( + Task( + task = todo + ) + ) } - }) { - Text(todo.text) } - } - } + .height(54.px) + .backgroundColor(Colors.White) + .padding(leftRight = 50.px) + .cursor(Cursor.Pointer) + .toAttrs() + ) { - Column( - modifier = Modifier.fillMaxSize(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Row(verticalAlignment = Alignment.CenterVertically ) { - P(attrs = Modifier + SpanText( + modifier = Modifier + .fontSize(18.px) + .fontWeight(FontWeight.Medium) + .color(Colors.Black), + text = "Create" + ) - .fontSize(40.px) - .toAttrs() - ) { - Text(value = "SUIIIIIII") - } } } - Spacer() - - // ====== FOOTER ====== - Footer { - Row(Modifier - .fontSize(20.px) - .color(Colors.Grey)) { - SpanText("Web application by ") - Link( - "https://github.com/Xera08", - "Xera08", - ) + Column( + modifier = Modifier + .margin(40.px) + .fillMaxWidth(), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.CenterHorizontally + ) { + when (response) { + is ApiListResponse.Success -> { + (response as ApiListResponse.Success).data.forEach { task -> + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = TaskStyle.toModifier() + ) { + SpanText( + text = task.task, + modifier = TaskStyle.toModifier() + ) + Button(ButtonStyle.toModifier() + /*.onClick { + scope.launch { + deleteTask(task._id) + } + }*/ + .toAttrs() + ) { + Text(value = "Delete") + } + } + } + } + + else -> { + console.log("Nothing to display") } + } } + } + + + Spacer() + + } -} + + + + diff --git a/site/src/jvmMain/kotlin/todo/api/Add.kt b/site/src/jvmMain/kotlin/todo/api/Add.kt deleted file mode 100644 index 4ce2da9..0000000 --- a/site/src/jvmMain/kotlin/todo/api/Add.kt +++ /dev/null @@ -1,21 +0,0 @@ -package todo.api - -import com.varabyte.kobweb.api.Api -import com.varabyte.kobweb.api.ApiContext -import com.varabyte.kobweb.api.data.getValue -import com.varabyte.kobweb.api.http.HttpMethod -import todo.model.TodoStore - -@Api -fun addTodo(ctx: ApiContext) { - if (ctx.req.method != HttpMethod.POST) return - - val ownerId = ctx.req.params["owner"] - val todo = ctx.req.params["todo"] - if (ownerId == null || todo == null) { - return - } - - ctx.data.getValue<TodoStore>().add(ownerId, todo) - ctx.res.status = 200 -} \ No newline at end of file diff --git a/site/src/jvmMain/kotlin/todo/api/Id.kt b/site/src/jvmMain/kotlin/todo/api/Id.kt deleted file mode 100644 index d5f19f9..0000000 --- a/site/src/jvmMain/kotlin/todo/api/Id.kt +++ /dev/null @@ -1,17 +0,0 @@ -package todo.api - -import com.varabyte.kobweb.api.Api -import com.varabyte.kobweb.api.ApiContext -import com.varabyte.kobweb.api.http.HttpMethod -import com.varabyte.kobweb.api.http.setBodyText -import java.util.* - -/** - * Instead of generating an ID on the client (where there's no UUID library by default), we just ask the server to do - * it. It's doing it anyways for its own stuff! - */ -@Api -fun generateId(ctx: ApiContext) { - if (ctx.req.method != HttpMethod.GET) return - ctx.res.setBodyText(UUID.randomUUID().toString()) -} \ No newline at end of file diff --git a/site/src/jvmMain/kotlin/todo/api/List.kt b/site/src/jvmMain/kotlin/todo/api/List.kt deleted file mode 100644 index b4808a1..0000000 --- a/site/src/jvmMain/kotlin/todo/api/List.kt +++ /dev/null @@ -1,19 +0,0 @@ -package todo.api - -import com.varabyte.kobweb.api.Api -import com.varabyte.kobweb.api.ApiContext -import com.varabyte.kobweb.api.data.getValue -import com.varabyte.kobweb.api.http.HttpMethod -import com.varabyte.kobweb.api.http.setBodyText -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -import todo.model.TodoStore - -@Api -fun listTodos(ctx: ApiContext) { - if (ctx.req.method != HttpMethod.GET) return - val ownerId = ctx.req.params["owner"] ?: return - - val todos = ctx.data.getValue<TodoStore>() - ctx.res.setBodyText(Json.encodeToString(todos[ownerId])) -} diff --git a/site/src/jvmMain/kotlin/todo/api/Remove.kt b/site/src/jvmMain/kotlin/todo/api/Remove.kt deleted file mode 100644 index 7c31452..0000000 --- a/site/src/jvmMain/kotlin/todo/api/Remove.kt +++ /dev/null @@ -1,21 +0,0 @@ -package todo.api - -import com.varabyte.kobweb.api.Api -import com.varabyte.kobweb.api.ApiContext -import com.varabyte.kobweb.api.data.getValue -import com.varabyte.kobweb.api.http.HttpMethod -import todo.model.TodoStore - -@Api -fun removeTodo(ctx: ApiContext) { - if (ctx.req.method != HttpMethod.POST) return - - val ownerId = ctx.req.params["owner"] - val todoId = ctx.req.params["todo"] - if (ownerId == null || todoId == null) { - return - } - - ctx.data.getValue<TodoStore>().remove(ownerId, todoId) - ctx.res.status = 200 -} \ No newline at end of file diff --git a/site/src/jvmMain/kotlin/todo/api/Tasks.kt b/site/src/jvmMain/kotlin/todo/api/Tasks.kt new file mode 100644 index 0000000..2f1fb9b --- /dev/null +++ b/site/src/jvmMain/kotlin/todo/api/Tasks.kt @@ -0,0 +1,51 @@ +package todo.api + +import com.varabyte.kobweb.api.Api +import com.varabyte.kobweb.api.ApiContext +import com.varabyte.kobweb.api.data.getValue +import com.varabyte.kobweb.api.http.setBodyText +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import org.bson.codecs.ObjectIdGenerator +import todo.data.MongoDbImp +import todo.model.ApiListResponse +import todo.model.Task + + +@Api(routeOverride = "addtask") +suspend fun addTask(context: ApiContext) { + try { + val task = context.req.body?.decodeToString()?.let { Json.decodeFromString<Task>(it) } + val newTask = task?.copy(_id = ObjectIdGenerator().generate().toString()) + val responseBody = Json.encodeToString(newTask?.let { context.data.getValue<MongoDbImp>().addTask(it) }) + context.res.setBodyText(responseBody) + }catch (e: Exception) { + context.res.setBodyText(Json.encodeToString(e)) + } + +} + + +@Api(routeOverride = "getalltasks") +suspend fun getAllTasks(context: ApiContext) { + try { + val myTasks = context.data.getValue<MongoDbImp>().getAllTasks() + 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 { + val task = context.req.body?.decodeToString()?.let { Json.decodeFromString<Task>(it) } + val responseBody = Json.encodeToString(task?.let { context.data.getValue<MongoDbImp>().deleteTask(it._id) }) + context.res.setBodyText(responseBody) + }catch (e: Exception) { + context.res.setBodyText(Json.encodeToString(e)) + } + +}*/ diff --git a/site/src/jvmMain/kotlin/todo/api/tasks b/site/src/jvmMain/kotlin/todo/api/tasks deleted file mode 100644 index e69de29..0000000 diff --git a/site/src/jvmMain/kotlin/todo/api/tasks.kt b/site/src/jvmMain/kotlin/todo/api/tasks.kt deleted file mode 100644 index f27e427..0000000 --- a/site/src/jvmMain/kotlin/todo/api/tasks.kt +++ /dev/null @@ -1,10 +0,0 @@ -package todo.api - -import com.varabyte.kobweb.api.Api -import com.varabyte.kobweb.api.ApiContext - - -@Api(routeOverride = "addtask") -suspend fun addTask(context: ApiContext) { - -} \ No newline at end of file diff --git a/site/src/jvmMain/kotlin/todo/data/MongoDb.kt b/site/src/jvmMain/kotlin/todo/data/MongoDb.kt index 3ef5439..3c8c744 100644 --- a/site/src/jvmMain/kotlin/todo/data/MongoDb.kt +++ b/site/src/jvmMain/kotlin/todo/data/MongoDb.kt @@ -5,4 +5,5 @@ import todo.model.Task interface MongoDb { suspend fun getAllTasks(): List<Task> suspend fun addTask(task: Task): Boolean + /*suspend fun deleteTask(taskID: String): Boolean*/ } \ No newline at end of file diff --git a/site/src/jvmMain/kotlin/todo/data/MongoDbImplementation.kt b/site/src/jvmMain/kotlin/todo/data/MongoDbImp.kt similarity index 66% rename from site/src/jvmMain/kotlin/todo/data/MongoDbImplementation.kt rename to site/src/jvmMain/kotlin/todo/data/MongoDbImp.kt index 6b1304e..b0ad57f 100644 --- a/site/src/jvmMain/kotlin/todo/data/MongoDbImplementation.kt +++ b/site/src/jvmMain/kotlin/todo/data/MongoDbImp.kt @@ -1,23 +1,27 @@ package todo.data +import com.mongodb.client.model.Filters import com.mongodb.kotlin.client.coroutine.MongoClient 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 todo.model.Task +@InitApi fun initializeMongo(context: InitApiContext) { System.setProperty( "org.litote.mongo.test.mapping.service", "org.litote.kmongo.serialization.SerializationClassMappingTypeService" ) - context.data.add(MongoDbImplementation()) + context.data.add(MongoDbImp()) } -class MongoDbImplementation():MongoDb { + +class MongoDbImp(): MongoDb { private val client = MongoClient.create() - private val database = client.getDatabase("tasks") + private val database = client.getDatabase("todolist") private val tasksCollection = database.getCollection<Task>("tasks") @@ -30,4 +34,10 @@ class MongoDbImplementation():MongoDb { override suspend fun addTask(task: Task): Boolean { return tasksCollection.insertOne(task).wasAcknowledged() } + + /*override suspend fun deleteTask(taskID: String): Boolean { + val filter = Filters.eq("_id", taskID) + + return tasksCollection.deleteOne(filter).wasAcknowledged() + }*/ } \ No newline at end of file -- GitLab