diff --git a/.github/instructions/ai.instructions.md b/.github/instructions/ai.instructions.md index 9c0d1b1b..88ec18e1 100644 --- a/.github/instructions/ai.instructions.md +++ b/.github/instructions/ai.instructions.md @@ -295,7 +295,7 @@ the Wizard's `Custom` path. `deletePermission`). In `Otomatik` mode the base is the read permission the Wizard created for the component and the commands get `''` / `.Create` / `.Update` / `.Delete` suffixes. - List screen permissions: `App.DeveloperKit.CustomComponents{,.Create,.Update,.Delete,.Export,.Import}`. -- Every save/delete is mirrored into `configs/seeds/{scope}/data/App.DeveloperKit.CustomComponents.json`; the endpoints it uses are +- Every save/delete is mirrored into `configs/seeds/{scope}/data/App.DeveloperKit.CustomComponents/{Id}.json`; the endpoints it uses are referenced through `crud/{Entity}.json`. A component that is not seed-backed is not done. ### 10.2 SQL View Designer @@ -910,9 +910,10 @@ own dependencies are collected under the same rules, so the archive stands on it - `wizard/{file}.json` — the screen itself plus the wizard files of the sub-forms it references through `SubFormsDto[].Code`, recursively -- `data/App.DeveloperKit.CustomComponents.json` — `Custom` path; the file is filtered down to the +- `data/App.DeveloperKit.CustomComponents/{Id}.json` — `Custom` path; the file is filtered down to the exported component and its dependency chain, never exported whole - `data/{ListFormCode}.json` — the screen's own data mirror when `SeedFilePath` is set + (an extensionless `SeedFilePath` is the folder form: `data/{ListFormCode}/{key}.json`, one file per row) - `crud/{entity}.json` — endpoints those components use - `{sql|postgres}/{object|execute}/{object}.sql` — the object behind `SelectCommand`, the objects a lookup query (`LookupDto.LookupQuery`) references through `FROM`/`JOIN`, and on the `Custom` diff --git a/.github/instructions/lowcode-reference.instructions.md b/.github/instructions/lowcode-reference.instructions.md index 78086d32..ada2522c 100644 --- a/.github/instructions/lowcode-reference.instructions.md +++ b/.github/instructions/lowcode-reference.instructions.md @@ -1172,7 +1172,7 @@ soneklerini alır. | **Definitions › Skill Type / Skill / Skill Level** | `App.Definitions.SkillType` (+ alt kayıtlar) | `Sas_H_SkillType` · `Sas_H_Skill` · `Sas_H_SkillLevel` | host | Yetkinlik ağacı; `SkillType` kendi kendine ebeveyn olan alt form örneğidir. | | **Definitions › UoM Category / UoM** | `App.Definitions.UomCategory` | `Sas_H_UomCategory` · `Sas_H_Uom` | host | Ölçü birimi, `Ratio`/`Rounding` — miktar alanı olan ekranlarda birim lookup'ı ve çevrim. | | **Definitions › Sequence** | `App.Definitions.Sequence` | `Adm_T_Sequence` | tenant | Belge numarası üretimi (`lowcode.instructions.md` §9.9); alan varsayılanında `CustomValueType: 5` ile bağlanır. | -| **Public › Home / About / Services / Contact** | `App.DeveloperKit.CustomComponents` — DeveloperKit > Custom Components (Visual Designer) | `data/App.DeveloperKit.CustomComponents.json` (bileşenin ayrı dosyası yok) | host | Genel siteye açık sayfalar Custom Component'tir; `RoutePath` `/admin/` ile başlamıyorsa otomatik public route olur. Public sayfa isteği kod değil bu tasarımcıdır (README.md §10.4). | +| **Public › Home / About / Services / Contact** | `App.DeveloperKit.CustomComponents` — DeveloperKit > Custom Components (Visual Designer) | `data/App.DeveloperKit.CustomComponents/{Id}.json` (her bileşen kendi satır dosyasında) | host | Genel siteye açık sayfalar Custom Component'tir; `RoutePath` `/admin/` ile başlamıyorsa otomatik public route olur. Public sayfa isteği kod değil bu tasarımcıdır (README.md §10.4). | | **Public › Products / Payment Methods / Installment Options** | `App.Orders.Products` · `.PaymentMethods` · `.InstallmentOptions` | `Sas_H_Product` · `Sas_H_PaymentMethod` · `Sas_H_InstallmentOption` | host | Ürün, ödeme yöntemi (`Commission`), taksit — satış/abonelik ekranlarının lookup kaynağı. | | **Public › Sales Orders** | `App.Orders.SalesOrders` | `Sas_H_Order` + `Sas_H_OrderItem` | host | **Onay akışı + alt kalem + toplam + diyalog** birlikte kullanılan tam örnek (§12.6). | | **Public › Blog Category / Posts** | `App.BlogManagement.Category` · `.Posts` | `Sas_H_BlogCategory` · `Sas_H_BlogPost` | host | Çok dilli içerik (`ContentTr`/`ContentEn`), slug, yayın durumu, sayaç alanları. Veri `configs/seeds/host/data/App.BlogManagement.Category.json` ve `App.BlogManagement.Posts.json` dosyalarından seed edilir. | @@ -1187,7 +1187,7 @@ soneklerini alır. | **Developer Kit › SQL Query Manager** | `App.SqlQueryManager` · `/admin/sqlQueryManager` | — | — | View/procedure/function yazma ve çalıştırma; karar sırasının 2. adımı buradan başlar. | | **Developer Kit › Custom Endpoints** | `App.DeveloperKit.CustomEndpoints` | `Sas_H_CustomEndpoint` | tenant | `Name`, `Url`, `Method`, `DataSourceCode`, `Sql`, `ParametersJson`, `PermissionsJson` — parametreli SQL'i yetkili bir HTTP ucuna çevirir. | | **Developer Kit › Dynamic Services** | `App.DeveloperKit.DynamicServices` · `/admin/list/App.DeveloperKit.DynamicServices` | `Sas_H_DynamicService` | host | Liste ListForm ekranıdır; kod, listenin toolbar/satır butonlarından açılan `DynamicServiceEditor` diyalogunda (Monaco) yazılıp derlenir ve yayınlanır. SQL'in yetmediği iş kuralı için 3. adım. | -| **Developer Kit › Components** | `App.DeveloperKit.CustomComponents` · `/admin/list/App.DeveloperKit.CustomComponents` | `Sas_H_CustomComponent` | host | Custom component listesi (ListForm) ve satırdaki **Design** butonuyla **Visual Designer** girişi (§10). Kayıtlar `data/App.DeveloperKit.CustomComponents.json` dosyasına senkronlanır. | +| **Developer Kit › Components** | `App.DeveloperKit.CustomComponents` · `/admin/list/App.DeveloperKit.CustomComponents` | `Sas_H_CustomComponent` | host | Custom component listesi (ListForm) ve satırdaki **Design** butonuyla **Visual Designer** girişi (§10). Kayıtlar `data/App.DeveloperKit.CustomComponents/{Id}.json` klasörüne senkronlanır. | | **Developer Kit › ListForm** | `App.Listforms.Listform` | `Sas_H_ListForm` · `Sas_H_ListFormField` · `Sas_H_ListFormWorkflow` · `Sas_H_ListFormCustomization` · `Sas_H_ListFormImport` | host | Ekran tanımlarının kendisi. Bu dosyada anlatılan bütün JSON kolonlarının canlı hâli; düzenleyicisi `/admin/listform/edit/{kod}`. | | **Developer Kit › Wizard Manager** | menü kodu `App.Listforms.WizardManager` · yetki `App.Listforms.Wizard` · `/admin/listform/wizardManager` | — | host | Wizard seed dosyalarının listesi, düzenlemesi, silinmesi, export/import'u. Sihirbazın kendisi ayrı bir rotadadır: `/admin/listform/wizard`. | | **Developer Kit › MCP Servers** | `App.DeveloperKit.McpServers` · `/admin/list/App.DeveloperKit.McpServers` | `Sas_H_McpServer` | host | `/mcp` ucunun kiracı bazlı sözleşmesi: `Name`, `Url`, `Tenants`, `AllowedTools` (boşsa tüm araçlar, doluysa yalnızca seçilenler açık), `MaxRows`. Araç listesi bu kayda göre süzülür, çağrı anında aynı kural yeniden uygulanır. Yetkiler: ekran için `App.DeveloperKit.McpServers`, araç çağırma için `.Invoke`, veri yazma için `.Write`, prompt ile tablo/ekran/bileşen geliştirme (`create_table`, wizard deploy, custom component kaydı) için `.Develop` (host; Wizard ve Custom Components yetkilerini ayrıca ister). Kiracı kullanıcısı yetkisi ne olursa olsun yalnızca okur (`McpToolNames.TenantTools`: şema araçları, `query_list`, `run_select`); yazma, `create_table`, wizard, component ve `run_query` kiracıya kapalıdır. Host için tek sunucu anahtarı `AllowedTools`. MCP yetki vermez: kullanıcı yalnızca uygulamada okuyabildiği listeleri görür ve sorgular; ham SQL `App.SqlQueryManager` ister. Kayıtlar `data/App.DeveloperKit.McpServers.json` dosyasına senkronlanır. Araçlar ve şema keşfinin kaynağı: `README.md` §13.9. | @@ -1297,7 +1297,7 @@ yazma, oradan yeni bir Custom Component üret. `Services` sayfası yoktur. | Route | Bileşen | Veri | | --- | --- | --- | -| `/home` · `/about` · `/contact` | Custom Component (`PublicHomePage` · `PublicAboutPage` · `PublicContactPage`) | `data/App.DeveloperKit.CustomComponents.json` · tasarımcı: DeveloperKit > Custom Components | +| `/home` · `/about` · `/contact` | Custom Component (`PublicHomePage` · `PublicAboutPage` · `PublicContactPage`) | `data/App.DeveloperKit.CustomComponents/{Id}.json` · tasarımcı: DeveloperKit > Custom Components | | `/products` · `/checkout` · `/payment` · `/success` | `views/public/{Products,Checkout,Payment,Success}` | `Sas_H_Product` · `Sas_H_PaymentMethod` · `Sas_H_InstallmentOption` · `Sas_H_Order` | | `/blog` · `/blog/:id` | `views/public/{Blog,BlogDetail}` | `Sas_H_BlogPost` · `Sas_H_BlogCategory` | | `/demo` | `views/public/Demo` | `Sas_H_Demo` | diff --git a/.github/instructions/lowcode.instructions.md b/.github/instructions/lowcode.instructions.md index 871fd2b5..84249815 100644 --- a/.github/instructions/lowcode.instructions.md +++ b/.github/instructions/lowcode.instructions.md @@ -55,7 +55,7 @@ configs/seeds/host/ ├── wizard/{WizardName}.json ← ekran + menü + yetki + dil anahtarları └── data/{ListFormCode}.json ← ekrandan girilen liste verisinin aynası (opsiyonel) Custom Component'ler de burada yaşar: - data/App.DeveloperKit.CustomComponents.json + data/App.DeveloperKit.CustomComponents/{Id}.json ``` Tenant kapsamı isteniyorsa `host/` yerine `tenants/{tenantId}/`; iç düzen aynıdır. @@ -66,7 +66,7 @@ Tenant kapsamı isteniyorsa `host/` yerine `tenants/{tenantId}/`; iç düzen ayn | --- | --- | --- | --- | | 1 | Tablo / view | `sql/object/…` | Ekranın bağlanacağı nesne önce var olmalı. MCP'de yeni tablo `create_table` ile kurulur (script ve dosya tasarımcıyla aynı); view MCP'den kurulamaz | | 2 | CRUD uçları (gerekiyorsa) | `crud/{Entity}.json` | Custom Component'in data source'ları buna bakar | -| 3 | Custom Component (gerekiyorsa) | `data/App.DeveloperKit.CustomComponents.json` | Wizard `Custom` yolunda bileşen adını arar | +| 3 | Custom Component (gerekiyorsa) | `data/App.DeveloperKit.CustomComponents/{Id}.json` | Wizard `Custom` yolunda bileşen adını arar | | 4 | Wizard | `wizard/{Ad}.json` | Menü, yetki, dil anahtarı ve ListForm'u üretir | | 5 | Alt ekranlar (varsa) | ek `wizard/…` dosyaları | Ana wizard'dan **önceki** `SeededAt` damgasını alır | | 6 | Liste verisi (isteniyorsa) | `data/{ListFormCode}.json` | Ekran ve tablo hazır olmalı; `ListForm.SeedFilePath` bu yolu gösterir | @@ -2030,7 +2030,7 @@ ListForm ile ifade edilemeyen ekranın yolu **fiziksel React sayfası değil**, ### 7.1 Seed dosyası -`configs/seeds/{kapsam}/data/App.DeveloperKit.CustomComponents.json` — Custom Components +`configs/seeds/{kapsam}/data/App.DeveloperKit.CustomComponents/{Id}.json` — Custom Components ekranının veri dosyası. Bileşenin **kendi dosyası yoktur**; her bileşen bu dosyada bir satırdır ve dosya `data/{ListFormCode}.json` sözleşmesini (§0.1) izler. @@ -2442,7 +2442,7 @@ Custom yolunda `ListForm` **üretilmez**; `Title`/`Desc` dil anahtarları da ür Bu çift, custom component'in menüye nasıl bağlandığının çalışan örneğidir. İncelenecek dosyalar: ``` -configs/seeds/host/data/App.DeveloperKit.CustomComponents.json ← RoleComponent satırı +configs/seeds/host/data/App.DeveloperKit.CustomComponents/{Id}.json ← RoleComponent satırı configs/seeds/host/crud/AbpRoles.json · AbpUsers.json · AbpUserRoles.json configs/seeds/host/wizard/RoleList.json ``` @@ -2484,7 +2484,7 @@ butonu, ekranın **özyinelemeli bağımlılık kapanışını** (`WizardExportC ``` wizard/{dosya}.json (ekranın kendisi + SubFormsDto[].Code ile bağlı alt formların wizard dosyaları, özyinelemeli) -data/App.DeveloperKit.CustomComponents.json (component + bağımlılıklarına süzülmüş kopya) +data/App.DeveloperKit.CustomComponents/{Id}.json (component + bağımlılıklarına süzülmüş kopya) data/{ListFormCode}.json (ekranın SeedFilePath'i doluysa kendi veri aynası) crud/{entity}.json (component'lerin/nesnenin kullandığı endpoint'ler) {sql|postgres}/{object|execute}/{nesne}.sql (SelectCommand'ın nesnesi + lookup sorgularının @@ -3579,7 +3579,15 @@ kullanılacağı** `lowcode-reference.instructions.md` §12'dedir. Ekrandan girilen kayıtların (widget tanımı, parametre tablosu, sabit liste) veritabanı sıfırlandığında geri gelmesi için kullanılır. Bağlantı ListForm kaydındaki `SeedFilePath` -alanıdır; boşsa ne geri yükleme ne de senkronizasyon yapılır. Ekrandaki hangi işlemin dosyaya +alanıdır; boşsa ne geri yükleme ne de senkronizasyon yapılır. + +**Hedef dosya ya da klasördür.** `SeedFilePath` `.json` ile bitiyorsa bütün satırlar tek +dosyadadır (varsayılan). Uzantısız yazılırsa hedef klasördür (`data/{ListFormCode}`) ve her satır +`{anahtar}.json` olarak kendi dosyasında durur; klasör yoksa ilk kayıtta oluşturulur. Klasör modu +satırı büyük ya da sık yazılan ekranlar içindir — şu an tek örnek Custom Components +(`SeedPathResolver.GetDataFolderPath`). Uygulayıcı `data/` altını özyinelemeli tarar ve bir +klasörün tamamını **tek unit of work'te**, ekran tanımını bir kez çözerek uygular; dosyadaki +`ListFormCode` boşsa ekran kodu klasör adından gelir. Ekrandaki hangi işlemin dosyaya yansıyacağını `SeedSyncInsert`, `SeedSyncUpdate` ve `SeedSyncDelete` bayrakları belirler — üçü de kapalıyken dosya yalnızca seed sırasında okunur, hiç yazılmaz. @@ -3649,7 +3657,7 @@ Kurallar: kur, `required` bayrağını bilinçli seç. 4. Görünürlük ve komut yetkilerini ver (`designerPermission`, `*Permission`). 5. Wizard `ComponentKind: 1` ile menüye bağla. -6. Doğrula: `data/App.DeveloperKit.CustomComponents.json` içindeki satır ve ilgili `crud/*.json` dosyaları oluştu mu. +6. Doğrula: `data/App.DeveloperKit.CustomComponents/{Id}.json` içindeki satır ve ilgili `crud/*.json` dosyaları oluştu mu. ### 10.4 "Kanban panosu" @@ -3695,7 +3703,7 @@ Bu tür isteklerin tamamı aynı iskeleti kullanır. Kullanıcı "avans talep fo configs/seeds/host/ ├── sql/object/Hr_T_AdvanceRequest.sql ← tablo (+ PostgreSQL hedefse postgres/object/…) ├── crud/Hr_T_AdvanceRequest.json ← yalnızca Custom Component yolunda gerekir -├── data/App.DeveloperKit.CustomComponents.json ← yalnızca serbest yerleşim isteniyorsa +├── data/App.DeveloperKit.CustomComponents/{Id}.json ← yalnızca serbest yerleşim isteniyorsa ├── wizard/AdvanceRequestApprovals.json ← (varsa) menüsüz alt ekran, küçük SeededAt └── wizard/AdvanceRequests.json ← ana ekran + menü + yetki + dil + onay akışı ``` diff --git a/README.md b/README.md index 93ab7ec9..87854480 100644 --- a/README.md +++ b/README.md @@ -270,7 +270,7 @@ Aktif veritabanı sağlayıcısına göre `sql/` (SQL Server) ya da `postgres/` | `{sql\|postgres}/execute` | Nesneyi oluşturur; ayrıca tüm migration ve seed'ler bittikten sonra `FinalSeeder` dosya adından türettiği procedure'ü çalıştırır (dosya adı = procedure adı, `schema.Procedure.sql` de olur). | | `wizard` | ListForm Wizard'ın ürettiği `.json` konfigürasyonları; `WizardDataSeeder` kendi kapsamındaki dosyaları okur. | | `crud` | CrudEndpoint konfigürasyonları; `CrudDataSeeder` kendi kapsamındaki dosyaları `ICrudEndpointSeedApplier` üzerinden uygular. Politika **damga karşılaştırmalıdır**: eksik endpoint her zaman eklenir, mevcut bir kayıt yalnızca dosyanın `GeneratedAt` damgası kaydın `LastModificationTime`/`CreationTime` değerinden **yeniyse** güncellenir ya da silinir; aksi halde kayıt korunur ve log'a drift uyarısı düşer. Böylece CRUD Endpoint Manager'dan yapılan bir revizyon depodan gelen eski bir dosya yüzünden migrate sırasında geri alınmaz. Damgası olmayan (eski biçimde yazılmış) dosya hiçbir zaman yeni sayılmaz. | -| `data` | Ekranlardan girilen listelerin veri aynası (`{ListFormCode}.json`). `ListForm.SeedFilePath` dolu olan ekranda `SeedSyncInsert/Update/Delete` bayraklarından işaretli olan işlemler dosyaya yansır (`ListFormSeedDataSynchronizer`); migrate/seed sırasında bütün contributor'lar bittikten sonra `ListFormSeedDataApplier` dosyayı geri uygular: anahtarı veritabanında **olmayan** satırları ekler, var olanlara dokunmaz. Aynı uygulayıcı wizard import'unun kapanışında da (`CompleteImport`) yalnızca o import'ta yazılan dosyalar için, `overwriteExisting` açık olarak çalışır — orada var olan satır dosyadaki değerlerle güncellenir. | +| `data` | Ekranlardan girilen listelerin veri aynası: tek dosya (`{ListFormCode}.json`) ya da satır başına dosya tutan klasör (`{ListFormCode}/{anahtar}.json`; klasör özyinelemeli taranır ve tamamı tek unit of work'te uygulanır). `ListForm.SeedFilePath` dolu olan ekranda `SeedSyncInsert/Update/Delete` bayraklarından işaretli olan işlemler dosyaya yansır (`ListFormSeedDataSynchronizer`); migrate/seed sırasında bütün contributor'lar bittikten sonra `ListFormSeedDataApplier` dosyayı geri uygular: anahtarı veritabanında **olmayan** satırları ekler, var olanlara dokunmaz. Aynı uygulayıcı wizard import'unun kapanışında da (`CompleteImport`) yalnızca o import'ta yazılan dosyalar için, `overwriteExisting` açık olarak çalışır — orada var olan satır dosyadaki değerlerle güncellenir. | Klasör kökünde kalmış eski `.sql` dosyaları geriye dönük uyumluluk için hâlâ işlenir, fakat `SqlDataSeeder` bunları uyarı ile loglar — ilgili klasöre taşınmaları beklenir. @@ -372,7 +372,7 @@ Ekran davranışı büyük ölçüde JSON kolonlarında saklanır. Öne çıkanl | `ColumnOptionJson`, `PagerOptionJson`, `SelectionJson`, `StateStoringJson` | Sütun, sayfalama, seçim ve durum saklama ayarları. | | `Insert/Update/DeleteCommand` (+ `Before` / `After`) | CRUD komutları ve öncesi/sonrası çalışacak SQL kancaları. | | `Insert/Update/DeleteServiceAddress` | Varsayılan olarak `list-form-data/*`; özel endpoint'e yönlendirilebilir. | -| `SeedFilePath` | Doluysa seed sırasında listenin verisi kapsam klasöründeki bu JSON dosyasından geri yüklenir (`data/{ListFormCode}.json`). | +| `SeedFilePath` | Doluysa seed sırasında listenin verisi kapsam klasöründeki bu hedeften geri yüklenir: `.json` ile biten değer tek dosya (`data/{ListFormCode}.json`), uzantısız değer satır başına dosya tutan klasördür (`data/{ListFormCode}/{anahtar}.json`). | | `SeedSyncInsert` / `SeedSyncUpdate` / `SeedSyncDelete` | Ekranda yapılan hangi işlemin seed dosyasına yansıyacağı; kapalıysa o işlem dosyaya dokunmaz. | | `SubFormsJson` | Ana-detay ilişkisi (ParentFieldName → ChildFieldName eşlemesi). | | `WidgetsJson` | Ekranın üstünde gösterilecek KPI kartları. | @@ -579,8 +579,8 @@ eklenir ve onların bağımlılıkları da aynı kurallarla toplanır. Böylece | Yol | İçerik | | --- | --- | | `wizard/{dosya}.json` | Wizard tanımının kendisi **ve** `SubFormsDto[].Code` ile bağlı alt form ekranlarının wizard dosyaları (özyinelemeli) | -| `data/App.DeveloperKit.CustomComponents.json` | Custom yolunda bağlı component ve bağımlılıkları — dosyanın yalnızca bu satırlara süzülmüş kopyası | -| `data/{ListFormCode}.json` | Ekranın `SeedFilePath` değeri doluysa kendi veri aynası | +| `data/App.DeveloperKit.CustomComponents/{Id}.json` | Custom yolunda bağlı component ve bağımlılıkları — yalnızca bu bileşenlerin satır dosyaları | +| `data/{ListFormCode}.json` ya da `data/{ListFormCode}/{anahtar}.json` | Ekranın `SeedFilePath` değeri doluysa kendi veri aynası (dosya ya da klasör) | | `crud/{entity}.json` | Component'lerin/nesnenin kullandığı CRUD endpoint tanımları | | `{sql\|postgres}/{object\|execute}/{nesne}.sql` | `SelectCommand`'a karşılık gelen SQL nesnesi, lookup sorgularının (`LookupDto.LookupQuery`) `FROM`/`JOIN` ile işaret ettiği nesneler ve Custom yolunda CRUD endpoint'lerinin dayandığı tablolar | @@ -739,7 +739,7 @@ Teknik kullanıcılar için `/admin/developerkit` altında toplanan araçlar: | **Custom Endpoint** | `/admin/list/App.DeveloperKit.CustomEndpoints` | SQL veya servis tanımından REST endpoint üretimi; GET/POST/PUT/DELETE için ayrı yetki kapıları (`App.DeveloperKit.CustomEndpoints.*`) ve kullanıcı/rol/global erişim kuralları. | | **CRUD Endpoint** | SQL Query Manager içinde | Tablo tanımından tam CRUD endpoint kümesi üretimi. Ayrı ekranı yoktur; bkz. aşağıdaki not. | | **Dynamic Service** | `/admin/list/App.DeveloperKit.DynamicServices` | Liste bir ListForm ekranıdır (KPI şeridi, Swagger butonu, kart/ızgara görünümü); kod yazma listeden açılan `DynamicServiceEditor` diyalogundadır: Monaco editörü, Roslyn ile derleme (`TestCompile`), yayınlama (`Publish`) ve çalışan uygulamaya controller olarak kaydetme. Yetkiler: Create/Edit/Delete/Manage/TestCompile/Publish/ViewCode. | -| **Custom Component** | `/admin/list/App.DeveloperKit.CustomComponents` | React bileşenini veritabanında saklama; `@babel/standalone` ile tarayıcıda derleyip route'a bağlama. Liste bir ListForm ekranıdır (KPI şeridi, ızgara/kart görünümü, grid'in kendi ekleme/düzenleme formu); satırdaki **Design** butonu `/admin/developerkit/components/design/{id}` tasarımcısını, **Open URL** butonu bileşenin `RoutePath`'ini açar. Kayıtlar `configs/seeds/host/data/App.DeveloperKit.CustomComponents.json` dosyasına senkronlanır. Yetkiler `App.DeveloperKit.CustomComponents{,.Create,.Update,.Delete,.Export,.Import}`. | +| **Custom Component** | `/admin/list/App.DeveloperKit.CustomComponents` | React bileşenini veritabanında saklama; `@babel/standalone` ile tarayıcıda derleyip route'a bağlama. Liste bir ListForm ekranıdır (KPI şeridi, ızgara/kart görünümü, grid'in kendi ekleme/düzenleme formu); satırdaki **Design** butonu `/admin/developerkit/components/design/{id}` tasarımcısını, **Open URL** butonu bileşenin `RoutePath`'ini açar. Kayıtlar `configs/seeds/host/data/App.DeveloperKit.CustomComponents/{Id}.json` klasörüne senkronlanır. Yetkiler `App.DeveloperKit.CustomComponents{,.Create,.Update,.Delete,.Export,.Import}`. | | **Visual Designer** | `/admin/developerkit/components/design/:id` | Custom Component listesindeki **Design** butonundan açılır; sürükle-bırak kanvas ile bileşen üretimi ve kod üretimi (`visualDesigner/codeGenerator.ts`). | | **ListForm** | `/admin/list/App.Listforms.Listform` | Var olan ekranların ham tanım listesi; buradan `/admin/listform/edit/{kod}` sekmeli editörüne geçilir. | | **Wizard Manager** | `/admin/listform/wizardManager` | Üretilmiş wizard seed dosyalarının listesi/kartı, düzenleme, silme, DB migrate, export/import. Yetki `App.Listforms.Wizard{,.Create,.Update,.Delete,.Export,.Import}`. | @@ -798,9 +798,10 @@ satırındaki `/*__SOZSOFT_VISUAL_DESIGNER__…__*/` yorumunda. Böylece yalnız tasarımcı dokümanı geri okunabilir. `sourceMode` alanı `visual` ya da `code` olur; kod moduna geçilen bir bileşen kanvasa geri dönmez. -**Seed senkronizasyonu.** Bileşenlerin seed kaynağı Custom Components ekranının veri dosyasıdır: -`configs/seeds/{host|tenants/{tenantId}}/data/App.DeveloperKit.CustomComponents.json`. Bileşene -yazan **iki yol** da bu dosyaya işlenir (`ListFormSeedDataSynchronizer`): listeden yapılan +**Seed senkronizasyonu.** Bileşenlerin seed kaynağı Custom Components ekranının veri klasörüdür: +`configs/seeds/{host|tenants/{tenantId}}/data/App.DeveloperKit.CustomComponents/{Id}.json` — her +bileşen kendi dosyasındadır; bileşen kodu satırın içinde durduğu için tek dosya hızla büyürdü. +Bileşene yazan **iki yol** da bu klasöre işlenir (`ListFormSeedDataSynchronizer`): listeden yapılan ekleme/güncelleme/silme ve Visual Designer'ın kaydı (`CustomComponentAppService`). Veritabanı sıfırlandığında `ListFormSeedDataApplier` satırları geri yazar; `Code`, `Props` ve `DataSources` kolonları bu yüzden ekranda gizli alan olarak tanımlıdır. `DataSources` sütunu tasarımcı @@ -863,7 +864,7 @@ tıklamak seçim yapar, sayfayı değiştirmez — tıklama yakalama evresinde d **Diğer notlar.** `PlatformIcon` tasarımcı dokümanındaki ikon adını çözer; `selectComponents.ts` `Select.componentAs` için saklanan adı (`ReactSelect`, `CreatableSelect`, `AsyncSelect`) gerçek bileşene çevirir — kanvas ve üretilen bileşenin aynı adı aynı şekilde yorumlamasını sağlayan tek -nokta budur. Yeni bileşen `configs/seeds/host/data/App.DeveloperKit.CustomComponents.json` +nokta budur. Yeni bileşen `configs/seeds/host/data/App.DeveloperKit.CustomComponents/{Id}.json` içindeki bir satır olarak başlar. **Dinamik servis yaşam döngüsü:** `DynamicServiceCompiler` kodu derler → @@ -993,7 +994,7 @@ bileşen otomatik olarak public route olur ve `PublicLayout` altında yayınlan DeveloperKit > Custom Components (Visual Designer) üzerinden değişir. Aynı yolu bir fiziksel route ile paylaşan custom component kazanır; karşılığı olan dosyası bulunmayan rota kaydı ise hiç yüklenmez (`hasPhysicalComponent`) — seeder yalnızca eklediği için eski kayıtlar veritabanında -kalsa bile ekran patlamaz. Seed: `configs/seeds/host/data/App.DeveloperKit.CustomComponents.json`; +kalsa bile ekran patlamaz. Seed: `configs/seeds/host/data/App.DeveloperKit.CustomComponents/{Id}.json`; bileşenin ayrı dosyası yoktur, her bileşen bu dosyada bir satırdır. Runtime kapsamı sınırlıdır: bileşen kodundaki `import` satırları derlemede temizlenir, @@ -1005,7 +1006,7 @@ aynı sözlük), gezinme `` ile yazılır. Kullanıcıya görünen metin gelir. **Tailwind sınıf yüzeyi.** Bileşenin kodu veritabanında yaşar; derleme sırasında taranan tek -kaynak seed dosyasıdır (`configs/seeds/*/data/App.DeveloperKit.CustomComponents.json`). Bu dosya +kaynak seed dosyalarıdır (`configs/seeds/*/data/App.DeveloperKit.CustomComponents/{Id}.json`). Bu klasör depo kökünde, `ui/` dışındadır — derleme bağlamı yalnızca `ui/` klasörü olduğunda (docker, ayrı checkout) glob boş döner ve seed'e özgü sınıflar (`md:col-span-4`, `bg-gray-950/80`, `opacity-20`) CSS'e hiç girmez: uygulama yerelde doğru, sunucuda stilsiz görünür. Bu yüzden aynı sınıflar var @@ -1748,7 +1749,7 @@ configs/seeds/ ├── host/ # host kapsamı │ ├── wizard/ # {Ad}.json → WizardDataSeeder │ ├── crud/ # {EntityName}.json → CrudEndpointSeedApplier -│ ├── data/ # {ListFormCode}.json → ListFormSeedDataApplier +│ ├── data/ # {ListFormCode}.json ya da {ListFormCode}/{anahtar}.json → ListFormSeedDataApplier │ │ # App.Menus.{Routes,Module,Menu}.json — route/modül/menü ağacı │ │ # Abp.Identity.Permission{Groups,s}.json — yetki tanımları │ │ # App.Definitions.{CountryGroup,Country,City,District}.json — adres zinciri diff --git a/api/src/Sozsoft.Platform.Application/ListForms/ListFormSeedDataSynchronizer.cs b/api/src/Sozsoft.Platform.Application/ListForms/ListFormSeedDataSynchronizer.cs index 49640604..9897a6ca 100644 --- a/api/src/Sozsoft.Platform.Application/ListForms/ListFormSeedDataSynchronizer.cs +++ b/api/src/Sozsoft.Platform.Application/ListForms/ListFormSeedDataSynchronizer.cs @@ -46,10 +46,15 @@ public enum SeedSyncOperation } /// -/// ListForm.SeedFilePath dolu olan ekranlarin verisi kapsam klasorundeki bir JSON dosyasinda -/// aynalanir; veritabani sifirlandiginda ListFormSeedDataApplier ayni dosyadan geri yukler. -/// Senkronizasyon satir bazlidir: yalnizca islem goren satir okunup dosyadaki karsiligina yazilir, +/// ListForm.SeedFilePath dolu olan ekranlarin verisi kapsam klasorunde aynalanir; veritabani +/// sifirlandiginda ListFormSeedDataApplier ayni yerden geri yukler. +/// Senkronizasyon satir bazlidir: yalnizca islem goren satir okunup karsiligina yazilir, /// boylece her kayitta listenin tamami sorgulanmaz ve dosyadaki satir sirasi korunur. +/// +/// Hedef iki bicimde olabilir: deger .json ile bitiyorsa butun satirlar tek dosyadadir, +/// aksi halde klasordur ve her satir {anahtar}.json olarak kendi dosyasinda durur. Klasor +/// modunda bir kayit yalnizca kendi dosyasina dokunur; tek dosya her kayitta bastan yazilir. +/// /// public class ListFormSeedDataSynchronizer( IRepository listFormRepository, @@ -60,21 +65,33 @@ public class ListFormSeedDataSynchronizer( { private const int SyncAllPageSize = 10000; + /// + /// Ekranin seed hedefi. true ise her satir klasorun altinda kendi + /// dosyasindadir ({anahtar}.json); false ise butun satirlar tek dosyadadir. + /// + private sealed record SeedTarget(string Path, bool IsFolder); + public async Task SyncRowAsync(string listFormCode, object key, SeedSyncOperation operation) { - await ExecuteAsync(listFormCode, IsEnabled(operation), async (listForm, filePath) => + await ExecuteAsync(listFormCode, IsEnabled(operation), async (listForm, target) => { // Insert ucu her veri kaynaginda anahtari geri dondurmez; anahtarsiz kaldigimizda - // ya da satir okunamadiginda dosya listenin tamamiyla yenilenir, bayat kalmaz. + // ya da satir okunamadiginda hedef listenin tamamiyla yenilenir, bayat kalmaz. var row = key == null ? null : await ReadRowAsync(listForm, key); if (row == null) { logger.LogInformation("Row '{Key}' could not be read for '{ListFormCode}', seed file is rewritten from the list.", key, listForm.ListFormCode); - await WriteAllRowsAsync(listForm, filePath); + await WriteAllRowsAsync(listForm, target); return; } - var seedFile = await ReadSeedFileAsync(filePath, listForm); + if (target.IsFolder) + { + await WriteRowFileAsync(listForm, target, key, row); + return; + } + + var seedFile = await ReadSeedFileAsync(target.Path, listForm); var index = FindRowIndex(seedFile, listForm.KeyFieldName, key); if (index >= 0) @@ -82,21 +99,39 @@ public class ListFormSeedDataSynchronizer( else seedFile.Rows.Add(row); - await WriteSeedFileAsync(filePath, seedFile); + await WriteSeedFileAsync(target.Path, seedFile); }); } public async Task RemoveRowsAsync(string listFormCode, object[] keys) { - await ExecuteAsync(listFormCode, listForm => listForm.SeedSyncDelete, async (listForm, filePath) => + await ExecuteAsync(listFormCode, listForm => listForm.SeedSyncDelete, async (listForm, target) => { - if (keys == null || keys.Length == 0 || !File.Exists(filePath)) + if (keys == null || keys.Length == 0) { - await WriteAllRowsAsync(listForm, filePath); + await WriteAllRowsAsync(listForm, target); return; } - var seedFile = await ReadSeedFileAsync(filePath, listForm); + if (target.IsFolder) + { + foreach (var key in keys) + { + var rowPath = ResolveRowFilePath(target, key); + if (rowPath != null && File.Exists(rowPath)) + File.Delete(rowPath); + } + + return; + } + + if (!File.Exists(target.Path)) + { + await WriteAllRowsAsync(listForm, target); + return; + } + + var seedFile = await ReadSeedFileAsync(target.Path, listForm); var removed = false; foreach (var key in keys) @@ -110,7 +145,7 @@ public class ListFormSeedDataSynchronizer( } if (removed) - await WriteSeedFileAsync(filePath, seedFile); + await WriteSeedFileAsync(target.Path, seedFile); }); } @@ -126,8 +161,8 @@ public class ListFormSeedDataSynchronizer( : listForm => listForm.SeedSyncUpdate; } - /// Dosyayi listenin tamamiyla yeniden yazar. - private async Task WriteAllRowsAsync(ListForm listForm, string filePath) + /// Hedefi listenin tamamiyla yeniden yazar. + private async Task WriteAllRowsAsync(ListForm listForm, SeedTarget target) { var result = await listFormSelectAppService.GetSelectAsync(new SelectRequestDto { @@ -138,13 +173,116 @@ public class ListFormSeedDataSynchronizer( RequireGroupCount = false, }); + List> rows = ToRows((object)result?.Data); + + if (target.IsFolder) + { + await WriteRowFilesAsync(listForm, target, rows); + return; + } + // Uygulama sirasi dosyanin kendi bilgisidir; listeden uretilemez, var olan degeri korunur. + var seedFile = await ReadSeedFileAsync(target.Path, listForm); + seedFile.Rows = rows; + + await WriteSeedFileAsync(target.Path, seedFile); + } + + /// + /// Klasor modunda her satiri kendi dosyasina yazar ve listede kalmayan satirlarin dosyalarini + /// siler — klasor listenin aynasidir. + /// + /// Temizlik yalnizca liste tek sayfaya sigdiginda yapilir: sayfa sinirina dayanan bir listede + /// gorulmeyen satirlarin dosyasi silinirse veri kaybolur. + /// + /// + private async Task WriteRowFilesAsync(ListForm listForm, SeedTarget target, List> rows) + { + var order = await ReadFolderOrderAsync(target); + var written = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (var row in rows) + { + var fileName = SeedPathResolver.GetRowFileName(ToComparableText(row.GetValueOrDefault(listForm.KeyFieldName))); + if (fileName == null) + { + logger.LogWarning("A row without a key was skipped while writing the '{ListFormCode}' seed folder.", listForm.ListFormCode); + continue; + } + + var filePath = Path.Combine(target.Path, fileName); + var seedFile = await ReadSeedFileAsync(filePath, listForm); + seedFile.Rows = [row]; + seedFile.Order = order; + + await WriteSeedFileAsync(filePath, seedFile); + written.Add(fileName); + } + + if (rows.Count >= SyncAllPageSize) + return; + + foreach (var filePath in Directory.EnumerateFiles(target.Path, "*.json")) + { + if (!written.Contains(Path.GetFileName(filePath))) + File.Delete(filePath); + } + } + + /// Klasor modunda tek bir satirin dosyasini yazar. + private async Task WriteRowFileAsync(ListForm listForm, SeedTarget target, object key, Dictionary row) + { + var filePath = ResolveRowFilePath(target, key); + if (filePath == null) + { + logger.LogWarning("Key '{Key}' cannot be used as a file name, the '{ListFormCode}' seed row was not written.", key, listForm.ListFormCode); + return; + } + + var isNew = !File.Exists(filePath); + var seedFile = await ReadSeedFileAsync(filePath, listForm); - seedFile.Rows = ToRows((object)result?.Data); + seedFile.Rows = [row]; + + // Yeni dosya uygulama sirasini kardeslerinden alir; 0 ile yazilsaydi ekran, bagimli oldugu + // tanimlardan once uygulanirdi. + if (isNew) + seedFile.Order = await ReadFolderOrderAsync(target); await WriteSeedFileAsync(filePath, seedFile); } + /// + /// Klasordeki satir dosyalarinin tasidigi uygulama sirasi (Order); bir ekranin butun satir + /// dosyalarinda ayni degerdir, bu yuzden ilk kardes dosyadan okunur. + /// + private static async Task ReadFolderOrderAsync(SeedTarget target) + { + var sibling = Directory.EnumerateFiles(target.Path, "*.json").FirstOrDefault(); + if (sibling == null) + return 0; + + try + { + var seedFile = JsonSerializer.Deserialize( + await File.ReadAllTextAsync(sibling), + ListFormSeedDataFileDto.JsonOptions); + + return seedFile?.Order ?? 0; + } + catch (JsonException) + { + return 0; + } + } + + private static string? ResolveRowFilePath(SeedTarget target, object key) + { + var fileName = SeedPathResolver.GetRowFileName(ToComparableText(key)); + + return fileName == null ? null : Path.Combine(target.Path, fileName); + } + /// /// Seed dosyasi bagli degilse ya da islemin bayragi kapaliysa hicbir sey yapmaz. /// Senkronizasyon hatasi kullanicinin kayit islemini bozmaz; yalnizca loglanir. @@ -152,7 +290,7 @@ public class ListFormSeedDataSynchronizer( private async Task ExecuteAsync( string listFormCode, Func isEnabled, - Func action) + Func action) { if (string.IsNullOrWhiteSpace(listFormCode)) return; @@ -163,14 +301,17 @@ public class ListFormSeedDataSynchronizer( if (listForm == null || string.IsNullOrWhiteSpace(listForm.SeedFilePath) || !isEnabled(listForm)) return; - var filePath = SeedPathResolver.GetScopePath( + var isFolder = SeedPathResolver.IsSeedFolderPath(listForm.SeedFilePath); + + var fullPath = SeedPathResolver.GetScopePath( configuration, currentTenant.Id, - listForm.SeedFilePath.Replace('\\', '/').Split('/')); + listForm.SeedFilePath.Replace('\\', '/').Trim('/').Split('/')); - Directory.CreateDirectory(Path.GetDirectoryName(filePath)!); + // Klasor ilk kayitta olusur; ekran, hedefi diskte hazir bulmak zorunda degildir. + Directory.CreateDirectory(isFolder ? fullPath : Path.GetDirectoryName(fullPath)!); - await action(listForm, filePath); + await action(listForm, new SeedTarget(fullPath, isFolder)); } catch (Exception ex) { diff --git a/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs b/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs index 8b3540eb..7c30293e 100644 --- a/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs +++ b/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs @@ -2082,12 +2082,24 @@ public class ListFormWizardAppService( var isScriptFolder = segments[0].Equals(SeedPathResolver.SqlFolder, StringComparison.OrdinalIgnoreCase) || segments[0].Equals(SeedPathResolver.PostgresFolder, StringComparison.OrdinalIgnoreCase); + var isDataFolder = segments[0].Equals(SeedPathResolver.DataFolder, StringComparison.OrdinalIgnoreCase); + // sql/postgres altinda object|execute klasoru zorunludur; digerlerinde alt klasor yoktur. if (isScriptFolder) { if (segments.Length != 3 || !ImportAllowedScriptFolders.Contains(segments[1], StringComparer.OrdinalIgnoreCase)) return null; } + else if (isDataFolder) + { + // Veri ya tek dosyadadir (data/{ListFormCode}.json) ya da satir basina dosya tutan bir + // klasorde (data/{ListFormCode}/{anahtar}.json); daha derin bir yol kural disidir. + if (segments.Length is not (2 or 3) + || (segments.Length == 3 && segments[1].IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)) + { + return null; + } + } else if (segments.Length != 2) { return null; diff --git a/api/src/Sozsoft.Platform.Application/ListForms/WizardExportCollector.cs b/api/src/Sozsoft.Platform.Application/ListForms/WizardExportCollector.cs index fa7bc9be..3b159298 100644 --- a/api/src/Sozsoft.Platform.Application/ListForms/WizardExportCollector.cs +++ b/api/src/Sozsoft.Platform.Application/ListForms/WizardExportCollector.cs @@ -57,6 +57,9 @@ public sealed class WizardExportCollector(string scopeRoot) /// ListFormCode -> wizard seed dosyasi; alt form cozumu icin bir kez kurulur. private Dictionary? wizardFilesByListFormCode; + /// Custom component seed dosyalari (kapsam-goreli yol + icerik); bir kez okunur. + private List<(string Path, ListFormSeedDataFileDto File)>? customComponentFiles; + public IReadOnlyList Warnings => warnings; /// Verilen wizard seed dosyasinin tasinabilir kapanisini toplar. @@ -121,7 +124,7 @@ public sealed class WizardExportCollector(string scopeRoot) // Ekranin verisi seed dosyasina aynalaniyorsa arsiv onsuz eksiktir. var seedFilePath = listForm?.SeedFilePath; - if (!string.IsNullOrWhiteSpace(seedFilePath) && !AddDiskFile(seedFilePath, $"{screen} verisi")) + if (!string.IsNullOrWhiteSpace(seedFilePath) && !AddSeedData(seedFilePath, $"{screen} verisi")) warnings.Add($"{seedFilePath}: ekranın veri seed dosyası bulunamadı."); foreach (var field in seed.Fields ?? []) @@ -249,17 +252,30 @@ public sealed class WizardExportCollector(string scopeRoot) if (customComponents.Count == 0) return; - var seedFile = await ReadCustomComponentFileAsync(); - if (seedFile == null) - return; + foreach (var (relativePath, seedFile) in await ReadCustomComponentFilesAsync()) + { + // Dosyadaki satir sirasi korunur; hedefte satirlar bu sirayla birlestirilir. + var rows = seedFile.Rows + .Where(x => customComponents.Contains(GetRowText(x, CustomComponentNameField) ?? string.Empty)) + .ToList(); - // Dosyadaki satir sirasi korunur; hedefte satirlar bu sirayla birlestirilir. - seedFile.Rows = [.. seedFile.Rows.Where(x => customComponents.Contains(GetRowText(x, CustomComponentNameField) ?? string.Empty))]; + // Satir basina dosya tutan klasorde ilgisiz bilesenin dosyasi arsive hic girmez. + if (rows.Count == 0) + continue; - var relativePath = SeedPathResolver.GetDataFilePath(CustomComponentsListFormCode); - var content = JsonSerializer.Serialize(seedFile, ListFormSeedDataFileDto.JsonOptions); + var filtered = new ListFormSeedDataFileDto + { + ListFormCode = seedFile.ListFormCode, + KeyFieldName = seedFile.KeyFieldName, + GeneratedAt = seedFile.GeneratedAt, + Order = seedFile.Order, + Rows = rows, + }; - items[relativePath] = new WizardExportItem(relativePath, "Custom component tanımı", null, content); + var content = JsonSerializer.Serialize(filtered, ListFormSeedDataFileDto.JsonOptions); + + items[relativePath] = new WizardExportItem(relativePath, "Custom component tanımı", null, content); + } } /// @@ -283,12 +299,12 @@ public sealed class WizardExportCollector(string scopeRoot) private async Task>?> ReadCustomComponentRowsAsync() { - var seedFile = await ReadCustomComponentFileAsync(); - if (seedFile == null) + var files = await ReadCustomComponentFilesAsync(); + if (files.Count == 0) return null; var rows = new Dictionary>(StringComparer.OrdinalIgnoreCase); - foreach (var row in seedFile.Rows) + foreach (var row in files.SelectMany(x => x.File.Rows)) { var name = GetRowText(row, CustomComponentNameField); if (!string.IsNullOrWhiteSpace(name)) @@ -298,25 +314,45 @@ public sealed class WizardExportCollector(string scopeRoot) return rows; } - private async Task ReadCustomComponentFileAsync() + /// + /// Custom component seed kaynagi. Ekranin SeedFilePath degeri burada bilinmedigi icin + /// kaynak diskten cozulur: satir basina dosya tutan klasor varsa oradaki dosyalar, yoksa tek + /// dosya okunur. + /// + private async Task> ReadCustomComponentFilesAsync() { - var relativePath = SeedPathResolver.GetDataFilePath(CustomComponentsListFormCode); - var sourcePath = ResolveScopeFilePath(relativePath); - if (sourcePath == null) + if (customComponentFiles != null) + return customComponentFiles; + + customComponentFiles = []; + + var folderPath = SeedPathResolver.GetDataFolderPath(CustomComponentsListFormCode); + var relativePaths = ResolveScopeFolderPath(folderPath) == null + ? [SeedPathResolver.GetDataFilePath(CustomComponentsListFormCode)] + : EnumerateSeedFolderFiles(folderPath).ToList(); + + foreach (var relativePath in relativePaths) { - warnings.Add($"{relativePath}: custom component seed dosyası bulunamadı."); - return null; + var sourcePath = ResolveScopeFilePath(relativePath); + if (sourcePath == null) + { + warnings.Add($"{relativePath}: custom component seed dosyası bulunamadı."); + continue; + } + + try + { + var file = JsonSerializer.Deserialize(await File.ReadAllTextAsync(sourcePath), JsonReadOptions); + if (file != null) + customComponentFiles.Add((relativePath, file)); + } + catch (JsonException) + { + warnings.Add($"{relativePath}: custom component seed dosyası çözülemedi."); + } } - try - { - return JsonSerializer.Deserialize(await File.ReadAllTextAsync(sourcePath), JsonReadOptions); - } - catch (JsonException) - { - warnings.Add($"{relativePath}: custom component seed dosyası çözülemedi."); - return null; - } + return customComponentFiles; } // ── Dosya toplama ────────────────────────────────────────────────────────────── @@ -386,6 +422,38 @@ public sealed class WizardExportCollector(string scopeRoot) return Directory.Exists(path) ? Directory.EnumerateDirectories(path) : []; } + /// + /// Ekranin veri seed hedefini arsive ekler. Hedef klasorse (satir basina dosya) klasordeki butun + /// satir dosyalari girer; tek dosyaysa o dosya eklenir. Hicbir sey eklenemezse false doner. + /// + private bool AddSeedData(string? relativePath, string reason) + { + if (!SeedPathResolver.IsSeedFolderPath(relativePath)) + return AddDiskFile(relativePath, reason); + + var added = false; + + foreach (var rowPath in EnumerateSeedFolderFiles(relativePath!)) + added |= AddDiskFile(rowPath, reason); + + return added; + } + + /// Kapsam-goreli bir seed klasorundeki satir dosyalarinin kapsam-goreli yollari. + private IEnumerable EnumerateSeedFolderFiles(string relativeFolderPath) + { + var normalized = relativeFolderPath.Replace('\\', '/').Trim('/'); + var fullPath = ResolveScopeFolderPath(normalized); + + if (fullPath == null) + return []; + + return Directory + .EnumerateFiles(fullPath, "*.json") + .Select(x => $"{normalized}/{System.IO.Path.GetFileName(x)}") + .OrderBy(x => x, StringComparer.OrdinalIgnoreCase); + } + /// Kapsam icindeki var olan bir dosyayi arsive ekler; kapsam disina cikan ya da bulunmayan yol icin false doner. private bool AddDiskFile(string? relativePath, string reason) { @@ -418,6 +486,21 @@ public sealed class WizardExportCollector(string scopeRoot) return File.Exists(fullPath) ? fullPath : null; } + /// Kapsam icindeki var olan bir klasorun tam yolu; kapsam disina cikan yol icin null. + private string? ResolveScopeFolderPath(string? relativePath) + { + if (string.IsNullOrWhiteSpace(relativePath) || relativePath.Contains("..", StringComparison.Ordinal)) + return null; + + var fullPath = System.IO.Path.GetFullPath( + System.IO.Path.Combine(scopeRoot, relativePath.Replace('/', System.IO.Path.DirectorySeparatorChar))); + + if (!fullPath.StartsWith(scopeRoot + System.IO.Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) + return null; + + return Directory.Exists(fullPath) ? fullPath : null; + } + private string? ResolveWizardFileByListFormCode(string listFormCode) { wizardFilesByListFormCode ??= BuildWizardIndex(scopeRoot); diff --git a/api/src/Sozsoft.Platform.Application/ListForms/WizardImportValidator.cs b/api/src/Sozsoft.Platform.Application/ListForms/WizardImportValidator.cs index 7995593f..6677bd4a 100644 --- a/api/src/Sozsoft.Platform.Application/ListForms/WizardImportValidator.cs +++ b/api/src/Sozsoft.Platform.Application/ListForms/WizardImportValidator.cs @@ -207,8 +207,8 @@ public sealed class WizardImportValidator( var seedFilePath = seed.ListForm.SeedFilePath; if (!string.IsNullOrWhiteSpace(seedFilePath) - && !paths.Contains(seedFilePath) - && !ScopeFileExists(seedFilePath)) + && !SeedDataIncoming(seedFilePath, paths) + && !ScopeSeedDataExists(seedFilePath)) { Add(WizardImportIssueSeverityEnum.Warning, path, $"{screen}: ekranın veri dosyası ({seedFilePath}) ne zip'te ne hedefte var; ekran boş gelecek."); @@ -248,9 +248,7 @@ public sealed class WizardImportValidator( /// Veri dosyasi, satirlarin yazilacagi ekran tanimi olmadan uygulanamaz. private async Task ValidateDataAsync(string path, ListFormSeedDataFileDto file, HashSet paths) { - var listFormCode = string.IsNullOrWhiteSpace(file.ListFormCode) - ? System.IO.Path.GetFileNameWithoutExtension(path) - : file.ListFormCode.Trim(); + var listFormCode = ResolveDataListFormCode(path, file); if (!incomingListFormCodes.Contains(listFormCode) && !await listFormExistsAsync(listFormCode)) { @@ -405,12 +403,28 @@ public sealed class WizardImportValidator( /// Veri dosyasinin custom component seed kaynagi olup olmadigi. private static bool IsCustomComponentsFile(string path, ListFormSeedDataFileDto file) { - var listFormCode = string.IsNullOrWhiteSpace(file.ListFormCode) - ? System.IO.Path.GetFileNameWithoutExtension(path) - : file.ListFormCode.Trim(); - return string.Equals( - listFormCode, WizardExportCollector.CustomComponentsListFormCode, StringComparison.OrdinalIgnoreCase); + ResolveDataListFormCode(path, file), + WizardExportCollector.CustomComponentsListFormCode, + StringComparison.OrdinalIgnoreCase); + } + + /// + /// Veri dosyasinin ait oldugu ekran. Dosyada yaziliysa oradan gelir; degilse yoldan cikarilir: + /// satir klasorunde (data/{ListFormCode}/{anahtar}.json) klasorun adi, tek dosyada + /// uzantisiz dosya adi ekran kodudur. + /// + private static string ResolveDataListFormCode(string path, ListFormSeedDataFileDto file) + { + if (!string.IsNullOrWhiteSpace(file.ListFormCode)) + return file.ListFormCode.Trim(); + + var segments = path.Replace('\\', '/').Trim('/').Split('/'); + + // data/{ListFormCode}/{anahtar}.json — ekran kodu klasor adidir. + return segments.Length > 2 + ? segments[^2] + : System.IO.Path.GetFileNameWithoutExtension(path); } // ── Yardimcilar ──────────────────────────────────────────────────────────────── @@ -471,30 +485,48 @@ public sealed class WizardImportValidator( return targetCustomComponents; var names = new HashSet(StringComparer.OrdinalIgnoreCase); - var relativePath = SeedPathResolver.GetDataFilePath(WizardExportCollector.CustomComponentsListFormCode); - var fullPath = ResolveScopeFilePath(relativePath); - if (fullPath == null) - return targetCustomComponents = names; - - try + foreach (var fullPath in ReadTargetCustomComponentFiles()) { - var file = JsonSerializer.Deserialize(File.ReadAllText(fullPath), JsonReadOptions); - foreach (var row in file?.Rows ?? []) + try { - var name = WizardExportCollector.GetRowText(row, WizardExportCollector.CustomComponentNameField); - if (!string.IsNullOrWhiteSpace(name)) - names.Add(name); + var file = JsonSerializer.Deserialize(File.ReadAllText(fullPath), JsonReadOptions); + foreach (var row in file?.Rows ?? []) + { + var name = WizardExportCollector.GetRowText(row, WizardExportCollector.CustomComponentNameField); + if (!string.IsNullOrWhiteSpace(name)) + names.Add(name); + } + } + catch (JsonException) + { + // Hedef dosya bozuksa import bunu duzeltmek icin yapiliyor olabilir; kontrol atlanir. } - } - catch (JsonException) - { - // Hedef dosya bozuksa import bunu duzeltmek icin yapiliyor olabilir; kontrol atlanir. } return targetCustomComponents = names; } + /// + /// Hedefteki custom component seed dosyalari: satir basina dosya tutan klasor varsa onun + /// icindekiler, yoksa tek dosya. + /// + private List ReadTargetCustomComponentFiles() + { + var folderPath = System.IO.Path.Combine( + scopeRoot, + SeedPathResolver.GetDataFolderPath(WizardExportCollector.CustomComponentsListFormCode) + .Replace('/', System.IO.Path.DirectorySeparatorChar)); + + if (Directory.Exists(folderPath)) + return [.. Directory.EnumerateFiles(folderPath, "*.json")]; + + var filePath = ResolveScopeFilePath( + SeedPathResolver.GetDataFilePath(WizardExportCollector.CustomComponentsListFormCode)); + + return filePath == null ? [] : [filePath]; + } + private SubFormDto[] ReadSubForms(WizardSeedFileDto seed, string path) { try @@ -547,6 +579,36 @@ public sealed class WizardImportValidator( private bool ScopeFileExists(string? relativePath) => ResolveScopeFilePath(relativePath) != null; + /// + /// Ekranin veri seed hedefi hedef ortamda var mi. Hedef klasorse (satir basina dosya) klasorun + /// kendisine bakilir; tek dosyaysa dosyaya. + /// + private bool ScopeSeedDataExists(string? relativePath) + { + if (!SeedPathResolver.IsSeedFolderPath(relativePath)) + return ScopeFileExists(relativePath); + + var normalized = relativePath!.Replace('\\', '/').Trim('/'); + if (normalized.Contains("..", StringComparison.Ordinal)) + return false; + + var fullPath = System.IO.Path.GetFullPath( + System.IO.Path.Combine(scopeRoot, normalized.Replace('/', System.IO.Path.DirectorySeparatorChar))); + + return fullPath.StartsWith(scopeRoot + System.IO.Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) + && Directory.Exists(fullPath); + } + + /// Ekranin veri seed hedefi zip'te var mi; klasor modunda klasorun altindaki satir dosyalari aranir. + private static bool SeedDataIncoming(string relativePath, HashSet paths) + { + var normalized = relativePath.Replace('\\', '/').Trim('/'); + + return SeedPathResolver.IsSeedFolderPath(normalized) + ? paths.Any(x => x.StartsWith(normalized + "/", StringComparison.OrdinalIgnoreCase)) + : paths.Contains(normalized); + } + private string? ResolveScopeFilePath(string? relativePath) { var normalized = relativePath?.Replace('\\', '/').Trim('/'); diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json b/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json index dec99ce7..078215dc 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json @@ -4965,8 +4965,8 @@ { "resourceName": "Platform", "key": "App.ListFormEdit.SeedFilePathHint", - "en": "Relative to the scope folder, e.g. data/{ListFormCode}.json. When filled, insert/update/delete operations are mirrored to this file.", - "tr": "Kapsam klasörüne göre görelidir, örneğin data/{ListFormCode}.json. Dolu ise ekleme/güncelleme/silme işlemleri bu dosyaya yansıtılır." + "en": "Relative to the scope folder. A file (data/{ListFormCode}.json) keeps every row in one file; a folder (data/{ListFormCode}) keeps each row in its own {key}.json file. When filled, insert/update/delete operations are mirrored to the target.", + "tr": "Kapsam klasörüne göre görelidir. Dosya (data/{ListFormCode}.json) bütün satırları tek dosyada tutar; klasör (data/{ListFormCode}) her satırı kendi {anahtar}.json dosyasında tutar. Dolu ise ekleme/güncelleme/silme işlemleri bu hedefe yansıtılır." }, { "resourceName": "Platform", diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Saas.cs b/api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Saas.cs index 75c77067..6b632f35 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Saas.cs +++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Saas.cs @@ -7640,7 +7640,9 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency Description = $"{listFormName}.Description", SelectCommandType = SelectCommandTypeEnum.Table, SelectCommand = customComponentTable, - SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName), + // Bilesen kodu satirin icinde durur; tek dosya hizla buyudugu icin her bilesen + // klasorun altinda kendi dosyasindadir. + SeedFilePath = SeedPathResolver.GetDataFolderPath(listFormName), SeedSyncInsert = true, SeedSyncUpdate = true, SeedSyncDelete = true, diff --git a/api/src/Sozsoft.Platform.Domain/Data/ListFormSeedDataApplier.cs b/api/src/Sozsoft.Platform.Domain/Data/ListFormSeedDataApplier.cs index 1f815698..e4716e4a 100644 --- a/api/src/Sozsoft.Platform.Domain/Data/ListFormSeedDataApplier.cs +++ b/api/src/Sozsoft.Platform.Domain/Data/ListFormSeedDataApplier.cs @@ -125,11 +125,11 @@ public class ListFormSeedDataApplier( using (currentTenant.Change(tenantId)) { - foreach (var filePath in OrderFiles([.. filePaths])) + foreach (var target in BuildTargets(scopeRoot, filePaths)) { - // Her dosya kendi unit of work'unde islenir; ApplyAsync ile ayni kural. + // Her hedef kendi unit of work'unde islenir; ApplyAsync ile ayni kural. using var uow = unitOfWorkManager.Begin(requiresNew: true, isTransactional: false); - results.Add(await ApplyFileAsync(filePath, overwriteExisting)); + results.Add(await ApplyTargetAsync(target, overwriteExisting)); await uow.CompleteAsync(); } } @@ -148,7 +148,11 @@ public class ListFormSeedDataApplier( return; } - var jsonFiles = OrderFiles(Directory.GetFiles(dataPath, "*.json")); + await MigrateSeedFilePathsAsync(tenantId, dataPath); + + // Alt klasorler de taranir: satir basina dosya tutan ekranin verisi + // data/{ListFormCode}/{anahtar}.json altindadir. + var jsonFiles = Directory.GetFiles(dataPath, "*.json", SearchOption.AllDirectories); if (jsonFiles.Length == 0) { logger.LogInformation("No JSON files found in Seeds/{Scope}/data directory, skipping ListFormSeedDataApplier.", scopeFolderName); @@ -157,14 +161,16 @@ public class ListFormSeedDataApplier( logger.LogInformation("ListFormSeedDataApplier started for {Scope}. {Count} file(s) to be processed.", scopeFolderName, jsonFiles.Length); + var scopeRoot = Path.GetFullPath(SeedPathResolver.GetScopePath(configuration, tenantId)); + using (currentTenant.Change(tenantId)) { - foreach (var filePath in jsonFiles) + foreach (var target in BuildTargets(scopeRoot, jsonFiles)) { - // Seed contributor'larin unit of work'u kapandigi icin her dosya kendi + // Seed contributor'larin unit of work'u kapandigi icin her hedef kendi // unit of work'unde islenir; aksi halde DbContext disposed gelir. using var uow = unitOfWorkManager.Begin(requiresNew: true, isTransactional: false); - await ApplyFileAsync(filePath, overwriteExisting: false); + await ApplyTargetAsync(target, overwriteExisting: false); await uow.CompleteAsync(); } } @@ -173,19 +179,77 @@ public class ListFormSeedDataApplier( } /// - /// Dosyalari Order alanina, esitlikte dosya adina gore siralar. Yabanci anahtarla - /// bagli tablolarda referans verilen tanim once uygulanmalidir; okunamayan dosya sona kalir - /// ve hatasi kendi isleminde raporlanir. + /// Dosyalari uygulama hedeflerine ayirir ve Order alanina, esitlikte yola gore siralar. + /// Tek dosyaya bagli ekran bir hedeftir; satir basina dosya tutan klasorde ise klasorun tamami + /// tek hedeftir — boylece klasordeki butun satirlar tek unit of work'te, ekran tanimi bir kez + /// cozulerek uygulanir. Yabanci anahtarla bagli tablolarda referans verilen tanim once + /// uygulanmalidir; okunamayan dosya sona kalir ve hatasi kendi isleminde raporlanir. /// - private static string[] OrderFiles(string[] filePaths) + private static SeedDataTarget[] BuildTargets(string scopeRoot, IEnumerable filePaths) { + var dataPath = Path.Combine(scopeRoot, SeedPathResolver.DataFolder); + return [.. filePaths - .Select(filePath => (FilePath: filePath, Order: ReadOrder(filePath))) + .GroupBy( + filePath => IsRowFile(dataPath, filePath) ? Path.GetDirectoryName(filePath)! : filePath, + StringComparer.OrdinalIgnoreCase) + .Select(group => new SeedDataTarget( + Path.GetRelativePath(scopeRoot, group.Key).Replace('\\', '/'), + [.. group.OrderBy(a => a, StringComparer.OrdinalIgnoreCase)])) + .Select(target => (Target: target, Order: ReadOrder(target.FilePaths[0]))) .OrderBy(a => a.Order) - .ThenBy(a => Path.GetFileName(a.FilePath)) - .Select(a => a.FilePath)]; + .ThenBy(a => a.Target.Path, StringComparer.OrdinalIgnoreCase) + .Select(a => a.Target)]; } + /// + /// Tek dosyaya bagli bir ekranin seed hedefini, kapsamda o ekranin satir klasoru varsa klasore + /// tasir. Bir ekran klasor moduna gecirildiginde var olan kurulumlarin kaydi artik uretilmeyen + /// eski dosyayi gosterir; klasorun diskte olmasi gecisin tek isaretidir. Elle verilmis bir yola + /// dokunulmaz: yalnizca eski varsayilan (data/{ListFormCode}.json) degerler tasinir. + /// + private async Task MigrateSeedFilePathsAsync(Guid? tenantId, string dataPath) + { + using (currentTenant.Change(tenantId)) + { + using var uow = unitOfWorkManager.Begin(requiresNew: true, isTransactional: false); + + var listForms = await listFormRepository.GetListAsync(a => a.SeedFilePath != null && a.SeedFilePath != ""); + + foreach (var listForm in listForms) + { + var filePath = SeedPathResolver.GetDataFilePath(listForm.ListFormCode); + if (!string.Equals(listForm.SeedFilePath?.Replace('\\', '/'), filePath, StringComparison.OrdinalIgnoreCase)) + continue; + + if (!Directory.Exists(Path.Combine(dataPath, listForm.ListFormCode))) + continue; + + listForm.SeedFilePath = SeedPathResolver.GetDataFolderPath(listForm.ListFormCode); + await listFormRepository.UpdateAsync(listForm); + + logger.LogInformation( + "[{ListFormCode}] Seed file path migrated to the row folder '{SeedFilePath}'.", + listForm.ListFormCode, listForm.SeedFilePath); + } + + await uow.CompleteAsync(); + } + } + + /// Dosya, bir ekranin satir klasorunun (data/{ListFormCode}/) altinda mi. + private static bool IsRowFile(string dataPath, string filePath) + { + return !string.Equals(Path.GetDirectoryName(filePath), dataPath, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Tek seferde uygulanan bir ekranin dosyalari: tek dosya ya da satir klasorundeki butun satir + /// dosyalari. kapsam klasorune goreli hedeftir (dosya ya da klasor) ve + /// sonuclarda, loglarda bu gorunur. + /// + private sealed record SeedDataTarget(string Path, string[] FilePaths); + private static int ReadOrder(string filePath) { try @@ -204,39 +268,49 @@ public class ListFormSeedDataApplier( } /// - /// Dosyayi okur ve satirlari ilgili ListForm'un tablosuna uygular. Tek bir dosyanin hatasi - /// diger dosyalari etkilemez; hata sonuca tasinir ve loglanir. + /// Hedefteki satirlari ilgili ListForm'un tablosuna uygular. Ekran tanimi, alan listesi ve veri + /// kaynagi hedef basina bir kez cozulur; satir klasorundeki her dosya icin tekrarlanmaz — aksi + /// halde binlerce satirlik bir listede seed suresi katlanir. Tek bir hedefin hatasi digerlerini + /// etkilemez; hata sonuca tasinir ve loglanir. /// - private async Task ApplyFileAsync(string filePath, bool overwriteExisting) + private async Task ApplyTargetAsync(SeedDataTarget target, bool overwriteExisting) { - var fileName = Path.GetFileName(filePath); try { - var seedFile = JsonSerializer.Deserialize( - await File.ReadAllTextAsync(filePath), - ListFormSeedDataFileDto.JsonOptions); + var seedFiles = new List(); - var listFormCode = string.IsNullOrWhiteSpace(seedFile?.ListFormCode) - ? Path.GetFileNameWithoutExtension(filePath) - : seedFile!.ListFormCode.Trim(); - - if (seedFile?.Rows == null || seedFile.Rows.Count == 0) + foreach (var filePath in target.FilePaths) { - logger.LogWarning("[{File}] No row found, skipped.", fileName); - return new ListFormSeedDataApplyResult(fileName, 0, 0, 0); + var seedFile = JsonSerializer.Deserialize( + await File.ReadAllTextAsync(filePath), + ListFormSeedDataFileDto.JsonOptions); + + if (seedFile?.Rows?.Count > 0) + seedFiles.Add(seedFile); + } + + var listFormCode = ResolveListFormCode(target, seedFiles); + + if (seedFiles.Count == 0) + { + logger.LogWarning("[{Target}] No row found, skipped.", target.Path); + return new ListFormSeedDataApplyResult(target.Path, 0, 0, 0); } var listForm = await listFormRepository.FirstOrDefaultAsync(a => a.ListFormCode == listFormCode); if (listForm == null) { - logger.LogWarning("[{File}] ListForm '{ListFormCode}' not found, skipped.", fileName, listFormCode); - return new ListFormSeedDataApplyResult(fileName, 0, 0, 0, $"ListForm '{listFormCode}' not found."); + logger.LogWarning("[{Target}] ListForm '{ListFormCode}' not found, skipped.", target.Path, listFormCode); + return new ListFormSeedDataApplyResult(target.Path, 0, 0, 0, $"ListForm '{listFormCode}' not found."); } var fields = await listFormFieldRepository.GetListAsync(a => a.ListFormCode == listFormCode); - var keyFieldName = string.IsNullOrWhiteSpace(seedFile.KeyFieldName) - ? listForm.KeyFieldName - : seedFile.KeyFieldName; + + // Anahtar alan ekranin butun satir dosyalarinda aynidir; dosyada yoksa ekrandan gelir. + var keyFieldName = seedFiles + .Select(a => a.KeyFieldName) + .FirstOrDefault(a => !string.IsNullOrWhiteSpace(a)) + ?? listForm.KeyFieldName; var (repository, connectionString, dataSourceType) = await dynamicDataManager.GetAsync(listForm.IsTenant, listForm.DataSourceCode); @@ -244,7 +318,7 @@ public class ListFormSeedDataApplier( var updated = 0; var skipped = 0; - foreach (var row in seedFile.Rows) + foreach (var row in seedFiles.SelectMany(a => a.Rows)) { switch (await ApplyRowAsync(listForm, fields, keyFieldName, row, repository, connectionString, dataSourceType, overwriteExisting)) { @@ -261,18 +335,37 @@ public class ListFormSeedDataApplier( } logger.LogInformation( - "[{File}] {Inserted} row(s) inserted, {Updated} row(s) updated, {Skipped} existing row(s) skipped.", - fileName, inserted, updated, skipped); - return new ListFormSeedDataApplyResult(fileName, inserted, updated, skipped); + "[{Target}] {Inserted} row(s) inserted, {Updated} row(s) updated, {Skipped} existing row(s) skipped.", + target.Path, inserted, updated, skipped); + return new ListFormSeedDataApplyResult(target.Path, inserted, updated, skipped); } catch (Exception ex) { - // Tek bir dosyanin hatasi diger listelerin seed edilmesini engellemez. - logger.LogError(ex, "Failed to process list form data seed file: {FileName}", fileName); - return new ListFormSeedDataApplyResult(fileName, 0, 0, 0, ex.Message); + // Tek bir hedefin hatasi diger listelerin seed edilmesini engellemez. + logger.LogError(ex, "Failed to process list form data seed target: {Target}", target.Path); + return new ListFormSeedDataApplyResult(target.Path, 0, 0, 0, ex.Message); } } + /// + /// Hedefin ekran kodu. Dosyada yaziliysa oradan gelir; degilse yoldan cikarilir: satir klasorunde + /// klasorun adi, tek dosyada uzantisiz dosya adi ekran kodudur. + /// + private static string ResolveListFormCode(SeedDataTarget target, List seedFiles) + { + var listFormCode = seedFiles + .Select(a => a.ListFormCode) + .FirstOrDefault(a => !string.IsNullOrWhiteSpace(a)); + + if (!string.IsNullOrWhiteSpace(listFormCode)) + return listFormCode.Trim(); + + // Ekran kodu nokta icerdigi icin uzanti yalnizca ".json" ile kesilir. + var name = Path.GetFileName(target.Path); + + return name.EndsWith(".json", StringComparison.OrdinalIgnoreCase) ? name[..^".json".Length] : name; + } + /// /// Satiri veritabanina yazar. Kayit yoksa eklenir; varsa /// true ise dosyadaki degerlerle guncellenir ve soft delete izleri temizlenir, degilse dokunulmaz. diff --git a/api/src/Sozsoft.Platform.Domain/Data/SeedPathResolver.cs b/api/src/Sozsoft.Platform.Domain/Data/SeedPathResolver.cs index 2bc9bd8c..d25d6adc 100644 --- a/api/src/Sozsoft.Platform.Domain/Data/SeedPathResolver.cs +++ b/api/src/Sozsoft.Platform.Domain/Data/SeedPathResolver.cs @@ -80,6 +80,47 @@ public static class SeedPathResolver return $"{DataFolder}/{listFormCode}.json"; } + /// + /// Bir ListForm icin satir basina dosya tutan veri seed klasorunun kapsam klasorune gore goreli + /// yolu. Cok satirli ya da satiri buyuk (bilesen kodu gibi) ekranlarda tek dosya her kayitta + /// bastan yazilir; klasor modunda yalnizca islem goren satirin dosyasina dokunulur. + /// + public static string GetDataFolderPath(string listFormCode) + { + return $"{DataFolder}/{listFormCode}"; + } + + /// + /// ListForm.SeedFilePath tek bir dosyayi mi yoksa satir basina dosya tutan bir klasoru mu + /// gosteriyor. Ayrim uzantidan yapilir: .json ile biten deger dosyadir, digerleri klasor. + /// Klasor diskte henuz yoksa da karar verilebilsin diye dosya sistemine bakilmaz. + /// + public static bool IsSeedFolderPath(string? seedFilePath) + { + var normalized = seedFilePath?.Replace('\\', '/').Trim('/'); + + return !string.IsNullOrWhiteSpace(normalized) + && !normalized.EndsWith(".json", StringComparison.OrdinalIgnoreCase); + } + + /// + /// Klasor modunda bir satirin dosya adi: anahtar degeri. Dosya adinda kullanilamayan karakterler + /// (kod tipi anahtarlarda gorulebilir) alt cizgiye cevrilir; anahtar bos ise null doner — o satir + /// icin dosya yolu uretilemez. + /// + public static string? GetRowFileName(string? key) + { + if (string.IsNullOrWhiteSpace(key)) + { + return null; + } + + var invalidChars = Path.GetInvalidFileNameChars(); + var name = new string([.. key.Trim().Select(c => invalidChars.Contains(c) ? '_' : c)]); + + return name.Length == 0 ? null : $"{name}.json"; + } + private const string RepositorySeedsFolder = "seeds"; private const string RepositoryConfigsFolder = "configs"; private const string FallbackFolderName = "Seeds"; diff --git a/claude.md b/claude.md index faefaef6..3d110d9a 100644 --- a/claude.md +++ b/claude.md @@ -231,10 +231,10 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` → seed dosyasını görür. Seed dosyası `ui/` dışında olduğu için aynı sınıflar `ui/safelist.txt` sonuna da yazılır (`twSafelistGenerator`, derlemede otomatik); yoksa uygulama sunucuda stilsiz kalır. İkinci bir safelist dosyası açma. -- **Custom component kaydı/silinmesi** → `data/App.DeveloperKit.CustomComponents.json` - (Custom Components ekranının seed verisi); bileşenin kendi dosyası yoktur, her bileşen bu - dosyada bir satırdır. Hem listeden hem Visual Designer'dan (`CustomComponentAppService`) yapılan - kayıt aynı dosyaya yansır; kullandığı endpoint'ler `crud/{Entity}.json`. +- **Custom component kaydı/silinmesi** → `data/App.DeveloperKit.CustomComponents/{Id}.json` + (Custom Components ekranının seed verisi); ekran klasör modundadır, her bileşen kendi satır + dosyasındadır. Hem listeden hem Visual Designer'dan (`CustomComponentAppService`) yapılan kayıt + aynı klasöre yansır; kullandığı endpoint'ler `crud/{Entity}.json`. - **Wizard dosyası** → tek başına taşınmaz; export zip'i **özyinelemeli bağımlılık kapanışını** taşır (`WizardExportCollector`): alt formların wizard dosyaları, ekranın `SeedFilePath` veri aynası, lookup sorgularının işaret ettiği nesnelerin scriptleri, `crud/` ve @@ -269,8 +269,17 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` → olmasıdır: tabloya dair bir bulgu üretirken `TableName` (+ tablo hedefte yoksa `TableMissing`) doldurulur, istemci tablo tasarımcısını buna göre açar. - **`ListForm.SeedFilePath` dolu bir ekran** → `SeedSyncInsert/SeedSyncUpdate/SeedSyncDelete` - bayraklarından işaretli olan işlemler kapsam klasöründeki `data/{ListFormCode}.json` dosyasına - yansır (`ListFormSeedDataSynchronizer`); migrate/seed sırasında ve wizard import'unun kapanışında + bayraklarından işaretli olan işlemler kapsam klasöründeki hedefe yansır + (`ListFormSeedDataSynchronizer`). Hedef iki biçimdedir ve ayrım uzantıdadır: `.json` ile biten + değer **tek dosyadır** (`data/{ListFormCode}.json`, bütün satırlar orada), uzantısız değer + **klasördür** (`data/{ListFormCode}/{anahtar}.json`, her satır kendi dosyasında) ve klasör yoksa + ilk kayıtta oluşturulur. Klasör modu satırı büyük ya da sık yazılan ekranlar içindir + (varsayılan tek dosya; şu an klasör kullanan tek ekran Custom Components). Applier `data/` + altını özyinelemeli tarar ve **bir klasörün tamamını tek unit of work'te** uygular — ekran + tanımı, alan listesi ve veri kaynağı satır dosyası başına değil ekran başına bir kez çözülür; + dosyadaki `ListFormCode` boşsa ekran kodu klasör adından gelir. Tek dosyaya bağlı eski bir kayıt, + klasörü diskte varsa migrate sırasında klasöre taşınır. + Migrate/seed sırasında ve wizard import'unun kapanışında (`CompleteImport`) `ListFormSeedDataApplier` dosyayı geri uygular. Migrate/seed'de kayıt yoksa eklenir, varsa dokunulmaz; import'ta `overwriteExisting` açıktır: var olan kayıt dosyadaki değerlerle güncellenir ve soft delete izleri (`IsDeleted`/`DeletionTime`/`DeleterId`) temizlenir. Yeni bir kayıt yolu (yeni endpoint, toplu işlem, tipli AppService) eklenirse @@ -297,7 +306,7 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` → (menü ucu `[Authorize]`). Sayfa ise Custom Component'tir: `/admin/` ile başlamayan `RoutePath` otomatik public route olur ve aynı yolu paylaşan fiziksel route'u gölgeler; `RoutePath` boş bırakılabilir (rotasız bileşen). Yeni public sayfa = - `data/App.DeveloperKit.CustomComponents.json` satırı + `Pub` menü kaydı; ikisi birlikte yazılır. + `data/App.DeveloperKit.CustomComponents/{Id}.json` satırı + `Pub` menü kaydı; ikisi birlikte yazılır. Üst ve alt bilgi rotasız bileşenlerdir (`PublicHeader`, `PublicFooter`), layout ikisini de `renderComponent` ile çizer; rotasız bileşende `RoutePath` **NULL** yazılır (tekil indeks yalnızca dolu değerleri kapsar). Üst bilginin davranışı `components/publicSite` katalog düğümlerinde, diff --git a/configs/seeds/host/data/App.DeveloperKit.CustomComponents.json b/configs/seeds/host/data/App.DeveloperKit.CustomComponents.json deleted file mode 100644 index e72b046b..00000000 --- a/configs/seeds/host/data/App.DeveloperKit.CustomComponents.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "ListFormCode": "App.DeveloperKit.CustomComponents", - "KeyFieldName": "Id", - "GeneratedAt": "2026-09-21T14:46:03.8784173Z", - "Order": 0, - "Rows": [ - { - "Id": "8389ccbc-3e7b-738d-35aa-3a237eea9b9d", - "Name": "DynamicEntityComponent", - "RoutePath": "/admin/dynamic-entity", - "Description": null, - "IsActive": true, - "Dependencies": "", - "Code": "import React, { useEffect, useState } from \"react\";\nimport axios from \"axios\";\n\ninterface DynamicEntityComponentProps {\n title: string;\n}\n\nconst api = axios.create({\n baseURL: \"https://localhost:44344/\",\n});\n\nconst DynamicEntityComponent: React.FC = ({ title }) => {\n const [data, setData] = useState>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(null);\n\n useEffect(() => {\n const fetchData = async () => {\n setLoading(true);\n setError(null);\n\n try {\n const res = await api.get(`/api/app/crudendpoint/${title}`);\n const raw = Array.isArray(res.data) ? res.data : res.data?.items ?? [];\n\n const filtered = raw.map((item: any) => ({\n id: item.Id ?? item.id,\n name: item.Name ?? item.name,\n }));\n\n setData(filtered);\n } catch (err: any) {\n setError(err.message || \"Failed to fetch data\");\n } finally {\n setLoading(false);\n }\n };\n\n if (title) fetchData();\n }, [title]);\n\n if (loading) return
Loading...
;\n if (error) return
Error: {error}
;\n if (!data.length) return
No records found
;\n\n const headers = [\"id\", \"name\", \"actions\"];\n\n return (\n
\n \n \n \n {headers.map((key) => (\n \n {key === \"actions\" ? \"Actions\" : key}\n \n ))}\n \n \n \n {data.map((item, rowIndex) => (\n \n \n \n \n \n ))}\n \n
\n {item.id}\n \n {item.name}\n \n alert(item.name)}\n shape=\"round\"\n size=\"xs\"\n variant=\"solid\"\n id=\"c_mrix4c1a_qrybhk\"\n >\n Show Name\n \n
\n
\n );\n};\n\nexport default DynamicEntityComponent;", - "Props": null, - "DataSources": "", - "LastModificationTime": null, - "CreationTime": "2026-09-05T17:22:11" - }, - { - "Id": "1acc9af1-6d5e-2940-160e-3a237eea9c0a", - "Name": "NewComponent", - "RoutePath": "/admin/new-component", - "Description": "", - "IsActive": true, - "Dependencies": [], - "Code": "/*__SOZSOFT_VISUAL_DESIGNER__%7B%22version%22%3A1%2C%22sourceMode%22%3A%22visual%22%2C%22nodes%22%3A%5B%5D%2C%22canvas%22%3A%7B%22width%22%3A%22responsive%22%7D%2C%22lifecycle%22%3A%7B%22onMount%22%3A%22%22%7D%2C%22dataSources%22%3A%5B%5D%7D__*/\nconst NewComponent = () => {\n\n\n return (\n <>\n\n \n )\n}\n\nexport default NewComponent", - "Props": "{\"visualDesigner\":{\"version\":1,\"sourceMode\":\"visual\",\"nodes\":[],\"canvas\":{\"width\":\"responsive\"},\"lifecycle\":{\"onMount\":\"\"},\"dataSources\":[]}}", - "DataSources": [] - }, - { - "Id": "514a142f-8aaa-685f-fd13-3a237eea9c27", - "Name": "RoleListComponent", - "RoutePath": "/admin/roles-list", - "Description": null, - "IsActive": true, - "Dependencies": [ - "DynamicEntityComponent" - ], - "Code": "const RoleListComponent = ({\n title = \"AbpRoles\"\n}) => {\n return (\n \n );\n};\n\nexport default RoleListComponent;", - "Props": null, - "DataSources": [] - }, - { - "Id": "932daf62-9f6c-0c8c-08c0-3a237eea9c19", - "Name": "RoleComponent", - "RoutePath": "/admin/roles", - "Description": "", - "IsActive": true, - "Dependencies": "[]", - "Code": "/*__SOZSOFT_VISUAL_DESIGNER__%7B%22version%22%3A1%2C%22sourceMode%22%3A%22visual%22%2C%22nodes%22%3A%5B%7B%22id%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22type%22%3A%22Form%22%2C%22kind%22%3A%22layout%22%2C%22props%22%3A%7B%22selectEndpoint%22%3A%22source_5w_5ibpv%22%2C%22insertEndpoint%22%3A%22source_h3_484qa%22%2C%22updateEndpoint%22%3A%22source_gb_d9cvs%22%2C%22deleteEndpoint%22%3A%22source_0t_avok9%22%2C%22keyFieldName%22%3A%22id%22%2C%22collectionPath%22%3A%22%22%2C%22keySource%22%3A%22query%22%2C%22keyParamName%22%3A%22id%22%2C%22previewKeyValue%22%3A%22%22%2C%22autoLoad%22%3Atrue%2C%22showToolbar%22%3Atrue%2C%22gap%22%3A16%2C%22className%22%3A%22%22%2C%22title%22%3A%22%3A%3AAbpIdentity.Roles%22%2C%22columnCount%22%3A2%2C%22selectPermission%22%3A%22%22%2C%22insertPermission%22%3A%22%22%7D%2C%22events%22%3A%7B%22onLoad%22%3A%22%22%2C%22onRecordChange%22%3A%22%22%2C%22onFieldChange%22%3A%22%22%2C%22onNewRecord%22%3A%22%22%2C%22onModeChange%22%3A%22%22%2C%22onBeforeSave%22%3A%22%22%2C%22onAfterSave%22%3A%22%22%2C%22onBeforeDelete%22%3A%22%22%2C%22onAfterDelete%22%3A%22%22%2C%22onError%22%3A%22%22%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_msyfpfia_ifhdq%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22%3A%3AApp.Platform.Name%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22text%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%2C%22sqlDefaultValue%22%3A%22%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22Name%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input1%22%7D%2C%7B%22id%22%3A%22cmp_msyjl707_crcn7%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22Normalized%20Name%20%3A%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22text%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22NormalizedName%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input2%22%7D%2C%7B%22id%22%3A%22cmp_msyjdodn_u3o74%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22Concurrent%20Stamp%20%3A%20%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22text%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22ConcurrencyStamp%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input3%22%7D%2C%7B%22id%22%3A%22cmp_msyje9ys_2c08a%22%2C%22type%22%3A%22Checkbox%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22checked%22%3Afalse%2C%22color%22%3A%22blue-500%22%2C%22disabled%22%3Afalse%2C%22labelRef%22%3A%22%22%2C%22name%22%3A%22%22%2C%22readOnly%22%3Afalse%2C%22value%22%3A%22%22%2C%22field%22%3Anull%2C%22className%22%3A%22%22%2C%22children%22%3A%22Is%20Default%22%2C%22style%22%3A%7B%7D%2C%22sqlDefaultValue%22%3A%22true%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22checked%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22IsDefault%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22checkbox1%22%7D%2C%7B%22id%22%3A%22cmp_msyjebu2_27tqi%22%2C%22type%22%3A%22Checkbox%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22checked%22%3Afalse%2C%22color%22%3A%22blue-500%22%2C%22disabled%22%3Afalse%2C%22labelRef%22%3A%22%22%2C%22name%22%3A%22%22%2C%22readOnly%22%3Afalse%2C%22value%22%3A%22%22%2C%22field%22%3Anull%2C%22className%22%3A%22%22%2C%22children%22%3A%22Is%20Static%22%2C%22style%22%3A%7B%7D%2C%22sqlDefaultValue%22%3A%22true%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22checked%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22IsStatic%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22checkbox2%22%7D%2C%7B%22id%22%3A%22cmp_msyjmetn_gojol%22%2C%22type%22%3A%22Checkbox%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22checked%22%3Afalse%2C%22color%22%3A%22blue-500%22%2C%22disabled%22%3Afalse%2C%22labelRef%22%3A%22%22%2C%22name%22%3A%22%22%2C%22readOnly%22%3Afalse%2C%22value%22%3A%22%22%2C%22field%22%3Anull%2C%22className%22%3A%22%22%2C%22children%22%3A%22Is%20Public%22%2C%22style%22%3A%7B%7D%2C%22sqlDefaultValue%22%3A%22false%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22checked%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22IsPublic%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22checkbox3%22%7D%2C%7B%22id%22%3A%22cmp_msyjo0g8_oathk%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22Entity%20Version%20%3A%20%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22number%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%2C%22sqlDefaultValue%22%3A%223%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22EntityVersion%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input4%22%7D%2C%7B%22id%22%3A%22cmp_msyjplfq_py8lz%22%2C%22type%22%3A%22Input%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22asElement%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22invalid%22%3Afalse%2C%22rows%22%3A3%2C%22prefix%22%3A%22Extra%20Properties%20%3A%22%2C%22size%22%3A%22md%22%2C%22suffix%22%3A%22%22%2C%22textArea%22%3Afalse%2C%22type%22%3A%22text%22%2C%22unstyle%22%3Afalse%2C%22field%22%3A%22%22%2C%22form%22%3A%22%22%2C%22className%22%3A%22%22%2C%22children%22%3A%22%22%2C%22style%22%3A%7B%7D%2C%22placeholder%22%3A%22Metin%20girin...%22%2C%22value%22%3A%22%22%2C%22name%22%3A%22%22%2C%22autoComplete%22%3A%22off%22%2C%22autoFocus%22%3Afalse%2C%22readOnly%22%3Afalse%2C%22required%22%3Afalse%2C%22maxLength%22%3A%22%22%2C%22minLength%22%3A%22%22%2C%22pattern%22%3A%22%22%2C%22sqlDefaultValue%22%3A%22%7B%7D%22%7D%2C%22events%22%3A%7B%22onChange%22%3A%22%22%7D%2C%22bindings%22%3A%7B%22value%22%3A%7B%22sourceId%22%3A%22cmp_msyfmegt_jkkn3%22%2C%22path%22%3A%22ExtraProperties%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22input5%22%7D%5D%2C%22ref%22%3A%22form1%22%7D%2C%7B%22id%22%3A%22cmp_mubctlbp_p6ajt%22%2C%22type%22%3A%22BarcodeScanField%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22label%22%3A%22%22%2C%22name%22%3A%22barcode%22%2C%22placeholder%22%3A%22%22%2C%22buttonText%22%3A%22%22%2C%22continuous%22%3Atrue%2C%22readOnly%22%3Atrue%2C%22className%22%3A%22mt-4%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22barcodeScanField1%22%7D%2C%7B%22id%22%3A%22cmp_mt023r5u_rb2jv%22%2C%22type%22%3A%22Grid%22%2C%22kind%22%3A%22ui%22%2C%22props%22%3A%7B%22hoverable%22%3Atrue%2C%22overflow%22%3Atrue%2C%22borderlessRow%22%3Afalse%2C%22compact%22%3Afalse%2C%22cols%22%3A%223%22%2C%22rows%22%3A%222%22%2C%22gap%22%3A%224%22%2C%22colGap%22%3A%224%22%2C%22rowGap%22%3A%224%22%2C%22responsive%22%3Atrue%2C%22smCols%22%3A%221%22%2C%22mdCols%22%3A%222%22%2C%22lgCols%22%3A%223%22%2C%22xlCols%22%3A%224%22%2C%22autoFit%22%3Afalse%2C%22minColWidth%22%3A%22200px%22%2C%22height%22%3A%22auto%22%2C%22className%22%3A%22mt-4%22%2C%22items%22%3A%5B%7B%22id%22%3A%22grid-item-1%22%2C%22content%22%3A%22Grid%20Item%201%22%2C%22colSpan%22%3A1%2C%22rowSpan%22%3A1%2C%22colStart%22%3A1%2C%22rowStart%22%3A1%2C%22className%22%3A%22bg-blue-100%20p-4%20border%20rounded%22%7D%2C%7B%22id%22%3A%22grid-item-2%22%2C%22content%22%3A%22Grid%20Item%202%22%2C%22colSpan%22%3A1%2C%22rowSpan%22%3A1%2C%22colStart%22%3A2%2C%22rowStart%22%3A1%2C%22className%22%3A%22bg-green-100%20p-4%20border%20rounded%22%7D%2C%7B%22id%22%3A%22grid-item-3%22%2C%22content%22%3A%22Grid%20Item%203%22%2C%22colSpan%22%3A1%2C%22rowSpan%22%3A1%2C%22colStart%22%3A3%2C%22rowStart%22%3A1%2C%22className%22%3A%22bg-yellow-100%20p-4%20border%20rounded%22%7D%2C%7B%22id%22%3A%22grid-item-4%22%2C%22content%22%3A%22Grid%20Item%204%22%2C%22colSpan%22%3A2%2C%22rowSpan%22%3A1%2C%22colStart%22%3A1%2C%22rowStart%22%3A2%2C%22className%22%3A%22bg-purple-100%20p-4%20border%20rounded%22%7D%2C%7B%22id%22%3A%22grid-item-5%22%2C%22content%22%3A%22Grid%20Item%205%22%2C%22colSpan%22%3A1%2C%22rowSpan%22%3A1%2C%22colStart%22%3A3%2C%22rowStart%22%3A2%2C%22className%22%3A%22bg-red-100%20p-4%20border%20rounded%22%7D%5D%2C%22dataColumns%22%3A%5B%22UserId%22%5D%2C%22columnLookups%22%3A%7B%22UserId%22%3A%7B%22sourceId%22%3A%22source_gq_0q1dz%22%2C%22path%22%3A%22%22%2C%22valueField%22%3A%22Id%22%2C%22textField%22%3A%22UserName%22%7D%7D%2C%22columnCaptions%22%3A%7B%7D%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%22items%22%3A%7B%22sourceId%22%3A%22source_tq_veb4m%22%2C%22path%22%3A%22%22%7D%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22grid1%22%7D%2C%7B%22id%22%3A%22cmp_mt0288i3_ebpj5%22%2C%22type%22%3A%22ListView%22%2C%22kind%22%3A%22platform%22%2C%22props%22%3A%7B%22listFormCode%22%3A%22AbpIdentity.Roles%22%2C%22height%22%3A%22520px%22%2C%22filters%22%3A%5B%7B%22id%22%3A%22filter_y9_tr73w%22%2C%22field%22%3A%22Id%22%2C%22operator%22%3A%22eq%22%2C%22source%22%3A%22record%22%2C%22value%22%3A%22form1.Id%22%2C%22previewValue%22%3A%22%22%2C%22required%22%3Atrue%7D%5D%2C%22className%22%3A%22mt-4%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%2C%22ref%22%3A%22listView1%22%7D%5D%2C%22canvas%22%3A%7B%22width%22%3A%22responsive%22%7D%2C%22lifecycle%22%3A%7B%22onMount%22%3A%22%22%7D%2C%22dataSources%22%3A%5B%7B%22id%22%3A%22source_5w_5ibpv%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20GetList%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_i7_msox4%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20GetById%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_0t_avok9%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20Delete%22%2C%22method%22%3A%22DELETE%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_h3_484qa%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20Create%22%2C%22method%22%3A%22POST%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_gb_d9cvs%22%2C%22name%22%3A%22AbpRoles%20%C2%B7%20Update%22%2C%22method%22%3A%22PUT%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpRoles%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_tq_veb4m%22%2C%22name%22%3A%22AbpUserRoles%20%C2%B7%20GetList%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpUserRoles%22%2C%22responsePath%22%3A%22%22%2C%22filters%22%3A%5B%7B%22id%22%3A%22filter_sw_86e0q%22%2C%22field%22%3A%22RoleId%22%2C%22operator%22%3A%22eq%22%2C%22source%22%3A%22record%22%2C%22value%22%3A%22form1.id%22%2C%22previewValue%22%3A%22%22%2C%22required%22%3Atrue%7D%5D%7D%2C%7B%22id%22%3A%22source_d2_bri5s%22%2C%22name%22%3A%22AbpUserRoles%20%C2%B7%20GetById%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpUserRoles%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_gq_0q1dz%22%2C%22name%22%3A%22AbpUsers%20%C2%B7%20GetList%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpUsers%22%2C%22responsePath%22%3A%22%22%7D%2C%7B%22id%22%3A%22source_u8_nu1lu%22%2C%22name%22%3A%22AbpUsers%20%C2%B7%20GetById%22%2C%22method%22%3A%22GET%22%2C%22url%22%3A%22%2Fapi%2Fapp%2Fcrudendpoint%2FAbpUsers%2F%7Bid%7D%22%2C%22responsePath%22%3A%22%22%7D%5D%2C%22permissionCode%22%3A%22%22%7D__*/\nconst RoleComponent = () => {\n const getByPath = (value, path) => {\n if (!path) return value\n const readPath = (target, targetPath) => targetPath.split('.').filter(Boolean).reduce((current, key) => current?.[key], target)\n const directValue = readPath(value, path)\n if (directValue !== undefined) return directValue\n if (path.startsWith('result.')) return readPath(value, path.slice('result.'.length))\n return value?.result === undefined ? undefined : readPath(value.result, path)\n }\n\n const resolveLookupText = (lookup, value) => {\n if (!lookup || value === null || value === undefined || value === \"\") return undefined\n const rows = getByPath(lookup.rows, lookup.path)\n if (!Array.isArray(rows)) return undefined\n const key = String(value)\n const row = rows.find((item) => item && typeof item === \"object\" && String(getByPath(item, lookup.value) ?? \"\") === key)\n if (!row) return undefined\n const text = getByPath(row, lookup.text)\n return text === null || text === undefined || text === \"\" ? undefined : text\n }\n\n const localeCulture = () => {\n const injected = typeof getCulture === \"function\" ? getCulture() : \"\"\n if (injected) return injected\n if (typeof document !== \"undefined\" && document.documentElement.lang) return document.documentElement.lang\n return undefined\n }\n // Patterns come from the culture itself; a fixed day/month/year order would\n // print the same layout in every language.\n const localeDateOptions = { dateStyle: \"short\" }\n const localeTimeOptions = { dateStyle: \"short\", timeStyle: \"short\" }\n const localeIsoPattern = /^(\\d{4})-(\\d{2})-(\\d{2})(?:[T ](\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/\n const formatLocaleValue = (value) => {\n if (value === null || value === undefined) return \"\"\n const culture = localeCulture()\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) return String(value)\n // Grouping and the decimal separator are the culture's decision; only the\n // precision guard stays, so a long decimal is not rounded away.\n return new Intl.NumberFormat(culture, { maximumFractionDigits: 20 }).format(value)\n }\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? \"\" : new Intl.DateTimeFormat(culture, localeTimeOptions).format(value)\n }\n if (typeof value !== \"string\") return String(value)\n const parts = localeIsoPattern.exec(value.trim())\n if (!parts) return value\n const parsed = new Date(value)\n if (Number.isNaN(parsed.getTime())) return value\n if (parts[4] === undefined) {\n const day = new Date(Number(parts[1]), Number(parts[2]) - 1, Number(parts[3]))\n return new Intl.DateTimeFormat(culture, localeDateOptions).format(day)\n }\n return new Intl.DateTimeFormat(culture, localeTimeOptions).format(parsed)\n }\n\n const readSqlField = (record, field) => {\n if (!record || typeof record !== \"object\" || Array.isArray(record)) return undefined\n if (field in record) return record[field]\n const matched = Object.keys(record).find((key) => key.toLowerCase() === String(field).toLowerCase())\n return matched === undefined ? undefined : record[matched]\n }\n const toSqlRows = (value, collectionPath) => {\n const source = collectionPath ? getByPath(value, collectionPath) : value\n const rows = Array.isArray(source)\n ? source\n : source && typeof source === \"object\"\n ? (Array.isArray(source.items) ? source.items : [source])\n : []\n return rows.filter((item) => item && typeof item === \"object\" && !Array.isArray(item))\n }\n const setSqlField = (record, path, value) => {\n const keys = String(path || \"\").split(\".\").filter(Boolean)\n if (!keys.length) return record\n const next = { ...(record || {}) }\n let target = next\n for (let index = 0; index < keys.length - 1; index += 1) {\n const child = target[keys[index]]\n target[keys[index]] = child && typeof child === \"object\" && !Array.isArray(child) ? { ...child } : {}\n target = target[keys[index]]\n }\n target[keys[keys.length - 1]] = value\n return next\n }\n const bindSqlUrl = (url, record, keyField) => {\n let keyBound = false\n const boundUrl = String(url).replace(/\\{([^}]+)\\}|(?<=\\/):([A-Za-z_][A-Za-z0-9_]*)/g, (match, braced, colon) => {\n const name = (braced || colon || \"\").trim()\n if (!name) return match\n const value = readSqlField(record, name) ?? readSqlField(record, keyField)\n if (value === undefined || value === null || value === \"\") return match\n if (name.toLowerCase() === String(keyField).toLowerCase()) keyBound = true\n return encodeURIComponent(String(value))\n })\n return { url: boundUrl, keyBound }\n }\n const appendQueryParam = (url, name, value) =>\n url + (url.includes(\"?\") ? \"&\" : \"?\") + encodeURIComponent(name) + \"=\" + encodeURIComponent(value)\n const hasSqlUrlParams = (url) => /\\{[^}]+\\}|(?<=\\/):[A-Za-z_][A-Za-z0-9_]*/.test(String(url))\n const readUrlKey = (source, name) => {\n if (typeof window === \"undefined\") return \"\"\n if (source === \"query\") return new URLSearchParams(window.location.search).get(name) ?? \"\"\n const segments = window.location.pathname.split(\"/\").filter(Boolean)\n return segments.length ? decodeURIComponent(segments[segments.length - 1]) : \"\"\n }\n const filterSqlRowsByKey = (rows, keyField, keyValue) => {\n if (!keyValue) return rows\n const matched = rows.filter((row) => String(readSqlField(row, keyField) ?? \"\") === String(keyValue))\n // No match means the endpoint already filtered server side, or the key column\n // is named differently — keeping the rows beats showing an empty form.\n return matched.length ? matched : rows\n }\n // Update payload: only the columns the user actually edited, plus the key.\n const toSqlChanges = (original, current, keyField) => {\n const changes = {}\n const isSame = (left, right) => JSON.stringify(left ?? null) === JSON.stringify(right ?? null)\n Object.keys(current || {}).forEach((column) => {\n if (!isSame(original ? original[column] : undefined, current[column])) changes[column] = current[column]\n })\n const keyColumn = Object.keys(current || {}).find((column) => column.toLowerCase() === String(keyField).toLowerCase())\n if (keyColumn !== undefined && changes[keyColumn] === undefined) changes[keyColumn] = current[keyColumn]\n return changes\n }\n const callSqlEndpoint = (target, record, keyField, payload) => {\n // The URL is always bound from the full record: a placeholder may reference a\n // column that the update payload does not carry.\n const bound = bindSqlUrl(target.url, record, keyField)\n const request = { url: bound.url, method: target.method }\n if (target.method === \"POST\" || target.method === \"PUT\") request.data = payload === undefined ? record : payload\n const keyValue = readSqlField(record, keyField)\n const hasKey = keyValue !== undefined && keyValue !== null && keyValue !== \"\"\n // The key still has to reach the endpoint when the URL carries no placeholder.\n if (!bound.keyBound && hasKey && target.method !== \"POST\") request.params = { [keyField]: keyValue }\n return apiService.fetchData(request)\n }\n // A new record starts at the top of the form: the first enabled editor takes the\n // cursor, so a Yeni click is immediately followed by typing.\n const focusFirstSqlField = (host) => {\n if (typeof window === \"undefined\") return\n window.requestAnimationFrame(() => {\n const field = host.current?.querySelector(\"input:not([type='hidden']):not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly]), select:not([disabled])\")\n if (!field) return\n field.focus()\n if (typeof field.select === \"function\") field.select()\n })\n }\n // Defaults declared on the bound children; only a new record starts from them.\n const buildSqlDefaults = (fields) => fields.reduce((record, field) => setSqlField(record, field.path, resolveSqlDefault(field)), {})\n const resolveSqlDefault = (field) => {\n if (field.token !== \"today\" && field.token !== \"now\") return field.value\n const now = new Date()\n const pad = (value) => String(value).padStart(2, \"0\")\n const day = now.getFullYear() + \"-\" + pad(now.getMonth() + 1) + \"-\" + pad(now.getDate())\n if (field.token === \"today\" || field.dateOnly) return day\n return day + \"T\" + pad(now.getHours()) + \":\" + pad(now.getMinutes()) + \":\" + pad(now.getSeconds())\n }\n const toSqlErrorMessage = (error) => error?.response?.data?.error?.message || error?.response?.data?.message || error?.message || translate(\"::App.Platform.IslemBasarisiz\")\n\n // Value of a filter that reads the page URL: a query string parameter, or a\n // path segment — the one after a segment named like the parameter, otherwise\n // the last one, which is the /orders/{id} detail page shape.\n const readDesignerUrlValue = (kind, name) => {\n if (typeof window === \"undefined\") return \"\"\n if (kind === \"query\") return new URLSearchParams(window.location.search).get(name) ?? \"\"\n const segments = window.location.pathname.split(\"/\").filter(Boolean)\n if (!segments.length) return \"\"\n const index = name ? segments.findIndex((segment) => segment.toLowerCase() === String(name).toLowerCase()) : -1\n if (index >= 0 && index < segments.length - 1) return decodeURIComponent(segments[index + 1])\n return decodeURIComponent(segments[segments.length - 1])\n }\n // Filters are appended as query parameters: `Column` for an equality and\n // `Column.operator` for anything else, which is what the endpoint parses.\n // Returns \"\" when a required filter has no value yet — the caller must skip the\n // request instead of loading the unfiltered collection.\n const buildFilteredUrl = (url, filters) => {\n const parts = []\n for (const filter of filters || []) {\n const value = filter.value === undefined || filter.value === null ? \"\" : String(filter.value)\n if (!value) {\n if (filter.required) return \"\"\n continue\n }\n parts.push(encodeURIComponent(filter.param) + \"=\" + encodeURIComponent(value))\n }\n if (!parts.length) return url\n return url + (url.includes(\"?\") ? \"&\" : \"?\") + parts.join(\"&\")\n }\n\n const buildPlatformFilterParams = (filters) => {\n const expressions = []\n for (const filter of filters || []) {\n if (filter.kind === \"isnull\" || filter.kind === \"notnull\") {\n expressions.push([filter.field, filter.operator, null])\n continue\n }\n const value = filter.value === undefined || filter.value === null ? \"\" : String(filter.value)\n if (!value) {\n if (filter.required) return null\n continue\n }\n if (filter.kind === \"in\") {\n const entries = value.split(\",\").map((entry) => entry.trim()).filter(Boolean).map((entry) => [filter.field, \"=\", entry])\n if (!entries.length) continue\n expressions.push(entries.reduce((result, entry) => (result ? [result, \"or\", entry] : entry), null))\n continue\n }\n expressions.push([filter.field, filter.operator, value])\n }\n const params = new URLSearchParams()\n const merged = expressions.reduce((result, expression) => (result ? [result, \"and\", expression] : expression), null)\n if (merged) params.set(\"filter\", JSON.stringify(merged))\n return params\n }\n\n const [data_source_5w_5ibpv, setData_source_5w_5ibpv] = React.useState(null)\n const [data_source_i7_msox4, setData_source_i7_msox4] = React.useState(null)\n const [data_source_0t_avok9] = React.useState(null)\n const [data_source_h3_484qa] = React.useState(null)\n const [data_source_gb_d9cvs] = React.useState(null)\n const [data_source_tq_veb4m, setData_source_tq_veb4m] = React.useState(null)\n const [data_source_d2_bri5s, setData_source_d2_bri5s] = React.useState(null)\n const [data_source_gq_0q1dz, setData_source_gq_0q1dz] = React.useState(null)\n const [data_source_u8_nu1lu, setData_source_u8_nu1lu] = React.useState(null)\n\n const sqlselect_cmp_msyfmegt_jkkn3 = { url: \"/api/app/crudendpoint/AbpRoles\", method: \"GET\", responsePath: \"\" }\n const sqlinsert_cmp_msyfmegt_jkkn3 = { url: \"/api/app/crudendpoint/AbpRoles\", method: \"POST\", responsePath: \"\" }\n const sqlupdate_cmp_msyfmegt_jkkn3 = { url: \"/api/app/crudendpoint/AbpRoles/{id}\", method: \"PUT\", responsePath: \"\" }\n const sqldelete_cmp_msyfmegt_jkkn3 = { url: \"/api/app/crudendpoint/AbpRoles/{id}\", method: \"DELETE\", responsePath: \"\" }\n const sqlCanSelect_cmp_msyfmegt_jkkn3 = true\n const sqlCanInsert_cmp_msyfmegt_jkkn3 = true\n const sqlCanUpdate_cmp_msyfmegt_jkkn3 = true\n const sqlCanDelete_cmp_msyfmegt_jkkn3 = true\n const sqlKey_cmp_msyfmegt_jkkn3 = \"id\"\n const sqlKeyParam_cmp_msyfmegt_jkkn3 = \"id\"\n const [data_cmp_msyfmegt_jkkn3, setData_cmp_msyfmegt_jkkn3] = React.useState({})\n const [sqlOriginal_cmp_msyfmegt_jkkn3, setSqlOriginal_cmp_msyfmegt_jkkn3] = React.useState({})\n const [sqlMode_cmp_msyfmegt_jkkn3, setSqlMode_cmp_msyfmegt_jkkn3] = React.useState(\"edit\")\n const [sqlIndex_cmp_msyfmegt_jkkn3, setSqlIndex_cmp_msyfmegt_jkkn3] = React.useState(0)\n const [sqlBusy_cmp_msyfmegt_jkkn3, setSqlBusy_cmp_msyfmegt_jkkn3] = React.useState(false)\n const [sqlError_cmp_msyfmegt_jkkn3, setSqlError_cmp_msyfmegt_jkkn3] = React.useState(\"\")\n // Container element, so New can hand the cursor to the first field of the form.\n const sqlHost_cmp_msyfmegt_jkkn3 = React.useRef(null)\n // The Select key can come from the page URL, which is how a detail page reads\n // /api/app/orders/{id} or /api/app/orders?id=… for a single record.\n const sqlUrlKey_cmp_msyfmegt_jkkn3 = readUrlKey(\"query\", sqlKeyParam_cmp_msyfmegt_jkkn3)\n const sqlSelectUrl_cmp_msyfmegt_jkkn3 = React.useMemo(() => {\n if (!sqlselect_cmp_msyfmegt_jkkn3) return \"\"\n const base = sqlUrlKey_cmp_msyfmegt_jkkn3\n ? (() => {\n const bound = bindSqlUrl(sqlselect_cmp_msyfmegt_jkkn3.url, { [sqlKeyParam_cmp_msyfmegt_jkkn3]: sqlUrlKey_cmp_msyfmegt_jkkn3 }, sqlKeyParam_cmp_msyfmegt_jkkn3)\n return bound.keyBound ? bound.url : appendQueryParam(bound.url, sqlKeyParam_cmp_msyfmegt_jkkn3, sqlUrlKey_cmp_msyfmegt_jkkn3)\n })()\n : sqlselect_cmp_msyfmegt_jkkn3.url\n return base\n }, [sqlUrlKey_cmp_msyfmegt_jkkn3])\n // A key in the page URL always narrows the result: a list endpoint ignores the\n // parameter server side, so the requested record is picked out here. Without a\n // key every row is kept and the navigation below takes over.\n const sqlRows_cmp_msyfmegt_jkkn3 = React.useMemo(\n () => filterSqlRowsByKey(toSqlRows(data_source_5w_5ibpv, \"\"), sqlKey_cmp_msyfmegt_jkkn3, sqlUrlKey_cmp_msyfmegt_jkkn3),\n [data_source_5w_5ibpv, sqlUrlKey_cmp_msyfmegt_jkkn3],\n )\n // A fresh result set resets the position; New mode is left untouched so an\n // unsaved draft is not overwritten by a re-render.\n React.useEffect(() => {\n setSqlIndex_cmp_msyfmegt_jkkn3((current) => (current < sqlRows_cmp_msyfmegt_jkkn3.length ? current : 0))\n }, [sqlRows_cmp_msyfmegt_jkkn3])\n React.useEffect(() => {\n const row = sqlRows_cmp_msyfmegt_jkkn3[sqlIndex_cmp_msyfmegt_jkkn3]\n if (!row) return\n setData_cmp_msyfmegt_jkkn3(row)\n setSqlOriginal_cmp_msyfmegt_jkkn3(row)\n setSqlMode_cmp_msyfmegt_jkkn3(\"edit\")\n }, [sqlRows_cmp_msyfmegt_jkkn3, sqlIndex_cmp_msyfmegt_jkkn3])\n const sqlHasKey_cmp_msyfmegt_jkkn3 = (() => {\n const value = readSqlField(data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3)\n return value !== undefined && value !== null && value !== \"\"\n })()\n const sqlSetField_cmp_msyfmegt_jkkn3 = React.useCallback((path, value) => setData_cmp_msyfmegt_jkkn3((current) => setSqlField(current, path, value)), [])\n const sqlReload_cmp_msyfmegt_jkkn3 = React.useCallback(async () => {\n // Without the Read permission the container is not rendered at all; the\n // request is dropped here so it is not fired for a form nobody can see.\n if (!sqlCanSelect_cmp_msyfmegt_jkkn3) return\n if (!sqlSelectUrl_cmp_msyfmegt_jkkn3) return\n // Calling a URL that still holds a placeholder is a guaranteed 400, so the\n // missing key is reported in the component instead.\n if (hasSqlUrlParams(sqlSelectUrl_cmp_msyfmegt_jkkn3)) {\n throw new Error(translate(\"::App.Form.MissingKeyParam\", { 0: sqlKeyParam_cmp_msyfmegt_jkkn3 }))\n }\n const response = await apiService.fetchData({ url: sqlSelectUrl_cmp_msyfmegt_jkkn3, method: \"GET\" })\n setData_source_5w_5ibpv(getByPath(response.data, sqlselect_cmp_msyfmegt_jkkn3.responsePath))\n }, [sqlSelectUrl_cmp_msyfmegt_jkkn3])\n // Reload wrapped with the busy/error handling the toolbar and mount effect need,\n // so a failing Select is reported in the component instead of the console.\n const sqlRefresh_cmp_msyfmegt_jkkn3 = React.useCallback(async () => {\n setSqlBusy_cmp_msyfmegt_jkkn3(true)\n setSqlError_cmp_msyfmegt_jkkn3(\"\")\n try {\n await sqlReload_cmp_msyfmegt_jkkn3()\n } catch (error) {\n setSqlError_cmp_msyfmegt_jkkn3(toSqlErrorMessage(error))\n } finally {\n setSqlBusy_cmp_msyfmegt_jkkn3(false)\n }\n }, [sqlReload_cmp_msyfmegt_jkkn3])\n const sqlNew_cmp_msyfmegt_jkkn3 = React.useCallback(() => {\n if (!sqlCanInsert_cmp_msyfmegt_jkkn3) return\n setSqlError_cmp_msyfmegt_jkkn3(\"\")\n // Defaults are resolved on every New, so an @today column carries the day the\n // record was opened rather than the day the component was generated.\n const sqlDraft_cmp_msyfmegt_jkkn3 = buildSqlDefaults([{\"path\":\"IsDefault\",\"value\":true},{\"path\":\"IsStatic\",\"value\":true},{\"path\":\"IsPublic\",\"value\":false},{\"path\":\"EntityVersion\",\"value\":3},{\"path\":\"ExtraProperties\",\"value\":\"{}\"}])\n setData_cmp_msyfmegt_jkkn3(sqlDraft_cmp_msyfmegt_jkkn3)\n setSqlOriginal_cmp_msyfmegt_jkkn3({})\n setSqlMode_cmp_msyfmegt_jkkn3(\"new\")\n // After the re-render that paints the empty form, not before it.\n focusFirstSqlField(sqlHost_cmp_msyfmegt_jkkn3)\n }, [])\n const sqlPrev_cmp_msyfmegt_jkkn3 = React.useCallback(() => setSqlIndex_cmp_msyfmegt_jkkn3((current) => Math.max(0, current - 1)), [])\n const sqlNext_cmp_msyfmegt_jkkn3 = React.useCallback(() => setSqlIndex_cmp_msyfmegt_jkkn3((current) => Math.min(sqlRows_cmp_msyfmegt_jkkn3.length - 1, current + 1)), [sqlRows_cmp_msyfmegt_jkkn3])\n const sqlSave_cmp_msyfmegt_jkkn3 = async () => {\n const isNew = sqlMode_cmp_msyfmegt_jkkn3 === \"new\"\n const target = isNew ? sqlinsert_cmp_msyfmegt_jkkn3 : sqlupdate_cmp_msyfmegt_jkkn3\n // The button is already hidden without the permission; a script calling save\n // through a ref must not be able to walk around it either.\n if (!(isNew ? sqlCanInsert_cmp_msyfmegt_jkkn3 : sqlCanUpdate_cmp_msyfmegt_jkkn3)) return\n if (!target) {\n setSqlError_cmp_msyfmegt_jkkn3(isNew ? translate(\"::App.Form.InsertEndpointMissing\") : translate(\"::App.Form.UpdateEndpointMissing\"))\n return\n }\n // Update carries only the edited columns plus the key; Insert sends the record.\n const payload = isNew ? data_cmp_msyfmegt_jkkn3 : toSqlChanges(sqlOriginal_cmp_msyfmegt_jkkn3, data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3)\n if (!isNew && Object.keys(payload).filter((column) => column.toLowerCase() !== String(sqlKey_cmp_msyfmegt_jkkn3).toLowerCase()).length === 0) {\n setSqlError_cmp_msyfmegt_jkkn3(translate(\"::App.Form.NoChanges\"))\n return\n }\n setSqlBusy_cmp_msyfmegt_jkkn3(true)\n setSqlError_cmp_msyfmegt_jkkn3(\"\")\n try {\n const response = await callSqlEndpoint(target, data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3, payload)\n // With a Select endpoint the reload is the source of truth and its effect\n // repopulates the form; without one, the response row is all there is.\n if (sqlselect_cmp_msyfmegt_jkkn3) {\n await sqlReload_cmp_msyfmegt_jkkn3()\n } else {\n const saved = toSqlRows(response?.data, \"\")[0]\n if (saved) {\n setData_cmp_msyfmegt_jkkn3(saved)\n setSqlOriginal_cmp_msyfmegt_jkkn3(saved)\n }\n }\n setSqlMode_cmp_msyfmegt_jkkn3(\"edit\")\n } catch (error) {\n setSqlError_cmp_msyfmegt_jkkn3(toSqlErrorMessage(error))\n } finally {\n setSqlBusy_cmp_msyfmegt_jkkn3(false)\n }\n }\n const sqlDelete_cmp_msyfmegt_jkkn3 = async () => {\n if (!sqlCanDelete_cmp_msyfmegt_jkkn3 || !sqldelete_cmp_msyfmegt_jkkn3 || !sqlHasKey_cmp_msyfmegt_jkkn3 || sqlMode_cmp_msyfmegt_jkkn3 === \"new\") return\n // Deleting cannot be undone from the form, so it always asks first.\n const confirmMessage = translate(\"::App.Form.DeleteConfirm\", { 0: sqlKey_cmp_msyfmegt_jkkn3 + \" = \" + readSqlField(data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3) })\n if (typeof window !== \"undefined\" && !window.confirm(confirmMessage)) return\n setSqlBusy_cmp_msyfmegt_jkkn3(true)\n setSqlError_cmp_msyfmegt_jkkn3(\"\")\n try {\n await callSqlEndpoint(sqldelete_cmp_msyfmegt_jkkn3, data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3)\n setData_cmp_msyfmegt_jkkn3({})\n setSqlOriginal_cmp_msyfmegt_jkkn3({})\n setSqlMode_cmp_msyfmegt_jkkn3(\"new\")\n await sqlReload_cmp_msyfmegt_jkkn3()\n } catch (error) {\n setSqlError_cmp_msyfmegt_jkkn3(toSqlErrorMessage(error))\n } finally {\n setSqlBusy_cmp_msyfmegt_jkkn3(false)\n }\n }\n React.useEffect(() => { void sqlRefresh_cmp_msyfmegt_jkkn3() }, [sqlRefresh_cmp_msyfmegt_jkkn3])\n\n const filters_source_tq_veb4m = [{ param: \"RoleId\", value: readSqlField(data_cmp_msyfmegt_jkkn3, \"id\"), required: true }]\n const filterKey_source_tq_veb4m = JSON.stringify(filters_source_tq_veb4m)\n React.useEffect(() => {\n const url = buildFilteredUrl(\"/api/app/crudendpoint/AbpUserRoles\", filters_source_tq_veb4m)\n // An unmet required filter leaves the component empty on purpose.\n if (!url) {\n setData_source_tq_veb4m(null)\n return\n }\n let active = true\n apiService.fetchData({ url, method: 'GET' }).then((response) => {\n if (active) setData_source_tq_veb4m(getByPath(response.data, \"\"))\n }).catch((error) => console.error(\"AbpUserRoles · GetList endpoint error:\", error))\n return () => { active = false }\n }, [filterKey_source_tq_veb4m])\n\n React.useEffect(() => {\n let active = true\n apiService.fetchData({ url: \"/api/app/crudendpoint/AbpUsers\", method: 'GET' }).then((response) => {\n if (active) setData_source_gq_0q1dz(getByPath(response.data, \"\"))\n }).catch((error) => console.error(\"AbpUsers · GetList endpoint error:\", error))\n return () => { active = false }\n }, [])\n\n const platformFilters_cmp_mt0288i3_ebpj5 = [{ field: \"Id\", operator: \"=\", kind: \"eq\", value: readSqlField(data_cmp_msyfmegt_jkkn3, \"Id\"), required: true }]\n const platformKey_cmp_mt0288i3_ebpj5 = JSON.stringify(platformFilters_cmp_mt0288i3_ebpj5)\n // The views keep the params object in their own state and write back to it, so\n // it must stay the same object until a filter value actually changes.\n const platformParams_cmp_mt0288i3_ebpj5 = React.useMemo(() => buildPlatformFilterParams(platformFilters_cmp_mt0288i3_ebpj5), [platformKey_cmp_mt0288i3_ebpj5])\n\n const handle_cmp_msyfpfia_ifhdq_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"Name\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n const handle_cmp_msyjl707_crcn7_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"NormalizedName\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n const handle_cmp_msyjdodn_u3o74_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"ConcurrencyStamp\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n const handle_cmp_msyje9ys_2c08a_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"IsDefault\", typeof originalEvent === \"object\" && originalEvent?.target ? Boolean(originalEvent.target.checked) : typeof valueOrEvent === \"object\" && valueOrEvent?.target ? Boolean(valueOrEvent.target.checked) : Boolean(valueOrEvent))\n const event = { checked: Boolean(valueOrEvent), originalEvent, target: originalEvent?.target }\n }\n\n const handle_cmp_msyjebu2_27tqi_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"IsStatic\", typeof originalEvent === \"object\" && originalEvent?.target ? Boolean(originalEvent.target.checked) : typeof valueOrEvent === \"object\" && valueOrEvent?.target ? Boolean(valueOrEvent.target.checked) : Boolean(valueOrEvent))\n const event = { checked: Boolean(valueOrEvent), originalEvent, target: originalEvent?.target }\n }\n\n const handle_cmp_msyjmetn_gojol_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"IsPublic\", typeof originalEvent === \"object\" && originalEvent?.target ? Boolean(originalEvent.target.checked) : typeof valueOrEvent === \"object\" && valueOrEvent?.target ? Boolean(valueOrEvent.target.checked) : Boolean(valueOrEvent))\n const event = { checked: Boolean(valueOrEvent), originalEvent, target: originalEvent?.target }\n }\n\n const handle_cmp_msyjo0g8_oathk_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"EntityVersion\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n const handle_cmp_msyjplfq_py8lz_onChange = (valueOrEvent, originalEvent) => {\n sqlSetField_cmp_msyfmegt_jkkn3(\"ExtraProperties\", valueOrEvent?.target?.value ?? valueOrEvent)\n const event = valueOrEvent\n }\n\n return (\n <>\n {sqlCanSelect_cmp_msyfmegt_jkkn3 ? (\n {sqlHasKey_cmp_msyfmegt_jkkn3 ? sqlKey_cmp_msyfmegt_jkkn3 + \": \" + readSqlField(data_cmp_msyfmegt_jkkn3, sqlKey_cmp_msyfmegt_jkkn3) : sqlMode_cmp_msyfmegt_jkkn3 === \"new\" ? translate(\"::App.Platform.NewRecord\") : \"\"}}>\n
\n
\n \n \n \n \n {\"Is Default\"}\n \n \n {\"Is Static\"}\n \n \n {\"Is Public\"}\n \n \n \n
\n
\n {sqlRows_cmp_msyfmegt_jkkn3.length > 1 ? (\n <>\n \n {`${sqlIndex_cmp_msyfmegt_jkkn3 + 1} / ${sqlRows_cmp_msyfmegt_jkkn3.length}`}\n \n \n \n ) : null}\n {sqlinsert_cmp_msyfmegt_jkkn3 && sqlCanInsert_cmp_msyfmegt_jkkn3 ? : null}\n {(sqlMode_cmp_msyfmegt_jkkn3 === \"new\" ? sqlinsert_cmp_msyfmegt_jkkn3 && sqlCanInsert_cmp_msyfmegt_jkkn3 : sqlupdate_cmp_msyfmegt_jkkn3 && sqlCanUpdate_cmp_msyfmegt_jkkn3) ? : null}\n {sqldelete_cmp_msyfmegt_jkkn3 && sqlCanDelete_cmp_msyfmegt_jkkn3 ? : null}\n {sqlselect_cmp_msyfmegt_jkkn3 && sqlCanSelect_cmp_msyfmegt_jkkn3 ? : null}\n {sqlMode_cmp_msyfmegt_jkkn3 === \"new\" ? translate(\"::App.Platform.NewRecord\") : translate(\"::App.Platform.Editing\")}\n
\n {sqlError_cmp_msyfmegt_jkkn3 ?
{sqlError_cmp_msyfmegt_jkkn3}
: null}\n
\n
\n ) : null}\n \n
\n {(() => {\n const gridItems_cmp_mt023r5u_rb2jv = (Array.isArray(getByPath(data_source_tq_veb4m, \"\")) ? getByPath(data_source_tq_veb4m, \"\") : Array.isArray(data_source_tq_veb4m) ? data_source_tq_veb4m : null)\n // Nothing loaded yet — an endpoint still in flight, or a required filter\n // holding the request back — is an empty grid, not a binding error.\n if (gridItems_cmp_mt023r5u_rb2jv === null || gridItems_cmp_mt023r5u_rb2jv === undefined) return null\n if (!Array.isArray(gridItems_cmp_mt023r5u_rb2jv)) return
Grid items bağlantısı bir koleksiyon döndürmelidir.
\n const gridColumns_cmp_mt023r5u_rb2jv = [\"UserId\"]\n const gridLookups_cmp_mt023r5u_rb2jv = { \"UserId\": { rows: data_source_gq_0q1dz, path: \"\", value: \"Id\", text: \"UserName\" } }\n if (!gridColumns_cmp_mt023r5u_rb2jv.length) return
Preview için en az bir sütun seçin.
\n return (\n
\n \n \n {gridColumns_cmp_mt023r5u_rb2jv.map((column) => )}\n \n \n {gridItems_cmp_mt023r5u_rb2jv.map((gridItem_cmp_mt023r5u_rb2jv, rowIndex) => (\n \n {gridColumns_cmp_mt023r5u_rb2jv.map((column) => {\n const cellValue_cmp_mt023r5u_rb2jv = column === \"value\" ? gridItem_cmp_mt023r5u_rb2jv : getByPath(gridItem_cmp_mt023r5u_rb2jv, column)\n const lookedUp = resolveLookupText(gridLookups_cmp_mt023r5u_rb2jv[column], cellValue_cmp_mt023r5u_rb2jv)\n const cellText = lookedUp === undefined ? cellValue_cmp_mt023r5u_rb2jv : lookedUp\n const text = typeof cellText === \"object\" && cellText !== null && !(cellText instanceof Date) ? JSON.stringify(cellText) : formatLocaleValue(cellText) || \"—\"\n return \n })}\n \n ))}\n \n
{(column === \"value\" ? \"Value\" : column)}
{text}
\n
\n )\n })()}\n
\n {platformParams_cmp_mt0288i3_ebpj5 === null ? null : (\n \n )}\n \n )\n}\n\nexport default RoleComponent", - "Props": "{\"visualDesigner\":{\"version\":1,\"sourceMode\":\"visual\",\"nodes\":[{\"id\":\"cmp_msyfmegt_jkkn3\",\"type\":\"Form\",\"kind\":\"layout\",\"props\":{\"selectEndpoint\":\"source_5w_5ibpv\",\"insertEndpoint\":\"source_h3_484qa\",\"updateEndpoint\":\"source_gb_d9cvs\",\"deleteEndpoint\":\"source_0t_avok9\",\"keyFieldName\":\"id\",\"collectionPath\":\"\",\"keySource\":\"query\",\"keyParamName\":\"id\",\"previewKeyValue\":\"\",\"autoLoad\":true,\"showToolbar\":true,\"gap\":16,\"className\":\"\",\"title\":\"::AbpIdentity.Roles\",\"columnCount\":2,\"selectPermission\":\"\",\"insertPermission\":\"\"},\"events\":{\"onLoad\":\"\",\"onRecordChange\":\"\",\"onFieldChange\":\"\",\"onNewRecord\":\"\",\"onModeChange\":\"\",\"onBeforeSave\":\"\",\"onAfterSave\":\"\",\"onBeforeDelete\":\"\",\"onAfterDelete\":\"\",\"onError\":\"\"},\"bindings\":{},\"children\":[{\"id\":\"cmp_msyfpfia_ifhdq\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"::App.Platform.Name\",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"text\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\",\"sqlDefaultValue\":\"\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"Name\"}},\"children\":[],\"ref\":\"input1\"},{\"id\":\"cmp_msyjl707_crcn7\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"Normalized Name :\",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"text\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"NormalizedName\"}},\"children\":[],\"ref\":\"input2\"},{\"id\":\"cmp_msyjdodn_u3o74\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"Concurrent Stamp : \",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"text\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"ConcurrencyStamp\"}},\"children\":[],\"ref\":\"input3\"},{\"id\":\"cmp_msyje9ys_2c08a\",\"type\":\"Checkbox\",\"kind\":\"ui\",\"props\":{\"checked\":false,\"color\":\"blue-500\",\"disabled\":false,\"labelRef\":\"\",\"name\":\"\",\"readOnly\":false,\"value\":\"\",\"field\":null,\"className\":\"\",\"children\":\"Is Default\",\"style\":{},\"sqlDefaultValue\":\"true\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"checked\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"IsDefault\"}},\"children\":[],\"ref\":\"checkbox1\"},{\"id\":\"cmp_msyjebu2_27tqi\",\"type\":\"Checkbox\",\"kind\":\"ui\",\"props\":{\"checked\":false,\"color\":\"blue-500\",\"disabled\":false,\"labelRef\":\"\",\"name\":\"\",\"readOnly\":false,\"value\":\"\",\"field\":null,\"className\":\"\",\"children\":\"Is Static\",\"style\":{},\"sqlDefaultValue\":\"true\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"checked\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"IsStatic\"}},\"children\":[],\"ref\":\"checkbox2\"},{\"id\":\"cmp_msyjmetn_gojol\",\"type\":\"Checkbox\",\"kind\":\"ui\",\"props\":{\"checked\":false,\"color\":\"blue-500\",\"disabled\":false,\"labelRef\":\"\",\"name\":\"\",\"readOnly\":false,\"value\":\"\",\"field\":null,\"className\":\"\",\"children\":\"Is Public\",\"style\":{},\"sqlDefaultValue\":\"false\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"checked\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"IsPublic\"}},\"children\":[],\"ref\":\"checkbox3\"},{\"id\":\"cmp_msyjo0g8_oathk\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"Entity Version : \",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"number\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\",\"sqlDefaultValue\":\"3\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"EntityVersion\"}},\"children\":[],\"ref\":\"input4\"},{\"id\":\"cmp_msyjplfq_py8lz\",\"type\":\"Input\",\"kind\":\"ui\",\"props\":{\"asElement\":\"\",\"disabled\":false,\"invalid\":false,\"rows\":3,\"prefix\":\"Extra Properties :\",\"size\":\"md\",\"suffix\":\"\",\"textArea\":false,\"type\":\"text\",\"unstyle\":false,\"field\":\"\",\"form\":\"\",\"className\":\"\",\"children\":\"\",\"style\":{},\"placeholder\":\"Metin girin...\",\"value\":\"\",\"name\":\"\",\"autoComplete\":\"off\",\"autoFocus\":false,\"readOnly\":false,\"required\":false,\"maxLength\":\"\",\"minLength\":\"\",\"pattern\":\"\",\"sqlDefaultValue\":\"{}\"},\"events\":{\"onChange\":\"\"},\"bindings\":{\"value\":{\"sourceId\":\"cmp_msyfmegt_jkkn3\",\"path\":\"ExtraProperties\"}},\"children\":[],\"ref\":\"input5\"}],\"ref\":\"form1\"},{\"id\":\"cmp_mubctlbp_p6ajt\",\"type\":\"BarcodeScanField\",\"kind\":\"custom\",\"props\":{\"label\":\"\",\"name\":\"barcode\",\"placeholder\":\"\",\"buttonText\":\"\",\"continuous\":true,\"readOnly\":true,\"className\":\"mt-4\"},\"events\":{},\"bindings\":{},\"children\":[],\"ref\":\"barcodeScanField1\"},{\"id\":\"cmp_mt023r5u_rb2jv\",\"type\":\"Grid\",\"kind\":\"ui\",\"props\":{\"hoverable\":true,\"overflow\":true,\"borderlessRow\":false,\"compact\":false,\"cols\":\"3\",\"rows\":\"2\",\"gap\":\"4\",\"colGap\":\"4\",\"rowGap\":\"4\",\"responsive\":true,\"smCols\":\"1\",\"mdCols\":\"2\",\"lgCols\":\"3\",\"xlCols\":\"4\",\"autoFit\":false,\"minColWidth\":\"200px\",\"height\":\"auto\",\"className\":\"mt-4\",\"items\":[{\"id\":\"grid-item-1\",\"content\":\"Grid Item 1\",\"colSpan\":1,\"rowSpan\":1,\"colStart\":1,\"rowStart\":1,\"className\":\"bg-blue-100 p-4 border rounded\"},{\"id\":\"grid-item-2\",\"content\":\"Grid Item 2\",\"colSpan\":1,\"rowSpan\":1,\"colStart\":2,\"rowStart\":1,\"className\":\"bg-green-100 p-4 border rounded\"},{\"id\":\"grid-item-3\",\"content\":\"Grid Item 3\",\"colSpan\":1,\"rowSpan\":1,\"colStart\":3,\"rowStart\":1,\"className\":\"bg-yellow-100 p-4 border rounded\"},{\"id\":\"grid-item-4\",\"content\":\"Grid Item 4\",\"colSpan\":2,\"rowSpan\":1,\"colStart\":1,\"rowStart\":2,\"className\":\"bg-purple-100 p-4 border rounded\"},{\"id\":\"grid-item-5\",\"content\":\"Grid Item 5\",\"colSpan\":1,\"rowSpan\":1,\"colStart\":3,\"rowStart\":2,\"className\":\"bg-red-100 p-4 border rounded\"}],\"dataColumns\":[\"UserId\"],\"columnLookups\":{\"UserId\":{\"sourceId\":\"source_gq_0q1dz\",\"path\":\"\",\"valueField\":\"Id\",\"textField\":\"UserName\"}},\"columnCaptions\":{}},\"events\":{},\"bindings\":{\"items\":{\"sourceId\":\"source_tq_veb4m\",\"path\":\"\"}},\"children\":[],\"ref\":\"grid1\"},{\"id\":\"cmp_mt0288i3_ebpj5\",\"type\":\"ListView\",\"kind\":\"platform\",\"props\":{\"listFormCode\":\"AbpIdentity.Roles\",\"height\":\"520px\",\"filters\":[{\"id\":\"filter_y9_tr73w\",\"field\":\"Id\",\"operator\":\"eq\",\"source\":\"record\",\"value\":\"form1.Id\",\"previewValue\":\"\",\"required\":true}],\"className\":\"mt-4\"},\"events\":{},\"bindings\":{},\"children\":[],\"ref\":\"listView1\"}],\"canvas\":{\"width\":\"responsive\"},\"lifecycle\":{\"onMount\":\"\"},\"dataSources\":[{\"id\":\"source_5w_5ibpv\",\"name\":\"AbpRoles · GetList\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpRoles\",\"responsePath\":\"\"},{\"id\":\"source_i7_msox4\",\"name\":\"AbpRoles · GetById\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpRoles/{id}\",\"responsePath\":\"\"},{\"id\":\"source_0t_avok9\",\"name\":\"AbpRoles · Delete\",\"method\":\"DELETE\",\"url\":\"/api/app/crudendpoint/AbpRoles/{id}\",\"responsePath\":\"\"},{\"id\":\"source_h3_484qa\",\"name\":\"AbpRoles · Create\",\"method\":\"POST\",\"url\":\"/api/app/crudendpoint/AbpRoles\",\"responsePath\":\"\"},{\"id\":\"source_gb_d9cvs\",\"name\":\"AbpRoles · Update\",\"method\":\"PUT\",\"url\":\"/api/app/crudendpoint/AbpRoles/{id}\",\"responsePath\":\"\"},{\"id\":\"source_tq_veb4m\",\"name\":\"AbpUserRoles · GetList\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpUserRoles\",\"responsePath\":\"\",\"filters\":[{\"id\":\"filter_sw_86e0q\",\"field\":\"RoleId\",\"operator\":\"eq\",\"source\":\"record\",\"value\":\"form1.id\",\"previewValue\":\"\",\"required\":true}]},{\"id\":\"source_d2_bri5s\",\"name\":\"AbpUserRoles · GetById\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpUserRoles/{id}\",\"responsePath\":\"\"},{\"id\":\"source_gq_0q1dz\",\"name\":\"AbpUsers · GetList\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpUsers\",\"responsePath\":\"\"},{\"id\":\"source_u8_nu1lu\",\"name\":\"AbpUsers · GetById\",\"method\":\"GET\",\"url\":\"/api/app/crudendpoint/AbpUsers/{id}\",\"responsePath\":\"\"}],\"permissionCode\":\"\"}}", - "DataSources": "[\r\n {\r\n \"Name\": \"AbpRoles · GetList\",\r\n \"Method\": \"GET\",\r\n \"Url\": \"/api/app/crudendpoint/AbpRoles\",\r\n \"ResponsePath\": null,\r\n \"EntityName\": \"AbpRoles\",\r\n \"OperationType\": \"GetList\",\r\n \"SeedFile\": \"crud/AbpRoles.json\"\r\n },\r\n {\r\n \"Name\": \"AbpRoles · GetById\",\r\n \"Method\": \"GET\",\r\n \"Url\": \"/api/app/crudendpoint/AbpRoles/{id}\",\r\n \"ResponsePath\": null,\r\n \"EntityName\": \"AbpRoles\",\r\n \"OperationType\": \"GetById\",\r\n \"SeedFile\": \"crud/AbpRoles.json\"\r\n },\r\n {\r\n \"Name\": \"AbpRoles · Delete\",\r\n \"Method\": \"DELETE\",\r\n \"Url\": \"/api/app/crudendpoint/AbpRoles/{id}\",\r\n \"ResponsePath\": null,\r\n \"EntityName\": \"AbpRoles\",\r\n \"OperationType\": \"Delete\",\r\n \"SeedFile\": \"crud/AbpRoles.json\"\r\n },\r\n {\r\n \"Name\": \"AbpRoles · Create\",\r\n \"Method\": \"POST\",\r\n \"Url\": \"/api/app/crudendpoint/AbpRoles\",\r\n \"ResponsePath\": null,\r\n \"EntityName\": \"AbpRoles\",\r\n \"OperationType\": \"Create\",\r\n \"SeedFile\": \"crud/AbpRoles.json\"\r\n },\r\n {\r\n \"Name\": \"AbpRoles · Update\",\r\n \"Method\": \"PUT\",\r\n \"Url\": \"/api/app/crudendpoint/AbpRoles/{id}\",\r\n \"ResponsePath\": null,\r\n \"EntityName\": \"AbpRoles\",\r\n \"OperationType\": \"Update\",\r\n \"SeedFile\": \"crud/AbpRoles.json\"\r\n },\r\n {\r\n \"Name\": \"AbpUserRoles · GetList\",\r\n \"Method\": \"GET\",\r\n \"Url\": \"/api/app/crudendpoint/AbpUserRoles\",\r\n \"ResponsePath\": null,\r\n \"EntityName\": \"AbpUserRoles\",\r\n \"OperationType\": \"GetList\",\r\n \"SeedFile\": \"crud/AbpUserRoles.json\"\r\n },\r\n {\r\n \"Name\": \"AbpUserRoles · GetById\",\r\n \"Method\": \"GET\",\r\n \"Url\": \"/api/app/crudendpoint/AbpUserRoles/{id}\",\r\n \"ResponsePath\": null,\r\n \"EntityName\": \"AbpUserRoles\",\r\n \"OperationType\": \"GetById\",\r\n \"SeedFile\": \"crud/AbpUserRoles.json\"\r\n },\r\n {\r\n \"Name\": \"AbpUsers · GetList\",\r\n \"Method\": \"GET\",\r\n \"Url\": \"/api/app/crudendpoint/AbpUsers\",\r\n \"ResponsePath\": null,\r\n \"EntityName\": \"AbpUsers\",\r\n \"OperationType\": \"GetList\",\r\n \"SeedFile\": \"crud/AbpUsers.json\"\r\n },\r\n {\r\n \"Name\": \"AbpUsers · GetById\",\r\n \"Method\": \"GET\",\r\n \"Url\": \"/api/app/crudendpoint/AbpUsers/{id}\",\r\n \"ResponsePath\": null,\r\n \"EntityName\": \"AbpUsers\",\r\n \"OperationType\": \"GetById\",\r\n \"SeedFile\": \"crud/AbpUsers.json\"\r\n }\r\n]" - }, - { - "Id": "8f1c2d40-0000-4000-8000-000000000101", - "Name": "PublicHomePage", - "RoutePath": "/home", - "Description": "Home Page", - "IsActive": true, - "Dependencies": [], - "Code": "/*__SOZSOFT_VISUAL_DESIGNER__%7B%22version%22%3A1%2C%22sourceMode%22%3A%22visual%22%2C%22nodes%22%3A%5B%7B%22id%22%3A%22cmp_seed_0089%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22min-h-screen%20bg-gray-50%20dark%3Abg-gray-950%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0088%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22relative%20overflow-hidden%20bg-blue-900%20text-white%20dark%3Abg-gray-950%20flex%20min-h-screen%20items-center%20px-6%20pb-16%20pt-40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0085%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22absolute%20inset-0%20bg-gradient-to-br%20from-blue-900%20via-indigo-900%20to-purple-900%20dark%3Afrom-gray-950%20dark%3Avia-gray-950%20dark%3Ato-gray-950%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0086%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22absolute%20inset-0%20opacity-20%20dark%3Aopacity-35%22%2C%22style%22%3A%7B%22backgroundImage%22%3A%22url(%5C%22https%3A%2F%2Fimages.pexels.com%2Fphotos%2F3183150%2Fpexels-photo-3183150.jpeg%3Fauto%3Dcompress%26cs%3Dtinysrgb%26w%3D1920%5C%22)%22%2C%22backgroundSize%22%3A%22cover%22%2C%22backgroundPosition%22%3A%22center%22%7D%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0087%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22absolute%20inset-0%20bg-blue-950%2F25%20dark%3Abg-gray-950%2F45%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0019%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22container%20relative%20mx-auto%20max-w-4xl%20px-4%20text-center%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0001%22%2C%22type%22%3A%22h1%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-6%20text-3xl%20font-bold%20text-white%20md%3Atext-6xl%22%2C%22children%22%3A%22%3A%3AApp.HeroSlide1.Slide1Title%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0002%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-12%20text-xl%20text-gray-300%20md%3Atext-2xl%22%2C%22children%22%3A%22%3A%3AApp.HeroSlide1.Subtitle%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0005%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-16%20flex%20flex-col%20justify-center%20gap-6%20md%3Aflex-row%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0003%22%2C%22type%22%3A%22a%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22href%22%3A%22%2Fcontact%22%2C%22className%22%3A%22inline-flex%20items-center%20justify-center%20rounded-lg%20bg-gradient-to-r%20from-blue-500%20to-purple-500%20px-8%20py-4%20font-semibold%20text-white%20transition-all%20hover%3Afrom-blue-600%20hover%3Ato-purple-600%22%2C%22children%22%3A%22%3A%3AApp.HeroCta.Consultation%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0004%22%2C%22type%22%3A%22a%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22href%22%3A%22%2Fproducts%22%2C%22className%22%3A%22inline-flex%20items-center%20justify-center%20rounded-lg%20bg-white%2F10%20px-8%20py-4%20font-semibold%20text-white%20backdrop-blur-sm%20transition-all%20hover%3Abg-white%2F20%22%2C%22children%22%3A%22%3A%3AApp.HeroCta.Discover%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0018%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mx-auto%20grid%20max-w-4xl%20grid-cols-1%20gap-8%20md%3Agrid-cols-3%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0009%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-2xl%20bg-white%2F5%20p-8%20text-center%20backdrop-blur-sm%20transition-all%20hover%3Abg-white%2F10%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0006%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaCalendarAlt%22%2C%22size%22%3A40%2C%22className%22%3A%22mx-auto%20mb-4%20text-blue-300%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0007%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-3%20text-xl%20font-semibold%20text-white%22%2C%22children%22%3A%22%3A%3AApp.Slide1Service1.Service1Title%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0008%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-300%22%2C%22children%22%3A%22%3A%3AApp.Slide1Service1.Service1Desc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0013%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-2xl%20bg-white%2F5%20p-8%20text-center%20backdrop-blur-sm%20transition-all%20hover%3Abg-white%2F10%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0010%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaUsers%22%2C%22size%22%3A40%2C%22className%22%3A%22mx-auto%20mb-4%20text-blue-300%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0011%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-3%20text-xl%20font-semibold%20text-white%22%2C%22children%22%3A%22%3A%3AApp.Slide1Service2.Service2Title%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0012%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-300%22%2C%22children%22%3A%22%3A%3AApp.Slide1Service2.Service2Desc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0017%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-2xl%20bg-white%2F5%20p-8%20text-center%20backdrop-blur-sm%20transition-all%20hover%3Abg-white%2F10%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0014%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaShieldAlt%22%2C%22size%22%3A40%2C%22className%22%3A%22mx-auto%20mb-4%20text-blue-300%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0015%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-3%20text-xl%20font-semibold%20text-white%22%2C%22children%22%3A%22%3A%3AApp.Slide1Service3.Service3Title%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0016%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-300%22%2C%22children%22%3A%22%3A%3AApp.Slide1Service3.Service3Desc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%5D%7D%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0057%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22bg-white%20py-20%20dark%3Abg-gray-950%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0056%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22container%20mx-auto%20px-4%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0022%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-16%20text-center%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0020%22%2C%22type%22%3A%22h2%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-4xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%20mb-4%22%2C%22children%22%3A%22%3A%3AApp.PublicFeatures.FeaturesTitle%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0021%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-xl%20text-gray-600%20dark%3Atext-gray-300%20max-w-2xl%20mx-auto%22%2C%22children%22%3A%22%3A%3AApp.PublicFeatures.Subtitle%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0055%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22grid%20grid-cols-1%20gap-8%20md%3Agrid-cols-2%20lg%3Agrid-cols-4%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0026%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20transition-shadow%20hover%3Ashadow-xl%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0023%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaUsers%22%2C%22size%22%3A48%2C%22className%22%3A%22mb-6%20text-blue-500%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0024%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-3%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AApp.PublicFeatures.Reliable%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0025%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.FeaturesReliable.ReliableDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0030%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20transition-shadow%20hover%3Ashadow-xl%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0027%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaCalendarAlt%22%2C%22size%22%3A48%2C%22className%22%3A%22mb-6%20text-blue-500%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0028%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-3%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AApp.VideoRoom.Planning%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0029%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.FeaturesRapid.RapidDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0034%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20transition-shadow%20hover%3Ashadow-xl%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0031%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaBookOpen%22%2C%22size%22%3A48%2C%22className%22%3A%22mb-6%20text-blue-500%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0032%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-3%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AApp.PublicFeatures.Expert%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0033%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.FeaturesExpert.ExpertDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0038%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20transition-shadow%20hover%3Ashadow-xl%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0035%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaCreditCard%22%2C%22size%22%3A48%2C%22className%22%3A%22mb-6%20text-blue-500%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0036%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-3%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AApp.PublicFeatures.Muhasebe%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0037%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.FeaturesMuhasebe.MuhasebeDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0042%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20transition-shadow%20hover%3Ashadow-xl%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0039%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaRegComment%22%2C%22size%22%3A48%2C%22className%22%3A%22mb-6%20text-blue-500%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0040%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-3%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AApp.PublicFeatures.Iletisim%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0041%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.FeaturesIletisim.IletisimDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0046%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20transition-shadow%20hover%3Ashadow-xl%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0043%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaPhone%22%2C%22size%22%3A48%2C%22className%22%3A%22mb-6%20text-blue-500%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0044%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-3%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AApp.PublicFeatures.Mobil%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0045%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.FeaturesMobil.MobilDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0050%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20transition-shadow%20hover%3Ashadow-xl%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0047%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaChartBar%22%2C%22size%22%3A48%2C%22className%22%3A%22mb-6%20text-blue-500%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0048%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-3%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AApp.PublicFeatures.Scalable%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0049%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.FeaturesScalable.ScalableDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0054%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20transition-shadow%20hover%3Ashadow-xl%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0051%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaShieldAlt%22%2C%22size%22%3A48%2C%22className%22%3A%22mb-6%20text-blue-500%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0052%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-3%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AApp.Listform.ListformField.Security%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0053%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.FeaturesGuvenlik.GuvenlikDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%5D%7D%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0079%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22bg-gray-50%20py-20%20dark%3Abg-gray-900%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0078%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22container%20mx-auto%20px-4%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0060%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-16%20text-center%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0058%22%2C%22type%22%3A%22h2%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-4xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%20mb-4%22%2C%22children%22%3A%22%3A%3AApp.PublicSolutions.SolutionsTitle%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0059%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-xl%20text-gray-600%20dark%3Atext-gray-300%20max-w-2xl%20mx-auto%22%2C%22children%22%3A%22%3A%3AApp.PublicSolutions.Subtitle%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0077%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22grid%20grid-cols-1%20gap-8%20md%3Agrid-cols-2%20lg%3Agrid-cols-4%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0064%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22bg-blue-600%20h-full%20rounded-2xl%20p-8%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0061%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaDesktop%22%2C%22size%22%3A64%2C%22className%22%3A%22mb-6%20text-white%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0062%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-4%20text-2xl%20font-semibold%20text-white%22%2C%22children%22%3A%22%3A%3AApp.BlogCategories.Webdev%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0063%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-white%2F90%22%2C%22children%22%3A%22%3A%3AApp.SolutionsWeb.WebDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0068%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22bg-purple-600%20h-full%20rounded-2xl%20p-8%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0065%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaMobileAlt%22%2C%22size%22%3A64%2C%22className%22%3A%22mb-6%20text-white%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0066%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-4%20text-2xl%20font-semibold%20text-white%22%2C%22children%22%3A%22%3A%3AApp.ServicesMobile.MobileTitle%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0067%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-white%2F90%22%2C%22children%22%3A%22%3A%3AApp.SolutionsMobile.MobileDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0072%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22bg-green-600%20h-full%20rounded-2xl%20p-8%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0069%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaServer%22%2C%22size%22%3A64%2C%22className%22%3A%22mb-6%20text-white%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0070%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-4%20text-2xl%20font-semibold%20text-white%22%2C%22children%22%3A%22%3A%3AApp.SolutionsCustom.CustomTitle%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0071%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-white%2F90%22%2C%22children%22%3A%22%3A%3AApp.SolutionsCustom.CustomDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0076%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22bg-red-600%20h-full%20rounded-2xl%20p-8%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0073%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaDatabase%22%2C%22size%22%3A64%2C%22className%22%3A%22mb-6%20text-white%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0074%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-4%20text-2xl%20font-semibold%20text-white%22%2C%22children%22%3A%22%3A%3AApp.SolutionsDatabase.DatabaseTitle%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0075%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-white%2F90%22%2C%22children%22%3A%22%3A%3AApp.SolutionsDatabase.DatabaseDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%5D%7D%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0084%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22bg-blue-900%20py-16%20dark%3Abg-gray-800%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0083%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22container%20mx-auto%20px-4%20text-center%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0080%22%2C%22type%22%3A%22h2%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-4%20text-3xl%20font-bold%20text-white%22%2C%22children%22%3A%22%3A%3AApp.PublicCommon.GetStarted%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0081%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-8%20text-lg%20text-white%22%2C%22children%22%3A%22%3A%3AApp.PublicCommon.Contact%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0082%22%2C%22type%22%3A%22a%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22href%22%3A%22%2Fcontact%22%2C%22className%22%3A%22inline-block%20rounded-lg%20bg-white%20px-8%20py-3%20font-semibold%20text-blue-600%20transition-colors%20hover%3Abg-blue-50%22%2C%22children%22%3A%22%3A%3AApp.PublicCommon.LearnMore%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%5D%7D%5D%7D%5D%2C%22canvas%22%3A%7B%22width%22%3A%22responsive%22%7D%2C%22lifecycle%22%3A%7B%22onMount%22%3A%22%22%7D%2C%22dataSources%22%3A%5B%5D%2C%22permissionCode%22%3A%22%22%7D__*/\nconst PublicHomePage = () => {\n\n\n return (\n <>\n
\n
\n
\n
\n
\n
\n

\n {translate(\"::App.HeroSlide1.Slide1Title\")}\n

\n

\n {translate(\"::App.HeroSlide1.Subtitle\")}\n

\n
\n
\n
\n \n

\n {translate(\"::App.Slide1Service1.Service1Title\")}\n

\n

\n {translate(\"::App.Slide1Service1.Service1Desc\")}\n

\n
\n
\n \n

\n {translate(\"::App.Slide1Service2.Service2Title\")}\n

\n

\n {translate(\"::App.Slide1Service2.Service2Desc\")}\n

\n
\n
\n \n

\n {translate(\"::App.Slide1Service3.Service3Title\")}\n

\n

\n {translate(\"::App.Slide1Service3.Service3Desc\")}\n

\n
\n
\n
\n
\n
\n
\n
\n

\n {translate(\"::App.PublicFeatures.FeaturesTitle\")}\n

\n

\n {translate(\"::App.PublicFeatures.Subtitle\")}\n

\n
\n
\n
\n \n

\n {translate(\"::App.PublicFeatures.Reliable\")}\n

\n

\n {translate(\"::App.FeaturesReliable.ReliableDesc\")}\n

\n
\n
\n \n

\n {translate(\"::App.VideoRoom.Planning\")}\n

\n

\n {translate(\"::App.FeaturesRapid.RapidDesc\")}\n

\n
\n
\n \n

\n {translate(\"::App.PublicFeatures.Expert\")}\n

\n

\n {translate(\"::App.FeaturesExpert.ExpertDesc\")}\n

\n
\n
\n \n

\n {translate(\"::App.PublicFeatures.Muhasebe\")}\n

\n

\n {translate(\"::App.FeaturesMuhasebe.MuhasebeDesc\")}\n

\n
\n
\n \n

\n {translate(\"::App.PublicFeatures.Iletisim\")}\n

\n

\n {translate(\"::App.FeaturesIletisim.IletisimDesc\")}\n

\n
\n
\n \n

\n {translate(\"::App.PublicFeatures.Mobil\")}\n

\n

\n {translate(\"::App.FeaturesMobil.MobilDesc\")}\n

\n
\n
\n \n

\n {translate(\"::App.PublicFeatures.Scalable\")}\n

\n

\n {translate(\"::App.FeaturesScalable.ScalableDesc\")}\n

\n
\n
\n \n

\n {translate(\"::App.Listform.ListformField.Security\")}\n

\n

\n {translate(\"::App.FeaturesGuvenlik.GuvenlikDesc\")}\n

\n
\n
\n
\n
\n
\n
\n
\n

\n {translate(\"::App.PublicSolutions.SolutionsTitle\")}\n

\n

\n {translate(\"::App.PublicSolutions.Subtitle\")}\n

\n
\n
\n
\n \n

\n {translate(\"::App.BlogCategories.Webdev\")}\n

\n

\n {translate(\"::App.SolutionsWeb.WebDesc\")}\n

\n
\n
\n \n

\n {translate(\"::App.ServicesMobile.MobileTitle\")}\n

\n

\n {translate(\"::App.SolutionsMobile.MobileDesc\")}\n

\n
\n
\n \n

\n {translate(\"::App.SolutionsCustom.CustomTitle\")}\n

\n

\n {translate(\"::App.SolutionsCustom.CustomDesc\")}\n

\n
\n
\n \n

\n {translate(\"::App.SolutionsDatabase.DatabaseTitle\")}\n

\n

\n {translate(\"::App.SolutionsDatabase.DatabaseDesc\")}\n

\n
\n
\n
\n
\n
\n
\n

\n {translate(\"::App.PublicCommon.GetStarted\")}\n

\n

\n {translate(\"::App.PublicCommon.Contact\")}\n

\n \n {translate(\"::App.PublicCommon.LearnMore\")}\n \n
\n
\n
\n \n )\n}\n\nexport default PublicHomePage\n", - "Props": "{\"visualDesigner\":{\"version\":1,\"sourceMode\":\"visual\",\"nodes\":[{\"id\":\"cmp_seed_0089\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"min-h-screen bg-gray-50 dark:bg-gray-950\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0088\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"relative overflow-hidden bg-blue-900 text-white dark:bg-gray-950 flex min-h-screen items-center px-6 pb-16 pt-40\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0085\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"absolute inset-0 bg-gradient-to-br from-blue-900 via-indigo-900 to-purple-900 dark:from-gray-950 dark:via-gray-950 dark:to-gray-950\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0086\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"absolute inset-0 opacity-20 dark:opacity-35\",\"style\":{\"backgroundImage\":\"url(\\\"https://images.pexels.com/photos/3183150/pexels-photo-3183150.jpeg?auto=compress&cs=tinysrgb&w=1920\\\")\",\"backgroundSize\":\"cover\",\"backgroundPosition\":\"center\"}},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0087\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"absolute inset-0 bg-blue-950/25 dark:bg-gray-950/45\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0019\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"container relative mx-auto max-w-4xl px-4 text-center\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0001\",\"type\":\"h1\",\"kind\":\"html\",\"props\":{\"className\":\"mb-6 text-3xl font-bold text-white md:text-6xl\",\"children\":\"::App.HeroSlide1.Slide1Title\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0002\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"mb-12 text-xl text-gray-300 md:text-2xl\",\"children\":\"::App.HeroSlide1.Subtitle\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0005\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"mb-16 flex flex-col justify-center gap-6 md:flex-row\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0003\",\"type\":\"a\",\"kind\":\"html\",\"props\":{\"href\":\"/contact\",\"className\":\"inline-flex items-center justify-center rounded-lg bg-gradient-to-r from-blue-500 to-purple-500 px-8 py-4 font-semibold text-white transition-all hover:from-blue-600 hover:to-purple-600\",\"children\":\"::App.HeroCta.Consultation\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0004\",\"type\":\"a\",\"kind\":\"html\",\"props\":{\"href\":\"/products\",\"className\":\"inline-flex items-center justify-center rounded-lg bg-white/10 px-8 py-4 font-semibold text-white backdrop-blur-sm transition-all hover:bg-white/20\",\"children\":\"::App.HeroCta.Discover\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0018\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"mx-auto grid max-w-4xl grid-cols-1 gap-8 md:grid-cols-3\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0009\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-2xl bg-white/5 p-8 text-center backdrop-blur-sm transition-all hover:bg-white/10\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0006\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaCalendarAlt\",\"size\":40,\"className\":\"mx-auto mb-4 text-blue-300\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0007\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-3 text-xl font-semibold text-white\",\"children\":\"::App.Slide1Service1.Service1Title\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0008\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-300\",\"children\":\"::App.Slide1Service1.Service1Desc\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0013\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-2xl bg-white/5 p-8 text-center backdrop-blur-sm transition-all hover:bg-white/10\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0010\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaUsers\",\"size\":40,\"className\":\"mx-auto mb-4 text-blue-300\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0011\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-3 text-xl font-semibold text-white\",\"children\":\"::App.Slide1Service2.Service2Title\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0012\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-300\",\"children\":\"::App.Slide1Service2.Service2Desc\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0017\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-2xl bg-white/5 p-8 text-center backdrop-blur-sm transition-all hover:bg-white/10\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0014\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaShieldAlt\",\"size\":40,\"className\":\"mx-auto mb-4 text-blue-300\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0015\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-3 text-xl font-semibold text-white\",\"children\":\"::App.Slide1Service3.Service3Title\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0016\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-300\",\"children\":\"::App.Slide1Service3.Service3Desc\"},\"events\":{},\"bindings\":{},\"children\":[]}]}]}]}]},{\"id\":\"cmp_seed_0057\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"bg-white py-20 dark:bg-gray-950\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0056\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"container mx-auto px-4\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0022\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"mb-16 text-center\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0020\",\"type\":\"h2\",\"kind\":\"html\",\"props\":{\"className\":\"text-4xl font-bold text-gray-900 dark:text-gray-100 mb-4\",\"children\":\"::App.PublicFeatures.FeaturesTitle\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0021\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto\",\"children\":\"::App.PublicFeatures.Subtitle\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0055\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-4\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0026\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl bg-white p-8 shadow-lg transition-shadow hover:shadow-xl dark:bg-gray-900 dark:shadow-gray-950/40\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0023\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaUsers\",\"size\":48,\"className\":\"mb-6 text-blue-500\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0024\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-3 text-2xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"::App.PublicFeatures.Reliable\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0025\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-600 dark:text-gray-300\",\"children\":\"::App.FeaturesReliable.ReliableDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0030\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl bg-white p-8 shadow-lg transition-shadow hover:shadow-xl dark:bg-gray-900 dark:shadow-gray-950/40\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0027\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaCalendarAlt\",\"size\":48,\"className\":\"mb-6 text-blue-500\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0028\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-3 text-2xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"::App.VideoRoom.Planning\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0029\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-600 dark:text-gray-300\",\"children\":\"::App.FeaturesRapid.RapidDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0034\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl bg-white p-8 shadow-lg transition-shadow hover:shadow-xl dark:bg-gray-900 dark:shadow-gray-950/40\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0031\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaBookOpen\",\"size\":48,\"className\":\"mb-6 text-blue-500\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0032\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-3 text-2xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"::App.PublicFeatures.Expert\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0033\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-600 dark:text-gray-300\",\"children\":\"::App.FeaturesExpert.ExpertDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0038\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl bg-white p-8 shadow-lg transition-shadow hover:shadow-xl dark:bg-gray-900 dark:shadow-gray-950/40\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0035\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaCreditCard\",\"size\":48,\"className\":\"mb-6 text-blue-500\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0036\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-3 text-2xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"::App.PublicFeatures.Muhasebe\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0037\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-600 dark:text-gray-300\",\"children\":\"::App.FeaturesMuhasebe.MuhasebeDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0042\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl bg-white p-8 shadow-lg transition-shadow hover:shadow-xl dark:bg-gray-900 dark:shadow-gray-950/40\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0039\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaRegComment\",\"size\":48,\"className\":\"mb-6 text-blue-500\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0040\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-3 text-2xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"::App.PublicFeatures.Iletisim\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0041\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-600 dark:text-gray-300\",\"children\":\"::App.FeaturesIletisim.IletisimDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0046\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl bg-white p-8 shadow-lg transition-shadow hover:shadow-xl dark:bg-gray-900 dark:shadow-gray-950/40\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0043\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaPhone\",\"size\":48,\"className\":\"mb-6 text-blue-500\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0044\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-3 text-2xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"::App.PublicFeatures.Mobil\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0045\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-600 dark:text-gray-300\",\"children\":\"::App.FeaturesMobil.MobilDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0050\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl bg-white p-8 shadow-lg transition-shadow hover:shadow-xl dark:bg-gray-900 dark:shadow-gray-950/40\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0047\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaChartBar\",\"size\":48,\"className\":\"mb-6 text-blue-500\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0048\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-3 text-2xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"::App.PublicFeatures.Scalable\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0049\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-600 dark:text-gray-300\",\"children\":\"::App.FeaturesScalable.ScalableDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0054\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl bg-white p-8 shadow-lg transition-shadow hover:shadow-xl dark:bg-gray-900 dark:shadow-gray-950/40\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0051\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaShieldAlt\",\"size\":48,\"className\":\"mb-6 text-blue-500\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0052\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-3 text-2xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"::App.Listform.ListformField.Security\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0053\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-600 dark:text-gray-300\",\"children\":\"::App.FeaturesGuvenlik.GuvenlikDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]}]}]}]},{\"id\":\"cmp_seed_0079\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"bg-gray-50 py-20 dark:bg-gray-900\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0078\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"container mx-auto px-4\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0060\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"mb-16 text-center\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0058\",\"type\":\"h2\",\"kind\":\"html\",\"props\":{\"className\":\"text-4xl font-bold text-gray-900 dark:text-gray-100 mb-4\",\"children\":\"::App.PublicSolutions.SolutionsTitle\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0059\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto\",\"children\":\"::App.PublicSolutions.Subtitle\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0077\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-4\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0064\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"bg-blue-600 h-full rounded-2xl p-8\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0061\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaDesktop\",\"size\":64,\"className\":\"mb-6 text-white\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0062\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-4 text-2xl font-semibold text-white\",\"children\":\"::App.BlogCategories.Webdev\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0063\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-white/90\",\"children\":\"::App.SolutionsWeb.WebDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0068\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"bg-purple-600 h-full rounded-2xl p-8\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0065\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaMobileAlt\",\"size\":64,\"className\":\"mb-6 text-white\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0066\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-4 text-2xl font-semibold text-white\",\"children\":\"::App.ServicesMobile.MobileTitle\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0067\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-white/90\",\"children\":\"::App.SolutionsMobile.MobileDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0072\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"bg-green-600 h-full rounded-2xl p-8\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0069\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaServer\",\"size\":64,\"className\":\"mb-6 text-white\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0070\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-4 text-2xl font-semibold text-white\",\"children\":\"::App.SolutionsCustom.CustomTitle\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0071\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-white/90\",\"children\":\"::App.SolutionsCustom.CustomDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0076\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"bg-red-600 h-full rounded-2xl p-8\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0073\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaDatabase\",\"size\":64,\"className\":\"mb-6 text-white\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0074\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-4 text-2xl font-semibold text-white\",\"children\":\"::App.SolutionsDatabase.DatabaseTitle\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0075\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-white/90\",\"children\":\"::App.SolutionsDatabase.DatabaseDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]}]}]}]},{\"id\":\"cmp_seed_0084\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"bg-blue-900 py-16 dark:bg-gray-800\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0083\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"container mx-auto px-4 text-center\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0080\",\"type\":\"h2\",\"kind\":\"html\",\"props\":{\"className\":\"mb-4 text-3xl font-bold text-white\",\"children\":\"::App.PublicCommon.GetStarted\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0081\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"mb-8 text-lg text-white\",\"children\":\"::App.PublicCommon.Contact\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0082\",\"type\":\"a\",\"kind\":\"html\",\"props\":{\"href\":\"/contact\",\"className\":\"inline-block rounded-lg bg-white px-8 py-3 font-semibold text-blue-600 transition-colors hover:bg-blue-50\",\"children\":\"::App.PublicCommon.LearnMore\"},\"events\":{},\"bindings\":{},\"children\":[]}]}]}]}],\"canvas\":{\"width\":\"responsive\"},\"lifecycle\":{\"onMount\":\"\"},\"dataSources\":[],\"permissionCode\":\"\"}}", - "DataSources": [] - }, - { - "Id": "8f1c2d40-0000-4000-8000-000000000102", - "Name": "PublicAboutPage", - "RoutePath": "/about", - "Description": "About", - "IsActive": true, - "Dependencies": [], - "Code": "/*__SOZSOFT_VISUAL_DESIGNER__%7B%22version%22%3A1%2C%22sourceMode%22%3A%22visual%22%2C%22nodes%22%3A%5B%7B%22id%22%3A%22cmp_seed_0135%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22min-h-screen%20bg-gray-50%20dark%3Abg-gray-950%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0134%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22relative%20overflow-hidden%20bg-blue-900%20text-white%20dark%3Abg-gray-950%20py-12%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0131%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22absolute%20inset-0%20bg-gradient-to-br%20from-blue-900%20via-indigo-900%20to-purple-900%20dark%3Afrom-gray-950%20dark%3Avia-gray-950%20dark%3Ato-gray-950%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0132%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22absolute%20inset-0%20opacity-20%20dark%3Aopacity-35%22%2C%22style%22%3A%7B%22backgroundImage%22%3A%22url(%5C%22https%3A%2F%2Fimages.pexels.com%2Fphotos%2F3183183%2Fpexels-photo-3183183.jpeg%3Fauto%3Dcompress%26cs%3Dtinysrgb%26w%3D1920%5C%22)%22%2C%22backgroundSize%22%3A%22cover%22%2C%22backgroundPosition%22%3A%22center%22%7D%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0133%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22absolute%20inset-0%20bg-blue-950%2F25%20dark%3Abg-gray-950%2F45%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0092%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22container%20relative%20mx-auto%20px-4%20pt-20%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0090%22%2C%22type%22%3A%22h1%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-2%20mt-3%20text-5xl%20font-bold%20text-white%22%2C%22children%22%3A%22%3A%3AApp.About%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0091%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22max-w-3xl%20text-xl%22%2C%22children%22%3A%22%3A%3AApp.PublicAbout.Subtitle%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0115%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22bg-white%20py-10%20dark%3Abg-gray-900%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0114%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22container%20mx-auto%20px-4%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0113%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22grid%20grid-cols-1%20gap-8%20sm%3Agrid-cols-2%20lg%3Agrid-cols-5%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0096%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20px-4%20py-6%20text-center%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0093%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaUser%22%2C%22size%22%3A48%2C%22className%22%3A%22mx-auto%20mb-4%20text-blue-600%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0094%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-2%20text-4xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22740%2B%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0095%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.AboutStats.Users%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0100%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20px-4%20py-6%20text-center%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0097%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaUsers%22%2C%22size%22%3A48%2C%22className%22%3A%22mx-auto%20mb-4%20text-red-600%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0098%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-2%20text-4xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22310%2B%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0099%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.AboutStats.Clients%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0104%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20px-4%20py-6%20text-center%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0101%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaAward%22%2C%22size%22%3A48%2C%22className%22%3A%22mx-auto%20mb-4%20text-green-600%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0102%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-2%20text-4xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%2220%2B%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0103%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.AboutStats.Experience%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0108%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20px-4%20py-6%20text-center%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0105%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaClock%22%2C%22size%22%3A48%2C%22className%22%3A%22mx-auto%20mb-4%20text-purple-600%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0106%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-2%20text-4xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%227%2F24%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0107%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.AboutStats.Support%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0112%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20px-4%20py-6%20text-center%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0109%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaGlobe%22%2C%22size%22%3A48%2C%22className%22%3A%22mx-auto%20mb-4%20text-yellow-600%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0110%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-2%20text-4xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%223%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0111%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.Definitions.Country%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%5D%7D%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0130%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22py-6%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0129%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22container%20mx-auto%20px-4%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0121%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-6%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0120%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mx-auto%20border-l-4%20border-blue-600%20bg-white%20p-5%20text-lg%20leading-relaxed%20text-gray-800%20shadow-md%20dark%3Abg-gray-900%20dark%3Atext-gray-200%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0116%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-4%20last%3Amb-0%22%2C%22children%22%3A%22%3A%3AApp.AboutDescription.Part1%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0117%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-4%20last%3Amb-0%22%2C%22children%22%3A%22%3A%3AApp.AboutDescription.Motto%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0118%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-4%20last%3Amb-0%22%2C%22children%22%3A%22%3A%3AApp.AboutDescription.Part2%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0119%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-4%20last%3Amb-0%22%2C%22children%22%3A%22%3A%3AApp.AboutDescription.Closing%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0128%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22grid%20grid-cols-1%20gap-12%20lg%3Agrid-cols-2%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0124%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0122%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-4%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AApp.PublicAbout.Mission%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0123%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-700%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.AboutMission.MissionDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0127%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0125%22%2C%22type%22%3A%22h3%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-4%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AApp.PublicAbout.Vision%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0126%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-700%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.AboutVision.VisionDesc%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%5D%7D%5D%7D%5D%7D%5D%7D%5D%2C%22canvas%22%3A%7B%22width%22%3A%22responsive%22%7D%2C%22lifecycle%22%3A%7B%22onMount%22%3A%22%22%7D%2C%22dataSources%22%3A%5B%5D%2C%22permissionCode%22%3A%22%22%7D__*/\nconst PublicAboutPage = () => {\n\n\n return (\n <>\n
\n
\n
\n
\n
\n
\n

\n {translate(\"::App.About\")}\n

\n

\n {translate(\"::App.PublicAbout.Subtitle\")}\n

\n
\n
\n
\n
\n
\n
\n \n
\n {\"740+\"}\n
\n
\n {translate(\"::App.AboutStats.Users\")}\n
\n
\n
\n \n
\n {\"310+\"}\n
\n
\n {translate(\"::App.AboutStats.Clients\")}\n
\n
\n
\n \n
\n {\"20+\"}\n
\n
\n {translate(\"::App.AboutStats.Experience\")}\n
\n
\n
\n \n
\n {\"7/24\"}\n
\n
\n {translate(\"::App.AboutStats.Support\")}\n
\n
\n
\n \n
\n {\"3\"}\n
\n
\n {translate(\"::App.Definitions.Country\")}\n
\n
\n
\n
\n
\n
\n
\n
\n
\n

\n {translate(\"::App.AboutDescription.Part1\")}\n

\n

\n {translate(\"::App.AboutDescription.Motto\")}\n

\n

\n {translate(\"::App.AboutDescription.Part2\")}\n

\n

\n {translate(\"::App.AboutDescription.Closing\")}\n

\n
\n
\n
\n
\n

\n {translate(\"::App.PublicAbout.Mission\")}\n

\n

\n {translate(\"::App.AboutMission.MissionDesc\")}\n

\n
\n
\n

\n {translate(\"::App.PublicAbout.Vision\")}\n

\n

\n {translate(\"::App.AboutVision.VisionDesc\")}\n

\n
\n
\n
\n
\n
\n \n )\n}\n\nexport default PublicAboutPage\n", - "Props": "{\"visualDesigner\":{\"version\":1,\"sourceMode\":\"visual\",\"nodes\":[{\"id\":\"cmp_seed_0135\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"min-h-screen bg-gray-50 dark:bg-gray-950\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0134\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"relative overflow-hidden bg-blue-900 text-white dark:bg-gray-950 py-12\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0131\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"absolute inset-0 bg-gradient-to-br from-blue-900 via-indigo-900 to-purple-900 dark:from-gray-950 dark:via-gray-950 dark:to-gray-950\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0132\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"absolute inset-0 opacity-20 dark:opacity-35\",\"style\":{\"backgroundImage\":\"url(\\\"https://images.pexels.com/photos/3183183/pexels-photo-3183183.jpeg?auto=compress&cs=tinysrgb&w=1920\\\")\",\"backgroundSize\":\"cover\",\"backgroundPosition\":\"center\"}},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0133\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"absolute inset-0 bg-blue-950/25 dark:bg-gray-950/45\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0092\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"container relative mx-auto px-4 pt-20\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0090\",\"type\":\"h1\",\"kind\":\"html\",\"props\":{\"className\":\"mb-2 mt-3 text-5xl font-bold text-white\",\"children\":\"::App.About\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0091\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"max-w-3xl text-xl\",\"children\":\"::App.PublicAbout.Subtitle\"},\"events\":{},\"bindings\":{},\"children\":[]}]}]},{\"id\":\"cmp_seed_0115\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"bg-white py-10 dark:bg-gray-900\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0114\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"container mx-auto px-4\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0113\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-5\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0096\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl px-4 py-6 text-center\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0093\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaUser\",\"size\":48,\"className\":\"mx-auto mb-4 text-blue-600\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0094\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"mb-2 text-4xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"740+\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0095\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-600 dark:text-gray-300\",\"children\":\"::App.AboutStats.Users\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0100\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl px-4 py-6 text-center\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0097\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaUsers\",\"size\":48,\"className\":\"mx-auto mb-4 text-red-600\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0098\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"mb-2 text-4xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"310+\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0099\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-600 dark:text-gray-300\",\"children\":\"::App.AboutStats.Clients\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0104\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl px-4 py-6 text-center\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0101\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaAward\",\"size\":48,\"className\":\"mx-auto mb-4 text-green-600\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0102\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"mb-2 text-4xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"20+\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0103\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-600 dark:text-gray-300\",\"children\":\"::App.AboutStats.Experience\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0108\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl px-4 py-6 text-center\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0105\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaClock\",\"size\":48,\"className\":\"mx-auto mb-4 text-purple-600\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0106\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"mb-2 text-4xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"7/24\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0107\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-600 dark:text-gray-300\",\"children\":\"::App.AboutStats.Support\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0112\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl px-4 py-6 text-center\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0109\",\"type\":\"PlatformIcon\",\"kind\":\"custom\",\"props\":{\"name\":\"FaGlobe\",\"size\":48,\"className\":\"mx-auto mb-4 text-yellow-600\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0110\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"mb-2 text-4xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"3\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0111\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-600 dark:text-gray-300\",\"children\":\"::App.Definitions.Country\"},\"events\":{},\"bindings\":{},\"children\":[]}]}]}]}]},{\"id\":\"cmp_seed_0130\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"py-6\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0129\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"container mx-auto px-4\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0121\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"mb-6\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0120\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"mx-auto border-l-4 border-blue-600 bg-white p-5 text-lg leading-relaxed text-gray-800 shadow-md dark:bg-gray-900 dark:text-gray-200\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0116\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"mb-4 last:mb-0\",\"children\":\"::App.AboutDescription.Part1\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0117\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"mb-4 last:mb-0\",\"children\":\"::App.AboutDescription.Motto\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0118\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"mb-4 last:mb-0\",\"children\":\"::App.AboutDescription.Part2\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0119\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"mb-4 last:mb-0\",\"children\":\"::App.AboutDescription.Closing\"},\"events\":{},\"bindings\":{},\"children\":[]}]}]},{\"id\":\"cmp_seed_0128\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"grid grid-cols-1 gap-12 lg:grid-cols-2\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0124\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl bg-white p-8 shadow-lg dark:bg-gray-900 dark:shadow-gray-950/40\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0122\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-4 text-2xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"::App.PublicAbout.Mission\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0123\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-700 dark:text-gray-300\",\"children\":\"::App.AboutMission.MissionDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]},{\"id\":\"cmp_seed_0127\",\"type\":\"div\",\"kind\":\"html\",\"props\":{\"className\":\"rounded-xl bg-white p-8 shadow-lg dark:bg-gray-900 dark:shadow-gray-950/40\"},\"events\":{},\"bindings\":{},\"children\":[{\"id\":\"cmp_seed_0125\",\"type\":\"h3\",\"kind\":\"html\",\"props\":{\"className\":\"mb-4 text-2xl font-bold text-gray-900 dark:text-gray-100\",\"children\":\"::App.PublicAbout.Vision\"},\"events\":{},\"bindings\":{},\"children\":[]},{\"id\":\"cmp_seed_0126\",\"type\":\"p\",\"kind\":\"html\",\"props\":{\"className\":\"text-gray-700 dark:text-gray-300\",\"children\":\"::App.AboutVision.VisionDesc\"},\"events\":{},\"bindings\":{},\"children\":[]}]}]}]}]}]}],\"canvas\":{\"width\":\"responsive\"},\"lifecycle\":{\"onMount\":\"\"},\"dataSources\":[],\"permissionCode\":\"\"}}", - "DataSources": [] - }, - { - "Id": "8f1c2d40-0000-4000-8000-000000000103", - "Name": "PublicContactPage", - "RoutePath": "/contact", - "Description": "Contact", - "IsActive": true, - "Dependencies": [], - "Code": "/*__SOZSOFT_VISUAL_DESIGNER__%7B%22version%22%3A1%2C%22sourceMode%22%3A%22visual%22%2C%22nodes%22%3A%5B%7B%22id%22%3A%22cmp_seed_0187%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22min-h-screen%20bg-gray-50%20dark%3Abg-gray-950%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0186%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22relative%20overflow-hidden%20bg-blue-900%20text-white%20dark%3Abg-gray-950%20py-12%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0183%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22absolute%20inset-0%20bg-gradient-to-br%20from-blue-900%20via-indigo-900%20to-purple-900%20dark%3Afrom-gray-950%20dark%3Avia-gray-950%20dark%3Ato-gray-950%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0184%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22absolute%20inset-0%20opacity-20%20dark%3Aopacity-35%22%2C%22style%22%3A%7B%22backgroundImage%22%3A%22url(%5C%22https%3A%2F%2Fimages.pexels.com%2Fphotos%2F3183171%2Fpexels-photo-3183171.jpeg%3Fauto%3Dcompress%26cs%3Dtinysrgb%26w%3D1920%5C%22)%22%2C%22backgroundSize%22%3A%22cover%22%2C%22backgroundPosition%22%3A%22center%22%7D%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0185%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22absolute%20inset-0%20bg-blue-950%2F25%20dark%3Abg-gray-950%2F45%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0138%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22container%20relative%20mx-auto%20px-4%20pt-20%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0136%22%2C%22type%22%3A%22h1%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-2%20mt-3%20text-5xl%20font-bold%20text-white%22%2C%22children%22%3A%22%3A%3AApp.Contact%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0137%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22max-w-3xl%20text-xl%22%2C%22children%22%3A%22%3A%3AApp.PublicContact.Subtitle%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0182%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22py-6%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0181%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22container%20mx-auto%20px-4%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0180%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22grid%20grid-cols-1%20gap-12%20lg%3Agrid-cols-2%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0179%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22space-y-4%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0156%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0139%22%2C%22type%22%3A%22h2%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-6%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AAbp.Identity.User.UserInformation.ContactInformation%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0155%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22space-y-4%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0142%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22flex%20items-start%20space-x-2%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0140%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaMapMarkerAlt%22%2C%22size%22%3A20%2C%22className%22%3A%22mt-1%20flex-shrink-0%20text-blue-600%20dark%3Atext-blue-400%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0141%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.ContactAddress.Full%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0145%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22flex%20items-start%20space-x-2%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0143%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaPhone%22%2C%22size%22%3A20%2C%22className%22%3A%22mt-1%20flex-shrink-0%20text-blue-600%20dark%3Atext-blue-400%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0144%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%2B90%20(544)%20769%207%20638%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0148%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22flex%20items-start%20space-x-2%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0146%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaEnvelope%22%2C%22size%22%3A20%2C%22className%22%3A%22mt-1%20flex-shrink-0%20text-blue-600%20dark%3Atext-blue-400%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0147%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22destek%40sozsoft.com%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0151%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22flex%20items-start%20space-x-2%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0149%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaBuilding%22%2C%22size%22%3A20%2C%22className%22%3A%22mt-1%20flex-shrink-0%20text-blue-600%20dark%3Atext-blue-400%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0150%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22Sar%C4%B1gazi%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0154%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22flex%20items-start%20space-x-2%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0152%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaIdCard%22%2C%22size%22%3A20%2C%22className%22%3A%22mt-1%20flex-shrink-0%20text-blue-600%20dark%3Atext-blue-400%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0153%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%2232374982750%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0163%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0157%22%2C%22type%22%3A%22h2%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-6%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AApp.PublicContact.TransferForm%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0158%22%2C%22type%22%3A%22img%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22src%22%3A%22%2Fimg%2Fenpara.svg%22%2C%22alt%22%3A%22Enpara%22%2C%22className%22%3A%22mb-2%20w-24%20object-contain%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0159%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-1%20ml-1%20text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%C3%96zlem%20%C3%96zt%C3%BCrk%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0160%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-1%20ml-1%20text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%2203663%20%2F%20Enpara%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0161%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-1%20ml-1%20text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%2273941177%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0162%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-1%20ml-1%20text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22TR11%200015%207000%200000%200073%209411%2077%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0175%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0164%22%2C%22type%22%3A%22h2%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-6%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AApp.PublicContact.WorkHours%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0174%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22space-y-2%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0167%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22flex%20items-center%20space-x-2%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0165%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaCalendarAlt%22%2C%22size%22%3A20%2C%22className%22%3A%22flex-shrink-0%20text-blue-500%20dark%3Atext-blue-400%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0166%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.ContactWorkHours.Weekday%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0170%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22flex%20items-center%20space-x-2%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0168%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaCalendarCheck%22%2C%22size%22%3A20%2C%22className%22%3A%22flex-shrink-0%20text-blue-500%20dark%3Atext-blue-400%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0169%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.ContactWorkHours.Weekend%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0173%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22flex%20items-center%20space-x-2%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0171%22%2C%22type%22%3A%22PlatformIcon%22%2C%22kind%22%3A%22custom%22%2C%22props%22%3A%7B%22name%22%3A%22FaRegComment%22%2C%22size%22%3A20%2C%22className%22%3A%22flex-shrink-0%20text-green-500%20dark%3Atext-green-400%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0172%22%2C%22type%22%3A%22p%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22text-gray-600%20dark%3Atext-gray-300%22%2C%22children%22%3A%22%3A%3AApp.ContactWorkHours.Whatsapp%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%5D%7D%5D%7D%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0178%22%2C%22type%22%3A%22div%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22rounded-xl%20bg-white%20p-8%20shadow-lg%20dark%3Abg-gray-900%20dark%3Ashadow-gray-950%2F40%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%7B%22id%22%3A%22cmp_seed_0176%22%2C%22type%22%3A%22h2%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22className%22%3A%22mb-2%20text-center%20text-2xl%20font-bold%20text-gray-900%20dark%3Atext-gray-100%22%2C%22children%22%3A%22%3A%3AApp.PublicContact.Location%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%2C%7B%22id%22%3A%22cmp_seed_0177%22%2C%22type%22%3A%22iframe%22%2C%22kind%22%3A%22html%22%2C%22props%22%3A%7B%22src%22%3A%22https%3A%2F%2Fwww.google.com%2Fmaps%2Fembed%3Fpb%3D!1m18!1m12!1m3!1d3006.209566407676!2d28.757000999999992!3d41.10811400000001!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x0%253A0x8a0bbbdfcfd3fd24!2zU8O2enNvZnQ!5e0!3m2!1str!2str!4v1450816303558%22%2C%22title%22%3A%22map%22%2C%22className%22%3A%22h-%5B600px%5D%20w-full%20overflow-hidden%20rounded-xl%20border-0%22%2C%22loading%22%3A%22lazy%22%7D%2C%22events%22%3A%7B%7D%2C%22bindings%22%3A%7B%7D%2C%22children%22%3A%5B%5D%7D%5D%7D%5D%7D%5D%7D%5D%7D%5D%7D%5D%2C%22canvas%22%3A%7B%22width%22%3A%22responsive%22%7D%2C%22lifecycle%22%3A%7B%22onMount%22%3A%22%22%7D%2C%22dataSources%22%3A%5B%5D%2C%22permissionCode%22%3A%22%22%7D__*/\nconst PublicContactPage = () => {\n\n\n return (\n <>\n
\n
\n
\n
\n
\n
\n

\n {translate(\"::App.Contact\")}\n

\n

\n {translate(\"::App.PublicContact.Subtitle\")}\n

\n
\n
\n
\n
\n
\n
\n
\n

\n {translate(\"::Abp.Identity.User.UserInformation.ContactInformation\")}\n

\n
\n
\n \n

\n {translate(\"::App.ContactAddress.Full\")}\n

\n
\n
\n \n

\n {\"+90 (544) 769 7 638\"}\n

\n
\n
\n \n

\n {\"destek@sozsoft.com\"}\n

\n
\n
\n \n

\n {\"Sarıgazi\"}\n

\n
\n
\n \n

\n {\"32374982750\"}\n

\n
\n
\n
\n
\n

\n {translate(\"::App.PublicContact.TransferForm\")}\n

\n {\"Enpara\"}\n

\n {\"Özlem Öztürk\"}\n

\n

\n {\"03663 / Enpara\"}\n

\n

\n {\"73941177\"}\n

\n

\n {\"TR11 0015 7000 0000 0073 9411 77\"}\n

\n
\n
\n

\n {translate(\"::App.PublicContact.WorkHours\")}\n

\n
\n
\n \n

\n {translate(\"::App.ContactWorkHours.Weekday\")}\n

\n
\n
\n \n

\n {translate(\"::App.ContactWorkHours.Weekend\")}\n

\n
\n
\n \n

\n {translate(\"::App.ContactWorkHours.Whatsapp\")}\n

\n
\n
\n
\n
\n
\n

\n {translate(\"::App.PublicContact.Location\")}\n

\n