progress
This commit is contained in:
1 parent
8976e25499
commit
e08a941c62
15 files changed
+213
-93
No files matched your search
@@ -1,3 +1,3 @@
|
|||||||
#auto generated
|
#auto generated
|
||||||
#Sun Oct 11 23:49:03 CEST 2026
|
#Mon Oct 19 00:45:22 CEST 2026
|
||||||
build.number=582
|
build.number=633
|
||||||
@@ -4,13 +4,13 @@ import net.keinekekse.version.Version
|
|||||||
|
|
||||||
object VERSION: Version {
|
object VERSION: Version {
|
||||||
override val version = "0.2.0-alpha"
|
override val version = "0.2.0-alpha"
|
||||||
override val fullVersion = "0.2.0-alpha+build.582"
|
override val fullVersion = "0.2.0-alpha+build.633"
|
||||||
override val major = 0
|
override val major = 0
|
||||||
override val minor = 2
|
override val minor = 2
|
||||||
override val patch = 0
|
override val patch = 0
|
||||||
override val preRelease = "alpha"
|
override val preRelease = "alpha"
|
||||||
override val buildMetadata = "build.582"
|
override val buildMetadata = "build.633"
|
||||||
override val buildDate = "2026-10-11T21:49:03.887757Z"
|
override val buildDate = "2026-10-18T22:45:22.165046Z"
|
||||||
override val displayName = "Application Server"
|
override val displayName = "Application Server"
|
||||||
override val copyright = "Copyright (c) 2026 Hartmut Bauer"
|
override val copyright = "Copyright (c) 2026 Hartmut Bauer"
|
||||||
override val name = "server"
|
override val name = "server"
|
||||||
@@ -18,13 +18,13 @@ object VERSION: Version {
|
|||||||
}
|
}
|
||||||
object VERSION_books: Version {
|
object VERSION_books: Version {
|
||||||
override val version = "0.2.0-alpha"
|
override val version = "0.2.0-alpha"
|
||||||
override val fullVersion = "0.2.0-alpha+build.582"
|
override val fullVersion = "0.2.0-alpha+build.633"
|
||||||
override val major = 0
|
override val major = 0
|
||||||
override val minor = 2
|
override val minor = 2
|
||||||
override val patch = 0
|
override val patch = 0
|
||||||
override val preRelease = "alpha"
|
override val preRelease = "alpha"
|
||||||
override val buildMetadata = "build.582"
|
override val buildMetadata = "build.633"
|
||||||
override val buildDate = "2026-10-11T21:49:03.887855Z"
|
override val buildDate = "2026-10-18T22:45:22.165140Z"
|
||||||
override val displayName = "Books-DB"
|
override val displayName = "Books-DB"
|
||||||
override val copyright = "Copyright (c) 2026 Hartmut Bauer"
|
override val copyright = "Copyright (c) 2026 Hartmut Bauer"
|
||||||
override val name = "books"
|
override val name = "books"
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import kotlinx.html.span
|
|||||||
import net.keinekekse.app.books.db.BookRepository
|
import net.keinekekse.app.books.db.BookRepository
|
||||||
import net.keinekekse.app.books.db.CalibreBookInfoRepositoryImpl
|
import net.keinekekse.app.books.db.CalibreBookInfoRepositoryImpl
|
||||||
import net.keinekekse.app.books.db.schema.emptyBook
|
import net.keinekekse.app.books.db.schema.emptyBook
|
||||||
|
import net.keinekekse.app.books.edit.getSession
|
||||||
|
import net.keinekekse.app.books.edit.newSession
|
||||||
import net.keinekekse.app.books.scan.getCover
|
import net.keinekekse.app.books.scan.getCover
|
||||||
import net.keinekekse.app.books.scan.getIsbnFromCalibre
|
import net.keinekekse.app.books.scan.getIsbnFromCalibre
|
||||||
import net.keinekekse.app.books.ui.*
|
import net.keinekekse.app.books.ui.*
|
||||||
@@ -75,8 +77,9 @@ fun Application.configureRouting(
|
|||||||
get("${navBooksScanEdit.second}/{id}") {
|
get("${navBooksScanEdit.second}/{id}") {
|
||||||
val id = call.parameters["id"]?.toInt() ?: 0
|
val id = call.parameters["id"]?.toInt() ?: 0
|
||||||
val bookInfo = CalibreBookInfoRepositoryImpl(db).readBookInfo(id)
|
val bookInfo = CalibreBookInfoRepositoryImpl(db).readBookInfo(id)
|
||||||
|
val session = newSession(call.principal.userId, sessions)
|
||||||
call.respondHtml {
|
call.respondHtml {
|
||||||
importEditPage(bookInfo, booksRepo)
|
importEditPage(bookInfo, booksRepo, session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete("${navBooksScanEdit.second}/{id}") {
|
delete("${navBooksScanEdit.second}/{id}") {
|
||||||
@@ -108,21 +111,71 @@ fun Application.configureRouting(
|
|||||||
get("${navBooksEdit.second}/{id}") {
|
get("${navBooksEdit.second}/{id}") {
|
||||||
val id = call.parameters["id"]?.toInt() ?: 0
|
val id = call.parameters["id"]?.toInt() ?: 0
|
||||||
val book = booksRepo.bookById(id) ?: emptyBook()
|
val book = booksRepo.bookById(id) ?: emptyBook()
|
||||||
call.respondHtml { bookEditPage(book, booksRepo) }
|
val session = newSession(call.principal.userId, sessions)
|
||||||
|
session.data.book = book
|
||||||
|
call.respondHtml { bookEditPage(booksRepo, session) }
|
||||||
}
|
}
|
||||||
|
|
||||||
hx.post(navBooksEdit.second) {
|
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<String> ->
|
val receiveParameters = call.receiveParameters()
|
||||||
|
receiveParameters.forEach { param: String, value: List<String> ->
|
||||||
call.application.log.info("## param: $param, value: $value")
|
call.application.log.info("## param: $param, value: $value")
|
||||||
}
|
}
|
||||||
|
val session =
|
||||||
|
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
|
||||||
|
|
||||||
call.respondHtmlPartial(HttpStatusCode.OK) {
|
call.respondHtmlPartial(HttpStatusCode.OK) {
|
||||||
p {
|
p {
|
||||||
+"OK"
|
+"OK ${session.sessionId}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hx.post("${navBooksEdit.second}/addpublisher") {
|
||||||
|
val receiveParameters = call.receiveParameters()
|
||||||
|
val session =
|
||||||
|
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
|
||||||
|
}
|
||||||
|
hx.post("${navBooksEdit.second}/addseries") {
|
||||||
|
val receiveParameters = call.receiveParameters()
|
||||||
|
val session =
|
||||||
|
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
|
||||||
|
}
|
||||||
|
hx.post("${navBooksEdit.second}/addtag") {
|
||||||
|
val receiveParameters = call.receiveParameters()
|
||||||
|
val session =
|
||||||
|
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
|
||||||
|
}
|
||||||
|
hx.post("${navBooksEdit.second}/addnewtag") {
|
||||||
|
val receiveParameters = call.receiveParameters()
|
||||||
|
val session =
|
||||||
|
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
|
||||||
|
}
|
||||||
|
hx.post("${navBooksEdit.second}/rmtag") {
|
||||||
|
val receiveParameters = call.receiveParameters()
|
||||||
|
val session =
|
||||||
|
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
|
||||||
|
}
|
||||||
|
hx.post("${navBooksEdit.second}/addartist") {
|
||||||
|
val receiveParameters = call.receiveParameters()
|
||||||
|
val session =
|
||||||
|
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
|
||||||
|
}
|
||||||
|
hx.post("${navBooksEdit.second}/addnewartist") {
|
||||||
|
val receiveParameters = call.receiveParameters()
|
||||||
|
val session =
|
||||||
|
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
|
||||||
|
}
|
||||||
|
hx.post("${navBooksEdit.second}/rmartist") {
|
||||||
|
val receiveParameters = call.receiveParameters()
|
||||||
|
val session =
|
||||||
|
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
|
||||||
|
}
|
||||||
|
hx.post("${navBooksEdit.second}/changeartistrole") {
|
||||||
|
val receiveParameters = call.receiveParameters()
|
||||||
|
val session =
|
||||||
|
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
|
||||||
|
}
|
||||||
|
|
||||||
// hx.get("/isbnlookupreset") {
|
// hx.get("/isbnlookupreset") {
|
||||||
// call.respondHtmlPartial(HttpStatusCode.OK) {
|
// call.respondHtmlPartial(HttpStatusCode.OK) {
|
||||||
|
|||||||
@@ -1,14 +1,43 @@
|
|||||||
package net.keinekekse.app.books.edit
|
package net.keinekekse.app.books.edit
|
||||||
|
|
||||||
|
import net.keinekekse.app.books.model.*
|
||||||
|
import net.keinekekse.app.core.auth.Sessions
|
||||||
import net.keinekekse.app.core.auth.UserSession
|
import net.keinekekse.app.core.auth.UserSession
|
||||||
|
|
||||||
class BooksSession(
|
fun newSession(uid: String, sessions: Sessions): UserSession<BooksSessionData> {
|
||||||
override val userId: String,
|
val session = UserSession(uid, Sessions.newSessionId(), BooksSessionData())
|
||||||
override val sessionId: String,
|
sessions.newSession(session)
|
||||||
override var data: BooksSessionData
|
return session
|
||||||
) : UserSession<BooksSessionData>
|
}
|
||||||
|
|
||||||
|
fun getSession(sessionId: String?, sessions: Sessions, uid: String): UserSession<BooksSessionData> {
|
||||||
|
// TODO besseres Fehlerhandling
|
||||||
|
if (sessionId == null) throw IllegalArgumentException("sessionId is required")
|
||||||
|
val session =
|
||||||
|
sessions.getSession(sessionId)
|
||||||
|
?: throw IllegalArgumentException("no session for sessionId ${sessionId}")
|
||||||
|
if (session.userId != uid) throw IllegalArgumentException("session is invalid: userId != ${uid}")
|
||||||
|
if (session.data is BooksSessionData) {
|
||||||
|
return session as UserSession<BooksSessionData>
|
||||||
|
}
|
||||||
|
throw IllegalArgumentException("session.data has wrong type")
|
||||||
|
}
|
||||||
|
|
||||||
class BooksSessionData() {
|
class BooksSessionData {
|
||||||
|
lateinit var book: Book
|
||||||
|
|
||||||
|
lateinit var publisherList: List<Publisher>
|
||||||
|
lateinit var seriesList: List<Series>
|
||||||
|
lateinit var artistList: List<Artist>
|
||||||
|
lateinit var tagList: List<Tag>
|
||||||
|
|
||||||
|
var newPublisherList: MutableList<Publisher> = mutableListOf()
|
||||||
|
var newSeriesList: MutableList<Series> = mutableListOf()
|
||||||
|
var addTagList: MutableList<Tag> = mutableListOf()
|
||||||
|
var newTagList: MutableList<Tag> = mutableListOf()
|
||||||
|
var rmTagList: MutableList<Tag> = mutableListOf()
|
||||||
|
var addArtistList: MutableList<ArtistForBook> = mutableListOf()
|
||||||
|
var newArtistList: MutableList<ArtistForBook> = mutableListOf()
|
||||||
|
var rmArtistList: MutableList<ArtistForBook> = mutableListOf()
|
||||||
|
var changeArtistRoleList: MutableList<ArtistForBook> = mutableListOf()
|
||||||
}
|
}
|
||||||
@@ -4,21 +4,26 @@ import io.ktor.htmx.html.*
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
import net.keinekekse.app.books.db.BookRepository
|
import net.keinekekse.app.books.db.BookRepository
|
||||||
import net.keinekekse.app.books.model.*
|
import net.keinekekse.app.books.edit.BooksSessionData
|
||||||
import net.keinekekse.app.books.model.Tag
|
import net.keinekekse.app.books.model.BookType
|
||||||
|
import net.keinekekse.app.books.model.Languages
|
||||||
|
import net.keinekekse.app.core.auth.UserSession
|
||||||
|
|
||||||
fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
fun FlowContent.bookEdit(
|
||||||
|
booksRepo: BookRepository,
|
||||||
|
session: UserSession<BooksSessionData>,
|
||||||
|
) {
|
||||||
|
|
||||||
val publishers: List<Publisher> = runBlocking {
|
session.data.publisherList = runBlocking {
|
||||||
booksRepo.allPublishers()
|
booksRepo.allPublishers()
|
||||||
}
|
}
|
||||||
val series: List<Series> = runBlocking {
|
session.data.seriesList = runBlocking {
|
||||||
booksRepo.allSeries()
|
booksRepo.allSeries()
|
||||||
}
|
}
|
||||||
val artists: List<Artist> = runBlocking {
|
session.data.artistList = runBlocking {
|
||||||
booksRepo.allArtists()
|
booksRepo.allArtists()
|
||||||
}
|
}
|
||||||
val tags: List<Tag> = runBlocking {
|
session.data.tagList = runBlocking {
|
||||||
booksRepo.allTags()
|
booksRepo.allTags()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,8 +34,11 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
target = "#response"
|
target = "#response"
|
||||||
swap = "innerHTML"
|
swap = "innerHTML"
|
||||||
}
|
}
|
||||||
|
input(type = InputType.hidden, name = "sessionId") {
|
||||||
|
value = session.sessionId
|
||||||
|
}
|
||||||
input(type = InputType.hidden, name = "bookId") {
|
input(type = InputType.hidden, name = "bookId") {
|
||||||
value = book.id.toString()
|
value = session.data.book.id.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
div(classes = "flex flex-row mb-5 h-9") {
|
div(classes = "flex flex-row mb-5 h-9") {
|
||||||
@@ -43,7 +51,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
|
|
||||||
for (type in BookType.entries) {
|
for (type in BookType.entries) {
|
||||||
option {
|
option {
|
||||||
if (type == book.type)
|
if (type == session.data.book.type)
|
||||||
selected = true
|
selected = true
|
||||||
value = type.ordinal.toString()
|
value = type.ordinal.toString()
|
||||||
+type.name
|
+type.name
|
||||||
@@ -57,7 +65,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
}
|
}
|
||||||
input(classes = "$cssTextInput w-150") {
|
input(classes = "$cssTextInput w-150") {
|
||||||
name = "title"
|
name = "title"
|
||||||
value = book.title
|
value = session.data.book.title
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -67,7 +75,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
}
|
}
|
||||||
input(classes = "$cssTextInput w-150") {
|
input(classes = "$cssTextInput w-150") {
|
||||||
name = "originaltitle"
|
name = "originaltitle"
|
||||||
value = book.originalTitle ?: ""
|
value = session.data.book.originalTitle ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -77,7 +85,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
}
|
}
|
||||||
input(classes = "$cssTextInput w-150") {
|
input(classes = "$cssTextInput w-150") {
|
||||||
name = "subtitle"
|
name = "subtitle"
|
||||||
value = book.subTitle ?: ""
|
value = session.data.book.subTitle ?: ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div(classes = "flex flex-row mb-5 h-9") {
|
div(classes = "flex flex-row mb-5 h-9") {
|
||||||
@@ -89,7 +97,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
|
|
||||||
for (language in Languages.entries) {
|
for (language in Languages.entries) {
|
||||||
option {
|
option {
|
||||||
if (language == book.language)
|
if (language == session.data.book.language)
|
||||||
selected = true
|
selected = true
|
||||||
value = language.ordinal.toString()
|
value = language.ordinal.toString()
|
||||||
+language.name
|
+language.name
|
||||||
@@ -102,7 +110,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
}
|
}
|
||||||
input(classes = "$cssTextInput w-16") {
|
input(classes = "$cssTextInput w-16") {
|
||||||
name = "year"
|
name = "year"
|
||||||
value = book.year.toString()
|
value = session.data.book.year.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
div(classes = "ml-8 pr-3 content-center") {
|
div(classes = "ml-8 pr-3 content-center") {
|
||||||
@@ -110,7 +118,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
}
|
}
|
||||||
input(classes = "$cssTextInput w-16") {
|
input(classes = "$cssTextInput w-16") {
|
||||||
name = "originalyear"
|
name = "originalyear"
|
||||||
value = book.originalYear?.toString() ?: ""
|
value = session.data.book.originalYear?.toString() ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -120,7 +128,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
}
|
}
|
||||||
input(classes = "$cssTextInput w-40") {
|
input(classes = "$cssTextInput w-40") {
|
||||||
name = "isbn"
|
name = "isbn"
|
||||||
value = book.isbn
|
value = session.data.book.isbn
|
||||||
}
|
}
|
||||||
|
|
||||||
div(classes = "ml-14 pr-3 content-center") {
|
div(classes = "ml-14 pr-3 content-center") {
|
||||||
@@ -133,9 +141,9 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
value = "0"
|
value = "0"
|
||||||
+"---"
|
+"---"
|
||||||
}
|
}
|
||||||
for (pub in publishers) {
|
for (pub in session.data.publisherList) {
|
||||||
option {
|
option {
|
||||||
if (pub.id == book.publisher.id)
|
if (pub.id == session.data.book.publisher.id)
|
||||||
selected = true
|
selected = true
|
||||||
value = pub.id.toString()
|
value = pub.id.toString()
|
||||||
+pub.name
|
+pub.name
|
||||||
@@ -145,9 +153,15 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
button(classes = cssFormIcon) {
|
button(classes = cssFormIcon) {
|
||||||
title = "Verlag hinzufügen"
|
title = "Verlag hinzufügen"
|
||||||
// TODO
|
// TODO
|
||||||
onClick = "document.getElementById('publisherDialog').showModal()"
|
/*
|
||||||
// attributes["command"] = "show-modal"
|
type = ButtonType.button -> sonst wird "submit" angenommen und dann funktioniert
|
||||||
// attributes["commandfor"] = "publisherDialog" //id
|
es nicht!
|
||||||
|
*/
|
||||||
|
type = ButtonType.button
|
||||||
|
// attributes["popovertarget"] = "publisherDialog"
|
||||||
|
// onClick = "document.getElementById('publisherDialog').showModal()"
|
||||||
|
attributes["command"] = "show-modal"
|
||||||
|
attributes["commandfor"] = "publisherDialog" //id
|
||||||
|
|
||||||
materialSymbolFormButton("add_box")
|
materialSymbolFormButton("add_box")
|
||||||
}
|
}
|
||||||
@@ -165,9 +179,9 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
value = "0"
|
value = "0"
|
||||||
+"---"
|
+"---"
|
||||||
}
|
}
|
||||||
for (serie in series) {
|
for (serie in session.data.seriesList) {
|
||||||
option {
|
option {
|
||||||
if (book.series != null && serie.id == book.series.id)
|
if (session.data.book.series != null && serie.id == session.data.book.series!!.id)
|
||||||
selected = true
|
selected = true
|
||||||
value = serie.id.toString()
|
value = serie.id.toString()
|
||||||
+serie.name
|
+serie.name
|
||||||
@@ -184,7 +198,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
}
|
}
|
||||||
input(classes = "$cssTextInput w-16") {
|
input(classes = "$cssTextInput w-16") {
|
||||||
name = "seriesindex"
|
name = "seriesindex"
|
||||||
value = book.seriesIndex?.toString() ?: ""
|
value = session.data.book.seriesIndex?.toString() ?: ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +223,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
}
|
}
|
||||||
tbody {
|
tbody {
|
||||||
|
|
||||||
for ((artist, role) in book.artists) {
|
for ((artist, role) in session.data.book.artists) {
|
||||||
tr {
|
tr {
|
||||||
td(classes = "border-t-1 $borderTable px-2 py-1") {
|
td(classes = "border-t-1 $borderTable px-2 py-1") {
|
||||||
+"${artist.name}, ${artist.givenName}"
|
+"${artist.name}, ${artist.givenName}"
|
||||||
@@ -249,7 +263,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tbody {
|
tbody {
|
||||||
for (tag in book.tags) {
|
for (tag in session.data.book.tags) {
|
||||||
tr {
|
tr {
|
||||||
td(classes = "border-t-1 $borderTable px-2 py-1") {
|
td(classes = "border-t-1 $borderTable px-2 py-1") {
|
||||||
+tag.tag
|
+tag.tag
|
||||||
@@ -273,7 +287,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
textArea(classes = "$cssTextInput w-150 h-18 resize-none") {
|
textArea(classes = "$cssTextInput w-150 h-18 resize-none") {
|
||||||
name = "comment"
|
name = "comment"
|
||||||
spellCheck = true
|
spellCheck = true
|
||||||
+book.comments
|
+session.data.book.comments
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO parent
|
// TODO parent
|
||||||
@@ -295,13 +309,23 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
dialog {
|
dialog(
|
||||||
|
classes = "m-auto h-fit w-full max-w-md rounded-lg shadow-2xl border-2 " +
|
||||||
|
"$borderTable px-3 py-2 $bgBody $textBody $bgDialogBackdrop"
|
||||||
|
) {
|
||||||
id = "publisherDialog"
|
id = "publisherDialog"
|
||||||
attributes["closedby"] = "any"
|
// attributes["popover"] = "true"
|
||||||
|
// attributes["closedby"] = "any"
|
||||||
div {
|
div {
|
||||||
+"Test-Verlag"
|
+"Test-Verlag"
|
||||||
}
|
}
|
||||||
|
button(classes = cssButtonStd) {
|
||||||
|
attributes["commandfor"] = "publisherDialog"
|
||||||
|
attributes["command"] = "close"
|
||||||
|
+"Close"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
output { }
|
||||||
div {
|
div {
|
||||||
id = "response"
|
id = "response"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,20 @@ package net.keinekekse.app.books.ui
|
|||||||
|
|
||||||
import kotlinx.html.HTML
|
import kotlinx.html.HTML
|
||||||
import net.keinekekse.app.books.db.BookRepository
|
import net.keinekekse.app.books.db.BookRepository
|
||||||
import net.keinekekse.app.books.model.Book
|
import net.keinekekse.app.books.edit.BooksSessionData
|
||||||
|
import net.keinekekse.app.core.auth.UserSession
|
||||||
|
|
||||||
fun HTML.bookEditPage(book: Book, booksRepo: BookRepository) {
|
fun HTML.bookEditPage(
|
||||||
|
booksRepo: BookRepository,
|
||||||
|
session: UserSession<BooksSessionData>,
|
||||||
|
) {
|
||||||
|
|
||||||
booksPage(
|
booksPage(
|
||||||
navBooksEdit.first,
|
navBooksEdit.first,
|
||||||
mapOf(navBooks, Pair(navBooksEdit.first, ""))
|
mapOf(navBooks, Pair(navBooksEdit.first, ""))
|
||||||
) {
|
) {
|
||||||
|
|
||||||
bookEdit(book, booksRepo)
|
bookEdit(booksRepo, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ fun HTML.booksPage(booksRepo: BookRepository) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
// content right
|
// content right
|
||||||
div(classes = "flex flex-auto $bgRightColumn") {
|
div(classes = "flex flex-auto $bgBody") {
|
||||||
div(classes = "flex flex-col flex-auto") {
|
div(classes = "flex flex-col flex-auto") {
|
||||||
// div {
|
// div {
|
||||||
// +"some heading"
|
// +"some heading"
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ fun HTML.booksPage(title: String, breadcrumbs: Map<String, String>, block: DIV.(
|
|||||||
|
|
||||||
body {
|
body {
|
||||||
// page
|
// page
|
||||||
div(classes = "$cssFullscreen flex-col") {
|
div(classes = "$cssFullscreen flex-col $textBody") {
|
||||||
// header
|
// header
|
||||||
div(classes = "flex flex-row justify-between py-2 px-5 $bgHeader $textHeader") {
|
div(classes = "flex flex-row justify-between py-2 px-5 $bgHeader $textHeader") {
|
||||||
// header left
|
// header left
|
||||||
@@ -80,7 +80,7 @@ fun HTML.booksPage(title: String, breadcrumbs: Map<String, String>, block: DIV.(
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
div(classes = "flex flex-row flex-auto overflow-hidden") {
|
div(classes = "flex flex-row flex-auto overflow-hidden $bgBody") {
|
||||||
|
|
||||||
block()
|
block()
|
||||||
|
|
||||||
|
|||||||
@@ -14,15 +14,16 @@ const val textHeader = "text-orange-100"
|
|||||||
const val bgHeaderIcon = "bg-orange-100"
|
const val bgHeaderIcon = "bg-orange-100"
|
||||||
const val bgFooter = "bg-orange-50"
|
const val bgFooter = "bg-orange-50"
|
||||||
|
|
||||||
const val bgLeftColumn = "bg-slate-200"
|
const val bgBody = "bg-taupe-100"
|
||||||
const val bgRightColumn = "bg-slate-50"
|
const val textBody = "text-taupe-900"
|
||||||
const val bgThead = "bg-slate-200"
|
const val bgLeftColumn = "bg-taupe-200"
|
||||||
|
const val bgThead = "bg-taupe-200"
|
||||||
|
const val bgDialogBackdrop = "backdrop:bg-taupe-400/40"
|
||||||
const val borderTable = "border-slate-400"
|
const val borderTable = "border-slate-400"
|
||||||
|
|
||||||
const val bgStdButton = "bg-taupe-500"
|
const val bgStdButton = "bg-taupe-400"
|
||||||
const val bgStdButtonHover = "hover:bg-taupe-700"
|
const val bgStdButtonHover = "hover:bg-taupe-500"
|
||||||
const val bgStdButtonFocus = "focus:bg-taupe-700"
|
const val bgStdButtonFocus = "focus:bg-taupe-500"
|
||||||
const val bgPriButton = "bg-orange-400"
|
const val bgPriButton = "bg-orange-400"
|
||||||
const val bgPriButtonHover = "hover:bg-orange-600"
|
const val bgPriButtonHover = "hover:bg-orange-600"
|
||||||
const val bgPriButtonFocus = "focus:bg-orange-600"
|
const val bgPriButtonFocus = "focus:bg-orange-600"
|
||||||
|
|||||||
@@ -5,12 +5,18 @@ import kotlinx.html.div
|
|||||||
import kotlinx.html.img
|
import kotlinx.html.img
|
||||||
import kotlinx.html.span
|
import kotlinx.html.span
|
||||||
import net.keinekekse.app.books.db.BookRepository
|
import net.keinekekse.app.books.db.BookRepository
|
||||||
|
import net.keinekekse.app.books.edit.BooksSessionData
|
||||||
import net.keinekekse.app.books.model.Book
|
import net.keinekekse.app.books.model.Book
|
||||||
import net.keinekekse.app.books.model.CalibreBookInfo
|
import net.keinekekse.app.books.model.CalibreBookInfo
|
||||||
import net.keinekekse.app.books.model.Languages
|
import net.keinekekse.app.books.model.Languages
|
||||||
import net.keinekekse.app.books.model.Publisher
|
import net.keinekekse.app.books.model.Publisher
|
||||||
|
import net.keinekekse.app.core.auth.UserSession
|
||||||
|
|
||||||
fun HTML.importEditPage(bookInfo: CalibreBookInfo?, booksRepo: BookRepository) {
|
fun HTML.importEditPage(
|
||||||
|
bookInfo: CalibreBookInfo?,
|
||||||
|
booksRepo: BookRepository,
|
||||||
|
session: UserSession<BooksSessionData>,
|
||||||
|
) {
|
||||||
val dataUrl = embedImageURL(bookInfo?.cover)
|
val dataUrl = embedImageURL(bookInfo?.cover)
|
||||||
booksPage(
|
booksPage(
|
||||||
navBooksScanEdit.first,
|
navBooksScanEdit.first,
|
||||||
@@ -18,15 +24,14 @@ fun HTML.importEditPage(bookInfo: CalibreBookInfo?, booksRepo: BookRepository) {
|
|||||||
) {
|
) {
|
||||||
div(classes = "flex flex-row flex-auto") {
|
div(classes = "flex flex-row flex-auto") {
|
||||||
|
|
||||||
bookEdit(
|
session.data.book = Book(
|
||||||
Book(
|
|
||||||
title = bookInfo?.title ?: "",
|
title = bookInfo?.title ?: "",
|
||||||
year = bookInfo?.published ?: 0,
|
year = bookInfo?.published ?: 0,
|
||||||
isbn = bookInfo?.isbn ?: "",
|
isbn = bookInfo?.isbn ?: "",
|
||||||
publisher = Publisher(0, ""),
|
publisher = Publisher(0, ""),
|
||||||
language = bookInfo?.language ?: Languages.DE
|
language = bookInfo?.language ?: Languages.DE
|
||||||
), booksRepo
|
|
||||||
)
|
)
|
||||||
|
bookEdit(booksRepo, session)
|
||||||
|
|
||||||
div(classes = "flex flex-col m-8 ") {
|
div(classes = "flex flex-col m-8 ") {
|
||||||
div(classes = "text-2xl mx-8") {
|
div(classes = "text-2xl mx-8") {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ fun HTML.importPage(books: List<CalibreBookInfo>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// content right
|
// content right
|
||||||
div(classes = "flex flex-auto $bgRightColumn") {
|
div(classes = "flex flex-auto $bgBody") {
|
||||||
div(classes = "flex flex-col flex-auto") {
|
div(classes = "flex flex-col flex-auto") {
|
||||||
div {
|
div {
|
||||||
// +"some heading"
|
// +"some heading"
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ fun HTML.testPage(booksDb: Database) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// content right
|
// content right
|
||||||
div(classes = "flex flex-auto $bgRightColumn") {
|
div(classes = "flex flex-auto $bgBody") {
|
||||||
div(classes = "flex flex-col flex-auto") {
|
div(classes = "flex flex-col flex-auto") {
|
||||||
// div {
|
// div {
|
||||||
// +"some heading"
|
// +"some heading"
|
||||||
|
|||||||
@@ -5,40 +5,29 @@ import kotlin.uuid.Uuid
|
|||||||
|
|
||||||
class Sessions {
|
class Sessions {
|
||||||
|
|
||||||
var sessions: MutableMap<String, MutableMap<String, UserSession<*>>> = mutableMapOf()
|
var sessions: MutableMap<String, UserSession<*>> = mutableMapOf()
|
||||||
|
|
||||||
|
companion object {
|
||||||
@OptIn(ExperimentalUuidApi::class)
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
fun newSessionId(): String {
|
fun newSessionId(): String {
|
||||||
return Uuid.generateV4().toHexDashString()
|
return Uuid.generateV4().toHexDashString()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUserSessions(uid: String): MutableMap<String, UserSession<*>>? {
|
|
||||||
return sessions.get(uid)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUserSession(uid: String, sessionId: String): UserSession<*>? {
|
fun getSession(sessionId: String): UserSession<*>? {
|
||||||
return sessions.get(uid)?.get(sessionId)
|
return sessions[sessionId]
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setUserSession(uid: String, sessionId: String, data: UserSession<*>) {
|
fun newSession(data: UserSession<*>) {
|
||||||
sessions.getOrPut(uid) { mutableMapOf() }[sessionId] = data
|
sessions[data.sessionId] = data
|
||||||
}
|
}
|
||||||
|
|
||||||
// fun addSession(uid: String) {
|
|
||||||
// sessions[uid] = BooksSession(uid, "", "")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun addTestSession(uid: String) {
|
|
||||||
// sessions[uid] = TestSession(uid, "", 0)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserSession<T> {
|
data class UserSession<T>(
|
||||||
val userId: String
|
val userId: String,
|
||||||
val sessionId: String
|
val sessionId: String,
|
||||||
|
var data: T,
|
||||||
var data: T
|
)
|
||||||
}
|
|
||||||
|
|
||||||
//data class BooksSession(
|
//data class BooksSession(
|
||||||
// override val userId: String,
|
// override val userId: String,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import kotlinx.html.*
|
|||||||
import net.keinekekse.app.VERSION_books
|
import net.keinekekse.app.VERSION_books
|
||||||
import net.keinekekse.app.books.ui.materialSymbol
|
import net.keinekekse.app.books.ui.materialSymbol
|
||||||
import net.keinekekse.app.books.ui.navBooks
|
import net.keinekekse.app.books.ui.navBooks
|
||||||
|
import net.keinekekse.app.books.ui.navBooksScan
|
||||||
import net.keinekekse.app.core.config.LOGIN_PATH
|
import net.keinekekse.app.core.config.LOGIN_PATH
|
||||||
import net.keinekekse.app.core.config.LOGOUT_PATH
|
import net.keinekekse.app.core.config.LOGOUT_PATH
|
||||||
|
|
||||||
@@ -37,12 +38,26 @@ fun HTML.landingPage(loggedOn: Boolean) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
div(classes = "lg:hidden p-8") {
|
||||||
|
a(classes = "inline-flex $cssMainLink") {
|
||||||
|
href = navBooksScan.second
|
||||||
|
|
||||||
// a(classes = "p-2 text-xl font-bold text-sky-500 hover:text-sky-700 hover:bg-sky-100 cursor-pointer") {
|
img(classes = "h-14 my-2") {
|
||||||
// href = "/books"
|
src = "/static/books.png"
|
||||||
// +VERSION_books.description
|
}
|
||||||
|
div(classes = "self-center text-right ml-3") {
|
||||||
|
div {
|
||||||
|
div {
|
||||||
|
+"Scan Books"
|
||||||
|
}
|
||||||
|
// div(classes = "text-xs") {
|
||||||
|
// +VERSION_books.version
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
div(classes = "m-8") {
|
div(classes = "m-8") {
|
||||||
a(classes = cssMainLink) {
|
a(classes = cssMainLink) {
|
||||||
href = "/admin"
|
href = "/admin"
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ fun HTML.mainApplicationPage(block: DIV.() -> Unit = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// body
|
// body
|
||||||
div(classes = "inline-flex $bgMainBody py-10 h-full overflow-hidden w-full rounded-b-lg") {
|
div(classes = "inline-flex $bgMainBody py-10 h-full overflow-auto w-full rounded-b-lg") {
|
||||||
|
|
||||||
block()
|
block()
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user