Schedule provides an Outlook, iCal like JSF component to manage events which is based on FullCalendar. Schedule is highly customizable featuring various views (month, day, week), built-in I18N, drag-drop, resize, customizable event dialog, ajax listeners for every user interaction and more.
<div class="card">
    <h:form>
        <p:growl id="messages" showDetail="true"/>
        <p:schedule id="schedule" value="#{scheduleJava8View.eventModel}" widgetVar="myschedule"
                    timeZone="#{scheduleJava8View.serverTimeZone}" clientTimeZone="#{scheduleJava8View.serverTimeZone}">
            <p:ajax event="dateSelect" listener="#{scheduleJava8View.onDateSelect}" update="eventDetails"
                    oncomplete="PF('eventDialog').show();"/>
            <p:ajax event="eventSelect" listener="#{scheduleJava8View.onEventSelect}" update="eventDetails"
                    oncomplete="PF('eventDialog').show();"/>
            <p:ajax event="eventMove" listener="#{scheduleJava8View.onEventMove}" update="messages"/>
            <p:ajax event="eventResize" listener="#{scheduleJava8View.onEventResize}" update="messages"/>
        </p:schedule>
        <p:dialog widgetVar="eventDialog" header="Event Details" showEffect="fade" hideEffect="fade">
            <h:panelGrid id="eventDetails" columns="2" cellpadding="7">
                <p:outputLabel for="title" value="Titles" styleClass="font-bold"/>
                <p:inputText id="title" value="#{scheduleJava8View.event.title}" required="true"/>
                <p:outputLabel for="from" value="From" styleClass="font-bold"/>
                <p:datePicker id="from" value="#{scheduleJava8View.event.startDate}" pattern="dd/MM/yyyy"
                              showTime="true" appendTo="@(body)"/>
                <p:outputLabel for="to" value="To" styleClass="font-bold"/>
                <p:datePicker id="to" value="#{scheduleJava8View.event.endDate}" pattern="dd/MM/yyyy"
                              showTime="true" appendTo="@(body)"/>
                <p:outputLabel for="allDay" value="All Day" styleClass="font-bold"/>
                <h:selectBooleanCheckbox id="allDay" value="#{scheduleJava8View.event.allDay}"/>
            </h:panelGrid>
            <div class="flex justify-content-between mt-3">
                <p:commandButton type="reset" value="Reset" styleClass="font-bold ui-button-outlined"/>
                <p:commandButton id="addButton" value="Save" action="#{scheduleJava8View.addEvent}" 
                            oncomplete="PF('myschedule').update();PF('eventDialog').hide();"/>
            </div>               
        </p:dialog>
    </h:form>
</div>
package org.primefaces.showcase.view.data;
import org.primefaces.event.SelectEvent;
import org.primefaces.event.schedule.ScheduleEntryMoveEvent;
import org.primefaces.event.schedule.ScheduleEntryResizeEvent;
import org.primefaces.event.schedule.ScheduleRangeEvent;
import org.primefaces.model.DefaultScheduleEvent;
import org.primefaces.model.DefaultScheduleModel;
import org.primefaces.model.LazyScheduleModel;
import org.primefaces.model.ScheduleDisplayMode;
import org.primefaces.model.ScheduleEvent;
import org.primefaces.model.ScheduleModel;
import org.primefaces.showcase.service.ExtenderService;
import org.primefaces.showcase.service.ExtenderService.ExtenderExample;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import jakarta.annotation.PostConstruct;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.event.AjaxBehaviorEvent;
import jakarta.faces.model.SelectItem;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
@Named
@ViewScoped
public class ScheduleJava8View implements Serializable {
    @Inject
    private ExtenderService extenderService;
    private ScheduleModel eventModel;
    private ScheduleModel lazyEventModel;
    private ScheduleEvent<?> event = new DefaultScheduleEvent<>();
    private boolean slotEventOverlap = true;
    private boolean showWeekNumbers = false;
    private boolean showHeader = true;
    private boolean draggable = true;
    private boolean resizable = true;
    private boolean selectable = false;
    private boolean showWeekends = true;
    private boolean tooltip = true;
    private boolean allDaySlot = true;
    private boolean rtl = false;
    private double aspectRatio = Double.MIN_VALUE;
    private String leftHeaderTemplate = "prev,next today";
    private String centerHeaderTemplate = "title";
    private String rightHeaderTemplate = "dayGridMonth,timeGridWeek,timeGridDay,listYear";
    private String nextDayThreshold = "09:00:00";
    private String weekNumberCalculation = "local";
    private String weekNumberCalculator = "date.getTime()";
    private String displayEventEnd;
    private String timeFormat;
    private String slotDuration = "00:30:00";
    private String slotLabelInterval;
    private String slotLabelFormat = "HH:mm";
    private String scrollTime = "06:00:00";
    private String minTime = "04:00:00";
    private String maxTime = "20:00:00";
    private String locale = "en";
    private String serverTimeZone = ZoneId.systemDefault().toString();
    private String timeZone = "";
    private String clientTimeZone = "local";
    private String columnHeaderFormat = "";
    private String view = "timeGridWeek";
    private String height = "auto";
    private String extenderCode = "// Write your code here or select an example from above";
    private String selectedExtenderExample = "";
    private Map<String, ExtenderExample> extenderExamples;
    @PostConstruct
    public void init() {
        eventModel = new DefaultScheduleModel();
        addEvents2EventModel(LocalDateTime.now());
        addEvents2EventModel(LocalDateTime.now().minusMonths(6));
        lazyEventModel = new LazyScheduleModel() {
            @Override
            public void loadEvents(LocalDateTime start, LocalDateTime end) {
                for (int i = 1; i <= 5; i++) {
                    LocalDateTime random = getRandomDateTime(start);
                    addEvent(DefaultScheduleEvent.builder()
                            .title("Lazy Event " + i)
                            .startDate(random)
                            .endDate(random.plusHours(3))
                            .build());
                }
            }
        };
        extenderExamples = extenderService.createExtenderExamples();
    }
    private void addEvents2EventModel(LocalDateTime referenceDate) {
        DefaultScheduleEvent<?> event = DefaultScheduleEvent.builder()
                .title("Champions League Match")
                .startDate(previousDay8Pm(referenceDate))
                .endDate(previousDay11Pm(referenceDate))
                .description("Team A vs. Team B")
                .url("https://www.uefa.com/uefachampionsleague/")
                .borderColor("orange")
                .build();
        eventModel.addEvent(event);
        event = DefaultScheduleEvent.builder()
                .startDate(referenceDate.minusDays(6))
                .endDate(referenceDate.minusDays(3))
                .overlapAllowed(true)
                .editable(false)
                .resizable(false)
                .display(ScheduleDisplayMode.BACKGROUND)
                .backgroundColor("lightgreen")
                .build();
        eventModel.addEvent(event);
        event = DefaultScheduleEvent.builder()
                .title("Birthday Party")
                .startDate(today1Pm(referenceDate))
                .endDate(today6Pm(referenceDate))
                .description("Aragon")
                .overlapAllowed(true)
                .borderColor("#CB4335")
                .build();
        eventModel.addEvent(event);
        event = DefaultScheduleEvent.builder()
                .title("Breakfast at Tiffanys (always resizable)")
                .startDate(nextDay9Am(referenceDate))
                .endDate(nextDay11Am(referenceDate))
                .description("all you can eat")
                .overlapAllowed(true)
                .resizable(true)
                .borderColor("#27AE60")
                .build();
        eventModel.addEvent(event);
        event = DefaultScheduleEvent.builder()
                .title("Plant the new garden stuff (always draggable)")
                .startDate(theDayAfter3Pm(referenceDate))
                .endDate(fourDaysLater3pm(referenceDate))
                .description("Trees, flowers, ...")
                .draggable(true)
                .borderColor("#27AE60")
                .build();
        eventModel.addEvent(event);
        DefaultScheduleEvent<?> scheduleEventAllDay = DefaultScheduleEvent.builder()
                .title("Holidays (AllDay)")
                .startDate(sevenDaysLater0am(referenceDate))
                .endDate(eightDaysLater0am(referenceDate))
                .description("sleep as long as you want")
                .borderColor("#27AE60")
                .allDay(true)
                .build();
        eventModel.addEvent(scheduleEventAllDay);
    }
    public ExtenderService getScheduleExtenderService() {
        return extenderService;
    }
    public void setScheduleExtenderService(ExtenderService extenderService) {
        this.extenderService = extenderService;
    }
    public LocalDateTime getRandomDateTime(LocalDateTime base) {
        LocalDateTime dateTime = base.withMinute(0).withSecond(0).withNano(0);
        return dateTime.plusDays(((int) (Math.random() * 30)));
    }
    public ScheduleModel getEventModel() {
        return eventModel;
    }
    public ScheduleModel getLazyEventModel() {
        return lazyEventModel;
    }
    private LocalDateTime previousDay8Pm(LocalDateTime referenceDate) {
        return referenceDate.minusDays(1).withHour(20).withMinute(0).withSecond(0).withNano(0);
    }
    private LocalDateTime previousDay11Pm(LocalDateTime referenceDate) {
        return referenceDate.minusDays(1).withHour(23).withMinute(0).withSecond(0).withNano(0);
    }
    private LocalDateTime today1Pm(LocalDateTime referenceDate) {
        return referenceDate.withHour(13).withMinute(0).withSecond(0).withNano(0);
    }
    private LocalDateTime theDayAfter3Pm(LocalDateTime referenceDate) {
        return referenceDate.plusDays(1).withHour(15).withMinute(0).withSecond(0).withNano(0);
    }
    private LocalDateTime today6Pm(LocalDateTime referenceDate) {
        return referenceDate.withHour(18).withMinute(0).withSecond(0).withNano(0);
    }
    private LocalDateTime nextDay9Am(LocalDateTime referenceDate) {
        return referenceDate.plusDays(1).withHour(9).withMinute(0).withSecond(0).withNano(0);
    }
    private LocalDateTime nextDay11Am(LocalDateTime referenceDate) {
        return referenceDate.plusDays(1).withHour(11).withMinute(0).withSecond(0).withNano(0);
    }
    private LocalDateTime fourDaysLater3pm(LocalDateTime referenceDate) {
        return referenceDate.plusDays(4).withHour(15).withMinute(0).withSecond(0).withNano(0);
    }
    private LocalDateTime sevenDaysLater0am(LocalDateTime referenceDate) {
        return referenceDate.plusDays(7).withHour(0).withMinute(0).withSecond(0).withNano(0);
    }
    private LocalDateTime eightDaysLater0am(LocalDateTime referenceDate) {
        return referenceDate.plusDays(7).withHour(0).withMinute(0).withSecond(0).withNano(0);
    }
    public LocalDate getInitialDate() {
        return LocalDate.now().plusDays(1);
    }
    public ScheduleEvent<?> getEvent() {
        return event;
    }
    public void setEvent(ScheduleEvent<?> event) {
        this.event = event;
    }
    public void addEvent() {
        if (event.isAllDay()) {
            // see https://github.com/primefaces/primefaces/issues/1164
            if (event.getStartDate().toLocalDate().equals(event.getEndDate().toLocalDate())) {
                event.setEndDate(event.getEndDate().plusDays(1));
            }
        }
        if (event.getId() == null) {
            eventModel.addEvent(event);
        }
        else {
            eventModel.updateEvent(event);
        }
        event = new DefaultScheduleEvent<>();
    }
    public void onEventSelect(SelectEvent<ScheduleEvent<?>> selectEvent) {
        event = selectEvent.getObject();
    }
    public void onViewChange(SelectEvent<String> selectEvent) {
        view = selectEvent.getObject();
        FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "View Changed", "View:" + view);
        addMessage(message);
    }
    public void onDateSelect(SelectEvent<LocalDateTime> selectEvent) {
        event = DefaultScheduleEvent.builder()
                .startDate(selectEvent.getObject())
                .endDate(selectEvent.getObject().plusHours(1))
                .build();
    }
    public void onEventMove(ScheduleEntryMoveEvent event) {
        FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Event moved",
                "Delta:" + event.getDeltaAsDuration());
        addMessage(message);
    }
    public void onEventResize(ScheduleEntryResizeEvent event) {
        FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Event resized",
                "Start-Delta:" + event.getDeltaStartAsDuration() + ", End-Delta: " + event.getDeltaEndAsDuration());
        addMessage(message);
    }
    public void onRangeSelect(ScheduleRangeEvent event) {
        FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Range selected",
                "Start-Date:" + event.getStartDate() + ", End-Date: " + event.getEndDate());
        addMessage(message);
    }
    public void onEventDelete() {
        String eventId = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("eventId");
        if (event != null) {
            ScheduleEvent<?> event = eventModel.getEvent(eventId);
            eventModel.deleteEvent(event);
        }
    }
    public void onExtenderExampleSelect(AjaxBehaviorEvent event) {
        ExtenderExample example = getExtenderExample();
        if (!"custom".equals(selectedExtenderExample) && example != null) {
            if (example.getDetails() != null && !example.getDetails().isEmpty()) {
                FacesMessage message = new FacesMessage(example.getName(), example.getDetails());
                FacesContext.getCurrentInstance().addMessage(event.getComponent().getClientId(), message);
            }
            this.extenderCode = example.getValue();
        }
    }
    private void addMessage(FacesMessage message) {
        FacesContext.getCurrentInstance().addMessage(null, message);
    }
    public boolean isShowWeekends() {
        return showWeekends;
    }
    public void setShowWeekends(boolean showWeekends) {
        this.showWeekends = showWeekends;
    }
    public boolean isSlotEventOverlap() {
        return slotEventOverlap;
    }
    public void setSlotEventOverlap(boolean slotEventOverlap) {
        this.slotEventOverlap = slotEventOverlap;
    }
    public boolean isShowWeekNumbers() {
        return showWeekNumbers;
    }
    public void setShowWeekNumbers(boolean showWeekNumbers) {
        this.showWeekNumbers = showWeekNumbers;
    }
    public boolean isShowHeader() {
        return showHeader;
    }
    public void setShowHeader(boolean showHeader) {
        this.showHeader = showHeader;
    }
    public boolean isDraggable() {
        return draggable;
    }
    public void setDraggable(boolean draggable) {
        this.draggable = draggable;
    }
    public boolean isResizable() {
        return resizable;
    }
    public void setResizable(boolean resizable) {
        this.resizable = resizable;
    }
    public boolean isSelectable() {
        return selectable;
    }
    public void setSelectable(boolean selectable) {
        this.selectable = selectable;
    }
    public boolean isTooltip() {
        return tooltip;
    }
    public void setTooltip(boolean tooltip) {
        this.tooltip = tooltip;
    }
    public boolean isRtl() {
        return rtl;
    }
    public void setRtl(boolean rtl) {
        this.rtl = rtl;
    }
    public boolean isAllDaySlot() {
        return allDaySlot;
    }
    public void setAllDaySlot(boolean allDaySlot) {
        this.allDaySlot = allDaySlot;
    }
    public double getAspectRatio() {
        return aspectRatio == 0 ? Double.MIN_VALUE : aspectRatio;
    }
    public void setAspectRatio(double aspectRatio) {
        this.aspectRatio = aspectRatio;
    }
    public String getLeftHeaderTemplate() {
        return leftHeaderTemplate;
    }
    public void setLeftHeaderTemplate(String leftHeaderTemplate) {
        this.leftHeaderTemplate = leftHeaderTemplate;
    }
    public String getCenterHeaderTemplate() {
        return centerHeaderTemplate;
    }
    public void setCenterHeaderTemplate(String centerHeaderTemplate) {
        this.centerHeaderTemplate = centerHeaderTemplate;
    }
    public String getRightHeaderTemplate() {
        return rightHeaderTemplate;
    }
    public void setRightHeaderTemplate(String rightHeaderTemplate) {
        this.rightHeaderTemplate = rightHeaderTemplate;
    }
    public String getView() {
        return view;
    }
    public void setView(String view) {
        this.view = view;
    }
    public String getNextDayThreshold() {
        return nextDayThreshold;
    }
    public void setNextDayThreshold(String nextDayThreshold) {
        this.nextDayThreshold = nextDayThreshold;
    }
    public String getWeekNumberCalculation() {
        return weekNumberCalculation;
    }
    public void setWeekNumberCalculation(String weekNumberCalculation) {
        this.weekNumberCalculation = weekNumberCalculation;
    }
    public String getWeekNumberCalculator() {
        return weekNumberCalculator;
    }
    public void setWeekNumberCalculator(String weekNumberCalculator) {
        this.weekNumberCalculator = weekNumberCalculator;
    }
    public String getTimeFormat() {
        return timeFormat;
    }
    public void setTimeFormat(String timeFormat) {
        this.timeFormat = timeFormat;
    }
    public String getSlotDuration() {
        return slotDuration;
    }
    public void setSlotDuration(String slotDuration) {
        this.slotDuration = slotDuration;
    }
    public String getSlotLabelInterval() {
        return slotLabelInterval;
    }
    public void setSlotLabelInterval(String slotLabelInterval) {
        this.slotLabelInterval = slotLabelInterval;
    }
    public String getSlotLabelFormat() {
        return slotLabelFormat;
    }
    public void setSlotLabelFormat(String slotLabelFormat) {
        this.slotLabelFormat = slotLabelFormat;
    }
    public String getDisplayEventEnd() {
        return displayEventEnd;
    }
    public void setDisplayEventEnd(String displayEventEnd) {
        this.displayEventEnd = displayEventEnd;
    }
    public String getScrollTime() {
        return scrollTime;
    }
    public void setScrollTime(String scrollTime) {
        this.scrollTime = scrollTime;
    }
    public String getMinTime() {
        return minTime;
    }
    public void setMinTime(String minTime) {
        this.minTime = minTime;
    }
    public String getMaxTime() {
        return maxTime;
    }
    public void setMaxTime(String maxTime) {
        this.maxTime = maxTime;
    }
    public String getLocale() {
        return locale;
    }
    public void setLocale(String locale) {
        this.locale = locale;
    }
    public String getTimeZone() {
        return timeZone;
    }
    public void setTimeZone(String timeZone) {
        this.timeZone = timeZone;
    }
    public String getClientTimeZone() {
        return clientTimeZone;
    }
    public void setClientTimeZone(String clientTimeZone) {
        this.clientTimeZone = clientTimeZone;
    }
    public String getColumnHeaderFormat() {
        return columnHeaderFormat;
    }
    public void setColumnHeaderFormat(String columnHeaderFormat) {
        this.columnHeaderFormat = columnHeaderFormat;
    }
    public ExtenderExample getExtenderExample() {
        return extenderExamples.get(selectedExtenderExample);
    }
    public String getSelectedExtenderExample() {
        return selectedExtenderExample;
    }
    public void setSelectedExtenderExample(String selectedExtenderExample) {
        this.selectedExtenderExample = selectedExtenderExample;
    }
    public String getExtenderCode() {
        return extenderCode;
    }
    public void setExtenderCode(String extenderCode) {
        this.extenderCode = extenderCode;
    }
    public String getHeight() {
        return height;
    }
    public void setHeight(String height) {
        this.height = height;
    }
    public List<SelectItem> getExtenderExamples() {
        return extenderExamples.values().stream() //
                .sorted(Comparator.comparing(ExtenderExample::getName)) //
                .map(example -> new SelectItem(example.getKey(), example.getName())) //
                .collect(Collectors.toList());
    }
    public String getServerTimeZone() {
        return serverTimeZone;
    }
    public void setServerTimeZone(String serverTimeZone) {
        this.serverTimeZone = serverTimeZone;
    }
}
package org.primefaces.showcase.service;
import org.primefaces.component.schedule.Schedule;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Named;
/**
 * Provides the examples for the [@code extender} options of various components,
 * such as the {@link Schedule}.
 */
@Named
@ApplicationScoped
public class ExtenderService {
    public Map<String, ExtenderExample> createExtenderExamples() {
        Properties properties = new Properties();
        try (InputStream inStream = ExtenderService.class.getResourceAsStream("/schedule-extender-examples.properties")) {
            properties.load(inStream);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        Map<String, ExtenderExample> extenderExamples = new HashMap<>();
        for (String key : properties.stringPropertyNames()) {
            if (key != null && key.endsWith(".name")) {
                String baseKey = key.substring(0, key.length() - 5);
                ExtenderExample example = new ExtenderExample(baseKey, properties);
                if (example.getName() != null && example.getValue() != null && !example.getName().trim().isEmpty()
                        && !example.getValue().trim().isEmpty()) {
                    extenderExamples.put(baseKey, example);
                }
            }
        }
        return extenderExamples;
    }
    public static class ExtenderExample {
        private String details;
        private String html;
        private String key;
        private String link;
        private String name;
        private String value;
        public ExtenderExample(String key, Properties properties) {
            this.key = key;
            this.details = properties.getProperty(key + ".details");
            this.html = properties.getProperty(key + ".html");
            this.link = properties.getProperty(key + ".link");
            this.name = properties.getProperty(key + ".name");
            this.value = properties.getProperty(key + ".value");
        }
        public String getDetails() {
            return details;
        }
        public String getHtml() {
            return html;
        }
        public String getKey() {
            return key;
        }
        public String getLink() {
            return link;
        }
        public String getName() {
            return name;
        }
        public String getValue() {
            return value;
        }
    }
}