Improve calendar header design
This commit is contained in:
parent
5c058f7fdc
commit
df8f04099a
12 changed files with 136 additions and 48 deletions
|
@ -5,7 +5,7 @@ import com.soywiz.klock.KlockLocale
|
|||
import com.soywiz.klock.format
|
||||
import com.soywiz.klock.locale.german
|
||||
|
||||
fun formatDate(unix: Long) =
|
||||
fun formatDateTime(unix: Long) =
|
||||
DateFormat("EEEE, d. MMMM y HH:mm")
|
||||
.withLocale(KlockLocale.german)
|
||||
.format(unix)
|
||||
|
|
|
@ -80,9 +80,7 @@ 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"
|
||||
val view = View.wrap(document.getElementById("calendar-check-constraints") as HTMLElement)
|
||||
view.onClick {
|
||||
launch {
|
||||
val errors = ScheduleRepository.checkConstraints()
|
||||
|
|
|
@ -14,13 +14,14 @@ import de.westermann.kwebview.extra.listFactory
|
|||
import org.w3c.dom.HTMLButtonElement
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.HTMLInputElement
|
||||
import kotlin.browser.document
|
||||
|
||||
class CalendarEdit(
|
||||
private val calendar: Calendar, view: HTMLElement
|
||||
) : View(view) {
|
||||
|
||||
private val toggleEditButton =
|
||||
Button.wrap(view.querySelector(".calendar-edit-top button") as HTMLButtonElement)
|
||||
Button.wrap(document.getElementById("calendar-edit-button") as HTMLButtonElement)
|
||||
|
||||
val search =
|
||||
InputView.wrap(view.querySelector(".calendar-edit-search input") as HTMLInputElement)
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
package de.kif.frontend.views.overview
|
||||
|
||||
import de.kif.common.formatDate
|
||||
import de.kif.common.formatDateTime
|
||||
import de.kif.frontend.launch
|
||||
import de.kif.frontend.repository.PostRepository
|
||||
import de.westermann.kobserve.event.emit
|
||||
import de.westermann.kwebview.View
|
||||
import de.westermann.kwebview.components.Link
|
||||
import de.westermann.kwebview.createHtmlView
|
||||
import org.w3c.dom.HTMLAnchorElement
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.get
|
||||
import org.w3c.dom.set
|
||||
import kotlin.browser.document
|
||||
|
||||
class PostView(
|
||||
view: HTMLElement
|
||||
|
@ -47,7 +45,7 @@ class PostView(
|
|||
}
|
||||
|
||||
contentView.innerHTML = PostRepository.htmlByUrl(p.url)
|
||||
footerView.innerText = formatDate(p.createdAt)
|
||||
footerView.innerText = formatDateTime(p.createdAt)
|
||||
|
||||
emit(PostChangeEvent(postId))
|
||||
}
|
||||
|
|
|
@ -303,8 +303,10 @@ a {
|
|||
position: absolute;
|
||||
z-index: 1;
|
||||
background: $background-primary-color;
|
||||
width: 100%;
|
||||
border: solid 1px $table-border-color;
|
||||
width: 10rem;
|
||||
border: solid 1px $input-border-color;
|
||||
border-radius: $border-radius;
|
||||
padding: 0.5rem 0;
|
||||
|
||||
span {
|
||||
padding: 0 0.5rem;
|
||||
|
@ -507,6 +509,33 @@ form {
|
|||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 2.2rem;
|
||||
line-height: 2.2rem;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
float: left;
|
||||
|
||||
& > * {
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 1.5rem;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.header-right {
|
||||
float: right;
|
||||
|
||||
&::after {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
@ -528,8 +557,7 @@ form {
|
|||
display: block;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -3rem;
|
||||
padding-top: 3rem;
|
||||
top: 0;
|
||||
|
||||
.calendar-edit-main {
|
||||
position: sticky;
|
||||
|
@ -538,19 +566,20 @@ form {
|
|||
transition: margin-left $transitionTime, opacity $transitionTime, visibility $transitionTime;
|
||||
top: 1rem;
|
||||
visibility: hidden;
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-edit-top {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.calendar-edit-search {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.calendar-edit-list {
|
||||
height: calc(100% - 4.5rem);
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.calendar-work-group {
|
||||
position: relative;
|
||||
display: block;
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.uchuhimo.konf.Item
|
|||
import java.io.FileNotFoundException
|
||||
import java.nio.file.Paths
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.ZoneId
|
||||
import java.util.*
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
|
@ -58,7 +59,11 @@ object Configuration {
|
|||
|
||||
object Schedule {
|
||||
val reference by c(ScheduleSpec.reference)
|
||||
val referenceDate: Date by lazy { SimpleDateFormat("yyyy-MM-dd").parse(reference) }
|
||||
val referenceDate: Date by lazy {
|
||||
val sdf = SimpleDateFormat("yyyy-MM-dd")
|
||||
sdf.timeZone = TimeZone.getTimeZone(ZoneId.of("UTC"))
|
||||
sdf.parse(reference)
|
||||
}
|
||||
}
|
||||
|
||||
private object SecuritySpec : ConfigSpec("security") {
|
||||
|
|
|
@ -3,7 +3,9 @@ package de.kif.backend.repository
|
|||
import de.kif.backend.database.DbSchedule
|
||||
import de.kif.backend.database.dbQuery
|
||||
import de.kif.backend.util.PushService
|
||||
import de.kif.common.*
|
||||
import de.kif.common.MessageType
|
||||
import de.kif.common.Repository
|
||||
import de.kif.common.RepositoryType
|
||||
import de.kif.common.model.Schedule
|
||||
import de.westermann.kobserve.event.EventHandler
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
@ -139,6 +141,15 @@ object ScheduleRepository : Repository<Schedule> {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getDayRange(): IntRange {
|
||||
val schedules = all()
|
||||
|
||||
val min = schedules.minBy { it.day }?.day ?: 0
|
||||
val max = schedules.maxBy { it.day }?.day ?: 0
|
||||
|
||||
return min..max
|
||||
}
|
||||
|
||||
init {
|
||||
RoomRepository.onUpdate { roomId ->
|
||||
runBlocking {
|
||||
|
|
|
@ -2,7 +2,7 @@ package de.kif.backend.route
|
|||
|
||||
import de.kif.backend.authenticate
|
||||
import de.kif.backend.authenticateOrRedirect
|
||||
import de.kif.backend.backup.Backup
|
||||
import de.kif.backend.util.Backup
|
||||
import de.kif.backend.repository.TrackRepository
|
||||
import de.kif.backend.repository.WorkGroupRepository
|
||||
import de.kif.backend.route.api.error
|
||||
|
@ -60,25 +60,25 @@ fun Route.account() {
|
|||
div("account-backup") {
|
||||
if (user.checkPermission(Permission.ROOM)) {
|
||||
a("/account/backup/rooms.json", classes = "form-btn") {
|
||||
attributes["download"] = "rooms-backup"
|
||||
attributes["download"] = "rooms-backup.json"
|
||||
+"Create room backup"
|
||||
}
|
||||
}
|
||||
if (user.checkPermission(Permission.USER)) {
|
||||
a("/account/backup/users.json", classes = "form-btn") {
|
||||
attributes["download"] = "users-backup"
|
||||
attributes["download"] = "users-backup.json"
|
||||
+"Create user backup"
|
||||
}
|
||||
}
|
||||
if (user.checkPermission(Permission.POST)) {
|
||||
a("/account/backup/posts.json", classes = "form-btn") {
|
||||
attributes["download"] = "posts-backup"
|
||||
attributes["download"] = "posts-backup.json"
|
||||
+"Create post backup"
|
||||
}
|
||||
}
|
||||
if (user.checkPermission(Permission.WORK_GROUP)) {
|
||||
a("/account/backup/work-groups.json", classes = "form-btn") {
|
||||
attributes["download"] = "work-groups-backup"
|
||||
attributes["download"] = "work-groups-backup.json"
|
||||
+"Create work group backup"
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ fun Route.account() {
|
|||
user.checkPermission(Permission.SCHEDULE)
|
||||
) {
|
||||
a("/account/backup/schedules.json", classes = "form-btn") {
|
||||
attributes["download"] = "schedules-backup"
|
||||
attributes["download"] = "schedules-backup.json"
|
||||
+"Create schedule backup"
|
||||
}
|
||||
}
|
||||
|
@ -205,6 +205,20 @@ fun Route.account() {
|
|||
+"Skip existing work groups"
|
||||
}
|
||||
}
|
||||
div("form-group form-switch") {
|
||||
input(
|
||||
name = "delete-existing",
|
||||
classes = "form-control",
|
||||
type = InputType.checkBox
|
||||
) {
|
||||
id = "delete-existing"
|
||||
checked = false
|
||||
}
|
||||
label {
|
||||
htmlFor = "delete-existing"
|
||||
+"Delete existing work groups"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button(type = ButtonType.submit, classes = "form-btn btn-primary") {
|
||||
|
@ -304,6 +318,7 @@ fun Route.account() {
|
|||
}
|
||||
|
||||
val skipExisting = params["skip-existing"] == "on"
|
||||
val deleteExisting = params["delete-existing"] == "on"
|
||||
|
||||
val map = mutableMapOf<Int, Pair<String, Long?>>()
|
||||
|
||||
|
@ -324,12 +339,22 @@ fun Route.account() {
|
|||
|
||||
val sections = map.values.toMap()
|
||||
|
||||
val existingWorkGroups = WorkGroupRepository.all().map { it.name }.toSet()
|
||||
var existingWorkGroups = WorkGroupRepository.all()
|
||||
|
||||
if (deleteExisting) {
|
||||
for (wg in existingWorkGroups) {
|
||||
if (wg.id != null) WorkGroupRepository.delete(wg.id)
|
||||
}
|
||||
existingWorkGroups = emptyList()
|
||||
}
|
||||
|
||||
val existingWorkGroupNames = existingWorkGroups.map { it.name }.toSet()
|
||||
|
||||
val importedWorkGroups = WikiImporter.import(sections = sections)
|
||||
|
||||
var counter = 0
|
||||
for (workGroup in importedWorkGroups) {
|
||||
if (skipExisting && workGroup.name in existingWorkGroups) continue
|
||||
if (skipExisting && workGroup.name in existingWorkGroupNames) continue
|
||||
|
||||
WorkGroupRepository.create(workGroup)
|
||||
counter++
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package de.kif.backend.route
|
||||
|
||||
import com.soywiz.klock.*
|
||||
import com.soywiz.klock.locale.german
|
||||
import de.kif.backend.Configuration
|
||||
import de.kif.backend.authenticateOrRedirect
|
||||
import de.kif.backend.isAuthenticated
|
||||
import de.kif.backend.repository.RoomRepository
|
||||
|
@ -255,6 +258,11 @@ fun Route.calendar() {
|
|||
|
||||
val day = call.parameters["day"]?.toIntOrNull() ?: return@get
|
||||
|
||||
val range = ScheduleRepository.getDayRange()
|
||||
if (!editable && day !in range) {
|
||||
return@get
|
||||
}
|
||||
|
||||
val rooms = RoomRepository.all()
|
||||
|
||||
val orientation = call.request.cookies["orientation"]?.let { name ->
|
||||
|
@ -289,6 +297,12 @@ fun Route.calendar() {
|
|||
min = (min / 60 - 1) * 60
|
||||
max = (max / 60 + 2) * 60
|
||||
|
||||
val refDate = DateTime(Configuration.Schedule.referenceDate.time)
|
||||
val date = refDate + day.days
|
||||
val dateString = DateFormat("EEEE, d. MMMM")
|
||||
.withLocale(KlockLocale.german)
|
||||
.format(date)
|
||||
|
||||
call.respondHtmlTemplate(MainTemplate()) {
|
||||
menuTemplate {
|
||||
this.user = user
|
||||
|
@ -301,19 +315,33 @@ fun Route.calendar() {
|
|||
|
||||
div("header") {
|
||||
div("header-left") {
|
||||
a("/calendar/${day - 1}") { +"<" }
|
||||
span {
|
||||
+"Day $day"
|
||||
if (day - 1 in range) {
|
||||
a("/calendar/${day - 1}") { i("material-icons") { +"chevron_left" } }
|
||||
}
|
||||
span {
|
||||
+dateString
|
||||
}
|
||||
if (day + 1 in range) {
|
||||
a("/calendar/${day + 1}") { i("material-icons") { +"chevron_right" } }
|
||||
}
|
||||
a("/calendar/${day + 1}") { +">" }
|
||||
}
|
||||
div("header-right") {
|
||||
a("/calendar/$day/rtt") {
|
||||
a("/calendar/$day/rtt", classes = "form-btn") {
|
||||
+"Room to time"
|
||||
}
|
||||
a("/calendar/$day/ttr") {
|
||||
a("/calendar/$day/ttr", classes = "form-btn") {
|
||||
+"Time to room"
|
||||
}
|
||||
if (editable) {
|
||||
button(classes = "form-btn") {
|
||||
id = "calendar-check-constraints"
|
||||
+"Check constraints"
|
||||
}
|
||||
button(classes = "form-btn") {
|
||||
id = "calendar-edit-button"
|
||||
+"Edit"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,11 +372,6 @@ fun Route.calendar() {
|
|||
|
||||
if (editable) {
|
||||
div("calendar-edit") {
|
||||
div("calendar-edit-top") {
|
||||
button(classes = "form-btn") {
|
||||
+"Edit"
|
||||
}
|
||||
}
|
||||
div("calendar-edit-main") {
|
||||
div("calendar-edit-search") {
|
||||
input(InputType.search, name = "search", classes = "form-control") {
|
||||
|
|
|
@ -8,7 +8,7 @@ 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.formatDate
|
||||
import de.kif.common.formatDateTime
|
||||
import de.kif.common.model.Permission
|
||||
import de.kif.common.model.Post
|
||||
import io.ktor.application.call
|
||||
|
@ -62,7 +62,7 @@ fun DIV.createPost(post: Post, editable: Boolean = false, additionalClasses: Str
|
|||
}
|
||||
}
|
||||
div("post-footer") {
|
||||
+formatDate(post.createdAt)
|
||||
+formatDateTime(post.createdAt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package de.kif.backend.backup
|
||||
package de.kif.backend.util
|
||||
|
||||
import de.kif.backend.database.Connection
|
||||
import de.kif.backend.repository.*
|
|
@ -84,8 +84,6 @@ object WikiImporter {
|
|||
}
|
||||
.toMap()
|
||||
|
||||
println(ak)
|
||||
|
||||
val name = ak["name"]?.trim() ?: continue
|
||||
var desc = ak["beschreibung"]?.trim() ?: ""
|
||||
val participant = ak["wieviele"]?.trim() ?: ""
|
||||
|
@ -96,9 +94,11 @@ object WikiImporter {
|
|||
if (name.isBlank() && desc.isBlank()) continue
|
||||
|
||||
if (who.isNotBlank() || time.isNotBlank() || length.isNotBlank()) {
|
||||
desc += "\n"
|
||||
desc += "\n"
|
||||
desc += "--- Aus dem Wiki übernommen ---"
|
||||
desc += "\n"
|
||||
desc += "\n"
|
||||
|
||||
if (who.isNotBlank()) {
|
||||
desc += "Wer = $who\n"
|
||||
|
@ -118,7 +118,7 @@ object WikiImporter {
|
|||
val match = regex.find(length)
|
||||
|
||||
if (match != null) {
|
||||
akLength = max(akLength, match.groupValues.getOrNull(1)?.toIntOrNull() ?: 0)
|
||||
akLength = max(akLength, (match.groupValues.getOrNull(1)?.toIntOrNull() ?: 0) * 60)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,8 +133,6 @@ object WikiImporter {
|
|||
}
|
||||
}
|
||||
|
||||
println(1)
|
||||
|
||||
workGroups += WorkGroup(
|
||||
id = null,
|
||||
name = name,
|
||||
|
|
Loading…
Reference in a new issue