(fullcalendar) Unknown option 'schedulerLicenseKey' , Unknown option 'resources'

605 Views Asked by At

I'm developing a project for which I'm using @fullcalendar/react the problem that I'm facing is that I want to use 'resourceTimeGridDay' but the when I run the my project it says in the console that Unknown option 'schedulerLicenseKey' , Unknown option 'resources', the project is an update of old version where the other company were using the schedulerLicenseKey which I do have, the above warning even appears on schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source', open source...

the versions that i'm using for the updated version of the project are

"@fullcalendar/core": "^6.1.1",
"@fullcalendar/daygrid": "^5.11.0",
"@fullcalendar/interaction": "^5.11.0",
"@fullcalendar/list": "^5.11.0",
"@fullcalendar/react": "^5.11.1",
"@fullcalendar/resource": "^6.1.1",
"@fullcalendar/resource-daygrid": "^6.1.1",
"@fullcalendar/resource-timegrid": "^6.1.1",
"@fullcalendar/resource-timeline": "^6.1.1",
"@fullcalendar/timegrid": "^5.11.0",
"@fullcalendar/timeline": "^5.11.0",

and old version where I'm getting the from are:

"@fullcalendar/core": "^4.3.1",
"@fullcalendar/daygrid": "^4.3.0",
"@fullcalendar/interaction": "^4.3.0",
"@fullcalendar/resource-timegrid": "^4.3.0",
"@fullcalendar/timegrid": "^4.3.0",

Below is my code for the calendar.

/* eslint-disable no-unused-vars */
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { getDurations } from '@utils'

// ** Reactstrap
import { Card, CardBody, Input, FormGroup, Label } from 'reactstrap'

// ** Calendar Plugins
import Flatpickr from 'react-flatpickr'
import FullCalendar from '@fullcalendar/react'
import listPlugin from '@fullcalendar/list'
import dayGridPlugin from '@fullcalendar/daygrid'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin from '@fullcalendar/interaction'
import resourceTimeGridPlugin from '@fullcalendar/resource-timegrid'
// import resourceTimelinePlugin from '@fullcalendar/resource-timeline'
// import resourceDayGridPlugin from '@fullcalendar/resource-daygrid'

// import '@fullcalendar/core/main.css'
import '@fullcalendar/daygrid/main.css'
import '@fullcalendar/timegrid/main.css'

// ** Third Party Components
import moment from 'moment'
import { Menu } from 'react-feather'
import * as Icon from 'react-feather'
import useMediaQuery from '@src/utility/hooks/useMediaQuery'

// ** Custom Components
import SelectField from '@select'
import CustomSpinner from '@spinner'
import Avatar from '@components/avatar'
import AddEventSideBar from './AddEventSideBar'
import defaultAvatar from '@src/assets/default.png'

// ** Styles
import '@styles/react/apps/app-calendar.scss'

// ** Store & Actions
import { useDispatch, useSelector } from 'react-redux'
import { resetBookings } from '@store/booking/bookingSlice'
import { getAllRoomsAction } from '@store/rooms/roomsAction'
import { getAllLocationsAction } from '@store/locations/locationsAction'
import {
  getAllBookingsAction,
  getBookingByIdAction
} from '@store/booking/bookingsAction'

const calendarsColor = {
  'Ethera Isaac': 'admin',
  'Ethera OC': 'admin',
  'Ethera Irvine': 'admin'
}

function Calendar() {
  const dispatch = useDispatch()
  const calendarRef = useRef(null)
  const isMobile = useMediaQuery('(max-width: 600px)')
  const smallScreen = useMediaQuery('(min-width: 900px)')
  const largeScreen = useMediaQuery('(min-width: 1350px)')

  // ** Selectors
    const {
      success,
      loading,
      getBooking,
      getAllBookings,
      bookingPending,
      updatePending
    } = useSelector((state) => state.booking)
    const getAllRooms = useSelector((state) => state.rooms.getAllRooms?.roomsList)
    const roomsPending = useSelector((state) => state.rooms.loading)
    const getAllLocations = useSelector(
      (state) => state.locations.getAllLocations?.locationsList
    )
    const locationsPending = useSelector(
      (state) => state.locations.getAllLocations?.loading
    )

    const rows = getAllBookings?.bookingsList

    // ** States
    const [room, setRoom] = useState()
    const [endDate, setEndDate] = useState('')
    const [location, setLocation] = useState()
    const [startDate, setStartDate] = useState('')
    const [roomsList, setRoomsList] = useState([])
    const [allRooms, setAllRooms] = useState(false)
    const [eventsList, setEventsList] = useState([])
    const [calendarApi, setCalendarApi] = useState(null)
    const [locationsList, setLocationsList] = useState([])
    const [addSidebarOpen, setAddSidebarOpen] = useState(false)
    const [allRoomsDayGrid, setAllRoomsDayGrid] = useState(true)

    // ** Rooms Resources
    const resources = useMemo(() => {
      return roomsList
        .map((room) => {
          return { id: room.value, title: room.text }
        })
        .filter((_, index) => index !== 0)
    }, [roomsList])

    // ** Function to handle sidebar visibility
      const handleAddAppointmentSidebar = (id) => {
        setAddSidebarOpen(!addSidebarOpen)
        if (id && addSidebarOpen === false) {
          dispatch(getBookingByIdAction(id))
        }
      }

    useEffect(() => {
      if (calendarApi === null) {
        setCalendarApi(calendarRef.current.getApi())
      }
    }, [calendarApi])

     // ** Getting Bookings
      useEffect(() => {
        if (location?.value) {
          dispatch(
            getAllBookingsAction({
              offset: 0,
              limit: 100,
              startDate,
              endDate,
              location: location?.value,
              room: room?.value,
              callback: () => setEventsList([])
            })
          )
        }
      }, [endDate, location, room, success])

     // ** Getting Locations
     useEffect(() => {
       dispatch(getAllLocationsAction())
     }, [])

     // ** Getting Rooms
     useEffect(() => {
       if (location?.value) {
         dispatch(getAllRoomsAction({ id: location?.value }))
       }
     }, [location?.value])

     // ** Creating Locations and Rooms lists
     useEffect(() => {
       if (getAllLocations) {
         const arrLocations = []
         getAllLocations.forEach((item) => {
           arrLocations.push({
             text: item?.name,
             value: item.id
           })
         })
         setLocation(arrLocations[0])
         setLocationsList(arrLocations)
       }
     }, [getAllLocations])

     useEffect(() => {
       if (getAllRooms) {
         const arrRooms = [{ text: 'All Rooms', value: '' }]
         getAllRooms.forEach((item) => {
           arrRooms.push({
             text: item?.name,
             value: item.id
           })
         })
         // setRoom(arrRooms[0])
         setRoomsList(arrRooms)
       }
     }, [getAllRooms])

     // ** Date Picker
     const onDateChangeHandler = (dates) => {
       if (dates.length === 1) {
         setStartDate(dates[0])
         calendarRef.current.getApi().gotoDate(new Date(dates[0]))
       }
       if (dates.length === 2) {
         setStartDate(dates[0])
         setEndDate(dates[1])
         calendarRef.current.getApi().gotoDate(new Date(dates[1]))
       }
     }

     // ** Function to handle filters
     const onChangeHandler = (name, value) => {
       if (name === 'location') setLocation(value)
       if (name === 'room') setRoom(value)
     }

     const events = () => {
let events = []
const durations = getDurations(rows)
console.log('durations : ', durations)
if (rows.length > 0 && room?.value) {
  events = rows.map((row) => {
    return {
      allDay: false,
      url: '',
      start: `${row?.start_date__date}T${row?.start_time}`,
      end: `${row?.start_date__date}T${row?.end_time}`,
      title: `${row?.provider__first_name} ${row?.provider__last_name}`,
      extendedProps: {
        id: row?.id,
        date: row?.start_date__date,
        calendar: 'Ethera OC',
        img: row?.provider__avatar,
        end_time: row?.appointments__end_time || '--',
        start_time: row?.appointments__start_time || '--',
        duration: `${row?.duration / 3600} hours`
      }
    }
  })
}

if (rows.length > 0 && durations.length > 0 && !room?.value) {
  durations.map((durationArr, index) => {
    let sum = 0
    durationArr.forEach((item) => {
      sum += parseFloat(item)
    })
    events.push({
      allDay: false,
      url: '',
      id: `${index}`,
      start: `${rows[index]?.start_date__date}T${rows[index]?.start_time}`,
      end: `${rows[index]?.start_date__date}T${rows[index]?.end_time}`,
      title: `${sum / 3600}`,
      extendsProps: {
        start: `${rows[index]?.start_date__date}T${rows[index]?.start_time}`,
        end: `${rows[index]?.start_date__date}T${rows[index]?.end_time}`,
        calendar: 'Ethera OC'
      }
    })
  })
}

  return events
}

useEffect(() => {
  setEventsList(events())
}, [rows])

 // ** Duration Component
const DurationComponent = ({ calendarEvent }) => {
  return (
    <div className="fc-event-title">
      <div className="fc-event-time">
        {smallScreen ? (
          <span className="fc-event-time-text f-700 fs-s-med">
            <Icon.Clock color="black" size={20} className="me-1" />
            {calendarEvent?._def?.title} Hours
          </span>
        ) : (
          <span className="fc-event-time-text f-700 fs-s-med">
            {calendarEvent?._def?.title} H
          </span>
        )}
      </div>
    </div>
  )
}


// ** View component
const ViewComponent = ({ calendarEvent, id }) => {
const nameImage = `https://ui-avatars.com/api/?    name=${calendarEvent?.title}&background=0D8ABC&color=fff&size=128`

return (
  <div
    className="fc-event d-flex align-items-center justify-content-between"
    // onClick={() => handleAddAppointmentSidebar(id)}
    onClick={() => handleAddAppointmentSidebar(id)}
  >
    <div className="fc-event-title">
      <div className="fc-event-time">
        <span className="fc-event-time-text">
          {(calendarEvent?.extendedProps?.start_time !== '--' &&
            moment(
              calendarEvent?.extendedProps?.start_time,
              'HH:mm:ss'
            ).format('hh:mm A')) ||
            '--'}
          {' - '}
          {(calendarEvent?.extendedProps?.end_time !== '--' &&
            moment(
              calendarEvent?.extendedProps?.end_time,
              'HH:mm:ss'
            ).format('hh:mm A')) ||
            '--'}
        </span>
      </div>
      <div className="fc-event-title-text">
        <span className="fc-event-title-text-text f-700 fs-s-med">
          {calendarEvent?.title}
        </span>
      </div>
    </div>

    <Avatar
      className="provider-avatar-calendar"
      // img={calendarEvent?.extendedProps?.img || defaultAvatar}

      img={calendarEvent?.extendedProps?.img || nameImage}
      imgHeight={smallScreen ? '35' : '30'}
      imgWidth={smallScreen ? '35' : '30'}
    />
  </div>
)
}

   // ** Calendar Options
  const calendarOptions = {
  schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source',
  // resources,
  resources: [
    { id: 'a', title: 'Resource A' },
    { id: 'b', title: 'Resource B' },
    { id: 'c', title: 'Resource C' }
  ],
  eventSources: [
    {
      events: eventsList,
      textColor: 'black'
    }
  ],
  // events: appointments,
  plugins: [
    interactionPlugin,
    resourceTimeGridPlugin,
    dayGridPlugin,
    timeGridPlugin,
    listPlugin
  ],
  // initialView: 'timeGridWeek',
  initialView: 'timeGridDay',
  headerToolbar: {
    start: (() => {
      if (isMobile) {
        return 'title, prev, next'
      } else return 'title, prev, next, timeGridDay,timeGridWeek,dayGridMonth'
    })(),
    center: (() => {
      if (isMobile) {
        return 'dayGridMonth,timeGridWeek,timeGridDay'
      } else return ''
    })(),
    end: 'venueSelect'
  },
  eventOverlap: false,
  ref: calendarRef,
  editable: false,
  dayMaxEvents: 2,
  eventResizableFromStart: true,
  dragScroll: true,
  navLinks: true,
  selectable: true,
  views: {
    timeGridDay: {
      type: 'timeGrid',
      duration: { days: 1 },
      slotDuration: '00:30:00',
      slotLabelInterval: '00:30:00',
      slotLabelFormat: {
        hour: 'numeric',
        minute: '2-digit',
        omitZeroMinute: false,
        meridiem: 'short'
      },
      slotMinTime: '00:00:00',
      slotMaxTime: '24:00:00',
      allDaySlot: true,
      dayHeaderContent: (arg) => {
        // console.log('get all rooms', arg)
        return (
          <div className="fc-daygrid-day-top">
            <div className="fc-daygrid-day-number">
              {moment(arg.date).format('MMMM D, YYYY')}
            </div>
          </div>
        )
      },
      eventContent: (arg) => {
        return <ViewComponent calendarEvent={arg.event} id={arg.event.id} />
      },
      eventDidMount: (arg) => {
        // console.log('eventDidMount', arg)
        setAllRoomsDayGrid(false)
      },
      eventWillUnmount: (arg) => {
        // console.log('eventWillUnmount', arg)
      },
      eventTimeFormat: {
        hour: 'numeric',
        minute: '2-digit',
        omitZeroMinute: false,
        meridiem: 'short'
      }
    }
  },
  customButtons: {
    sidebarToggle: {
      text: <Menu className="d-xl-none d-block" />,
      click() {
        toggleSidebar(true)
      }
    },

    venueSelect: {
      hint: 'Select',
      text: (
        <div className="calendar-selectors">
          <SelectField
            header
            wd="100%"
            search={false}
            value={location}
            data={locationsList}
            controlMaxWidth="270px"
            onChange={(e) => {
              onChangeHandler('location', e)
              onChangeHandler('room', null)
            }}
            controlMinWidth={largeScreen ? '230px' : '100%'}
            placeholder={locationsPending ? 'Loading...' : 'Locations'}
          />

          {allRoomsDayGrid && (
          <SelectField
            header
            wd="100%"
            value={room}
            search={false}
            data={roomsList}
            controlMaxWidth="270px"
            disabled={roomsPending}
            isLoading={roomsPending}
            formikError={!!room?.value === ''}
            onChange={(e) => onChangeHandler('room', e)}
            controlMinWidth={largeScreen ? '230px' : '100%'}
            placeholder={
              location?.value ? 'Select Room' : 'Select Location First'
            }
          />
        )}
      </div>
    )
  }
},
eventClassNames({ event: calendarEvent }) {
  const colorName =
    calendarsColor[calendarEvent?.extendedProps?.calendar] || 'admin'
  return [`bg-light-${colorName}`]
},
// eventClick({ event: clickedAppointment }) {
//   const id = clickedAppointment?.extendedProps?.id
//   handleAddAppointmentSidebar(id)
// },
eventContent({ event: calendarEvent }) {
  const day = calendarEvent?._context?.viewApi?.type === 'timeGridDay'
  const month = calendarEvent?._context?.viewApi?.type === 'dayGridMonth'
  const week = calendarEvent?._context?.viewApi?.type === 'timeGridWeek'
  const nameImage = `https://ui-avatars.com/api/?name=${calendarEvent?.title}&background=0D8ABC&color=fff&size=128`

  if ((month || week) && !room?.value) {
    return <DurationComponent calendarEvent={calendarEvent} />
  }

  if ((week || month) && room?.value) {
    return (
      <div className="event-small">
        {isMobile ? (
          <span className="f-calendar-small">{calendarEvent?.title}</span>
        ) : !smallScreen ? (
          <div className="fc-event d-flex align-items-center justify-content-between">
            <div className="fc-event-title">
              <span className="f-calendar-small">
                {calendarEvent?.title}
              </span>
            </div>

            <Avatar
              className="provider-avatar-calendar"
              img={calendarEvent?.extendedProps?.img || nameImage}
              imgHeight="25"
              imgWidth="25"
            />
          </div>
        ) : (
          <ViewComponent
            calendarEvent={calendarEvent}
            id={calendarEvent?._def?.extendedProps?.id}
          />
        )}
      </div>
    )
  }

    if (day) {
      return (
        <ViewComponent
          calendarEvent={calendarEvent}
          id={calendarEvent?._def?.extendedProps?.id}
        />
      )
    }
  }
}

  // ** Reset Bookings
useEffect(() => {
  return () => {
    dispatch(resetBookings())
  }
}, [])

return (
<>
  <AddEventSideBar
    open={addSidebarOpen}
    pending={bookingPending}
    updatePending={updatePending}
    selectedBooking={getBooking}
    locationsList={locationsList}
    handleOpen={handleAddAppointmentSidebar}
  />

  <Card className="shadow-none border-0 mb-0 bg-yellow rounded-0">
    <CardBody className="p-0 relative">
      <Label htmlFor="datePicker">
        <Icon.Calendar
          color="white"
          size={30}
          className="absolute datePicker-icon pointer"
        />
      </Label>
      <FullCalendar {...calendarOptions} />{' '}
      <Flatpickr
        id="datePicker"
        name="datePicker"
        className="form-control datePicker-non-visible"
        onChange={onDateChangeHandler}
        options={{
          mode: 'range',
          enableTime: false,
          dateFormat: 'F j, Y'
        }}
      />
    </CardBody>
  </Card>
</>
)
 }

export default Calendar  

so in the calendar what I'm doing is fetching locations and bookings on that location from an API, once the api's get fetched, on the Month, and Week calendar I will have to show total hours of the bookings on that day, if the room is selected then it will only fetch the bookings of that room on different dates, for which the month and week calendar events also gets changed, but on the day calendar I will have to show all the rooms in the header as resources so that it can show all the bookings on that day in different rooms...

also when changing "timeGridDay" to "resourceTimeGridDay" it gives me the error Uncaught Error: viewType "resourceTimeGridDay" is not available. Please make sure you've loaded all neccessary plugins

 initialView: 'timeGridDay',
  headerToolbar: {
  start: (() => {
    if (isMobile) {
      return 'title, prev, next'
    } else return 'title, prev, next, timeGridDay,timeGridWeek,dayGridMonth'
  })(),
  center: (() => {
    if (isMobile) {
      return 'dayGridMonth,timeGridWeek,timeGridDay'
    } else return ''
  })(),
  end: 'venueSelect'
},

if any one can help me with this issue of "Unknow option schedulerLicenseKey" and "Unknow option resources".

Thank you in advance

1

There are 1 best solutions below

0
chemsseddine Hadiby On

Check this issue : https://github.com/fullcalendar/fullcalendar/issues/5462

import { BASE_OPTION_REFINERS, BASE_OPTION_DEFAULTS } from '@fullcalendar/core'
BASE_OPTION_REFINERS.schedulerLicenseKey = String

BASE_OPTION_DEFAULTS.schedulerLicenseKey = 'GPL-My-Project-Is-Open-Source'