Аналитика OpenPanel в PlanForge
Актуально на: 2026-03-19
Обзор
PlanForge использует self-hosted OpenPanel через @openpanel/web без @openpanel/nextjs.
- Хост OpenPanel:
https://openpanel.basegrid.tech - Хост приложения:
https://planforge.basegrid.tech - Инициализация SDK:
app/analytics-provider.tsx - Поток идентификации:
app/analytics-identify.tsx - Отслеживание маршрутов:
app/analytics-navigation.tsx - Общие хелперы отслеживания:
lib/analytics-events.ts
Интеграция намеренно ручная для отслеживания screen_view, чтобы первый просмотр страницы всегда был связан с аутентифицированным профилем.
Идентификация и просмотры экранов
Почему используются ручные screen view
@openpanel/web с trackScreenViews: true отправляет первый screen_view сразу после создания SDK. В App Router это происходило до вызова identify() из дочерних аутентифицированных layout’ов, поэтому первое событие часто было анонимным.
Чтобы избежать этого race condition:
trackScreenViewsотключён вapp/analytics-provider.tsx- SDK всё равно инициализируется рано в корневом layout
identify()вызывается в аутентифицированных layout’ах черезapp/analytics-identify.tsx- после
identify()сразу вызываетсяscreenView() - последующие переходы по маршрутам отслеживаются в
app/analytics-navigation.tsx
Текущий жизненный цикл
- Корневой layout монтирует
AnalyticsProvider. - SDK OpenPanel создаётся один раз.
- Аутентифицированный дочерний layout рендерит
AnalyticsIdentify. - Вызывается
op.identify(...)с параметрами:profileId = userIdfirstName = nameemail = emailproperties.role = role
- Идентификация помечается как готовая в локальном runtime аналитики.
- Первый
screen_viewотправляется вручную. - Все последующие переходы по маршрутам отправляют
screen_viewиз навигационного трекера. - При выходе из системы вызывается
op.clear()и состояние готовности идентификации сбрасывается.
Задействованные файлы
app/layout.tsxapp/analytics-provider.tsxapp/analytics-identify.tsxapp/analytics-navigation.tsxcomponents/user-menu.tsx
Хелперы отслеживания
Всё клиентское отслеживание проходит через lib/analytics-events.ts.
Хелперы:
trackEvent(name, properties)— отправляет событие немедленно, если SDK существуетtrackWhenIdentified(name, properties)— отправляет только после готовности идентификацииclearAnalyticsIdentity()— очищает идентификацию пользователя/сессии OpenPanel и локальное состояние готовности
Что отслеживается
Автоматические функции SDK, которые остаются включёнными
Эти функции предоставляются самим SDK OpenPanel:
- отслеживание исходящих внешних ссылок через
trackOutgoingLinks: true - события на основе атрибутов через
trackAttributes: true
Примечание: события на основе атрибутов срабатывают только для DOM-элементов с data-track="...". PlanForge в основном использует явный trackEvent(...), а не data-track.
Контекст экранов и сессий
identify- первый аутентифицированный
screen_view - последующие
screen_viewпри переходах по маршрутам
События проектов
Отслеживаются в app/(app)/projects/projects-client.tsx.
project_createdproject_openedproject_renamedproject_archivedproject_unarchivedproject_deletedtemplate_usedlisting_publish_opened
Типичные свойства:
projectIdprojectNameprojectStatustemplateIdtemplateType
События листингов и маркетплейса
Отслеживаются в:
app/(app)/projects/[projectId]/publish/page.tsxapp/(app)/marketplace/marketplace-client.tsxapp/(app)/marketplace/[id]/page.tsxapp/(app)/my-listings/my-listings-client.tsxapp/(app)/my-purchases/my-purchases-client.tsx
События:
listing_publishedlisting_openedlisting_archivedlisting_resubmittedlisting_review_history_openedpurchase_openedcheckout_startedcheckout_completedpurchase_project_openedfilter_applied
Типичные свойства:
listingIdprojectIdprojectNamepurchaseTypepricepreviewFieldCountsortBy
События рабочего пространства
Отслеживаются преимущественно в app/workspace-client-legacy.tsx и lib/workspace/use-workspace-persistence.ts.
События:
workspace_openedworkspace_section_viewedworkspace_state_savedworkspace_settings_updatedproject_info_updatedproject_calc_updatedscenario_appliedperiod_value_changedmanual_override_setchart_point_adjustedcompare_mode_enabled
Типичные свойства:
projectIdprojectNameworkspaceViewclassicSectionsectionnormalizetriggercompareCountchartTab
Примечания к workspace_state_saved
Это событие генерируется из хука персистентности рабочего пространства после успешных сохранений.
Текущие триггеры:
manualautosavecashflow-autosavefull-autosaveflushunmount
Для шумных автосохранений в клиенте рабочего пространства есть throttle, чтобы повторные автосохранения не засоряли аналитику.
События сущностей на уровне строк
Отслеживаются путём сравнения текущих и предыдущих коллекций в app/workspace-client-legacy.tsx.
Охваченные коллекции:
- продукты
- строки МИК
- займы
- строки персонала
- строки общих расходов
- фазы календаря
События, генерируемые из diff коллекций:
row_createdrow_updatedrow_deleted
Типичные свойства:
entityTypeentityIdentityNameprojectIdprojectNameworkspaceViewclassicSection
Текущие значения entityType:
productmikloanpersonnelgeneral_expensecalendar_phase
События массового заполнения
Отслеживаются в:
components/workspace/sections/ops-sales-plan-controls-panel.tsxcomponents/workspace/sections/ops-mik-pricing-controls-panel.tsxcomponents/workspace/sections/ops-mik-procurement-controls-panel.tsx
События:
bulk_fill_applied
Текущие режимы:
linearrepeatmonthlyquarterNeed
Текущие секции:
ops-salesops-mik-pricingops-mik-procurement
Уведомления и авторизация
Отслеживаются в:
События:
notification_openednotification_link_openednotification_marked_all_readuser_signed_out
Где добавлять новые события
Используйте следующие правила:
- бизнес-действие, инициированное страницей или модалкой: отслеживать в компоненте страницы/клиента, который владеет действием
- мутации данных рабочего пространства, затрагивающие коллекции: предпочтительно diff-отслеживание в
app/workspace-client-legacy.tsx - повторяющиеся действия сохранения/персистентности: отслеживать в
lib/workspace/use-workspace-persistence.ts - жизненный цикл авторизации или сессии: отслеживать рядом с точкой входа/выхода
Предпочитайте явные имена событий вместо обобщённых событий кликов.
Хорошо:
project_createdworkspace_state_savedcheckout_completed
Избегайте:
button_clickedmodal_openedaction_done
Соглашения по именованию событий
Текущее соглашение — snake_case с продуктовой семантикой:
- объект + действие:
project_created - процесс + состояние:
checkout_started - семантическое действие рабочего пространства:
workspace_section_viewed - нормализованное обобщённое событие для diff сущностей:
row_created
Известные ограничения
- Отслеживается не каждый клик в UI. Фокус на продуктовых действиях и значимых изменениях состояния.
trackAttributesвключён, но кодовая база в настоящее время не зависит от большого количества атрибутовdata-track.- Diff-отслеживание строк считает любое изменение сериализованного объекта обновлением, поэтому даже очень мелкие правки считаются как
row_updated. - В репозитории всё ещё есть не связанное с аналитикой предупреждение Next.js о deprecated именовании
middleware; это не влияет на аналитику.
Чек-лист проверки
При изменении аналитики проверьте:
- Первый аутентифицированный
screen_viewне анонимный - Переходы по маршрутам продолжают отправлять
screen_view - Выход из системы очищает идентификацию
- Ключевые потоки проектов/рабочего пространства генерируют ожидаемые события в OpenPanel
pnpm prisma:generatepnpm build
Быстрый индекс событий
Идентификация / навигация
identifyscreen_viewuser_signed_out
Проекты
project_createdproject_openedproject_renamedproject_archivedproject_unarchivedproject_deletedtemplate_used
Рабочее пространство
workspace_openedworkspace_section_viewedworkspace_state_savedworkspace_settings_updatedproject_info_updatedproject_calc_updatedscenario_appliedperiod_value_changedmanual_override_setchart_point_adjustedcompare_mode_enabledbulk_fill_appliedrow_createdrow_updatedrow_deleted
Маркетплейс / листинги / покупки
listing_publish_openedlisting_publishedlisting_openedlisting_archivedlisting_resubmittedlisting_review_history_openedpurchase_openedcheckout_startedcheckout_completedpurchase_project_openedfilter_applied
Уведомления
notification_openednotification_link_openednotification_marked_all_read