Wire up svelte frontend with ktor backend
This commit is contained in:
parent
0c02cdbf3f
commit
9b0178f3cb
8 changed files with 46 additions and 95 deletions
|
@ -4,16 +4,15 @@
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import Table, { Pagination, Row, Search, Sort } from "./Table.svelte";
|
import Table, { Pagination, Row, Search, Sort } from "./Table.svelte";
|
||||||
import { getData } from "./server.js";
|
import { getData } from "./server.js";
|
||||||
import { sortNumber, sortString } from "./sorting.js";
|
|
||||||
|
|
||||||
let rows = [];
|
let rows = [];
|
||||||
let page = 0; //first page
|
let page = 0; //first page
|
||||||
let pageIndex = 0; //first row
|
let pageIndex = 0; //first row
|
||||||
let pageSize = 3; //optional, 10 by default
|
let pageSize = 10; //optional, 10 by default
|
||||||
|
|
||||||
let loading = true;
|
let loading = true;
|
||||||
let rowsCount = 0;
|
let rowsCount = 0;
|
||||||
let text;
|
let text = "";
|
||||||
let sorting;
|
let sorting;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
@ -57,24 +56,24 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
Name
|
Name
|
||||||
<Sort key="name" on:sort={onSort} />
|
<Sort key="schoolName" on:sort={onSort} />
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Lastname
|
Short
|
||||||
<Sort key="lastName" on:sort={onSort} />
|
<Sort key="schoolShort" on:sort={onSort} />
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Age
|
Address
|
||||||
<Sort key="age" on:sort={onSort} />
|
<Sort key="schoolAddress" on:sort={onSort} />
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each rows2 as row, index (row)}
|
{#each rows2 as row, index (row)}
|
||||||
<Row {index} on:click={() => onCellClick(row)}>
|
<Row {index} on:click={() => onCellClick(row)}>
|
||||||
<td data-label="Name">{row.name}</td>
|
<td data-label="Name">{row.schoolName}</td>
|
||||||
<td data-label="Lastname">{row.lastName}</td>
|
<td data-label="Short">{row.schoolShort}</td>
|
||||||
<td data-label="Age">{row.age}</td>
|
<td data-label="Address">{row.schoolAddress}</td>
|
||||||
</Row>
|
</Row>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
export let pageIndex = 0;
|
export let pageIndex = 0;
|
||||||
export let pageSize = 10;
|
export let pageSize = 10;
|
||||||
export let responsive = true;
|
export let responsive = true;
|
||||||
export let rows;
|
export let rows = [];
|
||||||
export let serverSide = false;
|
export let serverSide = false;
|
||||||
export let labels = {
|
export let labels = {
|
||||||
empty: "No records available",
|
empty: "No records available",
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
let buttons = [-2, -1, 0, 1, 2];
|
let buttons = [-2, -1, 0, 1, 2];
|
||||||
let pageCount = 0;
|
let pageCount = 0;
|
||||||
|
|
||||||
$: filteredRows = rows;
|
$: filteredRows = rows || [];
|
||||||
$: visibleRows = filteredRows.slice(pageIndex, pageIndex + pageSize);
|
$: visibleRows = filteredRows.slice(pageIndex, pageIndex + pageSize);
|
||||||
|
|
||||||
setContext("state", {
|
setContext("state", {
|
||||||
|
|
|
@ -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) {
|
export function getData(page, pageSize, text, sorting) {
|
||||||
let originalData = generateData();
|
let options = sorting || { dir: null, key: null };
|
||||||
|
return fetch(`/log/list?page=${page}&pageSize=${pageSize}&text=${text}&sortBy=${options.key}&order=${options.dir}`)
|
||||||
if (sorting) {
|
.then((res) => res.json());
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -39,6 +39,7 @@ dependencies {
|
||||||
implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktor_version")
|
implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktor_version")
|
||||||
implementation("io.ktor:ktor-serialization-kotlinx-json-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-netty-jvm:$ktor_version")
|
||||||
|
implementation("io.ktor:ktor-server-cors:$ktor_version")
|
||||||
implementation("ch.qos.logback:logback-classic:$logback_version")
|
implementation("ch.qos.logback:logback-classic:$logback_version")
|
||||||
implementation("io.ktor:ktor-server-auth:$ktor_version")
|
implementation("io.ktor:ktor-server-auth:$ktor_version")
|
||||||
implementation("com.google.apis:google-api-services-playintegrity:v1-rev20230910-2.0.0")
|
implementation("com.google.apis:google-api-services-playintegrity:v1-rev20230910-2.0.0")
|
||||||
|
|
|
@ -5,10 +5,7 @@ import io.github.wulkanowy.schools.model.LoginEvent
|
||||||
import io.github.wulkanowy.schools.model.LoginEvents
|
import io.github.wulkanowy.schools.model.LoginEvents
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.insert
|
|
||||||
import org.jetbrains.exposed.sql.insertIgnore
|
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
|
@ -25,8 +22,15 @@ class LoginEventDao {
|
||||||
uuid = row[LoginEvents.uuid],
|
uuid = row[LoginEvents.uuid],
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun allLoginEvents(): List<LoginEvent> = dbQuery {
|
suspend fun allLoginEvents(page: Long, pageSize: Int): List<LoginEvent> = dbQuery {
|
||||||
LoginEvents.selectAll().map(::resultRowToLoginEvent)
|
LoginEvents
|
||||||
|
.selectAll()
|
||||||
|
.limit(pageSize, page * pageSize)
|
||||||
|
.map(::resultRowToLoginEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getLoginEventsCount(): Long = dbQuery {
|
||||||
|
LoginEvents.selectAll().count()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun addLoginEvent(event: LoginEvent) = withContext(Dispatchers.IO) {
|
suspend fun addLoginEvent(event: LoginEvent) = withContext(Dispatchers.IO) {
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package io.github.wulkanowy.schools.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ListResponse(
|
||||||
|
val rows: List<LoginEvent>,
|
||||||
|
val rowsCount: Long,
|
||||||
|
)
|
|
@ -2,6 +2,7 @@ package io.github.wulkanowy.schools.plugins
|
||||||
|
|
||||||
import io.github.wulkanowy.schools.dao.LoginEventDao
|
import io.github.wulkanowy.schools.dao.LoginEventDao
|
||||||
import io.github.wulkanowy.schools.integrity.*
|
import io.github.wulkanowy.schools.integrity.*
|
||||||
|
import io.github.wulkanowy.schools.model.ListResponse
|
||||||
import io.github.wulkanowy.schools.model.LoginEvent
|
import io.github.wulkanowy.schools.model.LoginEvent
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
|
@ -38,7 +39,16 @@ fun Application.configureRouting() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get("/log/list") {
|
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 {
|
singlePageApplication {
|
||||||
useResources = true
|
useResources = true
|
||||||
|
|
Loading…
Reference in a new issue