From 6ab9a59597fde8026fa583af5ae5e72629073bd3 Mon Sep 17 00:00:00 2001 From: Hartmut Bauer Date: Sun, 11 Oct 2026 23:51:14 +0200 Subject: [PATCH] progress --- server/buildnumber.properties | 4 +- server/src/main/kotlin/Version.kt | 12 +- .../src/main/kotlin/books/config/Routing.kt | 4 +- server/src/main/kotlin/books/ui/BookEdit.kt | 122 ++++++++++++------ .../src/main/kotlin/books/ui/BooksTemplate.kt | 2 - .../main/kotlin/books/ui/ImportEditPage.kt | 90 +++++++------ .../src/main/kotlin/core/config/Security.kt | 2 +- 7 files changed, 142 insertions(+), 94 deletions(-) diff --git a/server/buildnumber.properties b/server/buildnumber.properties index 3667a5e..5ae9b2d 100644 --- a/server/buildnumber.properties +++ b/server/buildnumber.properties @@ -1,3 +1,3 @@ #auto generated -#Sun Oct 11 01:02:04 CEST 2026 -build.number=551 +#Sun Oct 11 23:49:03 CEST 2026 +build.number=582 diff --git a/server/src/main/kotlin/Version.kt b/server/src/main/kotlin/Version.kt index 09a0b73..c9cc9b3 100644 --- a/server/src/main/kotlin/Version.kt +++ b/server/src/main/kotlin/Version.kt @@ -4,13 +4,13 @@ import net.keinekekse.version.Version object VERSION: Version { override val version = "0.2.0-alpha" - override val fullVersion = "0.2.0-alpha+build.551" + override val fullVersion = "0.2.0-alpha+build.582" override val major = 0 override val minor = 2 override val patch = 0 override val preRelease = "alpha" - override val buildMetadata = "build.551" - override val buildDate = "2026-10-10T23:02:04.857293Z" + override val buildMetadata = "build.582" + override val buildDate = "2026-10-11T21:49:03.887757Z" override val displayName = "Application Server" override val copyright = "Copyright (c) 2026 Hartmut Bauer" override val name = "server" @@ -18,13 +18,13 @@ object VERSION: Version { } object VERSION_books: Version { override val version = "0.2.0-alpha" - override val fullVersion = "0.2.0-alpha+build.551" + override val fullVersion = "0.2.0-alpha+build.582" override val major = 0 override val minor = 2 override val patch = 0 override val preRelease = "alpha" - override val buildMetadata = "build.551" - override val buildDate = "2026-10-10T23:02:04.857387Z" + override val buildMetadata = "build.582" + override val buildDate = "2026-10-11T21:49:03.887855Z" override val displayName = "Books-DB" override val copyright = "Copyright (c) 2026 Hartmut Bauer" override val name = "books" diff --git a/server/src/main/kotlin/books/config/Routing.kt b/server/src/main/kotlin/books/config/Routing.kt index 23f32c4..9536e8b 100644 --- a/server/src/main/kotlin/books/config/Routing.kt +++ b/server/src/main/kotlin/books/config/Routing.kt @@ -110,9 +110,9 @@ fun Application.configureRouting( } hx.post(navBooksEdit.second) { - call.application.log.info("Edit book form OK") + call.application.log.info("## Edit book form OK") call.receiveParameters().forEach { param: String, value: List -> - call.application.log.info("param: $param, value: $value") + call.application.log.info("## param: $param, value: $value") } call.respondHtmlPartial(HttpStatusCode.OK) { diff --git a/server/src/main/kotlin/books/ui/BookEdit.kt b/server/src/main/kotlin/books/ui/BookEdit.kt index fd543b9..22d22a4 100644 --- a/server/src/main/kotlin/books/ui/BookEdit.kt +++ b/server/src/main/kotlin/books/ui/BookEdit.kt @@ -22,7 +22,6 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { booksRepo.allTags() } - var tabCount = 1 div(classes = "flex flex-col m-8") { form { attributes.hx { @@ -30,6 +29,9 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { target = "#response" swap = "innerHTML" } + input(type = InputType.hidden, name = "bookId") { + value = book.id.toString() + } div(classes = "flex flex-row mb-5 h-9") { div(classes = "w-30 content-center") { @@ -37,7 +39,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { } select(classes = "$cssTextInput w-40") { name = "type" - tabIndex = tabCount++.toString() + autoFocus = true for (type in BookType.entries) { option { @@ -55,7 +57,6 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { } input(classes = "$cssTextInput w-150") { name = "title" - tabIndex = tabCount++.toString() value = book.title } @@ -66,7 +67,6 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { } input(classes = "$cssTextInput w-150") { name = "originaltitle" - tabIndex = tabCount++.toString() value = book.originalTitle ?: "" } @@ -77,7 +77,6 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { } input(classes = "$cssTextInput w-150") { name = "subtitle" - tabIndex = tabCount++.toString() value = book.subTitle ?: "" } } @@ -87,7 +86,6 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { } select(classes = "$cssTextInput w-16") { name = "language" - tabIndex = tabCount++.toString() for (language in Languages.entries) { option { @@ -104,7 +102,6 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { } input(classes = "$cssTextInput w-16") { name = "year" - tabIndex = tabCount++.toString() value = book.year.toString() } @@ -113,7 +110,6 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { } input(classes = "$cssTextInput w-16") { name = "originalyear" - tabIndex = tabCount++.toString() value = book.originalYear?.toString() ?: "" } @@ -124,7 +120,6 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { } input(classes = "$cssTextInput w-40") { name = "isbn" - tabIndex = tabCount++.toString() value = book.isbn } @@ -133,7 +128,6 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { } select(classes = "$cssTextInput w-74") { name = "publisher" - tabIndex = tabCount++.toString() option { value = "0" @@ -141,16 +135,20 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { } for (pub in publishers) { option { - if (pub == book.publisher) + if (pub.id == book.publisher.id) selected = true value = pub.id.toString() +pub.name } } } - a(classes = cssFormIcon) { - tabIndex = tabCount++.toString() + button(classes = cssFormIcon) { title = "Verlag hinzufügen" +// TODO + onClick = "document.getElementById('publisherDialog').showModal()" +// attributes["command"] = "show-modal" +// attributes["commandfor"] = "publisherDialog" //id + materialSymbolFormButton("add_box") } @@ -162,7 +160,6 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { } select(classes = "$cssTextInput w-98") { name = "series" - tabIndex = tabCount++.toString() option { value = "0" @@ -170,15 +167,14 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { } for (serie in series) { option { - if (serie == book.series) + if (book.series != null && serie.id == book.series.id) selected = true value = serie.id.toString() +serie.name } } } - a(classes = cssFormIcon) { - tabIndex = tabCount++.toString() + button(classes = cssFormIcon) { title = "Serie hinzufügen" materialSymbolFormButton("add_box") } @@ -188,24 +184,23 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { } input(classes = "$cssTextInput w-16") { name = "seriesindex" - tabIndex = tabCount++.toString() value = book.seriesIndex?.toString() ?: "" } } - div(classes = "flex flex-row mb-5") { + div(classes = "flex flex-row mb-7") { div(classes = "w-30 content-center") { +"Künstler" } div(classes = "overflow-auto px-1 pb-1 h-40") { + tabIndex = "-1" // overflow setzt sonst einen Tabstopp table { thead(classes = "$cssFixedTHead ") { tr { th(classes = "py-3 px-2") { +"Name" } th(classes = "py-3 px-2") { +"Rolle" } th(classes = "py-3 px-2") { - a(classes = cssFormIcon) { - tabIndex = tabCount++.toString() + button(classes = cssFormIcon) { title = "Künstler hinzufügen" materialSymbolFormButton("add_box") } @@ -223,41 +218,88 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { +role.role } td(classes = "border-t-1 $borderTable px-2 py-1") { - a(classes = cssFormIcon) { - tabIndex = tabCount++.toString() + button(classes = cssFormIcon) { // href = "${navBooksEdit.second}/${book.id}" materialSymbolFormButton("Edit") } - a(classes = cssFormIcon) { - tabIndex = tabCount++.toString() + button(classes = cssFormIcon) { + materialSymbolFormButton("Delete") + } + } + } + } + } + } + } + div(classes = "ml-10 pr-8 content-center") { + +"Tags" + } + div(classes = "overflow-auto px-1 pb-1 h-40") { + tabIndex = "-1" // overflow setzt sonst einen Tabstopp + table { + thead(classes = "$cssFixedTHead ") { + tr { + th(classes = "py-3 px-2") { +"Tag" } + th(classes = "py-3 px-2") { + button(classes = cssFormIcon) { + title = "Tag hinzufügen" + materialSymbolFormButton("add_box") + } + } + } + } + tbody { + for (tag in book.tags) { + tr { + td(classes = "border-t-1 $borderTable px-2 py-1") { + +tag.tag + } + td(classes = "border-t-1 $borderTable px-2 py-1") { + button(classes = cssFormIcon) { materialSymbolFormButton("Delete") } } } } } - } } - -// artists tags -// | | | | -// | | | | - } - div(classes = "flex flex-row mb-5 h-9") { -// evtl. switch -// Teil von Sammelband _____v / enthält (-> Link) -// comment - + div(classes = "flex flex-row mb-5") { + div(classes = "w-30 content-center") { + +"Kommentar" + } + textArea(classes = "$cssTextInput w-150 h-18 resize-none") { + name = "comment" + spellCheck = true + +book.comments + } } +// TODO parent +// evtl. switch oder +// Teil von Sammelband _____v / enthält (-> Link) + div(classes = "flex flex-row mb-5 mr-2 justify-end") { + button(classes = "$cssButtonStd block mt-8 mr-10") { + type = ButtonType.button + onClick = + "window.history.back()" + +"Abbrechen" + } + button(classes = "$cssButtonPri block mt-8") { + type = ButtonType.submit + +"Speichern" + } + } + } - button(classes = "$cssButtonPri block mt-8") { - tabIndex = tabCount++.toString() - type = ButtonType.submit - +"Save" + // TODO + dialog { + id = "publisherDialog" + attributes["closedby"] = "any" + div { + +"Test-Verlag" } } div { diff --git a/server/src/main/kotlin/books/ui/BooksTemplate.kt b/server/src/main/kotlin/books/ui/BooksTemplate.kt index f1de8c5..5d0479b 100644 --- a/server/src/main/kotlin/books/ui/BooksTemplate.kt +++ b/server/src/main/kotlin/books/ui/BooksTemplate.kt @@ -51,7 +51,6 @@ fun HTML.booksPage(title: String, breadcrumbs: Map, block: DIV.( programName() div(classes = "ml-10 flex self-end pr-5 pt-2 border-t-1 items-baseline") { a(classes = cssHeaderLink) { - tabIndex = "0" href = "/" materialSymbol("home") // +"Start" @@ -61,7 +60,6 @@ fun HTML.booksPage(title: String, breadcrumbs: Map, block: DIV.( breadcrumbs.forEach { (name, url) -> if (url.isNotEmpty()) { a(classes = cssHeaderLink) { - tabIndex = "0" href = url +name } diff --git a/server/src/main/kotlin/books/ui/ImportEditPage.kt b/server/src/main/kotlin/books/ui/ImportEditPage.kt index fe3c666..5c87cc8 100644 --- a/server/src/main/kotlin/books/ui/ImportEditPage.kt +++ b/server/src/main/kotlin/books/ui/ImportEditPage.kt @@ -16,47 +16,7 @@ fun HTML.importEditPage(bookInfo: CalibreBookInfo?, booksRepo: BookRepository) { navBooksScanEdit.first, mapOf(navBooks, navBooksNew, Pair(navBooksScanEdit.first, "")) ) { - div(classes = "flex flex-col flex-auto") { - div(classes = "flex flex-row h-60 m-8 w-3/4") { - img(classes = "inline-block h-60 pr-6 ") { - src = dataUrl - } - div { - div { - span(classes = "inline-block w-25 ") { +"ISBN: " } - +(bookInfo?.isbn ?: "") - } - div { - span(classes = "inline-block w-25") { +"Titel: " } - +(bookInfo?.title ?: "") - } - div { - span(classes = "inline-block w-25") { +"Autor(en): " } - +(bookInfo?.authors ?: "") - } - div { - span(classes = "inline-block w-25") { +"Verlag: " } - +(bookInfo?.publisher ?: "") - } - div { - span(classes = "inline-block w-25") { +"Jahr: " } - +bookInfo?.published.toString() - } - div { - span(classes = "inline-block w-25") { +"Tags: " } - +(bookInfo?.tags ?: "") - } - div { - span(classes = "inline-block w-25") { +"Identifiers: " } - +(bookInfo?.identifiers ?: "") - } - div { - span(classes = "inline-block w-25") { +"Comment: " } - +(bookInfo?.comments ?: "") - } - - } - } + div(classes = "flex flex-row flex-auto") { bookEdit( Book( @@ -67,6 +27,54 @@ fun HTML.importEditPage(bookInfo: CalibreBookInfo?, booksRepo: BookRepository) { language = bookInfo?.language ?: Languages.DE ), booksRepo ) + + div(classes = "flex flex-col m-8 ") { + div(classes = "text-2xl mx-8") { + +"Daten aus ISBN-Import" + } + div(classes = "flex flex-row h-60 m-8 w-7/8") { + if (dataUrl.isNotEmpty()) { + img(classes = "inline-block h-60 pr-8") { + src = dataUrl + } + } + div { + div { + span(classes = "inline-block w-25 ") { +"ISBN: " } + +(bookInfo?.isbn ?: "") + } + div { + span(classes = "inline-block w-25") { +"Titel: " } + +(bookInfo?.title ?: "") + } + div { + span(classes = "inline-block w-25") { +"Autor(en): " } + +(bookInfo?.authors ?: "") + } + div { + span(classes = "inline-block w-25") { +"Verlag: " } + +(bookInfo?.publisher ?: "") + } + div { + span(classes = "inline-block w-25") { +"Jahr: " } + +bookInfo?.published.toString() + } + div { + span(classes = "inline-block w-25") { +"Tags: " } + +(bookInfo?.tags ?: "") + } + div { + span(classes = "inline-block w-25") { +"Identifiers: " } + +(bookInfo?.identifiers ?: "") + } + div { + span(classes = "inline-block w-25") { +"Comment: " } + +(bookInfo?.comments ?: "") + } + + } + } + } } } } diff --git a/server/src/main/kotlin/core/config/Security.kt b/server/src/main/kotlin/core/config/Security.kt index ed06c48..b37b53f 100644 --- a/server/src/main/kotlin/core/config/Security.kt +++ b/server/src/main/kotlin/core/config/Security.kt @@ -150,7 +150,7 @@ suspend fun Application.configureSecurity() { call.application.log.info("#### exit refresh (!?)") return@Custom null } - val stale = expiresAt != null && expiresAt <= now + 1.minutes + val stale = expiresAt != null && expiresAt <= now + 5.minutes val refreshToken = token.refreshToken if (stale && refreshToken != null) { call.application.log.info("#### Refreshing token with refresh token")