@appaflytech/zeyrek-ai-mcp 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +21 -7
- package/.env.production.example +42 -0
- package/README.md +14 -7
- package/dist/config.js +113 -47
- package/dist/knowledge/componentCatalog.js +474 -31
- package/dist/knowledge/componentEvents.js +150 -12
- package/dist/knowledge/errorPath.js +64 -0
- package/dist/knowledge/normalize.js +12 -1
- package/dist/knowledge/outlines.js +6 -0
- package/dist/knowledge/rules.js +184 -3
- package/dist/knowledge/styleResolve.js +485 -0
- package/dist/observability/trace.js +4 -0
- package/dist/platform/http.js +3 -0
- package/dist/platform/responses.js +80 -4
- package/dist/platform/screenshot.js +106 -0
- package/dist/resources/examples/layout-app-shell.json +368 -0
- package/dist/resources/examples/microservice-table-list.json +1 -0
- package/dist/resources/examples/middleware-authenticate.json +243 -47
- package/dist/resources/examples/workflow-basic.json +24 -2
- package/dist/resources/guides/component.md +6 -3
- package/dist/resources/guides/external-component.md +129 -0
- package/dist/resources/guides/layout.md +147 -0
- package/dist/resources/guides/microservice.md +35 -0
- package/dist/resources/guides/middleware.md +10 -0
- package/dist/resources/guides/page.md +132 -14
- package/dist/resources/guides/workflow.md +127 -2
- package/dist/server.js +2 -0
- package/dist/tools/externalComponents.js +285 -0
- package/dist/tools/layouts.js +48 -6
- package/dist/tools/microservices.js +2 -2
- package/dist/tools/pages.js +98 -30
- package/dist/tools/platformInfo.js +2 -2
- package/dist/tools/screens.js +36 -0
- package/dist/tools/workflows.js +3 -3
- package/package.json +4 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Layout Rehberi (yerleşim kabuğu)
|
|
2
|
+
|
|
3
|
+
Sayfaların paylaştığı kabuk: yan menü, üst şerit, kullanıcı menüsü ve sayfa içeriğinin
|
|
4
|
+
gireceği delik. Şema gövdesi ve koordinat modeli için `platform_guide('page')` (Kural 1,
|
|
5
|
+
1d, 1f); komponent sözleşmeleri için `platform_guide('component')`.
|
|
6
|
+
|
|
7
|
+
## Karar kuralları
|
|
8
|
+
|
|
9
|
+
**1. Layout, tek `slot` içeren SIRADAN bir komponent ağacıdır.** Ayrı bir dil yoktur:
|
|
10
|
+
sayfada kullandığın her kap, her prop, her `appearance` alanı burada da geçerlidir. Araç
|
|
11
|
+
kutusu layout modülünde süzülmez — tüm katalog kullanılabilir.
|
|
12
|
+
|
|
13
|
+
**2. Şema alanının adı `schema` değil `views`.** (LAY-001)
|
|
14
|
+
|
|
15
|
+
**3. Micro-op hattı YOKTUR.** `page_schema_patch`'in layout karşılığı yok (backend'de
|
|
16
|
+
`PATCH /layouts/.../schema` ucu yok). Değişiklik `layout_update` ile yazılır; gateway kaydı
|
|
17
|
+
önce okur ve yalnız verdiğin alanları değiştirir, `views` göndermezsen ağaç korunur.
|
|
18
|
+
Okumak için `layout_schema_get` (summary kipi hedef bulmak için yeterlidir).
|
|
19
|
+
|
|
20
|
+
**4. TEK slot.** Sayfa şeması **her** slot'a ayrı ayrı kopyalanır — iki slot, sayfayı iki
|
|
21
|
+
kez çizer ve `elementId`'leri tekrarlar. (LAY-002 / LAY-006)
|
|
22
|
+
|
|
23
|
+
**5. Kabuğu `container` + `slot` ile kur.** `layout` monoliti (sabit logo/menü/kullanıcı
|
|
24
|
+
bandı — yalnız doldurulur) ve `layout-shell` + `layout-region` ikilisi **emekliye
|
|
25
|
+
ayrıldı**; kayıtlı kalırlar ve mevcut layout'lar çalışır, ama yeni kabuk `container` ile
|
|
26
|
+
kurulur. Sebebi: `container` zaten `layout-region`'ın yaptığının üstünü yapıyor (flex/ızgara
|
|
27
|
+
düzeni, hizalama, gap, wrap), region'ın kattığı tek şey `dock` sözlüğüydü.
|
|
28
|
+
|
|
29
|
+
**5a. Kök, sayfanın `page_container`'ının yerleşim ikizidir.** Studio'nun yeni layout
|
|
30
|
+
iskeleti (`getDefaultLayout`) tam olarak şunu kurar ve sen de aynısını yazmalısın:
|
|
31
|
+
`states.isWrapper: true` (çerçevesiz, başlıksız, saydam) + `appearance.heightType: 'fill'`
|
|
32
|
+
+ `blocks` x0/w12. Sarmalayıcı olmayan bir kök, kenarlıklı beyaz bir kart olarak çizilir.
|
|
33
|
+
|
|
34
|
+
**5b. Tam ekran kabuk `heightType: 'fill'` ile kurulur (2026-08-28).** Yükseklik ne
|
|
35
|
+
içerikten (`auto`) ne satır sayısından (`fixed`) gelir — ebeveynin bıraktığı yerden gelir:
|
|
36
|
+
ızgara çocuğunda `height:100%`, flex sütununda `flex:1 1 auto`, flex satırında
|
|
37
|
+
`align-self:stretch`. **Kök** düğümde ekranı doldurur (100vh). Yan menünün tam boy olması
|
|
38
|
+
ve slot'un artan alanı kaplaması bununla ifade edilir; `fixed` + tahmini `size.h` ile
|
|
39
|
+
yapılan eski numaraya artık gerek yok.
|
|
40
|
+
|
|
41
|
+
**6. Konum `blocks[]`'ten gelir, prop'tan değil.** `layout-region`'daki `dock` bölgenin
|
|
42
|
+
NEREDE durduğunu değil, BİÇİMİNİ söyler (dikey kolon / yatay şerit / dolgu yüzeyi). Hem
|
|
43
|
+
runtime hem Studio tuvali bölgeleri 12 kolonluk ızgarayla yerleştirir.
|
|
44
|
+
|
|
45
|
+
**7. Mobilde flex YIĞILMAZ.** Runtime'ın mobil kuralı yalnız ızgara özelliklerini ezer.
|
|
46
|
+
Telefonda alt alta düşmesi gereken satırı `layout:'grid'` ile kur; `layout:'flex'` +
|
|
47
|
+
`direction:'row'` telefonda da yan yana kalır (375px'te ölçüldü: ızgara çocukları 375/375px
|
|
48
|
+
alt alta, flex çocukları 49/310px yan yana).
|
|
49
|
+
|
|
50
|
+
**8. Doğrulama tek yerden:** `page_runtime_get`. Layout'un kendi serve URL'i yoktur —
|
|
51
|
+
gerçek çıktıyı, layout'a bağlı bir sayfanın runtime paketinde görürsün (slot yerleşimi
|
|
52
|
+
dahil). Slot kurgusu için ayrıca `page_state` → `layoutSlots`.
|
|
53
|
+
|
|
54
|
+
## Mini tarif — uygulama kabuğu
|
|
55
|
+
|
|
56
|
+
Yan menü + üst şerit + içerik. Studio'nun `getDefaultLayout` iskeletiyle birebir aynı ağaç;
|
|
57
|
+
`layout_create`'in `views` alanına doğrudan verilebilir (`resource: layout-app-shell`).
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
container isWrapper:true heightType:'fill' layout:'flex' direction:'row' gap:'none'
|
|
61
|
+
│ verticalAlign:'stretch' block w12 x0
|
|
62
|
+
├ container heightType:'fill' layout:'flex' direction:'column' block w2 ← yan menü
|
|
63
|
+
│ └ navigations block w12
|
|
64
|
+
└ container heightType:'fill' layout:'flex' direction:'column' block w10 x2
|
|
65
|
+
├ container layout:'flex' direction:'row' states.isSticky block w12 h6
|
|
66
|
+
│ └ breadcrumb block w12 h3
|
|
67
|
+
└ slot heightType:'fill' block w12 y6
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- **Genişlik kolon payıdır:** yan menü `w:2` = 12'de 2 → 1440px tasarım genişliğinde 240px.
|
|
71
|
+
Sabit piksel istiyorsan `appearance.widthPx: 240` (yalnız flex çocuğunda okunur; ızgara
|
|
72
|
+
kabında okunmaz).
|
|
73
|
+
- **Yükseklik `fill`'dir**, `size.h` değil: kök ekranı doldurur, yan menü ve içerik sütunu
|
|
74
|
+
kökün bıraktığını, slot da üst şeritten artanı alır. Slot'un grid satırı ayrıca açık
|
|
75
|
+
uçludur (`y / auto`), yani sayfa uzadıkça slot uzar.
|
|
76
|
+
- **Kaydırma:** `fill` + `states.isScrollable:true` verilen düğüm büyümek yerine İÇİNDE
|
|
77
|
+
kaydırır — uzun içerikte kabuğu sabit tutmanın yolu budur.
|
|
78
|
+
- **MOBİL UYARISI:** bu ağaç `flex` olduğu için telefonda YIĞILMAZ (Kural 7) — 375px'te yan
|
|
79
|
+
menü ince bir şerite iner. Telefonda alt alta düşmesi gereken bir kabuk için kökü
|
|
80
|
+
`layout:'grid'` kur; o zaman `fill`'in flex anlamı (`flex:1 1 auto`) devreye girmez,
|
|
81
|
+
yükseklik yine `blocks` satırlarından gelir. İkisini aynı anda veren bir yol bugün yok.
|
|
82
|
+
- Menü satırlarını biçimlendirme: **bugün mümkün değil.** `navigations`'ın `style` prop'u
|
|
83
|
+
ölüdür (bkz. `component_get('navigations')`).
|
|
84
|
+
|
|
85
|
+
## Mini tarif — menüde aktif satır
|
|
86
|
+
|
|
87
|
+
`navigations.activeHref` boş bırakılırsa menü tarayıcıdaki adrese uyan satırı kendi bulur;
|
|
88
|
+
hiç ayar gerekmez. Elle sürmek istersen satırın `href`'iyle aynı biçimde yaz (ham metin,
|
|
89
|
+
bağlantı nesnesi ya da tam yol).
|
|
90
|
+
|
|
91
|
+
## Kötü / iyi
|
|
92
|
+
|
|
93
|
+
```jsonc
|
|
94
|
+
// KÖTÜ — iki slot: sayfa iki kez çizilir, elementId'ler çakışır
|
|
95
|
+
{ "views": [{ "type": "container", "children": [
|
|
96
|
+
{ "type": "slot" }, { "type": "slot" }
|
|
97
|
+
]}]}
|
|
98
|
+
|
|
99
|
+
// KÖTÜ — sarmalayıcı olmayan, sabit yükseklikli kök: ekranı doldurmaz,
|
|
100
|
+
// kenarlıklı beyaz bir kart olarak çizilir ve yüksekliği tahmini h'den gelir
|
|
101
|
+
{ "type": "container", "states": { "isDroppable": true },
|
|
102
|
+
"appearance": { "heightType": "fixed" },
|
|
103
|
+
"blocks": [{ "breakpoint": 1024, "size": { "w": 7, "h": 83 }, "position": { "x": 2, "y": 0 } }] }
|
|
104
|
+
|
|
105
|
+
// İYİ — sarmalayıcı, tam genişlikte, ekranı dolduran kök (Kural 5a/5b)
|
|
106
|
+
{ "type": "container",
|
|
107
|
+
"states": { "isDroppable": true, "isWrapper": true },
|
|
108
|
+
"appearance": { "heightType": "fill", "marginType": "none", "paddingType": "none" },
|
|
109
|
+
"props": { "layout": { "type": "static", "data": "flex" },
|
|
110
|
+
"direction": { "type": "static", "data": "row" } },
|
|
111
|
+
"blocks": [{ "breakpoint": 1024, "size": { "w": 12, "h": 80 }, "position": { "x": 0, "y": 0 } }] }
|
|
112
|
+
|
|
113
|
+
// KÖTÜ — telefonda 49px'lik yan menü: row flex mobilde yığılmaz (Kural 7)
|
|
114
|
+
// Mobilde alt alta düşmesi gerekiyorsa kökü layout:'grid' kur — bedeli, fill'in
|
|
115
|
+
// flex anlamının devreye girmemesi.
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Sayfayı layout'a bağlama
|
|
119
|
+
|
|
120
|
+
`page_update` ile `layoutId`. Bağlı sayfanın kendi şeması slot'un içine girer; sayfanın
|
|
121
|
+
kök `page_container`'ı korunur.
|
|
122
|
+
|
|
123
|
+
## Bilinen boşluklar (2026-08-28)
|
|
124
|
+
|
|
125
|
+
Bunlar **motorun** eksikleri, senin hatan değil — bir tasarımı karşılayamıyorsan sebebi
|
|
126
|
+
bunlardan biri olabilir:
|
|
127
|
+
|
|
128
|
+
| Eksik | Sonuç |
|
|
129
|
+
|---|---|
|
|
130
|
+
| ~~`heightType` yalnız `auto` \| `fixed`~~ | **28 Ağustos'ta kapandı:** üçüncü değer `fill` (Kural 5b) |
|
|
131
|
+
| ~~Kök kap viewport'a kilitlenmez~~ | **28 Ağustos'ta kapandı:** `fill` verilmiş kök 100vh (Kural 5b) |
|
|
132
|
+
| Flex mobilde yığılmaz | Kural 7 — kabuk bugün bir MASAÜSTÜ kabuğudur |
|
|
133
|
+
| Kırılıma duyarlı prop yok | `direction`/`gap` masaüstü ve mobilde AYNI; yalnız `blocks` ve `appearance.hideOn` kırılım bilir |
|
|
134
|
+
| Mobil çekmece (overlay) yok | Telefonda içeriğin üstüne binen panel kurulamaz |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
**Kaynaklar** — `zeyrek-frontend`: `apps/studio/src/features/layout/layout.functions.ts`
|
|
139
|
+
(`injectPageSchemaIntoSlots`, `getResolvedLayoutViews`), `apps/studio/src/features/page/api/page.mappers.ts`
|
|
140
|
+
(`mapPageLayoutToPreview`), `packages/ng-apps-ui/src/shared/render/render.functions.ts`
|
|
141
|
+
(`resolveNodeBoxStyle` — slot'un `y / auto` satırı, flex çocuk genişliği),
|
|
142
|
+
`packages/ng-apps-ui/src/shared/render/RenderComponent.svelte` (mobil media kuralı),
|
|
143
|
+
`packages/ng-apps-screen-builder/src/flex-layout.ts` (`getFlexChildStyle` — `widthPx` ve
|
|
144
|
+
`heightType:'fill'` dalı; tuval ve runtime aynı fonksiyonu okur),
|
|
145
|
+
`apps/studio/src/features/layout/layout.functions.ts` (`getDefaultLayout` — Kural 5a/5b'deki iskelet),
|
|
146
|
+
`docs/components/layout-composition.md`, `docs/specs/2026-08-28-free-layout-responsive-design.md`,
|
|
147
|
+
`docs/issues.md` ARC-031 / BE-002 / CQ-143.
|
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Microservice Rehberi
|
|
2
2
|
|
|
3
|
+
## Ucun NE İŞE YARADIĞI yazılır (MS-014, block)
|
|
4
|
+
|
|
5
|
+
Servis listesinde `name` ve `slug` var, "bu uç ne işe yarar" yok. Her microservice bir
|
|
6
|
+
`description` taşır; gateway açıklamasız gövdeyi platforma göndermez.
|
|
7
|
+
|
|
8
|
+
```jsonc
|
|
9
|
+
{ "name": "anket-kaydet", "slug": "anket-kaydet", "source": "workflow",
|
|
10
|
+
"description": "Anket başlığı ve sorularını tek çağrıda kaydeder; oluşan anketin id'sini döner." }
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Çağıranın ne elde ettiğini yaz**, kaynağın şeklini değil: `"workflow kaynaklı POST ucu"`
|
|
14
|
+
gövdeden zaten okunuyor. Alan kalıcıdır, çalışma zamanını etkilemez, Studio'da servis formunda
|
|
15
|
+
görünür ve elle düzeltilebilir. Adım düzeyindeki karşılığı `platform_guide('workflow')` §WF-024.
|
|
16
|
+
|
|
17
|
+
## Tuzağı varsa `caution` yaz (opsiyonel, kural yok)
|
|
18
|
+
|
|
19
|
+
`description` ucun ne işe yaradığını söyler; `caution` **çağıranın bilmezse yanılacağı şeyi**
|
|
20
|
+
söyler. Hiçbir kural zorlamaz — ucun gerçek bir tuzağı yoksa alanı hiç yazma.
|
|
21
|
+
|
|
22
|
+
- **Anonim uç** — "bu uç auth istemiyor; kimlik kapsaması yalnız bağlı middleware'de yapılır"
|
|
23
|
+
- **Sayfalama yok** — "limit uygulanmaz, tablo büyüdükçe yanıt büyür"
|
|
24
|
+
- **Atomik değil** — "çok adımlı yazım middleware'siz atomik değil; yarıda kalırsa kısmi yazar"
|
|
25
|
+
- **Sessiz boş dönüş** — "filtre eşleşmezse 404 değil boş dizi döner"
|
|
26
|
+
|
|
27
|
+
```jsonc
|
|
28
|
+
{ "name": "urun-listesi", "httpMethod": "Get",
|
|
29
|
+
"description": "Aktif ürünleri ada göre sıralı döner.",
|
|
30
|
+
"caution": "Sayfalama yok; katalog büyüdükçe yanıt boyutu doğrusal büyür." }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Doldurma metin yazma: `"bu uç veri döner, dikkat"` hiçbir şey eklemez. Alan inerttir, Studio'da
|
|
34
|
+
servis formunda görünür ve `microservice_get` outline görünümünde geri okunur.
|
|
35
|
+
|
|
3
36
|
## Karar kuralları
|
|
4
37
|
|
|
5
38
|
1. **Kaynak seçimi sırası: `table` → `view` → `workflow` → `query`.**
|
|
@@ -297,6 +330,8 @@ kabul etti. Ayrıntı ve doğru rol kapısı tarifi: `platform_guide('middleware
|
|
|
297
330
|
|
|
298
331
|
## Sık hatalar
|
|
299
332
|
|
|
333
|
+
- `description` boş (MS-014, block — uç listesinde ne işe yaradığı okunamaz)
|
|
334
|
+
|
|
300
335
|
- `selectFields: []` + join → tüm alanlar + katlanmış maliyet (MS-001, block)
|
|
301
336
|
- `operation: 'list'` → geçersiz; `get` + `isSingle: false` kullanın (PLT-006)
|
|
302
337
|
- Üst seviye `entityId` eksik (MS-004, warn)
|
|
@@ -13,6 +13,12 @@ kavramı olmayan bir üründe auth middleware'i, ayar tablosu olmayan bir ürün
|
|
|
13
13
|
kur** — giriş sayfası + korunacak her uç ve her sayfa `middlewareIds`'e bağlı. Kısmi koruma,
|
|
14
14
|
sistemi güvenli gösterip açık bırakır.
|
|
15
15
|
|
|
16
|
+
## Açıklama zorunlu
|
|
17
|
+
|
|
18
|
+
Middleware de bir workflow'dur: her adımı ve middleware'in kendisi `description` taşımak
|
|
19
|
+
zorundadır (**WF-024**, block). Guard'larda bu özellikle önemli — "neden reddediyor" sorusunun
|
|
20
|
+
cevabı adımın ifadesinde gizlidir. Ayrıntı ve yazım ölçütü: `platform_guide('workflow')` §WF-024.
|
|
21
|
+
|
|
16
22
|
## Çalışma modeli — bunları bilmeden middleware yazma
|
|
17
23
|
|
|
18
24
|
1. **Sıra ada bağlıdır.** Bağlı middleware'ler **ada göre alfabetik** koşar — uç yolunda
|
|
@@ -54,6 +60,10 @@ sistemi güvenli gösterip açık bırakır.
|
|
|
54
60
|
6. Middleware transaction içinde koşar: hedef başarısız olursa middleware'in yazdıkları da
|
|
55
61
|
geri alınır (microservice yolu).
|
|
56
62
|
|
|
63
|
+
> Gateway middleware tanımına da hata yolunu otomatik ekler (start adımına `global-error` dalı +
|
|
64
|
+
> 500 dönen `gatewayOnError` response adımı). Guard middleware'i patladığında ham exception
|
|
65
|
+
> yerine denetimli bir gövde döner. Ayrıntı: `platform_guide('workflow')` → "Hata yolu".
|
|
66
|
+
|
|
57
67
|
## Yalnız middleware'de geçerli step'ler
|
|
58
68
|
|
|
59
69
|
| Step | Ne yapar | Kritik alanlar |
|
|
@@ -155,10 +155,13 @@ olaylarında **boştur** (ölçüm 2026-08-25, kaynak okuması: `flow-machine.ts
|
|
|
155
155
|
|
|
156
156
|
Pratik kural: **`payload` anahtarları aynı zamanda prop adlarıdır; akışta `self.<anahtar>` ile
|
|
157
157
|
oku** (ya da `value { type:'property', ref:{ source:'component', element, property } }` ile).
|
|
158
|
-
`event.data` yalnız `params` ile gelen olaylarda dolar —
|
|
159
|
-
|
|
160
|
-
`
|
|
161
|
-
|
|
158
|
+
`event.data` yalnız `params` ile gelen olaylarda dolar — `html` komponentinin `fireEvent`
|
|
159
|
+
aksiyonları (`platform_guide('html')`) ve **2026-08-27 kuşağı komponentler** (`eventcalendar` ·
|
|
160
|
+
`importwizard` · `kanban` · `signature` · `stepper`): bu kuşak `params`'ı da gönderir, yani
|
|
161
|
+
yükleri `event.data.<anahtar>` ile okunur; `kanban` ve `eventcalendar` yükü `self`'e HİÇ
|
|
162
|
+
yazmaz (`component_get` çıktısındaki sözleşme notları ayrımı söyler). Sessiz hata örneği:
|
|
163
|
+
sayfalama akışını `event.data.limit`'e bağlarsan sorgu `LIMIT null OFFSET null` ile koşar ve
|
|
164
|
+
**tüm satırları** döndürür; hata yok, sayfalama yok.
|
|
162
165
|
|
|
163
166
|
| Komponent · olay | Yük (aynı zamanda prop adları) | Akışta nereden okunur |
|
|
164
167
|
|---|---|---|
|
|
@@ -175,7 +178,12 @@ döndürür; hata yok, sayfalama yok.
|
|
|
175
178
|
| `tabs.tab` | `{ selected }` | `self.selected` |
|
|
176
179
|
| `chart.dataPointClick` | `{ name, seriesName, value, dataIndex, row }` | `self.row` (kaynak satırın tamamı) |
|
|
177
180
|
| `repeater.change` | `{ selectedIndex }` | `item` / `Row` satır bağlamı |
|
|
178
|
-
| `html` aksiyonu (`fireEvent`) | ikinci argüman | **`event.data`**
|
|
181
|
+
| `html` aksiyonu (`fireEvent`) | ikinci argüman | **`event.data`** |
|
|
182
|
+
| `kanban.cardMove` | `{ id, row, from, to }` | **`event.data`** (self'e yazılmaz) |
|
|
183
|
+
| `eventcalendar.eventMove` | `{ id, row, start, end, previousStart, previousEnd }` | **`event.data`** (self'e yazılmaz) |
|
|
184
|
+
| `importwizard.complete` | `{ rows, errors, fileName, total }` | `self.rows`… ya da `event.data` |
|
|
185
|
+
| `stepper.stepChange` | `{ currentStep, previous }` | `self.currentStep` ya da `event.data` |
|
|
186
|
+
| `signature.change` | `{ value }` | `self.value` ya da `event.data.value` |
|
|
179
187
|
|
|
180
188
|
Adım gövdesi `StepManagementSchema`: `{ stepId, name, type, kind, args, branches }`.
|
|
181
189
|
**`branches[].nextStepId` bir GUID değil, hedef adımın `stepId` STRING'idir**
|
|
@@ -462,8 +470,8 @@ gateway **PAGE-011 / LAY-004** ile istek gitmeden reddeder.
|
|
|
462
470
|
|
|
463
471
|
| slug | Olaylar | Ayırt edici prop'lar |
|
|
464
472
|
|---|---|---|
|
|
465
|
-
| `table` | 8: `mount · pagination · filter · sort · select · selectionChange · doubleClick · rowChange` | `rowSelection` (none/single/multi), `selectedRow`/`selectedRows`, `filterMode` (panel/hide/row), `filters`, `showSort`, `sortField`/`sortType`, `isEditable`, `showColumnMenu`, `actions`, `emptyIcon/emptyText/emptyActions`, `style` (root/thead/th/tr/td) |
|
|
466
|
-
| `datatable` | 1: `mount` | `title`, `locale` (tr/en), `tableStyle` (zebra/plain), kolonlarda `isFilterable`/`isSortable`/`colorMap
|
|
473
|
+
| `table` | 8: `mount · pagination · filter · sort · select · selectionChange · doubleClick · rowChange` | `rowSelection` (none/single/multi), `selectedRow`/`selectedRows`, `filterMode` (panel/hide/row), `filters`, `showSort`, `sortField`/`sortType`, `isEditable`, `showColumnMenu`, `actions`, `emptyIcon/emptyText/emptyActions`, `style` (root/thead/th/tr/td), satır bazlı `_columns`/`_actions` ezmesi (Kural 1e-2) |
|
|
474
|
+
| `datatable` | 1: `mount` | `title`, `locale` (tr/en), `tableStyle` (zebra/plain — satır zemini varyantı), **`style`** (parça bazlı CSS: `&__toolbar`/`&__title`/`&__head`/`&__th`/`&__body`/`&__row`/`&__td`), kolonlarda `isFilterable`/`isSortable`/`colorMap` (statik)/`prefix`/`suffix`/`precision`/`thousands`/`dateFormat`/`imageShape`/**`progress`** kolon tipi, `filters`/`sortField`/`sortType` (hidden), `actions` |
|
|
467
475
|
|
|
468
476
|
**`datatable`'ın tek olay yayması eksiklik değil, tasarım.** Filtre paneli, sıralama ve sayfalama
|
|
469
477
|
komponentin İÇİNDE: kullanıcı süzdüğünde/sıraladığında/sayfa değiştirdiğinde `datatable` önce
|
|
@@ -487,8 +495,18 @@ olarak gider ve ancak `enableDynamicFilters: true` olan bir uçta karşılık bu
|
|
|
487
495
|
düzenleme (`isEditable`) ve boş-durum aksiyonları verir.
|
|
488
496
|
- *Satır seçimi / çift tıkla forma geçiş / hücre düzenleme gerekiyor mu?* → **`table`**; bunlar
|
|
489
497
|
`datatable`'da yok.
|
|
490
|
-
- *
|
|
491
|
-
mu?* → **`datatable`**;
|
|
498
|
+
- *Sayı/tarih/görsel biçimlendirme (para birimi öneki, ondalık, binlik ayracı, tarih formatı,
|
|
499
|
+
avatar şekli) gerekiyor mu?* → **`datatable`**; `prefix`/`suffix`/`precision`/`thousands`/
|
|
500
|
+
`dateFormat`/`imageShape` yalnız onun kolon şemasındadır, `table`'da yoktur.
|
|
501
|
+
- *Renk gerekiyor mu?* → **ikisi de yapar ama farklı şeyi.** `datatable`'ın `colorMap`'i
|
|
502
|
+
**statiktir**: hücrenin kendi değerine bakar; satır verisinde rengi taşırsan (`{ label, color }`)
|
|
503
|
+
dinamik de olur. `table` ise satır bazında **koşullu** renklendirir (`_columns` ezmesi,
|
|
504
|
+
Kural 1e-2). İkisinin de `style` prop'u vardır (datatable'ınki 2026-08-27'de eklendi), yani
|
|
505
|
+
renk uğruna stilden vazgeçmen gerekmiyor.
|
|
506
|
+
- *Hücrenin içinde ilerleme çubuğu ya da yüzde halkası mı istiyorsun?* → **`datatable`**, kolon
|
|
507
|
+
tipi `progress`. `progressVariant` çubuk/halka, `thresholds` yüzdeye göre renk, `captionField`
|
|
508
|
+
ise BAŞKA bir satır alanını çubuğun üstüne yazar — ad ve çubuk tek hücrede durur. `table`'da
|
|
509
|
+
böyle bir kolon tipi yok.
|
|
492
510
|
|
|
493
511
|
**Bu, katalogdaki her komponent için geçerlidir:** hangi komponenti kullanacağını
|
|
494
512
|
`component_list`/`component_get` ile keşfedip kendin seçersin, rehberdeki örnekler tercih değil
|
|
@@ -504,6 +522,48 @@ görünür, kullanıcı süzer, hiçbir şey olmaz. Ya süzme arayüzünü kapat
|
|
|
504
522
|
> eklersen elementId aynı olsa bile o bağ kopar ve flow'u yeniden kaydetmen gerekir. Tipi
|
|
505
523
|
> baştan doğru seç.
|
|
506
524
|
|
|
525
|
+
## Kural 1e-2 — Satır bazlı renk `table`'da: `_columns` ezmesi
|
|
526
|
+
|
|
527
|
+
`table` her hücreyi çizerken **satırın kendi `_columns` sözlüğünü** kolon tanımıyla birleştirir
|
|
528
|
+
(`column-view.ts` `resolveColumn` → `Table.svelte:1207` `<TableCell {column}>`). Yani bir satır
|
|
529
|
+
kendi `appearance`/`isBold`'unu dayatabilir; renk, satırın herhangi bir alanından hesaplanır:
|
|
530
|
+
|
|
531
|
+
```jsonc
|
|
532
|
+
// setter: rows'a yazmadan önce satırları zenginleştir
|
|
533
|
+
{ "type": "change", "element": "tbl", "property": "rows", "source": "component",
|
|
534
|
+
"mapping": { "source": "", "mappings": {} },
|
|
535
|
+
"value": { "type": "code", "ref": "(Steps.listele?.data || []).map(function (r) { return Object.assign({}, r, { _columns: [null, null, null, null, { appearance: r.durum === 'İptal' ? 'error' : 'success' }] }); })" } }
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
**`_columns` bir DİZİDİR ve konum kolonun `columns` dizisindeki İNDEKSİDİR** — yukarıdaki dört
|
|
539
|
+
`null`, hedef kolon 5. sırada (indeks 4) olduğu için var; ezilmeyen kolonlar atlanır.
|
|
540
|
+
`buildViewColumns` `index`'i `isHidden` kolonlar elenmeden ÖNCE atar, yani **görünür sıra değil
|
|
541
|
+
tanım sırası**: gizli bir `id` kolonu indeksleri kaydırmaz, kullanıcının kolon menüsünden kolon
|
|
542
|
+
gizlemesi de kaydırmaz (`hideColumns` `buildViewColumns`'dan SONRA uygulanır).
|
|
543
|
+
Aynı kalıp `_actions[i]` ile satır bazlı **aksiyon** ezmesinde de çalışır (tek bir satırda "Sil"i
|
|
544
|
+
`{ "isHidden": true }` ile gizlemek gibi).
|
|
545
|
+
|
|
546
|
+
> Sözleşme frontend'de belgeli ve testli: `column-view.ts` `resolveColumn` + `column-view.spec.ts`
|
|
547
|
+
> (`applies the row's own override fields on top`, `matches the override by the column definition
|
|
548
|
+
> order`). Ezilebilen alanlar `appearance` · `options` · `isBold`. CSV export'u da bu ezmeyi
|
|
549
|
+
> uygular, yani ekranda gördüğün renk dışa aktarımda da tutarlıdır.
|
|
550
|
+
|
|
551
|
+
İki renk yolu var:
|
|
552
|
+
|
|
553
|
+
| istenen | yol |
|
|
554
|
+
|---|---|
|
|
555
|
+
| Hazır palet | `tag` kolonu + `appearance`: `primary · info · success · warning · error`; `_columns` ile satır satır değişir |
|
|
556
|
+
| Serbest hex | `tags` kolonu + satır verisinde `[{ "value": "kritik", "color": "#da1e28" }]` |
|
|
557
|
+
|
|
558
|
+
**`style` yazarken iki ayrı kural geçerli:** `style.root` emotion'dır (camelCase çalışır, iç
|
|
559
|
+
seçici yazılabilir); `style.thead|th|tr|td` ise satır içi `style` attribute'una basılır
|
|
560
|
+
(`toStyleString` anahtarı olduğu gibi yazar) — bu dördünde **kebab-case zorunludur**, `fontSize`
|
|
561
|
+
sessizce düşer, `font-size` çalışır.
|
|
562
|
+
|
|
563
|
+
> `datatable`'da `style` prop'u **yoktur** (ne şemada ne `.svelte`'de) ve `_columns` ezmesi de
|
|
564
|
+
> yoktur; `tableStyle` CSS değil `zebra`/`plain` varyantıdır. Tabloya CSS ya da koşullu satır
|
|
565
|
+
> rengi gerekiyorsa **`table`**.
|
|
566
|
+
|
|
507
567
|
## Kural 1f — Koordinat modeli ve layout-slot yerleşimi
|
|
508
568
|
|
|
509
569
|
Izgara kapsayıcıya görelidir ve **bir satır ≈ 10px**'tir; `w` 12 kolon. Her komponentin
|
|
@@ -616,7 +676,11 @@ Dört şeyi bilmeden flex kurma:
|
|
|
616
676
|
3. **Flex kapsayıcının yüksekliği içeriğine oturur.** `verticalAlign`'ın dağıtacak boşluğu
|
|
617
677
|
olması için `appearance.heightType: 'fixed'` + `size.h` vermelisin. **İstisna
|
|
618
678
|
`page_container`**: `states.isWrapper: true` olduğu için zaten `min-height: 80vh` alır
|
|
619
|
-
(Kural 1f), dolayısıyla dikey hizalama onda kutu boyu vermeden de çalışır.
|
|
679
|
+
(Kural 1f), dolayısıyla dikey hizalama onda kutu boyu vermeden de çalışır. Üçüncü yol
|
|
680
|
+
`heightType: 'fill'` (2026-08-28): çocuk kabın bıraktığı yeri alır — sütunda `flex: 1 1 auto`,
|
|
681
|
+
satırda `align-self: stretch` — yani "şu kutu kalan alanı kaplasın" artık tahmini bir `h`
|
|
682
|
+
olmadan yazılabilir. Tam ekran kabuk kurmanın yolu da budur: `platform_guide('layout')`
|
|
683
|
+
Kural 5b.
|
|
620
684
|
4. **`layout: 'grid'` iken diğer altı prop ÖLÜDÜR.** Katalogda tanımlı oldukları için
|
|
621
685
|
doğrulama duvarı susar, renderer da okumaz: ızgara kapsayıcıya `direction: 'column'` yazmak
|
|
622
686
|
sessizce hiçbir şey yapmaz. Gateway bunu **PAGE-015 (uyarı)** ile söyler.
|
|
@@ -799,11 +863,11 @@ yapmazsan ekran "platform mavisi" görünür; marka rengine çevirmek tek satır
|
|
|
799
863
|
| Küme | Değişkenler |
|
|
800
864
|
|---|---|
|
|
801
865
|
| Vurgu | `--ng-accent-primary` · `--ng-accent-hover` · `--ng-accent-muted` · `--ng-accent-subtle` |
|
|
802
|
-
| Zemin | `--ng-background-canvas` · `--ng-background-surface` · `--ng-background-surface-alt` · `--ng-background-readonly` · `--ng-background-disabled` · `--ng-background-overlay` |
|
|
866
|
+
| Zemin | `--ng-background-canvas` · `--ng-background-surface` · `--ng-background-surface-alt` · `--ng-background-readonly` · `--ng-background-disabled` · `--ng-background-overlay` · `--ng-background-inverse` (zıt zemin: aydınlıkta koyu, karanlıkta açık) |
|
|
803
867
|
| Metin | `--ng-text-primary` · `--ng-text-secondary` · `--ng-text-disabled` |
|
|
804
|
-
| Çerçeve / köşe / gölge | `--ng-border-primary` · `--ng-border-secondary` · `--ng-rounded` · `--ng-shadow-none/sm/md/lg` |
|
|
868
|
+
| Çerçeve / köşe / gölge | `--ng-border-primary` · `--ng-border-secondary` · `--ng-rounded` (= `--ng-radius-md`) · `--ng-radius-sm/lg/pill` (temanın köşe kademeleri; sm/lg `--ng-rounded`'ın oranı) · `--ng-shadow-none/sm/md/lg` |
|
|
805
869
|
| Tipografi | `--ng-font` · `--ng-font-size` · `--ng-font-weight` |
|
|
806
|
-
| Boşluk | `--ng-spacing` · `--ng-spacing-small` · `--ng-spacing-large` |
|
|
870
|
+
| Boşluk | `--ng-spacing` · `--ng-spacing-xs/sm/md/lg/xl` (kademeler; builder'ın gap preset'leri sm/md/lg'yi okur — custom properties'te de bunları kullan) · `--ng-spacing-small` · `--ng-spacing-large` (eski adlar) |
|
|
807
871
|
| Durum | `--ng-status-success` · `--ng-status-warning` · `--ng-status-error` · `--ng-status-info` |
|
|
808
872
|
| Form (girdilerin tamamı) | `--ng-form-color` / `-disabled` / `-placeholder` / `-invalid` · `--ng-form-background` / `-disabled` / `-readonly` · `--ng-form-border` / `-focus` / `-disabled` / `-invalid` · `--ng-form-radius-sm/md/lg` · `--ng-form-height-sm/md/lg` · `--ng-form-padding-x-sm/md/lg` · `--ng-form-font-family` · `--ng-form-font-size-sm/md/lg` · `--ng-form-label-color` / `-font-weight` / `-font-size-*` / `-margin-bottom-*` · `--ng-form-default-size` |
|
|
809
873
|
|
|
@@ -879,6 +943,47 @@ kaplar — yardım metni opsiyonelse bunu hesaba kat.
|
|
|
879
943
|
> `input` `display:none` olduğu için klavye odağı görünmez. Klavyeyle gezilecek ekranlarda
|
|
880
944
|
> `&__item-input:focus-visible + &__item-text` için de bir dış çizgi tanımlayın.
|
|
881
945
|
|
|
946
|
+
### 1h-4 — Stilin ÜÇ kanalı ve efektif sonucu okuma
|
|
947
|
+
|
|
948
|
+
`style` tek kanal değildir; bir düğümün ekranda aldığı görünüm üç ayrı yerden gelir ve hangisini
|
|
949
|
+
kullanacağını bilmek gereksiz CSS yazmayı önler:
|
|
950
|
+
|
|
951
|
+
| Kanal | Ne için | Nereden yazılır |
|
|
952
|
+
|---|---|---|
|
|
953
|
+
| `appearance` (düğüm alanı) | dış/iç boşluk, genişlik-yükseklik, z-index, opaklık, taşma, **sarmalayıcının** zemini/çerçevesi/gölgesi | `page_schema_patch` ile düğümün `appearance` alanı |
|
|
954
|
+
| Şema prop'ları | yerleşim (`layout`/`direction`/`gap`/`gridColumns`), semantik renkler, boyut (`size`) | `replace_prop` |
|
|
955
|
+
| `style` prop'u | geri kalan her şey; iç seçiciler, pseudo-class, `--ng-*` token ezme | `replace_prop` / `set_binding` |
|
|
956
|
+
|
|
957
|
+
**Dikkat — `appearance` kutunun İÇİNİ boyamaz.** Yüzey stili düğüm sarmalayıcısına
|
|
958
|
+
(`.ng-app__node-item`) yazılır; `box` gibi kendi zeminini çizen bir komponentin içi bundan
|
|
959
|
+
etkilenmez. Tipli stil modeline göçen komponentler bu boşluğu kapatır: `box` (ve emekli
|
|
960
|
+
takma adı `aibox`: `surfaceStyle` · `typographyStyle` · `hoverStyle` · `sectionStyle`),
|
|
961
|
+
`container` (aynı üçlü + `headerStyle`), `text` (`typographyStyle` · `surfaceStyle`;
|
|
962
|
+
hover/bölge yok), `statcard` (üçlü + `valueStyle`) ve `icon` (yalnız `hoverStyle`;
|
|
963
|
+
yüzey/tipografi kararları çip ve boyut prop'larında) — `component_get('<slug>')` sözleşmeyi
|
|
964
|
+
verir, göçen komponentlerin listesi `TYPED_STYLE_COMPONENTS` kaydıdır.
|
|
965
|
+
|
|
966
|
+
**Stil yazma SIRASI (tipli gruplu komponentlerde):** önce tipli gruplar — kararın alanı
|
|
967
|
+
grupta varsa ORAYA yaz; grubun karşılamadığı her CSS kararı `style` kanalına (studio'daki
|
|
968
|
+
"custom properties" alanının veri kanalı budur). Tipli alan dururken aynı özelliği `style`'a
|
|
969
|
+
yazma: `style` kazanır ama panel alanı etkisiz görünür ve tasarımcı yanılır.
|
|
970
|
+
Bileşenin BİLDİRDİĞİ parçalara (`&__header`, `&__body` …) da yazabilirsin: `style: { '&__header':
|
|
971
|
+
{ minHeight: '0' } }` — studio'daki "custom properties" bölümünün Hedef seçicisi bunun panel
|
|
972
|
+
yüzüdür. Boyut alanlarında METİN `'0'` gerçek seçimdir (bileşenin taban dolgusunu/yüksekliğini
|
|
973
|
+
kaldırır); sayı `0` "seçilmedi" sayılır.
|
|
974
|
+
**Katman sırası (K4):** tema `componentStyles` → `$preset` TABANDIR (varsayılan verir);
|
|
975
|
+
tipli grup alanları temayı alan alan EZER; düğümün serbest `style`'ı en üstte SON SÖZDÜR.
|
|
976
|
+
Boyut alanları CSS uzunluğu metnidir (`'18px'`, `'1.2em'`); `radius` tek alanda preset
|
|
977
|
+
anahtarı YA DA CSS uzunluğu kabul eder.
|
|
978
|
+
**Tema preset'i bağlamak:** `style: { $preset: 'card' }` (adlar `project_get` → `theme.presets`;
|
|
979
|
+
`componentStyles[type]` referanssız uygulanır). Bir alanı temaya bırakmak = o alanı hiç yazmamak;
|
|
980
|
+
studio'da bunun adı "Temaya dön"dür. Tema değerini gruba kopyalama — bağ kopar, tema değişince izlemez.
|
|
981
|
+
|
|
982
|
+
**Efektif stili okumak:** `page_runtime_get(resolveStyles: true)` her düğüme `resolvedStyle`
|
|
983
|
+
(kaskad çözülmüş birleşik stil) ve `styleLayers` (`{ typedGroups, componentStyle, preset, own }`)
|
|
984
|
+
ekler. "Bu kutu neden böyle görünüyor / benim yazdığım stil neden görünmüyor" sorusunun cevabı
|
|
985
|
+
budur — bir preset ya da `componentStyles` sizin değerinizi eziyorsa `styleLayers` söyler.
|
|
986
|
+
|
|
882
987
|
## Kural 2 — Şema değişikliği `page_update` ile YAPILMAZ
|
|
883
988
|
|
|
884
989
|
İki ayrı yazma yolu vardır ve karıştırılmaz:
|
|
@@ -1337,6 +1442,10 @@ bağlanır, filtre `source:'expression'` + `value:'user.id'` olur.
|
|
|
1337
1442
|
| Kimlik alanları | `email` · `password` |
|
|
1338
1443
|
| Dosya | `fileUpload` (dropzone) · `fileInput` · `filebutton` · `file-viewer` |
|
|
1339
1444
|
| Liste | `table` · `datatable` (Kural 1e) · `repeater` (kart listesi VE dinamik form — Kural 1g) · `tree-view` (hiyerarşi; `isSearchable` ile istemci içi arama) · `timeline` (tarihli olay akışı) |
|
|
1445
|
+
| Pano / takvim | `kanban` (durum kolonlarında sürüklenebilir kartlar; taşıma `cardMove` ile `event.data`'dan okunur, kalıcılık AKIŞIN işidir) · `eventcalendar` (ay/hafta/gün; veri `rows`'a akışla yazılır, `monthChange` tazeleme kancasıdır, `month` 1-tabanlı) |
|
|
1446
|
+
| Kayıt detayı | `detailview` — tek kaydı etiket/değer ızgarası basar (alan başına format, iç içe alan yolu); OLAY YAYMAZ, tıklanabilirlik gerekiyorsa kapsayıcıya sar |
|
|
1447
|
+
| Adımlı akış | `stepper` — tabs gibi her adım kendi droppable görünümünü taşır; ileri gidiş `canProceed` kapısıyla (`{{ form1.isValid === true }}` biçimi), adım `currentStep` setter'ıyla sürülür |
|
|
1448
|
+
| İmza / CSV içe aktarma | `signature` (değer PNG data-URL ya da ham SVG; `change` setter'lı) · `importwizard` (3 adımlı sihirbaz; temiz+hatalı satırlar `complete` yükünde — satırları uca yazmak akışın işidir) |
|
|
1340
1449
|
| Sayfalama (tablo dışı listeler için) | `pagination` — offset/limit yazar, `self.offset`/`self.limit` ile sorguya bağlanır |
|
|
1341
1450
|
| Grafik / KPI | `chart` (ECharts: bar·line·area·pie·donut + waterfall/histogram/pareto/bullet/tornado/radial) · `statcard` (tek KPI + trend + sparkline) · `progress` |
|
|
1342
1451
|
| Kapsayıcı / düzen | `container` (ızgara ya da flex — Kural 1f-2) · `form` (düzen prop'u YOK, çocuklar ızgara koordinatıyla dizilir) · `tabs` · `collapsible-container` · `accordion` |
|
|
@@ -1344,7 +1453,7 @@ bağlanır, filtre `source:'expression'` + `value:'user.id'` olur.
|
|
|
1344
1453
|
| Katman | `modal` · `drawer` (şema kökünde — Kural 1d) |
|
|
1345
1454
|
| Geri bildirim | `notification` (sayfa içi kutu) · `toast` (geçici bildirim; isOpen yükselen kenarı) · `form-validation-errors` · `confirm-bar` · `result` (işlem sonucu ekranı) · `emptystate` (boş liste; ikon YA DA görsel + aksiyon düğmeleri) |
|
|
1346
1455
|
| Gezinme / başlık | `breadcrumb` · `breadcrumb-title` (başlık + daralan iz) · `page-header` · `header` · `sidebar` · `auth-layout` · `language-selector` (dil değiştirici — çoklu dil kurulumu `platform_guide('translation')`) |
|
|
1347
|
-
| Sunum | `text` · `html` · `image` · `video` · `icon` · `qrcode` · `tags` · `avatar` · `badge` (sayaç “99+” / nokta / durum metni) · `carousel` · `iframe` (sandbox’lı gömme) · `jsonviewer` (katlanabilir JSON; JSON METNİNİ de parse eder) · `audit-basic` · `audit-history` |
|
|
1456
|
+
| Sunum | `text` · `html` · `image` · `video` · `icon` (Material/Phosphor glif; çip + rozet + spin + click/href) · `qrcode` · `tags` · `avatar` · `badge` (sayaç “99+” / nokta / durum metni) · `carousel` · `iframe` (sandbox’lı gömme) · `jsonviewer` (katlanabilir JSON; JSON METNİNİ de parse eder) · `audit-basic` · `audit-history` |
|
|
1348
1457
|
|
|
1349
1458
|
> **`chart` komponenti VAR (2026-08-25 ölçümü) — bu satır düzeltildi.** Rehberin önceki hâli
|
|
1350
1459
|
> "grafik komponenti yoktur, `html` + `loadScript` kullanın" diyordu; `component_list` çıktısında
|
|
@@ -1515,6 +1624,15 @@ micro-op hattına dönün.
|
|
|
1515
1624
|
style + Kural 1h deseniyle yeniden kurulunca kart 400px'e ve ortaya oturdu. Gateway bunu **PAGE-014 (uyarı)** ile
|
|
1516
1625
|
yanıtın `layoutWarnings` alanında bildirir. **Düzeni `style` ile kurmaya hiç kalkışma:**
|
|
1517
1626
|
ortalama/yaslama/aralık işi kapsayıcının kendi `layout: 'flex'` prop grubuna aittir (Kural 1f-2).
|
|
1627
|
+
- **Menüde "bulunduğun sayfa" vurgusu: `sidebar`/`layout`'ta `activeHref`, `header`'da
|
|
1628
|
+
`activeNavHref`.** `sidebar` ve `layout` menü ağacını ortak renderer'la çizer: `activeHref` boşken
|
|
1629
|
+
tarayıcıdaki adrese uyan satırı kendileri vurgular, prop yazılırsa o satır vurgulanır (akıştan
|
|
1630
|
+
setter ile de yazılabilir). Eşleşmenin iki tarafı da `getHref`'ten geçtiği için satırın ham
|
|
1631
|
+
`href`'ini, bağlantı seçicisinin nesnesini ya da tam yolu yazmak aynı satırı gösterir. `header`
|
|
1632
|
+
yalnız prop'a bakar (adrese bakmaz), `qm-header` ise yalnız adrese bakar (prop'u yoktur). Menü
|
|
1633
|
+
satırları `style`'ın `&__navigations*` parçalarıyla biçimlendirilemez ve menü olay yaymaz —
|
|
1634
|
+
vurgu dışında bir "seçili satır" davranışı kurulamaz. Ayrıntı:
|
|
1635
|
+
`component_get('sidebar' | 'layout' | 'header' | 'qm-header')`.
|
|
1518
1636
|
- **`isHidden` olmayan komponent listesine `dropdown` da dahildir** (`text`, `multi-select`,
|
|
1519
1637
|
`dropdown`). Koşullu göstermek için `isHidden`'ı olan bir `container`'a sar (Kural 1g).
|
|
1520
1638
|
- **Ana sayfayı TAŞIYAMAZSIN — `application_update.homepageId` kalıcı DEĞİL ve yanıtı yanıltıcı.**
|
|
@@ -2,6 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
Buradaki kurallar gerçek projelerde (qm, hr) **deneyerek doğrulandı**.
|
|
4
4
|
|
|
5
|
+
## Her adım NE İŞE YARADIĞINI yazar (WF-024, block)
|
|
6
|
+
|
|
7
|
+
Studio kanvası adımın **adını** gösterir, ne yaptığını göstermez. 11 satırlık bir `code` adımının
|
|
8
|
+
işini anlamak için ifadeyi satır satır okumak gerekir — akışı devralan geliştirici için bu, her
|
|
9
|
+
seferinde tersine mühendisliktir. Bu yüzden **her adım** ve **akışın kendisi** bir `description`
|
|
10
|
+
taşır; gateway açıklamasız gövdeyi platforma göndermez.
|
|
11
|
+
|
|
12
|
+
```jsonc
|
|
13
|
+
{ "name": "anket-kaydet", "type": "workflow",
|
|
14
|
+
"description": "Anket başlığını ve sorularını tek çağrıda kaydeder.",
|
|
15
|
+
"steps": [
|
|
16
|
+
{ "id": "<GUID>", "name": "createSurvey", "type": "table",
|
|
17
|
+
"description": "Anket başlığını surveys'e yazar; oluşan id sonraki adımlarda kullanılır.",
|
|
18
|
+
"rule": { … } }
|
|
19
|
+
] }
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- **Mekanizmayı değil NİYETİ yaz.** `"assignments tablosunu status='Aktif' ile okur"` koddan zaten
|
|
23
|
+
okunuyor ve hiçbir şey eklemiyor. `"kullanıcının asal pozisyonunu seçer; birden çok aktif atama
|
|
24
|
+
varsa is_primary olanı tercih eder, o da yoksa ilkini alır"` — yazılmadıkça hiçbir yerde
|
|
25
|
+
bulunmayan bilgi budur.
|
|
26
|
+
- **`start` ve `end` muaftır** (kural onları hiç sormaz): ikisi de yapısal işaretçi, açıklanacak
|
|
27
|
+
davranışları yok.
|
|
28
|
+
- **Gateway'in enjekte ettiği `gatewayOnError` adımı kendi açıklamasını taşır** — o adımı sen
|
|
29
|
+
yazmadığın için kural seni onun yüzünden durdurmaz.
|
|
30
|
+
- Alan **kalıcıdır ve çalışma zamanını etkilemez**: motor okumaz, yalnız Studio'da kanvas
|
|
31
|
+
düğümünde ⓘ ikonu + popover olarak ve adım panelinde düzenlenebilir metin olarak görünür.
|
|
32
|
+
İnsan da düzeltebilir.
|
|
33
|
+
- `preview` bu kuralın dışındadır — kaydedilmeyen bir denemede açıklama aranmaz.
|
|
34
|
+
- Aynı disiplin ucun kendisi için de geçerli: `platform_guide('microservice')` §MS-014.
|
|
35
|
+
|
|
36
|
+
## Tuzağı varsa `caution` yaz (opsiyonel, kural yok)
|
|
37
|
+
|
|
38
|
+
`description` "bu adım ne işe yarar" sorusunu cevaplar. `caution` bambaşka bir soruyu cevaplar:
|
|
39
|
+
**"burada neye dikkat etmek gerekiyor."** İkisi ayrı alandır çünkü tek metne sıkıştırıldıklarında
|
|
40
|
+
ikisi de okunmaz olur — açıklama uyarı listesine döner, uyarı da açıklamanın içinde kaybolur.
|
|
41
|
+
|
|
42
|
+
**Bu alanı hiçbir kural zorlamaz ve doldurma metin kabul edilmez.** Adımın gerçek bir tuzağı
|
|
43
|
+
yoksa alanı hiç yazma. Yazmaya değer olanlar:
|
|
44
|
+
|
|
45
|
+
- **İdempotent değil** — "iki kez çalışırsa çift atama yazar; çağıran tarafta koruma yok"
|
|
46
|
+
- **Kapsam bu adımda değil** — "tenant filtresi burada yok, kimlik kapsaması bağlı middleware'de"
|
|
47
|
+
- **Dış bağımlılık** — "ödeme sağlayıcısına senkron çağrı; sağlayıcı yavaşlarsa istek bekler"
|
|
48
|
+
- **Sessiz düşen dal** — "eşleşme bulunamazsa hata dönmez, boş dizi döner"
|
|
49
|
+
|
|
50
|
+
```jsonc
|
|
51
|
+
{ "id": "<GUID>", "name": "createAssignment", "type": "table",
|
|
52
|
+
"description": "Kullanıcıyı seçilen pozisyona atar.",
|
|
53
|
+
"caution": "İki kez çalışırsa çift atama yazar; çağıran tarafta idempotency koruması yok." }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Yazılmayacaklar:** `"bu adım veriyi okur, dikkat"` gibi mekanizma tekrarı, `description`'ın
|
|
57
|
+
başka kelimelerle yazılmışı, `"dikkatli olun"` gibi genel tavsiye. Bunlar kanvastaki ⚠ işaretini
|
|
58
|
+
değersizleştirir — işaretin tüm değeri **seyrek** olmasından gelir.
|
|
59
|
+
|
|
60
|
+
Alan `description` gibi inerttir: motor okumaz. Studio'da kanvas düğümünde kehribar ⚠ ikonu,
|
|
61
|
+
adım panelinde düzenlenebilir metin, analiz belgesinde ayrı satır olarak görünür; `workflow_get`
|
|
62
|
+
outline görünümünde geri okunur — sonraki oturumda tuzağı tam gövdeyi almadan görürsün.
|
|
63
|
+
|
|
5
64
|
## Karar kuralları
|
|
6
65
|
|
|
7
66
|
1. **DTO önce (yalnız workflow tipinde).** `type='workflow'` için girdi/çıktı sözleşmesini
|
|
@@ -49,6 +108,55 @@ Bağlanmamış step çalışmaz. `start` tek branch taşır; normal step'ler `su
|
|
|
49
108
|
`condition` koşul branch'leri + `else`; terminal step'ler (`response`, `convert`, `revert`,
|
|
50
109
|
`end`) **branch taşımaz**.
|
|
51
110
|
|
|
111
|
+
## Hata yolu
|
|
112
|
+
|
|
113
|
+
**Gateway workflow ve middleware tanımlarına hata yolunu otomatik ekler** (yanıtta
|
|
114
|
+
`gatewayNormalized`): start adımına `type: 'global-error'` dalı + 500 dönen bir `response` adımı
|
|
115
|
+
(`gatewayOnError`). Kendi hata gövdeni istiyorsan bu adımın `bodyExpression`'ını değiştir ya da
|
|
116
|
+
tanımı kendi global-error dalınla gönder — dal **bağlıysa** (`nextStepId` doluysa) gateway
|
|
117
|
+
dokunmaz. `nextStepId: null` ile bağlanmamış gönderirsen gateway ikinci bir dal eklemez, mevcut
|
|
118
|
+
dalını enjekte ettiği hata adımına bağlar.
|
|
119
|
+
|
|
120
|
+
**Fail dallarını tek tek bağlamana gerek yok.** Bir adımın `fail` dalı bağlanmamışsa
|
|
121
|
+
(`nextStepId: null`) ya da hiç yoksa, hata **kendiliğinden** global-error dalına gider. `fail`
|
|
122
|
+
dalını yalnız o adıma özel bir telafi akışı (örn. "kayıt yoksa oluştur") istiyorsan bağla.
|
|
123
|
+
|
|
124
|
+
Global-error dalında `error` değişkeni tanımlıdır:
|
|
125
|
+
|
|
126
|
+
```jsonc
|
|
127
|
+
{ "code": "RowAlreadyExists", "message": "…", "detail": "…", "type": "…",
|
|
128
|
+
"step": { "id": "…", "name": "writeRow", "type": "table", "data": …, "input": [ … ] } }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`error.step` hangi adımın patladığını söyler — hata yanıtına koymadan önce iki kez düşün, iç
|
|
132
|
+
yapıyı dışarı sızdırır.
|
|
133
|
+
|
|
134
|
+
**Kurallar ve sınırlar:**
|
|
135
|
+
|
|
136
|
+
- **Global-error dalı TEK olmalı ve START adımında durmalı.** Başka adıma koyulan dal hiç
|
|
137
|
+
çalışmaz, platform 400 döner. Gateway bunu `WF-022` ile istek gitmeden reddeder.
|
|
138
|
+
- **Hata `response` adımı ReturnDto'ya bağlı değildir.** Global-error modunda platform sözleşmeyi
|
|
139
|
+
atlar, gövde serbesttir — `returnDtoId` ile çakışmaz.
|
|
140
|
+
- **Loop içi hatalar global-error dalına GİTMEZ.** Loop çocuk bağlamı bu yönlendirmeyi kapatır;
|
|
141
|
+
loop adımının kendi `fail` dalını bağla.
|
|
142
|
+
- **`automation` tipinde `response` adımı yasaktır**, bu yüzden gateway bu tipe enjeksiyon
|
|
143
|
+
yapmaz. Hata yolunu kendin yaz: global-error dalını `revert` (tetikleyen satır işlemini geri
|
|
144
|
+
alır) ya da `email`/`table` adımına bağla. Bağlı hata yolu yoksa `WF-021` uyarır.
|
|
145
|
+
- **Hata yolu hiç yoksa** akış patladığında platform 500 + ham exception mesajı döner. Çalışır,
|
|
146
|
+
ama çıktı denetimsizdir ve lokalize değildir.
|
|
147
|
+
|
|
148
|
+
> **Preview HTTP 200 dönerken bile akış patlamış olabilir.** `executionSuccess` yalnız SON adıma
|
|
149
|
+
> bakar; ortada patlayıp hata dalından dönen akış `true` gösterir. Gateway tüm adımları (iç içe
|
|
150
|
+
> `children` ve tetiklenen `automations` dâhil) tarar ve hata bulursa yanıta `runtimeErrors`
|
|
151
|
+
> alanını ekler — `workflow_preview` ve `automation_preview` yanıtlarında bu alanı kontrol et.
|
|
152
|
+
>
|
|
153
|
+
> **`runtimeErrors` her tool'da aynı şeyi ölçmez.** Alan yalnız `steps[]` taşıyan yanıtlarda adım
|
|
154
|
+
> adım tarama yapar. `microservice_execute` / `microservice_debug` / `microservice_preview`
|
|
155
|
+
> yanıtlarında gateway sadece `httpStatus >= 400` durumunu bulgu sayar: bu tool'lar adım ağacını
|
|
156
|
+
> `executionItems` olarak döner ve o düğümlerde hata işareti yoktur, yani HTTP 200 gövdesine gömülü
|
|
157
|
+
> bir microservice hatası `runtimeErrors` üretmez. Orada `body`'yi kendin oku ve şüpheliyse
|
|
158
|
+
> `microservice_debug` ile adım adım bak. `query_preview` yanıtları hiç kapsanmaz.
|
|
159
|
+
|
|
52
160
|
## Condition step kuralları
|
|
53
161
|
|
|
54
162
|
Koşullar `rule.items[]` dizisindedir ve **sırayla** değerlendirilir; ilk `true` olan dala
|
|
@@ -151,6 +259,11 @@ Tek uçta "başlık + N alt kayıt" yazmanın yolu budur (anket + sorular + seç
|
|
|
151
259
|
|
|
152
260
|
- `rule` içinde `datasourceId` + `entityId` zorunlu; `selectFields`/`filters` öğelerinde
|
|
153
261
|
**`fieldId` zorunlu** (yalnız `fieldSlug` yetmez → `EvaluateWorkflowStepFailedException`).
|
|
262
|
+
- **`selectFields` = akışın gerçekten OKUDUĞU alanlar.** Entity'nin tüm alanlarını işaretlemek
|
|
263
|
+
her satırı gereksiz büyütür (join'li adımda maliyet katlanır) ve adımın ne ürettiğini
|
|
264
|
+
okunamaz hâle getirir. Sonraki adımların, filtrelerin ve response `bodyExpression`'ının
|
|
265
|
+
dokunmadığı alanları çıkarın — gateway bunları **WF-023 (warn)** ile adıyla sayar. Çıktıyı
|
|
266
|
+
bütün olarak geçiriyorsanız (`{ list: rows.data }`) kural susar.
|
|
154
267
|
- **Table step JOIN kurabilir** ve alias/path disiplini microservice'inkiyle **birebir aynıdır**
|
|
155
268
|
(Studio aynı form bileşenlerini kullanır): `joins[].path` = `<kökEntityId>.<ilişkiAlanı…>`,
|
|
156
269
|
join'li `selectFields[].path` = kök id olmadan aynı zincir, kök alanlarda `path` yok.
|
|
@@ -243,12 +356,15 @@ zorunludur (`CreateWorkflowCommandValidator`: `When(ReturnDtoId.HasValue) → Re
|
|
|
243
356
|
```jsonc
|
|
244
357
|
{
|
|
245
358
|
"name": "Product Lookup", "slug": "product-lookup", "type": "workflow",
|
|
359
|
+
"description": "Ada göre tek ürün getirir; bulunamazsa hata yoluna düşer.",
|
|
246
360
|
"parameterDtoId": "<girdi DTO id>", "parameterBehavior": "object",
|
|
247
361
|
"returnDtoId": "<çıktı DTO id>", "returnBehavior": "object",
|
|
248
362
|
"steps": [
|
|
249
363
|
{ "id": "<GUID-1>", "name": "Start", "type": "start",
|
|
250
|
-
"branches": [{ "id": "<GUID-2>", "name": "start", "type": "success", "nextStepId": "<GUID-3>" }
|
|
364
|
+
"branches": [{ "id": "<GUID-2>", "name": "start", "type": "success", "nextStepId": "<GUID-3>" },
|
|
365
|
+
{ "id": "<GUID-7>", "name": "global-error", "type": "global-error", "nextStepId": "<GUID-8>" }] },
|
|
251
366
|
{ "id": "<GUID-3>", "name": "load_product", "type": "table",
|
|
367
|
+
"description": "input.productName ile ürünü okur; tek kayıt bekler.",
|
|
252
368
|
"rule": { "stepType": "table", "datasourceId": "<datasource id>", "entityId": "<entity id>",
|
|
253
369
|
"operation": "get", "isSingle": true,
|
|
254
370
|
"selectFields": [{ "alias": "<entity slug>", "fieldId": "<alan id>", "fieldSlug": "name" }],
|
|
@@ -258,9 +374,16 @@ zorunludur (`CreateWorkflowCommandValidator`: `When(ReturnDtoId.HasValue) → Re
|
|
|
258
374
|
"limit": { "name": "limit", "offset": "offset", "source": "query-string" } },
|
|
259
375
|
"branches": [{ "id": "<GUID-4>", "name": "success", "type": "success", "nextStepId": "<GUID-5>" },
|
|
260
376
|
{ "id": "<GUID-6>", "name": "fail", "type": "fail", "nextStepId": null }] },
|
|
377
|
+
// ^ bağlanmamış fail dalı: hata global-error dalına gider
|
|
261
378
|
{ "id": "<GUID-5>", "name": "product_response", "type": "response",
|
|
379
|
+
"description": "Bulunan ürünün adını 200 ile döner.",
|
|
262
380
|
"rule": { "bodyExpression": "{ name: load_product.data.name }", "httpStatusCode": 200,
|
|
263
|
-
"headerExpression": "{ }" }, "branches": [] }
|
|
381
|
+
"headerExpression": "{ }" }, "branches": [] },
|
|
382
|
+
{ "id": "<GUID-8>", "name": "onError", "type": "response",
|
|
383
|
+
"description": "Akış patlarsa hata kodunu ve mesajını 500 ile döner.",
|
|
384
|
+
"rule": { "stepType": "response", "httpStatusCode": 500,
|
|
385
|
+
"bodyExpression": "({ code: error.code, message: error.message })" },
|
|
386
|
+
"branches": [] }
|
|
264
387
|
]
|
|
265
388
|
}
|
|
266
389
|
```
|
|
@@ -315,6 +438,8 @@ zorunludur (`CreateWorkflowCommandValidator`: `When(ReturnDtoId.HasValue) → Re
|
|
|
315
438
|
- Automation'da `isQueue`/`operation` eksik ya da returnDtoId verilmiş (WF-010, block)
|
|
316
439
|
- `fields` ∩ `selectFields` çakışması (WF-012, warn) · join'li table'da boş selectFields (WF-007, warn)
|
|
317
440
|
- Join'li table step'inde kök `rule.alias` boş (WF-019, block — Postgres 42601'in sebebi)
|
|
441
|
+
- Akışta hiç okunmayan `selectFields` alanı (WF-023, warn — gereksiz taşınan sütun)
|
|
442
|
+
- Adımda ya da akışta `description` yok (WF-024, block — Studio'da adım okunamaz kalır)
|
|
318
443
|
- İlişki alanının içini okuyan ifade (WF-020, warn — join yoksa alan düz FK'dır)
|
|
319
444
|
- `set-user` adımı olmadan `user` okuyan middleware (MW-003, warn)
|
|
320
445
|
|