Add overview
This commit is contained in:
parent
e88db9c75c
commit
c28317aefd
|
@ -22,7 +22,8 @@ version "0.1.0"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
maven { url "http://dl.bintray.com/kotlin/ktor" }
|
maven { url "https://dl.bintray.com/kotlin/ktor" }
|
||||||
|
maven { url "https://dl.bintray.com/jetbrains/markdown" }
|
||||||
maven { url "https://kotlin.bintray.com/kotlinx" }
|
maven { url "https://kotlin.bintray.com/kotlinx" }
|
||||||
maven { url "https://kotlin.bintray.com/kotlin-js-wrappers" }
|
maven { url "https://kotlin.bintray.com/kotlin-js-wrappers" }
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -87,6 +88,9 @@ kotlin {
|
||||||
|
|
||||||
implementation "de.westermann:KObserve-jvm:$observable_version"
|
implementation "de.westermann:KObserve-jvm:$observable_version"
|
||||||
|
|
||||||
|
//api 'org.jetbrains:markdown:0.1.28'
|
||||||
|
//implementation 'com.atlassian.commonmark:commonmark:0.12.1'
|
||||||
|
implementation 'com.vladsch.flexmark:flexmark-all:0.42.10'
|
||||||
api 'io.github.microutils:kotlin-logging:1.6.23'
|
api 'io.github.microutils:kotlin-logging:1.6.23'
|
||||||
api 'ch.qos.logback:logback-classic:1.2.3'
|
api 'ch.qos.logback:logback-classic:1.2.3'
|
||||||
api 'org.fusesource.jansi:jansi:1.8'
|
api 'org.fusesource.jansi:jansi:1.8'
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
|
|
60
src/commonMain/kotlin/de/kif/common/CacheRepository.kt
Normal file
60
src/commonMain/kotlin/de/kif/common/CacheRepository.kt
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package de.kif.common
|
||||||
|
|
||||||
|
import de.kif.common.model.Model
|
||||||
|
import de.westermann.kobserve.event.EventHandler
|
||||||
|
|
||||||
|
|
||||||
|
class CacheRepository<T : Model>(val repository: Repository<T>) : Repository<T> {
|
||||||
|
|
||||||
|
override val onCreate = EventHandler<Long>()
|
||||||
|
override val onUpdate = EventHandler<Long>()
|
||||||
|
override val onDelete = EventHandler<Long>()
|
||||||
|
|
||||||
|
var cache: Map<Long, T> = emptyMap()
|
||||||
|
var cacheComplete: Boolean = false
|
||||||
|
|
||||||
|
override suspend fun get(id: Long): T? {
|
||||||
|
return if (id in cache) {
|
||||||
|
cache[id]
|
||||||
|
} else {
|
||||||
|
val element = repository.get(id)
|
||||||
|
|
||||||
|
if (element != null) {
|
||||||
|
cache = cache + (id to element)
|
||||||
|
}
|
||||||
|
|
||||||
|
element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun create(model: T): Long {
|
||||||
|
return repository.create(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun update(model: T) {
|
||||||
|
val id = model.id
|
||||||
|
if (id != null) {
|
||||||
|
cache = cache - id
|
||||||
|
}
|
||||||
|
repository.update(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun delete(id: Long) {
|
||||||
|
cache = cache - id
|
||||||
|
repository.delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun all(): List<T> {
|
||||||
|
if (cacheComplete) {
|
||||||
|
return cache.values.toList()
|
||||||
|
} else {
|
||||||
|
val all = repository.all()
|
||||||
|
|
||||||
|
cache = all.associateBy { it.id!! }
|
||||||
|
cacheComplete = true
|
||||||
|
|
||||||
|
return all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
86
src/commonMain/kotlin/de/kif/common/ConstraintChecking.kt
Normal file
86
src/commonMain/kotlin/de/kif/common/ConstraintChecking.kt
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package de.kif.common
|
||||||
|
|
||||||
|
import de.kif.common.model.ConstraintType
|
||||||
|
import de.kif.common.model.Schedule
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ConstraintError(
|
||||||
|
val reason: String = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ConstraintMap(
|
||||||
|
val map: List<Pair<Long, List<ConstraintError>>>
|
||||||
|
)
|
||||||
|
|
||||||
|
fun checkConstraints(
|
||||||
|
check: List<Schedule>,
|
||||||
|
against: List<Schedule>
|
||||||
|
): ConstraintMap {
|
||||||
|
val map = mutableMapOf<Long, List<ConstraintError>>()
|
||||||
|
|
||||||
|
for (schedule in check) {
|
||||||
|
if (schedule.id == null) continue
|
||||||
|
val errors = mutableListOf<ConstraintError>()
|
||||||
|
|
||||||
|
if (schedule.workGroup.projector && !schedule.room.projector) {
|
||||||
|
errors += ConstraintError("Work group requires projector, but room does not have one!")
|
||||||
|
}
|
||||||
|
if (schedule.workGroup.internet && !schedule.room.internet) {
|
||||||
|
errors += ConstraintError("Work group requires internet, but room does not have one!")
|
||||||
|
}
|
||||||
|
if (schedule.workGroup.whiteboard && !schedule.room.whiteboard) {
|
||||||
|
errors += ConstraintError("Work group requires whiteboard, but room does not have one!")
|
||||||
|
}
|
||||||
|
if (schedule.workGroup.blackboard && !schedule.room.blackboard) {
|
||||||
|
errors += ConstraintError("Work group requires blackboard, but room does not have one!")
|
||||||
|
}
|
||||||
|
if (schedule.workGroup.accessible && !schedule.room.accessible) {
|
||||||
|
errors += ConstraintError("Work group requires accessible, but room does not have one!")
|
||||||
|
}
|
||||||
|
|
||||||
|
for (constraint in schedule.workGroup.constraints) {
|
||||||
|
when (constraint.type) {
|
||||||
|
ConstraintType.OnlyOnDay -> {
|
||||||
|
if (constraint.number.toInt() != schedule.day) {
|
||||||
|
errors += ConstraintError("Work group requires day ${constraint.number}, but is on ${schedule.day}!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConstraintType.OnlyAfterTime -> {
|
||||||
|
if (constraint.number.toInt() > schedule.time) {
|
||||||
|
errors += ConstraintError("Work group requires time after ${constraint.number}, but is on ${schedule.time}!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConstraintType.NotAtSameTime -> {
|
||||||
|
val start = schedule.getAbsoluteStartTime()
|
||||||
|
val end = schedule.getAbsoluteEndTime()
|
||||||
|
for (s in against) {
|
||||||
|
if (
|
||||||
|
s.workGroup.id == constraint.number &&
|
||||||
|
start <= s.getAbsoluteEndTime() &&
|
||||||
|
s.getAbsoluteStartTime() <= end
|
||||||
|
) {
|
||||||
|
errors += ConstraintError("Work group requires not same time with ${s.workGroup.name}!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConstraintType.OnlyAfterWorkGroup -> {
|
||||||
|
val start = schedule.getAbsoluteStartTime()
|
||||||
|
for (s in against) {
|
||||||
|
if (
|
||||||
|
s.workGroup.id == constraint.number &&
|
||||||
|
s.getAbsoluteEndTime() > start
|
||||||
|
) {
|
||||||
|
errors += ConstraintError("Work group requires after ${s.workGroup.name}!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
map[schedule.id] = errors
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConstraintMap(map.map { it.toPair() })
|
||||||
|
}
|
|
@ -31,5 +31,5 @@ enum class MessageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class RepositoryType {
|
enum class RepositoryType {
|
||||||
ROOM, SCHEDULE, TRACK, USER, WORK_GROUP
|
ROOM, SCHEDULE, TRACK, USER, WORK_GROUP, POST
|
||||||
}
|
}
|
||||||
|
|
13
src/commonMain/kotlin/de/kif/common/model/Constraint.kt
Normal file
13
src/commonMain/kotlin/de/kif/common/model/Constraint.kt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package de.kif.common.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Constraint(
|
||||||
|
val type: ConstraintType,
|
||||||
|
val number: Long
|
||||||
|
)
|
||||||
|
|
||||||
|
enum class ConstraintType {
|
||||||
|
OnlyOnDay, OnlyAfterTime, NotAtSameTime, OnlyAfterWorkGroup
|
||||||
|
}
|
|
@ -3,5 +3,6 @@ package de.kif.common.model
|
||||||
import de.kif.common.SearchElement
|
import de.kif.common.SearchElement
|
||||||
|
|
||||||
interface Model {
|
interface Model {
|
||||||
|
val id : Long?
|
||||||
fun createSearch(): SearchElement
|
fun createSearch(): SearchElement
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
package de.kif.common.model
|
package de.kif.common.model
|
||||||
|
|
||||||
enum class Permission {
|
enum class Permission {
|
||||||
USER, SCHEDULE, WORK_GROUP, ROOM, PERSON, ADMIN
|
USER, SCHEDULE, WORK_GROUP, ROOM, POST, ADMIN
|
||||||
}
|
}
|
||||||
|
|
29
src/commonMain/kotlin/de/kif/common/model/Post.kt
Normal file
29
src/commonMain/kotlin/de/kif/common/model/Post.kt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package de.kif.common.model
|
||||||
|
|
||||||
|
import de.kif.common.SearchElement
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
data class Post(
|
||||||
|
override val id: Long? = null,
|
||||||
|
val name: String,
|
||||||
|
val content: String,
|
||||||
|
val url: String
|
||||||
|
) : Model {
|
||||||
|
|
||||||
|
override fun createSearch() = SearchElement(
|
||||||
|
mapOf(
|
||||||
|
"name" to name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val chars = "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
private const val length = 32
|
||||||
|
fun generateUrl() = (0 until length).asSequence()
|
||||||
|
.map { Random.nextInt(chars.length) }
|
||||||
|
.map { chars[it] }
|
||||||
|
.map {
|
||||||
|
if (Random.nextBoolean()) it else it.toUpperCase()
|
||||||
|
}.joinToString("")
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,10 +5,14 @@ import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Room(
|
data class Room(
|
||||||
val id: Long? = null,
|
override val id: Long? = null,
|
||||||
val name: String,
|
val name: String,
|
||||||
val places: Int,
|
val places: Int,
|
||||||
val projector: Boolean
|
val projector: Boolean,
|
||||||
|
val internet: Boolean,
|
||||||
|
val whiteboard: Boolean,
|
||||||
|
val blackboard: Boolean,
|
||||||
|
val accessible: Boolean
|
||||||
) : Model {
|
) : Model {
|
||||||
|
|
||||||
override fun createSearch() = SearchElement(
|
override fun createSearch() = SearchElement(
|
||||||
|
|
|
@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Schedule(
|
data class Schedule(
|
||||||
val id: Long?,
|
override val id: Long?,
|
||||||
val workGroup: WorkGroup,
|
val workGroup: WorkGroup,
|
||||||
val room: Room,
|
val room: Room,
|
||||||
val day: Int,
|
val day: Int,
|
||||||
|
@ -22,4 +22,7 @@ data class Schedule(
|
||||||
"day" to day.toDouble()
|
"day" to day.toDouble()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun getAbsoluteStartTime(): Int = day * 60 * 24 + time
|
||||||
|
fun getAbsoluteEndTime(): Int = getAbsoluteStartTime() + workGroup.length
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Track(
|
data class Track(
|
||||||
val id: Long?,
|
override val id: Long?,
|
||||||
val name: String,
|
val name: String,
|
||||||
val color: Color
|
val color: Color
|
||||||
) : Model {
|
) : Model {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class User(
|
data class User(
|
||||||
val id: Long?,
|
override val id: Long?,
|
||||||
val username: String,
|
val username: String,
|
||||||
val password: String,
|
val password: String,
|
||||||
val permissions: Set<Permission>
|
val permissions: Set<Permission>
|
||||||
|
|
|
@ -5,14 +5,19 @@ import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class WorkGroup(
|
data class WorkGroup(
|
||||||
val id: Long?,
|
override val id: Long?,
|
||||||
val name: String,
|
val name: String,
|
||||||
val interested: Int,
|
val interested: Int,
|
||||||
val track: Track?,
|
val track: Track?,
|
||||||
val projector: Boolean,
|
val projector: Boolean,
|
||||||
val resolution: Boolean,
|
val resolution: Boolean,
|
||||||
|
val internet: Boolean,
|
||||||
|
val whiteboard: Boolean,
|
||||||
|
val blackboard: Boolean,
|
||||||
|
val accessible: Boolean,
|
||||||
val length: Int,
|
val length: Int,
|
||||||
val language: Language
|
val language: Language,
|
||||||
|
val constraints: List<Constraint>
|
||||||
) : Model {
|
) : Model {
|
||||||
|
|
||||||
override fun createSearch() = SearchElement(
|
override fun createSearch() = SearchElement(
|
||||||
|
|
|
@ -2,6 +2,7 @@ package de.kif.frontend
|
||||||
|
|
||||||
import de.kif.frontend.views.calendar.initCalendar
|
import de.kif.frontend.views.calendar.initCalendar
|
||||||
import de.kif.frontend.views.initTableLayout
|
import de.kif.frontend.views.initTableLayout
|
||||||
|
import de.kif.frontend.views.initWorkGroupConstraints
|
||||||
import de.westermann.kwebview.components.init
|
import de.westermann.kwebview.components.init
|
||||||
import kotlin.browser.document
|
import kotlin.browser.document
|
||||||
|
|
||||||
|
@ -14,4 +15,7 @@ fun main() = init {
|
||||||
if (document.getElementsByClassName("table-layout").length > 0) {
|
if (document.getElementsByClassName("table-layout").length > 0) {
|
||||||
initTableLayout()
|
initTableLayout()
|
||||||
}
|
}
|
||||||
|
if (document.getElementsByClassName("work-group-constraints").length > 0) {
|
||||||
|
initWorkGroupConstraints()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package de.kif.frontend.repository
|
package de.kif.frontend.repository
|
||||||
|
|
||||||
|
import de.kif.common.ConstraintMap
|
||||||
import de.kif.common.Message
|
import de.kif.common.Message
|
||||||
import de.kif.common.Repository
|
import de.kif.common.Repository
|
||||||
import de.kif.common.RepositoryType
|
import de.kif.common.RepositoryType
|
||||||
|
@ -49,4 +50,14 @@ object ScheduleRepository : Repository<Schedule> {
|
||||||
|
|
||||||
override fun onDelete(id: Long) = onDelete.emit(id)
|
override fun onDelete(id: Long) = onDelete.emit(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun checkConstraints(): ConstraintMap {
|
||||||
|
val json = repositoryGet("/api/constraints")
|
||||||
|
return parser.parse(json, ConstraintMap.serializer())
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun checkConstraintsFor(schedule: Schedule): ConstraintMap {
|
||||||
|
val json = repositoryGet("/api/constraint/${schedule.id}")
|
||||||
|
return parser.parse(json, ConstraintMap.serializer())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
118
src/jsMain/kotlin/de/kif/frontend/views/WorkGroupConstraints.kt
Normal file
118
src/jsMain/kotlin/de/kif/frontend/views/WorkGroupConstraints.kt
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
package de.kif.frontend.views
|
||||||
|
|
||||||
|
import de.kif.frontend.launch
|
||||||
|
import de.kif.frontend.repository.WorkGroupRepository
|
||||||
|
import de.westermann.kobserve.event.EventListener
|
||||||
|
import de.westermann.kwebview.View
|
||||||
|
import de.westermann.kwebview.async
|
||||||
|
import de.westermann.kwebview.components.*
|
||||||
|
import de.westermann.kwebview.createHtmlView
|
||||||
|
import org.w3c.dom.*
|
||||||
|
import kotlin.browser.document
|
||||||
|
|
||||||
|
fun initWorkGroupConstraints() {
|
||||||
|
var index = 10000
|
||||||
|
|
||||||
|
val constraints =
|
||||||
|
ListView.wrap<View>(document.getElementsByClassName("work-group-constraints")[0] as HTMLElement)
|
||||||
|
val addButton =
|
||||||
|
View.wrap(document.getElementsByClassName("work-group-constraints-add")[0] as HTMLElement)
|
||||||
|
val addList =
|
||||||
|
ListView.wrap<View>(document.getElementsByClassName("work-group-constraints-add-list")[0] as HTMLElement)
|
||||||
|
|
||||||
|
console.log(constraints.html)
|
||||||
|
console.log(addButton.html)
|
||||||
|
console.log(addList.html)
|
||||||
|
|
||||||
|
addButton.onClick {
|
||||||
|
addList.classList += "active"
|
||||||
|
|
||||||
|
var listener: EventListener<*>? = null
|
||||||
|
|
||||||
|
async {
|
||||||
|
listener = Body.onClick.reference {
|
||||||
|
addList.classList -= "active"
|
||||||
|
listener?.detach()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addList.textView("Add only on day") {
|
||||||
|
onClick {
|
||||||
|
constraints.html.appendChild(View.wrap(createHtmlView<HTMLDivElement>()) {
|
||||||
|
classList += "input-group"
|
||||||
|
html.appendChild(TextView("On day").apply { classList += "form-btn" }.html)
|
||||||
|
html.appendChild(InputView(InputType.NUMBER).apply {
|
||||||
|
classList += "form-control"
|
||||||
|
html.name = "constraint-only-on-day-${index++}"
|
||||||
|
min = -1337.0
|
||||||
|
max = 1337.0
|
||||||
|
}.html)
|
||||||
|
}.html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addList.textView("Add only after time") {
|
||||||
|
onClick {
|
||||||
|
constraints.html.appendChild(View.wrap(createHtmlView<HTMLDivElement>()) {
|
||||||
|
classList += "input-group"
|
||||||
|
html.appendChild(TextView("After time").apply { classList += "form-btn" }.html)
|
||||||
|
html.appendChild(InputView(InputType.NUMBER).apply {
|
||||||
|
classList += "form-control"
|
||||||
|
html.name = "constraint-only-after-time-${index++}"
|
||||||
|
min = -1337.0
|
||||||
|
max = 133700.0
|
||||||
|
}.html)
|
||||||
|
}.html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addList.textView("Add not at same time") {
|
||||||
|
onClick {
|
||||||
|
constraints.html.appendChild(View.wrap(createHtmlView<HTMLDivElement>()) {
|
||||||
|
classList += "input-group"
|
||||||
|
html.appendChild(TextView("Not with").apply { classList += "form-btn" }.html)
|
||||||
|
|
||||||
|
val select = createHtmlView<HTMLSelectElement>()
|
||||||
|
select.classList.add("form-control")
|
||||||
|
select.name = "constraint-not-at-same-time-${index++}"
|
||||||
|
|
||||||
|
launch {
|
||||||
|
val all = WorkGroupRepository.all()
|
||||||
|
|
||||||
|
for (wg in all) {
|
||||||
|
val option = createHtmlView<HTMLOptionElement>()
|
||||||
|
option.value = wg.id.toString()
|
||||||
|
option.textContent = wg.name
|
||||||
|
select.appendChild(option)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html.appendChild(select)
|
||||||
|
}.html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addList.textView("Add only after work group") {
|
||||||
|
onClick {
|
||||||
|
constraints.html.appendChild(View.wrap(createHtmlView<HTMLDivElement>()) {
|
||||||
|
classList += "input-group"
|
||||||
|
html.appendChild(TextView("After AK").apply { classList += "form-btn" }.html)
|
||||||
|
|
||||||
|
val select = createHtmlView<HTMLSelectElement>()
|
||||||
|
select.classList.add("form-control")
|
||||||
|
select.name = "constraint-only-after-work-group-${index++}"
|
||||||
|
|
||||||
|
launch {
|
||||||
|
val all = WorkGroupRepository.all()
|
||||||
|
|
||||||
|
for (wg in all) {
|
||||||
|
val option = createHtmlView<HTMLOptionElement>()
|
||||||
|
option.value = wg.id.toString()
|
||||||
|
option.textContent = wg.name
|
||||||
|
select.appendChild(option)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html.appendChild(select)
|
||||||
|
}.html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import de.kif.frontend.iterator
|
||||||
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.View
|
import de.westermann.kwebview.View
|
||||||
|
import de.westermann.kwebview.createHtmlView
|
||||||
import org.w3c.dom.*
|
import org.w3c.dom.*
|
||||||
import kotlin.browser.document
|
import kotlin.browser.document
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@ class Calendar(calendar: HTMLElement) : View(calendar) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun scrollHorizontalTo(pixel: Double) {
|
fun scrollHorizontalTo(pixel: Double) {
|
||||||
calendarTable.scrollTo (ScrollToOptions(pixel, 0.0, ScrollBehavior.SMOOTH))
|
calendarTable.scrollTo(ScrollToOptions(pixel, 0.0, ScrollBehavior.SMOOTH))
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -76,6 +77,27 @@ class Calendar(calendar: HTMLElement) : View(calendar) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val cont = document.getElementsByClassName("header-right")[0] as HTMLElement
|
||||||
|
|
||||||
|
val view = View.wrap(createHtmlView<HTMLAnchorElement>())
|
||||||
|
cont.appendChild(view.html)
|
||||||
|
view.html.textContent = "Check"
|
||||||
|
view.onClick {
|
||||||
|
launch {
|
||||||
|
val errors = ScheduleRepository.checkConstraints()
|
||||||
|
|
||||||
|
println(errors)
|
||||||
|
|
||||||
|
for ((s, l) in errors.map) {
|
||||||
|
for (entry in calendarEntries) {
|
||||||
|
if (entry.scheduleId == s) {
|
||||||
|
entry.error = l.isNotEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi
|
||||||
private lateinit var workGroup: WorkGroup
|
private lateinit var workGroup: WorkGroup
|
||||||
|
|
||||||
var pending by classList.property("pending")
|
var pending by classList.property("pending")
|
||||||
|
var error by classList.property("error")
|
||||||
private var nextScroll = 0.0
|
private var nextScroll = 0.0
|
||||||
|
|
||||||
var editable: Boolean = false
|
var editable: Boolean = false
|
||||||
|
|
|
@ -144,6 +144,6 @@ abstract class View(view: HTMLElement = createHtmlView()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun wrap(htmlElement: HTMLElement) = object : View(htmlElement) {}
|
fun wrap(htmlElement: HTMLElement, init: View.() -> Unit = {}) = object : View(htmlElement) {}.also(init)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,6 +296,7 @@ a {
|
||||||
|
|
||||||
span {
|
span {
|
||||||
padding: 0 0.5rem;
|
padding: 0 0.5rem;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba($text-primary-color, 0.06);
|
background-color: rgba($text-primary-color, 0.06);
|
||||||
}
|
}
|
||||||
|
@ -321,6 +322,12 @@ a {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textarea.form-control {
|
||||||
|
height: 16rem;
|
||||||
|
line-height: 1.1rem;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
select:-moz-focusring {
|
select:-moz-focusring {
|
||||||
color: transparent;
|
color: transparent;
|
||||||
text-shadow: 0 0 0 $text-primary-color;
|
text-shadow: 0 0 0 $text-primary-color;
|
||||||
|
@ -671,6 +678,10 @@ form {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
outline: solid 0.4rem $error-color;
|
||||||
|
}
|
||||||
|
|
||||||
@include no-select()
|
@include no-select()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -908,4 +919,115 @@ form {
|
||||||
width: 4rem;
|
width: 4rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.work-group-constraints {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.work-group-constraints-add {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.work-group-constraints-add-list {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
background: $background-primary-color;
|
||||||
|
border: solid 1px rgba($text-primary-color, 0.1);
|
||||||
|
|
||||||
|
span {
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba($text-primary-color, 0.06);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-main {
|
||||||
|
flex-grow: 4;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-side {
|
||||||
|
min-width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-shortcuts {
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-twitter {
|
||||||
|
height: 20rem;
|
||||||
|
background-color: #b3e6f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post {
|
||||||
|
position: relative;
|
||||||
|
padding-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-name {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: $primary-color;
|
||||||
|
line-height: 2rem;
|
||||||
|
|
||||||
|
&:empty::before {
|
||||||
|
display: block;
|
||||||
|
content: 'No title';
|
||||||
|
opacity: 0.5;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
color: $text-primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.post-edit {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
line-height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-content {
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
h4 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
h5 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
h6 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,9 @@ import io.ktor.http.content.static
|
||||||
import io.ktor.jackson.jackson
|
import io.ktor.jackson.jackson
|
||||||
import io.ktor.routing.routing
|
import io.ktor.routing.routing
|
||||||
import io.ktor.websocket.WebSockets
|
import io.ktor.websocket.WebSockets
|
||||||
|
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||||
|
|
||||||
|
@ImplicitReflectionSerializer
|
||||||
fun Application.main() {
|
fun Application.main() {
|
||||||
install(DefaultHeaders)
|
install(DefaultHeaders)
|
||||||
install(CallLogging)
|
install(CallLogging)
|
||||||
|
@ -35,7 +37,7 @@ fun Application.main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UI routes
|
// UI routes
|
||||||
dashboard()
|
overview()
|
||||||
calendar()
|
calendar()
|
||||||
login()
|
login()
|
||||||
account()
|
account()
|
||||||
|
@ -53,6 +55,7 @@ fun Application.main() {
|
||||||
trackApi()
|
trackApi()
|
||||||
userApi()
|
userApi()
|
||||||
workGroupApi()
|
workGroupApi()
|
||||||
|
constraintsApi()
|
||||||
|
|
||||||
// Web socket push notifications
|
// Web socket push notifications
|
||||||
pushService()
|
pushService()
|
||||||
|
|
|
@ -8,8 +8,10 @@ import io.ktor.application.Application
|
||||||
import io.ktor.server.engine.embeddedServer
|
import io.ktor.server.engine.embeddedServer
|
||||||
import io.ktor.server.netty.Netty
|
import io.ktor.server.netty.Netty
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||||
|
|
||||||
object Main {
|
object Main {
|
||||||
|
@ImplicitReflectionSerializer
|
||||||
@Suppress("UnusedMainParameter")
|
@Suppress("UnusedMainParameter")
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
|
|
|
@ -18,7 +18,8 @@ object Connection {
|
||||||
SchemaUtils.create(
|
SchemaUtils.create(
|
||||||
DbTrack, DbWorkGroup,
|
DbTrack, DbWorkGroup,
|
||||||
DbRoom, DbSchedule,
|
DbRoom, DbSchedule,
|
||||||
DbUser, DbUserPermission
|
DbUser, DbUserPermission,
|
||||||
|
DbPost
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,15 +12,22 @@ object DbTrack : Table() {
|
||||||
|
|
||||||
object DbWorkGroup : Table() {
|
object DbWorkGroup : Table() {
|
||||||
val id = long("id").autoIncrement().primaryKey()
|
val id = long("id").autoIncrement().primaryKey()
|
||||||
val name = varchar("first_name", 64)
|
val name = varchar("name", 64)
|
||||||
|
|
||||||
val interested = integer("interested")
|
val interested = integer("interested")
|
||||||
val trackId = long("track_id").nullable()
|
val trackId = long("track_id").nullable()
|
||||||
val projector = bool("projector")
|
val projector = bool("projector")
|
||||||
val resolution = bool("resolution")
|
val resolution = bool("resolution")
|
||||||
|
|
||||||
|
val internet = bool("internet")
|
||||||
|
val whiteboard = bool("whiteboard")
|
||||||
|
val blackboard = bool("blackboard")
|
||||||
|
val accessible = bool("accessible")
|
||||||
|
|
||||||
val language = enumeration("language", Language::class)
|
val language = enumeration("language", Language::class)
|
||||||
|
|
||||||
val length = integer("length")
|
val length = integer("length")
|
||||||
|
val constraints = text("constraints")
|
||||||
}
|
}
|
||||||
|
|
||||||
object DbRoom : Table() {
|
object DbRoom : Table() {
|
||||||
|
@ -29,6 +36,11 @@ object DbRoom : Table() {
|
||||||
|
|
||||||
val places = integer("places")
|
val places = integer("places")
|
||||||
val projector = bool("projector")
|
val projector = bool("projector")
|
||||||
|
|
||||||
|
val internet = bool("internet")
|
||||||
|
val whiteboard = bool("whiteboard")
|
||||||
|
val blackboard = bool("blackboard")
|
||||||
|
val accessible = bool("accessible")
|
||||||
}
|
}
|
||||||
|
|
||||||
object DbSchedule : Table() {
|
object DbSchedule : Table() {
|
||||||
|
@ -49,3 +61,11 @@ object DbUserPermission : Table() {
|
||||||
val userId = long("id").primaryKey(0)
|
val userId = long("id").primaryKey(0)
|
||||||
val permission = enumeration("permission", Permission::class).primaryKey(1)
|
val permission = enumeration("permission", Permission::class).primaryKey(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object DbPost : Table() {
|
||||||
|
val id = long("id").autoIncrement().primaryKey()
|
||||||
|
val name = varchar("name", 64)
|
||||||
|
|
||||||
|
val content = text("content")
|
||||||
|
val url = varchar("url", 64).uniqueIndex()
|
||||||
|
}
|
||||||
|
|
107
src/jvmMain/kotlin/de/kif/backend/repository/PostRepository.kt
Normal file
107
src/jvmMain/kotlin/de/kif/backend/repository/PostRepository.kt
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
package de.kif.backend.repository
|
||||||
|
|
||||||
|
import de.kif.backend.database.DbPost
|
||||||
|
import de.kif.backend.database.dbQuery
|
||||||
|
import de.kif.backend.util.PushService
|
||||||
|
import de.kif.common.MessageType
|
||||||
|
import de.kif.common.Repository
|
||||||
|
import de.kif.common.RepositoryType
|
||||||
|
import de.kif.common.model.Post
|
||||||
|
import de.westermann.kobserve.event.EventHandler
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
|
||||||
|
object PostRepository : Repository<Post> {
|
||||||
|
|
||||||
|
override val onCreate = EventHandler<Long>()
|
||||||
|
override val onUpdate = EventHandler<Long>()
|
||||||
|
override val onDelete = EventHandler<Long>()
|
||||||
|
|
||||||
|
private fun rowToModel(row: ResultRow): Post {
|
||||||
|
val id = row[DbPost.id]
|
||||||
|
val name = row[DbPost.name]
|
||||||
|
val content = row[DbPost.content]
|
||||||
|
val url = row[DbPost.url]
|
||||||
|
|
||||||
|
return Post(id, name, content, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun get(id: Long): Post? {
|
||||||
|
return dbQuery {
|
||||||
|
rowToModel(DbPost.select { DbPost.id eq id }.firstOrNull() ?: return@dbQuery null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun create(model: Post): Long {
|
||||||
|
return dbQuery {
|
||||||
|
val id = DbPost.insert {
|
||||||
|
it[name] = model.name
|
||||||
|
it[content] = model.content
|
||||||
|
it[url] = model.url
|
||||||
|
}[DbPost.id] ?: throw IllegalStateException("Cannot create model!")
|
||||||
|
|
||||||
|
onCreate.emit(id)
|
||||||
|
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun update(model: Post) {
|
||||||
|
if (model.id == null) throw IllegalStateException("Cannot update model which was not created!")
|
||||||
|
dbQuery {
|
||||||
|
DbPost.update({ DbPost.id eq model.id }) {
|
||||||
|
it[name] = model.name
|
||||||
|
it[content] = model.content
|
||||||
|
it[url] = model.url
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdate.emit(model.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun delete(id: Long) {
|
||||||
|
onDelete.emit(id)
|
||||||
|
|
||||||
|
dbQuery {
|
||||||
|
DbPost.deleteWhere { DbPost.id eq id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun all(): List<Post> {
|
||||||
|
return dbQuery {
|
||||||
|
val result = DbPost.selectAll()
|
||||||
|
|
||||||
|
result.map(this::rowToModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getByUrl(url: String): Post? {
|
||||||
|
return dbQuery {
|
||||||
|
val result = DbPost.select { DbPost.url eq url }
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
result.firstOrNull()?.let {
|
||||||
|
rowToModel(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerPushService() {
|
||||||
|
onCreate {
|
||||||
|
runBlocking {
|
||||||
|
PushService.notify(MessageType.CREATE, RepositoryType.POST, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onUpdate {
|
||||||
|
runBlocking {
|
||||||
|
PushService.notify(MessageType.UPDATE, RepositoryType.POST, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onDelete {
|
||||||
|
runBlocking {
|
||||||
|
PushService.notify(MessageType.DELETE, RepositoryType.POST, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,8 +20,12 @@ object RoomRepository : Repository<Room> {
|
||||||
val name = row[DbRoom.name]
|
val name = row[DbRoom.name]
|
||||||
val places = row[DbRoom.places]
|
val places = row[DbRoom.places]
|
||||||
val projector = row[DbRoom.projector]
|
val projector = row[DbRoom.projector]
|
||||||
|
val internet = row[DbRoom.internet]
|
||||||
|
val whiteboard = row[DbRoom.whiteboard]
|
||||||
|
val blackboard = row[DbRoom.blackboard]
|
||||||
|
val accessible = row[DbRoom.accessible]
|
||||||
|
|
||||||
return Room(id, name, places, projector)
|
return Room(id, name, places, projector, internet, whiteboard, blackboard, accessible)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun get(id: Long): Room? {
|
override suspend fun get(id: Long): Room? {
|
||||||
|
@ -36,6 +40,10 @@ object RoomRepository : Repository<Room> {
|
||||||
it[name] = model.name
|
it[name] = model.name
|
||||||
it[places] = model.places
|
it[places] = model.places
|
||||||
it[projector] = model.projector
|
it[projector] = model.projector
|
||||||
|
it[internet] = model.internet
|
||||||
|
it[whiteboard] = model.whiteboard
|
||||||
|
it[blackboard] = model.blackboard
|
||||||
|
it[accessible] = model.accessible
|
||||||
}[DbRoom.id] ?: throw IllegalStateException("Cannot create model!")
|
}[DbRoom.id] ?: throw IllegalStateException("Cannot create model!")
|
||||||
|
|
||||||
onCreate.emit(id)
|
onCreate.emit(id)
|
||||||
|
@ -51,6 +59,10 @@ object RoomRepository : Repository<Room> {
|
||||||
it[name] = model.name
|
it[name] = model.name
|
||||||
it[places] = model.places
|
it[places] = model.places
|
||||||
it[projector] = model.projector
|
it[projector] = model.projector
|
||||||
|
it[internet] = model.internet
|
||||||
|
it[whiteboard] = model.whiteboard
|
||||||
|
it[blackboard] = model.blackboard
|
||||||
|
it[accessible] = model.accessible
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdate.emit(model.id)
|
onUpdate.emit(model.id)
|
||||||
|
|
|
@ -3,12 +3,15 @@ package de.kif.backend.repository
|
||||||
import de.kif.backend.database.DbWorkGroup
|
import de.kif.backend.database.DbWorkGroup
|
||||||
import de.kif.backend.database.dbQuery
|
import de.kif.backend.database.dbQuery
|
||||||
import de.kif.backend.util.PushService
|
import de.kif.backend.util.PushService
|
||||||
|
import de.kif.common.Message
|
||||||
import de.kif.common.MessageType
|
import de.kif.common.MessageType
|
||||||
import de.kif.common.Repository
|
import de.kif.common.Repository
|
||||||
import de.kif.common.RepositoryType
|
import de.kif.common.RepositoryType
|
||||||
|
import de.kif.common.model.Constraint
|
||||||
import de.kif.common.model.WorkGroup
|
import de.kif.common.model.WorkGroup
|
||||||
import de.westermann.kobserve.event.EventHandler
|
import de.westermann.kobserve.event.EventHandler
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.serialization.list
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
|
|
||||||
object WorkGroupRepository : Repository<WorkGroup> {
|
object WorkGroupRepository : Repository<WorkGroup> {
|
||||||
|
@ -24,12 +27,31 @@ object WorkGroupRepository : Repository<WorkGroup> {
|
||||||
val trackId = row[DbWorkGroup.trackId]
|
val trackId = row[DbWorkGroup.trackId]
|
||||||
val projector = row[DbWorkGroup.projector]
|
val projector = row[DbWorkGroup.projector]
|
||||||
val resolution = row[DbWorkGroup.resolution]
|
val resolution = row[DbWorkGroup.resolution]
|
||||||
|
val internet = row[DbWorkGroup.internet]
|
||||||
|
val whiteboard = row[DbWorkGroup.whiteboard]
|
||||||
|
val blackboard = row[DbWorkGroup.blackboard]
|
||||||
|
val accessible = row[DbWorkGroup.accessible]
|
||||||
val length = row[DbWorkGroup.length]
|
val length = row[DbWorkGroup.length]
|
||||||
val language = row[DbWorkGroup.language]
|
val language = row[DbWorkGroup.language]
|
||||||
|
val constraints = Message.json.parse(Constraint.serializer().list, row[DbWorkGroup.constraints])
|
||||||
|
|
||||||
val track = trackId?.let { TrackRepository.get(it) }
|
val track = trackId?.let { TrackRepository.get(it) }
|
||||||
|
|
||||||
return WorkGroup(id, name, interested, track, projector, resolution, length, language)
|
return WorkGroup(
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
interested,
|
||||||
|
track,
|
||||||
|
projector,
|
||||||
|
resolution,
|
||||||
|
internet,
|
||||||
|
whiteboard,
|
||||||
|
blackboard,
|
||||||
|
accessible,
|
||||||
|
length,
|
||||||
|
language,
|
||||||
|
constraints
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun get(id: Long): WorkGroup? {
|
override suspend fun get(id: Long): WorkGroup? {
|
||||||
|
@ -50,8 +72,13 @@ object WorkGroupRepository : Repository<WorkGroup> {
|
||||||
it[trackId] = model.track?.id
|
it[trackId] = model.track?.id
|
||||||
it[projector] = model.projector
|
it[projector] = model.projector
|
||||||
it[resolution] = model.resolution
|
it[resolution] = model.resolution
|
||||||
|
it[internet] = model.internet
|
||||||
|
it[whiteboard] = model.whiteboard
|
||||||
|
it[blackboard] = model.blackboard
|
||||||
|
it[accessible] = model.accessible
|
||||||
it[length] = model.length
|
it[length] = model.length
|
||||||
it[language] = model.language
|
it[language] = model.language
|
||||||
|
it[constraints] = Message.json.stringify(Constraint.serializer().list, model.constraints)
|
||||||
}[DbWorkGroup.id] ?: throw IllegalStateException("Cannot create model!")
|
}[DbWorkGroup.id] ?: throw IllegalStateException("Cannot create model!")
|
||||||
|
|
||||||
onCreate.emit(id)
|
onCreate.emit(id)
|
||||||
|
@ -69,8 +96,13 @@ object WorkGroupRepository : Repository<WorkGroup> {
|
||||||
it[trackId] = model.track?.id
|
it[trackId] = model.track?.id
|
||||||
it[projector] = model.projector
|
it[projector] = model.projector
|
||||||
it[resolution] = model.resolution
|
it[resolution] = model.resolution
|
||||||
|
it[internet] = model.internet
|
||||||
|
it[whiteboard] = model.whiteboard
|
||||||
|
it[blackboard] = model.blackboard
|
||||||
|
it[accessible] = model.accessible
|
||||||
it[length] = model.length
|
it[length] = model.length
|
||||||
it[language] = model.language
|
it[language] = model.language
|
||||||
|
it[constraints] = Message.json.stringify(Constraint.serializer().list, model.constraints)
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdate.emit(model.id)
|
onUpdate.emit(model.id)
|
||||||
|
|
|
@ -136,7 +136,7 @@ private fun DIV.renderTimeToRoom(
|
||||||
val minutes = (time % 60).toString().padStart(2, '0')
|
val minutes = (time % 60).toString().padStart(2, '0')
|
||||||
val hours = (time / 60).toString().padStart(2, '0')
|
val hours = (time / 60).toString().padStart(2, '0')
|
||||||
title = "$hours:$minutes"
|
title = "$hours:$minutes"
|
||||||
attributes["data-time"] = time.toString()
|
attributes["data-time"] = start.toString()
|
||||||
attributes["data-room"] = room.id.toString()
|
attributes["data-room"] = room.id.toString()
|
||||||
attributes["data-day"] = day.toString()
|
attributes["data-day"] = day.toString()
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ private fun DIV.renderRoomToTime(
|
||||||
|
|
||||||
for (room in rooms) {
|
for (room in rooms) {
|
||||||
div("calendar-cell") {
|
div("calendar-cell") {
|
||||||
attributes["data-time"] = time.toString()
|
attributes["data-time"] = start.toString()
|
||||||
attributes["data-room"] = room.id.toString()
|
attributes["data-room"] = room.id.toString()
|
||||||
attributes["data-day"] = day.toString()
|
attributes["data-day"] = day.toString()
|
||||||
title = timeString
|
title = timeString
|
||||||
|
@ -251,6 +251,7 @@ fun Route.calendar() {
|
||||||
|
|
||||||
get("/calendar/{day}") {
|
get("/calendar/{day}") {
|
||||||
val user = isAuthenticated(Permission.SCHEDULE)
|
val user = isAuthenticated(Permission.SCHEDULE)
|
||||||
|
val editable = user != null
|
||||||
|
|
||||||
val day = call.parameters["day"]?.toIntOrNull() ?: return@get
|
val day = call.parameters["day"]?.toIntOrNull() ?: return@get
|
||||||
|
|
||||||
|
@ -280,11 +281,14 @@ fun Route.calendar() {
|
||||||
min = h1
|
min = h1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (editable) {
|
||||||
|
min = min(min, 0)
|
||||||
|
max = max(max, 24 * 60)
|
||||||
|
}
|
||||||
|
|
||||||
min = (min / 60 - 1) * 60
|
min = (min / 60 - 1) * 60
|
||||||
max = (max / 60 + 2) * 60
|
max = (max / 60 + 2) * 60
|
||||||
|
|
||||||
min = min(min, 0)
|
|
||||||
|
|
||||||
call.respondHtmlTemplate(MainTemplate()) {
|
call.respondHtmlTemplate(MainTemplate()) {
|
||||||
menuTemplate {
|
menuTemplate {
|
||||||
this.user = user
|
this.user = user
|
||||||
|
@ -314,7 +318,6 @@ fun Route.calendar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
div("calendar") {
|
div("calendar") {
|
||||||
val editable = user != null
|
|
||||||
attributes["data-day"] = day.toString()
|
attributes["data-day"] = day.toString()
|
||||||
attributes["data-editable"] = editable.toString()
|
attributes["data-editable"] = editable.toString()
|
||||||
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
package de.kif.backend.route
|
|
||||||
|
|
||||||
import de.kif.backend.PortalSession
|
|
||||||
import de.kif.backend.view.MainTemplate
|
|
||||||
import de.kif.backend.view.MenuTemplate
|
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.html.respondHtmlTemplate
|
|
||||||
import io.ktor.routing.Route
|
|
||||||
import io.ktor.routing.get
|
|
||||||
import io.ktor.sessions.get
|
|
||||||
import io.ktor.sessions.sessions
|
|
||||||
import kotlinx.html.h1
|
|
||||||
|
|
||||||
fun Route.dashboard() {
|
|
||||||
get("") {
|
|
||||||
val user = call.sessions.get<PortalSession>()?.getUser(call)
|
|
||||||
call.respondHtmlTemplate(MainTemplate()) {
|
|
||||||
menuTemplate {
|
|
||||||
this.user = user
|
|
||||||
active = MenuTemplate.Tab.DASHBOARD
|
|
||||||
}
|
|
||||||
content {
|
|
||||||
h1 { +"Dashboard" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
281
src/jvmMain/kotlin/de/kif/backend/route/Overview.kt
Normal file
281
src/jvmMain/kotlin/de/kif/backend/route/Overview.kt
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
package de.kif.backend.route
|
||||||
|
|
||||||
|
import de.kif.backend.authenticateOrRedirect
|
||||||
|
import de.kif.backend.isAuthenticated
|
||||||
|
import de.kif.backend.repository.PostRepository
|
||||||
|
import de.kif.backend.util.markdownToHtml
|
||||||
|
import de.kif.backend.view.MainTemplate
|
||||||
|
import de.kif.backend.view.MenuTemplate
|
||||||
|
import de.kif.common.model.Permission
|
||||||
|
import de.kif.common.model.Post
|
||||||
|
import io.ktor.application.call
|
||||||
|
import io.ktor.html.respondHtmlTemplate
|
||||||
|
import io.ktor.request.receiveParameters
|
||||||
|
import io.ktor.response.respondRedirect
|
||||||
|
import io.ktor.routing.Route
|
||||||
|
import io.ktor.routing.get
|
||||||
|
import io.ktor.routing.post
|
||||||
|
import io.ktor.util.toMap
|
||||||
|
import kotlinx.html.*
|
||||||
|
|
||||||
|
fun Route.overview() {
|
||||||
|
get("") {
|
||||||
|
val user = isAuthenticated(Permission.POST)
|
||||||
|
val editable = user != null
|
||||||
|
|
||||||
|
val postList = PostRepository.all().asReversed()
|
||||||
|
|
||||||
|
call.respondHtmlTemplate(MainTemplate()) {
|
||||||
|
menuTemplate {
|
||||||
|
this.user = user
|
||||||
|
active = MenuTemplate.Tab.BOARD
|
||||||
|
}
|
||||||
|
content {
|
||||||
|
div("overview") {
|
||||||
|
div("overview-main") {
|
||||||
|
if (editable) {
|
||||||
|
div("overview-new") {
|
||||||
|
a("post/new", classes = "form-btn") {
|
||||||
|
+"New"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (post in postList) {
|
||||||
|
div("overview-post post") {
|
||||||
|
span("post-name") {
|
||||||
|
+post.name
|
||||||
|
}
|
||||||
|
if (editable) {
|
||||||
|
a("/post/${post.id}", classes = "post-edit") {
|
||||||
|
i("material-icons") { +"edit" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div("post-content") {
|
||||||
|
unsafe {
|
||||||
|
raw(markdownToHtml(post.content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div("overview-side") {
|
||||||
|
div("overview-shortcuts") {
|
||||||
|
a {
|
||||||
|
+"Wiki"
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
+"Wiki"
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
+"Wiki"
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
+"Wiki"
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
+"Wiki"
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
+"Wiki"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div("overview-twitter") {
|
||||||
|
+"The Twitter Wall"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get("/post/{id}") {
|
||||||
|
authenticateOrRedirect(Permission.POST) { user ->
|
||||||
|
val postId = call.parameters["id"]?.toLongOrNull() ?: return@get
|
||||||
|
val editPost = PostRepository.get(postId) ?: return@get
|
||||||
|
call.respondHtmlTemplate(MainTemplate()) {
|
||||||
|
menuTemplate {
|
||||||
|
this.user = user
|
||||||
|
active = MenuTemplate.Tab.BOARD
|
||||||
|
}
|
||||||
|
content {
|
||||||
|
h1 { +"Edit post" }
|
||||||
|
form(method = FormMethod.post) {
|
||||||
|
div("form-group") {
|
||||||
|
label {
|
||||||
|
htmlFor = "name"
|
||||||
|
+"Name"
|
||||||
|
}
|
||||||
|
input(
|
||||||
|
name = "name",
|
||||||
|
classes = "form-control"
|
||||||
|
) {
|
||||||
|
id = "name"
|
||||||
|
placeholder = "Name"
|
||||||
|
value = editPost.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div("form-group") {
|
||||||
|
label {
|
||||||
|
htmlFor = "url"
|
||||||
|
+"Url"
|
||||||
|
}
|
||||||
|
input(
|
||||||
|
name = "url",
|
||||||
|
classes = "form-control"
|
||||||
|
) {
|
||||||
|
id = "places"
|
||||||
|
placeholder = "Places"
|
||||||
|
value = editPost.url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-group") {
|
||||||
|
label {
|
||||||
|
htmlFor = "content"
|
||||||
|
+"Content"
|
||||||
|
}
|
||||||
|
textArea(rows = "10", classes = "form-control") {
|
||||||
|
name = "content"
|
||||||
|
id = "projector"
|
||||||
|
|
||||||
|
+editPost.content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-group") {
|
||||||
|
a("/") {
|
||||||
|
button(classes = "form-btn") {
|
||||||
|
+"Cancel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button(type = ButtonType.submit, classes = "form-btn btn-primary") {
|
||||||
|
+"Save"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a("/post/${editPost.id}/delete") {
|
||||||
|
button(classes = "form-btn btn-danger") {
|
||||||
|
+"Delete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post("/post/{id}") {
|
||||||
|
authenticateOrRedirect(Permission.POST) { user ->
|
||||||
|
val postId = call.parameters["id"]?.toLongOrNull() ?: return@post
|
||||||
|
val params = call.receiveParameters().toMap().mapValues { (_, list) ->
|
||||||
|
list.firstOrNull()
|
||||||
|
}
|
||||||
|
var post = PostRepository.get(postId) ?: return@post
|
||||||
|
|
||||||
|
params["name"]?.let { post = post.copy(name = it) }
|
||||||
|
params["url"]?.let { post = post.copy(url = it) }
|
||||||
|
params["content"]?.let { post = post.copy(content = it) }
|
||||||
|
|
||||||
|
PostRepository.update(post)
|
||||||
|
|
||||||
|
call.respondRedirect("/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/post/new") {
|
||||||
|
authenticateOrRedirect(Permission.POST) { user ->
|
||||||
|
call.respondHtmlTemplate(MainTemplate()) {
|
||||||
|
menuTemplate {
|
||||||
|
this.user = user
|
||||||
|
active = MenuTemplate.Tab.BOARD
|
||||||
|
}
|
||||||
|
content {
|
||||||
|
h1 { +"Create post" }
|
||||||
|
form(method = FormMethod.post) {
|
||||||
|
div("form-group") {
|
||||||
|
label {
|
||||||
|
htmlFor = "name"
|
||||||
|
+"Name"
|
||||||
|
}
|
||||||
|
input(
|
||||||
|
name = "name",
|
||||||
|
classes = "form-control"
|
||||||
|
) {
|
||||||
|
id = "name"
|
||||||
|
placeholder = "Name"
|
||||||
|
value = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div("form-group") {
|
||||||
|
label {
|
||||||
|
htmlFor = "url"
|
||||||
|
+"Url"
|
||||||
|
}
|
||||||
|
input(
|
||||||
|
name = "url",
|
||||||
|
classes = "form-control"
|
||||||
|
) {
|
||||||
|
id = "places"
|
||||||
|
placeholder = "Places"
|
||||||
|
value = Post.generateUrl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-group") {
|
||||||
|
label {
|
||||||
|
htmlFor = "content"
|
||||||
|
+"Content"
|
||||||
|
}
|
||||||
|
textArea(rows = "10", classes = "form-control") {
|
||||||
|
name = "content"
|
||||||
|
id = "projector"
|
||||||
|
|
||||||
|
+""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-group") {
|
||||||
|
a("/") {
|
||||||
|
button(classes = "form-btn") {
|
||||||
|
+"Cancel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button(type = ButtonType.submit, classes = "form-btn btn-primary") {
|
||||||
|
+"Create"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post("/post/new") {
|
||||||
|
authenticateOrRedirect(Permission.POST) { user ->
|
||||||
|
val params = call.receiveParameters().toMap().mapValues { (_, list) ->
|
||||||
|
list.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
val name = params["name"] ?: return@post
|
||||||
|
val content = params["content"] ?: return@post
|
||||||
|
val url = params["url"] ?: return@post
|
||||||
|
|
||||||
|
val post = Post(null, name, content, url)
|
||||||
|
|
||||||
|
PostRepository.create(post)
|
||||||
|
|
||||||
|
call.respondRedirect("/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/post/{id}/delete") {
|
||||||
|
authenticateOrRedirect(Permission.POST) { user ->
|
||||||
|
val postId = call.parameters["id"]?.toLongOrNull() ?: return@get
|
||||||
|
|
||||||
|
PostRepository.delete(postId)
|
||||||
|
|
||||||
|
call.respondRedirect("/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,7 +36,6 @@ fun Route.room() {
|
||||||
active = MenuTemplate.Tab.ROOM
|
active = MenuTemplate.Tab.ROOM
|
||||||
}
|
}
|
||||||
content {
|
content {
|
||||||
h1 { +"Rooms" }
|
|
||||||
insert(TableTemplate()) {
|
insert(TableTemplate()) {
|
||||||
searchValue = search
|
searchValue = search
|
||||||
|
|
||||||
|
@ -169,6 +168,74 @@ fun Route.room() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div("form-switch-group") {
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "internet",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "internet"
|
||||||
|
checked = editRoom.internet
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "internet"
|
||||||
|
+"Internet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-switch-group") {
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "whiteboard",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "whiteboard"
|
||||||
|
checked = editRoom.whiteboard
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "whiteboard"
|
||||||
|
+"Whiteboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-switch-group") {
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "blackboard",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "blackboard"
|
||||||
|
checked = editRoom.blackboard
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "blackboard"
|
||||||
|
+"Blackboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-switch-group") {
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "accessible",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "accessible"
|
||||||
|
checked = editRoom.accessible
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "accessible"
|
||||||
|
+"Accessible"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
div("form-group") {
|
div("form-group") {
|
||||||
a("/room") {
|
a("/room") {
|
||||||
button(classes = "form-btn") {
|
button(classes = "form-btn") {
|
||||||
|
@ -201,6 +268,10 @@ fun Route.room() {
|
||||||
params["name"]?.let { room = room.copy(name = it) }
|
params["name"]?.let { room = room.copy(name = it) }
|
||||||
params["places"]?.let { room = room.copy(places = it.toIntOrNull() ?: 0) }
|
params["places"]?.let { room = room.copy(places = it.toIntOrNull() ?: 0) }
|
||||||
params["projector"]?.let { room = room.copy(projector = it == "on") }
|
params["projector"]?.let { room = room.copy(projector = it == "on") }
|
||||||
|
params["internet"]?.let { room = room.copy(internet = it == "on") }
|
||||||
|
params["whiteboard"]?.let { room = room.copy(whiteboard = it == "on") }
|
||||||
|
params["blackboard"]?.let { room = room.copy(blackboard = it == "on") }
|
||||||
|
params["accessible"]?.let { room = room.copy(accessible = it == "on") }
|
||||||
|
|
||||||
RoomRepository.update(room)
|
RoomRepository.update(room)
|
||||||
|
|
||||||
|
@ -268,6 +339,74 @@ fun Route.room() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div("form-switch-group") {
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "internet",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "internet"
|
||||||
|
checked = false
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "internet"
|
||||||
|
+"Internet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-switch-group") {
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "whiteboard",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "whiteboard"
|
||||||
|
checked = false
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "whiteboard"
|
||||||
|
+"Whiteboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-switch-group") {
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "blackboard",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "blackboard"
|
||||||
|
checked = false
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "blackboard"
|
||||||
|
+"Blackboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-switch-group") {
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "accessible",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "accessible"
|
||||||
|
checked = false
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "accessible"
|
||||||
|
+"Accessible"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
div("form-group") {
|
div("form-group") {
|
||||||
a("/room") {
|
a("/room") {
|
||||||
button(classes = "form-btn") {
|
button(classes = "form-btn") {
|
||||||
|
@ -293,8 +432,12 @@ fun Route.room() {
|
||||||
val name = params["name"] ?: return@post
|
val name = params["name"] ?: return@post
|
||||||
val places = (params["places"] ?: return@post).toIntOrNull() ?: 0
|
val places = (params["places"] ?: return@post).toIntOrNull() ?: 0
|
||||||
val projector = params["projector"] == "on"
|
val projector = params["projector"] == "on"
|
||||||
|
val internet = params["internet"] == "on"
|
||||||
|
val whiteboard = params["whiteboard"] == "on"
|
||||||
|
val blackboard = params["blackboard"] == "on"
|
||||||
|
val accessible = params["accessible"] == "on"
|
||||||
|
|
||||||
val room = Room(null, name, places, projector)
|
val room = Room(null, name, places, projector, internet, whiteboard, blackboard, accessible)
|
||||||
|
|
||||||
RoomRepository.create(room)
|
RoomRepository.create(room)
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,6 @@ fun Route.track() {
|
||||||
active = MenuTemplate.Tab.WORK_GROUP
|
active = MenuTemplate.Tab.WORK_GROUP
|
||||||
}
|
}
|
||||||
content {
|
content {
|
||||||
h1 { +"Tracks" }
|
|
||||||
insert(TableTemplate()) {
|
insert(TableTemplate()) {
|
||||||
searchValue = search
|
searchValue = search
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,6 @@ fun Route.user() {
|
||||||
active = MenuTemplate.Tab.USER
|
active = MenuTemplate.Tab.USER
|
||||||
}
|
}
|
||||||
content {
|
content {
|
||||||
h1 { +"Users" }
|
|
||||||
insert(TableTemplate()) {
|
insert(TableTemplate()) {
|
||||||
searchValue = search
|
searchValue = search
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package de.kif.backend.route
|
package de.kif.backend.route
|
||||||
|
|
||||||
|
|
||||||
import de.kif.backend.authenticateOrRedirect
|
import de.kif.backend.authenticateOrRedirect
|
||||||
import de.kif.backend.repository.TrackRepository
|
import de.kif.backend.repository.TrackRepository
|
||||||
import de.kif.backend.repository.WorkGroupRepository
|
import de.kif.backend.repository.WorkGroupRepository
|
||||||
|
@ -8,9 +7,7 @@ import de.kif.backend.view.MainTemplate
|
||||||
import de.kif.backend.view.MenuTemplate
|
import de.kif.backend.view.MenuTemplate
|
||||||
import de.kif.backend.view.TableTemplate
|
import de.kif.backend.view.TableTemplate
|
||||||
import de.kif.common.Search
|
import de.kif.common.Search
|
||||||
import de.kif.common.model.Language
|
import de.kif.common.model.*
|
||||||
import de.kif.common.model.Permission
|
|
||||||
import de.kif.common.model.WorkGroup
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.html.insert
|
import io.ktor.html.insert
|
||||||
import io.ktor.html.respondHtmlTemplate
|
import io.ktor.html.respondHtmlTemplate
|
||||||
|
@ -36,7 +33,6 @@ fun Route.workGroup() {
|
||||||
active = MenuTemplate.Tab.WORK_GROUP
|
active = MenuTemplate.Tab.WORK_GROUP
|
||||||
}
|
}
|
||||||
content {
|
content {
|
||||||
h1 { +"Work groups" }
|
|
||||||
insert(TableTemplate()) {
|
insert(TableTemplate()) {
|
||||||
searchValue = search
|
searchValue = search
|
||||||
|
|
||||||
|
@ -157,6 +153,17 @@ fun Route.workGroup() {
|
||||||
val workGroupId = call.parameters["id"]?.toLongOrNull() ?: return@get
|
val workGroupId = call.parameters["id"]?.toLongOrNull() ?: return@get
|
||||||
val editWorkGroup = WorkGroupRepository.get(workGroupId) ?: return@get
|
val editWorkGroup = WorkGroupRepository.get(workGroupId) ?: return@get
|
||||||
val tracks = TrackRepository.all()
|
val tracks = TrackRepository.all()
|
||||||
|
|
||||||
|
val workGroups = editWorkGroup.constraints.mapNotNull {
|
||||||
|
when (it.type) {
|
||||||
|
ConstraintType.NotAtSameTime -> it.number
|
||||||
|
ConstraintType.OnlyAfterWorkGroup -> it.number
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}.distinct().associateWith {
|
||||||
|
WorkGroupRepository.get(it)!!
|
||||||
|
}
|
||||||
|
|
||||||
call.respondHtmlTemplate(MainTemplate()) {
|
call.respondHtmlTemplate(MainTemplate()) {
|
||||||
menuTemplate {
|
menuTemplate {
|
||||||
this.user = user
|
this.user = user
|
||||||
|
@ -201,7 +208,6 @@ fun Route.workGroup() {
|
||||||
htmlFor = "track"
|
htmlFor = "track"
|
||||||
+"Track"
|
+"Track"
|
||||||
}
|
}
|
||||||
//div("input-group") {
|
|
||||||
select(
|
select(
|
||||||
classes = "form-control"
|
classes = "form-control"
|
||||||
) {
|
) {
|
||||||
|
@ -220,12 +226,6 @@ fun Route.workGroup() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
a("/tracks", classes = "form-btn") {
|
|
||||||
i("material-icons") { +"edit" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
div("form-group") {
|
div("form-group") {
|
||||||
label {
|
label {
|
||||||
|
@ -294,6 +294,149 @@ fun Route.workGroup() {
|
||||||
+"Resolution"
|
+"Resolution"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "internet",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "internet"
|
||||||
|
checked = editWorkGroup.internet
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "internet"
|
||||||
|
+"Internet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "whiteboard",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "whiteboard"
|
||||||
|
checked = editWorkGroup.whiteboard
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "whiteboard"
|
||||||
|
+"Whiteboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "blackboard",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "blackboard"
|
||||||
|
checked = editWorkGroup.blackboard
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "blackboard"
|
||||||
|
+"Blackboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "accessible",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "accessible"
|
||||||
|
checked = editWorkGroup.accessible
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "accessible"
|
||||||
|
+"Accessible"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("work-group-constraints")
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-group work-group-constraints") {
|
||||||
|
label {
|
||||||
|
+"Constraints"
|
||||||
|
}
|
||||||
|
span("form-btn work-group-constraints-add") {
|
||||||
|
i("material-icons") { +"add" }
|
||||||
|
}
|
||||||
|
div("work-group-constraints-add-list") {
|
||||||
|
|
||||||
|
}
|
||||||
|
for ((index, constraint) in editWorkGroup.constraints.withIndex()) {
|
||||||
|
div("input-group") {
|
||||||
|
when (constraint.type) {
|
||||||
|
ConstraintType.OnlyOnDay -> {
|
||||||
|
span("form-btn") {
|
||||||
|
+"On day"
|
||||||
|
}
|
||||||
|
input(
|
||||||
|
name = "constraint-only-on-day-$index",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.number
|
||||||
|
) {
|
||||||
|
value = constraint.number.toString()
|
||||||
|
|
||||||
|
min = "-1337"
|
||||||
|
max = "1337"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConstraintType.OnlyAfterTime -> {
|
||||||
|
span("form-btn") {
|
||||||
|
+"After time"
|
||||||
|
}
|
||||||
|
input(
|
||||||
|
name = "constraint-only-after-time-$index",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.number
|
||||||
|
) {
|
||||||
|
value = constraint.number.toString()
|
||||||
|
|
||||||
|
min = "-1337"
|
||||||
|
max = "133700"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConstraintType.NotAtSameTime -> {
|
||||||
|
span("form-btn") {
|
||||||
|
+"Not with"
|
||||||
|
}
|
||||||
|
select(
|
||||||
|
classes = "form-control"
|
||||||
|
) {
|
||||||
|
name = "constraint-not-at-same-time-$index"
|
||||||
|
|
||||||
|
option {
|
||||||
|
selected = true
|
||||||
|
value = constraint.number.toString()
|
||||||
|
+(workGroups[constraint.number]?.name ?: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConstraintType.OnlyAfterWorkGroup -> {
|
||||||
|
span("form-btn") {
|
||||||
|
+"After AK"
|
||||||
|
}
|
||||||
|
select(
|
||||||
|
classes = "form-control"
|
||||||
|
) {
|
||||||
|
name = "constraint-only-after-work-group-$index"
|
||||||
|
|
||||||
|
option {
|
||||||
|
selected = true
|
||||||
|
value = constraint.number.toString()
|
||||||
|
+(workGroups[constraint.number]?.name ?: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div("form-group") {
|
div("form-group") {
|
||||||
|
@ -333,12 +476,37 @@ fun Route.workGroup() {
|
||||||
}
|
}
|
||||||
params["projector"]?.let { editWorkGroup = editWorkGroup.copy(projector = it == "on") }
|
params["projector"]?.let { editWorkGroup = editWorkGroup.copy(projector = it == "on") }
|
||||||
params["resolution"]?.let { editWorkGroup = editWorkGroup.copy(resolution = it == "on") }
|
params["resolution"]?.let { editWorkGroup = editWorkGroup.copy(resolution = it == "on") }
|
||||||
|
params["internet"]?.let { editWorkGroup = editWorkGroup.copy(internet = it == "on") }
|
||||||
|
params["whiteboard"]?.let { editWorkGroup = editWorkGroup.copy(whiteboard = it == "on") }
|
||||||
|
params["blackboard"]?.let { editWorkGroup = editWorkGroup.copy(blackboard = it == "on") }
|
||||||
|
params["accessible"]?.let { editWorkGroup = editWorkGroup.copy(accessible = it == "on") }
|
||||||
|
|
||||||
params["length"]?.toIntOrNull()?.let { editWorkGroup = editWorkGroup.copy(length = it) }
|
params["length"]?.toIntOrNull()?.let { editWorkGroup = editWorkGroup.copy(length = it) }
|
||||||
params["language"]?.let {
|
params["language"]?.let {
|
||||||
editWorkGroup =
|
editWorkGroup =
|
||||||
editWorkGroup.copy(language = Language.values().find { l -> l.code == it } ?: Language.GERMAN)
|
editWorkGroup.copy(language = Language.values().find { l -> l.code == it } ?: Language.GERMAN)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val constraints = params.mapNotNull { (key, value) ->
|
||||||
|
when {
|
||||||
|
key.startsWith("constraint-only-on-day") -> {
|
||||||
|
value?.toLongOrNull()?.let { Constraint(ConstraintType.OnlyOnDay, it) }
|
||||||
|
}
|
||||||
|
key.startsWith("constraint-only-after-time") -> {
|
||||||
|
value?.toLongOrNull()?.let { Constraint(ConstraintType.OnlyAfterTime, it) }
|
||||||
|
}
|
||||||
|
key.startsWith("constraint-not-at-same-time") -> {
|
||||||
|
value?.toLongOrNull()?.let { Constraint(ConstraintType.NotAtSameTime, it) }
|
||||||
|
}
|
||||||
|
key.startsWith("constraint-only-after-work-group") -> {
|
||||||
|
value?.toLongOrNull()?.let { Constraint(ConstraintType.OnlyAfterWorkGroup, it) }
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editWorkGroup = editWorkGroup.copy(constraints = constraints)
|
||||||
|
|
||||||
WorkGroupRepository.update(editWorkGroup)
|
WorkGroupRepository.update(editWorkGroup)
|
||||||
|
|
||||||
call.respondRedirect("/workgroups")
|
call.respondRedirect("/workgroups")
|
||||||
|
@ -477,6 +645,78 @@ fun Route.workGroup() {
|
||||||
+"Resolution"
|
+"Resolution"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "internet",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "internet"
|
||||||
|
checked = false
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "internet"
|
||||||
|
+"Internet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "whiteboard",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "whiteboard"
|
||||||
|
checked = false
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "whiteboard"
|
||||||
|
+"Whiteboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "blackboard",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "blackboard"
|
||||||
|
checked = false
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "blackboard"
|
||||||
|
+"Blackboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-group form-switch") {
|
||||||
|
input(
|
||||||
|
name = "accessible",
|
||||||
|
classes = "form-control",
|
||||||
|
type = InputType.checkBox
|
||||||
|
) {
|
||||||
|
id = "accessible"
|
||||||
|
checked = false
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
htmlFor = "accessible"
|
||||||
|
+"Accessible"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div("form-group work-group-constraints") {
|
||||||
|
label {
|
||||||
|
+"Constraints"
|
||||||
|
}
|
||||||
|
span("form-btn work-group-constraints-add") {
|
||||||
|
i("material-icons") { +"add" }
|
||||||
|
}
|
||||||
|
div("work-group-constraints-add-list") {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div("form-group") {
|
div("form-group") {
|
||||||
|
@ -511,6 +751,29 @@ fun Route.workGroup() {
|
||||||
Language.values().find { l -> l.code == it } ?: Language.GERMAN
|
Language.values().find { l -> l.code == it } ?: Language.GERMAN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val internet = params["internet"] == "on"
|
||||||
|
val whiteboard = params["whiteboard"] == "on"
|
||||||
|
val blackboard = params["blackboard"] == "on"
|
||||||
|
val accessible = params["accessible"] == "on"
|
||||||
|
|
||||||
|
val constraints = params.mapNotNull { (key, value) ->
|
||||||
|
when {
|
||||||
|
key.startsWith("constraint-only-on-day") -> {
|
||||||
|
value?.toLongOrNull()?.let { Constraint(ConstraintType.OnlyOnDay, it) }
|
||||||
|
}
|
||||||
|
key.startsWith("constraint-only-after-time") -> {
|
||||||
|
value?.toLongOrNull()?.let { Constraint(ConstraintType.OnlyAfterTime, it) }
|
||||||
|
}
|
||||||
|
key.startsWith("constraint-not-at-same-time") -> {
|
||||||
|
value?.toLongOrNull()?.let { Constraint(ConstraintType.NotAtSameTime, it) }
|
||||||
|
}
|
||||||
|
key.startsWith("constraint-only-after-work-group") -> {
|
||||||
|
value?.toLongOrNull()?.let { Constraint(ConstraintType.OnlyAfterWorkGroup, it) }
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val workGroup = WorkGroup(
|
val workGroup = WorkGroup(
|
||||||
null,
|
null,
|
||||||
name = name,
|
name = name,
|
||||||
|
@ -518,8 +781,13 @@ fun Route.workGroup() {
|
||||||
track = track,
|
track = track,
|
||||||
projector = projector,
|
projector = projector,
|
||||||
resolution = resolution,
|
resolution = resolution,
|
||||||
|
internet = internet,
|
||||||
|
whiteboard = whiteboard,
|
||||||
|
blackboard = blackboard,
|
||||||
|
accessible = accessible,
|
||||||
length = length,
|
length = length,
|
||||||
language = language
|
language = language,
|
||||||
|
constraints = constraints
|
||||||
)
|
)
|
||||||
|
|
||||||
WorkGroupRepository.create(workGroup)
|
WorkGroupRepository.create(workGroup)
|
||||||
|
|
47
src/jvmMain/kotlin/de/kif/backend/route/api/Constraints.kt
Normal file
47
src/jvmMain/kotlin/de/kif/backend/route/api/Constraints.kt
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package de.kif.backend.route.api
|
||||||
|
|
||||||
|
import de.kif.backend.authenticate
|
||||||
|
import de.kif.backend.repository.ScheduleRepository
|
||||||
|
import de.kif.common.checkConstraints
|
||||||
|
import de.kif.common.model.Permission
|
||||||
|
import io.ktor.application.call
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import io.ktor.routing.Route
|
||||||
|
import io.ktor.routing.get
|
||||||
|
|
||||||
|
fun Route.constraintsApi() {
|
||||||
|
get("/api/constraints") {
|
||||||
|
try {
|
||||||
|
authenticate(Permission.SCHEDULE) {
|
||||||
|
val schedules = ScheduleRepository.all()
|
||||||
|
|
||||||
|
val errors = checkConstraints(schedules, schedules)
|
||||||
|
|
||||||
|
call.success(errors)
|
||||||
|
} onFailure {
|
||||||
|
call.error(HttpStatusCode.Unauthorized)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {
|
||||||
|
call.error(HttpStatusCode.InternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/api/constraint/{id}") {
|
||||||
|
try {
|
||||||
|
authenticate(Permission.SCHEDULE) {
|
||||||
|
val id = call.parameters["id"]?.toLongOrNull()
|
||||||
|
val schedules = ScheduleRepository.all()
|
||||||
|
|
||||||
|
val check = schedules.filter { it.workGroup.id == id }
|
||||||
|
|
||||||
|
val errors = checkConstraints(check, schedules)
|
||||||
|
|
||||||
|
call.success(errors)
|
||||||
|
} onFailure {
|
||||||
|
call.error(HttpStatusCode.Unauthorized)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {
|
||||||
|
call.error(HttpStatusCode.InternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
src/jvmMain/kotlin/de/kif/backend/util/ParseMarkdown.kt
Normal file
60
src/jvmMain/kotlin/de/kif/backend/util/ParseMarkdown.kt
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package de.kif.backend.util
|
||||||
|
|
||||||
|
import com.vladsch.flexmark.ext.autolink.AutolinkExtension
|
||||||
|
import com.vladsch.flexmark.ext.emoji.EmojiExtension
|
||||||
|
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension
|
||||||
|
import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension
|
||||||
|
import com.vladsch.flexmark.ext.tables.TablesExtension
|
||||||
|
import com.vladsch.flexmark.util.ast.Node;
|
||||||
|
import com.vladsch.flexmark.html.HtmlRenderer;
|
||||||
|
import com.vladsch.flexmark.parser.Parser;
|
||||||
|
import com.vladsch.flexmark.util.options.MutableDataSet;
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/*
|
||||||
|
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
|
||||||
|
import org.intellij.markdown.html.HtmlGenerator
|
||||||
|
import org.intellij.markdown.parser.MarkdownParser
|
||||||
|
|
||||||
|
fun markdownToHtml(content: String): String {
|
||||||
|
val flavour = CommonMarkFlavourDescriptor()
|
||||||
|
val parsedTree = MarkdownParser(flavour).buildMarkdownTreeFromString(content)
|
||||||
|
val html = HtmlGenerator(content, parsedTree, flavour).generateHtml()
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
import org.commonmark.parser.Parser
|
||||||
|
import org.commonmark.renderer.html.HtmlRenderer
|
||||||
|
|
||||||
|
fun markdownToHtml(content: String): String {
|
||||||
|
val parser = Parser.builder().build()
|
||||||
|
val document = parser.parse(content)
|
||||||
|
val renderer = HtmlRenderer.builder().build()
|
||||||
|
return renderer.render(document)
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun markdownToHtml(content: String): String {
|
||||||
|
val options = MutableDataSet()
|
||||||
|
|
||||||
|
options.set(Parser.EXTENSIONS, Arrays.asList(
|
||||||
|
TablesExtension.create(),
|
||||||
|
StrikethroughExtension.create(),
|
||||||
|
TaskListExtension.create(),
|
||||||
|
EmojiExtension.create(),
|
||||||
|
AutolinkExtension.create()
|
||||||
|
));
|
||||||
|
|
||||||
|
//options.set(HtmlRenderer.SOFT_BREAK, "<br />\n");
|
||||||
|
|
||||||
|
val parser = Parser.builder(options).build()
|
||||||
|
val renderer = HtmlRenderer.builder(options).build()
|
||||||
|
|
||||||
|
// You can re-use parser and renderer instances
|
||||||
|
val document = parser.parse(content)
|
||||||
|
val html = renderer.render(document)
|
||||||
|
|
||||||
|
return html
|
||||||
|
}
|
|
@ -50,4 +50,5 @@ fun Route.pushService() {
|
||||||
TrackRepository.registerPushService()
|
TrackRepository.registerPushService()
|
||||||
UserRepository.registerPushService()
|
UserRepository.registerPushService()
|
||||||
WorkGroupRepository.registerPushService()
|
WorkGroupRepository.registerPushService()
|
||||||
|
PostRepository.registerPushService()
|
||||||
}
|
}
|
|
@ -7,14 +7,14 @@ import kotlinx.html.*
|
||||||
|
|
||||||
class MenuTemplate() : Template<FlowContent> {
|
class MenuTemplate() : Template<FlowContent> {
|
||||||
|
|
||||||
var active: Tab = Tab.DASHBOARD
|
var active: Tab = Tab.BOARD
|
||||||
var user: User? = null
|
var user: User? = null
|
||||||
|
|
||||||
override fun FlowContent.apply() {
|
override fun FlowContent.apply() {
|
||||||
nav("menu") {
|
nav("menu") {
|
||||||
div("container") {
|
div("container") {
|
||||||
div("menu-left") {
|
div("menu-left") {
|
||||||
a("/", classes = if (active == Tab.DASHBOARD) "active" else null) {
|
a("/", classes = if (active == Tab.BOARD) "active" else null) {
|
||||||
+"Dashboard"
|
+"Dashboard"
|
||||||
}
|
}
|
||||||
a("/calendar", classes = if (active == Tab.CALENDAR) "active" else null) {
|
a("/calendar", classes = if (active == Tab.CALENDAR) "active" else null) {
|
||||||
|
@ -42,7 +42,7 @@ class MenuTemplate() : Template<FlowContent> {
|
||||||
+"Rooms"
|
+"Rooms"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (user.checkPermission(Permission.PERSON)) {
|
if (user.checkPermission(Permission.USER)) {
|
||||||
a("/users", classes = if (active == Tab.USER) "active" else null) {
|
a("/users", classes = if (active == Tab.USER) "active" else null) {
|
||||||
+"Users"
|
+"Users"
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,6 @@ class MenuTemplate() : Template<FlowContent> {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Tab {
|
enum class Tab {
|
||||||
DASHBOARD, CALENDAR, ACCOUNT, WORK_GROUP, ROOM, PERSON, USER
|
BOARD, CALENDAR, ACCOUNT, WORK_GROUP, ROOM, PERSON, USER
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue