From 9b0178f3cbc6636979bac7715fc11f0f2f0c97eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 29 Oct 2023 12:59:42 +0100 Subject: [PATCH] Wire up svelte frontend with ktor backend --- app/src/routes/RemoteTable.svelte | 21 +++--- app/src/routes/Table.svelte | 4 +- app/src/routes/server.js | 65 +------------------ app/src/routes/sorting.js | 13 ---- build.gradle.kts | 1 + .../wulkanowy/schools/dao/LoginEventDao.kt | 16 +++-- .../wulkanowy/schools/model/ListResponse.kt | 9 +++ .../wulkanowy/schools/plugins/Routing.kt | 12 +++- 8 files changed, 46 insertions(+), 95 deletions(-) delete mode 100644 app/src/routes/sorting.js create mode 100644 src/main/kotlin/io/github/wulkanowy/schools/model/ListResponse.kt diff --git a/app/src/routes/RemoteTable.svelte b/app/src/routes/RemoteTable.svelte index e358a47..cc9621e 100644 --- a/app/src/routes/RemoteTable.svelte +++ b/app/src/routes/RemoteTable.svelte @@ -4,16 +4,15 @@ import { onMount } from "svelte"; import Table, { Pagination, Row, Search, Sort } from "./Table.svelte"; import { getData } from "./server.js"; - import { sortNumber, sortString } from "./sorting.js"; let rows = []; let page = 0; //first page let pageIndex = 0; //first row - let pageSize = 3; //optional, 10 by default + let pageSize = 10; //optional, 10 by default let loading = true; let rowsCount = 0; - let text; + let text = ""; let sorting; onMount(async () => { @@ -57,24 +56,24 @@ Name - + - Lastname - + Short + - Age - + Address + {#each rows2 as row, index (row)} onCellClick(row)}> - {row.name} - {row.lastName} - {row.age} + {row.schoolName} + {row.schoolShort} + {row.schoolAddress} {/each} diff --git a/app/src/routes/Table.svelte b/app/src/routes/Table.svelte index 098a0c4..989e9eb 100644 --- a/app/src/routes/Table.svelte +++ b/app/src/routes/Table.svelte @@ -27,7 +27,7 @@ export let pageIndex = 0; export let pageSize = 10; export let responsive = true; - export let rows; + export let rows = []; export let serverSide = false; export let labels = { empty: "No records available", @@ -38,7 +38,7 @@ let buttons = [-2, -1, 0, 1, 2]; let pageCount = 0; - $: filteredRows = rows; + $: filteredRows = rows || []; $: visibleRows = filteredRows.slice(pageIndex, pageIndex + pageSize); setContext("state", { diff --git a/app/src/routes/server.js b/app/src/routes/server.js index fe89ee6..ab41b7d 100644 --- a/app/src/routes/server.js +++ b/app/src/routes/server.js @@ -1,64 +1,5 @@ -import { sortNumber, sortString } from "./sorting.js"; - -function generateData() { - const rand = Math.floor(Math.random() * 1000); - return [ - { name: "a-" + rand.toString(), lastName: "o", age: 12 }, - { name: "b", lastName: "n", age: 1 }, - { name: "c", lastName: "m", age: 13 }, - { name: "d", lastName: "l", age: 21 }, - { name: "e", lastName: "k", age: 2 }, - { name: "f", lastName: "j", age: 4 } - ]; -} - - -export function getAll(text) { - return new Promise((resolve, reject) => { - setTimeout(function() { - resolve(generateData()); - }, 500); - }); -} - export function getData(page, pageSize, text, sorting) { - let originalData = generateData(); - - if (sorting) { - if (sorting.key === "age") { - originalData = sortNumber(originalData, sorting.dir, sorting.key); - } else { - originalData = sortString(originalData, sorting.dir, sorting.key); - } - } - - return new Promise((resolve, reject) => { - setTimeout(function() { - let rowsCount = originalData.length; - const originalRows = originalData; - let rows = []; - - if (text && text.length > 0) { - for (let i in originalRows) { - for (let j in originalRows[i]) { - if ( - originalRows[i][j] - .toString() - .toLowerCase() - .indexOf(text) > -1 - ) { - rows.push(originalRows[i]); - break; - } - } - } - - rowsCount = rows.length; - } else { - rows = originalRows; - } - - resolve({ rows: rows.slice(0, pageSize), rowsCount: rowsCount - 1 }); - }, 500); - }); + let options = sorting || { dir: null, key: null }; + return fetch(`/log/list?page=${page}&pageSize=${pageSize}&text=${text}&sortBy=${options.key}&order=${options.dir}`) + .then((res) => res.json()); } diff --git a/app/src/routes/sorting.js b/app/src/routes/sorting.js deleted file mode 100644 index c2645b5..0000000 --- a/app/src/routes/sorting.js +++ /dev/null @@ -1,13 +0,0 @@ -export function sortString(rows, dir, key) { - return rows.sort((a, b) => - dir === "asc" - ? ("" + a[key]).localeCompare(b[key]) - : ("" + b[key]).localeCompare(a[key]) - ); -} - -export function sortNumber(rows, dir, key) { - return rows.sort((a, b) => - dir === "asc" ? a[key] - b[key] : b[key] - a[key] - ); -} diff --git a/build.gradle.kts b/build.gradle.kts index fe760ba..afe98ff 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,6 +39,7 @@ dependencies { implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktor_version") implementation("io.ktor:ktor-serialization-kotlinx-json-jvm:$ktor_version") implementation("io.ktor:ktor-server-netty-jvm:$ktor_version") + implementation("io.ktor:ktor-server-cors:$ktor_version") implementation("ch.qos.logback:logback-classic:$logback_version") implementation("io.ktor:ktor-server-auth:$ktor_version") implementation("com.google.apis:google-api-services-playintegrity:v1-rev20230910-2.0.0") diff --git a/src/main/kotlin/io/github/wulkanowy/schools/dao/LoginEventDao.kt b/src/main/kotlin/io/github/wulkanowy/schools/dao/LoginEventDao.kt index 8c9baad..42f744d 100644 --- a/src/main/kotlin/io/github/wulkanowy/schools/dao/LoginEventDao.kt +++ b/src/main/kotlin/io/github/wulkanowy/schools/dao/LoginEventDao.kt @@ -5,10 +5,7 @@ import io.github.wulkanowy.schools.model.LoginEvent import io.github.wulkanowy.schools.model.LoginEvents import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.insertIgnore -import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.transaction import java.time.Instant @@ -25,8 +22,15 @@ class LoginEventDao { uuid = row[LoginEvents.uuid], ) - suspend fun allLoginEvents(): List = dbQuery { - LoginEvents.selectAll().map(::resultRowToLoginEvent) + suspend fun allLoginEvents(page: Long, pageSize: Int): List = dbQuery { + LoginEvents + .selectAll() + .limit(pageSize, page * pageSize) + .map(::resultRowToLoginEvent) + } + + suspend fun getLoginEventsCount(): Long = dbQuery { + LoginEvents.selectAll().count() } suspend fun addLoginEvent(event: LoginEvent) = withContext(Dispatchers.IO) { diff --git a/src/main/kotlin/io/github/wulkanowy/schools/model/ListResponse.kt b/src/main/kotlin/io/github/wulkanowy/schools/model/ListResponse.kt new file mode 100644 index 0000000..b135136 --- /dev/null +++ b/src/main/kotlin/io/github/wulkanowy/schools/model/ListResponse.kt @@ -0,0 +1,9 @@ +package io.github.wulkanowy.schools.model + +import kotlinx.serialization.Serializable + +@Serializable +data class ListResponse( + val rows: List, + val rowsCount: Long, +) diff --git a/src/main/kotlin/io/github/wulkanowy/schools/plugins/Routing.kt b/src/main/kotlin/io/github/wulkanowy/schools/plugins/Routing.kt index 3c61633..3242e2b 100644 --- a/src/main/kotlin/io/github/wulkanowy/schools/plugins/Routing.kt +++ b/src/main/kotlin/io/github/wulkanowy/schools/plugins/Routing.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.schools.plugins import io.github.wulkanowy.schools.dao.LoginEventDao import io.github.wulkanowy.schools.integrity.* +import io.github.wulkanowy.schools.model.ListResponse import io.github.wulkanowy.schools.model.LoginEvent import io.ktor.http.* import io.ktor.server.application.* @@ -38,7 +39,16 @@ fun Application.configureRouting() { } } get("/log/list") { - call.respond(loginEventDao.allLoginEvents()) + val params = call.request.queryParameters + call.respond( + ListResponse( + rows = loginEventDao.allLoginEvents( + page = params["page"]?.toLongOrNull() ?: 0, + pageSize = params["pageSize"]?.toIntOrNull() ?: 10, + ), + rowsCount = loginEventDao.getLoginEventsCount(), + ) + ) } singlePageApplication { useResources = true