diff --git a/src/commonMain/kotlin/de/kif/common/CacheRepository.kt b/src/commonMain/kotlin/de/kif/common/CachedRepository.kt similarity index 64% rename from src/commonMain/kotlin/de/kif/common/CacheRepository.kt rename to src/commonMain/kotlin/de/kif/common/CachedRepository.kt index f205dfe..83da80d 100644 --- a/src/commonMain/kotlin/de/kif/common/CacheRepository.kt +++ b/src/commonMain/kotlin/de/kif/common/CachedRepository.kt @@ -3,15 +3,14 @@ package de.kif.common import de.kif.common.model.Model import de.westermann.kobserve.event.EventHandler - -class CacheRepository(val repository: Repository) : Repository { +abstract class CachedRepository(val repository: Repository) : Repository { override val onCreate = EventHandler() override val onUpdate = EventHandler() override val onDelete = EventHandler() - var cache: Map = emptyMap() - var cacheComplete: Boolean = false + private var cache: Map = emptyMap() + private var cacheComplete: Boolean = false override suspend fun get(id: Long): T? { return if (id in cache) { @@ -32,29 +31,37 @@ class CacheRepository(val repository: Repository) : Repository } 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 { - if (cacheComplete) { - return cache.values.toList() + return if (cacheComplete) { + cache.values.toList() } else { val all = repository.all() cache = all.associateBy { it.id!! } cacheComplete = true - return all + all } } + init { + repository.onCreate { + cacheComplete = false + cache = cache - it + } + repository.onUpdate { + cacheComplete = false + cache = cache - it + } + repository.onDelete { + cache = cache - it + } + } } diff --git a/src/commonMain/kotlin/de/kif/common/model/Schedule.kt b/src/commonMain/kotlin/de/kif/common/model/Schedule.kt index 5530c8e..d3905c1 100644 --- a/src/commonMain/kotlin/de/kif/common/model/Schedule.kt +++ b/src/commonMain/kotlin/de/kif/common/model/Schedule.kt @@ -10,6 +10,8 @@ data class Schedule( val room: Room, val day: Int, val time: Int, + val lookRoom: Boolean, + val lookTime: Boolean, override val createdAt: Long = 0, override val updateAt: Long = 0 ) : Model { @@ -27,4 +29,16 @@ data class Schedule( fun getAbsoluteStartTime(): Int = day * 60 * 24 + time fun getAbsoluteEndTime(): Int = getAbsoluteStartTime() + workGroup.length + + companion object { + fun timeToString(time: Int): String { + var day = time % (24 * 60) + if (day < 0) day += 24 * 60 + + val hour = day / 60 + val minute = day % 60 + + return hour.toString().padStart(2, '0') + ":" + minute.toString().padStart(2, '0') + } + } } diff --git a/src/jsMain/kotlin/de/kif/frontend/views/calendar/Calendar.kt b/src/jsMain/kotlin/de/kif/frontend/views/calendar/Calendar.kt index 2330319..a992836 100644 --- a/src/jsMain/kotlin/de/kif/frontend/views/calendar/Calendar.kt +++ b/src/jsMain/kotlin/de/kif/frontend/views/calendar/Calendar.kt @@ -78,8 +78,6 @@ class Calendar(calendar: HTMLElement) : View(calendar) { } } - val cont = document.getElementsByClassName("header-right")[0] as HTMLElement - val view = View.wrap(document.getElementById("calendar-check-constraints") as HTMLElement) view.onClick { launch { diff --git a/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarEntry.kt b/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarEntry.kt index 3364628..be5fed6 100644 --- a/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarEntry.kt +++ b/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarEntry.kt @@ -1,6 +1,7 @@ package de.kif.frontend.views.calendar import de.kif.common.CALENDAR_GRID_WIDTH +import de.kif.common.model.Room import de.kif.common.model.Schedule import de.kif.common.model.WorkGroup import de.kif.frontend.iterator @@ -36,6 +37,9 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi var editable: Boolean = false + var moveLookRoom: Long? = null + var moveLookTime: Int? = null + private fun onMove(event: MouseEvent) { val position = event.toPoint() - mouseDelta @@ -44,6 +48,13 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi } if (cell != null) { + if (moveLookRoom != null && cell.roomId != moveLookRoom) { + return + } + if (moveLookTime != null && cell.time != moveLookTime) { + return + } + cell += this if (newCell == null) { @@ -80,6 +91,10 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi } } + launch { + calendarTools.setName(cell.getRoom(), cell.time) + } + newCell = cell } @@ -104,7 +119,9 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi workGroup, newRoom, calendar.day, - newTime + newTime, + lookRoom = false, + lookTime = false ) ) html.remove() @@ -127,6 +144,8 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi private var listeners: List> = emptyList() fun startDrag() { + classList += "drag" + listeners = listOf( Body.onMouseMove.reference(this::onMove), Body.onMouseUp.reference(this::onFinishMove), @@ -134,6 +153,8 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi ) } + private val calendarTools = CalendarTools(this) + init { onMouseDown { event -> if (!editable || event.target != html || "pending" in classList) { @@ -142,8 +163,6 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi } launch { - classList += "drag" - val s = schedule.get() val time = s.time / CALENDAR_GRID_WIDTH * CALENDAR_GRID_WIDTH @@ -153,22 +172,20 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi mouseDelta = event.toPoint() - p + moveLookRoom = if (s.lookRoom) s.room.id else null + moveLookTime = if (s.lookTime) s.time else null + startDrag() } event.preventDefault() event.stopPropagation() } - var calendarTools: CalendarTools? = null - for (item in html.children) { - if (item.classList.contains("calendar-tools")) { - calendarTools = CalendarTools(this, item) - break - } - } - if (calendarTools == null) { - calendarTools = CalendarTools(this, createHtmlView()) - html.appendChild(calendarTools.html) + html.appendChild(calendarTools.html) + + launch { + val s = schedule.get() + calendarTools.update(s) } } @@ -188,6 +205,7 @@ class CalendarEntry(private val calendar: Calendar, view: HTMLElement) : View(vi } load(schedule.workGroup) + calendarTools.update(schedule) val time = schedule.time / CALENDAR_GRID_WIDTH * CALENDAR_GRID_WIDTH val cell = calendar.calendarCells.find { diff --git a/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarTools.kt b/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarTools.kt index 227f130..4f6a085 100644 --- a/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarTools.kt +++ b/src/jsMain/kotlin/de/kif/frontend/views/calendar/CalendarTools.kt @@ -1,129 +1,116 @@ package de.kif.frontend.views.calendar -import de.kif.common.CALENDAR_GRID_WIDTH -import de.kif.frontend.iterator +import de.kif.common.model.Room +import de.kif.common.model.Schedule import de.kif.frontend.launch import de.kif.frontend.repository.ScheduleRepository import de.westermann.kwebview.View -import de.westermann.kwebview.createHtmlView -import org.w3c.dom.HTMLAnchorElement -import org.w3c.dom.HTMLElement -import org.w3c.dom.events.EventListener +import de.westermann.kwebview.ViewCollection +import de.westermann.kwebview.components.* -class CalendarTools(entry: CalendarEntry, view: HTMLElement) : View(view) { +class CalendarTools(entry: CalendarEntry) : ViewCollection() { + + fun setName(room: Room, time: Int) { + nameView.text = room.name + " - " + Schedule.timeToString(time) + } + + lateinit var schedule: Schedule + + fun update(schedule: Schedule) { + this.schedule = schedule + setName(schedule.room, schedule.time) + lookRoomButton.classList["disabled"] = !schedule.lookRoom + lookTimeButton.classList["disabled"] = !schedule.lookTime + } + + private lateinit var nameView: TextView + private lateinit var lookRoomButton: IconView + private lateinit var lookTimeButton: IconView init { - var linkM10: HTMLAnchorElement? = null - var linkM5: HTMLAnchorElement? = null - var linkReset: HTMLAnchorElement? = null - var linkP5: HTMLAnchorElement? = null - var linkP10: HTMLAnchorElement? = null - var linkDel: HTMLAnchorElement? = null + boxView { + nameView = textView { } - for (element in html.children) { - when { - element.classList.contains("calendar-tools-m10") -> linkM10 = element as? HTMLAnchorElement - element.classList.contains("calendar-tools-m5") -> linkM5 = element as? HTMLAnchorElement - element.classList.contains("calendar-tools-reset") -> linkReset = element as? HTMLAnchorElement - element.classList.contains("calendar-tools-p5") -> linkP5 = element as? HTMLAnchorElement - element.classList.contains("calendar-tools-p10") -> linkP10 = element as? HTMLAnchorElement - element.classList.contains("calendar-tools-del") -> linkDel = element as? HTMLAnchorElement + lookRoomButton = iconView(MaterialIcon.GPS_FIXED) { + title = "Look room" + onClick { + entry.pending = true + launch { + val s = entry.schedule.get() + ScheduleRepository.update(s.copy(lookRoom = "disabled" in this.classList)) + } + } + } + lookTimeButton = iconView(MaterialIcon.ALARM) { + title = "Look time slot" + onClick { + entry.pending = true + launch { + val s = entry.schedule.get() + ScheduleRepository.update(s.copy(lookTime = "disabled" in this.classList)) + } + } } } + boxView { + textView("-10") { + title = "Schedule 10 minutes earlier" + onClick { + if (schedule.lookTime) return@onClick - linkM10 = linkM10 ?: run { - val link = createHtmlView() - link.classList.add("calendar-tools-m10") - link.textContent = "-10" - html.appendChild(link) - link - } - linkM10.removeAttribute("href") - linkM10.addEventListener("click", EventListener { - entry.pending = true - launch { - val s = entry.schedule.get() - ScheduleRepository.update(s.copy(time = s.time - 10)) + entry.pending = true + launch { + val s = entry.schedule.get() + ScheduleRepository.update(s.copy(time = s.time - 10)) + } + } } - }) + textView("-5") { + title = "Schedule 5 minutes earlier" + onClick { + if (schedule.lookTime) return@onClick - linkM5 = linkM5 ?: run { - val link = createHtmlView() - link.classList.add("calendar-tools-m5") - link.textContent = "-5" - html.appendChild(link) - link - } - linkM5.removeAttribute("href") - linkM5.addEventListener("click", EventListener { - entry.pending = true - launch { - val s = entry.schedule.get() - ScheduleRepository.update(s.copy(time = s.time - 5)) + entry.pending = true + launch { + val s = entry.schedule.get() + ScheduleRepository.update(s.copy(time = s.time - 5)) + } + } } - }) + textView("+5") { + title = "Schedule 5 minutes later" + onClick { + if (schedule.lookTime) return@onClick - linkReset = linkReset ?: run { - val link = createHtmlView() - link.classList.add("calendar-tools-reset") - link.textContent = "reset" - html.appendChild(link) - link - } - linkReset.removeAttribute("href") - linkReset.addEventListener("click", EventListener { - entry.pending = true - launch { - val s = entry.schedule.get() - ScheduleRepository.update(s.copy(time = s.time / CALENDAR_GRID_WIDTH * CALENDAR_GRID_WIDTH)) + entry.pending = true + launch { + val s = entry.schedule.get() + ScheduleRepository.update(s.copy(time = s.time + 5)) + } + } } - }) + textView("+10") { + title = "Schedule 10 minutes later" + onClick { + if (schedule.lookTime) return@onClick - linkP5 = linkP5 ?: run { - val link = createHtmlView() - link.classList.add("calendar-tools-p5") - link.textContent = "+5" - html.appendChild(link) - link - } - linkP5.removeAttribute("href") - linkP5.addEventListener("click", EventListener { - entry.pending = true - launch { - val s = entry.schedule.get() - ScheduleRepository.update(s.copy(time = s.time + 5)) + entry.pending = true + launch { + val s = entry.schedule.get() + ScheduleRepository.update(s.copy(time = s.time + 10)) + } + } } - }) - linkP10 = linkP10 ?: run { - val link = createHtmlView() - link.classList.add("calendar-tools-p10") - link.textContent = "+10" - html.appendChild(link) - link - } - linkP10.removeAttribute("href") - linkP10.addEventListener("click", EventListener { - entry.pending = true - launch { - val s = entry.schedule.get() - ScheduleRepository.update(s.copy(time = s.time + 10)) + iconView(MaterialIcon.DELETE) { + title = "Delete" + onClick { + entry.pending = true + launch { + ScheduleRepository.delete(entry.scheduleId) + } + } } - }) - - linkDel = linkDel ?: run { - val link = createHtmlView() - link.classList.add("calendar-tools-del") - link.textContent = "del" - html.appendChild(link) - link } - linkDel.removeAttribute("href") - linkDel.addEventListener("click", EventListener { - entry.pending = true - launch { - ScheduleRepository.delete(entry.scheduleId) - } - }) } } \ No newline at end of file diff --git a/src/jsMain/kotlin/de/westermann/kwebview/components/Icon.kt b/src/jsMain/kotlin/de/westermann/kwebview/components/Icon.kt new file mode 100644 index 0000000..7ec5f49 --- /dev/null +++ b/src/jsMain/kotlin/de/westermann/kwebview/components/Icon.kt @@ -0,0 +1,15 @@ +package de.westermann.kwebview.components + +import org.w3c.dom.Element + +/** + * Base interface for all icons. + * + * @author lars + */ +interface Icon { + /** + * Dom element that represents an icon. + */ + val element: Element +} \ No newline at end of file diff --git a/src/jsMain/kotlin/de/westermann/kwebview/components/IconView.kt b/src/jsMain/kotlin/de/westermann/kwebview/components/IconView.kt new file mode 100644 index 0000000..bc39bf4 --- /dev/null +++ b/src/jsMain/kotlin/de/westermann/kwebview/components/IconView.kt @@ -0,0 +1,53 @@ +package de.westermann.kwebview.components + +import de.westermann.kobserve.Property +import de.westermann.kobserve.ReadOnlyProperty +import de.westermann.kobserve.property.property +import de.westermann.kwebview.KWebViewDsl +import de.westermann.kwebview.View +import de.westermann.kwebview.ViewCollection +import de.westermann.kwebview.createHtmlView +import org.w3c.dom.HTMLSpanElement +import kotlin.dom.clear + +/** + * Represents all kinds of icon views. + * + * @author lars + */ +class IconView(icon: Icon?) : View(createHtmlView()) { + + override val html = super.html as HTMLSpanElement + + fun bind(property: ReadOnlyProperty) { + iconProperty.bind(property) + } + + fun unbind() { + iconProperty.unbind() + } + + var icon: Icon? = null + set(value) { + field = value + html.clear() + value?.let { + html.appendChild(it.element) + } + iconProperty.invalidate() + } + + val iconProperty: Property = property(this::icon) + + init { + this.icon = icon + } +} + +@KWebViewDsl +fun ViewCollection.iconView(icon: Icon? = null, init: IconView.() -> Unit = {}) = + IconView(icon).also(this::append).also(init) + +@KWebViewDsl +fun ViewCollection.iconView(icon: ReadOnlyProperty, init: IconView.() -> Unit = {}) = + IconView(icon.value).also(this::append).also { it.bind(icon) }.also(init) diff --git a/src/jsMain/kotlin/de/westermann/kwebview/components/MaterialIcon.kt b/src/jsMain/kotlin/de/westermann/kwebview/components/MaterialIcon.kt new file mode 100644 index 0000000..45c94bb --- /dev/null +++ b/src/jsMain/kotlin/de/westermann/kwebview/components/MaterialIcon.kt @@ -0,0 +1,948 @@ +package de.westermann.kwebview.components + +import org.w3c.dom.Element +import kotlin.browser.document + +/** + * List of material design icons. + */ +enum class MaterialIcon(private val ligature: String) : Icon { + ROTATION_3D("3d_rotation"), + AC_UNIT("ac_unit"), + ACCESS_ALARM("access_alarm "), + ACCESS_ALARMS("access_alarms"), + ACCESS_TIME("access_time"), + ACCESSIBILITY("accessibility"), + ACCESSIBLE("accessible"), + ACCOUNT_BALANCE("account_balance"), + ACCOUNT_BALANCE_WALLET("account_balance_wallet"), + ACCOUNT_BOX("account_box"), + ACCOUNT_CIRCLE("account_circle"), + ADB("adb"), + ADD("add"), + ADD_A_PHOTO("add_a_photo"), + ADD_ALARM("add_alarm"), + ADD_ALERT("add_alert"), + ADD_BOX("add_box"), + ADD_CIRCLE("add_circle"), + ADD_CIRCLE_OUTLINE("add_circle_outline"), + ADD_LOCATION("add_location "), + ADD_SHOPPING_CART("add_shopping_cart"), + ADD_TO_PHOTOS("add_to_photos"), + ADD_TO_QUEUE("add_to_queue "), + ADJUST("adjust"), + AIRLINE_SEAT_FLAT("airline_seat_flat"), + AIRLINE_SEAT_FLAT_ANGLED("airline_seat_flat_angled"), + AIRLINE_SEAT_INDIVIDUAL_SUITE("airline_seat_individual_suite"), + AIRLINE_SEAT_LEGROOM_EXTRA("airline_seat_legroom_extra"), + AIRLINE_SEAT_LEGROOM_NORMAL("airline_seat_legroom_normal"), + AIRLINE_SEAT_LEGROOM_REDUCED("airline_seat_legroom_reduced"), + AIRLINE_SEAT_RECLINE_EXTRA("airline_seat_recline_extra"), + AIRLINE_SEAT_RECLINE_NORMAL("airline_seat_recline_normal"), + AIRPLANEMODE_ACTIVE("airplanemode_active"), + AIRPLANEMODE_INACTIVE("airplanemode_inactive"), + AIRPLAY("airplay"), + AIRPORT_SHUTTLE("airport_shuttle"), + ALARM("alarm"), + ALARM_ADD("alarm_add"), + ALARM_OFF("alarm_off"), + ALARM_ON("alarm_on"), + ALBUM("album"), + ALL_INCLUSIVE("all_inclusive"), + ALL_OUT("all_out"), + ANDROID("android"), + ANNOUNCEMENT("announcement "), + APPS("apps"), + ARCHIVE("archive"), + ARROW_BACK("arrow_back"), + ARROW_DOWNWARD("arrow_downward"), + ARROW_DROP_DOWN("arrow_drop_down"), + ARROW_DROP_DOWN_CIRCLE("arrow_drop_down_circle"), + ARROW_DROP_UP("arrow_drop_up"), + ARROW_FORWARD("arrow_forward"), + ARROW_UPWARD("arrow_upward "), + ART_TRACK("art_track"), + ASPECT_RATIO("aspect_ratio "), + ASSESSMENT("assessment"), + ASSIGNMENT("assignment"), + ASSIGNMENT_IND("assignment_ind"), + ASSIGNMENT_LATE("assignment_late"), + ASSIGNMENT_RETURN("assignment_return"), + ASSIGNMENT_RETURNED("assignment_returned"), + ASSIGNMENT_TURNED_IN("assignment_turned_in"), + ASSISTANT("assistant"), + ASSISTANT_PHOTO("assistant_photo"), + ATTACH_FILE("attach_file"), + ATTACH_MONEY("attach_money "), + ATTACHMENT("attachment"), + AUDIOTRACK("audiotrack"), + AUTORENEW("autorenew"), + AV_TIMER("av_timer"), + BACKSPACE("backspace"), + BACKUP("backup"), + BATTERY_ALERT("battery_alert"), + BATTERY_CHARGING_FULL("battery_charging_full"), + BATTERY_FULL("battery_full "), + BATTERY_STD("battery_std"), + BATTERY_UNKNOWN("battery_unknown"), + BEACH_ACCESS("beach_access "), + BEENHERE("beenhere"), + BLOCK("block"), + BLUETOOTH("bluetooth"), + BLUETOOTH_AUDIO("bluetooth_audio"), + BLUETOOTH_CONNECTED("bluetooth_connected"), + BLUETOOTH_DISABLED("bluetooth_disabled"), + BLUETOOTH_SEARCHING("bluetooth_searching"), + BLUR_CIRCULAR("blur_circular"), + BLUR_LINEAR("blur_linear"), + BLUR_OFF("blur_off"), + BLUR_ON("blur_on"), + BOOK("book"), + BOOKMARK("bookmark"), + BOOKMARK_BORDER("bookmark_border"), + BORDER_ALL("border_all"), + BORDER_BOTTOM("border_bottom"), + BORDER_CLEAR("border_clear "), + BORDER_COLOR("border_color "), + BORDER_HORIZONTAL("border_horizontal"), + BORDER_INNER("border_inner "), + BORDER_LEFT("border_left"), + BORDER_OUTER("border_outer "), + BORDER_RIGHT("border_right "), + BORDER_STYLE("border_style "), + BORDER_TOP("border_top"), + BORDER_VERTICAL("border_vertical"), + BRANDING_WATERMARK("branding_watermark"), + BRIGHTNESS_1("brightness_1 "), + BRIGHTNESS_2("brightness_2 "), + BRIGHTNESS_3("brightness_3 "), + BRIGHTNESS_4("brightness_4 "), + BRIGHTNESS_5("brightness_5 "), + BRIGHTNESS_6("brightness_6 "), + BRIGHTNESS_7("brightness_7 "), + BRIGHTNESS_AUTO("brightness_auto"), + BRIGHTNESS_HIGH("brightness_high"), + BRIGHTNESS_LOW("brightness_low"), + BRIGHTNESS_MEDIUM("brightness_medium"), + BROKEN_IMAGE("broken_image "), + BRUSH("brush"), + BUBBLE_CHART("bubble_chart "), + BUG_REPORT("bug_report"), + BUILD("build"), + BURST_MODE("burst_mode"), + BUSINESS("business"), + BUSINESS_CENTER("business_center"), + CACHED("cached"), + CAKE("cake"), + CALL("call"), + CALL_END("call_end"), + CALL_MADE("call_made"), + CALL_MERGE("call_merge"), + CALL_MISSED("call_missed"), + CALL_MISSED_OUTGOING("call_missed_outgoing"), + CALL_RECEIVED("call_received"), + CALL_SPLIT("call_split"), + CALL_TO_ACTION("call_to_action"), + CAMERA("camera"), + CAMERA_ALT("camera_alt"), + CAMERA_ENHANCE("camera_enhance"), + CAMERA_FRONT("camera_front "), + CAMERA_REAR("camera_rear"), + CAMERA_ROLL("camera_roll"), + CANCEL("cancel"), + CARD_GIFTCARD("card_giftcard"), + CARD_MEMBERSHIP("card_membership"), + CARD_TRAVEL("card_travel"), + CASINO("casino"), + CAST("cast"), + CAST_CONNECTED("cast_connected"), + CENTER_FOCUS_STRONG("center_focus_strong"), + CENTER_FOCUS_WEAK("center_focus_weak"), + CHANGE_HISTORY("change_history"), + CHAT("chat"), + CHAT_BUBBLE("chat_bubble"), + CHAT_BUBBLE_OUTLINE("chat_bubble_outline"), + CHECK("checkUpdates"), + CHECK_BOX("check_box"), + CHECK_BOX_OUTLINE_BLANK("check_box_outline_blank"), + CHECK_CIRCLE("check_circle "), + CHEVRON_LEFT("chevron_left "), + CHEVRON_RIGHT("chevron_right"), + CHILD_CARE("child_care"), + CHILD_FRIENDLY("child_friendly"), + CHROME_RR_MODE("chrome_rr_mode"), + CLASS("class"), + CLEAR("clear"), + CLEAR_ALL("clear_all"), + CLOSE("close"), + CLOSED_CAPTION("closed_caption"), + CLOUD("cloud"), + CLOUD_CIRCLE("cloud_circle "), + CLOUD_DONE("cloud_done"), + CLOUD_DOWNLOAD("cloud_download"), + CLOUD_OFF("cloud_off"), + CLOUD_QUEUE("cloud_queue"), + CLOUD_UPLOAD("cloud_upload "), + CODE("code"), + COLLECTIONS("collections"), + COLLECTIONS_BOOKMARK("collections_bookmark"), + COLOR_LENS("color_lens"), + COLORIZE("colorize"), + COMMENT("comment"), + COMPARE("compare"), + COMPARE_ARROWS("compare_arrows"), + COMPUTER("computer"), + CONFIRMATION_NUMBER("confirmation_number"), + CONTACT_MAIL("contact_mail "), + CONTACT_PHONE("contact_phone"), + CONTACTS("contacts"), + CONTENT_COPY("content_copy "), + CONTENT_CUT("content_cut"), + CONTENT_PASTE("content_paste"), + CONTROL_POINT("control_point"), + CONTROL_POINT_DUPLICATE("control_point_duplicate"), + COPYRIGHT("copyright"), + CREATE("onCreate"), + CREATE_NEW_FOLDER("create_new_folder"), + CREDIT_CARD("credit_card"), + CROP("crop"), + CROP_16_9("crop_16_9"), + CROP_3_2("crop_3_2"), + CROP_5_4("crop_5_4"), + CROP_7_5("crop_7_5"), + CROP_DIN("crop_din"), + CROP_FREE("crop_free"), + CROP_LANDSCAPE("crop_landscape"), + CROP_ORIGINAL("crop_original"), + CROP_PORTRAIT("crop_portrait"), + CROP_ROTATE("crop_rotate"), + CROP_SQUARE("crop_square"), + DASHBOARD("dashboard"), + DATA_USAGE("data_usage"), + DATE_RANGE("date_range"), + DEHAZE("dehaze"), + DELETE("delete"), + DELETE_FOREVER("delete_forever"), + DELETE_SWEEP("delete_sweep "), + DESCRIPTION("description"), + DESKTOP_MAC("desktop_mac"), + DESKTOP_WINDOWS("desktop_windows"), + DETAILS("details"), + DEVELOPER_BOARD("developer_board"), + DEVELOPER_MODE("developer_mode"), + DEVICE_HUB("device_hub"), + DEVICES("devices"), + DEVICES_OTHER("devices_other"), + DIALER_SIP("dialer_sip"), + DIALPAD("dialpad"), + DIRECTIONS("directions"), + DIRECTIONS_BIKE("directions_bike"), + DIRECTIONS_BOAT("directions_boat"), + DIRECTIONS_BUS("directions_bus"), + DIRECTIONS_CAR("directions_car"), + DIRECTIONS_RAILWAY("directions_railway"), + DIRECTIONS_RUN("directions_run"), + DIRECTIONS_SUBWAY("directions_subway"), + DIRECTIONS_TRANSIT("directions_transit"), + DIRECTIONS_WALK("directions_walk"), + DISC_FULL("disc_full"), + DNS("dns"), + DO_NOT_DISTURB("do_not_disturb"), + DO_NOT_DISTURB_ALT("do_not_disturb_alt"), + DO_NOT_DISTURB_OFF("do_not_disturb_off"), + DO_NOT_DISTURB_ON("do_not_disturb_on"), + DOCK("dock"), + DOMAIN("domain"), + DONE("done"), + DONE_ALL("done_all"), + DONUT_LARGE("donut_large"), + DONUT_SMALL("donut_small"), + DRAFTS("drafts"), + DRAG_HANDLE("drag_handle"), + DRIVE_ETA("drive_eta"), + DVR("dvr"), + EDIT("edit"), + EDIT_LOCATION("edit_location"), + EJECT("eject"), + EMAIL("email"), + ENHANCED_ENCRYPTION("enhanced_encryption"), + EQUALIZER("equalizer"), + ERROR("error"), + ERROR_OUTLINE("error_outline"), + EURO_SYMBOL("euro_symbol"), + EV_STATION("ev_station"), + EVENT("model"), + EVENT_AVAILABLE("event_available"), + EVENT_BUSY("event_busy"), + EVENT_NOTE("event_note"), + EVENT_SEAT("event_seat"), + EXIT_TO_APP("exit_to_app"), + EXPAND_LESS("expand_less"), + EXPAND_MORE("expand_more"), + EXPLICIT("explicit"), + EXPLORE("explore"), + EXPOSURE("exposure"), + EXPOSURE_NEG_1("exposure_neg_1"), + EXPOSURE_NEG_2("exposure_neg_2"), + EXPOSURE_PLUS_1("exposure_plus_1"), + EXPOSURE_PLUS_2("exposure_plus_2"), + EXPOSURE_ZERO("exposure_zero"), + EXTENSION("extension"), + FACE("face"), + FAST_FORWARD("fast_forward "), + FAST_REWIND("fast_rewind"), + FAVORITE("favorite"), + FAVORITE_BORDER("favorite_border"), + FEATURED_PLAY_LIST("featured_play_list"), + FEATURED_VIDEO("featured_video"), + FACK("fack"), + FIBER_DVR("fiber_dvr"), + FIBER_MANUAL_RECORD("fiber_manual_record"), + FIBER_NEW("fiber_new"), + FIBER_PIN("fiber_pin"), + FIBER_SMART_RECORD("fiber_smart_record"), + FILE_DOWNLOAD("file_download"), + FILE_UPLOAD("file_upload"), + FILTER("filter"), + FILTER_1("filter_1"), + FILTER_2("filter_2"), + FILTER_3("filter_3"), + FILTER_4("filter_4"), + FILTER_5("filter_5"), + FILTER_6("filter_6"), + FILTER_7("filter_7"), + FILTER_8("filter_8"), + FILTER_9("filter_9"), + FILTER_9_PLUS("filter_9_plus"), + FILTER_B_AND_W("filter_b_and_w"), + FILTER_CENTER_FOCUS("filter_center_focus"), + FILTER_DRAMA("filter_drama "), + FILTER_FRAMES("filter_frames"), + FILTER_HDR("filter_hdr"), + FILTER_LIST("filter_list"), + FILTER_NONE("filter_none"), + FILTER_TILT_SHIFT("filter_tilt_shift"), + FILTER_VINTAGE("filter_vintage"), + FIND_IN_PAGE("find_in_page "), + FIND_REPLACE("find_replace "), + FINGERPRINT("fingerprint"), + FIRST_PAGE("first_page"), + FITNESS_CENTER("fitness_center"), + FLAG("flag"), + FLARE("flare"), + FLASH_AUTO("flash_auto"), + FLASH_OFF("flash_off"), + FLASH_ON("flash_on"), + FLIGHT("flight"), + FLIGHT_LAND("flight_land"), + FLIGHT_TAKEOFF("flight_takeoff"), + FLIP("flip"), + FLIP_TO_BACK("flip_to_back "), + FLIP_TO_FRONT("flip_to_front"), + FOLDER("folder"), + FOLDER_OPEN("folder_open"), + FOLDER_SHARED("folder_shared"), + FOLDER_SPECIAL("folder_special"), + FONT_DOWNLOAD("font_download"), + FORMAT_ALIGN_CENTER("format_align_center"), + FORMAT_ALIGN_JUSTIFY("format_align_justify"), + FORMAT_ALIGN_LEFT("format_align_left"), + FORMAT_ALIGN_RIGHT("format_align_right"), + FORMAT_BOLD("format_bold"), + FORMAT_CLEAR("format_clear "), + FORMAT_COLOR_FILL("format_color_fill"), + FORMAT_COLOR_RESET("format_color_reset"), + FORMAT_COLOR_TEXT("format_color_text"), + FORMAT_INDENT_DECREASE("format_indent_decrease"), + FORMAT_INDENT_INCREASE("format_indent_increase"), + FORMAT_ITALIC("format_italic"), + FORMAT_LINE_SPACING("format_line_spacing"), + FORMAT_LIST_BULLETED("format_list_bulleted"), + FORMAT_LIST_NUMBERED("format_list_numbered"), + FORMAT_PAINT("format_paint "), + FORMAT_QUOTE("format_quote "), + FORMAT_SHAPES("format_shapes"), + FORMAT_SIZE("format_size"), + FORMAT_STRIKETHROUGH("format_strikethrough"), + FORMAT_TEXTDIRECTION_L_TO_R("format_textdirection_l_to_r"), + FORMAT_TEXTDIRECTION_R_TO_L("format_textdirection_r_to_l"), + FORMAT_UNDERLINED("format_underlined"), + FORUM("forum"), + FORWARD("forward"), + FORWARD_10("forward_10"), + FORWARD_30("forward_30"), + FORWARD_5("forward_5"), + FREE_BREAKFAST("free_breakfast"), + FULLSCREEN("fullscreen"), + FULLSCREEN_EXIT("fullscreen_exit"), + FUNCTIONS("functions"), + G_TRANSLATE("g_translate"), + GAMEPAD("gamepad"), + GAMES("games"), + GAVEL("gavel"), + GESTURE("gesture"), + GET_APP("get_app"), + GIF("gif"), + GOLF_COURSE("golf_course"), + GPS_FIXED("gps_fixed"), + GPS_NOT_FIXED("gps_not_fixed"), + GPS_OFF("gps_off"), + GRADE("grade"), + GRADIENT("gradient"), + GRAIN("grain"), + GRAPHIC_EQ("graphic_eq"), + GRID_OFF("grid_off"), + GRID_ON("grid_on"), + GROUP("group"), + GROUP_ADD("group_add"), + GROUP_WORK("group_work"), + HD("hd"), + HDR_OFF("hdr_off"), + HDR_ON("hdr_on"), + HDR_STRONG("hdr_strong"), + HDR_WEAK("hdr_weak"), + HEADSET("headset"), + HEADSET_MIC("headset_mic"), + HEALING("healing"), + HEARING("hearing"), + HELP("help"), + HELP_OUTLINE("help_outline "), + HIGH_QUALITY("high_quality "), + HIGHLIGHT("highlight"), + HIGHLIGHT_OFF("highlight_off"), + HISTORY("history"), + HOME("home"), + HOT_TUB("hot_tub"), + HOTEL("hotel"), + HOURGLASS_EMPTY("hourglass_empty"), + HOURGLASS_FULL("hourglass_full"), + HTTP("http"), + HTTPS("https"), + IMAGE("image"), + IMAGE_ASPECT_RATIO("image_aspect_ratio"), + IMPORT_CONTACTS("import_contacts"), + IMPORT_EXPORT("import_export"), + IMPORTANT_DEVICES("important_devices"), + INBOX("inbox"), + INDETERMINATE_CHECK_BOX("indeterminate_check_box"), + INFO("info"), + INFO_OUTLINE("info_outline "), + INPUT("input"), + INSERT_CHART("insert_chart "), + INSERT_COMMENT("insert_comment"), + INSERT_DRIVE_FILE("insert_drive_file"), + INSERT_EMOTICON("insert_emoticon"), + INSERT_INVITATION("insert_invitation"), + INSERT_LINK("insert_link"), + INSERT_PHOTO("insert_photo "), + INVERT_COLORS("invert_colors"), + INVERT_COLORS_OFF("invert_colors_off"), + ISO("iso"), + KEYBOARD("keyboard"), + KEYBOARD_ARROW_DOWN("keyboard_arrow_down"), + KEYBOARD_ARROW_LEFT("keyboard_arrow_left"), + KEYBOARD_ARROW_RIGHT("keyboard_arrow_right"), + KEYBOARD_ARROW_UP("keyboard_arrow_up"), + KEYBOARD_BACKSPACE("keyboard_backspace"), + KEYBOARD_CAPSLOCK("keyboard_capslock"), + KEYBOARD_HIDE("keyboard_hide"), + KEYBOARD_RETURN("keyboard_return"), + KEYBOARD_TAB("keyboard_tab "), + KEYBOARD_VOICE("keyboard_voice"), + KITCHEN("kitchen"), + LABEL("label"), + LABEL_OUTLINE("label_outline"), + LANDSCAPE("landscape"), + LANGUAGE("language"), + LAPTOP("laptop"), + LAPTOP_CHROMEBOOK("laptop_chromebook"), + LAPTOP_MAC("laptop_mac"), + LAPTOP_WINDOWS("laptop_windows"), + LAST_PAGE("last_page"), + LAUNCH("launch"), + LAYERS("layers"), + LAYERS_CLEAR("layers_clear "), + LEAK_ADD("leak_add"), + LEAK_REMOVE("leak_remove"), + LENS("lens"), + LIBRARY_ADD("library_add"), + LIBRARY_BOOKS("library_books"), + LIBRARY_MUSIC("library_music"), + LIGHTBULB_OUTLINE("lightbulb_outline"), + LINE_STYLE("line_style"), + LINE_WEIGHT("line_weight"), + LINEAR_SCALE("linear_scale "), + LINK("link"), + LINKED_CAMERA("linked_camera"), + LIST("list"), + LIVE_HELP("live_help"), + LIVE_TV("live_tv"), + LOCAL_ACTIVITY("local_activity"), + LOCAL_AIRPORT("local_airport"), + LOCAL_ATM("local_atm"), + LOCAL_BAR("local_bar"), + LOCAL_CAFE("local_cafe"), + LOCAL_CAR_WASH("local_car_wash"), + LOCAL_CONVENIENCE_STORE("local_convenience_store"), + LOCAL_DINING("local_dining "), + LOCAL_DRINK("local_drink"), + LOCAL_FLORIST("local_florist"), + LOCAL_GAS_STATION("local_gas_station"), + LOCAL_GROCERY_STORE("local_grocery_store"), + LOCAL_HOSPITAL("local_hospital"), + LOCAL_HOTEL("local_hotel"), + LOCAL_LAUNDRY_SERVICE("local_laundry_service"), + LOCAL_LIBRARY("local_library"), + LOCAL_MALL("local_mall"), + LOCAL_MOVIES("local_movies "), + LOCAL_OFFER("local_offer"), + LOCAL_PARKING("local_parking"), + LOCAL_PHARMACY("local_pharmacy"), + LOCAL_PHONE("local_phone"), + LOCAL_PIZZA("local_pizza"), + LOCAL_PLAY("local_play"), + LOCAL_POST_OFFICE("local_post_office"), + LOCAL_PRINTSHOP("local_printshop"), + LOCAL_SEE("local_see"), + LOCAL_SHIPPING("local_shipping"), + LOCAL_TAXI("local_taxi"), + LOCATION_CITY("location_city"), + LOCATION_DISABLED("location_disabled"), + LOCATION_OFF("location_off "), + LOCATION_ON("location_on"), + LOCATION_SEARCHING("location_searching"), + LOCK("lock"), + LOCK_OPEN("lock_open"), + LOCK_OUTLINE("lock_outline "), + LOOKS("looks"), + LOOKS_3("looks_3"), + LOOKS_4("looks_4"), + LOOKS_5("looks_5"), + LOOKS_6("looks_6"), + LOOKS_ONE("looks_one"), + LOOKS_TWO("looks_two"), + LOOP("loop"), + LOUPE("loupe"), + LOW_PRIORITY("low_priority "), + LOYALTY("loyalty"), + MAIL("mail"), + MAIL_OUTLINE("mail_outline "), + MAP("map"), + MARKUNREAD("markunread"), + MARKUNREAD_MAILBOX("markunread_mailbox"), + MEMORY("memory"), + MENU("menu"), + MERGE_TYPE("merge_type"), + MESSAGE("message"), + MIC("mic"), + MIC_NONE("mic_none"), + MIC_OFF("mic_off"), + MMS("mms"), + MODE_COMMENT("mode_comment "), + MODE_EDIT("mode_edit"), + MONETIZATION_ON("monetization_on"), + MONEY_OFF("money_off"), + MONOCHROME_PHOTOS("monochrome_photos"), + MOOD("mood"), + MOOD_BAD("mood_bad"), + MORE("more"), + MORE_HORIZ("more_horiz"), + MORE_VERT("more_vert"), + MOTORCYCLE("motorcycle"), + MOUSE("mouse"), + MOVE_TO_INBOX("move_to_inbox"), + MOVIE("movie"), + MOVIE_CREATION("movie_creation"), + MOVIE_FILTER("movie_filter "), + MULTILINE_CHART("multiline_chart"), + MUSIC_NOTE("music_note"), + MUSIC_VIDEO("music_video"), + MY_LOCATION("my_location"), + NATURE("nature"), + NATURE_PEOPLE("nature_people"), + NAVIGATE_BEFORE("navigate_before"), + NAVIGATE_NEXT("navigate_next"), + NAVIGATION("navigationDrawer"), + NEAR_ME("near_me"), + NETWORK_CELL("network_cell "), + NETWORK_CHECK("network_check"), + NETWORK_LOCKED("network_locked"), + NETWORK_WIFI("network_wifi "), + NEW_RELEASES("new_releases "), + NEXT_WEEK("next_week"), + NFC("nfc"), + NO_ENCRYPTION("no_encryption"), + NO_SIM("no_sim"), + NOT_INTERESTED("not_interested"), + NOTE("note"), + NOTE_ADD("note_add"), + NOTIFICATIONS("notifications"), + NOTIFICATIONS_ACTIVE("notifications_active"), + NOTIFICATIONS_NONE("notifications_none"), + NOTIFICATIONS_OFF("notifications_off"), + NOTIFICATIONS_PAUSED("notifications_paused"), + OFFLINE_PIN("offline_pin"), + ONDEMAND_VIDEO("ondemand_video"), + OPACITY("opacity"), + OPEN_IN_BROWSER("open_in_browser"), + OPEN_IN_NEW("open_in_new"), + OPEN_WITH("open_with"), + PAGES("pages"), + PAGEVIEW("pageview"), + PALETTE("palette"), + PAN_TOOL("pan_tool"), + PANORAMA("panorama"), + PANORAMA_FISH_EYE("panorama_fish_eye"), + PANORAMA_HORIZONTAL("panorama_horizontal"), + PANORAMA_VERTICAL("panorama_vertical"), + PANORAMA_WIDE_ANGLE("panorama_wide_angle"), + PARTY_MODE("party_mode"), + PAUSE("pause"), + PAUSE_CIRCLE_FILLED("pause_circle_filled"), + PAUSE_CIRCLE_OUTLINE("pause_circle_outline"), + PAYMENT("payment"), + PEOPLE("people"), + PEOPLE_OUTLINE("people_outline"), + PERM_CAMERA_MIC("perm_camera_mic"), + PERM_CONTACT_CALENDAR("perm_contact_calendar"), + PERM_DATA_SETTING("perm_data_setting"), + PERM_DEVICE_INFORMATION("perm_device_information"), + PERM_IDENTITY("perm_identity"), + PERM_MEDIA("perm_media"), + PERM_PHONE_MSG("perm_phone_msg"), + PERM_SCAN_WIFI("perm_scan_wifi"), + PERSON("person"), + PERSON_ADD("person_add"), + PERSON_OUTLINE("person_outline"), + PERSON_PIN("person_pin"), + PERSON_PIN_CIRCLE("person_pin_circle"), + PERSONAL_VIDEO("personal_video"), + PETS("pets"), + PHONE("phone"), + PHONE_ANDROID("phone_android"), + PHONE_BLUETOOTH_SPEAKER("phone_bluetooth_speaker"), + PHONE_FORWARDED("phone_forwarded"), + PHONE_IN_TALK("phone_in_talk"), + PHONE_IPHONE("phone_iphone "), + PHONE_LOCKED("phone_locked "), + PHONE_MISSED("phone_missed "), + PHONE_PAUSED("phone_paused "), + PHONELINK("phonelink"), + PHONELINK_ERASE("phonelink_erase"), + PHONELINK_LOCK("phonelink_lock"), + PHONELINK_OFF("phonelink_off"), + PHONELINK_RING("phonelink_ring"), + PHONELINK_SETUP("phonelink_setup"), + PHOTO("photo"), + PHOTO_ALBUM("photo_album"), + PHOTO_CAMERA("photo_camera "), + PHOTO_FILTER("photo_filter "), + PHOTO_LIBRARY("photo_library"), + PHOTO_SIZE_SELECT_ACTUAL("photo_size_select_actual"), + PHOTO_SIZE_SELECT_LARGE("photo_size_select_large"), + PHOTO_SIZE_SELECT_SMALL("photo_size_select_small"), + PICTURE_AS_PDF("picture_as_pdf"), + PICTURE_IN_PICTURE("picture_in_picture"), + PICTURE_IN_PICTURE_ALT("picture_in_picture_alt"), + PIE_CHART("pie_chart"), + PIE_CHART_OUTLINED("pie_chart_outlined"), + PIN_DROP("pin_drop"), + PLACE("place"), + PLAY_ARROW("play_arrow"), + PLAY_CIRCLE_FILLED("play_circle_filled"), + PLAY_CIRCLE_OUTLINE("play_circle_outline"), + PLAY_FOR_WORK("play_for_work"), + PLAYLIST_ADD("playlist_add "), + PLAYLIST_ADD_CHECK("playlist_add_check"), + PLAYLIST_PLAY("playlist_play"), + PLUS_ONE("plus_one"), + POLL("poll"), + POLYMER("polymer"), + POOL("pool"), + PORTABLE_WIFI_OFF("portable_wifi_off"), + PORTRAIT("portrait"), + POWER("power"), + POWER_INPUT("power_input"), + POWER_SETTINGS_NEW("power_settings_new"), + PREGNANT_WOMAN("pregnant_woman"), + PRESENT_TO_ALL("present_to_all"), + PRINT("print"), + PRIORITY_HIGH("priority_high"), + PUBLIC("public"), + PUBLISH("publish"), + QUERY_BUILDER("query_builder"), + QUESTION_ANSWER("question_answer"), + QUEUE("queue"), + QUEUE_MUSIC("queue_music"), + QUEUE_PLAY_NEXT("queue_play_next"), + RADIO("radio"), + RADIO_BUTTON_CHECKED("radio_button_checked"), + RADIO_BUTTON_UNCHECKED("radio_button_unchecked"), + RATE_REVIEW("rate_review"), + RECEIPT("receipt"), + RECENT_ACTORS("recent_actors"), + RECORD_VOICE_OVER("record_voice_over"), + RM("rm"), + REDO("redo"), + REFRESH("refresh"), + REMOVE("remove"), + REMOVE_CIRCLE("remove_circle"), + REMOVE_CIRCLE_OUTLINE("remove_circle_outline"), + REMOVE_FROM_QUEUE("remove_from_queue"), + REMOVE_RED_EYE("remove_red_eye"), + REMOVE_SHOPPING_CART("remove_shopping_cart"), + REORDER("reorder"), + REPEAT("repeat"), + REPEAT_ONE("repeat_one"), + REPLAY("replay"), + REPLAY_10("replay_10"), + REPLAY_30("replay_30"), + REPLAY_5("replay_5"), + REPLY("reply"), + REPLY_ALL("reply_all"), + REPORT("report"), + REPORT_PROBLEM("report_problem"), + RESTAURANT("restaurant"), + RESTAURANT_MENU("restaurant_menu"), + RESTORE("restore"), + RESTORE_PAGE("restore_page "), + RING_VOLUME("ring_volume"), + ROOM("room"), + ROOM_SERVICE("room_service "), + ROTATE_90_DEGREES_CCW("rotate_90_degrees_ccw"), + ROTATE_LEFT("rotate_left"), + ROTATE_RIGHT("rotate_right "), + ROUNDED_CORNER("rounded_corner"), + ROUTER("router"), + ROWING("rowing"), + RSS_FEED("rss_feed"), + RV_HOOKUP("rv_hookup"), + SATELLITE("satellite"), + SAVE("save"), + SCANNER("scanner"), + SCHEDULE("schedule"), + SCHOOL("school"), + SCREEN_LOCK_LANDSCAPE("screen_lock_landscape"), + SCREEN_LOCK_PORTRAIT("screen_lock_portrait"), + SCREEN_LOCK_ROTATION("screen_lock_rotation"), + SCREEN_ROTATION("screen_rotation"), + SCREEN_SHARE("screen_share "), + SD_CARD("sd_card"), + SD_STORAGE("sd_storage"), + SEARCH("search"), + SECURITY("security"), + SELECT_ALL("select_all"), + SEND("send"), + SENTIMENT_DISSATISFIED("sentiment_dissatisfied"), + SENTIMENT_NEUTRAL("sentiment_neutral"), + SENTIMENT_SATISFIED("sentiment_satisfied"), + SENTIMENT_VERY_DISSATISFIED("sentiment_very_dissatisfied"), + SENTIMENT_VERY_SATISFIED("sentiment_very_satisfied"), + SETTINGS("settings"), + SETTINGS_APPLICATIONS("settings_applications"), + SETTINGS_BACKUP_RESTORE("settings_backup_restore"), + SETTINGS_BLUETOOTH("settings_bluetooth"), + SETTINGS_BRIGHTNESS("settings_brightness"), + SETTINGS_CELL("settings_cell"), + SETTINGS_ETHERNET("settings_ethernet"), + SETTINGS_INPUT_ANTENNA("settings_input_antenna"), + SETTINGS_INPUT_COMPONENT("settings_input_component"), + SETTINGS_INPUT_COMPOSITE("settings_input_composite"), + SETTINGS_INPUT_HDMI("settings_input_hdmi"), + SETTINGS_INPUT_SVIDEO("settings_input_svideo"), + SETTINGS_OVERSCAN("settings_overscan"), + SETTINGS_PHONE("settings_phone"), + SETTINGS_POWER("settings_power"), + SETTINGS_REMOTE("settings_remote"), + SETTINGS_SYSTEM_DAYDREAM("settings_system_daydream"), + SETTINGS_VOICE("settings_voice"), + SHARE("share"), + SHOP("shop"), + SHOP_TWO("shop_two"), + SHOPPING_BASKET("shopping_basket"), + SHOPPING_CART("shopping_cart"), + SHORT_TEXT("short_text"), + SHOW_CHART("show_chart"), + SHUFFLE("shuffle"), + SIGNAL_CELLULAR_4_BAR("signal_cellular_4_bar"), + SIGNAL_CELLULAR_CONNECTED_NO_INTERNET_4_BAR("signal_cellular_connected_no_internet_4_bar"), + SIGNAL_CELLULAR_NO_SIM("signal_cellular_no_sim"), + SIGNAL_CELLULAR_NULL("signal_cellular_null"), + SIGNAL_CELLULAR_OFF("signal_cellular_off"), + SIGNAL_WIFI_4_BAR("signal_wifi_4_bar"), + SIGNAL_WIFI_4_BAR_LOCK("signal_wifi_4_bar_lock"), + SIGNAL_WIFI_OFF("signal_wifi_off"), + SIM_CARD("sim_card"), + SIM_CARD_ALERT("sim_card_alert"), + SKIP_NEXT("skip_next"), + SKIP_PREVIOUS("skip_previous"), + SLIDESHOW("slideshow"), + SLOW_MOTION_VIDEO("slow_motion_video"), + SMARTPHONE("smartphone"), + SMOKE_FREE("smoke_free"), + SMOKING_ROOMS("smoking_rooms"), + SMS("sms"), + SMS_FAILED("sms_failed"), + SNOOZE("snooze"), + SORT("sort"), + SORT_BY_ALPHA("sort_by_alpha"), + SPA("spa"), + SPACE_BAR("space_bar"), + SPEAKER("speaker"), + SPEAKER_GROUP("speaker_group"), + SPEAKER_NOTES("speaker_notes"), + SPEAKER_NOTES_OFF("speaker_notes_off"), + SPEAKER_PHONE("speaker_phone"), + SPELLCHECK("spellcheck"), + STAR("star"), + STAR_BORDER("star_border"), + STAR_HALF("star_half"), + STARS("stars"), + STAY_CURRENT_LANDSCAPE("stay_current_landscape"), + STAY_CURRENT_PORTRAIT("stay_current_portrait"), + STAY_PRIMARY_LANDSCAPE("stay_primary_landscape"), + STAY_PRIMARY_PORTRAIT("stay_primary_portrait"), + STOP("stop"), + STOP_SCREEN_SHARE("stop_screen_share"), + STORAGE("storage"), + STORE("store"), + STORE_MALL_DIRECTORY("store_mall_directory"), + STRAIGHTEN("straighten"), + STREETVIEW("streetview"), + STRIKETHROUGH_S("strikethrough_s"), + STYLE("style"), + SUBDIRECTORY_ARROW_LEFT("subdirectory_arrow_left"), + SUBDIRECTORY_ARROW_RIGHT("subdirectory_arrow_right"), + SUBJECT("subject"), + SUBSCRIPTIONS("subscriptions"), + SUBTITLES("subtitles"), + SUBWAY("subway"), + SUPERVISOR_ACCOUNT("supervisor_account"), + SURROUND_SOUND("surround_sound"), + SWAP_CALLS("swap_calls"), + SWAP_HORIZ("swap_horiz"), + SWAP_VERT("swap_vert"), + SWAP_VERTICAL_CIRCLE("swap_vertical_circle"), + SWITCH_CAMERA("switch_camera"), + SWITCH_VIDEO("switch_video "), + SYNC("sync"), + SYNC_DISABLED("sync_disabled"), + SYNC_PROBLEM("sync_problem "), + SYSTEM_UPDATE("system_update"), + SYSTEM_UPDATE_ALT("system_update_alt"), + TAB("tab"), + TAB_UNSELECTED("tab_unselected"), + TABLET("tablet"), + TABLET_ANDROID("tablet_android"), + TABLET_MAC("tablet_mac"), + TAG_FACES("tag_faces"), + TAP_AND_PLAY("tap_and_play "), + TERRAIN("terrain"), + TEXT_FIELDS("text_fields"), + TEXT_FORMAT("text_format"), + TEXTSMS("textsms"), + TEXTURE("texture"), + THEATERS("theaters"), + THUMB_DOWN("thumb_down"), + THUMB_UP("thumb_up"), + THUMBS_UP_DOWN("thumbs_up_down"), + TIME_TO_LEAVE("time_to_leave"), + TIMELAPSE("timelapse"), + TIMELINE("timeline"), + TIMER("timer"), + TIMER_10("timer_10"), + TIMER_3("timer_3"), + TIMER_OFF("timer_off"), + TITLE("title"), + TOC("toc"), + TODAY("today"), + TOLL("toll"), + TONALITY("tonality"), + TOUCH_APP("touch_app"), + TOYS("toys"), + TRACK_CHANGES("track_changes"), + TRAFFIC("traffic"), + TRAIN("train"), + TRAM("tram"), + TRANSFER_WITHIN_A_STATION("transfer_within_a_station"), + TRANSFORM("transform"), + TRANSLATE("translate"), + TRENDING_DOWN("trending_down"), + TRENDING_FLAT("trending_flat"), + TRENDING_UP("trending_up"), + TUNE("tune"), + TURNED_IN("turned_in"), + TURNED_IN_NOT("turned_in_not"), + TV("tv"), + UNARCHIVE("unarchive"), + UNDO("undo"), + UNFOLD_LESS("unfold_less"), + UNFOLD_MORE("unfold_more"), + UPDATE("update"), + USB("usb"), + VERIFIED_USER("verified_user"), + VERTICAL_ALIGN_BOTTOM("vertical_align_bottom"), + VERTICAL_ALIGN_CENTER("vertical_align_center"), + VERTICAL_ALIGN_TOP("vertical_align_top"), + VIBRATION("vibration"), + VIDEO_CALL("video_call"), + VIDEO_LABEL("video_label"), + VIDEO_LIBRARY("video_library"), + VIDEOCAM("videocam"), + VIDEOCAM_OFF("videocam_off "), + VIDEOGAME_ASSET("videogame_asset"), + VIEW_AGENDA("view_agenda"), + VIEW_ARRAY("view_array"), + VIEW_CAROUSEL("view_carousel"), + VIEW_COLUMN("view_column"), + VIEW_COMFY("view_comfy"), + VIEW_COMPACT("view_compact "), + VIEW_DAY("view_day"), + VIEW_HEADLINE("view_headline"), + VIEW_LIST("view_list"), + VIEW_MODULE("view_module"), + VIEW_QUILT("view_quilt"), + VIEW_STREAM("view_stream"), + VIEW_WEEK("view_week"), + VIGNETTE("vignette"), + VISIBILITY("visibility"), + VISIBILITY_OFF("visibility_off"), + VOICE_CHAT("voice_chat"), + VOICEMAIL("voicemail"), + VOLUME_DOWN("volume_down"), + VOLUME_MUTE("volume_mute"), + VOLUME_OFF("volume_off"), + VOLUME_UP("volume_up"), + VPN_KEY("vpn_key"), + VPN_LOCK("vpn_lock"), + WALLPAPER("wallpaper"), + WARNING("warning"), + WATCH("watch"), + WATCH_LATER("watch_later"), + WB_AUTO("wb_auto"), + WB_CLOUDY("wb_cloudy"), + WB_INCANDESCENT("wb_incandescent"), + WB_IRIDESCENT("wb_iridescent"), + WB_SUNNY("wb_sunny"), + WC("wc"), + WEB("web"), + WEB_ASSET("web_asset"), + WEEKEND("weekend"), + WHATSHOT("whatshot"), + WIDGETS("widgets"), + WIFI("wifi"), + WIFI_LOCK("wifi_lock"), + WIFI_TETHERING("wifi_tethering"), + WORK("work"), + WRAP_TEXT("wrap_text"), + YOUTUBE_SEARCHED_FOR("youtube_searched_for"), + ZOOM_IN("zoom_in"), + ZOOM_OUT("zoom_out"), + ZOOM_OUT_MAP("zoom_out_map "); + + override val element: Element + get() = document.createElement("i").apply { + classList.add("material-icons") + textContent = ligature + } +} \ No newline at end of file diff --git a/src/jsMain/resources/style/_color.scss b/src/jsMain/resources/style/_color.scss index be962f4..d00ab4c 100644 --- a/src/jsMain/resources/style/_color.scss +++ b/src/jsMain/resources/style/_color.scss @@ -1,39 +1,59 @@ $background-primary-color: #fff; $background-secondary-color: #fcfcfc; + $text-primary-color: #333; $text-secondary-color: rgba($text-primary-color, 0.5); + $primary-color: #B11D33; $primary-text-color: #fff; + $error-color: #F00; $error-text-color: #fff; +$error-background-color: rgba($error-color, 0.5); + $input-border-color: #888; $table-border-color: rgba($text-primary-color, 0.1); $table-header-color: rgba($text-primary-color, 0.06); + $shadow-color: rgba($text-primary-color, 0.8); + +$icon-color-focused: rgba($text-primary-color, 0.87); +$icon-color: rgba($text-primary-color, 0.54); +$icon-color-inactive: rgba($text-primary-color, 0.38); + $bg-disabled-color: rgba($text-primary-color, .26); $bg-enabled-color: rgba($primary-color, .5); $lever-disabled-color: $background-primary-color; $lever-enabled-color: $primary-color; -$error-background-color: rgba($error-color, 0.5); @mixin color-setting { :root { --background-primary-color: $background-primary-color; --background-secondary-color: $background-secondary-color; + --text-primary-color: $text-primary-color; --text-secondary-color: $text-secondary-color; + --primary-color: $primary-color; --primary-text-color: $primary-text-color; + --error-color: $error-color; --error-text-color: $error-text-color; + --error-background-color: $error-background-color; + --input-border-color: $input-border-color; --table-border-color: $table-border-color; --table-header-color: $table-header-color; + --shadow-color: $shadow-color; + + --icon-color-focused: $icon-color-focused; + --icon-color: $icon-color; + --icon-color-inactive: $icon-color-inactive; + --bg-disabled-color: $bg-disabled-color; --bg-enabled-color: $bg-enabled-color; --lever-disabled-color: $lever-disabled-color; --lever-enabled-color: $lever-enabled-color; - --error-background-color: $error-background-color; } } \ No newline at end of file diff --git a/src/jsMain/resources/style/dark.scss b/src/jsMain/resources/style/dark.scss index 1cde9bd..4d7bf52 100644 --- a/src/jsMain/resources/style/dark.scss +++ b/src/jsMain/resources/style/dark.scss @@ -2,20 +2,30 @@ $background-primary-color: #2d2d2d; $background-secondary-color: #373737; + $text-primary-color: #fff; $text-secondary-color: rgba($text-primary-color, 0.5); + $primary-color: #dd213d; $primary-text-color: #fff; + $error-color: #F00; $error-text-color: #fff; +$error-background-color: rgba($error-color, 0.5); + $input-border-color: #888; $table-border-color: rgba($text-primary-color, 0.1); $table-header-color: rgba($text-primary-color, 0.06); + $shadow-color: rgba($text-primary-color, 0.8); + +$icon-color-focused: rgba($text-primary-color, 1.0); +$icon-color: rgba($text-primary-color, 0.7); +$icon-color-inactive: rgba($text-primary-color, 0.5); + $bg-disabled-color: rgba($text-primary-color, .26); $bg-enabled-color: rgba($primary-color, .5); $lever-disabled-color: $background-primary-color; $lever-enabled-color: $primary-color; -$error-background-color: rgba($error-color, 0.5); @include color-setting; diff --git a/src/jsMain/resources/style/style.scss b/src/jsMain/resources/style/style.scss index 50e7235..302839f 100644 --- a/src/jsMain/resources/style/style.scss +++ b/src/jsMain/resources/style/style.scss @@ -26,6 +26,8 @@ body, html { margin: 0; padding: 0; + overflow-x: hidden; + & > *:last-child { //margin-bottom: 1rem; } @@ -549,18 +551,18 @@ form { } .calendar-edit { - width: 16rem; + width: 0; height: 100%; display: block; position: absolute; right: 0; top: 0; + transition: width $transitionTime; .calendar-edit-main { position: sticky; - margin-left: 16rem; opacity: 0; - transition: margin-left $transitionTime, opacity $transitionTime, visibility $transitionTime; + transition: opacity $transitionTime, visibility $transitionTime; top: 1rem; visibility: hidden; height: 100vh; @@ -610,7 +612,7 @@ form { z-index: 2; .calendar-tools { - display: none; + display: block; } } @@ -635,8 +637,11 @@ form { border-right: solid 1px var(--table-border-color); } + .calendar-edit { + width: 16rem; + } + .calendar-edit-main { - margin-left: 0; opacity: 1; visibility: visible; } @@ -644,19 +649,71 @@ form { .calendar-tools { position: absolute; - top: -3rem; + top: -5rem; right: 0; - background-color: #fff; - padding: 0.2rem 0.5rem; + background-color: var(--background-primary-color); border-radius: $border-radius; display: none; z-index: 10; + width: max-content; border: solid 1px var(--input-border-color); - box-shadow: 0 0.1rem 0.2rem var(--primary-text-color); + box-shadow: 0 0.1rem 0.2rem var(--shadow-color); - a { - padding: 0.2rem; + div { + clear: left; + + span { + float: left; + display: block; + width: 2rem; + line-height: 2rem; + height: 2rem; + text-align: center; + color: var(--icon-color-focused); + transition: color $transitionTime, background-color $transitionTime; + box-sizing: content-box; + + i { + font-size: 1rem; + } + + &:hover { + background: var(--table-header-color); + } + + &.disabled { + color: var(--icon-color-inactive); + } + + &:first-child { + padding-left: 0.5rem; + } + &:last-child { + padding-right: 0.5rem; + } + } + + &:first-child { + span:first-child { + color: var(--text-primary-color); + width: 6rem; + + &:hover { + background-color: transparent; + } + } + + span { + padding-top: 0.2rem; + } + } + + &:last-child { + span { + padding-bottom: 0.2rem; + } + } } &::after { @@ -669,10 +726,6 @@ form { } } -.calendar[data-editable=false] .calendar-tools { - display: none !important; -} - .calendar-entry { position: absolute; display: block; @@ -706,7 +759,7 @@ form { z-index: 2; .calendar-tools { - display: none; + display: block; } } @@ -729,161 +782,154 @@ form { @include no-select() } -.calendar-table-time-to-room { +.calendar-table-box { + display: flex; + flex-wrap: nowrap; + flex-direction: column; + width: max-content; + height: max-content; + .calendar-header, .calendar-row { display: flex; flex-wrap: nowrap; + flex-direction: row; width: max-content; - } - - .calendar-header { - line-height: 2rem; - height: 2rem; - - .calendar-cell:not(:first-child) { - font-size: 0.8rem; - width: 6rem; - position: relative; - padding-left: 0.2rem; - - &::before { - content: ''; - height: 100%; - left: 0; - top: 0; - border-left: solid 1px var(--table-border-color); - position: absolute; - } - } + height: max-content; } .calendar-row { - border-top: solid 1px var(--table-border-color); - line-height: 3rem; - height: 3rem; - .calendar-cell { - position: relative; - width: 1.5rem; - - &:nth-child(2n + 2)::before { - content: ''; - height: 100%; - left: 0; - top: 0; - border-left: solid 1px var(--table-border-color); - position: absolute; - } - &:hover { background-color: var(--table-header-color); } - - .calendar-entry { - top: 0.5rem !important; - bottom: 0.5rem; - width: 0; - left: 0; - height: auto !important; - } } } - .calendar-cell:first-child { - width: 6rem; - left: 0; - text-align: center; - border-right: solid 1px var(--table-border-color); - } + &.room-to-time { + .calendar-header { + line-height: 2rem; + height: 2rem; + width: 100%; - .calendar-link { - display: block; - width: 100%; - height: 100%; - } -} - -.calendar-table-room-to-time { - .calendar-header, .calendar-row { - display: flex; - flex-wrap: nowrap; - width: max-content; - } - - .calendar-header { - line-height: 2rem; - height: 2rem; - - .calendar-cell:not(:first-child) { - width: 12rem; - position: relative; - padding-left: 0.2rem; - - &::before { - content: ''; - height: 100%; - left: 0; - top: 0; - border-left: solid 1px var(--table-border-color); - position: absolute; - } - } - } - - .calendar-row { - line-height: 2rem; - height: 1.3rem; - - .calendar-cell { - position: relative; - width: 12rem; - - &::before { - content: ''; - height: 100%; - width: 100%; - left: 0; - top: 0; - border-left: solid 1px var(--table-border-color); - position: absolute; + .calendar-cell:first-child { + flex-grow: 1; + text-align: center; } - &:hover { - background-color: var(--table-header-color); - } - - .calendar-entry { - top: 0.1rem; - bottom: 0; - width: auto !important; - left: 0.1rem !important; - right: 0.1rem; - } - - a { + .calendar-cell:not(:first-child) { + width: 12rem; position: relative; + padding-left: 0.2rem; + + &::before { + content: ''; + height: 100%; + left: 0; + top: 0; + border-left: solid 1px var(--table-border-color); + position: absolute; + } } } - .calendar-cell:first-child::before { - border-left: none; - } + .calendar-row { + line-height: 2rem; + height: 1.3rem; - &:nth-child(4n + 2) .calendar-cell::before { - border-top: solid 1px var(--table-border-color); + .calendar-cell { + position: relative; + width: 12rem; + + &::before { + content: ''; + height: 100%; + width: 100%; + left: 0; + top: 0; + border-left: solid 1px var(--table-border-color); + position: absolute; + } + + .calendar-entry { + top: 0.1rem; + bottom: 0; + width: auto !important; + left: 0.1rem !important; + right: 0.1rem; + } + } + + .calendar-cell:first-child { + width: 6rem; + left: 0; + text-align: center; + } + + .calendar-cell:first-child::before { + border-left: none; + } + + &:nth-child(4n + 2) .calendar-cell::before { + border-top: solid 1px var(--table-border-color); + } } } - .calendar-cell:first-child { - width: 6rem; - left: 0; - text-align: center; - } + &.time-to-room { + flex-direction: row; - .calendar-link { - display: block; - width: 100%; - height: 100%; + .calendar-header, .calendar-row { + flex-direction: column; + line-height: 3rem; + + .calendar-cell { + height: 3rem; + } + } + + .calendar-header { + .calendar-cell { + position: relative; + width: 6rem; + + &:not(:first-child) { + border-top: solid 1px var(--table-border-color); + } + } + } + + .calendar-row { + .calendar-cell { + position: relative; + width: 1.5rem; + + &:not(:first-child) { + border-top: solid 1px var(--table-border-color); + } + .calendar-entry { + top: 0.5rem !important; + bottom: 0.5rem; + width: 0; + left: 0; + height: auto !important; + } + + &:first-child { + font-size: 0.8rem; + position: relative; + padding-left: 0.2rem; + } + } + + &:nth-child(2n + 2) .calendar-cell::before { + content: ''; + height: 100%; + left: 0; + top: 0; + border-left: solid 1px var(--table-border-color); + position: absolute; + } + } } } diff --git a/src/jvmMain/kotlin/de/kif/backend/database/Schema.kt b/src/jvmMain/kotlin/de/kif/backend/database/Schema.kt index 0122b90..c22f6dc 100644 --- a/src/jvmMain/kotlin/de/kif/backend/database/Schema.kt +++ b/src/jvmMain/kotlin/de/kif/backend/database/Schema.kt @@ -59,6 +59,8 @@ object DbSchedule : Table() { val roomId = long("room_id").index() val day = integer("day").index() val time = integer("time_slot") + val lookRoom = bool("look_room").default(false) + val lookTime = bool("look_time").default(false) val createdAt = long("createdAt") val updatedAt = long("updatedAt") diff --git a/src/jvmMain/kotlin/de/kif/backend/repository/ScheduleRepository.kt b/src/jvmMain/kotlin/de/kif/backend/repository/ScheduleRepository.kt index 9b54c54..9ad772f 100644 --- a/src/jvmMain/kotlin/de/kif/backend/repository/ScheduleRepository.kt +++ b/src/jvmMain/kotlin/de/kif/backend/repository/ScheduleRepository.kt @@ -24,6 +24,8 @@ object ScheduleRepository : Repository { val roomId = row[DbSchedule.roomId] val day = row[DbSchedule.day] val time = row[DbSchedule.time] + val lookRoom = row[DbSchedule.lookRoom] + val lookTime = row[DbSchedule.lookTime] val createdAt = row[DbSchedule.createdAt] val updatedAt = row[DbSchedule.updatedAt] @@ -33,7 +35,7 @@ object ScheduleRepository : Repository { val room = RoomRepository.get(roomId) ?: throw IllegalStateException("Room for schedule does not exist!") - return Schedule(id, workGroup, room, day, time, createdAt, updatedAt) + return Schedule(id, workGroup, room, day, time, lookRoom, lookTime, createdAt, updatedAt) } override suspend fun get(id: Long): Schedule? { @@ -55,6 +57,8 @@ object ScheduleRepository : Repository { it[roomId] = model.room.id ?: throw IllegalArgumentException("Room does not exist!") it[day] = model.day it[time] = model.time + it[lookRoom] = model.lookRoom + it[lookTime] = model.lookTime it[createdAt] = now it[updatedAt] = now @@ -77,6 +81,8 @@ object ScheduleRepository : Repository { it[roomId] = model.room.id ?: throw IllegalArgumentException("Room does not exist!") it[day] = model.day it[time] = model.time + it[lookRoom] = model.lookRoom + it[lookTime] = model.lookTime it[updatedAt] = now } diff --git a/src/jvmMain/kotlin/de/kif/backend/route/Calendar.kt b/src/jvmMain/kotlin/de/kif/backend/route/Calendar.kt index 77e0fb6..bbccc35 100644 --- a/src/jvmMain/kotlin/de/kif/backend/route/Calendar.kt +++ b/src/jvmMain/kotlin/de/kif/backend/route/Calendar.kt @@ -3,23 +3,16 @@ 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 import de.kif.backend.repository.ScheduleRepository -import de.kif.backend.repository.WorkGroupRepository -import de.kif.backend.view.MainTemplate import de.kif.backend.view.MenuTemplate -import de.kif.backend.view.TableTemplate import de.kif.backend.view.respondMain import de.kif.common.CALENDAR_GRID_WIDTH -import de.kif.common.Search import de.kif.common.model.Permission import de.kif.common.model.Room import de.kif.common.model.Schedule import io.ktor.application.call -import io.ktor.html.insert -import io.ktor.html.respondHtmlTemplate import io.ktor.response.respondRedirect import io.ktor.routing.Route import io.ktor.routing.get @@ -59,116 +52,22 @@ private fun DIV.calendarCell(schedule: Schedule?) { attributes["data-id"] = schedule.id.toString() +schedule.workGroup.name - - div("calendar-tools") { - a( - classes = "calendar-tools-m10", - href = "/calendar/${schedule.day}/${schedule.id}/delete?redirect=/calendar/${schedule.day}/${schedule.room.id}/${schedule.time - 10}/${schedule.workGroup.id}" - ) { +"-10" } - a( - classes = "calendar-tools-m5", - href = "/calendar/${schedule.day}/${schedule.id}/delete?redirect=/calendar/${schedule.day}/${schedule.room.id}/${schedule.time - 5}/${schedule.workGroup.id}" - ) { +"-05" } - a( - classes = "calendar-tools-reset", - href = "/calendar/${schedule.day}/${schedule.id}/delete?redirect=/calendar/${schedule.day}/${schedule.room.id}/${schedule.time / CALENDAR_GRID_WIDTH * CALENDAR_GRID_WIDTH}/${schedule.workGroup.id}" - ) { +"reset" } - a( - classes = "calendar-tools-p5", - href = "/calendar/${schedule.day}/${schedule.id}/delete?redirect=/calendar/${schedule.day}/${schedule.room.id}/${schedule.time + 5}/${schedule.workGroup.id}" - ) { +"+05" } - a( - classes = "calendar-tools-p10", - href = "/calendar/${schedule.day}/${schedule.id}/delete?redirect=/calendar/${schedule.day}/${schedule.room.id}/${schedule.time + 10}/${schedule.workGroup.id}" - ) { +"+10" } - a( - classes = "calendar-tools-del", - href = "/calendar/${schedule.day}/${schedule.id}/delete" - ) { +"del" } - } } } } -private fun DIV.renderTimeToRoom( +private fun DIV.renderCalendar( + orientation: CalendarOrientation, day: Int, from: Int, to: Int, rooms: List, - schedules: Map>, - allowEdit: Boolean + schedules: Map> ) { val gridLabelWidth = 60 val minutesOfDay = to - from - div("calendar-table-time-to-room") { - div("calendar-header") { - div("calendar-cell") { - span { - +"Room" - } - } - - for (i in 0 until minutesOfDay / gridLabelWidth) { - div("calendar-cell") { - span { - val time = ((i * gridLabelWidth + from) % MINUTES_OF_DAY).let { - if (it < 0) it + MINUTES_OF_DAY else it - } - val minutes = (time % 60).toString().padStart(2, '0') - val hours = (time / 60).toString().padStart(2, '0') - +"$hours:$minutes" - } - } - } - } - - for (room in rooms) { - div("calendar-row") { - div("calendar-cell") { - span { - +room.name - } - } - - for (i in 0 until minutesOfDay / CALENDAR_GRID_WIDTH) { - val start = i * CALENDAR_GRID_WIDTH + from - val end = (i + 1) * CALENDAR_GRID_WIDTH + from - 1 - - div("calendar-cell") { - val time = i * CALENDAR_GRID_WIDTH - val minutes = (time % 60).toString().padStart(2, '0') - val hours = (time / 60).toString().padStart(2, '0') - title = "$hours:$minutes" - attributes["data-time"] = start.toString() - attributes["data-room"] = room.id.toString() - attributes["data-day"] = day.toString() - - val schedule = (start..end).mapNotNull { schedules[room]?.get(it) }.firstOrNull() - - calendarCell(schedule) - - val href = if (allowEdit) "/calendar/$day/${room.id}/$start" else null - a(href, classes = "calendar-link") - } - } - } - } - } -} - -private fun DIV.renderRoomToTime( - day: Int, - from: Int, - to: Int, - rooms: List, - schedules: Map>, - allowEdit: Boolean -) { - val gridLabelWidth = 60 - val minutesOfDay = to - from - - div("calendar-table-room-to-time") { + div("calendar-table-box ${orientation.name.toLowerCase().replace("_", "-")}") { div("calendar-header") { div("calendar-cell") { span { @@ -210,14 +109,11 @@ private fun DIV.renderRoomToTime( attributes["data-time"] = start.toString() attributes["data-room"] = room.id.toString() attributes["data-day"] = day.toString() - title = timeString + title = room.name + " - " + timeString val schedule = (start..end).mapNotNull { schedules[room]?.get(it) }.firstOrNull() calendarCell(schedule) - - val href = if (allowEdit) "/calendar/$day/${room.id}/$start" else null - a(href, classes = "calendar-link") } } } @@ -351,24 +247,14 @@ fun Route.calendar() { attributes["data-editable"] = editable.toString() div("calendar-table") { - when (orientation) { - CalendarOrientation.ROOM_TO_TIME -> renderRoomToTime( - day, - min, - max, - rooms, - schedules, - editable - ) - CalendarOrientation.TIME_TO_ROOM -> renderTimeToRoom( - day, - min, - max, - rooms, - schedules, - editable - ) - } + renderCalendar( + orientation, + day, + min, + max, + rooms, + schedules + ) } if (editable) { @@ -390,119 +276,6 @@ fun Route.calendar() { } } } - - get("/calendar/{day}/{room}/{time}") { - authenticateOrRedirect(Permission.SCHEDULE) { user -> - val day = call.parameters["day"]?.toIntOrNull() ?: return@get - val time = call.parameters["time"]?.toIntOrNull() ?: return@get - val roomId = call.parameters["room"]?.toLongOrNull() ?: return@get - val search = call.parameters["search"] ?: "" - - val list = WorkGroupRepository.all() - val room = RoomRepository.get(roomId) ?: return@get - - respondMain { - menuTemplate { - this.user = user - active = MenuTemplate.Tab.WORK_GROUP - } - content { - h1 { +"Select work groups" } - insert(TableTemplate()) { - searchValue = search - - action { - a("/calendar/$day") { - button(classes = "form-btn btn-primary") { - +"Cancel" - } - } - } - - header { - th { - +"Name" - } - th { - +"Interested" - } - th { - +"Track" - } - th { - +"Projector" - } - th { - +"Resolution" - } - th { - +"Length" - } - } - - for (u in list) { - val s = u.createSearch() - if (Search.match(search, s)) { - val href = "/calendar/$day/${room.id}/$time/${u.id}" - entry { - attributes["data-search"] = s.stringify() - td { - a(href) { - +u.name - } - } - td { - +u.interested.toString() - } - td { - +(u.track?.name ?: "") - } - td { - +u.projector.toString() - } - td { - +u.resolution.toString() - } - td { - +u.length.toString() - } - } - } - } - } - } - } - } - } - get("/calendar/{day}/{room}/{time}/{workgroup}") { - authenticateOrRedirect(Permission.SCHEDULE) { user -> - val day = call.parameters["day"]?.toIntOrNull() ?: return@get - val time = call.parameters["time"]?.toIntOrNull() ?: return@get - val roomId = call.parameters["room"]?.toLongOrNull() ?: return@get - val workGroupId = call.parameters["workgroup"]?.toLongOrNull() ?: return@get - - val room = RoomRepository.get(roomId) ?: return@get - val workGroup = WorkGroupRepository.get(workGroupId) ?: return@get - - val schedule = Schedule(null, workGroup, room, day, time) - ScheduleRepository.create(schedule) - - val redirect = call.parameters["redirect"] - call.respondRedirect(redirect ?: "/calendar/$day") - } - } - - get("/calendar/{day}/{schedule}/delete") { - authenticateOrRedirect(Permission.SCHEDULE) { user -> - val day = call.parameters["day"]?.toIntOrNull() ?: return@get - val scheduleId = call.parameters["schedule"]?.toLongOrNull() ?: return@get - - ScheduleRepository.delete(scheduleId) - - val redirect = call.parameters["redirect"] - call.respondRedirect(redirect ?: "/calendar/$day") - } - } } enum class CalendarOrientation {