# Formularze krok po kroku

## Działający formularz w szablonie Loom

Formularz składa się z **instancji GrowSite**, konfiguracji pól i elementu strony wskazującego `instance_id`. Paczka szablonu definiuje wygląd i miejsce na treści, a instancja należy do konkretnej witryny. Nie przenoś identyfikatora formularza jednego klienta do paczki dla innych klientów.

Paczka przykładowa zawiera stronę `contact` i sekcję `sections/contact.html`. Zawiera nagłówek, opis i `gs-form`. Wpisz ID instancji w ustawieniu `form_id` albo dodaj osobny formularz na płótnie. Obecny importer nie tworzy instancji formularza z samego schema Loom.

## 1. Przygotuj witrynę i instancję

1. Włącz plugin `contact-form` dla witryny/workspace.
2. Otwórz podstronę kontaktową i dodaj element formularza na płótnie sekcji.
3. W ustawieniach elementu wybierz istniejącą instancję albo utwórz nową.
4. Nadaj jej nazwę, dodaj pola, wybierz wymagane odpowiedzi i komunikat sukcesu.
5. Zapisz formularz. Kreator przypisze otrzymane ID do elementu.
6. Zapisz stronę i przetestuj wysyłkę w publicznym widoku testowej witryny.

W kreatorze wysyłanie jest zablokowane. Brak działania submitu podczas edycji nie oznacza, że endpoint nie działa. Instancja musi być aktywna i należeć do tej samej witryny.

## 2. Konfiguracja instancji

Poniższy JSON jest treścią konfiguracji instancji, a nie plikiem `templates/contact.json` ani schema Loom. Możesz odwzorować go w edytorze formularzy. Integrator aplikacji może wysłać go do uwierzytelnionego `POST /api/v1/sites/{site}/plugin-instances` w kontekście użytkownika mającego prawo zarządzania witryną.

```json
{
  "key":"contact-form",
  "name":"Formularz kontaktowy",
  "status":"active",
  "page_id":null,
  "configuration":{
    "fields":[
      {"key":"name","label":"Imię","type":"text","required":true},
      {"key":"email","label":"E-mail","type":"email","required":true},
      {"key":"topic","label":"Temat","type":"select","required":true,"options":["Wycena","Wsparcie"]},
      {"key":"message","label":"Wiadomość","type":"textarea","required":true}
    ],
    "success_message":"Dziękujemy. Wiadomość została odebrana."
  }
}
```

`page_id` może wskazywać podstronę tej witryny albo być `null`. Nazwa instancji ma do 120 znaków, komunikat sukcesu do 300. Limity pól są w [instrukcji inputów](/pl/inputs/). Zmianę instancji wykonuje `PATCH /api/v1/sites/{site}/plugin-instances/{instance}`.

## 3. Powiązanie z płótnem

Poniższy obiekt przedstawia kształt węzła w `content.canvas.nodes`. Zastąp przykładowe ID rzeczywistym ID zapisanej instancji; najbezpieczniej pozwolić kreatorowi wybrać i zapisać je automatycznie.

```json
{
  "id":"contact-node",
  "type":"form",
  "name":"Kontakt",
  "instance_id":"01ARZ3NDEKTSV4RRFFQ69G5FAV",
  "style":{"padding":24,"radius":16}
}
```

`content.canvas` ma też `version:1`, `slots`, `fields` i opcjonalne `elements`. To dane strony, nie ustawienia bloku Loom. Renderer odczytuje węzeł `form` i uruchamia `PluginRenderer` dla `contact-form`.

W natywnej sekcji Loom wspólny `SectionCanvas` dodaje węzły płótna po treści sekcji. Nie dopisuj dodatkowego `gs-canvas` tylko po to, żeby pokazać formularz: możesz wyrenderować te same elementy dwa razy. Do osadzania wewnątrz sekcji użyj `gs-form` z punktu 5.

## 4. Wysłanie i błędy

Wspólny komponent wykonuje `POST /api/v1/render/sites/{slug}/contact`. Host API i adres endpointu dostarcza `PluginProvider`; nie wpisuj produkcyjnej domeny na sztywno w szablonie. Kształt danych:

```json
{
  "instance_id":"01ARZ3NDEKTSV4RRFFQ69G5FAV",
  "values":{"name":"Anna","email":"anna@example.com","topic":"Wycena","message":"Proszę o kontakt."},
  "website":""
}
```

`website` jest ukrytym polem ochronnym i musi pozostać puste. Do `values` trafiają tylko klucze zadeklarowane w instancji. API weryfikuje aktywną instancję, witrynę, dostępność pluginu i typy odpowiedzi. Endpoint ma ograniczanie częstotliwości żądań.

Przy sukcesie komponent pokazuje `success_message` lub tekst językowy i czyści pola. Przy błędzie zachowuje dane i wyświetla komunikat. Przycisk jest zablokowany podczas żądania; status ma `aria-live="polite"`. Nie twórz obok drugiego handlera wysyłania.

Wysyłka wymaga opublikowanej witryny. Lokalny wyjątek dla draftu zależy od ustawienia `sites.render_drafts`; nie traktuj go jako zachowania produkcji. Zapis zgłoszenia nie jest sam w sobie integracją z zewnętrznym systemem newsletterowym.

## 5. Formularz bezpośrednio w Loom

```html
<gs-form instance-id="[[ section.settings.form_id ]]" submit-label="Wyślij"></gs-form>
```

Dodaj ustawienie schema typu `text` z ID `form_id`, etykietą „ID formularza” i pustym defaultem. Po utworzeniu formularza w panelu wpisz jego rzeczywiste ID w ustawieniu sekcji. Używaj tej nazwy ustawienia także w blokach, aby podgląd mógł załadować instancję. Formularz przypisz do tej strony lub całej witryny. Nie dołączaj równocześnie tego samego formularza jako węzła canvas.

Nie potrzebujesz adaptera TSX. Element korzysta ze wspólnego komponentu: plugin wyłączony, puste ID lub niedostępna instancja dają pusty wynik. Opcjonalny `submit-label` zmienia etykietę przycisku, a pola i komunikat sukcesu pochodzą z instancji. Nie wpisuj identyfikatorów klienta do paczki przeznaczonej do dystrybucji.

## 6. Test kompletnego przepływu

Sprawdź brak instancji, wyłączony plugin, instancję innej witryny, pusty wymagany e-mail, błędną opcję, poprawne wysłanie, błąd sieci i ponowną próbę. Odpowiedź powinna pojawić się w zgłoszeniach właściwej witryny. Upewnij się, że podwójne kliknięcie nie wysyła dwóch żądań równolegle, a odświeżenie edytora zachowuje przypisanie formularza.

## Pliki strony kontaktowej

### sections/contact.html

```html
<section class="launch-contact">
  <h1 data-text-field="[[ section.settings_path ]].title" gs-text="section.settings.title"></h1>
  <p data-text-field="[[ section.settings_path ]].text" gs-text="section.settings.text"></p>
<gs-form instance-id="[[ section.settings.form_id ]]"></gs-form>
</section>
```

### templates/contact.json

```json
{"title":"Contact","sections":{"contact":{"type":"contact"}},"order":["contact"]}
```
