Как написать Google Calendar на коленке? Обзор FullCallendar

от автора

Привет, Хабр! В свободное от работы время я занимаюсь разработкой своего проекта. На днях мне понадобилось разработать раздел с календарем и задачами, чтобы пользователи могли отслеживать свою деятельность. Увы, но полностью готовых решений я не нашел. API стандартного календаря Google не подходит, так как данные хочу хранить внутри контура проект.

Спустя несколько часов поисков я наткнулся на плейлист разработчиков из Индии. В жизни все циклично — именно эти видео мне и помогли. Так я познакомился с классной open source-библиотекой FullCalendar, о которой расскажу в этой статье. Если вы уже начали составлять календари на 2025 год, добро пожаловать под кат!

Что за библиотека

FullCalendar — это open source-библиотека, которая позволяет создавать кастомные календари без лишних ручек по части бэкенда. Почти полностью фронтенд-решение, которое можно интегрировать без особых навыков. Есть версии как на чистом JavaScript, так и на React, Vue и старом добром Angular.

Главная страница FullCalendar.


Чем понравился FullCalendar


Есть два ограничения, которые я должен был учитывать при поиске решения. Первое: я не самый лучший фронтендер. Второе: мой проект построен на Django. Я убежден, что поиск решения — это всегда перебор разных вариантов, поэтому разводить лишний dependency hell из всяких React и Vue не хотелось.

Это open source

При интеграции вы получаете не какой-то iframe, а полноценный DOM-объект. Это значит, вы можете изменять CSS-стили для элементов календаря и обрабатывать события, связанные с ними. Это полезно, если нужно, например, показывать поп-апы по клику на конкретное задание.

Как я стилизовал стандартный шаблон под мобильный календарь.

Есть версия на нативном JavaScript

FullCalendar подкупил именно наличием версии из нативного JavaScript. Если не понравится, можно просто удалить link-импорты — и проект снова чистый. Но как это иногда бывает, любовь с первого взгляда превратилась в нечто большее.

Есть версия для бэкендеров с лапками

Нативный JavaScript — это хорошо. Но с учетом того, что и его я знаю так себе, не помешал бы Jquery. Разработчики FullCalendar предусмотрели и это. На каждый тип календаря, как я понял, есть две версии: расширенная (нативный JavaScript) и упрощенная (Jquery). К слову, версия с Jquery неплохая, для моего MVP подходит более чем.

Есть документация с песочницей

Очень порадовало, что на главном сайте библиотеки есть раздел Demos, в котором расположены самые популярные форматы календарей. Можно выбрать понравившийся, открыть его в Codepen и переиспользовать.

Раздел Demos с разными типами календарей.

Все — в JSON

Вам не нужно описывать логику генерации и отображения событий внутри календаря, все это сделано уже разработчиками. Просто даете ссылку на JSON-файл со списком событий, а дальше библиотека сама их разместит в колонках календаря. Требуемая структура описана в документации.

$(function() {     $('#calendar').fullCalendar({         defaultView: 'agendaWeek',         header: {             left: 'prev,next today',             center: 'title',             right: 'agendaWeek,agendaDay'         },          // вот эта ссылка может быть размещена локально или в вашем S3         events: 'https://fullcalendar.io/api/demo-feeds/events.json'     }) }); 

Как сделать свой календарь


Рассмотрим реализацию календаря, на примере самого классического шаблона, в котором уже реализована механика, приближенная к Google Calendar.

Шаг 1. Прикрутите фронтенд

Неважно, на чем у вас построено веб-приложение. Откройте шаблон вашего календаря и добавьте импорты, как в примере ниже:

<!-- calendar.html --> <!DOCTYPE html> <html lang="en" > <head>     <meta charset="UTF-8">     <title>Мой календарь</title>     <link rel='stylesheet' href="./style.css">     <!-- стили календаря рекомендую хранить локально -->             <link rel='stylesheet' href="./fullcalendar.min.css"> </head> <body>     <!-- тут что-то ваше –->      <!-- вот этот блок понадобится, это контейнер для календаря –->     <div id='calendar'></div>      <!-- тут что-то ваше –->      <script src='https://cdn.jsdelivr.net/npm/moment@2/min/moment.min.js'></script>     <script src='https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js'></script>     <script src='https://cdn.jsdelivr.net/npm/fullcalendar@3.10.5/dist/fullcalendar.min.js'></script>      <!-- подгружаем главный скрипт, который все объединит –->     <script src="./calendar_loader.js"></script> </body> </html> 

Далее создайте файл со стилями календаря и копируйте в него стандартный набор. Вместе с этим — заведите файл со скриптом загрузки календаря, он тоже должен быть размещен локально.

// calendar_loader.js $(function() {  $('#calendar').fullCalendar({    defaultView: 'agendaWeek',    header: {      left: 'prev,next today',      center: 'title',      right: 'agendaWeek,agendaDay'    },    // https://fullcalendar.io/docs/events-json-feed    events: 'https://fullcalendar.io/api/demo-feeds/events.json'  }) }); 

Отлично! На этом этапе календарь уже отображается, пусть и с тестовым набором данных. Кстати, если у вас в календаре будут другие события, не удивляйтесь: разработчики периодически изменяют содержание тестового файла JSON.

Фронтенд FullCalendar с тестовыми данными.

Шаг 2. Настраиваем отображение

В шаблоне выше я рекомендую хранить fullcalendar.min.css локально. Во-первых, он так будет быстрее загружаться. Во-вторых, мы можем его видоизменять. В этом ничего сложного нет: просто откройте инспектор кода и посмотрите, к каким классам принадлежат элементы вашего календаря. Нужно поменять цвет события? Посмотрите класс в инспекторе — и замените background-color в fullcalendar.min.css. Никакого rocket science.

Шаг 3. Продумайте логику

Если у вас, как и у меня, многопользовательское приложение, продумайте логику: какие есть события, через какую форму пользователи будут их добавлять и так далее. Самый простой из вариантов логики может выглядеть так:

Схема хранения событий и подтягивания данных.

На самом деле, данные можно хранить любым способом. S3-хранилище мне нравится, потому что JSON-структуры сохраняются как объекты. Для меня это довольно удобно, т. к. в перспективе позволяет забыть о базах данных на десятки тысяч строк: пользователей и событий с каждым месяцем может быть все больше.

К тому же объектное хранилище выходит очень дешево, особенно если использовать холодный класс хранения. И последнее преимущество — возможность записи метаданных в объекты. В целом, это киллер-фича, которая позволяет записывать дополнительные сведения о событиях, не ограничиваясь стандартной структурой JSON для работы FullCalendar.

Выбор хранилища зависит от вас и вводных. Например, если есть события, которые встречаются у разных пользователей (это могут быть общие встречи), то такие данные лучше грузить в базу данных. Так получится избежать дублей.

Шаг 4. Реализуйте логику

Рассмотрим на примере предложенной логики.

1. Создайте контейнер объектного хранилища S3. Это можно сделать, например, в облаке Selectel. Для этого перейдите в панель управления my.selectel.ru и откройте раздел Объектное хранилище. Нажмите Создать контейнер, введите его название, выберите тип и класс хранения. Для простоты эксперимента создадим публичный контейнер — не делайте так, если заботитесь о конфиденциальности событий.

Страница создания контейнера в панели управления.

2. Создайте сервисного пользователя в разделе Управление доступом → Сервисные пользователи, нажав на кнопку Добавить пользователя. После сгенерируйте для него S3-ключи — они понадобятся для подключения к объектному хранилищу из бэкенда вашего приложения. Так что сохраните их после получения.

Генерация S3-ключей для сервисного пользователя.

3. Подключитесь к контейнеру из своего приложения и создайте объект с JSON-структурой событий пользователя. Как это сделать с помощью S3-API, рассказали в документации.

# calendar_generator.py # импорт boto3 import boto3  # ключи генерируем в разделе сервисных пользователей # (замените на свои, эти — для примера) access_key = "914866b003e94c569f7b5beb19cb0adf" secret_key = "70232d4de75c4877916d0998db78dbd0" # указываем типовой ендпоинт, название региона, access_key и aws_secret_access_key s3 = boto3.client("s3", endpoint_url="https://s3.ru-1.storage.selcloud.ru",                  region_name="ru-1", aws_access_key_id=access_key, aws_secret_access_key=secret_key,                  verify=False) # пользовательские данные (для примера) users_calendars = {    'user_1': {        '09.12.24-13.12.24' : '[{"title":"Lunch","start":"2024-12-09T12:00:00+00:00"}]'    } }  # название контейнера до первой точки # (можно посмотреть в панели управления) bucket_name = 'fullcalendar'  # список пользователей и дат users_list = list(users_calendars.keys()) dates_list = list(users_calendars["user_1"].keys())  # название объекта, в который мы запишем данные (наш json-файл) # учтите: если хотите расположить внутри директории, # пропишите ее на уровне названия объекта — она появится key = f'{users_list[0]}/{dates_list[0]}.json'  #  данные, которые запишем внутрь объекта (тело объекта) body = users_calendars[users_list[0]][dates_list[0]]  # загрузка объекта из строки s3.put_object(Bucket=bucket_name, Key=key, Body=body) 

Отлично! Мы подгрузили «откуда-то» данные о событиях пользователя user-1 на рабочей неделе с 9 по 13 декабря. Теперь при переходе в S3-хранилище вы увидите директорию и подгруженный объект:

4. Внутри fullcalendar.min.js замените адрес events на уникальную ссылку с событиями пользователя:

// calendar_loader.js $(function() {     ...     // теперь здесь подгружаем ссылку на объект в s3     // ссылку можно скопировать из панели управления     events: 'https://f7f2013c-d34e-4560-99c6-5a60b6d5b291.selstorage.ru/user_1/09.12.24-13.12.24.json' }) }); 

Готово — все события отображаются в календаре.

При необходимости вы можете его стилизовать и, например, синхронизировать с другими календарями с помощью протокола CalDAV. Но это уже другая история.


ссылка на оригинал статьи https://habr.com/ru/articles/865304/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *