rjaros / kvision

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

Gantt and Calendar view of tasks #533

Open chavu opened 2 weeks ago

chavu commented 2 weeks ago

I'm working on project task management tool. I'm currently displaying the tasks in tabulator table. I now want to add Gantt and Calendar views. Does Kvision have any component that can do this? If not, how can I use extrernal JS libraries like these two below:

Google Charts (https://developers.google.com/chart/interactive/docs/gallery/ganttchart) https://dlhsoft.com/GanttChartHyperLibrary/

rjaros commented 2 weeks ago

I haven't done that, but you can try using scatter chart with chart.js module. See this SO example: https://stackoverflow.com/questions/41259441/how-to-draw-gantt-chart-using-chart-js-or-other-libraries

chavu commented 2 weeks ago

Thanks. I will try it out and give feedback

chavu commented 2 weeks ago

Instead of using the Chart module, I desided to try integrating this full-fledged react scheduler component https://github.com/Bitnoise/react-scheduler The scduler is no display and see the following error message: "TypeError: e.reduce is not a function or its return value is not iterable" Below are my code .

Data clasess define in CommonMain module


import io.kvision.types.LocalDateTime
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable

@Serializable
data class ScheduleData(
    val id: String,
    val label: ScheduleRowLabel,
    val data: List<ScheduleResourceItem>? = null
)

@Serializable
data class ScheduleRowLabel(
    val icon: String? = null,
    val title: String,
    val subTitle: String? = null,
)

@Serializable
data class ScheduleResourceItem(
    val id: String, //unique resource id
    val title: String, //resource title that will be displayed on resource tile
    val subTitle: String? = null, //resource subtitle that will be displayed on resource tile
    val description: String? = null, //resource description that will be displayed on resource tile
    @Contextual val startDate: LocalDateTime, //date for calculating start position for resource
    @Contextual val endDate: LocalDateTime, //date for calculating end position for resource
    @Contextual  val occupancy: Number = 30, //number of seconds resource takes up for given row that will be visible on resource tooltip when hovered
    val bgColor: String, //tile color
)

data class ScheduleConfig(
    val zoom: Number = 1, // 0 - weeks, 1 - Days
    val filterButtonState: Number,
    val maxRecordsPerPage: Number,
    val lang: String = "en",
    val includeTakenHoursOnWeekendsInDayView: Boolean
)

View to display the scheduler


import com.digitres.pmis.models.scheduling.ScheduleConfig
import com.digitres.pmis.models.scheduling.ScheduleData
import com.digitres.pmis.models.scheduling.ScheduleResourceItem
import io.kvision.core.Container
import io.kvision.react.react
import react.ComponentClass
import react.PropsWithChildren
import react.PropsWithRef

external interface ReactSchedulerProps : PropsWithRef<dynamic>, PropsWithChildren {
    var data: List<ScheduleData>
    var isLoading: Boolean
    var onRangeChange: (String) -> Unit
    var onTileClick: (String) -> Unit
    var onItemClick: (ScheduleResourceItem) -> Unit
    var onFilterData: () -> Unit
    var onClearData: () -> Unit
    var config: ScheduleConfig
}

val Scheduler: ComponentClass<ReactSchedulerProps> = io.kvision.require("@bitnoi.se/react-scheduler").Scheduler

fun Container.scheduleView() {
    react {
        Scheduler {
            data = SampleSchedule.data
            isLoading = false
            config = ScheduleConfig(
                zoom = 1,
                filterButtonState = 0,
                maxRecordsPerPage = 10,
                lang = "en",
                includeTakenHoursOnWeekendsInDayView = true
            )
        }
    }
}

Sample Data to display


import com.digitres.pmis.models.scheduling.ScheduleData
import com.digitres.pmis.models.scheduling.ScheduleResourceItem
import com.digitres.pmis.models.scheduling.ScheduleRowLabel
import kotlin.js.Date

object SampleSchedule {
    val data = listOf(
        ScheduleData(
            id = "1",
            label = ScheduleRowLabel(
                icon = null,
                title = "Facility 1",
                subTitle = null
            ),
            data = listOf(
                ScheduleResourceItem(
                    id = "1.1",
                    title = "Team 1",
                    subTitle = null,
                    description = null,
                    startDate = Date("2024-09-02T08:00"),
                    endDate = Date("2024-09-11T08:00"),
                    occupancy = 20,
                    bgColor = "rgb(254,165,177)"
                )
            )
        ),
        ScheduleData(
                id = "2",
        label = ScheduleRowLabel(
            icon = null,
            title = "Facility 2",
            subTitle = null
        ),
        data = listOf(
            ScheduleResourceItem(
                id = "2.1",
                title = "Team 2",
                subTitle = null,
                description = null,
                startDate = Date("2024-09-13T08:00"),
                endDate = Date("2024-09-20T08:00"),
                occupancy = 20,
                bgColor = "rgb(254,165,177)"
            )
        )
    )
    )
}

build.gradle dependencies addedd

        val jsMain by getting {
            resources.srcDir(webDir)
            dependencies {
                implementation(npm("react-is", "18.3.1"))
                implementation(npm("@babel/core", "7.24.7"))
                implementation(npm("@babel/plugin-syntax-jsx", "7.24.7"))
                implementation(npm("@bitnoi.se/react-scheduler", "0.2.0"))
                 implementation("io.kvision:kvision-react:$kvisionVersion")
                ...
            }
            kotlin.srcDir("build/generated-src/frontend")
        }

Error Stack

index.umd.cjs:114 Uncaught TypeError: e.reduce is not a function or its return value is not iterable
    at Bo (index.umd.cjs:114:4169)
    at eval (index.umd.cjs:114:4591)
    at mountMemo (react-dom.development.js:17225:19)
    at Object.useMemo (react-dom.development.js:17670:16)
    at Object.useMemo (react.development.js:1651:21)
    at Ro (index.umd.cjs:114:4579)
    at as (index.umd.cjs:138:544)
    at renderWithHooks (react-dom.development.js:16305:18)
    at mountIndeterminateComponent (react-dom.development.js:20069:13)
    at beginWork (react-dom.development.js:21582:16)
rjaros commented 2 weeks ago

I'm almost sure you can't use Kotlin types (lists or data classes), even serializable, as a data model for React components. So it's very unlikely List<ScheduleData> will work. In my opinion you need to use Array<dynamic> or other external types.

chavu commented 2 weeks ago

Thanks. Using Arrary worked. I then converted the Kotlin List as below.

data =  JSON.parse(Json.encodeToString(SampleSchedule.data))

I am now able to display the scheduler. However it is displaying fullscreen and yet it's supposed to be within the content area under the header. I am also not sure if its style I added to App.kt is being utilised.

class App : Application() {
    init {
        require("./css/custom.css")
        require("@bitnoi.se/react-scheduler/dist/style.css")
    }
...
}
rjaros commented 2 weeks ago

Have you checked this: https://github.com/Bitnoise/react-scheduler/issues/60 ?

chavu commented 2 weeks ago

Thanks, wrapping the scheduler in div and setting Relative position worked.