Declarative Formlar (Native UI)

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.

Kavram: siz bildirirsiniz, Restomenum render eder

Declarative formiframe (Custom UI)
Siz ne verirsinizAlan şeması (JSON)Tam HTML sayfası
RenderRestomenum (native)Sizin sayfanız
Stil / güvenlikRestomenum'dan, tutarlı, escape'liSizde (App Bridge, origin pin, CSP)
Ne zamanGünlük form/onay (default)Karmaşık/çok-adımlı UI, ödeme widget'ı

Nerede kullanılır

  • Action buton (action.type:"form"): paket detayda butona basılır → form modal → formDataaction ucunuza POST.
  • Before-hook (ui.kind:"form"): işlem öncesi gate formu → formData ile allow/deny kararı verirsiniz.

Form şeması her ikisinde de aynıdır.

Form şeması

{
  "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:

AlanTipZorunluAçıklama
idstringBenzersiz. ui.formId / action.form bununla eşleşir.
titlei18n objForm başlığı (text-only).
submitLabeli18n objGönder butonu metni.
fields[]arrayEn 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" } } ] }
    ]
  }
]

Alan tipleri (whitelist)

GET /plugin-meta/catalogformFieldTypes ile güncel liste. Bunun dışı reddedilir (drift-free). Şu an: Metin · Çok satır · Sayı · Onay kutusu · Açılır liste · Tarih.

typeRenderformData değeriEk alanlar
texttek satırstringmaxLength, pattern, required
textareaçok satırstringmaxLength, required
numbersayınumbermin, max, required
checkboxonay kutusubooleanrequired
selectaçılır listestring (seçilen value)options zorunlu, required
datetarihstringrequired

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/textarea

select 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)

Geri dönen formData

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
}

Render & güvenlik kuralları

  • label/option.label metinleri text-only — HTML basılmaz (XSS yok), escape edilir.
  • Stil Restomenum'dan — eklenti CSS enjekte edemez; tüm eklentilerde tutarlı görünüm.
  • Sınırlar uygulanır: required boşsa submit engellenir, maxLength aşılamaz.
  • Geçersiz şema sessizce düşürülür: whitelist dışı type, tekrarlı key, options'sız select → o alan/form render edilmez.
  • Yine de gelen formData'ya her zaman güvenmeyin, sunucuda doğrulayın.

Manifest örnekleri

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… */ } }
}]

Akış (geliştirici gözünden)

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 })
Token/DOM/origin derdi yok — yalnız şema bildirir, formData alırsınız. Karmaşık/çok-adımlı UI veya widget gerekiyorsa iframe Custom UI'a bakın.