Improve calendar header design

This commit is contained in:
Lars Westermann 2019-05-31 16:39:43 +02:00
parent 5c058f7fdc
commit df8f04099a
Signed by: lars.westermann
GPG key ID: 9D417FA5BB9D5E1D
12 changed files with 136 additions and 48 deletions

View file

@ -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)

View file

@ -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()

View file

@ -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)

View file

@ -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))
}

View file

@ -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;

View file

@ -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") {

View file

@ -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 {

View file

@ -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++

View file

@ -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") {

View file

@ -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)
}
}
}

View file

@ -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.*

View file

@ -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,