diff --git a/src/commonMain/kotlin/de/kif/common/model/Post.kt b/src/commonMain/kotlin/de/kif/common/model/Post.kt index 6b8f1ad..26e4ed6 100644 --- a/src/commonMain/kotlin/de/kif/common/model/Post.kt +++ b/src/commonMain/kotlin/de/kif/common/model/Post.kt @@ -23,6 +23,37 @@ data class Post( ) ) + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as Post + + if (id != other.id) return false + if (name != other.name) return false + if (content != other.content) return false + if (url != other.url) return false + if (image != other.image) return false + if (pinned != other.pinned) return false + if (hideOnProjector != other.hideOnProjector) return false + + return true + } + + fun equalsIgnoreId(other: Post): Boolean = copy(id = null) == other.copy(id = null) + + override fun hashCode(): Int { + var result = id?.hashCode() ?: 0 + result = 31 * result + name.hashCode() + result = 31 * result + content.hashCode() + result = 31 * result + url.hashCode() + result = 31 * result + (image?.hashCode() ?: 0) + result = 31 * result + pinned.hashCode() + result = 31 * result + hideOnProjector.hashCode() + return result + } + + companion object { private const val chars = "abcdefghijklmnopqrstuvwxyz" private const val length = 32 diff --git a/src/commonMain/kotlin/de/kif/common/model/Room.kt b/src/commonMain/kotlin/de/kif/common/model/Room.kt index b76e2e6..f51085f 100644 --- a/src/commonMain/kotlin/de/kif/common/model/Room.kt +++ b/src/commonMain/kotlin/de/kif/common/model/Room.kt @@ -17,6 +17,8 @@ data class Room( override val updateAt: Long = 0 ) : Model { + + override fun createSearch() = SearchElement( mapOf( "name" to name @@ -26,4 +28,36 @@ data class Room( "places" to places.toDouble() ) ) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as Room + + if (id != other.id) return false + if (name != other.name) return false + if (places != other.places) return false + if (projector != other.projector) return false + if (internet != other.internet) return false + if (whiteboard != other.whiteboard) return false + if (blackboard != other.blackboard) return false + if (accessible != other.accessible) return false + + return true + } + + fun equalsIgnoreId(other: Room): Boolean = copy(id = null) == other.copy(id = null) + + override fun hashCode(): Int { + var result = id?.hashCode() ?: 0 + result = 31 * result + name.hashCode() + result = 31 * result + places + result = 31 * result + projector.hashCode() + result = 31 * result + internet.hashCode() + result = 31 * result + whiteboard.hashCode() + result = 31 * result + blackboard.hashCode() + result = 31 * result + accessible.hashCode() + return result + } } diff --git a/src/commonMain/kotlin/de/kif/common/model/Schedule.kt b/src/commonMain/kotlin/de/kif/common/model/Schedule.kt index 2622d61..85662a0 100644 --- a/src/commonMain/kotlin/de/kif/common/model/Schedule.kt +++ b/src/commonMain/kotlin/de/kif/common/model/Schedule.kt @@ -16,6 +16,7 @@ data class Schedule( override val updateAt: Long = 0 ) : Model { + override fun createSearch() = SearchElement( mapOf( "workgroup" to workGroup.name, @@ -29,6 +30,35 @@ data class Schedule( fun getAbsoluteStartTime(): Int = day * 60 * 24 + time fun getAbsoluteEndTime(): Int = getAbsoluteStartTime() + workGroup.length + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as Schedule + + if (id != other.id) return false + if (workGroup != other.workGroup) return false + if (room != other.room) return false + if (day != other.day) return false + if (time != other.time) return false + if (lockRoom != other.lockRoom) return false + if (lockTime != other.lockTime) return false + + return true + } + + fun equalsIgnoreId(other: Schedule): Boolean = copy(id = null) == other.copy(id = null) + + override fun hashCode(): Int { + var result = id?.hashCode() ?: 0 + result = 31 * result + workGroup.hashCode() + result = 31 * result + room.hashCode() + result = 31 * result + day + result = 31 * result + time + result = 31 * result + lockRoom.hashCode() + result = 31 * result + lockTime.hashCode() + return result + } companion object { fun timeOfDayToString(time: Int): String { diff --git a/src/commonMain/kotlin/de/kif/common/model/Track.kt b/src/commonMain/kotlin/de/kif/common/model/Track.kt index d05270a..d6e5dc3 100644 --- a/src/commonMain/kotlin/de/kif/common/model/Track.kt +++ b/src/commonMain/kotlin/de/kif/common/model/Track.kt @@ -12,9 +12,33 @@ data class Track( override val updateAt: Long = 0 ) : Model { + + override fun createSearch() = SearchElement( mapOf( "name" to name ) ) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as Track + + if (id != other.id) return false + if (name != other.name) return false + if (color != other.color) return false + + return true + } + + fun equalsIgnoreId(other: Track): Boolean = copy(id = null) == other.copy(id = null) + + override fun hashCode(): Int { + var result = id?.hashCode() ?: 0 + result = 31 * result + name.hashCode() + result = 31 * result + color.hashCode() + return result + } } diff --git a/src/commonMain/kotlin/de/kif/common/model/User.kt b/src/commonMain/kotlin/de/kif/common/model/User.kt index 1b6745a..00e465b 100644 --- a/src/commonMain/kotlin/de/kif/common/model/User.kt +++ b/src/commonMain/kotlin/de/kif/common/model/User.kt @@ -13,6 +13,8 @@ data class User( override val updateAt: Long = 0 ) : Model { + + fun checkPermission(permission: Permission): Boolean { return permission in permissions || Permission.ADMIN in permissions } @@ -22,4 +24,28 @@ data class User( "username" to username ) ) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as User + + if (id != other.id) return false + if (username != other.username) return false + if (password != other.password) return false + if (permissions != other.permissions) return false + + return true + } + + fun equalsIgnoreId(other: User): Boolean = copy(id = null) == other.copy(id = null) + + override fun hashCode(): Int { + var result = id?.hashCode() ?: 0 + result = 31 * result + username.hashCode() + result = 31 * result + password.hashCode() + result = 31 * result + permissions.hashCode() + return result + } } diff --git a/src/commonMain/kotlin/de/kif/common/model/WorkGroup.kt b/src/commonMain/kotlin/de/kif/common/model/WorkGroup.kt index bdd6fed..11b3078 100644 --- a/src/commonMain/kotlin/de/kif/common/model/WorkGroup.kt +++ b/src/commonMain/kotlin/de/kif/common/model/WorkGroup.kt @@ -37,4 +37,50 @@ data class WorkGroup( "length" to length.toDouble() ) ) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as WorkGroup + + if (id != other.id) return false + if (name != other.name) return false + if (description != other.description) return false + if (interested != other.interested) return false + if (track != other.track) return false + if (projector != other.projector) return false + if (resolution != other.resolution) return false + if (internet != other.internet) return false + if (whiteboard != other.whiteboard) return false + if (blackboard != other.blackboard) return false + if (accessible != other.accessible) return false + if (length != other.length) return false + if (language != other.language) return false + if (leader != other.leader) return false + if (constraints != other.constraints) return false + + return true + } + + fun equalsIgnoreId(other: WorkGroup): Boolean = copy(id = null) == other.copy(id = null) + + override fun hashCode(): Int { + var result = id?.hashCode() ?: 0 + result = 31 * result + name.hashCode() + result = 31 * result + description.hashCode() + result = 31 * result + interested + result = 31 * result + (track?.hashCode() ?: 0) + result = 31 * result + projector.hashCode() + result = 31 * result + resolution.hashCode() + result = 31 * result + internet.hashCode() + result = 31 * result + whiteboard.hashCode() + result = 31 * result + blackboard.hashCode() + result = 31 * result + accessible.hashCode() + result = 31 * result + length + result = 31 * result + language.hashCode() + result = 31 * result + leader.hashCode() + result = 31 * result + constraints.hashCode() + return result + } } diff --git a/src/jsMain/kotlin/de/kif/frontend/views/board/Board.kt b/src/jsMain/kotlin/de/kif/frontend/views/board/Board.kt index dba753f..f3cd636 100644 --- a/src/jsMain/kotlin/de/kif/frontend/views/board/Board.kt +++ b/src/jsMain/kotlin/de/kif/frontend/views/board/Board.kt @@ -1,7 +1,8 @@ package de.kif.frontend.views.board -import de.kif.common.formatDateTime -import de.westermann.kwebview.iterator +import com.soywiz.klock.DateFormat +import com.soywiz.klock.KlockLocale +import com.soywiz.klock.locale.german import de.westermann.kwebview.interval import org.w3c.dom.HTMLElement import org.w3c.dom.get @@ -9,21 +10,29 @@ import kotlin.browser.document import kotlin.js.Date fun initBoard() { - val boardSchedules = document.getElementsByClassName("board-schedules")[0] as HTMLElement + //val boardSchedules = document.getElementsByClassName("board-schedules")[0] as HTMLElement val dateView = document.getElementsByClassName("board-header-date")[0] as HTMLElement - val referenceTime = boardSchedules.dataset["reference"]?.toLongOrNull() ?: 0L + //val referenceTime = boardSchedules.dataset["reference"]?.toLongOrNull() ?: 0L + /* val scheduleList = mutableListOf() for (bs in boardSchedules.getElementsByClassName("board-schedule").iterator()) { scheduleList += BoardSchedule(bs) } + */ interval(1000) { - val currentTime = Date.now().toLong() - dateView.innerText = formatDateTime(currentTime) + val currentTime = Date().let { + it.getHours().toString().padStart(2, '0') + ":" + it.getMinutes().toString().padStart(2, '0') + } + + dateView.innerText = currentTime + + /* val now = referenceTime - currentTime / 1000 scheduleList.forEach { it.updateTime(now) } + */ } } \ No newline at end of file diff --git a/src/jsMain/kotlin/de/kif/frontend/views/calendar/Calendar.kt b/src/jsMain/kotlin/de/kif/frontend/views/calendar/Calendar.kt index 1e21537..47c1121 100644 --- a/src/jsMain/kotlin/de/kif/frontend/views/calendar/Calendar.kt +++ b/src/jsMain/kotlin/de/kif/frontend/views/calendar/Calendar.kt @@ -13,33 +13,43 @@ import kotlin.browser.window class Calendar(calendar: HTMLElement) : View(calendar) { + var autoScroll = true + val day: Int = calendar.dataset["day"]?.toIntOrNull() ?: -1 - private val htmlTag = document.body as HTMLElement val calendarTable = calendar.getElementsByClassName("calendar-table")[0] as HTMLElement - val calendarTableHeader = calendar.getElementsByClassName("calendar-header")[0] as HTMLElement + private val calendarTableHeader = calendar.getElementsByClassName("calendar-header")[0] as HTMLElement fun scrollVerticalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) { - htmlTag.scrollBy(ScrollToOptions(0.0, pixel, scrollBehavior)) + scrollAllVerticalBy(pixel, scrollBehavior) } fun scrollHorizontalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) { - calendarTable.scrollBy(ScrollToOptions(pixel, 0.0, scrollBehavior)) + scrollAllHorizontalBy(pixel, scrollBehavior) } fun scrollVerticalTo(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) { - htmlTag.scrollTo(ScrollToOptions(0.0, pixel, scrollBehavior)) + scrollAllVerticalTo(pixel, scrollBehavior) } fun scrollHorizontalTo(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) { - calendarTable.scrollTo(ScrollToOptions(pixel, 0.0, scrollBehavior)) + scrollAllHorizontalTo(pixel, scrollBehavior) } val editable = calendar.dataset["editable"]?.toBoolean() ?: false val body = CalendarBody(this, calendar.getElementsByClassName("calendar-body")[0] as HTMLElement) + val orientation: Orientation = if (document.getElementsByClassName("time-to-room").length > 0) { + Orientation.TIME_TO_ROOM + } else { + Orientation.ROOM_TO_TIME + } + + init { + scroll += calendarTable + if (editable) { CalendarEdit(this, calendar.querySelector(".calendar-edit") as HTMLElement) } @@ -60,6 +70,8 @@ class Calendar(calendar: HTMLElement) : View(calendar) { } onWheel { + autoScroll = false + val multiplier = when (it.deltaMode) { 1 -> 16.0 2 -> window.innerHeight.toDouble() @@ -95,8 +107,56 @@ class Calendar(calendar: HTMLElement) : View(calendar) { } } } + + enum class Orientation { + + /** + * Columns contains time + * Rows contains rooms + * + * Like the old kif tool + */ + TIME_TO_ROOM, + + /** + * Columns contains rooms + * Rows contains time + * + * Like the congress schedule + */ + ROOM_TO_TIME + } + + companion object { + private var scroll = listOf() + + private fun scrollAllVerticalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) { + println("scroll ${scroll.size} elemenets") + for (calendarTable in scroll) { + calendarTable.scrollBy(ScrollToOptions(0.0, pixel, scrollBehavior)) + } + } + + private fun scrollAllHorizontalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) { + for (calendarTable in scroll) { + calendarTable.scrollBy(ScrollToOptions(pixel, 0.0, scrollBehavior)) + } + } + + private fun scrollAllVerticalTo(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) { + for (calendarTable in scroll) { + calendarTable.scrollTo(ScrollToOptions(0.0, pixel, scrollBehavior)) + } + } + + private fun scrollAllHorizontalTo(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) { + for (calendarTable in scroll) { + calendarTable.scrollTo(ScrollToOptions(pixel, 0.0, scrollBehavior)) + } + } + } } fun initCalendar() { - Calendar(document.getElementsByClassName("calendar")[0] as? HTMLElement ?: return) + document.getElementsByClassName("calendar").iterator().forEach { Calendar(it) } } \ No newline at end of file diff --git a/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarBody.kt b/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarBody.kt index 449a0bb..082b490 100644 --- a/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarBody.kt +++ b/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarBody.kt @@ -3,9 +3,13 @@ package de.kif.frontend.views.calendar import de.kif.frontend.launch import de.kif.frontend.repository.ScheduleRepository import de.westermann.kwebview.ViewCollection +import de.westermann.kwebview.async import de.westermann.kwebview.interval import de.westermann.kwebview.iterator import org.w3c.dom.HTMLElement +import org.w3c.dom.INSTANT +import org.w3c.dom.SMOOTH +import org.w3c.dom.ScrollBehavior import kotlin.browser.document import kotlin.js.Date import kotlin.math.max @@ -81,6 +85,44 @@ class CalendarBody(val calendar: Calendar, view: HTMLElement) : ViewCollection + + + + + + \ No newline at end of file diff --git a/src/jsMain/resources/style/components/_board.scss b/src/jsMain/resources/style/components/_board.scss index 3ef7bc1..716faf3 100644 --- a/src/jsMain/resources/style/components/_board.scss +++ b/src/jsMain/resources/style/components/_board.scss @@ -1,90 +1,73 @@ @import "../config"; -.board-header { - line-height: 3rem; - flex-grow: 1; - font-family: "Bungee", sans-serif; - font-weight: normal; - font-size: 1.1rem; - padding-left: 0.3rem; -} - -.board-card { - background-color: var(--background-card-color); - box-shadow: 0 0.1rem 0.2rem var(--shadow-color); - border-radius: $border-radius; - overflow: hidden; -} - .board { - padding: 1rem 0.4rem 2rem; - display: flex; + top: 0; + left: 0; + position: absolute; + width: 100%; + height: 100%; overflow: hidden; - height: calc(100vh - 0.5rem); & > div { - flex-grow: 1; - flex-basis: 0; - padding: 0 0.4rem; - - &:nth-child(1) { - flex-grow: 5; - } - - &:nth-child(2) { - flex-grow: 4; - } - - &:nth-child(3) { - flex-grow: 4; - } - } -} - -.board-twitter { - .board-card { - height: calc(100% - 1.3rem); - - & > * { - margin-top: -1px !important; - } - } -} - -.board-post { - margin-bottom: 0.5rem; - - .post-name { - top: 0.5rem; - } - - padding-top: 2.5rem; - padding-bottom: 0.5rem; -} - -.board-schedule-box { - margin-bottom: 0.5rem; - padding: 1rem 1rem 0.5rem; - - table { - border-collapse: collapse; + position: absolute; + left: 0; + top: 0; width: 100%; + height: 100%; + + & > div { + position: relative; + overflow: hidden; + + &:first-child { + width: 70%; + float: left; + } + + &:last-child { + width: 30%; + float: right; + } + } } } -.board-card-header { - font-family: 'Montserrat', sans-serif; - font-weight: 600; - line-height: 1.5rem; +.board-header { + height: 8rem !important; + + & > div { + height: 100%; + } +} + +.board-content { + top: 8rem !important; + bottom: 0; + height: auto !important; +} + +.board-logo { + img { + height: 6rem; + width: 6rem; + position: absolute; + top: 50%; + left: 4rem; + margin-top: -3rem; + margin-left: -3rem; + } +} + +.board-running { + column-count: 2; } .board-schedule { line-height: 1.3rem; height: 2rem; - - &:not(:last-child) { - border-bottom: solid 1px var(--table-border-color) - } + min-width: 10rem; + display: flex; + border-bottom: solid 1px var(--table-border-color) } .board-schedule-color { @@ -112,4 +95,46 @@ width: 4rem; text-align: right; color: var(--text-secondary-color) -} \ No newline at end of file +} + +.board-calendar { + height: 100% !important; + margin-top: 0 !important; + + .calendar-table { + overflow: hidden; + } + + .calendar-table-box { + width: 100%; + } + .calendar-row { + width: 100% !important; + height: 0.7rem !important; + } + .calendar-cell { + &:not(:first-child) { + flex-grow: 1; + flex-shrink: 1; + flex-basis: 0; + } + } + .calendar-entry::after { + content: none; + } +} + +.board-header-date { + position: absolute; + line-height: 2rem; + font-size: 2rem; + top: 50%; + margin-top: -1rem; + left: 8rem; +} + +.board-twitter { + & > * { + margin-top: -1px !important; + } +} diff --git a/src/jsMain/resources/style/components/_calendar.scss b/src/jsMain/resources/style/components/_calendar.scss index c49b293..f2a505c 100644 --- a/src/jsMain/resources/style/components/_calendar.scss +++ b/src/jsMain/resources/style/components/_calendar.scss @@ -50,15 +50,16 @@ width: 100%; position: relative; margin-top: 1rem; + height: 100vh; } .calendar-table { width: 100%; - overflow-x: scroll; - overflow-y: visible; + overflow: scroll; padding-bottom: 1rem; position: relative; transition: width $transitionTime; + height: 100%; } .calendar-edit { @@ -301,7 +302,6 @@ flex-wrap: nowrap; flex-direction: column; width: max-content; - height: max-content; .calendar-header, .calendar-row { display: flex; @@ -325,10 +325,15 @@ line-height: 2rem; height: 2rem; width: 100%; + position: sticky; + top: 0; + background-color: var(--background-secondary-color); + z-index: 5; + border-bottom: solid 1px var(--table-border-color); .calendar-cell:first-child { - flex-grow: 1; text-align: center; + width: 6rem; } .calendar-cell:not(:first-child) { @@ -347,6 +352,10 @@ } } + .calendar-body { + margin-top: -1px; + } + .calendar-row { line-height: 2rem; height: 1.3rem; @@ -438,6 +447,7 @@ } .calendar-header { + .calendar-cell { position: relative; width: 6rem; diff --git a/src/jsMain/resources/style/components/_wall.scss b/src/jsMain/resources/style/components/_wall.scss new file mode 100644 index 0000000..9287fb7 --- /dev/null +++ b/src/jsMain/resources/style/components/_wall.scss @@ -0,0 +1,78 @@ +@import "../config"; + +.wall { + top: 0; + left: 0; + position: absolute; + width: 100%; + height: 100%; + overflow: hidden; +} + +.wall-box { + width: 100%; + height: calc(33.3333% - 1rem); + overflow: hidden; + border-bottom: solid 2px var(--input-border-color); + + &:first-child { + height: calc(33.3333% + 2rem); + } + + &:not(:first-child) { + .calendar-row, .calendar-header { + .calendar-cell:first-child { + display: none; + } + } + } + + .calendar-row, .calendar-header { + height: 100%; + display: flex; + flex-direction: column; + line-height: 2rem; + } + + .calendar-entry { + top: 0 !important; + margin-top: -0.5rem !important; + bottom: 0 !important; + } + .calendar-entry::after { + content: none; + } + + .calendar-table-box { + height: 100%; + } + + .calendar-body { + display: flex; + } + + .calendar-row { + width: 100%; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 1; + } + + .calendar-cell { + flex-basis: 0; + flex-grow: 1; + flex-shrink: 1; + line-height: 2rem; + width: 100%; + } +} + +.wall-calendar { + margin-top: 0 !important; + height: 100% !important; + + .calendar-table { + overflow: hidden; + padding: 0; + } +} \ No newline at end of file diff --git a/src/jsMain/resources/style/style.scss b/src/jsMain/resources/style/style.scss index 6279491..108d162 100644 --- a/src/jsMain/resources/style/style.scss +++ b/src/jsMain/resources/style/style.scss @@ -3,12 +3,13 @@ @include color-setting; -@import "components/board"; @import "components/calendar"; @import "components/form"; @import "components/menu"; @import "components/overview"; @import "components/table-layout"; +@import "components/board"; +@import "components/wall"; body, html { color: var(--text-primary-color); @@ -143,6 +144,7 @@ a { margin-top: -2rem; position: absolute; top: 48%; + left: 0; span { display: block; diff --git a/src/jvmMain/kotlin/de/kif/backend/Application.kt b/src/jvmMain/kotlin/de/kif/backend/Application.kt index ab3eabc..9a977cc 100644 --- a/src/jvmMain/kotlin/de/kif/backend/Application.kt +++ b/src/jvmMain/kotlin/de/kif/backend/Application.kt @@ -64,6 +64,7 @@ fun Application.main() { account() board() + wall() workGroup() track() diff --git a/src/jvmMain/kotlin/de/kif/backend/route/Board.kt b/src/jvmMain/kotlin/de/kif/backend/route/Board.kt index 2323162..fc12978 100644 --- a/src/jvmMain/kotlin/de/kif/backend/route/Board.kt +++ b/src/jvmMain/kotlin/de/kif/backend/route/Board.kt @@ -2,6 +2,7 @@ package de.kif.backend.route import de.kif.backend.Configuration import de.kif.backend.repository.PostRepository +import de.kif.backend.repository.RoomRepository import de.kif.backend.repository.ScheduleRepository import de.kif.backend.view.respondMain import de.kif.common.model.Schedule @@ -11,10 +12,11 @@ import kotlinx.css.CSSBuilder import kotlinx.css.Color import kotlinx.html.* import java.util.* +import kotlin.math.max +import kotlin.math.min fun Route.board() { get("/brett") { - val postList = PostRepository.all().asReversed() val scheduleList = ScheduleRepository.all().map { it to it.getAbsoluteStartTime() * 60 }.sortedBy { it.second } @@ -22,53 +24,114 @@ fun Route.board() { val referenceTime = Configuration.Schedule.referenceDate.time / 1000 val now = referenceTime - (Date().time / 1000) + val refDate = Configuration.Schedule.referenceDate + val todayDate = Date() + + val refDay = refDate.time / (1000 * 60 * 60 * 24) + val todayDay = todayDate.time / (1000 * 60 * 60 * 24) + val day = (todayDay - refDay).toInt() + + val list = ScheduleRepository.getByDay(day) + val rooms = RoomRepository.all() + val schedules = list.groupBy { it.room }.mapValues { (_, it) -> + it.associateBy { + it.time + } + } + + var max = 0 + var min = 24 * 60 + for (s in list) { + max = max(max, s.time + s.workGroup.length) + min = min(min, s.time) + } + + if (min > max) { + val h1 = max + max = min + min = h1 + } + + if (true) { + min = min(min, 0) + max = max(max, 24 * 60) + } + + min = (min / 60 - 1) * 60 + max = (max / 60 + 2) * 60 + respondMain(true, true) { theme -> content { div("board") { - div("board-schedules") { - attributes["data-reference"] = referenceTime.toString() - - div("board-header") { - +"Arbeitskreise" - } - - div("board-card board-schedule-box") { - div("board-card-header") { - +"Aktuell" - } - table { + div("board-header") { + div("board-running") { for ((schedule, time) in scheduleList) { - createBoardSchedule(schedule, time) - } - } - } + div("board-schedule") { + attributes["data-id"] = schedule.id.toString() - div("board-card board-schedule-box") { - div("board-card-header") { - +"Später" - } - table { - for ((schedule, time) in scheduleList) { - createBoardSchedule(schedule, time) + div("board-schedule-color") { + span { + attributes["style"] = CSSBuilder().apply { + val c = schedule.workGroup.track?.color + if (c != null) { + backgroundColor = Color(c.toString()) + } + }.toString() + } + } + + div("board-schedule-time") { + attributes["data-time"] = time.toString() + attributes["data-duration"] = schedule.workGroup.length.toString() + + + val startTime = (time % MINUTES_OF_DAY).let { + if (it < 0) it + MINUTES_OF_DAY else it + } + val sm = (startTime % 60).toString().padStart(2, '0') + val sh = (startTime / 60).toString().padStart(2, '0') + val startTimeString = "$sh:$sm" + + val endTime = ((time + schedule.workGroup.length) % MINUTES_OF_DAY).let { + if (it < 0) it + MINUTES_OF_DAY else it + } + val em = (endTime % 60).toString().padStart(2, '0') + val eh = (endTime / 60).toString().padStart(2, '0') + val endTimeString = "$eh:$em" + + +"$startTimeString - $endTimeString" + } + + div("board-schedule-name") { + +schedule.workGroup.name + } + + div("board-schedule-room") { + +schedule.room.name + } } } } - } - - div("board-posts") { - div("board-header") { - +"Neuigkeiten" - } - for (post in postList) { - createPost(post, false, "board-card board-post") + div("board-logo") { + img("KIF 47.0", "/static/images/logo.svg") + div("board-header-date") { + } } } - - div("board-twitter") { - div("board-header") { - +"Twitter" + div("board-content") { + div("board-calendar calendar") { + div("calendar-table") { + renderCalendar( + CalendarOrientation.ROOM_TO_TIME, + day, + min, + max, + rooms, + schedules + ) + } } - div("board-card") { + div("board-twitter") { unsafe { raw(""" >, + val max: Int, + val min: Int +) + +suspend fun genWallData(day: Int): WallData { + val list = ScheduleRepository.getByDay(day) + val schedules = RoomRepository.all().associateWith { emptyMap() } + list.groupBy { it.room }.mapValues { (_, it) -> + it.associateBy { + it.time + } + } + + var max = 0 + var min = 24 * 60 + for (s in list) { + max = max(max, s.time + s.workGroup.length) + min = min(min, s.time) + } + + if (min > max) { + val h1 = max + max = min + min = h1 + } + + min = (min / 60 - 1) * 60 + max = (max / 60 + 2) * 60 + + return WallData(day, schedules, max, min) +} + +fun Route.wall() { + get("/wand") { + + val days = (0..2).map { genWallData(it) } + + val min = days.map { it.min }.min() ?: days.first().min + val max = days.map { it.max }.max() ?: days.first().max + + respondMain(true, true) { + content { + div("wall") { + for (day in days) { + div("wall-box") { + div("wall-calendar calendar") { + div("calendar-table") { + renderCalendar( + CalendarOrientation.TIME_TO_ROOM, + day.number, + min, + max, + day.schedules.keys.toList().sortedBy { it.id }, + day.schedules + ) + } + } + } + } + } + } + } + } +} diff --git a/src/jvmMain/kotlin/de/kif/backend/util/Backup.kt b/src/jvmMain/kotlin/de/kif/backend/util/Backup.kt index 8a96cb1..b9f758f 100644 --- a/src/jvmMain/kotlin/de/kif/backend/util/Backup.kt +++ b/src/jvmMain/kotlin/de/kif/backend/util/Backup.kt @@ -49,33 +49,62 @@ data class Backup( suspend fun import(data: String) { val backup = Message.json.parse(serializer(), data) - backup.users.forEach { UserRepository.create(it) } - backup.posts.forEach { PostRepository.create(it) } + backup.users.forEach { UserRepository.create(it); println("Import user ${it.username}") } + backup.posts.forEach { PostRepository.create(it); println("Import post") } - backup.rooms.forEach { RoomRepository.create(it) } - val roomMap = RoomRepository.all().associateWith { it.id!! } + val oldRooms = RoomRepository.all() + val newRooms = backup.rooms.filterNot { oldRooms.any { i -> i.equalsIgnoreId(it) } } + val roomMap = (oldRooms.associateWith { it.id!! } + + newRooms.map { + val id = RoomRepository.create(it) + println("Import room ${it.name}") + it to id + }).toList() - backup.tracks.forEach { TrackRepository.create(it) } - val trackMap =TrackRepository.all().associateWith { it.id!! } + val oldTracks = TrackRepository.all() + val newTracks = backup.tracks.filterNot { oldTracks.any { i -> i.equalsIgnoreId(it) } } + val trackMap = (oldTracks.associateWith { it.id!! } + + newTracks.map { + val id = TrackRepository.create(it) + println("Import track ${it.name}") + it to id + }).toList() - backup.workGroups.forEach { - var workGroup = it - val track = workGroup.track - if (track != null) { - workGroup = workGroup.copy(track = track.copy(id = trackMap[track] ?: return@forEach)) - } + val oldWorkGroups = WorkGroupRepository.all() + val newWorkGroups = backup.workGroups.filterNot { oldWorkGroups.any { i -> i.equalsIgnoreId(it) } } + val workGroupMap = (oldWorkGroups.associateWith { it.id!! } + + newWorkGroups.mapNotNull { + var workGroup = it + val track = workGroup.track + if (track != null) { + workGroup = workGroup.copy(track = track.copy(id = trackMap.firstOrNull { (i,_) -> i.equalsIgnoreId(track) }?.second ?: run { + println("Cannot import work group, due to missing track") + return@mapNotNull null + })) + } - WorkGroupRepository.create(workGroup) - } - val workGroupMap = WorkGroupRepository.all().associateWith { it.id!! } + val id = WorkGroupRepository.create(workGroup) + println("Import work group ${it.name}") + it to id + }).toList() - backup.schedules.forEach { + val oldSchedules = ScheduleRepository.all() + val newSchedules = backup.schedules.filterNot { oldSchedules.any { i -> i.equalsIgnoreId(it) } } + newSchedules.forEach { ScheduleRepository.create( it.copy( - room = it.room.copy(id = roomMap[it.room] ?: return@forEach), - workGroup = it.workGroup.copy(id = workGroupMap[it.workGroup] ?: return@forEach) + room = it.room.copy(id = roomMap.firstOrNull { (i,_) -> i.equalsIgnoreId(it.room) }?.second ?: run { + println("Cannot import schedule, due to missing room") + return@forEach + }), + workGroup = it.workGroup.copy(id = workGroupMap.firstOrNull { (i,_) -> i.equalsIgnoreId(it.workGroup) }?.second ?: run { + println("Cannot import schedule, due to missing work group") + return@forEach + }) ) ) + + println("Import schedule day=${it.day}, time=${it.time}, work group=${it.workGroup.name}") } }