Compare commits

..

No commits in common. "19956ebafb929769a8d6b7c5c653d8b6e583e956" and "f5b937a293bc481e399f8de3294db96be31cd5ba" have entirely different histories.

23 changed files with 213 additions and 756 deletions

View file

@ -3,7 +3,7 @@ host = "localhost"
port = 8080 port = 8080
[schedule] [schedule]
reference = "2019-06-12" reference = "2019-06-06"
[general] [general]
wiki_url = "https://wiki.kif.rocks/w/index.php?title=KIF470:Arbeitskreise&action=raw" wiki_url = "https://wiki.kif.rocks/w/index.php?title=KIF470:Arbeitskreise&action=raw"

View file

@ -23,37 +23,6 @@ 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 { companion object {
private const val chars = "abcdefghijklmnopqrstuvwxyz" private const val chars = "abcdefghijklmnopqrstuvwxyz"
private const val length = 32 private const val length = 32

View file

@ -17,8 +17,6 @@ data class Room(
override val updateAt: Long = 0 override val updateAt: Long = 0
) : Model { ) : Model {
override fun createSearch() = SearchElement( override fun createSearch() = SearchElement(
mapOf( mapOf(
"name" to name "name" to name
@ -28,36 +26,4 @@ data class Room(
"places" to places.toDouble() "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
}
} }

View file

@ -16,7 +16,6 @@ data class Schedule(
override val updateAt: Long = 0 override val updateAt: Long = 0
) : Model { ) : Model {
override fun createSearch() = SearchElement( override fun createSearch() = SearchElement(
mapOf( mapOf(
"workgroup" to workGroup.name, "workgroup" to workGroup.name,
@ -30,35 +29,6 @@ data class Schedule(
fun getAbsoluteStartTime(): Int = day * 60 * 24 + time fun getAbsoluteStartTime(): Int = day * 60 * 24 + time
fun getAbsoluteEndTime(): Int = getAbsoluteStartTime() + workGroup.length 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 { companion object {
fun timeOfDayToString(time: Int): String { fun timeOfDayToString(time: Int): String {

View file

@ -12,33 +12,9 @@ data class Track(
override val updateAt: Long = 0 override val updateAt: Long = 0
) : Model { ) : Model {
override fun createSearch() = SearchElement( override fun createSearch() = SearchElement(
mapOf( mapOf(
"name" to name "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
}
} }

View file

@ -13,8 +13,6 @@ data class User(
override val updateAt: Long = 0 override val updateAt: Long = 0
) : Model { ) : Model {
fun checkPermission(permission: Permission): Boolean { fun checkPermission(permission: Permission): Boolean {
return permission in permissions || Permission.ADMIN in permissions return permission in permissions || Permission.ADMIN in permissions
} }
@ -24,28 +22,4 @@ data class User(
"username" to username "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
}
} }

View file

@ -37,50 +37,4 @@ data class WorkGroup(
"length" to length.toDouble() "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
}
} }

View file

@ -1,8 +1,7 @@
package de.kif.frontend.views.board package de.kif.frontend.views.board
import com.soywiz.klock.DateFormat import de.kif.common.formatDateTime
import com.soywiz.klock.KlockLocale import de.westermann.kwebview.iterator
import com.soywiz.klock.locale.german
import de.westermann.kwebview.interval import de.westermann.kwebview.interval
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
import org.w3c.dom.get import org.w3c.dom.get
@ -10,29 +9,21 @@ import kotlin.browser.document
import kotlin.js.Date import kotlin.js.Date
fun initBoard() { 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 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<BoardSchedule>() val scheduleList = mutableListOf<BoardSchedule>()
for (bs in boardSchedules.getElementsByClassName("board-schedule").iterator()) { for (bs in boardSchedules.getElementsByClassName("board-schedule").iterator()) {
scheduleList += BoardSchedule(bs) scheduleList += BoardSchedule(bs)
} }
*/
interval(1000) { 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 val now = referenceTime - currentTime / 1000
scheduleList.forEach { it.updateTime(now) } scheduleList.forEach { it.updateTime(now) }
*/
} }
} }

View file

@ -13,43 +13,33 @@ import kotlin.browser.window
class Calendar(calendar: HTMLElement) : View(calendar) { class Calendar(calendar: HTMLElement) : View(calendar) {
var autoScroll = true
val day: Int = calendar.dataset["day"]?.toIntOrNull() ?: -1 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 calendarTable = calendar.getElementsByClassName("calendar-table")[0] as HTMLElement
private val calendarTableHeader = calendar.getElementsByClassName("calendar-header")[0] as HTMLElement val calendarTableHeader = calendar.getElementsByClassName("calendar-header")[0] as HTMLElement
fun scrollVerticalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) { fun scrollVerticalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
scrollAllVerticalBy(pixel, scrollBehavior) htmlTag.scrollBy(ScrollToOptions(0.0, pixel, scrollBehavior))
} }
fun scrollHorizontalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) { fun scrollHorizontalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
scrollAllHorizontalBy(pixel, scrollBehavior) calendarTable.scrollBy(ScrollToOptions(pixel, 0.0, scrollBehavior))
} }
fun scrollVerticalTo(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) { fun scrollVerticalTo(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
scrollAllVerticalTo(pixel, scrollBehavior) htmlTag.scrollTo(ScrollToOptions(0.0, pixel, scrollBehavior))
} }
fun scrollHorizontalTo(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) { fun scrollHorizontalTo(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
scrollAllHorizontalTo(pixel, scrollBehavior) calendarTable.scrollTo(ScrollToOptions(pixel, 0.0, scrollBehavior))
} }
val editable = calendar.dataset["editable"]?.toBoolean() ?: false val editable = calendar.dataset["editable"]?.toBoolean() ?: false
val body = CalendarBody(this, calendar.getElementsByClassName("calendar-body")[0] as HTMLElement) 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 { init {
scroll += calendarTable
if (editable) { if (editable) {
CalendarEdit(this, calendar.querySelector(".calendar-edit") as HTMLElement) CalendarEdit(this, calendar.querySelector(".calendar-edit") as HTMLElement)
} }
@ -70,8 +60,6 @@ class Calendar(calendar: HTMLElement) : View(calendar) {
} }
onWheel { onWheel {
autoScroll = false
val multiplier = when (it.deltaMode) { val multiplier = when (it.deltaMode) {
1 -> 16.0 1 -> 16.0
2 -> window.innerHeight.toDouble() 2 -> window.innerHeight.toDouble()
@ -107,56 +95,8 @@ 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<HTMLElement>()
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() { fun initCalendar() {
document.getElementsByClassName("calendar").iterator().forEach { Calendar(it) } Calendar(document.getElementsByClassName("calendar")[0] as? HTMLElement ?: return)
} }

View file

@ -3,13 +3,9 @@ package de.kif.frontend.views.calendar
import de.kif.frontend.launch import de.kif.frontend.launch
import de.kif.frontend.repository.ScheduleRepository import de.kif.frontend.repository.ScheduleRepository
import de.westermann.kwebview.ViewCollection import de.westermann.kwebview.ViewCollection
import de.westermann.kwebview.async
import de.westermann.kwebview.interval import de.westermann.kwebview.interval
import de.westermann.kwebview.iterator import de.westermann.kwebview.iterator
import org.w3c.dom.HTMLElement 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.browser.document
import kotlin.js.Date import kotlin.js.Date
import kotlin.math.max import kotlin.math.max
@ -85,44 +81,6 @@ class CalendarBody(val calendar: Calendar, view: HTMLElement) : ViewCollection<C
} }
} }
fun update(scroll: ScrollBehavior) {
val currentTime = Date().let {
it.getHours() * 60 + it.getMinutes()
}
val rowTime = (currentTime / 15) * 15
var activeRow: CalendarRow? = null
for (row in this) {
if (row.time == rowTime) {
row.classList.clear()
for (str in row.classList) {
if ("now" in str) {
row.classList -= str
}
}
row.classList += "calendar-row"
row.classList += "calendar-now"
row.classList += "calendar-now-${currentTime - rowTime}"
activeRow = row
} else {
for (str in row.classList) {
if ("now" in str) {
row.classList -= str
}
}
}
}
if (calendar.autoScroll && activeRow != null) {
if (calendar.orientation == Calendar.Orientation.ROOM_TO_TIME) {
calendar.scrollVerticalTo((activeRow.offsetTop).toDouble(), scroll)
} else {
calendar.scrollHorizontalTo((activeRow.offsetLeft - 100).toDouble(), scroll)
}
}
}
init { init {
calendarEntries = document.getElementsByClassName("calendar-entry") calendarEntries = document.getElementsByClassName("calendar-entry")
.iterator().asSequence().map { CalendarEntry(this, it) }.toList() .iterator().asSequence().map { CalendarEntry(this, it) }.toList()
@ -160,7 +118,6 @@ class CalendarBody(val calendar: Calendar, view: HTMLElement) : ViewCollection<C
updateRows() updateRows()
} }
} }
ScheduleRepository.onDelete { ScheduleRepository.onDelete {
for (entry in calendarEntries) { for (entry in calendarEntries) {
if (entry.scheduleId == it) { if (entry.scheduleId == it) {
@ -174,12 +131,31 @@ class CalendarBody(val calendar: Calendar, view: HTMLElement) : ViewCollection<C
} }
} }
async {
update(ScrollBehavior.INSTANT)
}
interval(1000) { interval(1000) {
update(ScrollBehavior.SMOOTH) val currentTime = Date().let {
it.getHours() * 60 + it.getMinutes()
}
val rowTime = (currentTime / 15) * 15
for (row in this) {
if (row.time == rowTime) {
row.classList.clear()
for (str in row.classList) {
if ("now" in str) {
row.classList -= str
}
}
row.classList += "calendar-row"
row.classList += "calendar-now"
row.classList += "calendar-now-${currentTime - rowTime}"
} else {
for (str in row.classList) {
if ("now" in str) {
row.classList -= str
}
}
}
}
} }
} }
} }

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,73 +1,90 @@
@import "../config"; @import "../config";
.board { .board-header {
top: 0; line-height: 3rem;
left: 0; flex-grow: 1;
position: absolute; font-family: "Bungee", sans-serif;
width: 100%; font-weight: normal;
height: 100%; 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; overflow: hidden;
}
.board {
padding: 1rem 0.4rem 2rem;
display: flex;
overflow: hidden;
height: calc(100vh - 0.5rem);
& > div { & > div {
position: absolute; flex-grow: 1;
left: 0; flex-basis: 0;
top: 0; padding: 0 0.4rem;
width: 100%;
height: 100%;
& > div { &:nth-child(1) {
position: relative; flex-grow: 5;
overflow: hidden; }
&:first-child { &:nth-child(2) {
width: 70%; flex-grow: 4;
float: left; }
}
&:last-child { &:nth-child(3) {
width: 30%; flex-grow: 4;
float: right;
}
} }
} }
} }
.board-header { .board-twitter {
height: 8rem !important; .board-card {
height: calc(100% - 1.3rem);
& > div { & > * {
height: 100%; margin-top: -1px !important;
}
} }
} }
.board-content { .board-post {
top: 8rem !important; margin-bottom: 0.5rem;
bottom: 0;
height: auto !important; .post-name {
top: 0.5rem;
}
padding-top: 2.5rem;
padding-bottom: 0.5rem;
} }
.board-logo { .board-schedule-box {
img { margin-bottom: 0.5rem;
height: 6rem; padding: 1rem;
width: 6rem; padding-bottom: 0.5rem;
position: absolute;
top: 50%; table {
left: 4rem; border-collapse: collapse;
margin-top: -3rem;
margin-left: -3rem;
} }
} }
.board-running { .board-card-header {
column-count: 2; font-family: 'Montserrat', sans-serif;
font-weight: 600;
line-height: 1.5rem;
} }
.board-schedule { .board-schedule {
line-height: 1.3rem; line-height: 1.3rem;
height: 2rem; height: 2rem;
min-width: 10rem;
display: flex; &:not(:last-child) {
border-bottom: solid 1px var(--table-border-color) border-bottom: solid 1px var(--table-border-color)
}
} }
.board-schedule-color { .board-schedule-color {
@ -95,46 +112,4 @@
width: 4rem; width: 4rem;
text-align: right; text-align: right;
color: var(--text-secondary-color) color: var(--text-secondary-color)
} }
.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;
}
}

View file

@ -50,16 +50,15 @@
width: 100%; width: 100%;
position: relative; position: relative;
margin-top: 1rem; margin-top: 1rem;
height: 100vh;
} }
.calendar-table { .calendar-table {
width: 100%; width: 100%;
overflow: scroll; overflow-x: scroll;
overflow-y: visible;
padding-bottom: 1rem; padding-bottom: 1rem;
position: relative; position: relative;
transition: width $transitionTime; transition: width $transitionTime;
height: 100%;
} }
.calendar-edit { .calendar-edit {
@ -302,6 +301,7 @@
flex-wrap: nowrap; flex-wrap: nowrap;
flex-direction: column; flex-direction: column;
width: max-content; width: max-content;
height: max-content;
.calendar-header, .calendar-row { .calendar-header, .calendar-row {
display: flex; display: flex;
@ -325,15 +325,10 @@
line-height: 2rem; line-height: 2rem;
height: 2rem; height: 2rem;
width: 100%; 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 { .calendar-cell:first-child {
flex-grow: 1;
text-align: center; text-align: center;
width: 6rem;
} }
.calendar-cell:not(:first-child) { .calendar-cell:not(:first-child) {
@ -352,10 +347,6 @@
} }
} }
.calendar-body {
margin-top: -1px;
}
.calendar-row { .calendar-row {
line-height: 2rem; line-height: 2rem;
height: 1.3rem; height: 1.3rem;
@ -447,7 +438,6 @@
} }
.calendar-header { .calendar-header {
.calendar-cell { .calendar-cell {
position: relative; position: relative;
width: 6rem; width: 6rem;

View file

@ -51,7 +51,6 @@ select:-moz-focusring {
line-height: 2.5rem; line-height: 2.5rem;
margin-left: 1rem; margin-left: 1rem;
width: 100%; width: 100%;
top: 1.6rem;
} }
} }
@ -186,13 +185,10 @@ form {
white-space: nowrap; white-space: nowrap;
} }
.form-control {
flex-grow: 1;
flex-basis: 0;
}
& > * { & > * {
margin-right: 0; margin-right: 0;
flex-grow: 1;
flex-basis: 0;
&:not(:first-child) { &:not(:first-child) {
border-top-left-radius: 0; border-top-left-radius: 0;

View file

@ -108,7 +108,6 @@
table { table {
border-collapse: collapse; border-collapse: collapse;
border-spacing: 0; border-spacing: 0;
width: 100%;
} }
td { td {

View file

@ -1,78 +0,0 @@
@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;
}
}

View file

@ -3,13 +3,12 @@
@include color-setting; @include color-setting;
@import "components/board";
@import "components/calendar"; @import "components/calendar";
@import "components/form"; @import "components/form";
@import "components/menu"; @import "components/menu";
@import "components/overview"; @import "components/overview";
@import "components/table-layout"; @import "components/table-layout";
@import "components/board";
@import "components/wall";
body, html { body, html {
color: var(--text-primary-color); color: var(--text-primary-color);
@ -144,7 +143,6 @@ a {
margin-top: -2rem; margin-top: -2rem;
position: absolute; position: absolute;
top: 48%; top: 48%;
left: 0;
span { span {
display: block; display: block;

View file

@ -64,7 +64,6 @@ fun Application.main() {
account() account()
board() board()
wall()
workGroup() workGroup()
track() track()

View file

@ -15,13 +15,13 @@ object Configuration {
private val config: Config private val config: Config
private class ConfigDelegate<T>(val item: Item<T>) { class ConfigDelegate<T>(val item: Item<T>) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T { operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return config[item] return config[item]
} }
} }
private fun <T> c(item: Item<T>) = ConfigDelegate(item) fun <T> c(item: Item<T>) = ConfigDelegate(item)
private object ServerSpec : ConfigSpec("server") { private object ServerSpec : ConfigSpec("server") {
val host by required<String>() val host by required<String>()

View file

@ -2,7 +2,6 @@ package de.kif.backend.route
import de.kif.backend.Configuration import de.kif.backend.Configuration
import de.kif.backend.repository.PostRepository import de.kif.backend.repository.PostRepository
import de.kif.backend.repository.RoomRepository
import de.kif.backend.repository.ScheduleRepository import de.kif.backend.repository.ScheduleRepository
import de.kif.backend.view.respondMain import de.kif.backend.view.respondMain
import de.kif.common.model.Schedule import de.kif.common.model.Schedule
@ -12,11 +11,10 @@ import kotlinx.css.CSSBuilder
import kotlinx.css.Color import kotlinx.css.Color
import kotlinx.html.* import kotlinx.html.*
import java.util.* import java.util.*
import kotlin.math.max
import kotlin.math.min
fun Route.board() { fun Route.board() {
get("/brett") { get("/brett") {
val postList = PostRepository.all().asReversed()
val scheduleList = ScheduleRepository.all().map { val scheduleList = ScheduleRepository.all().map {
it to it.getAbsoluteStartTime() * 60 it to it.getAbsoluteStartTime() * 60
}.sortedBy { it.second } }.sortedBy { it.second }
@ -24,114 +22,53 @@ fun Route.board() {
val referenceTime = Configuration.Schedule.referenceDate.time / 1000 val referenceTime = Configuration.Schedule.referenceDate.time / 1000
val now = referenceTime - (Date().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 -> respondMain(true, true) { theme ->
content { content {
div("board") { div("board") {
div("board-header") { div("board-schedules") {
div("board-running") { attributes["data-reference"] = referenceTime.toString()
div("board-header") {
+"Arbeitskreise"
}
div("board-card board-schedule-box") {
div("board-card-header") {
+"Aktuell"
}
table {
for ((schedule, time) in scheduleList) { for ((schedule, time) in scheduleList) {
div("board-schedule") { createBoardSchedule(schedule, time)
attributes["data-id"] = schedule.id.toString() }
}
}
div("board-schedule-color") { div("board-card board-schedule-box") {
span { div("board-card-header") {
attributes["style"] = CSSBuilder().apply { +"Später"
val c = schedule.workGroup.track?.color }
if (c != null) { table {
backgroundColor = Color(c.toString()) for ((schedule, time) in scheduleList) {
} createBoardSchedule(schedule, time)
}.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-logo") { }
img("KIF 47.0", "/static/images/logo.svg")
div("board-header-date") { div("board-posts") {
} div("board-header") {
+"Neuigkeiten"
}
for (post in postList) {
createPost(post, false, "board-card board-post")
} }
} }
div("board-content") {
div("board-calendar calendar") { div("board-twitter") {
div("calendar-table") { div("board-header") {
renderCalendar( +"Twitter"
CalendarOrientation.ROOM_TO_TIME,
day,
min,
max,
rooms,
schedules
)
}
} }
div("board-twitter") { div("board-card") {
unsafe { unsafe {
raw(""" raw("""
<a <a
@ -154,3 +91,50 @@ fun Route.board() {
} }
} }
} }
private fun TABLE.createBoardSchedule(schedule: Schedule, time: Int) {
tr("board-schedule") {
attributes["data-id"] = schedule.id.toString()
td("board-schedule-color") {
span {
attributes["style"] = CSSBuilder().apply {
val c = schedule.workGroup.track?.color
if (c != null) {
backgroundColor = Color(c.toString())
}
}.toString()
}
}
td("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"
}
td("board-schedule-name") {
+schedule.workGroup.name
}
td("board-schedule-room") {
+schedule.room.name
}
}
}

View file

@ -58,7 +58,7 @@ private fun DIV.calendarCell(schedule: Schedule?) {
} }
} }
fun DIV.renderCalendar( private fun DIV.renderCalendar(
orientation: CalendarOrientation, orientation: CalendarOrientation,
day: Int, day: Int,
from: Int, from: Int,
@ -141,14 +141,7 @@ fun DIV.renderCalendar(
fun Route.calendar() { fun Route.calendar() {
get("/calendar") { get("/calendar") {
val refDate = Configuration.Schedule.referenceDate call.respondRedirect("/calendar/0", true)
val todayDate = Date()
val refDay = refDate.time / (1000 * 60 * 60 * 24)
val todayDay = todayDate.time / (1000 * 60 * 60 * 24)
val day = todayDay - refDay
call.respondRedirect("/calendar/$day", false)
} }
get("/calendar/{day}/rtt") { get("/calendar/{day}/rtt") {

View file

@ -1,79 +0,0 @@
package de.kif.backend.route
import de.kif.backend.repository.RoomRepository
import de.kif.backend.repository.ScheduleRepository
import de.kif.backend.view.respondMain
import de.kif.common.model.Room
import de.kif.common.model.Schedule
import io.ktor.routing.Route
import io.ktor.routing.get
import kotlinx.html.div
import kotlin.math.max
import kotlin.math.min
data class WallData(
val number: Int,
val schedules: Map<Room, Map<Int, Schedule>>,
val max: Int,
val min: Int
)
suspend fun genWallData(day: Int): WallData {
val list = ScheduleRepository.getByDay(day)
val schedules = RoomRepository.all().associateWith { emptyMap<Int, Schedule>() } + 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
)
}
}
}
}
}
}
}
}
}

View file

@ -49,62 +49,33 @@ data class Backup(
suspend fun import(data: String) { suspend fun import(data: String) {
val backup = Message.json.parse(serializer(), data) val backup = Message.json.parse(serializer(), data)
backup.users.forEach { UserRepository.create(it); println("Import user ${it.username}") } backup.users.forEach { UserRepository.create(it) }
backup.posts.forEach { PostRepository.create(it); println("Import post") } backup.posts.forEach { PostRepository.create(it) }
val oldRooms = RoomRepository.all() backup.rooms.forEach { RoomRepository.create(it) }
val newRooms = backup.rooms.filterNot { oldRooms.any { i -> i.equalsIgnoreId(it) } } val roomMap = RoomRepository.all().associateWith { it.id!! }
val roomMap = (oldRooms.associateWith { it.id!! } +
newRooms.map {
val id = RoomRepository.create(it)
println("Import room ${it.name}")
it to id
}).toList()
val oldTracks = TrackRepository.all() backup.tracks.forEach { TrackRepository.create(it) }
val newTracks = backup.tracks.filterNot { oldTracks.any { i -> i.equalsIgnoreId(it) } } val trackMap =TrackRepository.all().associateWith { it.id!! }
val trackMap = (oldTracks.associateWith { it.id!! } +
newTracks.map {
val id = TrackRepository.create(it)
println("Import track ${it.name}")
it to id
}).toList()
val oldWorkGroups = WorkGroupRepository.all() backup.workGroups.forEach {
val newWorkGroups = backup.workGroups.filterNot { oldWorkGroups.any { i -> i.equalsIgnoreId(it) } } var workGroup = it
val workGroupMap = (oldWorkGroups.associateWith { it.id!! } + val track = workGroup.track
newWorkGroups.mapNotNull { if (track != null) {
var workGroup = it workGroup = workGroup.copy(track = track.copy(id = trackMap[track] ?: return@forEach))
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
}))
}
val id = WorkGroupRepository.create(workGroup) WorkGroupRepository.create(workGroup)
println("Import work group ${it.name}") }
it to id val workGroupMap = WorkGroupRepository.all().associateWith { it.id!! }
}).toList()
val oldSchedules = ScheduleRepository.all() backup.schedules.forEach {
val newSchedules = backup.schedules.filterNot { oldSchedules.any { i -> i.equalsIgnoreId(it) } }
newSchedules.forEach {
ScheduleRepository.create( ScheduleRepository.create(
it.copy( it.copy(
room = it.room.copy(id = roomMap.firstOrNull { (i,_) -> i.equalsIgnoreId(it.room) }?.second ?: run { room = it.room.copy(id = roomMap[it.room] ?: return@forEach),
println("Cannot import schedule, due to missing room") workGroup = it.workGroup.copy(id = workGroupMap[it.workGroup] ?: return@forEach)
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}")
} }
} }