@appaflytech/zeyrek-ai-mcp 0.0.49 → 0.0.50

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.
@@ -0,0 +1,491 @@
1
+ {
2
+ "_meta": {
3
+ "synthetic": true,
4
+ "note": "Sırası kalıcı kaydedilen mobil liste: repeater tutamaçtan sürüklenir, reorder akışı event.data.patches'ı bulk-update ucuna TEK istekte kök dizi olarak yazar, ardından listeyi sıra alanına göre yeniden okur. mobiltest › siralama-kalici'de (lokal, 2026-10-27) uçta ölçülen sayfanın kimliksiz hâli: sürükleme → tek PUT 200, yeniden okunan satırlar yeni sırada. Sayfa mizanpaj sözleşmesine ve MOB-035'e uyar, akışlar mobil profilden ve kendi lint kurallarından bulgusuz geçer (test/mobile-rules.test.ts, test/mobile-layout-lint.test.ts).",
5
+ "pairsWith": "mobile-offline-list (aynı listenin okuma deseni), mobile-list-detail-form (satıra dokunma), mobile-app-shell-stack ya da mobile-app-shell-tab (kabuk)",
6
+ "sourceRef": "platform_guide('page') §Kural 1g-5; platform_guide('microservice') §Toplu yazma; MOB-035; zeyrek-frontend docs/adr/0029-akis-adiminda-kok-dizi-govde.md"
7
+ },
8
+ "_rules": {
9
+ "list": "repeater'a reorderable: true + orderField: '<sayı alanı>' yazılır; tutamaç her satırın sonunda çizilir. Yalnız dikey ve tek sütunlu listede çalışır — direction: 'horizontal' ya da columnCount > 1 MOB-035 uyarısıdır ve tutamaç çizilmez. Satırlar orderField'a göre çizilir; okuma ucunun sıralı dönmesi yine de yenilemede aynı sırayı garanti eder.",
10
+ "payload": "Bırakınca reorder yayılır: event.data = { fromIndex, toIndex, row, patches: [{ id, value }] }. patches yalnız sırası değişen satırlardır; id satırın _id'si (yoksa id), value orderField'a yazılacak 1 tabanlı yeni adım. Yerinde bırakılan sürükleme olay yaymaz; orderField yoksa ya da bir satırda anahtar yoksa patches BOŞ dizidir.",
11
+ "endpoint": "Yazma ucu tablo kaynaklı operation: 'bulk-update'; fields = birincil anahtar + sıra alanı, başka alan yok (tanımdaki alan gövdede yoksa NULL yazılır). Gövdede birincil anahtar zorunludur, WHERE'e taşınır. Tek istekte en fazla 1000 satır, parti hep-ya-hiç.",
12
+ "bodyKeys": "Gövde anahtarları alan slug'larıdır; fields[].name ile yeniden adlandırma yoktur. { id, value } yaması uca olduğu gibi gitmez: map ile { id, <orderField>: value }'ya eşlenir.",
13
+ "rootArray": "Satır listesi ucun gövde parametrelerinden YALNIZ BİRİNE (birincil anahtarınkine) code ile verilir, öbürleri hiç yazılmaz. Servis şeması isParameterArray dediği için değeri olan tek gövde parametresinin dizisi gövdenin KENDİSİ olur — parametre adı anlamını yitirir. İkinci bir gövde parametresine değer verilirse gövde nesneye döner ve platform tek satır okur: 422 'Please provide the primary key value'. ADR 0029'dan eski platformda bu akış da 422 ile düşer.",
14
+ "onError": "Servis hata dönerse liste kendiliğinden eski sıraya DÖNMEZ: yeniden okuma sürüklemeden önceki satırların aynısını getirir, liste aynı satırlarda tuttuğu sırayı korur ve ekran kaydedilmemiş sırada kalır. Hatayı Steps.<yazma adımı>.error ile kullanıcıya gösterin; eski sıraya döndürmenin doğrulanmış bir yolu yoktur.",
15
+ "verify": "page_screenshot({ applicationSlug: '<uygulama>', pageSlug: 'siralama', actions: [{ action: 'swipe', target: 'lst_gorev__row-2__handle', to: 'lst_gorev__row-0' }] }) üçüncü satırı başa taşır; txt_sonuc 'kaydedildi 3 satır' yazar ve row_list (order sira) aynı sırayı verir. Tutamaç kimliği yalnız çalışma anında basılır. Her denemeden sonra satırları bulk-update ile ilk sıralarına geri yazın."
16
+ },
17
+ "microservices": [
18
+ {
19
+ "role": "okuma",
20
+ "definition": {
21
+ "name": "gorev-sirali",
22
+ "slug": "gorev-sirali",
23
+ "url": "gorev-sirali",
24
+ "description": "Görevleri (id, ad, sira) liste olarak döner; sıralamayı çağıran sortField/sortType ile verir.",
25
+ "type": "rest-api",
26
+ "httpMethod": "get",
27
+ "source": "table",
28
+ "returnType": "flat",
29
+ "entityId": "<gorev entity id>",
30
+ "sourceParameters": {
31
+ "operation": "get",
32
+ "entityId": "<gorev entity id>",
33
+ "alias": "gorev",
34
+ "isSingle": false,
35
+ "joins": [],
36
+ "filters": [],
37
+ "selectFields": [
38
+ {
39
+ "alias": "gorev",
40
+ "fieldSlug": "id"
41
+ },
42
+ {
43
+ "alias": "gorev",
44
+ "fieldSlug": "ad"
45
+ },
46
+ {
47
+ "alias": "gorev",
48
+ "fieldSlug": "sira"
49
+ }
50
+ ],
51
+ "sort": {
52
+ "name": "sortField",
53
+ "type": "sortType",
54
+ "source": "query-string"
55
+ },
56
+ "limit": {
57
+ "name": "limit",
58
+ "offset": "offset",
59
+ "source": "query-string"
60
+ }
61
+ }
62
+ }
63
+ },
64
+ {
65
+ "role": "yazma",
66
+ "definition": {
67
+ "name": "gorev-sira-yaz",
68
+ "slug": "gorev-sira-yaz",
69
+ "url": "gorev-sira-yaz",
70
+ "description": "Görevlerin sıra alanını tek istekte toplu yazar; gövde [{ id, sira }] dizisidir.",
71
+ "type": "rest-api",
72
+ "httpMethod": "put",
73
+ "source": "table",
74
+ "returnType": "flat",
75
+ "entityId": "<gorev entity id>",
76
+ "sourceParameters": {
77
+ "operation": "bulk-update",
78
+ "entityId": "<gorev entity id>",
79
+ "alias": "gorev",
80
+ "isSingle": true,
81
+ "joins": [],
82
+ "filters": [],
83
+ "selectFields": [],
84
+ "fields": [
85
+ {
86
+ "fieldSlug": "id",
87
+ "source": "body",
88
+ "value": "id"
89
+ },
90
+ {
91
+ "fieldSlug": "sira",
92
+ "source": "body",
93
+ "value": "sira"
94
+ }
95
+ ],
96
+ "returnFields": [],
97
+ "sort": {
98
+ "name": "sortField",
99
+ "type": "sortType",
100
+ "source": "query-string"
101
+ },
102
+ "limit": {
103
+ "name": "limit",
104
+ "offset": "offset",
105
+ "source": "query-string"
106
+ }
107
+ }
108
+ }
109
+ }
110
+ ],
111
+ "pages": [
112
+ {
113
+ "pageCreateArgs": {
114
+ "name": "Sıralama",
115
+ "slug": "siralama"
116
+ },
117
+ "schemaPatchOps": [
118
+ {
119
+ "op": "add_component",
120
+ "parent": "page_container",
121
+ "component": {
122
+ "elementId": "txt_sonuc",
123
+ "name": "txt_sonuc",
124
+ "type": "text",
125
+ "kind": "component",
126
+ "props": {
127
+ "content": {
128
+ "type": "static",
129
+ "ref": null,
130
+ "data": "Bir satırı tutamacından sürükleyin"
131
+ }
132
+ },
133
+ "appearance": {
134
+ "marginType": "none",
135
+ "widthType": "fill"
136
+ },
137
+ "blocks": [],
138
+ "events": [],
139
+ "validations": [],
140
+ "stateBindings": [],
141
+ "children": []
142
+ }
143
+ },
144
+ {
145
+ "op": "add_component",
146
+ "parent": "page_container",
147
+ "component": {
148
+ "elementId": "lst_gorev",
149
+ "name": "lst_gorev",
150
+ "type": "repeater",
151
+ "kind": "component",
152
+ "props": {
153
+ "rows": {
154
+ "type": "static",
155
+ "ref": null,
156
+ "data": []
157
+ },
158
+ "reorderable": {
159
+ "type": "static",
160
+ "ref": null,
161
+ "data": true
162
+ },
163
+ "orderField": {
164
+ "type": "static",
165
+ "ref": null,
166
+ "data": "sira"
167
+ },
168
+ "verticalSpacing": {
169
+ "type": "static",
170
+ "ref": null,
171
+ "data": 8
172
+ }
173
+ },
174
+ "appearance": {
175
+ "marginType": "none",
176
+ "widthType": "fill"
177
+ },
178
+ "blocks": [],
179
+ "events": [],
180
+ "validations": [],
181
+ "stateBindings": [],
182
+ "children": [
183
+ {
184
+ "elementId": "lst_gorev_satir",
185
+ "name": "lst_gorev_satir",
186
+ "type": "view",
187
+ "kind": "component",
188
+ "props": {},
189
+ "blocks": [],
190
+ "children": [
191
+ {
192
+ "elementId": "kart_gorev",
193
+ "name": "kart_gorev",
194
+ "type": "card",
195
+ "kind": "component",
196
+ "props": {
197
+ "title": {
198
+ "type": "code",
199
+ "ref": "{{ item.ad }}"
200
+ },
201
+ "description": {
202
+ "type": "code",
203
+ "ref": "{{ 'Sıra ' + item.sira }}"
204
+ }
205
+ },
206
+ "appearance": {
207
+ "marginType": "none",
208
+ "widthType": "fill"
209
+ },
210
+ "blocks": [],
211
+ "events": [],
212
+ "validations": [],
213
+ "stateBindings": [],
214
+ "children": []
215
+ }
216
+ ]
217
+ },
218
+ {
219
+ "elementId": "lst_gorev_bos_gorunum",
220
+ "name": "lst_gorev_bos_gorunum",
221
+ "type": "view",
222
+ "kind": "component",
223
+ "props": {},
224
+ "blocks": [],
225
+ "children": [
226
+ {
227
+ "elementId": "lst_gorev_bos",
228
+ "name": "lst_gorev_bos",
229
+ "type": "emptystate",
230
+ "kind": "component",
231
+ "props": {
232
+ "title": {
233
+ "type": "static",
234
+ "data": "Görev yok"
235
+ }
236
+ },
237
+ "blocks": [],
238
+ "children": []
239
+ }
240
+ ]
241
+ }
242
+ ]
243
+ }
244
+ }
245
+ ],
246
+ "flows": [
247
+ {
248
+ "name": "siralama yukle",
249
+ "slug": "siralama-yukle",
250
+ "steps": [
251
+ {
252
+ "stepId": "start",
253
+ "name": "start",
254
+ "type": "start",
255
+ "kind": "normal",
256
+ "args": {},
257
+ "branches": [
258
+ {
259
+ "id": "<GUID>",
260
+ "name": "always",
261
+ "nextStepId": "oku"
262
+ }
263
+ ]
264
+ },
265
+ {
266
+ "stepId": "oku",
267
+ "name": "oku",
268
+ "type": "microservice",
269
+ "kind": "normal",
270
+ "args": {
271
+ "microserviceId": "<gorev-sirali microservice id>",
272
+ "slug": "gorev-sirali",
273
+ "httpMethod": "get",
274
+ "isRaw": false,
275
+ "parameters": [
276
+ {
277
+ "name": "sortField",
278
+ "type": "text",
279
+ "source": "query-string",
280
+ "value": {
281
+ "type": "static",
282
+ "ref": "sira"
283
+ }
284
+ },
285
+ {
286
+ "name": "sortType",
287
+ "type": "text",
288
+ "source": "query-string",
289
+ "value": {
290
+ "type": "static",
291
+ "ref": "asc"
292
+ }
293
+ }
294
+ ]
295
+ },
296
+ "branches": [
297
+ {
298
+ "id": "<GUID>",
299
+ "name": "always",
300
+ "nextStepId": "yaz"
301
+ }
302
+ ]
303
+ },
304
+ {
305
+ "stepId": "yaz",
306
+ "name": "yaz",
307
+ "type": "setter",
308
+ "kind": "normal",
309
+ "args": {
310
+ "iteratees": [
311
+ {
312
+ "id": "<GUID>",
313
+ "type": "change",
314
+ "source": "component",
315
+ "element": "lst_gorev",
316
+ "property": "rows",
317
+ "mapping": {
318
+ "source": "",
319
+ "mappings": {}
320
+ },
321
+ "value": {
322
+ "type": "code",
323
+ "ref": "Steps.oku?.data ?? []"
324
+ }
325
+ }
326
+ ]
327
+ },
328
+ "branches": [
329
+ {
330
+ "id": "<GUID>",
331
+ "name": "always",
332
+ "nextStepId": null
333
+ }
334
+ ]
335
+ }
336
+ ]
337
+ },
338
+ {
339
+ "name": "siralama kaydet",
340
+ "slug": "siralama-kaydet",
341
+ "steps": [
342
+ {
343
+ "stepId": "start",
344
+ "name": "start",
345
+ "type": "start",
346
+ "kind": "normal",
347
+ "args": {},
348
+ "branches": [
349
+ {
350
+ "id": "<GUID>",
351
+ "name": "always",
352
+ "nextStepId": "kaydet"
353
+ }
354
+ ]
355
+ },
356
+ {
357
+ "stepId": "kaydet",
358
+ "name": "kaydet",
359
+ "type": "microservice",
360
+ "kind": "normal",
361
+ "args": {
362
+ "microserviceId": "<gorev-sira-yaz microservice id>",
363
+ "slug": "gorev-sira-yaz",
364
+ "httpMethod": "put",
365
+ "isRaw": false,
366
+ "parameters": [
367
+ {
368
+ "name": "id",
369
+ "type": "guid",
370
+ "source": "body",
371
+ "value": {
372
+ "type": "code",
373
+ "ref": "event.data.patches.map(p => ({ id: p.id, sira: p.value }))"
374
+ }
375
+ }
376
+ ]
377
+ },
378
+ "branches": [
379
+ {
380
+ "id": "<GUID>",
381
+ "name": "always",
382
+ "nextStepId": "oku"
383
+ }
384
+ ]
385
+ },
386
+ {
387
+ "stepId": "oku",
388
+ "name": "oku",
389
+ "type": "microservice",
390
+ "kind": "normal",
391
+ "args": {
392
+ "microserviceId": "<gorev-sirali microservice id>",
393
+ "slug": "gorev-sirali",
394
+ "httpMethod": "get",
395
+ "isRaw": false,
396
+ "parameters": [
397
+ {
398
+ "name": "sortField",
399
+ "type": "text",
400
+ "source": "query-string",
401
+ "value": {
402
+ "type": "static",
403
+ "ref": "sira"
404
+ }
405
+ },
406
+ {
407
+ "name": "sortType",
408
+ "type": "text",
409
+ "source": "query-string",
410
+ "value": {
411
+ "type": "static",
412
+ "ref": "asc"
413
+ }
414
+ }
415
+ ]
416
+ },
417
+ "branches": [
418
+ {
419
+ "id": "<GUID>",
420
+ "name": "always",
421
+ "nextStepId": "yaz"
422
+ }
423
+ ]
424
+ },
425
+ {
426
+ "stepId": "yaz",
427
+ "name": "yaz",
428
+ "type": "setter",
429
+ "kind": "normal",
430
+ "args": {
431
+ "iteratees": [
432
+ {
433
+ "id": "<GUID>",
434
+ "type": "change",
435
+ "source": "component",
436
+ "element": "lst_gorev",
437
+ "property": "rows",
438
+ "mapping": {
439
+ "source": "",
440
+ "mappings": {}
441
+ },
442
+ "value": {
443
+ "type": "code",
444
+ "ref": "Steps.oku?.data ?? []"
445
+ }
446
+ },
447
+ {
448
+ "id": "<GUID>",
449
+ "type": "change",
450
+ "source": "component",
451
+ "element": "txt_sonuc",
452
+ "property": "content",
453
+ "mapping": {
454
+ "source": "",
455
+ "mappings": {}
456
+ },
457
+ "value": {
458
+ "type": "code",
459
+ "ref": "Steps.kaydet?.error ? 'Sıra kaydedilemedi: ' + Steps.kaydet.error.message : 'kaydedildi ' + event.data.patches.length + ' satır'"
460
+ }
461
+ }
462
+ ]
463
+ },
464
+ "branches": [
465
+ {
466
+ "id": "<GUID>",
467
+ "name": "always",
468
+ "nextStepId": null
469
+ }
470
+ ]
471
+ }
472
+ ]
473
+ }
474
+ ],
475
+ "bindOps": [
476
+ {
477
+ "op": "set_event",
478
+ "target": "page_container",
479
+ "event": "mount",
480
+ "eventValue": "<siralama-yukle akış id>"
481
+ },
482
+ {
483
+ "op": "set_event",
484
+ "target": "lst_gorev",
485
+ "event": "reorder",
486
+ "eventValue": "<siralama-kaydet akış id>"
487
+ }
488
+ ]
489
+ }
490
+ ]
491
+ }
@@ -225,6 +225,13 @@ DTO'dan süzülmez (WF-026 — `platform_guide('workflow')` §Hata yolu). Sözle
225
225
  ucundan kullanılır.
226
226
  - Adımda tanımlı ama değeri boş bırakılan alanlar gövdeden/expression'dan gelen veriyi
227
227
  **ezmez**; yalnız değeri dolu olan sabitler her satıra uygulanır.
228
+ - **Sayfa (UI) akışından tek istekte çağrılır** (zeyrek-frontend ADR 0029; uçta ölçüldü
229
+ 2026-10-27): ucun şeması `isParameterSchemaArray`
230
+ olduğu için adımda **değeri olan tek gövde parametresinin** değeri dizi ise o dizi gövdenin
231
+ kendisi olur. Satır listesini bir gövde parametresine (ör. birincil anahtarınkine) `code` ile
232
+ verin, öbürlerini boş bırakın; iki gövde parametresi dolarsa gövde nesneye döner ve tek satır
233
+ okunur. Gövde anahtarları alan slug'larıdır — `fields[].name` ile yeniden adlandırma yoktur
234
+ (kaydedilmez). Tam tarif: `platform_guide('page')` §Kural 1g-5.
228
235
  - **`count` vardır** — kayıt sayısı için tüm satırları çekip uzunluğuna bakmayın; panel/KPI
229
236
  sayaçlarının doğru şekli budur. Dönüşü tek bir sayıdır: `isReturnSchemaArray` `isSingle`'dan
230
237
  bağımsız `false` okunur (2026-09-28'den önce kaydedilmiş servislerde de).
@@ -102,6 +102,19 @@ language, colorScheme: 'light', outPath }` → `mobile_asset_create { path, kind
102
102
  tablet10 `{800,1280,2}`. JPEG şart (iOS alfa kabul etmez); giriş/splash sayfası çekilmez (App Review 2.3.3).
103
103
  ikon/splash/öne çıkan görsel tekli yuvadır: yeni yükleme eskisinin yerine geçer.
104
104
 
105
+ **Pazarlama görseli (çerçeve + başlık):** ham kareler yukarıdaki gibi çekilir, sonra `mobile_asset_preview
106
+ { applicationId, deviceClass, locale, slides: [{ path?, eyebrow?, title, subtitle?, layout? }], styles?, palette?,
107
+ captionPosition?, frame? }` onları mağaza ölçüsünde JPEG'e çizer ve `galleryUrl` döner. Stiller (en çok dördü
108
+ birlikte, her biri galeride bir satır): `clean` · `midnight` · `aurora` · `spotlight` · `swiss` · `japandi` ·
109
+ `pastel` · `neon`. Yerleşim kare başına: `classic` · `bleed` (büyük cihaz alttan taşar) · `tilt` (3D eğik) · `text`
110
+ (cihazsız, `path` istemez); ardışık karelerde yerleşimi değiştirmek seriyi canlı gösterir. Kullanıcı stil
111
+ istemediyse dört farklı stil önerin, seçimi galeride yapsın. Galeri bir editördür: kullanıcı başlığı karenin
112
+ üstünde yazar, stil/yerleşim/renk değiştirir, kartı kopyalar ve yeniden çizer — metni sizin yeniden çizmeniz gerekmez. Adresi kullanıcıya verin: seçtiklerini "Seçilenleri
113
+ yükle" ile `screenshot/<locale>/<deviceClass>` yuvasına kare sırasıyla ekler — ajan ayrıca yüklemez (kopya düşer).
114
+ Başlık uygulamanın gerçekten yaptığını söyler (App Review 2.3.7: fiyat, sıralama, rakip adı yok); uygulamanın ana
115
+ rengi `palette.primary` ile verilir (`theme_get`). Galeri gateway süreci yaşadıkça açıktır; `AUTH_MODE=passthrough`'ta
116
+ açılmaz, yükleme `mobile_asset_create { path: files[i].path }` ile yapılır.
117
+
105
118
  ## Akış
106
119
 
107
120
  1. `mobile_store_get` → kimlik varsayılandır (`app.zeyrek.o<org>.a<uygulama>`); değiştirilecekse **ilk
@@ -194,6 +194,10 @@ docs/adr/0005; başka şekil 422 `fixed-views`). Satır yokken boş görünüm b
194
194
  `emptystate` yolu 2026-09-30'da kaldırıldı. Mobil `view` çocuklarını sütunda dizer. Tam gövde:
195
195
  `ngapps://examples/mobile-list-detail-form`.
196
196
 
197
+ Sürükle-sırala: `reorderable: true` + `orderField` (yalnız dikey, tek sütun — MOB-035); bırakınca `reorder`
198
+ `event.data.patches` taşır. Yük, tutulan sıra ve kalıcılığın bugünkü açığı: `platform_guide('page')` §Kural 1g-5.
199
+ Jesti `page_screenshot` ile sınamak: `{ action:'swipe', target:'<liste>__row-2__handle', to:'<liste>__row-0' }`.
200
+
197
201
  ## Güne göre görev listesi
198
202
 
199
203
  Başlıklar altında gruplanmış liste (güne, odaya, kategoriye göre) **tek `grouped-list`** ile kurulur.
@@ -1618,6 +1618,56 @@ kayıt — artık bekleyen değildir, önbellekteki eski listede de yoktur; list
1618
1618
  `page_screenshot` önbellek ve kuyruk kullanmaz — davranış yalnız cihazda görülür. Tam gövde (iki ekran, beş akış, iki
1619
1619
  uç, ayar ve değişken): `ngapps://examples/mobile-offline-seamless`.
1620
1620
 
1621
+ ## Kural 1g-5 — Mobil sürükle-sırala: `repeater` `reorderable` ve sıranın kalıcılığı
1622
+
1623
+ `repeater`'a `reorderable: true` + `orderField: '<sayı alanı>'` yazılınca her satırın sonunda bir tutamaç çizilir; satır
1624
+ tutamaçtan sürüklenir, bırakınca `reorder` yayılır. Yalnız dikey ve tek sütunlu listede çalışır (yatay şerit ya da
1625
+ `columnCount` > 1 iken tutamaç çizilmez, gateway **MOB-035** ile uyarır). Web'de karşılığı `datatable`
1626
+ `canReorderRows`'tur (save olayı, `self.operation === 'rowReorder'`).
1627
+
1628
+ | Ne | Değer |
1629
+ |---|---|
1630
+ | Yük | `event.data = { fromIndex, toIndex, row, patches: [{ id, value }] }` — `self`'e yazılmaz |
1631
+ | `patches` | Sırası değişen satırlar: `id` satırın `_id`'si (yoksa `id`), `value` `orderField`'a yazılacak 1 tabanlı yeni adım. Eş değerli satırlar tek adımdır (aynı `value`'yu paylaşır); iki eş satırın arasına bırakılan satır o adıma katılır. `orderField` yoksa ya da bir satırda anahtar yoksa BOŞ dizi |
1632
+ | Çizim sırası | Satırlar `orderField`'a göre çizilir (boşlar sonda); servis sıralı dönmek zorunda değildir |
1633
+ | Tutulan sıra | Liste bırakılan sırayı FARKLI satırlar gelene kadar tutar; aynı satırlar yeniden okunursa (yenileme) sıra bozulmaz |
1634
+ | Seçim | Seçili satır varsa düğümün `selectedIndices`/`selectedItems`/`selectedIndex` değeri yeni sıraya taşınır |
1635
+ | Tutamacın adresi | `<elementId>__row-<i>__handle` (rol kimliği) — yalnız çalışma anında (Dene, `/dev`, `page_screenshot`); tasarım ve Düzenle kipinde tutamaç çizilir ama kimlik basılmaz |
1636
+
1637
+ **Sırayı kalıcı yapmak** akışın işidir: `reorder` akışı `event.data.patches`'ı sıra alanını yazan bir servise verir,
1638
+ ardından `rows`'u yeniden okur — yazılan sıra değerleriyle gelen satırlar farklıdır, liste onları çizer. Boş
1639
+ `patches`'ta (anahtarsız satır) servis çağrılmaz. **Servis hata dönerse liste kendiliğinden eski sıraya DÖNMEZ:** sunucu
1640
+ sürüklemeden önceki satırların aynısını döndürür ve liste aynı satırlarda tuttuğu sırayı korur (yukarıdaki tablo) — ekran
1641
+ kaydedilmemiş sırada kalır. Hatayı kullanıcıya gösterin; eski sıraya döndürmenin doğrulanmış bir yolu yoktur.
1642
+
1643
+ **Tarif — N satır tek istekte (`bulk-update`; zeyrek-frontend ADR 0029).** Uçta ölçüldü (2026-10-27, `mobiltest`
1644
+ `siralama-kalici`): üçüncü satır başa sürüklenince sayfadan tek `PUT` 200 döner, üç satır yazılır ve yeniden okunan
1645
+ `rows` yeni sırayı verir. ADR 0029'dan eski platformda aynı akış gövdeyi nesne kurar ve 422 ile düşer.
1646
+
1647
+ 1. Uç: tablo kaynaklı `operation: 'bulk-update'`, `fields` = birincil anahtar + sıra alanı (başka alan koymayın —
1648
+ tanımdaki alan gövdede yoksa NULL yazılır, `platform_guide('microservice')` §Güncelleme sözleşmesi). Gövdedeki
1649
+ anahtarlar alan **slug'larıdır**; `fields[].name` ile yeniden adlandırma yoktur (platform sessizce düşürür), yani
1650
+ `{ id, value }` yaması uca olduğu gibi gitmez, eşlenir.
1651
+ 2. Akış adımı: ucun gövde parametrelerinden **yalnız birine** (birincil anahtarınkine, ör. `id`) satır listesini
1652
+ `code` ile verin, öbürlerini boş bırakın. Servisin şeması `isParameterArray` dediği için değeri olan tek gövde
1653
+ parametresinin dizisi gövdenin KENDİSİ olur — parametrenin adı anlamını yitirir:
1654
+
1655
+ ```json
1656
+ { "name": "id", "type": "guid", "source": "body",
1657
+ "value": { "type": "code", "ref": "event.data.patches.map(p => ({ id: p.id, sira: p.value }))" } }
1658
+ ```
1659
+
1660
+ İkinci bir gövde parametresine değer verirseniz gövde yine NESNE olur ve platform onu tek satır okur
1661
+ (`BulkRowFailedException` "Please provide the primary key value", 422).
1662
+ 3. Ardından okuma ucu (`sortField: '<sıra alanı>'`, `sortType: 'asc'`) ve `rows`'a setter; hata metni için
1663
+ `Steps.<yazma adımı>.error`.
1664
+
1665
+ Kural motorda, servis şemasına bağlıdır: `bulk-create`/`bulk-delete` ve parametre
1666
+ davranışı dizi olan workflow ucu da aynı yoldan kök dizi alır. Çevrimdışı kuyruğa giren toplu yazma da kök diziyle
1667
+ gönderilir.
1668
+
1669
+ Örnek: `ngapps://examples/mobile-reorder-persist` (iki uç, sayfa, yükleme ve kaydetme akışları, `swipe` ile doğrulama).
1670
+
1621
1671
  ## Kural 1h — Tasarım: `style` prop'u ve tema değişkenleri
1622
1672
 
1623
1673
  Her komponentin `style` prop'u vardır ve emotion `css()` bloğudur: iç seçiciler, pseudo-class'lar
@@ -2601,6 +2651,18 @@ hiçbiri** bağlı değildi; üç portalın da rol menüsü boş çıkıyor, pan
2601
2651
  > adım `applied:false` ve notla döner. Çok günlü etkinlik aynı kimliği her gün hücresinde taşır;
2602
2652
  > hedef ilk eşleşmedir.
2603
2653
  >
2654
+ > **Mobil jesti sınamak (`swipe`, 2026-10-27).** `drag` web'in HTML5 sürüklemesidir; mobil jestler (tutamaçtan
2655
+ > sıralama, satırın kaydırma eylemi, kart destesi, kaydırıcı) işaretçiyi dinler ve `drag`'le sürülmez. `swipe`
2656
+ > hedefin merkezine basar, 12 adımda 16 ms arayla taşır ve bırakır; bitiş ya bir öğedir (`to`) ya göreli kaymadır
2657
+ > (`dx`/`dy`, CSS pikseli) — ikisi birden reddedilir. Hedefler: sıralama `{ action:'swipe',
2658
+ > target:'lst__row-2__handle', to:'lst__row-0' }` (`reorder` akışı koşar, `flowRuns: 1`); satır kaydırma
2659
+ > `{ action:'swipe', target:'lst__row-1', dx:-160, dy:0 }` (eylem paneli açık kalır); deste `{ action:'swipe',
2660
+ > target:'<deste>', dx:320, dy:0 }` (üstteki kart atılır). `to` verilince jest hedefin içinde, hareket yönündeki
2661
+ > kenarına 2 px kala biter — sıralamada satır tam hedef satırın yerine iner. Jest görünür alanın üst ya da alt 72 dp'lik
2662
+ > bandına girerse liste sayfayı kendiliğinden kaydırır ve satırın ineceği yer zamanlamaya bağlı olur: hedefleri
2663
+ > görünür alanda ve kenar bandından uzak seçin. Web sayfasında adım yine koşar ama `note` HTML5 sürükleme için `drag`'i
2664
+ > gösterir.
2665
+ >
2604
2666
  > **Üye kimliğiyle doğrulama (`asUser`).** Kapının kapalı olduğunu görmek yarısıdır; açık
2605
2667
  > olduğunu görmek diğer yarısı. `page_runtime_get` ve `page_screenshot` aynı `asUser` alanını
2606
2668
  > alır: uygulamanın kendi giriş ucundan (`microservice_execute`) aldığın üye jetonunu verirsin,
@@ -59,7 +59,9 @@ başlamadan önce §Yolculuk, §Test derlemesi, §Akış, §Yaş derecelendirmes
59
59
  yuvanın `locale`'idir — verilmezse kare projenin varsayılan dilinde çıkar ve başka dilin yuvasına yanlış dilde
60
60
  yüklenir. Yuva birikimlidir (en çok 10 görsel): 1. adımda okuduğun yuvada görsel varsa kullanıcıya sormadan
61
61
  ekleme yapma. Giriş ve splash sayfası çekilmez. Production'da sayfa yoksa kullanıcıya sor: ekran görüntüsü
62
- canlıya almadan sonraya kalabilir.
62
+ canlıya almadan sonraya kalabilir. Kullanıcı çerçeveli, başlıklı pazarlama görseli isterse ham kareleri
63
+ `mobile_asset_create` yerine `mobile_asset_preview`'a ver ve dönen `galleryUrl`'i kullanıcıya ilet; seçimi ve
64
+ yüklemeyi kullanıcı galeriden yapar.
63
65
  7. **Yaş derecelendirmesi** (iOS açıksa). `settings.apple.advisory`: rehberdeki **bütün** soruları cevapla;
64
66
  reklam açıksa `advertising: 'true'`. Emin olmadığın soruyu kullanıcıya sor.
65
67
  8. **Gizlilik.** `settings.urls.privacyPolicy` (https). Yoksa kullanıcıdan iste; uydurma adres yazma.