progress
This commit is contained in:
1 parent
3d165ee610
commit
e8bd1bcb97
9 files changed
+136
-58
No files matched your search
@@ -1,3 +1,3 @@
|
||||
#auto generated
|
||||
#Wed Oct 21 01:06:20 CEST 2026
|
||||
build.number=655
|
||||
#Wed Oct 21 23:44:14 CEST 2026
|
||||
build.number=668
|
||||
@@ -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.655"
|
||||
override val fullVersion = "0.2.0-alpha+build.668"
|
||||
override val major = 0
|
||||
override val minor = 2
|
||||
override val patch = 0
|
||||
override val preRelease = "alpha"
|
||||
override val buildMetadata = "build.655"
|
||||
override val buildDate = "2026-10-20T23:06:20.951648Z"
|
||||
override val buildMetadata = "build.668"
|
||||
override val buildDate = "2026-10-21T21:44:14.662995Z"
|
||||
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.655"
|
||||
override val fullVersion = "0.2.0-alpha+build.668"
|
||||
override val major = 0
|
||||
override val minor = 2
|
||||
override val patch = 0
|
||||
override val preRelease = "alpha"
|
||||
override val buildMetadata = "build.655"
|
||||
override val buildDate = "2026-10-20T23:06:20.951769Z"
|
||||
override val buildMetadata = "build.668"
|
||||
override val buildDate = "2026-10-21T21:44:14.663097Z"
|
||||
override val displayName = "Books-DB"
|
||||
override val copyright = "Copyright (c) 2026 Hartmut Bauer"
|
||||
override val name = "books"
|
||||
|
||||
@@ -15,4 +15,6 @@ interface BookRepository {
|
||||
suspend fun allArtists(): List<Artist>
|
||||
suspend fun allSeries(): List<Series>
|
||||
suspend fun allTags(): List<Tag>
|
||||
suspend fun allSamplers(): List<Book>
|
||||
|
||||
}
|
||||
@@ -58,4 +58,7 @@ class BookRepositoryImpl(val booksDb: Database) : BookRepository {
|
||||
TagsDAO.all().map(::tagDaoToModel)
|
||||
}
|
||||
|
||||
override suspend fun allSamplers(): List<Book> = suspendTransaction(booksDb) {
|
||||
SamplerDAO.all().map { sampler -> bookDaoToModel(sampler.book) }
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,11 @@ object BooksTable : IntIdTable("books") {
|
||||
var type = integer("type")
|
||||
var read = bool("read").default(false)
|
||||
var comments = varchar("comments", 255)
|
||||
var parentBook = optReference("parent_book_id", id)
|
||||
|
||||
// Dieses Buch ist Teil eines Sammelbands.
|
||||
// Null bedeutet: kein übergeordneter Sammelband.
|
||||
var parent = optReference("parent_id", id)
|
||||
|
||||
val cover = blob("cover").nullable()
|
||||
}
|
||||
|
||||
@@ -48,10 +52,12 @@ class BookDAO(id: EntityID<Int>) : IntEntity(id) {
|
||||
val tags by TagsToBookDAO referrersOn TagsToBookTable.bookId
|
||||
|
||||
// Verweis auf Sammelband (falls dieses Buch ein Teilroman ist)
|
||||
var parentBook by BookDAO optionalReferencedOn BooksTable.parentBook
|
||||
var parent by BookDAO optionalReferencedOn BooksTable.parent
|
||||
|
||||
// Enthaltene Einzelromane (falls dieses Buch der Sammelband ist)
|
||||
val containedBooks by BookDAO optionalReferrersOn BooksTable.parentBook
|
||||
val containedBooks by BookDAO optionalReferrersOn BooksTable.parent
|
||||
|
||||
val samplerEntry by SamplerDAO optionalBackReferencedOn SamplerTable.bookId
|
||||
}
|
||||
|
||||
fun bookDaoToModel(dao: BookDAO, includeChildren: Boolean = true): Book = Book(
|
||||
@@ -72,12 +78,15 @@ fun bookDaoToModel(dao: BookDAO, includeChildren: Boolean = true): Book = Book(
|
||||
series = if (dao.series != null) Series(dao.series!!.id.value, dao.series!!.name) else null,
|
||||
seriesIndex = dao.seriesIndex,
|
||||
|
||||
dao.parentBook?.id?.value,
|
||||
parentBookId = dao.parent?.id?.value,
|
||||
containedBooks = if (includeChildren) {
|
||||
dao.containedBooks.map { bookDaoToModel(it, includeChildren = false) }
|
||||
dao.containedBooks.map { child ->
|
||||
bookDaoToModel(child, includeChildren = false)
|
||||
}
|
||||
} else {
|
||||
emptyList()
|
||||
},
|
||||
isSampler = dao.samplerEntry != null,
|
||||
|
||||
artists = dao.artists.map {
|
||||
ArtistForBook(
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package net.keinekekse.app.books.db.schema
|
||||
|
||||
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
||||
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.v1.dao.IntEntity
|
||||
import org.jetbrains.exposed.v1.dao.IntEntityClass
|
||||
|
||||
object SamplerTable : IntIdTable("samplers") {
|
||||
var bookId = reference("book_id", BooksTable.id).uniqueIndex()
|
||||
}
|
||||
|
||||
class SamplerDAO(id: EntityID<Int>) : IntEntity(id) {
|
||||
companion object : IntEntityClass<SamplerDAO>(SamplerTable)
|
||||
|
||||
var book by BookDAO referencedOn SamplerTable.bookId
|
||||
}
|
||||
@@ -23,6 +23,7 @@ data class Book(
|
||||
val series: Series? = null,
|
||||
val seriesIndex: Int? = null,
|
||||
val parentBookId: Int? = null, // ID des Sammelbands
|
||||
val isSampler: Boolean = false,
|
||||
|
||||
val containedBooks: List<Book> = emptyList(), // Enthaltene Romane
|
||||
val artists: List<ArtistForBook> = emptyList(),
|
||||
|
||||
@@ -61,7 +61,7 @@ fun FlowContent.bookEdit(
|
||||
}
|
||||
div(classes = "flex flex-row mb-5 h-9") {
|
||||
div(classes = "w-30 content-center") {
|
||||
+"Titel:"
|
||||
+"Titel"
|
||||
}
|
||||
input(classes = "$cssTextInput w-150") {
|
||||
name = "title"
|
||||
@@ -71,7 +71,7 @@ fun FlowContent.bookEdit(
|
||||
}
|
||||
div(classes = "flex flex-row mb-5 h-9") {
|
||||
div(classes = "w-30 content-center") {
|
||||
+"Originaltitel:"
|
||||
+"Originaltitel"
|
||||
}
|
||||
input(classes = "$cssTextInput w-150") {
|
||||
name = "originaltitle"
|
||||
@@ -81,7 +81,7 @@ fun FlowContent.bookEdit(
|
||||
}
|
||||
div(classes = "flex flex-row mb-5 h-9") {
|
||||
div(classes = "w-30 content-center") {
|
||||
+"Untertitel:"
|
||||
+"Untertitel"
|
||||
}
|
||||
input(classes = "$cssTextInput w-150") {
|
||||
name = "subtitle"
|
||||
@@ -105,16 +105,16 @@ fun FlowContent.bookEdit(
|
||||
}
|
||||
}
|
||||
|
||||
div(classes = "ml-9 pr-3 content-center") {
|
||||
+"Erscheinungsjahr:"
|
||||
div(classes = "w-45 pr-3 content-center text-end") {
|
||||
+"Erscheinungsjahr"
|
||||
}
|
||||
input(classes = "$cssTextInput w-16") {
|
||||
name = "year"
|
||||
value = session.data.book.year.toString()
|
||||
}
|
||||
|
||||
div(classes = "ml-8 pr-3 content-center") {
|
||||
+"Jahr der Originalausgabe:"
|
||||
div(classes = "w-57 pr-3 content-center text-end") {
|
||||
+"Jahr der Originalausgabe"
|
||||
}
|
||||
input(classes = "$cssTextInput w-16") {
|
||||
name = "originalyear"
|
||||
@@ -123,7 +123,7 @@ fun FlowContent.bookEdit(
|
||||
|
||||
}
|
||||
div(classes = "flex flex-row mb-5 h-9") {
|
||||
div(classes = "w-30 content-center") {
|
||||
div(classes = "w-32 content-center") {
|
||||
+"ISBN"
|
||||
}
|
||||
input(classes = "$cssTextInput w-40") {
|
||||
@@ -131,7 +131,7 @@ fun FlowContent.bookEdit(
|
||||
value = session.data.book.isbn
|
||||
}
|
||||
|
||||
div(classes = "ml-14 pr-3 content-center") {
|
||||
div(classes = "w-26 pr-3 content-center text-end") {
|
||||
+"Verlag"
|
||||
}
|
||||
select(classes = "$cssTextInput w-74") {
|
||||
@@ -173,7 +173,7 @@ fun FlowContent.bookEdit(
|
||||
div(classes = "w-30 content-center") {
|
||||
+"Serie"
|
||||
}
|
||||
select(classes = "$cssTextInput w-98") {
|
||||
select(classes = "$cssTextInput w-100") {
|
||||
name = "series"
|
||||
|
||||
option {
|
||||
@@ -194,45 +194,45 @@ fun FlowContent.bookEdit(
|
||||
materialSymbolFormButton("add_box")
|
||||
}
|
||||
|
||||
div(classes = "ml-13 pr-3 content-center") {
|
||||
div(classes = "w-20 pr-3 content-center text-end") {
|
||||
+"Index"
|
||||
}
|
||||
input(classes = "$cssTextInput w-16") {
|
||||
input(classes = "$cssTextInput w-20") {
|
||||
name = "seriesindex"
|
||||
value = session.data.book.seriesIndex?.toString() ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
div(classes = "flex flex-row mb-7") {
|
||||
div(classes = "w-30 content-center") {
|
||||
div(classes = "w-30 content-start pt-3") {
|
||||
+"Künstler"
|
||||
}
|
||||
div(classes = "overflow-auto px-1 pb-1 h-40") {
|
||||
div(classes = "overflow-auto px-1 pb-1 h-40 w-82") {
|
||||
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") {
|
||||
button(classes = cssFormIcon) {
|
||||
title = "Künstler hinzufügen"
|
||||
materialSymbolFormButton("add_box")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// thead(classes = "$cssFixedTHead ") {
|
||||
// tr {
|
||||
// th(classes = "py-3 px-2") { +"Name" }
|
||||
// th(classes = "py-3 px-2") { +"Rolle" }
|
||||
// th(classes = "py-3 px-2") {
|
||||
// button(classes = cssFormIcon) {
|
||||
// title = "Künstler hinzufügen"
|
||||
// materialSymbolFormButton("add_box")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
tbody {
|
||||
|
||||
for ((artist, role) in session.data.book.artists) {
|
||||
tr {
|
||||
td(classes = "border-t-1 $borderTable px-2 py-1") {
|
||||
td(classes = "border-b-1 $borderTable px-2 py-1") {
|
||||
+"${artist.name}, ${artist.givenName}"
|
||||
}
|
||||
td(classes = "border-t-1 $borderTable px-2 py-1") {
|
||||
td(classes = "border-b-1 $borderTable px-2 py-1") {
|
||||
+role.role
|
||||
}
|
||||
td(classes = "border-t-1 $borderTable px-2 py-1") {
|
||||
td(classes = "border-b-1 $borderTable px-2 py-1") {
|
||||
button(classes = cssFormIcon) {
|
||||
// href = "${navBooksEdit.second}/${book.id}"
|
||||
materialSymbolFormButton("Edit")
|
||||
@@ -246,30 +246,35 @@ fun FlowContent.bookEdit(
|
||||
}
|
||||
}
|
||||
}
|
||||
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"
|
||||
button(classes = "$cssFormIcon self-start mt-2 ") {
|
||||
title = "Künstler hinzufügen"
|
||||
materialSymbolFormButton("add_box")
|
||||
}
|
||||
|
||||
div(classes = "w-20 pr-3 content-start text-end pt-3") {
|
||||
+"Tags"
|
||||
}
|
||||
}
|
||||
}
|
||||
div(classes = "overflow-auto px-1 pb-1 h-40 w-30") {
|
||||
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 session.data.book.tags) {
|
||||
tr {
|
||||
td(classes = "border-t-1 $borderTable px-2 py-1") {
|
||||
td(classes = "border-b-1 $borderTable px-2 py-1") {
|
||||
+tag.tag
|
||||
}
|
||||
td(classes = "border-t-1 $borderTable px-2 py-1") {
|
||||
td(classes = "border-b-1 $borderTable px-2 py-1") {
|
||||
button(classes = cssFormIcon) {
|
||||
materialSymbolFormButton("Delete")
|
||||
}
|
||||
@@ -279,6 +284,11 @@ fun FlowContent.bookEdit(
|
||||
}
|
||||
}
|
||||
}
|
||||
button(classes = "$cssFormIcon self-start mt-2") {
|
||||
title = "Tag hinzufügen"
|
||||
materialSymbolFormButton("add_box")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
div(classes = "flex flex-row mb-5") {
|
||||
@@ -294,6 +304,41 @@ fun FlowContent.bookEdit(
|
||||
// TODO parent
|
||||
// evtl. switch oder <detail>
|
||||
// Teil von Sammelband _____v / enthält (-> Link)
|
||||
div(classes = "flex flex-row mb-5 h-9") {
|
||||
div(classes = "w-30 content-center") {
|
||||
+"Sammelband"
|
||||
}
|
||||
label(
|
||||
classes = "w-10 justify-center grid grid-cols-[1.3em] " +
|
||||
"focus-within:bg-orange-300 hover:bg-orange-300"
|
||||
) {
|
||||
input {
|
||||
name = "sampler"
|
||||
type = InputType.checkBox // liefert sampler=on || nix !
|
||||
checked = session.data.book.isSampler
|
||||
}
|
||||
}
|
||||
div(classes = "w-30 content-center text-end pr-3") {
|
||||
+"Teil von"
|
||||
}
|
||||
select(classes = "$cssTextInput w-50") {
|
||||
name = "parent"
|
||||
option {
|
||||
value = "0"
|
||||
+"---"
|
||||
}
|
||||
// for (serie in session.data.seriesList) {
|
||||
// option {
|
||||
// if (session.data.book.series != null && serie.id == session.data.book.series!!.id)
|
||||
// selected = true
|
||||
// value = serie.id.toString()
|
||||
// +serie.name
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
div(classes = "flex flex-row mb-5 mr-2 justify-end w-180") {
|
||||
button(classes = "$cssButtonStd block mt-8 mr-10") {
|
||||
|
||||
@@ -20,6 +20,7 @@ suspend fun dbTest(db: Database, booksRepo: BookRepository) {
|
||||
logger.debug("start DB reset - drop")
|
||||
// create if not exists
|
||||
SchemaUtils.drop(
|
||||
SamplerTable,
|
||||
PublisherTable,
|
||||
TagsTable,
|
||||
TagsToBookTable,
|
||||
@@ -32,6 +33,7 @@ suspend fun dbTest(db: Database, booksRepo: BookRepository) {
|
||||
)
|
||||
logger.debug("create tables")
|
||||
SchemaUtils.create(
|
||||
SamplerTable,
|
||||
PublisherTable,
|
||||
TagsTable,
|
||||
TagsToBookTable,
|
||||
|
||||
Reference in new issue
Block a user