Инструкция: Лид-форма на лендинге

Trigger: лендинг содержит форму для сбора заявок / контактов

Uses: instructions/create_landing_page, instructions/landing_brief

Зачем

Добавить форму на лендинг, написать HTML/JS для кастомного рендера, поставить cron на уведомление о необработанных заявках.

Шаг 1 — Уточнить у пользователя

Перед созданием формы — спросить:

  • Какие поля нужны? (имя, email, телефон, сообщение, Telegram username)
  • Нужен ли редирект после отправки? Куда? (success_url)
  • Кто может отправлять? guest (все) / admin (только авторизованные)
  • Куда уведомлять о заявках? Telegram username?

Шаг 2 — Определить форму в frontmatter заметки

---
title: Название лендинга
layout: html-page  # или кастомный layout
form:
  can_submit: guest
  success_url: /thanks
  fields:
    - name: name
      type: text
      required: true
      max_length: 200
    - name: email
      type: email
      required: true
    - name: phone
      type: text
      required: false
      max_length: 20
    - name: message
      type: text
      required: false
      max_length: 2000
---

trip2g встроит <script id="form-spec" type="application/json"> в страницу автоматически.

Шаг 3 — Написать layout с формой

Основа — готовый пример: docs/_layouts/forms/example.html в репозитории trip2g.

Пример уже содержит:

  • Динамический рендер полей из form-spec JSON (все типы: text, email, int, bool, textarea для длинных полей)
  • Полный GraphQL submit с обработкой всех ответов (SubmitFormPayload, FormSubmitDeniedPayload, ErrorPayload)
  • BEM-классы form-card__*, disabled-состояние кнопки, aria-live для доступности
  • Редирект по success_url после успеха

Как адаптировать для лендинга:

  1. Скопировать example.html → в _layouts/ лендинга
  2. Встроить форму в нужное место страницы (не в конце, а в CTA-секцию)
  3. Поменять CSS-переменные под стиль бренда (цвета, шрифты)
  4. Перевести лейблы и сообщения на русский:
    • 'Send a message' → нужный заголовок
    • 'Submit' → текст кнопки
    • 'Thanks — submission #...' → сообщение об успехе
    • Сообщения об ошибках в объекте messages
  5. Добавить <script id="form-spec">{{ note.FormSpecJSON() | unsafe }}</script> в layout

Ключевые строки из примера (не менять):

const spec = JSON.parse(document.getElementById('form-spec').textContent);
const definition = (spec.forms || {})[formId];  // formId = '' для одиночной формы
// ...
variables: { input: { noteVersionId: spec.note_version_id, formId, fields } }

Шаг 4 — Объяснить пользователю про токен

Перед настройкой cron — обязательно сообщить:

"Чтобы я мог проверять заявки автоматически, нужно создать личный токен в админке trip2g:
Настройки → API → Создать токен (personal admin token, начинается с t2g_)
Пришли мне этот токен — я сохраню его и поставлю автопроверку."

После получения токена — сохранить в ~/.env или extra_creds.md:

TRIP2G_ADMIN_TOKEN=t2g_...

Шаг 5 — Узнать notePathId формы

curl -sS https://$TRIP2G_URL/_system/graphql \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $TRIP2G_ADMIN_TOKEN" \
  -d '{"query": "query { admin { formNotes { notePathId path title submitCount } } }"}'

Найти нужную заметку по path → запомнить notePathId.

Шаг 6 — Поставить cron на проверку каждые 4 часа

/cron create "Проверь заявки формы. 

Выполни GraphQL запрос:
curl -sS https://$TRIP2G_URL/_system/graphql \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer $TRIP2G_ADMIN_TOKEN' \
  -d '{\"query\": \"query($p: Int64!) { admin { formSubmits(notePathId: $p) { nodes { id createdAt status fields { ... on AdminFormStringValue { name value } } } } } }\", \"variables\": {\"p\": NOTE_PATH_ID}}'

Если есть записи со status != processed — посчитай их и сообщи пользователю:
'У тебя N необработанных заявок. Смотри: $TRIP2G_URL/admin/forms'"
--schedule "0 */4 * * *"

Шаг 7 — Отметить заявку как обработанную

Агент может пометить заявку обработанной по запросу пользователя:

mutation Mark($input: MarkFormSubmitProcessedInput!) {
  markFormSubmitProcessed(input: $input) {
    ... on MarkFormSubmitProcessedPayload { submit { id processedAt } }
    ... on ErrorPayload { message }
  }
}

Variables: { "input": { "submitId": "ID_из_заявки" } }

Типы полей для справки

type Используй для В JS поле
text имя, телефон, Telegram, сообщение stringValue
email email stringValue
int рейтинг, количество intValue
bool чекбокс согласия boolValue

Кейсы

Кейс 1: Простая форма "перезвоните мне"

form:
  fields:
    - name: phone
      type: text
      required: true

Кейс 2: Форма с согласием на обработку данных

form:
  fields:
    - name: email
      type: email
      required: true
    - name: agree
      type: bool
      required: true
      enum: [true]

Кейс 3: Форма только для залогиненных

form:
  can_submit: admin
  fields:
    - name: message
      type: text
      required: true