Внутреннее Next.js-приложение для оперативной работы с производственным регистром в Google Sheets. После авторизации через Auth0 пользователи получают единый рабочий стол: интерактивную таблицу на Handsontable и производственный календарь, синхронизированные с одной и той же гугл-таблицей. Сервис поддерживает редактирование записей, отметку статусов, перемещение заказов между датами и полу-реальное время (Drive push + SSE, fallback polling).
- Авторизация через Auth0 с Refresh Token Rotation:
- Автоматическое обновление токенов (до 30 дней без повторного логина)
- Токены сохраняются в localStorage для работы между сессиями
- Idle timeout: 7 дней неактивности
- Отображение Google Sheet в Handsontable с возможностью редактирования, добавления и удаления строк.
- Очередь изменений с дебаунсом и безопасной записью в лист через API /api/sheet.
- Производственный календарь, группирующий заказы по плановым датам; drag&drop обновляет дату прямо в таблице.
- Отметка статуса заказа чекбоксом внутри производственного календаря (обновляет соответствующий столбец в sheet).
- Попытка подключения к push-уведомлениям Google Drive и трансляция изменений в браузер через Server-Sent Events; автоматический откат на опрос каждые 5 секунд при отсутствии вебхуков.
- REST API-обёртка над Google Sheets для front-end и внешних интеграций.
- Контекстное меню карточек (v2):
- Desktop: ПКМ для вызова меню, hover для раскрытия подменю, клик для фиксации подменю
- Mobile: двойной тап для вызова меню, тап для раскрытия подменю с адаптивным позиционированием
- 11 свойств заказа с динамической загрузкой значений из Google Sheets data validation
- Оптимистичные обновления с автоматическим откатом при ошибках
- Визуальная индикация текущих значений
- Визуальные индикаторы статусов производства на карточках Kanban:
- 5 статусов технологического процесса: З Р Ш П У (Закуп пленки, Распил, Шлифовка, Пленка, Упаковка)
- Цветовая индикация: оранжевый (#ff6f00) = "Готов", невидимый (цвет фона) = "-" (не готов)
- Специальный режим: при всех статусах "Готов" отображается светло-оранжевая полоска внизу карточки
- Tooltip при hover (desktop) с полным названием статуса и текущим значением
- Компактное размещение в нижней части карточки с адаптивными размерами для mobile/desktop
- Next.js 14 + React 18.
- Auth0 SPA SDK (@auth0/auth0-react).
- Handsontable (@handsontable/react) для табличного UI.
- Google Sheets API (google-spreadsheet) и Google Drive API (googleapis) для чтения/записи и push-уведомлений.
- JWT верификация (jsonwebtoken, jwks-rsa).
- Server-Sent Events для доставки уведомлений в браузер.
- components/Layout.js — оболочка рабочего стола: управляет навигацией между таблицей, производственным календарем и настройками, хранит общий список заказов, вычисляет набор дат для доски, предоставляет callback-функции для обновления статусов через DataTable ref.
- components/DataTable.js — Handsontable-обёртка. Подтягивает данные из /api/sheet, ведёт очередь отложенных изменений, создаёт/удаляет строки, выставляет высоту таблицы, управляет push/SSE и fallback polling. Экспортирует метод
updateOrderFields
черезuseImperativeHandle
для обновления статусов из Kanban. - components/KanbanBoard.js — визуализация заказов по дням. Показывает статусы, материалы, подсчитывает суммарную площадь, поддерживает drag&drop и оптимистичные обновления, интегрирует контекстное меню для быстрого изменения статусов, отображает визуальные индикаторы статусов производства.
- components/OrderContextMenu.js — компонент контекстного меню для карточек заказов. Поддерживает desktop (ПКМ/hover/клик) и mobile (двойной тап/тап) взаимодействие, динамически загружает доступные значения из Google Sheets validation rules, реализует адаптивное позиционирование подменю.
- lib/google-sheets.js — доступ к Google Sheet через сервисный аккаунт: чтение всех строк, добавление, обновление (по rowNumber) и удаление. Функция
getColumnValidationRules()
извлекает правила валидации для динамической загрузки списков статусов. - lib/auth.js — проверка JWT от Auth0, в том числе через JWKS, и утилита requireAuth для API-роутов.
- pages/api/sheet.js — REST CRUD для таблицы (GET/POST/PUT/DELETE). Все запросы защищены requireAuth.
- pages/api/sheet/statuses.js — API эндпоинт для получения списка доступных статусов из Google Sheets data validation с fallback на жестко закодированные значения.
- pages/api/setup-push.js — настройка drive.files.watch, проверяет окружение, создаёт канал на Google Drive и возвращает информацию о вебхуке.
- pages/api/webhook/drive-changes.js — точка приёма push-уведомлений от Google и SSE-эндпоинт для клиентов. Рассылает события всем подключённым браузерам.
- pages/index.js, _app.js, _document.js — корневые страницы и провайдер Auth0.
- styles/globals.css — единый стиль интерфейса (светлая/тёмная тема, состояния drag&drop и т.д.).
Переменная | Описание | Обязательность |
---|---|---|
AUTH0_BASE_URL | Базовый URL приложения (например, http://localhost:3000 локально или продовый домен). Используется на клиенте и при формировании вебхуков. | Да |
AUTH0_ISSUER_BASE_URL | Домен арендатора Auth0, например https://your-tenant.eu.auth0.com. | Да |
AUTH0_CLIENT_ID | Client ID SPA-приложения в Auth0. | Да |
AUTH0_SECRET | Секрет приложения (используется Next.js на сервере; нужен в production, для разработки можно заменить случайной строкой). | Рекомендуется |
GOOGLE_SHEET_ID | ID таблицы в Google Sheets (часть URL после /d/). | Да |
GOOGLE_CREDENTIALS | JSON сервисного аккаунта (как выдаёт Google; строка одним значением). Предпочтительный способ передачи ключей. | Да, если не используете пары ниже |
GOOGLE_SERVICE_ACCOUNT_EMAIL | Email сервисного аккаунта. Задавайте вместе с GOOGLE_PRIVATE_KEY, когда GOOGLE_CREDENTIALS недоступна. | Условно |
GOOGLE_PRIVATE_KEY | Приватный ключ сервисного аккаунта. Храните в кавычках, переводите строки через \n. | Условно |
Совет: убедитесь, что сервисный аккаунт имеет доступ на чтение/запись к целевой таблице (поделитесь таблицей на email аккаунта). Если используете push-уведомления, учётке также нужна роль Drive API Service Agent или аналогичный доступ к Drive.
- Создайте SPA-приложение в Auth0.
- В разделе Application URIs добавьте AUTH0_BASE_URL в поля Allowed Callback URLs, Allowed Logout URLs и Allowed Web Origins.
- Разрешите audience https://{tenant}.auth0.com/api/v2/ для выдачи access token (Management API). Добавьте scope
openid profile email offline_access
. - Включите встроенный Login, убедитесь что silent login работает (для getAccessTokenSilently).
- Настройте Refresh Token Rotation (Settings → Advanced Settings → Grant Types):
- ✅ Включите "Refresh Token Rotation"
- ✅ Включите "Set Idle Refresh Token Lifetime"
- ID Token Expiration: 86400 секунд (24 часа)
- Idle Refresh Token Lifetime: 604800 секунд (7 дней)
- Maximum Refresh Token Lifetime: 2592000 секунд (30 дней)
- Rotation Overlap Period: 0 секунд
- Создайте сервисный аккаунт в Google Cloud Console, включите Google Sheets API и Google Drive API.
- Скачайте JSON ключ. Либо положите содержимое в GOOGLE_CREDENTIALS, либо вынесите client_email и private_key в отдельные переменные.
- Поделитесь Google Sheet с сервисным аккаунтом (роль «Редактор»).
- Для push-уведомлений:
- Подтвердите домен, на который будет указывать вебхук (AUTH0_BASE_URL/api/webhook/drive-changes) в Google Search Console.
- Убедитесь, что приложение доступно по HTTPS из интернета.
- После запуска авторизуйтесь и выполните POST /api/setup-push (DataTable делает это автоматически; статус смотрите в консоли браузера).
Без публичного домена приложение продолжит работать на fallback polling каждые 5 секунд.
- Установите зависимости: pm install.
- Создайте .env.local с переменными из таблицы.
- Запустите dev-сервер: pm run dev (по умолчанию http://localhost:3000).
- Авторизуйтесь через Auth0. После входа:
- Вкладка «Таблица» загрузит данные из Google Sheets. Индикатор статус-бара покажет состояние соединения (polling / push / ошибка).
- Вкладка «Производственный календарь» отобразит заказы по дням. Перетаскивание заказа меняет дату в таблице.
- Для теста realtime убедитесь, что Google может достучаться до вебхука или используйте инструмент вроде ngrok, обновите AUTH0_BASE_URL и перезапустите сервер.
Метод и путь | Описание |
---|---|
GET /api/sheet | Возвращает все строки листа. Требует Authorization: Bearer от Auth0. |
POST /api/sheet | Добавляет строку (ожидает JSON с полями, совпадающими с заголовками таблицы). |
PUT /api/sheet?rowId={n} | Обновляет строку по номеру. Тело — объект со значениями столбцов. |
DELETE /api/sheet?rowId={n} | Удаляет строку. |
GET /api/sheet/statuses | Возвращает доступные статусы для всех свойств заказа из Google Sheets data validation (с fallback на hardcoded значения). |
POST /api/setup-push | Регистрирует канал drive.files.watch для текущего листа. Требует доступного из интернета URL. |
GET /api/webhook/drive-changes?token=... | SSE-канал для браузера. Токен — тот же access token Auth0. |
POST /api/webhook/drive-changes | Точка приёма вебхуков Google Drive. Google вызывает её, когда лист меняется. |
При разработке следите за логами console (в коде оставлено много диагностических сообщений, помогающих отладить интеграцию).
components/
DataTable.js # Handsontable + realtime логика
KanbanBoard.js # Календарное представление заказов
OrderContextMenu.js # Контекстное меню для редактирования статусов
Layout.js # Общий фрейм приложения
lib/
auth.js # Верификация JWT и guard для API
google-sheets.js # Утилиты работы с Google Sheets + парсинг validation rules
pages/
api/
sheet/
statuses.js # API для получения доступных статусов
sheet.js # CRUD над таблицей
setup-push.js # Регистрация push-канала
webhook/
drive-changes.js # Вебхук и SSE
_app.js # Auth0Provider с refresh tokens
_document.js
index.js
styles/
globals.css
Google Sheets Api Quota.md
Setting push Google Drive…md
- Названия столбцов в таблице должны точно совпадать с теми, что используются в коде (см. DataTable и KanbanBoard). Если вы переименуете колонку в Google Sheet, обновите маппинг в
PROPERTY_COLUMN_MAP
(OrderContextMenu.js). - Производственный календарь рассчитывает набор дат динамически: 5 дней назад от текущего дня + все рабочие дни до максимальной плановой даты в таблице (воскресенье пропускается).
- Очередь изменений в DataTable работает с задержкой 500 мс; не закрывайте вкладку, пока индикатор «Сохранение…» активен.
- При локальной разработке push-канал, как правило, не поднимется — ориентируйтесь на polling и логи.
- Если заметите неожиданные изменения в файлах, остановитесь и уточните у команды (репозиторий может использоваться как рабочий).
- Первый логин после обновления: пользователям нужно залогиниться заново для получения refresh token.
- Срок действия токенов: Access token обновляется автоматически каждые 24 часа через refresh token.
- Автоматический выход: через 7 дней неактивности или 30 дней с последнего логина (в зависимости от того, что наступит раньше).
- Токены в localStorage: при работе на общем компьютере рекомендуется выходить из системы через кнопку Logout.
- Desktop: ПКМ на карточке → выбор свойства (hover для раскрытия подменю) → клик на значение
- Mobile: двойной тап на карточке → тап на свойство → тап на значение
- Изменение статусов выполняется с оптимистичным обновлением (карточка обновляется сразу); при ошибке значения автоматически откатываются.
- Доступные значения для выбора подтягиваются из Google Sheets data validation rules через
/api/sheet/statuses
. - Если data validation не настроена в Google Sheets, используются hardcoded fallback значения.
- З Р Ш П У = Закуп пленки, Распил, Шлифовка, Пленка, Упаковка
- Оранжевая буква = статус "Готов"
- Невидимая буква = статус "-" (не готов)
- Светло-оранжевая полоска внизу карточки = все 5 статусов в "Готов" (заказ полностью готов)
- Tooltip при hover (desktop) показывает полное название статуса и текущее значение
- DataTable импортируется статически (не через
dynamic import
), чтобыref
был доступен из Layout для обновления статусов через контекстное меню. - При добавлении новых свойств в контекстное меню:
- Обновите
ORDERED_PROPERTIES
в OrderContextMenu.js - Добавьте маппинг в
PROPERTY_COLUMN_MAP
(если название в UI ≠ название в Google Sheets) - Настройте data validation в Google Sheets для автоматической загрузки списков значений
- Обновите
npm run dev
— запуск Next.js в режиме разработки.npm run build
— сборка production.npm start
— запуск production-сборки.npm run lint
— проверка линтером.
branch main - production branch stage - stage