# MOSPLA Forms — интеграция форм обратной связи на сайт

> Инструкция для Claude Code. Отдай этот файл клод-коду вместе с API-ключом из
> платформы MOSPLA (`Forms → карточка проекта → API-ключ`) и доменом проекта.
> Клод настроит все формы на сайте так, чтобы заявки сыпались в базу MOSPLA,
> присылались уведомлениями в Telegram/MAX-боты и были видны в платформе.

---

## Что получает клод-код

1. `API_KEY` — 32 hex-символа, из UI платформы.
2. `DOMAIN` — опционально, для самопроверки (должен совпадать с `project.domain`).

## Куда шлются заявки

```
POST https://002-forms.markonline.ru/forms/submit
Host: 002-forms.markonline.ru
Content-Type: application/json
X-API-Key: <API_KEY>

{
  "name": "Иван",
  "phone": "+7999...",
  "email": "ivan@example.com",          // опц.
  "message": "Комментарий",             // опц.
  "page_url": "https://site.ru/page",   // опц., автозаполняется
  "form_name": "Форма в футере",        // опц., для различения форм на странице
  "utm_source": "yandex",               // опц., из querystring/cookie
  "utm_medium": "cpc",
  "utm_campaign": "spring-sale",
  "utm_content": "banner",
  "utm_term": "купить"
}
```

Ответ: `200 OK {"ok": true, "lead_id": "...", "notifications": {...}}`.

DEV-эндпоинт (для локальных тестов): `https://dev-002-forms.markonline.dev/forms/submit`.

---

## Как клод-код должен настроить сайт

Универсальный подход — внедрить **один JS-файл**, который перехватывает сабмиты
всех форм с маркером `data-mos-forms` и шлёт их на API.

### 1. Добавь в `<head>` или перед `</body>`:

```html
<script src="/mos-forms.js" defer></script>
<meta name="mos-forms-key" content="ВАШ_API_KEY">
```

(Или инлайн-ключом — `<script>window.MOS_FORMS_KEY='...';</script>` перед подключением.)

### 2. Создай `public/mos-forms.js` (или положи рядом с `index.html`):

```javascript
(function () {
  const API = "https://002-forms.markonline.ru/forms/submit";
  const KEY =
    (document.querySelector('meta[name="mos-forms-key"]') || {}).content ||
    window.MOS_FORMS_KEY;

  if (!KEY) {
    console.warn("[mos-forms] API key is missing");
    return;
  }

  const qs = new URLSearchParams(location.search);
  const utm = {
    utm_source: qs.get("utm_source") || getCookie("utm_source") || null,
    utm_medium: qs.get("utm_medium") || getCookie("utm_medium") || null,
    utm_campaign: qs.get("utm_campaign") || getCookie("utm_campaign") || null,
    utm_content: qs.get("utm_content") || getCookie("utm_content") || null,
    utm_term: qs.get("utm_term") || getCookie("utm_term") || null,
  };

  function getCookie(name) {
    const m = document.cookie.match(
      new RegExp("(?:^|; )" + name.replace(/([.$?*|{}()[\]\\/+^])/g, "\\$1") + "=([^;]*)")
    );
    return m ? decodeURIComponent(m[1]) : null;
  }

  function gather(form) {
    const fd = new FormData(form);
    const data = {};
    fd.forEach((v, k) => (data[k] = v));
    // Map common field names
    const pick = (obj, ...keys) => keys.map((k) => obj[k]).find(Boolean) || null;
    return {
      name: pick(data, "name", "Name", "fio", "ФИО", "имя"),
      phone: pick(data, "phone", "tel", "Phone", "телефон"),
      email: pick(data, "email", "Email", "email-mini", "почта") || null,
      message: pick(data, "message", "comment", "Message", "комментарий") || null,
      form_name: form.dataset.mosFormsName || form.getAttribute("name") || null,
      page_url: location.href,
      ...utm,
    };
  }

  async function submit(form, payload) {
    try {
      const resp = await fetch(API, {
        method: "POST",
        headers: { "Content-Type": "application/json", "X-API-Key": KEY },
        body: JSON.stringify(payload),
      });
      const body = await resp.json().catch(() => ({}));
      if (!resp.ok) throw new Error(body.detail || resp.statusText);
      form.dispatchEvent(new CustomEvent("mos-forms:success", { detail: body }));
    } catch (e) {
      form.dispatchEvent(new CustomEvent("mos-forms:error", { detail: e }));
    }
  }

  document.addEventListener(
    "submit",
    function (ev) {
      const form = ev.target;
      if (!(form instanceof HTMLFormElement)) return;
      if (!form.hasAttribute("data-mos-forms")) return;
      ev.preventDefault();
      const payload = gather(form);
      if (!payload.name || !payload.phone) {
        form.dispatchEvent(
          new CustomEvent("mos-forms:error", { detail: new Error("name and phone required") })
        );
        return;
      }
      submit(form, payload);
    },
    true
  );
})();
```

### 3. На всех формах проставь атрибут:

```html
<form data-mos-forms data-mos-forms-name="Форма в футере">
  <input name="name" required />
  <input name="phone" required />
  <input name="email" />
  <textarea name="message"></textarea>
  <button type="submit">Отправить</button>
</form>
```

### 4. Добавь UX-обратную связь (по желанию):

```html
<script>
  document.addEventListener("mos-forms:success", function (e) {
    alert("Спасибо! Заявка отправлена.");
    e.target.reset();
  });
  document.addEventListener("mos-forms:error", function (e) {
    alert("Не удалось отправить форму. Попробуйте позже.");
    console.error(e.detail);
  });
</script>
```

---

## Проверка

1. Открой сайт, заполни форму, отправь.
2. Открой DevTools → Network → ищи запрос на `002-forms.markonline.ru/forms/submit` со статусом 200.
3. В платформе MOSPLA: Forms → нужный проект → видишь новый лид.
4. В привязанных TG/MAX-чатах: пришло уведомление.

## Что делать, если сайт использует готовый форм-билдер (Tilda, WP, etc.)

- **Tilda**: в блоке формы → настройки → Webhook → вставь `https://002-forms.markonline.ru/forms/submit`.
  Tilda шлёт `application/x-www-form-urlencoded` — попроси клод-кода добавить
  промежуточный endpoint, который маппит поля и ставит `X-API-Key`.
- **WordPress/Contact Form 7**: хук `wpcf7_mail_sent` → `wp_remote_post` на наш API с `X-API-Key`.
- **HTML + PHP**: в обработчике формы — `curl_setopt(..., CURLOPT_HTTPHEADER, ["X-API-Key: ..."])`
  и пересылай JSON.

## Безопасность

- `X-API-Key` **не прячется** от клиента (он в JS) — это ожидаемо. API-ключ
  работает только для сабмита форм; стоит только за одним проектом и
  одним доменом. В случае злоупотребления — `Forms → Project → Regenerate key`.
- На бэкенде можно включить smartcaptcha (поле `smartcaptcha_key` у проекта) —
  тогда поле добавляется в форму и валидируется до сабмита.
- CORS открыт для `/forms/submit` — заявки можно слать с любого домена
  (ограничение идёт только по ключу/проекту).

## Полезные эндпоинты платформы (для клода)

```bash
# Проверить, что ключ валиден (админский JWT не нужен — достаточно попробовать submit):
curl -X POST https://002-forms.markonline.ru/forms/submit \
  -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" \
  -d '{"name":"Тест","phone":"+70000000000"}'
```
