Formularze i Form Builder
Twórz formularze w wizualnym Form Builderze i podłączaj je do custom bloków zapisując ID formularza w polu bloku.
Przegląd
Cmssy posiada wbudowany Form Builder, który pozwala tworzyć formularze wizualnie i podłączać je do dowolnego custom bloku. Formularze obsługują walidację, zgłoszenia, powiadomienia email i zdarzenia dostarczane webhookami — bloki muszą tylko renderować UI.
System składa się z dwóch części:
- Form Builder (Dashboard > Formularze) — definiowanie pól, walidacji i akcji
- Pole
formIdna bloku — przechowuje, który formularz blok ma renderować
Tworzenie formularza
1. Otwórz Form Builder
Przejdź do Dashboard → Formularze → Utwórz formularz. Nadaj nazwę i slug (np. formularz-kontaktowy).
2. Dodaj pola
Każde pole posiada:
| Właściwość | Opis |
|---|---|
| name | Unikalny klucz pola (np. email, wiadomosc) |
| type | text, email, textarea, number, phone, url, date, select, multiselect, checkbox, radio, file, hidden |
| label | Etykieta wyświetlana (wielojęzyczna) |
| placeholder | Tekst zastępczy (wielojęzyczny) |
| validation | required, minLength, maxLength, minValue, maxValue, pattern, customMessage |
| width | full, half lub third — kontroluje szerokość pola w siatce |
| options | Dla select, multiselect, radio — tablica { value, label } |
| order | Kolejność wyświetlania |
3. Skonfiguruj ustawienia
| Ustawienie | Opis |
|---|---|
| Typ akcji | contact albo custom — nic innego nie jest przyjmowane. Żaden nie zmienia tego, co serwer robi ze zgłoszeniem; to etykieta, po której Twój blok może wybrać layout. |
| Odbiorcy email | Lista emaili otrzymujących powiadomienia |
| Wiadomość sukcesu | Wyświetlana po udanym wysłaniu (wielojęzyczna) |
| Wiadomość błędu | Wyświetlana przy niepowodzeniu (wielojęzyczna) |
| Tekst przycisku | Tekst na przycisku submit (wielojęzyczny) |
| Zapisuj zgłoszenia | Przechowuj zgłoszenia w bazie (domyślnie: true) |
| Wysyłaj powiadomienia | Email do odbiorców przy każdym zgłoszeniu (domyślnie: true) |
| Wymagaj logowania | Tylko zalogowani użytkownicy mogą wysłać |
4. Opublikuj
Ustaw status na Opublikowany. Skopiuj jego ID formularza — będziesz się do niego odwoływać z bloku.
Używanie formularzy w custom blokach
Odwołanie do formularza
Dodaj pole formId do schematu bloku. Użyj fields.form(): renderuje w edytorze picker formularzy i zapisuje id wybranego formularza, więc nikt nie musi przeklejać id ręcznie.
// blocks/contact/block.ts
import { defineBlock, fields } from "@cmssy/react";
import Contact from "./Contact";
export const contactBlock = defineBlock({
type: "contact",
label: "Contact",
component: Contact,
props: {
formId: fields.form({ label: "Formularz" }),
submitLoadingText: fields.text({ label: "Tekst ładowania", defaultValue: "Wysyłanie..." }),
successHeading: fields.text({ label: "Nagłówek sukcesu", defaultValue: "Wiadomość wysłana!" }),
},
});Renderowanie formularza
SDK automatycznie pobiera każdy formularz wskazany przez formId bloku i wstrzykuje jego definicję do context bloku jako context.forms[formId] — bez ręcznego pobierania:
// blocks/contact/src/Contact.tsx
export default function Contact({ content, context }) {
const { formId } = content;
const formDef = formId ? context?.forms?.[formId] ?? null : null;
// formDef.fields - tablica definicji pól
// formDef.settings - tekst przycisku, wiadomości, typ akcji
// ...wyrenderuj pola
}Wysyłanie formularza
Wysyłka idzie przez public.form.submit. Trzymaj mutację we własnym repo i wysyłaj ją swoim skonfigurowanym klientem - SDK niesie ten sam string jako SUBMIT_FORM_MUTATION, ale pod @cmssy/core/internal, które nie jest publicznym API. Backend waliduje pola, zapisuje zgłoszenie, wysyła maile z powiadomieniami, emituje form.submitted i odpowiada wynikiem sukces/błąd:
// blocks/contact/actions.ts
"use server";
import { createCmssyClient, type CmssyFormSubmitResponse } from "@cmssy/react";
import { cmssy } from "@/cmssy.config";
const SUBMIT_FORM = `mutation SubmitForm($formId: ID!, $input: SubmitFormInput!) {
public {
form {
submit(formId: $formId, input: $input) {
success
message
submissionId
redirectUrl
}
}
}
}`;
const client = createCmssyClient(cmssy);
export async function submitForm(formId: string, data: Record<string, string>) {
const res = await client.query<{
public: { form: { submit: CmssyFormSubmitResponse } };
}>(SUBMIT_FORM, { formId, input: { data } });
return res.public.form.submit; // { success, message, submissionId, redirectUrl }
}Wysłanie formularza to jedyny zapis, jaki publiczny frontend może wykonać bez tokenu. Wszystko inne wokół formularzy - tworzenie ich, czytanie zgłoszeń, zmiana statusu - wymaga autoryzowanego klienta.
Referencyjne bloki formularzy
Cmssy nie dostarcza bloków — w modelu headless bloki budujesz w swoim repozytorium za pomocą SDK. To typowe bloki formularzy, które możesz zaimplementować jako wzorce referencyjne, każdy podłącza formularz z Form Buildera (jego actionType) do bloku renderowanego przez Ciebie:
| Blok | Typ akcji | Opis |
|---|---|---|
| Contact | contact | Formularz kontaktowy z kartami info i cytatem |
| Newsletter | custom | Zapis na maila, który webhook workspace'u przekazuje Twojemu dostawcy mailingu |
| Zapytanie ofertowe | contact | Dłuższe zapytanie, które ląduje w Zgłoszeniach i idzie mailem |
Każdy definiuje formularz po stronie serwera w Form Builderze i renderuje go w bloku przez pole formId — markup należy do dewelopera. Zobacz Uwierzytelnianie członków po flow auth.
Logowanie, rejestracja i reset hasła to nie formularze z Form Buildera: wołają bezpośrednio mutacje auth członków (login, register, forgotPassword, resetPassword).
Zgłoszenia formularzy
Przeglądanie zgłoszeń
Przejdź do Dashboard → Formularze → [Twój formularz] → Zgłoszenia. Każde zgłoszenie pokazuje:
- Wszystkie wartości pól
- Status (
pending,processed,spam,archived) - Adres IP, user agent, referrer
- Znacznik czasu
- Status dostarczenia email
Cykl życia zgłoszenia
- Użytkownik wysyła formularz na Twojej opublikowanej stronie
- Serwer waliduje pola i sprawdza limity częstotliwości
- Zgłoszenie zapisane ze statusem
pending - Powiadomienie email wysłane do odbiorców (jeśli włączone)
form.submittedwyemitowane — już po zapisie zgłoszenia, do każdego webhooka workspace'u zapisanego na to zdarzenie, a nieudana dostawa nigdy nie psuje zgłoszenia- Status zostaje
pending, dopóki nie zmienisz go w zakładce Zgłoszenia
Jak zgłoszenie trafia do zewnętrznego systemu
Zarejestruj webhook workspace'u na zdarzenie form.submitted. To jedyna droga na zewnątrz i jest trwała: podpisana, z ponowieniami, pozbierana przez sweeper i widoczna w logu dostaw, który możesz przeczytać i odtworzyć. Formularz nie ma własnego ustawienia webhooka.
Zdarzenie niesie id zgłoszenia, nie odpowiedzi. Odczytasz je przez get_form_submission albo form.submission(submissionId:) w API, na tej samej permisji forms:submissions:view, która pozwala endpointowi zapisać się na to zdarzenie. Odpowiedzi zostają wtedy w jednym miejscu z jedną ścieżką usunięcia, a żadne duże zgłoszenie nie przepchnie zdarzenia przez limit rozmiaru dostawy i nie uciszy go wszystkim pozostałym odbiorcom.
Ochrona przed spamem
- Limit per-IP na formularz
- Pola honeypot można dodać jako pole typu
hidden
Typy akcji - referencja
contact
Zapisuje zgłoszenie + wysyła email do skonfigurowanych odbiorców. Domyślny dla większości formularzy.
custom
Etykieta i nic więcej: serwer traktuje formularz custom dokładnie jak contact. Używaj jej, żeby oznaczyć formularze, które Twój blok renderuje inaczej. Żeby dotrzeć do zewnętrznego serwisu (Zapier, Slack, CRM), zapisz webhook workspace'u na form.submitted.
Logowanie i rejestracja członków to nie typy akcji formularza — zobacz Uwierzytelnianie członków.
Wskazówki
- Zawsze opublikuj formularz zanim się do niego odwołasz — formularze draft nie mogą być pobrane
- Używaj szerokości
halfithirdaby tworzyć wielokolumnowe układy (np. imię + nazwisko obok siebie) - Wielojęzyczne etykiety — pola formularza wspierają etykiety i placeholdery per-język
- Testowe zgłoszenia są zapisywane jak prawdziwe — usuń je z zakładki Zgłoszenia po zakończeniu
- Debugowanie webhooków — otwórz Ustawienia → Webhooki → Dostawy: każda próba jest tam z kodem odpowiedzi, jedną możesz odtworzyć