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
Check this issue : https://github.com/fullcalendar/fullcalendar/issues/5462