HTML/CSS yazmazsınız. Manifest'te formun şemasını (alanlar) bildirirsiniz; Restomenum onu panelde native gösterir, doldurtur ve değerleri size formData olarak iletir. Sonuç: tutarlı görünüm, güvenlik (XSS yok), App Bridge derdi yok. Action butonlarında (action.type:form) ve before-hook'larda (ui.kind:form) kullanılır.
| Declarative form | iframe (Custom UI) | |
|---|---|---|
| Siz ne verirsiniz | Alan şeması (JSON) | Tam HTML sayfası |
| Render | Restomenum (native) | Sizin sayfanız |
| Stil / güvenlik | Restomenum'dan, tutarlı, escape'li | Sizde (App Bridge, origin pin, CSP) |
| Ne zaman | Günlük form/onay (default) | Karmaşık/çok-adımlı UI, ödeme widget'ı |
action.type:"form"): paket detayda butona basılır → form modal → formData → action ucunuza POST.ui.kind:"form"): işlem öncesi gate formu → formData ile allow/deny kararı verirsiniz.Form şeması her ikisinde de aynıdır.
{
"title": { "tr": "Kurye Bilgileri" }, // opsiyonel başlık
"fields": [ … ], // alanlar (↓)
"submitLabel": { "tr": "Gönder" } // opsiyonel buton metni
}Editörde formu id'li olarak forms[]'te tanımlarsınız; buton/hook bunu formId ile referanslar, portal yayında içeriğini inline gömer:
| Alan | Tip | Zorunlu | Açıklama |
|---|---|---|---|
| id | string | ✓ | Benzersiz. ui.formId / action.form bununla eşleşir. |
| title | i18n obj | – | Form başlığı (text-only). |
| submitLabel | i18n obj | – | Gönder butonu metni. |
| fields[] | array | ✓ | En az bir alan. |
"forms": [
{
"id": "invoiceForm", // benzersiz; ui.formId / action.form bununla eşleşir
"title": { "tr": "Fatura Bilgisi" },
"submitLabel": { "tr": "Devam" },
"fields": [
{ "key": "vkn", "type": "text", "label": { "tr": "VKN" }, "required": true, "pattern": "^[0-9]{10,11}$" },
{ "key": "type", "type": "select", "label": { "tr": "Tür" },
"options": [ { "value": "kurumsal", "label": { "tr": "Kurumsal" } },
{ "value": "bireysel", "label": { "tr": "Bireysel" } } ] }
]
}
]GET /plugin-meta/catalog → formFieldTypes ile güncel liste. Bunun dışı reddedilir (drift-free). Şu an: Metin · Çok satır · Sayı · Onay kutusu · Açılır liste · Tarih.
| type | Render | formData değeri | Ek alanlar |
|---|---|---|---|
text | tek satır | string | maxLength, pattern, required |
textarea | çok satır | string | maxLength, required |
number | sayı | number | min, max, required |
checkbox | onay kutusu | boolean | required |
select | açılır liste | string (seçilen value) | options zorunlu, required |
date | tarih | string | required |
Alan ortak alanları:
{ "key": "note", // ZORUNLU, benzersiz — formData'da bu anahtarla döner
"type": "textarea", // whitelist'ten
"label": { "tr": "Kurye notu" }, // text-only (HTML değil)
"required": false, // opsiyonel
"maxLength": 200 } // text/textareaselect için:
{ "key": "priority", "type": "select", "label": { "tr": "Öncelik" },
"options": [ { "value": "normal", "label": { "tr": "Normal" } },
{ "value": "urgent", "label": { "tr": "Acil" } } ] }
// formData.priority = "normal" | "urgent" (seçilen value)Kullanıcı doldurunca, action/hook isteğinizdeki formData alanında key → değer olarak gelir (tipine göre):
// Form: note(textarea) + priority(select) + weight(number) + verify(checkbox) + date(date)
"formData": {
"note": "Cam kenarı",
"priority": "urgent", // select → seçilen value
"weight": 3, // number → number
"verify": true, // checkbox → boolean
"date": "2026-06-07" // date → string
}label/option.label metinleri text-only — HTML basılmaz (XSS yok), escape edilir.required boşsa submit engellenir, maxLength aşılamaz.type, tekrarlı key, options'sız select → o alan/form render edilmez.formData'ya her zaman güvenmeyin, sunucuda doğrulayın.Action butonu (type:form):
"buttons": [{
"id": "send-form", "slot": "packet.detail.actions", "label": { "tr": "Kuryeye Gönder (form)" },
"action": { "type": "form", "hook": "packet.sendToCourier",
"form": { "title": { "tr": "Kurye Bilgileri" },
"fields": [
{ "key": "note", "type": "textarea", "label": { "tr": "Kurye notu" }, "maxLength": 200 },
{ "key": "priority", "type": "select", "label": { "tr": "Öncelik" },
"options": [ { "value": "normal", "label": { "tr": "Normal" } },
{ "value": "urgent", "label": { "tr": "Acil" } } ] }
], "submitLabel": { "tr": "Gönder" } } }
}]Hook UI (kind:form) — aynı şema:
"hooks": [{
"action": "table.close", "blocking": true,
"ui": { "kind": "form", "form": { /* …aynı şema: title, fields, submitLabel… */ } }
}]Buton/gate → Restomenum formu native gösterir (sizin şemanız)
→ kullanıcı doldurur (validasyon Restomenum'da)
→ değerler formData olarak action/hook isteğinize gelir
→ siz formData ile iş yaparsınız ({ success } / { decision })formData alırsınız. Karmaşık/çok-adımlı UI veya widget gerekiyorsa iframe Custom UI'a bakın.