1. Привязка календаря к данным
В этом разделе вы создадите:
- 
Сущность JPA Meeting.
- 
Компонент FullCalendarс поставщиком данных.
Событие встречи
Сущность Meeting будет представлять событие в компоненте FullCalendar. Для корректного отображения требуются определенные свойства:
- 
name- название события.
- 
startDate- дата и время начала события. Компонент не будет отображать события безstartDate.
- 
endDate- дата окончания события. ЕслиendDateне указано, компонент применит продолжительность по умолчанию на основе свойстваallDay. В этом руководстве свойствоallDayне используется; следовательно, события рассматриваются как события, не охватывающие весь день.
Создание сущности Meeting
Подробные инструкции по созданию сущностей JPA смотрите в разделе Простой CRUD самоучителя.
Сущность Meeting имеет следующие атрибуты:
- 
nameтипаStringtype. Установите флажок Mandatory.
- 
startDateтипаLocalDateTime. Установите флажок Mandatory.
- 
endDateтипаLocalDateTime.
- 
user- тип атрибутаAssociation, сущностьUserв качестве типа Java и кардинальность many-to-one (многие-к-одному). Установите флажок Mandatory.
Сущность Meeting не требует стандартных экранов списка и деталей, поскольку она будет генерироваться программно и отображаться в компоненте FullCalendar.
Генерация Meeting
После того как пользователь завершит все этапы онбординга, приложение сгенерирует событие встречи для обсуждения выполненных задач. Это событие будет отображаться в экране Мой календарь пользователя.
Наилучший момент для проверки того, завершил ли пользователь все этапы, - это событие PreSaveEvent контекста данных (DataContext). Слушатель этого события проверяет наличие незавершенных этапов адаптации. Если незавершенных этапов нет, создается новое событие встречи.
@Subscribe(target = Target.DATA_CONTEXT)
public void onPreSave(final DataContext.PreSaveEvent event) {
    List<UserStep> userSteps = userStepsDc.getItems().stream()
            .filter(us -> us.getCompletedDate() == null)
            .toList();
    if (userSteps.isEmpty()) {
        generateOnboardingResultsMeeting();
    }
}Далее мы реализуем метод generateOnboardingResultsMeeting(). Этот метод создает экземпляр события Meeting, запланированного на следующий рабочий день.
protected void generateOnboardingResultsMeeting() {
    Meeting meeting = dataContext.create(Meeting.class); (1)
    meeting.setName("Results meeting");
    meeting.setUser((User) currentAuthentication.getUser());
    int inDays = LocalDate.now().getDayOfWeek() == DayOfWeek.FRIDAY ? 3 : 1; (2)
    LocalDateTime start = LocalDateTime.of( (3)
            LocalDate.now().plusDays(inDays),
            LocalTime.of(9, 30));
    meeting.setStartDate(start);
    meeting.setEndDate(start.plusMinutes(30));
}| 1 | Создает и добавляет новый экземпляр MeetingвDataContext, автоматически сохраняя сущность. | 
| 2 | Вычисляет следующий рабочий день; если сегодня пятница, встреча планируется на понедельник. | 
| 3 | Устанавливает дату и время начала встречи на 9:30 утра следующего рабочего дня. | 
Добавление компонентов в MyCalendar
Сначала создайте новый пустой экран с именем MyCalendar.
 
Studio сгенерирует и отобразит пустой экран в дизайнере.
Загрузка данных
Перед добавлением компонента FullCalendar создайте контейнер коллекций для сущности Meeting.
В панели действий нажмите кнопку Add Component, перейдите в раздел Data components и дважды щелкните Collection container. В появившемся диалоговом окне выберите Meeting в поле Entity и нажмите OK.
<collection id="meetingsDc" class="com.company.onboarding.entity.Meeting">
    <loader id="meetingsDl" readOnly="true">
        <query>
            <![CDATA[select e from Meeting e]]>
        </query>
    </loader>
    <fetchPlan extends="_base"/>
</collection>Текущая конфигурация загрузчика данных извлекает все события от всех пользователей. Однако пользователи должны видеть только свои собственные события. Для реализации этой фильтрации необходимо изменить запрос JPQL - либо с помощью редактора, либо вручную - добавив условие, которое ограничивает результаты текущим пользователем, который вошел в систему:
<query>
    <![CDATA[select e from Meeting e where e.user = :user]]>
</query>Мы получаем данные о текущем авторизованном пользователе из бина CurrentAuthentication и передаем эту информацию загрузчику данных в обработчике события BeforeShowEvent:
- 
Подпишитесь на событие BeforeShowEvent.
- 
Инжектируйте загрузчик meetingsDl.
- 
Инжектируйте бин CurrentAuthentication.
Теперь мы можем установить параметр для загрузчика, чтобы он извлекал события для текущего пользователя:
@ViewComponent
private CollectionLoader<Meeting> meetingsDl;
@Autowired
private CurrentAuthentication currentAuthentication;
@Subscribe
public void onBeforeShow(final BeforeShowEvent event) {
    final User user = (User) currentAuthentication.getUser();
    meetingsDl.setParameter("user", user);
    meetingsDl.load();
}Добавление компонента FullCalendar
В панели действий нажмите Add Component, найдите элемент FullCalendar и дважды щелкните по нему.
После этого будет создан новый элемент calendar. Настройте атрибуты id, height и width, как показано ниже.
<calendar:calendar id="calendar"
                   height="100%"
                   width="100%"/>Добавление поставщика данных
Поставщик данных является источником событий, отображаемых в FullCalendar. Существуют различные типы поставщиков данных, но мы будем использовать поставщик данных на основе контейнера данных.
Чтобы добавить ContainerDataProvider в компонент FullCalendar:
- 
Выберите компонент calendarв панели структуры Jmix UI или в XML-дескрипторе экрана.
- 
Нажмите кнопку Add на панели инспектора. 
- 
В выпадающем меню выберите Data Providers → ContainerDataProvider. 
 
В появившемся диалоговом окне выберите meetingsDc.
Элемент containerDataProvider позволяет настроить сопоставление свойств сущности. Сущность Meeting содержит следующие свойства:
- 
name
- 
startDate
- 
endDate
Поэтому следует указать следующие атрибуты:
<calendar:dataProviders>
    <calendar:containerDataProvider dataContainer="meetingsDc"
                                    title="name"
                                    startDateTime="startDate"
                                    endDateTime="endDate"/>
</calendar:dataProviders>Давайте посмотрим: полное описание нашего календаря выглядит следующим образом:
<calendar:calendar id="calendar"
                   height="100%"
                   width="100%">
    <calendar:dataProviders>
        <calendar:containerDataProvider dataContainer="meetingsDc"
                                        title="name"
                                        startDateTime="startDate"
                                        endDateTime="endDate"/>
    </calendar:dataProviders>
</calendar:calendar>Запустите приложение и войдите в систему как пользователь admin. У пользователя admin нет шагов онбординга, поэтому необходимо их сгенерировать:
- 
Перейдите в экран User.list, выбрав Users в меню приложения.
- 
Выберите пользователя adminв DataGrid и нажмите кнопку Edit. Затем заполните поле Joining Date, сгенерируйте шаги онбординга и сохраните изменения.
- 
Перейдите в экран MyCalendar, выбрав My Calendar в меню приложения.
