rjaros / kvision

Object oriented web framework for Kotlin/JS
https://kvision.io
MIT License
1.23k stars 65 forks source link

Issue integrating react-big-schedule #534

Closed chavu closed 3 months ago

chavu commented 3 months ago

I have managed to integrate react-big-schedule library but i have two issues that I'm failing to resolve.

  1. NEXT and PREV navigation buttons only work first time they are pressed, pressing the second time is not refreshing the view but moves to the correct time window.

  2. Resources View is not showing the events, but listing the resources correctly on the left column. When I inspect the events they correctly have the resource Id

SchedulerView.kt

import com.digitres.pmis.modules.coreProject.activities.ActivitiesManager
import com.digitres.pmis.modules.scheduling.models.Event
import com.digitres.pmis.modules.scheduling.models.SchedulerData
import com.digitres.pmis.redux.appStore
import io.kvision.core.Border
import io.kvision.core.Container
import io.kvision.core.Position
import io.kvision.core.onChange
import io.kvision.html.div
import io.kvision.react.react
import io.kvision.require
import io.kvision.types.toStringF
import io.kvision.utils.obj
import io.kvision.utils.px
import react.ComponentClass
import react.PropsWithChildren
import kotlin.js.Date

external interface BigSchedulerProps : PropsWithChildren {
    var schedulerData: SchedulerData
    var prevClick: ()-> Unit
    var nextClick: ()-> Unit
    var onViewChange: (scheduleData: SchedulerData, view : dynamic) -> Unit
    var onSelectDate: (scheduleData: SchedulerData, date: String) -> Unit
    var eventItemClick: (scheduleData: SchedulerData, event : dynamic) -> Unit
}

val Scheduler: ComponentClass<BigSchedulerProps> = require("react-big-schedule").Scheduler
val ViewType  = require("react-big-schedule").ViewType

fun Container.schedulerView() {

    div {
        position = Position.RELATIVE
        paddingBottom = 100.px

        val appState = appStore.getState()

        val resources = appState.teams.teamCollection.map {
            obj {
                this.id = it.id!!.toString()
                this.name = it.name
                this.groupOnly = true
            }
        }.toTypedArray()

        val events: Array<dynamic> = ActivitiesManager.activitiesToScheduleEvents(appState.activities.activityCollection)
        console.log(events)
        val firstEvent = events.firstOrNull()
        val viewStartDate: String = if (firstEvent != null) firstEvent.unsafeCast<Event>().start as String else Date().toStringF("YYYY-MM-DD")
        val data = SchedulerData(
            date = viewStartDate,
            viewType = ViewType.Week ,
            showAgenda = false,
            isEventPerspective = true,
        )
        data.setEvents(events)
        data.config.views = arrayOf(
            obj {
                    viewName = "Week"
                    viewType = ViewType.Week
                    showAgenda = false
                    isEventPerspective = true
            },
            obj {
                viewName = "Month"
                viewType = ViewType.Month
                showAgenda = false
                isEventPerspective = true
            },
            obj {
                viewName = "Year"
                viewType = ViewType.Year
                showAgenda = false
                isEventPerspective = true
            },
            obj {
                viewName = "Resources"
                viewType = ViewType.Year
                showAgenda = false
                isEventPerspective = false
            }
        )
        data.config.dragAndDropEnabled = false
        data.config.schedulerWidth = "85%"
        data.config.schedulerContentHeight = "100%"

         data.setResources(resources)

        react(data) { getState, changeState ->

            Scheduler {
                schedulerData = getState()
                prevClick = {
                    schedulerData.prev()
                    schedulerData.setEvents(events)
                    changeState {  schedulerData }
                    console.log("PREV button clicked")
                }
                nextClick = {
                    data.next()
                    data.setEvents(events)
                    changeState {  data }
                    console.log("NEXT button clicked")
                }
                onSelectDate = { scheduleData, date ->
                    scheduleData.setDate(date)
                    schedulerData.setEvents(events)
                    changeState {  schedulerData }
                    console.log("Date selected")
                    console.log(date)
                }
                onViewChange = { schedulerData , view ->
                    schedulerData.setViewType(view.viewType, showAgenda = view.showAgenda,  isEventPerspective = view.isEventPerspective)
                    schedulerData.setEvents(events)
                }
                eventItemClick = {scheduleData, event ->
                    console.log("Event clicked")
                    console.log(event)
                }

           }
        }

    }
}

ScheduleData.kt

@file:JsModule("react-big-schedule")
@file:JsNonModule
package com.digitres.pmis.modules.scheduling.models

open external class SchedulerData(
    date: String,
    viewType: dynamic,
    showAgenda: Boolean,
    isEventPerspective: Boolean,
) {
    open var config: dynamic
    open var events: Array<dynamic>
    open var views: Array<dynamic>
    open fun setEvents(events: Array<dynamic>)
    open fun setDate(date: String)
    open fun setResources(resources: Array<dynamic>)
    open fun prev()
    open fun next()
    open fun setViewType(viewType: dynamic, showAgenda: dynamic, isEventPerspective: dynamic)

}

external interface Event {
    var id: String
    var title: String
    var start: dynamic
    var end: dynamic
    var bgColor: String?
    var movable: Boolean?
    var resizable: Boolean?
    var groupId: Int?
    var groupName: String?
    var resourceId: String?
}
rjaros commented 3 months ago

I'm not sure I can help you, because I don't know this react component. But I'm wondering why do you use:

react(data) { getState, changeState ->

form of react function call, because you don't use the reference it returns (and the whole idea of this is to access react component internal state from kvision component, see: https://kvision.gitbook.io/kvision-guide/4.-integrating-with-javascript-libraries/using-react-components#advanced-components). Perhaps simplyfing the code and dropping getState, changeState calls will help you?

chavu commented 3 months ago

When I don't use this form of react the PREV, NEXT and OnslectDate don't work at all even once. That's why I think its to do with the Kvision/KotlinJS side of state management rather than the the library. Let me keep digging. Will update here if I find something.

By the way I was following this example from you. https://slack-chats.kotlinlang.org/t/515741/is-there-any-calendar-widget-i-could-use-that-could-be-prese

chavu commented 3 months ago

I have managed by keeping the schedulerData viewModel inside redux state then refreshing it during the PREV and NEXT schudeler functions. Below is the final code for SchedulerView.kt

SchedulerView.kt

package com.digitres.pmis.modules.scheduling

import com.digitres.pmis.modules.coreProject.activities.redux.ActivitiesActions
import com.digitres.pmis.modules.scheduling.models.SchedulerData
import com.digitres.pmis.redux.appStore
import io.kvision.core.Container
import io.kvision.core.Position
import io.kvision.html.div
import io.kvision.react.react
import io.kvision.require
import io.kvision.state.bind
import io.kvision.utils.px
import react.ComponentClass
import react.PropsWithChildren

external interface BigSchedulerProps : PropsWithChildren {
    var schedulerData: SchedulerData
    var prevClick: (scheduleData: SchedulerData)-> Unit
    var nextClick: (scheduleData: SchedulerData)-> Unit
    var onViewChange: (scheduleData: SchedulerData, view : dynamic) -> Unit
    var onSelectDate: (scheduleData: SchedulerData, date: String) -> Unit
    var eventItemClick: (scheduleData: SchedulerData, event : dynamic) -> Unit
}

val Scheduler: ComponentClass<BigSchedulerProps> = require("react-big-schedule").Scheduler
val ViewType  = require("react-big-schedule").ViewType

fun Container.schedulerView() {

    div().bind(appStore) {appState ->
        position = Position.RELATIVE
        paddingBottom = 100.px

        val data:SchedulerData = appState.activities.schedulerData!!
        val events = data.events

        react {
            Scheduler {
                schedulerData = data
                prevClick = { schedulerData  ->
                    schedulerData.prev()
                    schedulerData.setEvents(events)
                    appStore.dispatch(ActivitiesActions.SetSchedulerData(schedulerData))
                }
                nextClick = { schedulerData  ->
                    schedulerData.next()
                    schedulerData.setEvents(events)
                    appStore.dispatch(ActivitiesActions.SetSchedulerData(schedulerData))
                }
                onSelectDate = { schedulerData, date ->
                    schedulerData.setDate(date)
                    schedulerData.setEvents(events)
                    appStore.dispatch(ActivitiesActions.SetSchedulerData(schedulerData))
                }
                onViewChange = { schedulerData , view ->
                    schedulerData.setViewType(view.viewType, showAgenda = view.showAgenda,  isEventPerspective = view.isEventPerspective)
                    schedulerData.setEvents(events)
                }
                eventItemClick = { _, event ->
                    console.log("Event clicked")
                    console.log(event)
                }

           }
        }
    }
}