From 6742393271b62e03d9750e7ecf9c0b30e7775043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Tue, 8 Sep 2026 18:00:23 +0300 Subject: [PATCH] =?UTF-8?q?Public=20sayfalar=20Custom=20componente=20ta?= =?UTF-8?q?=C5=9F=C4=B1nd=C4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 119 ++- .../DeveloperKit/CustomComponentDto.cs | 12 +- .../Public/AboutDto.cs | 52 - .../Public/ContactDto.cs | 54 -- .../Public/HomeDto.cs | 96 -- .../Public/SavePublicPageContentInput.cs | 217 ----- .../Public/ServiceDto.cs | 15 - .../DeveloperKit/CustomComponentAppService.cs | 14 + .../Menu/MenuAppService.cs | 5 +- .../Public/PublicAppService.cs | 376 +------- .../Public/PublicMappers.cs | 64 -- .../Migrations/LanguagesData.json | 156 ++- .../Migrations/ListFormSeeder_Saas.cs | 13 +- .../Migrations/MenuDataSeeder.cs | 2 + .../Migrations/MenusData.json | 190 ++-- .../Migrations/PermissionsData.json | 72 -- .../Enums/TableNameEnum.cs | 4 - .../PlatformConsts.cs | 34 +- .../TableNameResolver.cs | 4 - .../Data/IAfterAllMigrationsSqlExecutor.cs | 2 +- .../Data/SeedConsts.cs | 17 - .../Data/SeedPathResolver.cs | 4 +- .../Host/DeveloperKit/CustomComponent.cs | 5 +- .../Tenant/Administration/Website/About.cs | 11 - .../Tenant/Administration/Website/Contact.cs | 18 - .../Tenant/Administration/Website/Home.cs | 21 - .../Tenant/Administration/Website/Service.cs | 17 - .../EntityFrameworkCore/PlatformDbContext.cs | 83 +- ....cs => 20260908125150_Initial.Designer.cs} | 275 +----- ...5_Initial.cs => 20260908125150_Initial.cs} | 124 +-- .../PlatformDbContextModelSnapshot.cs | 273 +----- .../Seeds/TenantData.Dev.json | 2 - .../Seeds/TenantData.Production.json | 2 - .../Seeds/TenantData.json | 360 ------- .../Seeds/TenantDataSeeder.cs | 238 ----- ...ozsoft.Platform.EntityFrameworkCore.csproj | 16 - ...formApplicationConfigurationContributor.cs | 22 + claude.md | 21 + .../App.DeveloperKit.CustomComponents.json | 59 +- .../seeds/host/data/App.Orders.Products.json | 22 - ui/safelist.txt | 291 +++++- .../codeLayout/data/componentDefinitions.ts | 46 + .../componentEditor/PlatformIcon.tsx | 6 +- .../componentEditor/runtimeNodes.ts | 27 + ui/src/components/layouts/PublicLayout.tsx | 625 +----------- ui/src/components/orders/BillingControls.tsx | 4 +- ui/src/components/publicSite/index.tsx | 343 +++++++ .../components/visualDesigner/CanvasFrame.tsx | 83 ++ .../visualDesigner/VisualCanvas.tsx | 277 +++--- ui/src/components/visualDesigner/catalog.ts | 135 ++- ui/src/constants/permission.constant.ts | 12 - ui/src/contexts/ComponentContext.tsx | 12 +- ui/src/contexts/DemoContext.tsx | 14 +- ui/src/contexts/ScrollContext.tsx | 6 - ui/src/proxy/about/models.ts | 22 - ui/src/proxy/contact/models.ts | 39 - ui/src/routes/dynamicRouteLoader.tsx | 43 +- ui/src/routes/dynamicRouter.tsx | 16 +- ui/src/routes/route.constant.ts | 7 - ui/src/services/about.ts | 60 -- ui/src/services/contact.service.ts | 73 -- ui/src/services/country.service.ts | 30 + ui/src/services/home.service.ts | 179 ---- ui/src/services/service.service.ts | 61 -- ui/src/store/abpConfig.model.ts | 10 +- ui/src/utils/hooks/usePublicMenu.ts | 65 ++ ui/src/utils/hooks/useScrolled.ts | 20 + ui/src/views/admin/listForm/edit/options.ts | 43 - ui/src/views/admin/listForm/edit/types.ts | 2 - .../admin/profile/components/General.tsx | 2 +- .../views/admin/user-management/Details.tsx | 2 +- .../developerKit/VisualComponentDesigner.tsx | 152 ++- ui/src/views/public/About.tsx | 691 ------------- ui/src/views/public/Blog.tsx | 2 +- ui/src/views/public/BlogDetail.tsx | 2 +- ui/src/views/public/Contact.tsx | 608 ------------ ui/src/views/public/Demo.tsx | 2 +- ui/src/views/public/Home.tsx | 904 ------------------ ui/src/views/public/Services.tsx | 740 -------------- .../views/public/designer/DesignerDrawer.tsx | 410 -------- .../views/public/designer/DesignerLayer.tsx | 100 -- .../views/public/designer/SelectableBlock.tsx | 77 -- ui/src/views/public/designer/fields.ts | 45 - ui/src/views/public/designer/index.ts | 10 - ui/src/views/public/designer/localization.ts | 50 - ui/src/views/public/designer/paths.ts | 61 -- ui/src/views/public/designer/types.ts | 40 - .../public/designer/useDesignerLanguages.ts | 65 -- .../views/public/designer/useDesignerState.ts | 272 ------ .../views/public/designer/usePageDesigner.tsx | 188 ---- ui/src/views/public/localization.ts | 13 + ui/src/views/public/pageStyles.ts | 20 - ui/tailwind.config.ts | 13 +- ui/twSafelistGenerator/index.js | 111 ++- 94 files changed, 1959 insertions(+), 8263 deletions(-) delete mode 100644 api/src/Sozsoft.Platform.Application.Contracts/Public/AboutDto.cs delete mode 100644 api/src/Sozsoft.Platform.Application.Contracts/Public/ContactDto.cs delete mode 100644 api/src/Sozsoft.Platform.Application.Contracts/Public/HomeDto.cs delete mode 100644 api/src/Sozsoft.Platform.Application.Contracts/Public/SavePublicPageContentInput.cs delete mode 100644 api/src/Sozsoft.Platform.Application.Contracts/Public/ServiceDto.cs delete mode 100644 api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/About.cs delete mode 100644 api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/Contact.cs delete mode 100644 api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/Home.cs delete mode 100644 api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/Service.cs rename api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/{20260904084415_Initial.Designer.cs => 20260908125150_Initial.Designer.cs} (97%) rename api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/{20260904084415_Initial.cs => 20260908125150_Initial.cs} (97%) delete mode 100644 api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.Dev.json delete mode 100644 api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.Production.json delete mode 100644 api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.json delete mode 100644 api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantDataSeeder.cs create mode 100644 ui/src/components/componentEditor/runtimeNodes.ts create mode 100644 ui/src/components/publicSite/index.tsx create mode 100644 ui/src/components/visualDesigner/CanvasFrame.tsx delete mode 100644 ui/src/contexts/ScrollContext.tsx delete mode 100644 ui/src/proxy/about/models.ts delete mode 100644 ui/src/proxy/contact/models.ts delete mode 100644 ui/src/services/about.ts delete mode 100644 ui/src/services/contact.service.ts create mode 100644 ui/src/services/country.service.ts delete mode 100644 ui/src/services/home.service.ts delete mode 100644 ui/src/services/service.service.ts create mode 100644 ui/src/utils/hooks/usePublicMenu.ts create mode 100644 ui/src/utils/hooks/useScrolled.ts delete mode 100644 ui/src/views/public/About.tsx delete mode 100644 ui/src/views/public/Contact.tsx delete mode 100644 ui/src/views/public/Home.tsx delete mode 100644 ui/src/views/public/Services.tsx delete mode 100644 ui/src/views/public/designer/DesignerDrawer.tsx delete mode 100644 ui/src/views/public/designer/DesignerLayer.tsx delete mode 100644 ui/src/views/public/designer/SelectableBlock.tsx delete mode 100644 ui/src/views/public/designer/fields.ts delete mode 100644 ui/src/views/public/designer/index.ts delete mode 100644 ui/src/views/public/designer/localization.ts delete mode 100644 ui/src/views/public/designer/paths.ts delete mode 100644 ui/src/views/public/designer/types.ts delete mode 100644 ui/src/views/public/designer/useDesignerLanguages.ts delete mode 100644 ui/src/views/public/designer/useDesignerState.ts delete mode 100644 ui/src/views/public/designer/usePageDesigner.tsx create mode 100644 ui/src/views/public/localization.ts delete mode 100644 ui/src/views/public/pageStyles.ts diff --git a/README.md b/README.md index 0b244848..fc975fda 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ React/C# kodu yazmadan, yalnızca konfigürasyon ile mümkündür. | **Arka plan işleri** | Hangfire + ABP Background Worker; zamanlanmış SQL, mail kuyruğu, yedekleme, oturum temizliği. | | **Gerçek zamanlı** | SignalR tabanlı messenger ve video oda (WebRTC/TURN) altyapısı. | | **Intranet & Forum** | Duyuru, anket, sosyal duvar, etkinlik; kategori/konu/gönderi tabanlı forum. | -| **Public web sitesi** | Ana sayfa, hakkımızda, hizmetler, ürün, ödeme, blog, demo ve iletişim içerikleri (tasarımcı ekranlarıyla düzenlenebilir). | +| **Public web sitesi** | Menüsü ve sayfaları veritabanından yönetilen ziyaretçi sitesi: sayfalar Custom Component, menü `Pub` modülü (bkz. §10.4). Ürün/ödeme ve demo akışları fiziksel sayfalardır. | | **PWA** | Service worker ile offline kabuk, kontrollü sürüm güncellemesi ve changelog bildirimi. | --- @@ -845,6 +845,21 @@ parametresi (`?RoleId=…`), diğerleri son ek taşır (`?Name.contains=…`). O (`designerScriptDialect.ts` + `designerScriptRecipes.ts`); tarif grupları bileşen erişimi, API çağrıları, form ve olay/sayfa başlıkları altında toplanır. +**Kanvas doğruluğu.** Tasarım zamanında görülen, canlıda görülendir; bunu iki mekanizma sağlar. +(1) *Yerleşim sınıflarının taşınması*: kanvas her düğümü seçim/sürükleme için bir sarmalayıcıya +alır ve üst öğe grid/flex olduğunda asıl öğe o sarmalayıcı olur. `splitLayoutClasses` düğümün +yerleşim sınıflarını sarmalayıcıya geçirir — `col-span`/`row-span`/`order`/`self`/`flex-1`/`basis`, +sabit ve kesirli `w-*`, kenar boşlukları taşınır; konum sınıfları (`absolute`, `inset-*`, `z-*`, +`w-full`) her ikisinde de kalır. Görünürlük sınıfları da sarmalayıcıya kopyalanır (`hidden lg:flex` +→ sarmalayıcıda `hidden lg:block`): aksi hâlde mobilde gizlenmesi gereken masaüstü şeridi yine bir +grid hücresi işgal eder ve komşularını yanlış hücreye iter. `fixed` kanvasta akışa çevrilir, yoksa +düğüm tasarım yüzeyinden taşardı. (2) *Cihaz çerçevesi*: araç çubuğundan masaüstü/tablet/mobil seçildiğinde kanvas kendi +belgesinde çizilir (`CanvasFrame`), böylece `md:`/`lg:` kuralları tarayıcı penceresine değil seçilen +genişliğe göre çözülür. Cihaz modunda tasarım yüzeyi kenar boşluğunu bırakır: kanvas o cihazın ekranıdır. `Responsive` +seçimi aynı sayfada, çerçevesiz ve boşluklu çizer. (3) *Gezinme kapalıdır*: kanvastaki bir bağlantıya +tıklamak seçim yapar, sayfayı değiştirmez — tıklama yakalama evresinde durdurulur, `react-router`'ın +`Link`'i de `defaultPrevented` görünce gezinmez. + **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 @@ -875,6 +890,9 @@ Route.Authority ──┘ └► Permission - Frontend'de `dynamicRoutesContext` route'ları çeker, `dynamicRouteLoader` bunları fiziksel view'lara veya runtime derlenmiş Custom Component'lere eşler, `DynamicRouter` React Router ağacını kurar; her korumalı route `ProtectedRoute` + `PermissionGuard` ile sarılır. +- Aynı yolu bir fiziksel route ile bir Custom Component paylaşıyorsa **Custom Component kazanır**; + karşılığı olan dosyası bulunmayan rota kaydı hiç yüklenmez. Public site menüsü bu modelin + dışındadır: `Pub` modülü altında tutulur ve uygulama konfigürasyonuyla taşınır (bkz. §10.4). **Yetki kodları** `PlatformConsts.AppCodes` içinde merkezî olarak tanımlıdır ve `PermissionsData.json` ile seed edilir. Ana gruplar: `App.Saas`, `App.Branches`, `App.Intranet.*`, @@ -895,7 +913,6 @@ içindedir; oradaki kontroller yalnızca butonları gizler, asıl kontrol AppSer | `App.DeveloperKit.DynamicServices` | `.Create`, `.Edit`, `.Delete`, `.Manage`, `.TestCompile`, `.Publish`, `.ViewCode` | | `App.SqlQueryManager` | `.CrudEndpoints` | | `App.Setup.Migrate` | Migration + seed tetikleme (host tarafı) | -| `App.{Home,About,Services,Contact}.Design` | Public site sayfa tasarım modu (`?design=1`) | > **Kural:** Yetki sözleşmesi olmayan menü/route önerilmez ve eklenmez. @@ -943,9 +960,101 @@ içindedir; oradaki kontroller yalnızca butonları gizler, asıl kontrol AppSer ### 10.4. Public site -`/home` · `/about` · `/services` · `/products` · `/checkout` · `/payment` · `/success` · -`/blog` · `/blog/:id` · `/demo` · `/contact` — içerikler veritabanından yönetilir; -`/about/designer` ve `/services/designer` sayfa tasarımcılarıdır. +`/home` · `/about` · `/contact` (Custom Component) · `/products` · `/checkout` · `/payment` · +`/success` · `/blog` · `/blog/:id` · `/demo` (fiziksel sayfalar). + +Ziyaretçiye açık kısım iki parçadan oluşur ve ikisi de veritabanında yaşar: + +**Menü.** Public menüler `Menu` tablosunda `ModuleId = "Pub"` modülü altında tutulur ve Menü +Yönetimi ekranından girilir (sözleşme: `PlatformConsts.PublicSite`). `App.Public.Header` kökü hem +üst navigasyonu hem de alt bilgideki hızlı bağlantıları besler — ikisi de `PublicNav` düğümüdür, +menü bir kez eklenir iki yerde birden görünür; alt kaydı olan bir menü üstte açılır liste olur. +Alt bilgide `cta` kayıtları (Giriş) yer almaz: oradaki düğüm `source="navigation"` ile çizilir. `Url` sayfa yolu, `Icon` +react-icons adı, `Order` sıra, `IsDisabled` yayın anahtarı, `DisplayName` dil anahtarıdır; +`CssClass` içinde `cta` geçen kayıt düz bağlantı yerine vurgulu buton olarak çizilir. Menü ucu +`[Authorize]` olduğu için menüler uygulama konfigürasyonunun `publicMenus` alanıyla taşınır +(`PlatformApplicationConfigurationContributor`), istemcide `usePublicMenu` okur. `Pub` modülü +yönetim navigasyonuna girmez (`abpConfig.model.ts` menüyü kurarken bu modülü eler; Menü Yönetimi +ekranı elemez, menüler oradan düzenlenir). Seed: `MenusData.json` → `Modules` + `Menus`. + +**Sayfa.** Her public sayfa bir Custom Component'tir: `RoutePath` `/admin/` ile başlamadığı sürece +bileşen otomatik olarak public route olur ve `PublicLayout` altında yayınlanır. Tasarım yalnızca +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`; +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, +kullanılabilenler `React`, `ui` kit, `PlatformViewHost`, `apiService`, `axios`, `DOMPurify`, +`translate`, `checkPermission`, `getCulture` ve **katalog düğümleri** +(`components/componentEditor/runtimeNodes.ts`). **`react-icons` ve `react-router` yoktur** — ikon +`` ile ada göre çizilir (menü ikonlarıyla +aynı sözlük), gezinme `` ile yazılır. Kullanıcıya görünen metinler dil anahtarı üzerinden +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 +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 +olan `ui/safelist.txt` dosyasına da yazılır. Dosyayı zaten `twSafelistGenerator` eklentisi üretiyor +(renk/ölçü listesi); eklenti artık seed dosyalarındaki `className` değerlerini de toplayıp +`# --- custom component classes ---` işaretinin altına ekliyor. `safelist.txt` `ui/` içinde ve +depoda olduğu için her ortamda taranır. Seed kökü görünmüyorsa işaretin altındaki liste olduğu gibi +korunur — eksik bir bağlam listeyi silip sorunu büyütmez. Ayrı bir betik ya da ikinci bir safelist +dosyası yoktur; liste her `npm run build` / `npm start` ile tazelenir. + +**Katalog düğümleri.** Kanvasın doğrudan çizdiği, derlenen bileşen listesinde yer almayan +bileşenler tek bir sözlükte durur (`RUNTIME_NODE_COMPONENTS`): kanvas onu `VisualCanvas` içinde, +üretilen kod ise runtime kapsamı üzerinden kullanır — iki taraf aynı kaynaktan beslendiği için +tasarım ve çalışma zamanı ayrışamaz. Sözlükte `PlatformIcon` ve public site parçaları +(`components/publicSite`) bulunur. Yeni bir düğüm eklerken: bileşen `components/publicSite` (ya da +uygun klasör) → `runtimeNodes.ts` sözlüğü → `catalog.ts` tanımı (`kind: 'custom'`) → `LanguagesData.json` +açıklama anahtarı. + +Elle yazılmış bir bileşen Visual Designer'da **kod modunda** açılır: tasarımcı yalnızca kendi +ürettiği kodu düğüm ağacına çevirir (`isDesignerGeneratedCode`), aksi hâlde tanımadığı JSX'i +düşürüp kaydederken kodu yok ederdi. + +Public bileşenlerin tamamı — `/home`, `/about`, `/contact` sayfaları ile üst ve alt bilgi +(`PublicHeader`, `PublicFooter`; rotaları yok) — **tasarımcı belgesi** olarak tutulur: `Props` alanındaki +`visualDesigner` düğüm ağacı kaynaktır (`sourceMode: "visual"`), `Code` ondan üretilir +(`generateDesignerCode`) ve runtime da bileşeni bu belgeden derler. Yani hepsi kanvasta görünür, +sürükle-bırak ve özellik paneliyle düzenlenir. Belgeler `div`/`h1`-`h5`/`p`/`span`/`img`/`a` +HTML düğümleri ve `PlatformIcon` ikon düğümüyle kurulmuştur; `::` ile başlayan metinler üretilen +kodda `translate()` çağrısına döner. `RoutePath` boş bırakılabilir; rotası olmayan bir bileşen +yalnızca başka bir ekranın içinde çizilir (alt bilgi gibi). + +Üst ve alt bilgi de birer Custom Component'tir (`PublicHeader`, `PublicFooter`; rota yok): +`PublicLayout` ikisini de `renderComponent` ile çizer ve geriye yalnızca rota değişiminde başa +sarma ile demo penceresi kalır. Bileşen kayıtlı değilse o parça çizilmez. + +Üst bilginin davranışı (menü listesi, aktif bağlantı, kaydırmada değişen görünüm, mobil panel, +tema/dil/demo kontrolleri) `components/publicSite` altındaki katalog düğümlerinde yaşar; yerleşim +ve sınıflar tasarımcıdadır: `PublicHeaderBar` (kaydırıldığında `scrolledClassName`'e geçen kabuk), +`PublicNav` (`source`: `navigation`/`actions`/`all`, `variant`: `horizontal`/`vertical`), +`PublicMobileMenu` (hamburger + içeriği tasarlanan panel), `PublicLogo`, `PublicThemeToggle`, +`PublicLanguageSelector`, `PublicDemoButton`. + +Rotası olmayan bileşende `RoutePath` **NULL** yazılır; `Sas_H_CustomComponent`'in `RoutePath` +tekil indeksi yalnızca dolu değerleri kapsar (`[IsDeleted] = 0 AND [RoutePath] IS NOT NULL`), aksi +hâlde ikinci rotasız bileşen birincisiyle çakışırdı. + +`/home`, `/about`, `/contact` bu yolla çalışır (`PublicHomePage`, `PublicAboutPage`, +`PublicContactPage`); üst ve alt bilgi rotasızdır (`PublicHeader`, `PublicFooter`). + +Veriden beslenen public sayfalar (`/products`, `/checkout`, `/payment`, `/success`, `/blog`, +`/blog/:id`, `/demo`) fiziksel kalır; içerik sayfaları Custom Component'tir. Blog listesi ve detayı +`PublicAppService`'in `post-list` / `post-by-slug` uçlarından beslenir, yönetimi +`App.BlogManagement.*` ekranlarındadır. + +Eski statik içerik sayfaları ve sayfa içi tasarım modu (`?design=1`) kaldırılmıştır: +`views/public/Home|About|Contact|Services`, `views/public/designer/`, bunlara özel istemci +servisleri; sunucuda `Home`/`About`/`Contact`/`Service` entity'leri, `PublicAppService` sayfa +Get/Save uçları ve DTO'ları, `TenantData.json` + `TenantDataSeeder`, +`App.{Home,About,Services,Contact}` yetkileri ve `.Design` alt yetkileri, `/admin/public/*/designer` +rota ve menüleri. --- diff --git a/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/CustomComponentDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/CustomComponentDto.cs index 4bb5a004..cd471518 100644 --- a/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/CustomComponentDto.cs +++ b/api/src/Sozsoft.Platform.Application.Contracts/DeveloperKit/CustomComponentDto.cs @@ -7,7 +7,8 @@ namespace Sozsoft.Platform.DeveloperKit; public class CustomComponentDto : FullAuditedEntityDto { public string Name { get; set; } = string.Empty; - public string RoutePath { get; set; } = string.Empty; + /// Rotasi olmayan bilesende (ust bilgi, alt bilgi) bostur. + public string? RoutePath { get; set; } public string Code { get; set; } = string.Empty; public string? Props { get; set; } public string? Description { get; set; } @@ -24,10 +25,13 @@ public class CustomComponentDto : FullAuditedEntityDto public class CreateUpdateCustomComponentDto { public string Name { get; set; } = string.Empty; - [Required] + /// + /// Bilesenin kendi rotasi. Bos birakilabilir: rotasi olmayan bir bilesen (ornegin + /// baska bir ekranin icinde cizilen alt bilgi) hicbir adrese baglanmaz. + /// [StringLength(512)] - [RegularExpression(@"^/.*", ErrorMessage = "RoutePath must start with '/'.")] - public string RoutePath { get; set; } = string.Empty; + [RegularExpression(@"^(/.*)?$", ErrorMessage = "RoutePath must start with '/'.")] + public string? RoutePath { get; set; } public string Code { get; set; } = string.Empty; public string? Props { get; set; } public string? Description { get; set; } diff --git a/api/src/Sozsoft.Platform.Application.Contracts/Public/AboutDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/Public/AboutDto.cs deleted file mode 100644 index e637409e..00000000 --- a/api/src/Sozsoft.Platform.Application.Contracts/Public/AboutDto.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json; -using System.Text.Json.Serialization; -using Volo.Abp.Application.Dtos; - -namespace Sozsoft.Platform.Public; - -public class AboutDto : EntityDto -{ - public Guid? TenantId { get; set; } - - [JsonIgnore] - public string StatsJson { get; set; } - public List StatsDto - { - get - { - if (!string.IsNullOrEmpty(StatsJson)) - return JsonSerializer.Deserialize>(StatsJson); - return new List(); - } - set { StatsJson = JsonSerializer.Serialize(value); } - } - - [JsonIgnore] - public string DescriptionsJson { get; set; } - public List DescriptionsDto - { - get - { - if (!string.IsNullOrEmpty(DescriptionsJson)) - return JsonSerializer.Deserialize>(DescriptionsJson); - return []; - } - set { DescriptionsJson = JsonSerializer.Serialize(value); } - } - - [JsonIgnore] - public string SectionsJson { get; set; } - public List SectionsDto - { - get - { - if (!string.IsNullOrEmpty(SectionsJson)) - return JsonSerializer.Deserialize>(SectionsJson); - return new List(); - } - set { SectionsJson = JsonSerializer.Serialize(value); } - } -} - diff --git a/api/src/Sozsoft.Platform.Application.Contracts/Public/ContactDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/Public/ContactDto.cs deleted file mode 100644 index 57f25573..00000000 --- a/api/src/Sozsoft.Platform.Application.Contracts/Public/ContactDto.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; -using Volo.Abp.Application.Dtos; - -namespace Sozsoft.Platform.Public; - -public class ContactDto : EntityDto -{ - public string Address { get; set; } - public string PhoneNumber { get; set; } - public string Email { get; set; } - public string Location { get; set; } - public string TaxNumber { get; set; } - - [JsonIgnore] - public string BankJson { get; set; } - public BankDto BankDto - { - get - { - if (!string.IsNullOrEmpty(BankJson)) - return JsonSerializer.Deserialize(BankJson); - return new BankDto(); - } - set { BankJson = JsonSerializer.Serialize(value); } - } - - [JsonIgnore] - public string WorkHoursJson { get; set; } - public WorkHoursDto WorkHoursDto - { - get - { - if (!string.IsNullOrEmpty(WorkHoursJson)) - return JsonSerializer.Deserialize(WorkHoursJson); - return new WorkHoursDto(); - } - set { WorkHoursJson = JsonSerializer.Serialize(value); } - } - - [JsonIgnore] - public string MapJson { get; set; } - public MapDto MapDto - { - get - { - if (!string.IsNullOrEmpty(MapJson)) - return JsonSerializer.Deserialize(MapJson); - return new MapDto(); - } - set { MapJson = JsonSerializer.Serialize(value); } - } -} diff --git a/api/src/Sozsoft.Platform.Application.Contracts/Public/HomeDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/Public/HomeDto.cs deleted file mode 100644 index a2e23cd5..00000000 --- a/api/src/Sozsoft.Platform.Application.Contracts/Public/HomeDto.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json; -using System.Text.Json.Serialization; -using Volo.Abp.Application.Dtos; - -namespace Sozsoft.Platform.Public; - -public class HomeDto : EntityDto -{ - public string HeroBackgroundImageKey { get; set; } - public string HeroPrimaryCtaKey { get; set; } - public string HeroSecondaryCtaKey { get; set; } - public string FeaturesTitleKey { get; set; } - public string FeaturesSubtitleKey { get; set; } - public string SolutionsTitleKey { get; set; } - public string SolutionsSubtitleKey { get; set; } - public string CtaTitleKey { get; set; } - public string CtaSubtitleKey { get; set; } - public string CtaButtonLabelKey { get; set; } - - [JsonIgnore] - public string SlidesJson { get; set; } - - public List SlidesDto - { - get - { - if (!string.IsNullOrEmpty(SlidesJson)) - return JsonSerializer.Deserialize>(SlidesJson); - return []; - } - set { SlidesJson = JsonSerializer.Serialize(value); } - } - - [JsonIgnore] - public string FeaturesJson { get; set; } - - public List FeaturesDto - { - get - { - if (!string.IsNullOrEmpty(FeaturesJson)) - return JsonSerializer.Deserialize>(FeaturesJson); - return []; - } - set { FeaturesJson = JsonSerializer.Serialize(value); } - } - - [JsonIgnore] - public string SolutionsJson { get; set; } - - public List SolutionsDto - { - get - { - if (!string.IsNullOrEmpty(SolutionsJson)) - return JsonSerializer.Deserialize>(SolutionsJson); - return []; - } - set { SolutionsJson = JsonSerializer.Serialize(value); } - } -} - -public class HomeSlideDto -{ - public string TitleKey { get; set; } - public string SubtitleKey { get; set; } - public string StyleClass { get; set; } - public List Services { get; set; } = []; -} - -public class HomeSlideServiceDto -{ - public string Icon { get; set; } - public string TitleKey { get; set; } - public string DescriptionKey { get; set; } - public string StyleClass { get; set; } -} - -public class HomeFeatureDto -{ - public string Icon { get; set; } - public string TitleKey { get; set; } - public string DescriptionKey { get; set; } - public string StyleClass { get; set; } -} - -public class HomeSolutionDto -{ - public string Icon { get; set; } - public string ColorClass { get; set; } - public string TitleKey { get; set; } - public string DescriptionKey { get; set; } - public string StyleClass { get; set; } -} diff --git a/api/src/Sozsoft.Platform.Application.Contracts/Public/SavePublicPageContentInput.cs b/api/src/Sozsoft.Platform.Application.Contracts/Public/SavePublicPageContentInput.cs deleted file mode 100644 index c45d19a2..00000000 --- a/api/src/Sozsoft.Platform.Application.Contracts/Public/SavePublicPageContentInput.cs +++ /dev/null @@ -1,217 +0,0 @@ -using System.Collections.Generic; - -namespace Sozsoft.Platform.Public; - -public class SaveAboutPageInput -{ - public string CultureName { get; set; } - public string HeroTitleKey { get; set; } - public string HeroTitleValue { get; set; } - public string HeroSubtitleKey { get; set; } - public string HeroSubtitleValue { get; set; } - public string HeroImageKey { get; set; } - public string HeroImageValue { get; set; } - public List Stats { get; set; } = []; - public List Descriptions { get; set; } = []; - public List Sections { get; set; } = []; - public List StyleTexts { get; set; } = []; -} - -public class SaveAboutStatInput -{ - public string Icon { get; set; } - public string Value { get; set; } - public string LabelKey { get; set; } - public string LabelValue { get; set; } - public bool? UseCounter { get; set; } - public string? CounterEnd { get; set; } - public string CounterSuffix { get; set; } - public int? CounterDuration { get; set; } -} - -public class SaveAboutSectionInput -{ - public string TitleKey { get; set; } - public string TitleValue { get; set; } - public string DescriptionKey { get; set; } - public string DescriptionValue { get; set; } -} - -public class SaveServicesPageInput -{ - public string CultureName { get; set; } - public string HeroTitleKey { get; set; } - public string HeroTitleValue { get; set; } - public string HeroSubtitleKey { get; set; } - public string HeroSubtitleValue { get; set; } - public string HeroImageKey { get; set; } - public string HeroImageValue { get; set; } - public string SupportTitleKey { get; set; } - public string SupportTitleValue { get; set; } - public string SupportButtonLabelKey { get; set; } - public string SupportButtonLabelValue { get; set; } - public string CtaTitleKey { get; set; } - public string CtaTitleValue { get; set; } - public string CtaDescriptionKey { get; set; } - public string CtaDescriptionValue { get; set; } - public string CtaButtonLabelKey { get; set; } - public string CtaButtonLabelValue { get; set; } - public List ServiceItems { get; set; } = []; - public List SupportItems { get; set; } = []; - public List StyleTexts { get; set; } = []; -} - -public class SaveServiceItemInput -{ - public string? Icon { get; set; } - public string TitleKey { get; set; } - public string TitleValue { get; set; } - public string? DescriptionKey { get; set; } - public string? DescriptionValue { get; set; } - public string Type { get; set; } - public List Features { get; set; } = []; -} - -public class SaveLocalizedTextInput -{ - public string Key { get; set; } - public string Value { get; set; } -} - -public class SaveHomePageInput -{ - public string CultureName { get; set; } - public string HeroBackgroundImageKey { get; set; } - public string HeroBackgroundImageValue { get; set; } - public string HeroPrimaryCtaKey { get; set; } - public string HeroPrimaryCtaValue { get; set; } - public string HeroPrimaryCtaStyleKey { get; set; } - public string HeroPrimaryCtaStyleValue { get; set; } - public string HeroSecondaryCtaKey { get; set; } - public string HeroSecondaryCtaValue { get; set; } - public string HeroSecondaryCtaStyleKey { get; set; } - public string HeroSecondaryCtaStyleValue { get; set; } - public string FeaturesTitleKey { get; set; } - public string FeaturesTitleValue { get; set; } - public string FeaturesTitleStyleKey { get; set; } - public string FeaturesTitleStyleValue { get; set; } - public string FeaturesSubtitleKey { get; set; } - public string FeaturesSubtitleValue { get; set; } - public string FeaturesSubtitleStyleKey { get; set; } - public string FeaturesSubtitleStyleValue { get; set; } - public string SolutionsTitleKey { get; set; } - public string SolutionsTitleValue { get; set; } - public string SolutionsTitleStyleKey { get; set; } - public string SolutionsTitleStyleValue { get; set; } - public string SolutionsSubtitleKey { get; set; } - public string SolutionsSubtitleValue { get; set; } - public string SolutionsSubtitleStyleKey { get; set; } - public string SolutionsSubtitleStyleValue { get; set; } - public string CtaTitleKey { get; set; } - public string CtaTitleValue { get; set; } - public string CtaTitleStyleKey { get; set; } - public string CtaTitleStyleValue { get; set; } - public string CtaSubtitleKey { get; set; } - public string CtaSubtitleValue { get; set; } - public string CtaSubtitleStyleKey { get; set; } - public string CtaSubtitleStyleValue { get; set; } - public string CtaButtonLabelKey { get; set; } - public string CtaButtonLabelValue { get; set; } - public string CtaButtonStyleKey { get; set; } - public string CtaButtonStyleValue { get; set; } - public List Slides { get; set; } = []; - public List Features { get; set; } = []; - public List Solutions { get; set; } = []; -} - -public class SaveHomeSlideInput -{ - public string TitleKey { get; set; } - public string TitleValue { get; set; } - public string SubtitleKey { get; set; } - public string SubtitleValue { get; set; } - public string StyleClass { get; set; } - public List Services { get; set; } = []; -} - -public class SaveHomeSlideServiceInput -{ - public string Icon { get; set; } - public string TitleKey { get; set; } - public string TitleValue { get; set; } - public string DescriptionKey { get; set; } - public string DescriptionValue { get; set; } - public string StyleClass { get; set; } -} - -public class SaveHomeFeatureInput -{ - public string Icon { get; set; } - public string TitleKey { get; set; } - public string TitleValue { get; set; } - public string DescriptionKey { get; set; } - public string DescriptionValue { get; set; } - public string StyleClass { get; set; } -} - -public class SaveHomeSolutionInput -{ - public string Icon { get; set; } - public string ColorClass { get; set; } - public string TitleKey { get; set; } - public string TitleValue { get; set; } - public string DescriptionKey { get; set; } - public string DescriptionValue { get; set; } - public string StyleClass { get; set; } -} - -public class SaveContactPageInput -{ - public string CultureName { get; set; } - - public string HeroTitleKey { get; set; } - public string HeroTitleValue { get; set; } - public string HeroSubtitleKey { get; set; } - public string HeroSubtitleValue { get; set; } - public string HeroImageKey { get; set; } - public string HeroImageValue { get; set; } - - public string ContactInfoTitleKey { get; set; } - public string ContactInfoTitleValue { get; set; } - public string AddressKey { get; set; } - public string AddressValue { get; set; } - public string PhoneNumber { get; set; } - public string Email { get; set; } - public string Location { get; set; } - public string TaxNumber { get; set; } - - public string BankTitleKey { get; set; } - public string BankTitleValue { get; set; } - public string BankAccountHolder { get; set; } - public string BankBranch { get; set; } - public string BankAccountNumber { get; set; } - public string BankIban { get; set; } - public string BankStyleClass { get; set; } - - public string WorkHoursTitleKey { get; set; } - public string WorkHoursTitleValue { get; set; } - public string WorkWeekdayKey { get; set; } - public string WorkWeekdayValue { get; set; } - public string WorkWeekendKey { get; set; } - public string WorkWeekendValue { get; set; } - public string WorkWhatsappKey { get; set; } - public string WorkWhatsappValue { get; set; } - public string WorkHoursStyleClass { get; set; } - - public string MapTitleKey { get; set; } - public string MapTitleValue { get; set; } - public string MapSrc { get; set; } - public string MapWidth { get; set; } - public string MapHeight { get; set; } - public bool MapAllowFullScreen { get; set; } - public string MapLoading { get; set; } - public string MapReferrerPolicy { get; set; } - public string MapContainerStyleClass { get; set; } - public string MapFrameStyleClass { get; set; } - public List StyleTexts { get; set; } = []; -} diff --git a/api/src/Sozsoft.Platform.Application.Contracts/Public/ServiceDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/Public/ServiceDto.cs deleted file mode 100644 index 15a0448b..00000000 --- a/api/src/Sozsoft.Platform.Application.Contracts/Public/ServiceDto.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using Volo.Abp.Application.Dtos; - -namespace Sozsoft.Platform.Public; - -public class ServiceDto : FullAuditedEntityDto -{ - public string? Icon { get; set; } - public string Title { get; set; } - public string? Description { get; set; } - public string Type { get; set; } - - public string[] Features { get; set; } = []; -} - diff --git a/api/src/Sozsoft.Platform.Application/DeveloperKit/CustomComponentAppService.cs b/api/src/Sozsoft.Platform.Application/DeveloperKit/CustomComponentAppService.cs index 8e6d1541..defea4d1 100644 --- a/api/src/Sozsoft.Platform.Application/DeveloperKit/CustomComponentAppService.cs +++ b/api/src/Sozsoft.Platform.Application/DeveloperKit/CustomComponentAppService.cs @@ -66,6 +66,8 @@ public class CustomComponentAppService : CrudAppService< public override async Task CreateAsync(CreateUpdateCustomComponentDto input) { + NormalizeRoutePath(input); + var result = await base.CreateAsync(input); await SyncDataSourcesAsync(result.Id); await _seedDataSynchronizer.SyncRowAsync(ListFormCode, result.Id, SeedSyncOperation.Insert); @@ -76,6 +78,8 @@ public class CustomComponentAppService : CrudAppService< Guid id, CreateUpdateCustomComponentDto input) { + NormalizeRoutePath(input); + var existingComponent = await Repository.GetAsync(id); if (input.Props is null) @@ -90,6 +94,16 @@ public class CustomComponentAppService : CrudAppService< return result; } + /// + /// Rotasi olmayan bilesen (ust bilgi, alt bilgi) icin yol bostur. Bos dize saklanirsa + /// RoutePath tekil indeksi ikinci rotasiz bilesende carpisir; deger NULL yazilir. + /// + private static void NormalizeRoutePath(CreateUpdateCustomComponentDto input) + { + if (string.IsNullOrWhiteSpace(input.RoutePath)) + input.RoutePath = null; + } + public override async Task DeleteAsync(Guid id) { await base.DeleteAsync(id); diff --git a/api/src/Sozsoft.Platform.Application/Menu/MenuAppService.cs b/api/src/Sozsoft.Platform.Application/Menu/MenuAppService.cs index 0be5085c..5a204666 100644 --- a/api/src/Sozsoft.Platform.Application/Menu/MenuAppService.cs +++ b/api/src/Sozsoft.Platform.Application/Menu/MenuAppService.cs @@ -277,7 +277,10 @@ public class MenuAppService : CrudAppService< await CheckGetPolicyAsync(); var query = await _menuRepository.GetQueryableAsync(); - query = query.Where(a => !a.IsDisabled && a.ModuleId != null); + // Public site menusu yonetim kenar cubugunda yer almaz; o agac ziyaretci + // duzenine aittir ve uygulama konfigurasyonu uzerinden tasinir. + query = query.Where(a => !a.IsDisabled && a.ModuleId != null && + a.ModuleId != PlatformConsts.PublicSite.ModuleId); var entities = await AsyncExecuter.ToListAsync(query); var entityDtos = await MapToGetListOutputDtosAsync(entities); diff --git a/api/src/Sozsoft.Platform.Application/Public/PublicAppService.cs b/api/src/Sozsoft.Platform.Application/Public/PublicAppService.cs index b8aebadf..8bc695d4 100644 --- a/api/src/Sozsoft.Platform.Application/Public/PublicAppService.cs +++ b/api/src/Sozsoft.Platform.Application/Public/PublicAppService.cs @@ -21,13 +21,11 @@ using static Sozsoft.Platform.Data.Seeds.SeedConsts; namespace Sozsoft.Platform.Public; /// -/// Public site uc noktalari. Okuma metotlari anonim erisime aciktir; sayfa tasarimini -/// degistiren Save*PageAsync metotlari -/// yetkileriyle korunur (yetkiler PermissionsData.json icinde tanimli, seeder ile yonetilir). +/// Public site uc noktalari: demo talebi, urun/odeme listeleri, siparis olusturma ve +/// ulke/il/ilce aramalari. Tamami anonim erisime aciktir. /// public class PublicAppService : PlatformAppService { - private readonly IRepository _serviceRepository; private readonly ISettingProvider _settingProvider; private readonly ISozsoftEmailSender _emailSender; private readonly IRepository _demoRepository; @@ -37,18 +35,11 @@ public class PublicAppService : PlatformAppService private readonly IRepository _paymentMethodRepository; private readonly IRepository _installmentOptionRepository; private readonly IRepository _orderRepository; - private readonly IRepository _aboutRepository; - private readonly IRepository _homeRepository; - private readonly IRepository _contactRepository; private readonly IRepository _countryRepository; private readonly IRepository _cityRepository; private readonly IRepository _districtRepository; - private readonly IRepository _languageKeyRepository; - private readonly IRepository _languageTextRepository; - private readonly LanguageTextAppService _languageTextAppService; public PublicAppService( - IRepository serviceRepository, ISettingProvider settingProvider, ISozsoftEmailSender emailSender, IRepository demoRepository, @@ -58,18 +49,11 @@ public class PublicAppService : PlatformAppService IRepository paymentMethodRepository, IRepository installmentOptionRepository, IRepository orderRepository, - IRepository aboutRepository, - IRepository homeRepository, - IRepository contactRepository, IRepository countryRepository, IRepository cityRepository, - IRepository districtRepository, - IRepository languageKeyRepository, - IRepository languageTextRepository, - LanguageTextAppService languageTextAppService + IRepository districtRepository ) { - _serviceRepository = serviceRepository; _settingProvider = settingProvider; _emailSender = emailSender; _demoRepository = demoRepository; @@ -79,235 +63,11 @@ public class PublicAppService : PlatformAppService _paymentMethodRepository = paymentMethodRepository; _installmentOptionRepository = installmentOptionRepository; _orderRepository = orderRepository; - _aboutRepository = aboutRepository; - _homeRepository = homeRepository; - _contactRepository = contactRepository; - _languageKeyRepository = languageKeyRepository; - _languageTextRepository = languageTextRepository; - _languageTextAppService = languageTextAppService; _countryRepository = countryRepository; _cityRepository = cityRepository; _districtRepository = districtRepository; } - public async Task> GetServicesListAsync() - { - var queryable = await _serviceRepository.GetQueryableAsync(); - var entity = await AsyncExecuter.ToListAsync(queryable.OrderBy(a => a.CreationTime)); - - return ObjectMapper.Map, List>(entity); - } - - [Authorize(AppCodes.WebSiteDesign.About)] - public async Task SaveAboutPageAsync(SaveAboutPageInput input) - { - var entity = await _aboutRepository.FirstOrDefaultAsync() ?? throw new EntityNotFoundException(typeof(About)); - - entity.StatsJson = JsonSerializer.Serialize(input.Stats.Select(stat => new StatDto - { - Icon = stat.Icon, - Value = stat.Value, - LabelKey = stat.LabelKey, - UseCounter = stat.UseCounter, - CounterEnd = stat.CounterEnd, - CounterSuffix = stat.CounterSuffix, - CounterDuration = stat.CounterDuration, - }).ToList()); - - entity.DescriptionsJson = JsonSerializer.Serialize(input.Descriptions.Select(item => item.Key).ToList()); - - entity.SectionsJson = JsonSerializer.Serialize(input.Sections.Select(section => new SectionDto - { - Key = section.TitleKey, - DescKey = section.DescriptionKey, - }).ToList()); - - await UpsertLanguageTextAsync(input.CultureName, input.HeroTitleKey, input.HeroTitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.HeroSubtitleKey, input.HeroSubtitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.HeroImageKey, input.HeroImageValue); - - foreach (var stat in input.Stats) - { - await UpsertLanguageTextAsync(input.CultureName, stat.LabelKey, stat.LabelValue); - } - - foreach (var description in input.Descriptions) - { - await UpsertLanguageTextAsync(input.CultureName, description.Key, description.Value); - } - - foreach (var section in input.Sections) - { - await UpsertLanguageTextAsync(input.CultureName, section.TitleKey, section.TitleValue); - await UpsertLanguageTextAsync(input.CultureName, section.DescriptionKey, section.DescriptionValue); - } - - foreach (var styleText in input.StyleTexts) - { - await UpsertLanguageTextAsync(input.CultureName, styleText.Key, styleText.Value); - } - - await _aboutRepository.UpdateAsync(entity, autoSave: true); - await _languageTextAppService.ClearRedisCacheAsync(); - } - - [Authorize(AppCodes.WebSiteDesign.Services)] - public async Task SaveServicesPageAsync(SaveServicesPageInput input) - { - var existingEntities = await _serviceRepository.GetListAsync(); - - foreach (var entity in existingEntities) - { - await _serviceRepository.DeleteAsync(entity, autoSave: false); - } - - foreach (var item in input.ServiceItems.Concat(input.SupportItems)) - { - var entity = new Service - { - Icon = item.Icon, - Title = item.TitleKey, - Description = item.DescriptionKey, - Type = item.Type, - Features = item.Features.Select(feature => feature.Key).ToArray(), - }; - - await _serviceRepository.InsertAsync(entity, autoSave: false); - - await UpsertLanguageTextAsync(input.CultureName, item.TitleKey, item.TitleValue); - - if (!item.DescriptionKey.IsNullOrWhiteSpace()) - { - await UpsertLanguageTextAsync(input.CultureName, item.DescriptionKey!, item.DescriptionValue ?? string.Empty); - } - - foreach (var feature in item.Features) - { - await UpsertLanguageTextAsync(input.CultureName, feature.Key, feature.Value); - } - } - - await UpsertLanguageTextAsync(input.CultureName, input.HeroTitleKey, input.HeroTitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.HeroSubtitleKey, input.HeroSubtitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.HeroImageKey, input.HeroImageValue); - await UpsertLanguageTextAsync(input.CultureName, input.SupportTitleKey, input.SupportTitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.SupportButtonLabelKey, input.SupportButtonLabelValue); - await UpsertLanguageTextAsync(input.CultureName, input.CtaTitleKey, input.CtaTitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.CtaDescriptionKey, input.CtaDescriptionValue); - await UpsertLanguageTextAsync(input.CultureName, input.CtaButtonLabelKey, input.CtaButtonLabelValue); - - foreach (var styleText in input.StyleTexts) - { - await UpsertLanguageTextAsync(input.CultureName, styleText.Key, styleText.Value); - } - - await CurrentUnitOfWork!.SaveChangesAsync(); - await _languageTextAppService.ClearRedisCacheAsync(); - } - - public async Task GetHomeAsync() - { - var entity = await _homeRepository.FirstOrDefaultAsync() ?? throw new EntityNotFoundException(typeof(Home)); - return ObjectMapper.Map(entity); - } - - [Authorize(AppCodes.WebSiteDesign.Home)] - public async Task SaveHomePageAsync(SaveHomePageInput input) - { - var entity = await _homeRepository.FirstOrDefaultAsync() ?? throw new EntityNotFoundException(typeof(Home)); - - entity.HeroBackgroundImageKey = input.HeroBackgroundImageKey; - entity.HeroPrimaryCtaKey = input.HeroPrimaryCtaKey; - entity.HeroSecondaryCtaKey = input.HeroSecondaryCtaKey; - entity.FeaturesTitleKey = input.FeaturesTitleKey; - entity.FeaturesSubtitleKey = input.FeaturesSubtitleKey; - entity.SolutionsTitleKey = input.SolutionsTitleKey; - entity.SolutionsSubtitleKey = input.SolutionsSubtitleKey; - entity.CtaTitleKey = input.CtaTitleKey; - entity.CtaSubtitleKey = input.CtaSubtitleKey; - entity.CtaButtonLabelKey = input.CtaButtonLabelKey; - - entity.SlidesJson = JsonSerializer.Serialize(input.Slides.Select(slide => new HomeSlideDto - { - TitleKey = slide.TitleKey, - SubtitleKey = slide.SubtitleKey, - StyleClass = slide.StyleClass, - Services = slide.Services.Select(service => new HomeSlideServiceDto - { - Icon = service.Icon, - TitleKey = service.TitleKey, - DescriptionKey = service.DescriptionKey, - StyleClass = service.StyleClass, - }).ToList(), - }).ToList()); - - entity.FeaturesJson = JsonSerializer.Serialize(input.Features.Select(feature => new HomeFeatureDto - { - Icon = feature.Icon, - TitleKey = feature.TitleKey, - DescriptionKey = feature.DescriptionKey, - StyleClass = feature.StyleClass, - }).ToList()); - - entity.SolutionsJson = JsonSerializer.Serialize(input.Solutions.Select(solution => new HomeSolutionDto - { - Icon = solution.Icon, - ColorClass = solution.ColorClass, - TitleKey = solution.TitleKey, - DescriptionKey = solution.DescriptionKey, - StyleClass = solution.StyleClass, - }).ToList()); - - await _homeRepository.UpdateAsync(entity, autoSave: false); - - await UpsertLanguageTextAsync(input.CultureName, input.HeroBackgroundImageKey, input.HeroBackgroundImageValue); - await UpsertLanguageTextAsync(input.CultureName, input.HeroPrimaryCtaKey, input.HeroPrimaryCtaValue); - await UpsertLanguageTextAsync(input.CultureName, input.HeroPrimaryCtaStyleKey, input.HeroPrimaryCtaStyleValue); - await UpsertLanguageTextAsync(input.CultureName, input.HeroSecondaryCtaKey, input.HeroSecondaryCtaValue); - await UpsertLanguageTextAsync(input.CultureName, input.HeroSecondaryCtaStyleKey, input.HeroSecondaryCtaStyleValue); - await UpsertLanguageTextAsync(input.CultureName, input.FeaturesTitleKey, input.FeaturesTitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.FeaturesTitleStyleKey, input.FeaturesTitleStyleValue); - await UpsertLanguageTextAsync(input.CultureName, input.FeaturesSubtitleKey, input.FeaturesSubtitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.FeaturesSubtitleStyleKey, input.FeaturesSubtitleStyleValue); - await UpsertLanguageTextAsync(input.CultureName, input.SolutionsTitleKey, input.SolutionsTitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.SolutionsTitleStyleKey, input.SolutionsTitleStyleValue); - await UpsertLanguageTextAsync(input.CultureName, input.SolutionsSubtitleKey, input.SolutionsSubtitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.SolutionsSubtitleStyleKey, input.SolutionsSubtitleStyleValue); - await UpsertLanguageTextAsync(input.CultureName, input.CtaTitleKey, input.CtaTitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.CtaTitleStyleKey, input.CtaTitleStyleValue); - await UpsertLanguageTextAsync(input.CultureName, input.CtaSubtitleKey, input.CtaSubtitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.CtaSubtitleStyleKey, input.CtaSubtitleStyleValue); - await UpsertLanguageTextAsync(input.CultureName, input.CtaButtonLabelKey, input.CtaButtonLabelValue); - await UpsertLanguageTextAsync(input.CultureName, input.CtaButtonStyleKey, input.CtaButtonStyleValue); - - foreach (var slide in input.Slides) - { - await UpsertLanguageTextAsync(input.CultureName, slide.TitleKey, slide.TitleValue); - await UpsertLanguageTextAsync(input.CultureName, slide.SubtitleKey, slide.SubtitleValue); - - foreach (var service in slide.Services) - { - await UpsertLanguageTextAsync(input.CultureName, service.TitleKey, service.TitleValue); - await UpsertLanguageTextAsync(input.CultureName, service.DescriptionKey, service.DescriptionValue); - } - } - - foreach (var feature in input.Features) - { - await UpsertLanguageTextAsync(input.CultureName, feature.TitleKey, feature.TitleValue); - await UpsertLanguageTextAsync(input.CultureName, feature.DescriptionKey, feature.DescriptionValue); - } - - foreach (var solution in input.Solutions) - { - await UpsertLanguageTextAsync(input.CultureName, solution.TitleKey, solution.TitleValue); - await UpsertLanguageTextAsync(input.CultureName, solution.DescriptionKey, solution.DescriptionValue); - } - - await CurrentUnitOfWork!.SaveChangesAsync(); - await _languageTextAppService.ClearRedisCacheAsync(); - } - public async Task CreateDemoAsync(DemoDto input) { var demo = ObjectMapper.Map(input); @@ -565,7 +325,6 @@ public class PublicAppService : PlatformAppService Categories = categoryDtos }; } - private async Task GetPostAsync(Guid id) { // Tek sorguda post ve category'yi çek (N+1 önleme) @@ -586,7 +345,6 @@ public class PublicAppService : PlatformAppService return dto; } - public async Task GetPostBySlugAsync(string slug) { var post = await _postRepository.FirstOrDefaultAsync(x => x.Slug == slug); @@ -1043,19 +801,6 @@ public class PublicAppService : PlatformAppService return WebUtility.HtmlEncode(value ?? string.Empty); } - public async Task GetAboutAsync() - { - var entity = await _aboutRepository.FirstOrDefaultAsync() ?? throw new EntityNotFoundException(typeof(About)); - return ObjectMapper.Map(entity); - } - - public async Task GetContactAsync() - { - var entity = await _contactRepository.FirstOrDefaultAsync() ?? throw new EntityNotFoundException(typeof(Contact)); - - return ObjectMapper.Map(entity); - } - public async Task> GetCountryAsync() { var queryable = await _countryRepository.GetQueryableAsync(); @@ -1128,121 +873,6 @@ public class PublicAppService : PlatformAppService .ToList(); } - [Authorize(AppCodes.WebSiteDesign.Contact)] - public async Task SaveContactPageAsync(SaveContactPageInput input) - { - var entity = await _contactRepository.FirstOrDefaultAsync() ?? throw new EntityNotFoundException(typeof(Contact)); - - entity.Address = input.AddressKey; - entity.PhoneNumber = input.PhoneNumber; - entity.Email = input.Email; - entity.Location = input.Location; - entity.TaxNumber = long.TryParse(input.TaxNumber, out var taxNumber) ? taxNumber : null; - - entity.BankJson = JsonSerializer.Serialize(new BankDto - { - AccountHolder = input.BankAccountHolder, - Branch = input.BankBranch, - AccountNumber = input.BankAccountNumber, - Iban = input.BankIban, - StyleClass = input.BankStyleClass, - }); - - entity.WorkHoursJson = JsonSerializer.Serialize(new WorkHoursDto - { - Weekday = input.WorkWeekdayKey, - Weekend = input.WorkWeekendKey, - Whatsapp = input.WorkWhatsappKey, - StyleClass = input.WorkHoursStyleClass, - }); - - entity.MapJson = JsonSerializer.Serialize(new MapDto - { - Title = input.MapTitleKey, - Src = input.MapSrc, - Width = input.MapWidth, - Height = input.MapHeight, - AllowFullScreen = input.MapAllowFullScreen, - Loading = input.MapLoading, - ReferrerPolicy = input.MapReferrerPolicy, - ContainerStyleClass = input.MapContainerStyleClass, - FrameStyleClass = input.MapFrameStyleClass, - }); - - await _contactRepository.UpdateAsync(entity, autoSave: false); - - await UpsertLanguageTextAsync(input.CultureName, input.HeroTitleKey, input.HeroTitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.HeroSubtitleKey, input.HeroSubtitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.HeroImageKey, input.HeroImageValue); - await UpsertLanguageTextAsync(input.CultureName, input.ContactInfoTitleKey, input.ContactInfoTitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.AddressKey, input.AddressValue); - await UpsertLanguageTextAsync(input.CultureName, input.BankTitleKey, input.BankTitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.WorkHoursTitleKey, input.WorkHoursTitleValue); - await UpsertLanguageTextAsync(input.CultureName, input.WorkWeekdayKey, input.WorkWeekdayValue); - await UpsertLanguageTextAsync(input.CultureName, input.WorkWeekendKey, input.WorkWeekendValue); - await UpsertLanguageTextAsync(input.CultureName, input.WorkWhatsappKey, input.WorkWhatsappValue); - await UpsertLanguageTextAsync(input.CultureName, input.MapTitleKey, input.MapTitleValue); - - foreach (var styleText in input.StyleTexts) - { - await UpsertLanguageTextAsync(input.CultureName, styleText.Key, styleText.Value); - } - - await CurrentUnitOfWork!.SaveChangesAsync(); - await _languageTextAppService.ClearRedisCacheAsync(); - } - - private async Task UpsertLanguageTextAsync(string cultureName, string key, string value) - { - if (key.IsNullOrWhiteSpace()) - { - return; - } - - var normalizedCultureName = NormalizeCultureName(cultureName); - var resourceName = PlatformConsts.AppName; - - var languageKey = await _languageKeyRepository.FirstOrDefaultAsync(a => a.ResourceName == resourceName && a.Key == key); - if (languageKey == null) - { - languageKey = await _languageKeyRepository.InsertAsync(new LanguageKey - { - ResourceName = resourceName, - Key = key, - }, autoSave: false); - } - - var languageText = await _languageTextRepository.FirstOrDefaultAsync(a => - a.ResourceName == resourceName && - a.Key == languageKey.Key && - a.CultureName == normalizedCultureName); - - if (languageText == null) - { - await _languageTextRepository.InsertAsync(new LanguageText - { - ResourceName = resourceName, - Key = languageKey.Key, - CultureName = normalizedCultureName, - Value = value ?? string.Empty, - }, autoSave: false); - - return; - } - - languageText.Value = value ?? string.Empty; - await _languageTextRepository.UpdateAsync(languageText, autoSave: false); - } - - private static string NormalizeCultureName(string cultureName) - { - if (cultureName.IsNullOrWhiteSpace()) - { - return PlatformConsts.DefaultLanguage; - } - - return cultureName.Split('-')[0].ToLowerInvariant(); - } } diff --git a/api/src/Sozsoft.Platform.Application/Public/PublicMappers.cs b/api/src/Sozsoft.Platform.Application/Public/PublicMappers.cs index f726aa05..29f50629 100644 --- a/api/src/Sozsoft.Platform.Application/Public/PublicMappers.cs +++ b/api/src/Sozsoft.Platform.Application/Public/PublicMappers.cs @@ -33,22 +33,6 @@ public partial class DemoToDemoDtoMapper : TwoWayMapperBase } } -[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] -public partial class ServiceToServiceDtoMapper : MapperBase -{ - public override partial ServiceDto Map(Service source); - - public override partial void Map(Service source, ServiceDto destination); - - public override void BeforeMap(Service source) - { - } - - public override void AfterMap(Service source, ServiceDto destination) - { - } -} - [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] public partial class BlogCategoryToBlogCategoryDtoMapper : MapperBase { @@ -173,54 +157,6 @@ public partial class OrderToOrderDtoMapper : MapperBase } } -[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] -public partial class AboutToAboutDtoMapper : MapperBase -{ - public override partial AboutDto Map(About source); - - public override partial void Map(About source, AboutDto destination); - - public override void BeforeMap(About source) - { - } - - public override void AfterMap(About source, AboutDto destination) - { - } -} - -[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] -public partial class HomeToHomeDtoMapper : MapperBase -{ - public override partial HomeDto Map(Home source); - - public override partial void Map(Home source, HomeDto destination); - - public override void BeforeMap(Home source) - { - } - - public override void AfterMap(Home source, HomeDto destination) - { - } -} - -[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] -public partial class ContactToContactDtoMapper : MapperBase -{ - public override partial ContactDto Map(Contact source); - - public override partial void Map(Contact source, ContactDto destination); - - public override void BeforeMap(Contact source) - { - } - - public override void AfterMap(Contact source, ContactDto destination) - { - } -} - [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] public partial class CountryToCountryDtoMapper : MapperBase { diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json b/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json index 1e41a390..11b97b17 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json @@ -1194,6 +1194,12 @@ "en": "Web Site", "tr": "Web Site" }, + { + "resourceName": "Platform", + "key": "App.Public.Header", + "en": "Site Menu", + "tr": "Site Menüsü" + }, { "resourceName": "Platform", "key": "App.IdentityManagement.ClaimType", @@ -7266,6 +7272,12 @@ "tr": "Vergi Dairesi", "en": "Tax Office" }, + { + "resourceName": "Platform", + "key": "App.PublicContact.WorkHours", + "en": "Working Hours", + "tr": "Çalışma Saatleri" + }, { "resourceName": "Platform", "key": "App.PublicContact.TransferForm", @@ -8934,90 +8946,6 @@ "tr": "Müşteri Yorumları", "en": "Testimonials" }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.DesignerTitle", - "tr": "Tasarımcı", - "en": "Designer" - }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.IkonAnahtari", - "tr": "İkon Anahtarı", - "en": "Icon Key" - }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.ShowPanel", - "tr": "Paneli Göster", - "en": "Show Panel" - }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.NoSelection", - "tr": "Bir alan seçin", - "en": "Select a field" - }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.DesignerDesc1", - "tr": "İkon, Başlık, Açıklama ve Özellik listesini düzenleyin.", - "en": "Edit the icon, title, description, and feature list." - }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.DesignerDesc2", - "tr": "Alt CTA metinlerini ve buton etiketini düzenleyin.", - "en": "Edit the sub-CTA texts and button label." - }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.DesignerDesc3", - "tr": "Destek alanı başlığını ve buton metnini düzenleyin.", - "en": "Edit the support area title and button text." - }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.DesignerDesc4", - "tr": "Başlık, alt başlık ve arka plan görselini düzenleyin.", - "en": "Edit the title, subtitle, and background image." - }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.DesignerDesc5", - "tr": "İletişim bilgilerini düzenleyin.", - "en": "Edit the contact information." - }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.DesignerDesc6", - "tr": "Banka bilgilerini düzenleyin.", - "en": "Edit the bank information." - }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.DesignerDesc7", - "tr": "Çalışma saatleri metinlerini düzenleyin.", - "en": "Edit the work hours information." - }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.DesignerDesc8", - "tr": "Harita başlığı ve iframe ayarlarını düzenleyin.", - "en": "Edit the map title and iframe settings." - }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.SelectField", - "tr": "Sayfa üzerinden bir blok seçerek özelliklerini buradan düzenleyin.", - "en": "Select a block on the page to edit its properties here." - }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.NoSelectionDetails", - "tr": "Seçili bölümün düzenlenebilir alanları burada gösterilir.", - "en": "Select a block on the page to edit its properties here." - }, { "resourceName": "Platform", "key": "App.Listform.ListformField.DataSources", @@ -24078,12 +24006,6 @@ "en": "Blog post not found.", "tr": "Blog yazısı bulunamadı." }, - { - "resourceName": "Platform", - "key": "App.PublicDesigner.IconPlaceholder", - "en": "Example: {icon}", - "tr": "Örnek: {icon}" - }, { "resourceName": "Platform", "key": "App.PublicPayment.CreateOrderError", @@ -24240,6 +24162,60 @@ "en": "Date picker component", "tr": "Tarih seçici bileşeni" }, + { + "resourceName": "Platform", + "key": "App.CatalogPublicHeaderBar.PublicHeaderBarDescription", + "en": "Public site header bar — swaps its classes when the page is scrolled", + "tr": "Public site üst bilgi çubuğu — sayfa kaydırıldığında sınıflarını değiştirir" + }, + { + "resourceName": "Platform", + "key": "App.CatalogPublicNav.PublicNavDescription", + "en": "Public site menu links (Menu Manager, Pub module)", + "tr": "Public site menü bağlantıları (Menü Yöneticisi, Pub modülü)" + }, + { + "resourceName": "Platform", + "key": "App.CatalogPublicMobileMenu.PublicMobileMenuDescription", + "en": "Mobile menu button and the panel it opens; the panel content is designed here", + "tr": "Mobil menü düğmesi ve açtığı panel; panelin içeriği burada tasarlanır" + }, + { + "resourceName": "Platform", + "key": "App.CatalogPublicLogo.PublicLogoDescription", + "en": "Logo that follows the active theme", + "tr": "Aktif temayı izleyen logo" + }, + { + "resourceName": "Platform", + "key": "App.CatalogPublicThemeToggle.PublicThemeToggleDescription", + "en": "Light/dark theme switch", + "tr": "Aydınlık/karanlık tema anahtarı" + }, + { + "resourceName": "Platform", + "key": "App.CatalogPublicLanguageSelector.PublicLanguageSelectorDescription", + "en": "Language selector", + "tr": "Dil seçici" + }, + { + "resourceName": "Platform", + "key": "App.CatalogPublicDemoButton.PublicDemoButtonDescription", + "en": "Button that opens the demo window", + "tr": "Demo penceresini açan düğme" + }, + { + "resourceName": "Platform", + "key": "App.CatalogPlatformIcon.PlatformIconDescription", + "en": "Icon drawn by name (FaUser, FcHome …)", + "tr": "Ada göre çizilen ikon (FaUser, FcHome …)" + }, + { + "resourceName": "Platform", + "key": "App.ComponentLink.LinkDescription", + "en": "Link (a) — navigates to the given address", + "tr": "Bağlantı (a) — verilen adrese gider" + }, { "resourceName": "Platform", "key": "App.ComponentDiv.DivDescription", diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Saas.cs b/api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Saas.cs index 929a1534..56b0296e 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Saas.cs +++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Saas.cs @@ -7726,10 +7726,10 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency IsActive = true, AllowSearch = true, - // Rota "/" ile baslamalidir; sunucu tarafinda da ayni kural dogrulanir. + // Rota bos birakilabilir (rotasi olmayan bilesen); dolduruldugunda "/" ile + // baslamalidir. Ayni kural sunucu tarafinda da dogrulanir. ValidationRuleJson = JsonSerializer.Serialize(new ValidationRuleDto[] { - new() { Type = Enum.GetName(UiColumnValidationRuleTypeEnum.required) }, - new() { Type = Enum.GetName(UiColumnValidationRuleTypeEnum.pattern), Pattern = "^/.*", Message = "Route path must start with /" }, + new() { Type = Enum.GetName(UiColumnValidationRuleTypeEnum.pattern), Pattern = "^(/.*)?$", Message = "Route path must start with /" }, }), ColumnCustomizationJson = DefaultColumnCustomizationJson, PermissionJson = DefaultFieldPermissionJson(listForm.Name), @@ -7781,13 +7781,16 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency CultureName = LanguageCodes.En, SourceDbType = DbType.String, FieldName = "Dependencies", - EditorType2 = EditorTypes.dxTextBox, CaptionName = "App.Platform.Dependencies", + EditGroupOrderNo = 1, + EditOrderNo = 5, + EditorType2 = EditorTypes.dxTextBox, + ColSpan = 1, Width = 0, ListOrderNo = 6, Visible = true, IsActive = true, - + AllowSearch = true, ColumnCustomizationJson = DefaultColumnCustomizationJson, PermissionJson = DefaultFieldPermissionJson(listForm.Name), diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/MenuDataSeeder.cs b/api/src/Sozsoft.Platform.DbMigrator/Migrations/MenuDataSeeder.cs index d388c69f..f23634ad 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/MenuDataSeeder.cs +++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/MenuDataSeeder.cs @@ -34,6 +34,7 @@ public class MenuSeedDto public int Order { get; set; } public string Url { get; set; } public string Icon { get; set; } + public string CssClass { get; set; } public string RequiredPermissionName { get; set; } public bool IsDisabled { get; set; } public string ModuleId { get; set; } @@ -120,6 +121,7 @@ public class MenuDataSeeder : IDataSeedContributor, ITransientDependency Order = item.Order, Url = item.Url, Icon = item.Icon, + CssClass = item.CssClass, RequiredPermissionName = item.RequiredPermissionName, IsDisabled = item.IsDisabled, ModuleId = item.ModuleId diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/MenusData.json b/api/src/Sozsoft.Platform.DbMigrator/Migrations/MenusData.json index bf017332..157d4014 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/MenusData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/MenusData.json @@ -1,21 +1,5 @@ { "Routes": [ - { - "key": "home", - "path": "/home", - "componentType": "normal", - "componentPath": "@/views/public/Home", - "routeType": "public", - "authority": [] - }, - { - "key": "about", - "path": "/about", - "componentType": "normal", - "componentPath": "@/views/public/About", - "routeType": "public", - "authority": [] - }, { "key": "products", "path": "/products", @@ -48,14 +32,6 @@ "routeType": "public", "authority": [] }, - { - "key": "services", - "path": "/services", - "componentType": "normal", - "componentPath": "@/views/public/Services", - "routeType": "public", - "authority": [] - }, { "key": "blog", "path": "/blog", @@ -80,14 +56,6 @@ "routeType": "public", "authority": [] }, - { - "key": "contact", - "path": "/contact", - "componentType": "normal", - "componentPath": "@/views/public/Contact", - "routeType": "public", - "authority": [] - }, { "key": "access-denied", "path": "/access-denied", @@ -370,38 +338,6 @@ "routeType": "protected", "authority": [] }, - { - "key": "homeDesigner", - "path": "/admin/public/home/designer", - "componentType": "normal", - "componentPath": "@/views/public/Home", - "routeType": "protected", - "authority": ["App.Home"] - }, - { - "key": "aboutDesigner", - "path": "/admin/public/about/designer", - "componentType": "normal", - "componentPath": "@/views/public/About", - "routeType": "protected", - "authority": ["App.About"] - }, - { - "key": "servicesDesigner", - "path": "/admin/public/services/designer", - "componentType": "normal", - "componentPath": "@/views/public/Services", - "routeType": "protected", - "authority": ["App.Services"] - }, - { - "key": "contactDesigner", - "path": "/admin/public/contact/designer", - "componentType": "normal", - "componentPath": "@/views/public/Contact", - "routeType": "protected", - "authority": ["App.Contact"] - }, { "key": "admin.videoroom", "path": "/admin/videoroom/dashboard", @@ -433,6 +369,9 @@ }, { "Name": "Adm" + }, + { + "Name": "Pub" } ], "Menus": [ @@ -590,39 +529,6 @@ "IsDisabled": false, "ModuleId": "Sas" }, - { - "ParentCode": "App.Public", - "Code": "App.Home", - "DisplayName": "App.Home", - "Order": 0, - "Url": "/admin/public/home/designer", - "Icon": "FcHome", - "RequiredPermissionName": "App.Home", - "IsDisabled": false, - "ModuleId": "Sas" - }, - { - "ParentCode": "App.Public", - "Code": "App.About", - "DisplayName": "App.About", - "Order": 1, - "Url": "/admin/public/about/designer", - "Icon": "FcAbout", - "RequiredPermissionName": "App.About", - "IsDisabled": false, - "ModuleId": "Sas" - }, - { - "ParentCode": "App.Public", - "Code": "App.Services", - "DisplayName": "App.Services", - "Order": 2, - "Url": "/admin/public/services/designer", - "Icon": "FcServices", - "RequiredPermissionName": "App.Services", - "IsDisabled": false, - "ModuleId": "Sas" - }, { "ParentCode": "App.Public", "Code": "App.Orders.Products", @@ -700,17 +606,6 @@ "IsDisabled": false, "ModuleId": "Sas" }, - { - "ParentCode": "App.Public", - "Code": "App.Contact", - "DisplayName": "App.Contact", - "Order": 10, - "Url": "/admin/public/contact/designer", - "Icon": "FcContacts", - "RequiredPermissionName": "App.Contact", - "IsDisabled": false, - "ModuleId": "Sas" - }, { "ParentCode": "App.Saas", "Code": "App.DevRequests", @@ -1470,6 +1365,85 @@ "RequiredPermissionName": "App.ForumManagement.Publish", "IsDisabled": false, "ModuleId": "Adm" + }, + { + "ParentCode": null, + "Code": "App.Public.Header", + "DisplayName": "App.Public.Header", + "Order": 900, + "Url": null, + "Icon": "FcGlobe", + "RequiredPermissionName": null, + "IsDisabled": false, + "ModuleId": "Pub" + }, + { + "ParentCode": "App.Public.Header", + "Code": "App.Public.Header.Home", + "DisplayName": "App.Home", + "Order": 10, + "Url": "/home", + "Icon": "FaHome", + "RequiredPermissionName": null, + "IsDisabled": false, + "ModuleId": "Pub" + }, + { + "ParentCode": "App.Public.Header", + "Code": "App.Public.Header.About", + "DisplayName": "App.About", + "Order": 20, + "Url": "/about", + "Icon": "FaInfoCircle", + "RequiredPermissionName": null, + "IsDisabled": false, + "ModuleId": "Pub" + }, + { + "ParentCode": "App.Public.Header", + "Code": "App.Public.Header.Products", + "DisplayName": "App.Platform.Orders", + "Order": 40, + "Url": "/products", + "Icon": "FaBoxOpen", + "RequiredPermissionName": null, + "IsDisabled": false, + "ModuleId": "Pub" + }, + { + "ParentCode": "App.Public.Header", + "Code": "App.Public.Header.Blog", + "DisplayName": "App.Platform.BlogManagement", + "Order": 50, + "Url": "/blog", + "Icon": "FaBookOpen", + "RequiredPermissionName": null, + "IsDisabled": false, + "ModuleId": "Pub" + }, + { + "ParentCode": "App.Public.Header", + "Code": "App.Public.Header.Contact", + "DisplayName": "App.Contact", + "Order": 60, + "Url": "/contact", + "Icon": "FaPhoneAlt", + "RequiredPermissionName": null, + "IsDisabled": false, + "ModuleId": "Pub" + }, + { + "ParentCode": "App.Public.Header", + "Code": "App.Public.Header.Login", + "DisplayName": "App.PublicNav.Giris", + "Order": 900, + "Url": "/login", + "Icon": null, + "CssClass": "cta", + "RequiredPermissionName": null, + "IsDisabled": false, + "ModuleId": "Pub" } + ] } diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/PermissionsData.json b/api/src/Sozsoft.Platform.DbMigrator/Migrations/PermissionsData.json index 4f984eed..9fe4a711 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/PermissionsData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/PermissionsData.json @@ -811,60 +811,6 @@ "MultiTenancySide": 2, "Modules": "Sas|Adm" }, - { - "GroupName": "App.Saas", - "Name": "App.Home", - "ParentName": "App.Public", - "DisplayName": "App.Home", - "IsEnabled": true, - "MultiTenancySide": 2, - "Modules": "Sas|Adm" - }, - { - "GroupName": "App.Saas", - "Name": "App.Home.Design", - "ParentName": "App.Home", - "DisplayName": "Design", - "IsEnabled": true, - "MultiTenancySide": 2, - "Modules": "Sas|Adm" - }, - { - "GroupName": "App.Saas", - "Name": "App.About", - "ParentName": "App.Public", - "DisplayName": "App.About", - "IsEnabled": true, - "MultiTenancySide": 2, - "Modules": "Sas|Adm" - }, - { - "GroupName": "App.Saas", - "Name": "App.About.Design", - "ParentName": "App.About", - "DisplayName": "Design", - "IsEnabled": true, - "MultiTenancySide": 2, - "Modules": "Sas|Adm" - }, - { - "GroupName": "App.Saas", - "Name": "App.Services", - "ParentName": "App.Public", - "DisplayName": "App.Services", - "IsEnabled": true, - "MultiTenancySide": 2, - "Modules": "Sas|Adm" - }, - { - "GroupName": "App.Saas", - "Name": "App.Services.Design", - "ParentName": "App.Services", - "DisplayName": "Design", - "IsEnabled": true, - "MultiTenancySide": 2, - "Modules": "Sas|Adm" - }, { "GroupName": "App.Saas", "Name": "App.Orders.Products", @@ -1315,24 +1261,6 @@ "MultiTenancySide": 2, "Modules": "Sas|Adm" }, - { - "GroupName": "App.Saas", - "Name": "App.Contact", - "ParentName": "App.Public", - "DisplayName": "App.Contact", - "IsEnabled": true, - "MultiTenancySide": 2, - "Modules": "Sas|Adm" - }, - { - "GroupName": "App.Saas", - "Name": "App.Contact.Design", - "ParentName": "App.Contact", - "DisplayName": "Design", - "IsEnabled": true, - "MultiTenancySide": 2, - "Modules": "Sas|Adm" - }, { "GroupName": "App.Saas", "Name": "App.DevRequests", diff --git a/api/src/Sozsoft.Platform.Domain.Shared/Enums/TableNameEnum.cs b/api/src/Sozsoft.Platform.Domain.Shared/Enums/TableNameEnum.cs index 8f8cc230..2c7eb4f8 100644 --- a/api/src/Sozsoft.Platform.Domain.Shared/Enums/TableNameEnum.cs +++ b/api/src/Sozsoft.Platform.Domain.Shared/Enums/TableNameEnum.cs @@ -55,9 +55,6 @@ public enum TableNameEnum UomCategory, Uom, WorkHour, - About, - Home, - Service, Product, PaymentMethod, InstallmentOption, @@ -67,7 +64,6 @@ public enum TableNameEnum BlogCategory, BlogPost, Demo, - Contact, Announcement, AnnouncementComment, AnnouncementLike, diff --git a/api/src/Sozsoft.Platform.Domain.Shared/PlatformConsts.cs b/api/src/Sozsoft.Platform.Domain.Shared/PlatformConsts.cs index 85483b82..2a6b1119 100644 --- a/api/src/Sozsoft.Platform.Domain.Shared/PlatformConsts.cs +++ b/api/src/Sozsoft.Platform.Domain.Shared/PlatformConsts.cs @@ -522,10 +522,6 @@ public static class PlatformConsts public const string CrudEndpoints = Default + ".CrudEndpoints"; } - //Web Site - public const string Home = Prefix.App + ".Home"; - public const string About = Prefix.App + ".About"; - public const string Services = Prefix.App + ".Services"; public static class Orders { public const string Default = Prefix.App + ".Orders"; @@ -544,19 +540,6 @@ public static class PlatformConsts public const string BlogCategory = Default + ".Category"; } public const string Demos = Prefix.App + ".Demos"; - public const string Contact = Prefix.App + ".Contact"; - - /// - /// Public sitedeki sayfa tasarim modu (`?design=1`) yetkileri. - /// Yetkiler Migrations/PermissionsData.json icinde tanimlanir ve seeder ile yonetilir. - /// - public static class WebSiteDesign - { - public const string Home = AppCodes.Home + ".Design"; - public const string About = AppCodes.About + ".Design"; - public const string Services = AppCodes.Services + ".Design"; - public const string Contact = AppCodes.Contact + ".Design"; - } //Administration public const string Administration = Prefix.App + ".Administration"; @@ -774,6 +757,23 @@ public static class PlatformConsts public const string ColumnLookupTagBoxEditorApplyValueModeUseButtons = "useButtons"; } + /// + /// Public site menusunun sozlesmesi. Menuler modulu altinda + /// tutulur; altindaki kayitlar ust menuyu besler. Alt bilgi + /// menuden beslenmez, kendi Custom Component'inde tasarlanir. + /// + public static class PublicSite + { + public const string ModuleId = "Pub"; + public const string HeaderCode = "App.Public.Header"; + + /// + /// Menunun CssClass alaninda bu belirtec varsa baglanti duz yazi yerine + /// vurgulu buton olarak cizilir (orn. "Giris"). + /// + public const string CallToActionClass = "cta"; + } + public static class Urls { public const string EmailConfirmation = "Account.EmailConfirmation"; diff --git a/api/src/Sozsoft.Platform.Domain.Shared/TableNameResolver.cs b/api/src/Sozsoft.Platform.Domain.Shared/TableNameResolver.cs index d5abfa45..d080e8f3 100644 --- a/api/src/Sozsoft.Platform.Domain.Shared/TableNameResolver.cs +++ b/api/src/Sozsoft.Platform.Domain.Shared/TableNameResolver.cs @@ -52,9 +52,6 @@ public static class TableNameResolver { nameof(TableNameEnum.ForumCategory), (TablePrefix.TenantByName, MenuPrefix.Sas) }, { nameof(TableNameEnum.ForumTopic), (TablePrefix.TenantByName, MenuPrefix.Sas) }, { nameof(TableNameEnum.ForumPost), (TablePrefix.TenantByName, MenuPrefix.Sas) }, - { nameof(TableNameEnum.Home), (TablePrefix.PlatformByName, MenuPrefix.Sas) }, - { nameof(TableNameEnum.About), (TablePrefix.PlatformByName, MenuPrefix.Sas) }, - { nameof(TableNameEnum.Service), (TablePrefix.PlatformByName, MenuPrefix.Sas) }, { nameof(TableNameEnum.Product), (TablePrefix.PlatformByName, MenuPrefix.Sas) }, { nameof(TableNameEnum.PaymentMethod), (TablePrefix.PlatformByName, MenuPrefix.Sas) }, { nameof(TableNameEnum.InstallmentOption), (TablePrefix.PlatformByName, MenuPrefix.Sas) }, @@ -64,7 +61,6 @@ public static class TableNameResolver { nameof(TableNameEnum.BlogCategory), (TablePrefix.PlatformByName, MenuPrefix.Sas) }, { nameof(TableNameEnum.BlogPost), (TablePrefix.PlatformByName, MenuPrefix.Sas) }, { nameof(TableNameEnum.Demo), (TablePrefix.PlatformByName, MenuPrefix.Sas) }, - { nameof(TableNameEnum.Contact), (TablePrefix.PlatformByName, MenuPrefix.Sas) }, { nameof(TableNameEnum.Route), (TablePrefix.PlatformByName, MenuPrefix.Sas) }, { nameof(TableNameEnum.Module), (TablePrefix.PlatformByName, MenuPrefix.Sas) }, { nameof(TableNameEnum.Menu), (TablePrefix.PlatformByName, MenuPrefix.Sas) }, diff --git a/api/src/Sozsoft.Platform.Domain/Data/IAfterAllMigrationsSqlExecutor.cs b/api/src/Sozsoft.Platform.Domain/Data/IAfterAllMigrationsSqlExecutor.cs index 80200288..17075261 100644 --- a/api/src/Sozsoft.Platform.Domain/Data/IAfterAllMigrationsSqlExecutor.cs +++ b/api/src/Sozsoft.Platform.Domain/Data/IAfterAllMigrationsSqlExecutor.cs @@ -10,7 +10,7 @@ namespace Sozsoft.Platform.Data; public interface IAfterAllMigrationsSqlExecutor { /// - /// null ise HostData/ExecuteData, aksi halde TenantData/ExecuteData klasörü işlenir. + /// null ise host/, aksi halde tenants/{tenantId}/ kapsamındaki {sql|postgres}/execute klasörü işlenir. /// Task ExecuteAsync(Guid? tenantId = null); } diff --git a/api/src/Sozsoft.Platform.Domain/Data/SeedConsts.cs b/api/src/Sozsoft.Platform.Domain/Data/SeedConsts.cs index 75f9cd5a..11b207e8 100644 --- a/api/src/Sozsoft.Platform.Domain/Data/SeedConsts.cs +++ b/api/src/Sozsoft.Platform.Domain/Data/SeedConsts.cs @@ -422,10 +422,6 @@ public static class SeedConsts public const string Licence = Prefix.App + ".Licence"; public const string LicenceItem = Prefix.App + ".LicenceItem"; - //Web Site - public const string Home = Prefix.App + ".Home"; - public const string About = Prefix.App + ".About"; - public const string Services = Prefix.App + ".Services"; public static class Orders { public const string Default = Prefix.App + ".Orders"; @@ -444,19 +440,6 @@ public static class SeedConsts public const string BlogCategory = Default + ".Category"; } public const string Demos = Prefix.App + ".Demos"; - public const string Contact = Prefix.App + ".Contact"; - - /// - /// Public sitedeki sayfa tasarim modu (`?design=1`) yetkileri. - /// Yetkiler Migrations/PermissionsData.json icinde tanimlanir ve seeder ile yonetilir. - /// - public static class WebSiteDesign - { - public const string Home = AppCodes.Home + ".Design"; - public const string About = AppCodes.About + ".Design"; - public const string Services = AppCodes.Services + ".Design"; - public const string Contact = AppCodes.Contact + ".Design"; - } public static class Menus { diff --git a/api/src/Sozsoft.Platform.Domain/Data/SeedPathResolver.cs b/api/src/Sozsoft.Platform.Domain/Data/SeedPathResolver.cs index a0c0ca47..2bc9bd8c 100644 --- a/api/src/Sozsoft.Platform.Domain/Data/SeedPathResolver.cs +++ b/api/src/Sozsoft.Platform.Domain/Data/SeedPathResolver.cs @@ -8,8 +8,8 @@ namespace Sozsoft.Platform.Data.Seeds; /// /// Runtime'da duzenlenebilen seed dosyalarinin klasorunu cozer. /// -/// Kod ile birlikte versiyonlanan sabit seed'ler (ornegin TenantDataSeeder'in okudugu -/// TenantData.json) buraya dahil degildir; onlar kendi projelerinin ciktisinda kalir. +/// Kod ile birlikte versiyonlanan sabit seed'ler (DbMigrator projesindeki *Data.json dosyalari) +/// buraya dahil degildir; onlar kendi projelerinin ciktisinda kalir. /// /// /// Klasor bilincli olarak hicbir projenin icinde degildir: depoda configs/seeds altinda durur ve diff --git a/api/src/Sozsoft.Platform.Domain/Entities/Host/DeveloperKit/CustomComponent.cs b/api/src/Sozsoft.Platform.Domain/Entities/Host/DeveloperKit/CustomComponent.cs index 69cacf79..40f074ae 100644 --- a/api/src/Sozsoft.Platform.Domain/Entities/Host/DeveloperKit/CustomComponent.cs +++ b/api/src/Sozsoft.Platform.Domain/Entities/Host/DeveloperKit/CustomComponent.cs @@ -6,7 +6,8 @@ namespace Sozsoft.Platform.Entities; public class CustomComponent : FullAuditedEntity { public string Name { get; set; } = string.Empty; - public string RoutePath { get; set; } = string.Empty; + /// Rotasi olmayan bilesende (ust bilgi, alt bilgi) NULL kalir; tekil indeks dolu degerleri kapsar. + public string? RoutePath { get; set; } public string Code { get; set; } = string.Empty; public string? Props { get; set; } public string? Description { get; set; } @@ -21,7 +22,7 @@ public class CustomComponent : FullAuditedEntity /// public string? DataSources { get; set; } - public CustomComponent(string name, string routePath, string code, string? props, string? description, bool isActive, string? dependencies, string? dataSources = null) + public CustomComponent(string name, string? routePath, string code, string? props, string? description, bool isActive, string? dependencies, string? dataSources = null) { Name = name; RoutePath = routePath; diff --git a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/About.cs b/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/About.cs deleted file mode 100644 index 7799a04d..00000000 --- a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/About.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using Volo.Abp.Domain.Entities.Auditing; - -namespace Sozsoft.Platform.Entities; - -public class About : FullAuditedEntity -{ - public string StatsJson { get; set; } - public string DescriptionsJson { get; set; } - public string SectionsJson { get; set; } -} diff --git a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/Contact.cs b/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/Contact.cs deleted file mode 100644 index 0a6f9133..00000000 --- a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/Contact.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using Volo.Abp.Domain.Entities.Auditing; - -namespace Sozsoft.Platform.Entities; - -public class Contact : FullAuditedEntity -{ - public string Address { get; set; } - public string PhoneNumber { get; set; } - public string Email { get; set; } - public string Location { get; set; } - public long? TaxNumber { get; set; } - - // JSON string kolonlar - public string BankJson { get; set; } - public string WorkHoursJson { get; set; } - public string MapJson { get; set; } -} diff --git a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/Home.cs b/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/Home.cs deleted file mode 100644 index b98ba152..00000000 --- a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/Home.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Volo.Abp.Domain.Entities.Auditing; - -namespace Sozsoft.Platform.Entities; - -public class Home : FullAuditedEntity -{ - public string HeroBackgroundImageKey { get; set; } - public string HeroPrimaryCtaKey { get; set; } - public string HeroSecondaryCtaKey { get; set; } - public string FeaturesTitleKey { get; set; } - public string FeaturesSubtitleKey { get; set; } - public string SolutionsTitleKey { get; set; } - public string SolutionsSubtitleKey { get; set; } - public string CtaTitleKey { get; set; } - public string CtaSubtitleKey { get; set; } - public string CtaButtonLabelKey { get; set; } - public string SlidesJson { get; set; } - public string FeaturesJson { get; set; } - public string SolutionsJson { get; set; } -} diff --git a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/Service.cs b/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/Service.cs deleted file mode 100644 index 448b5097..00000000 --- a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Administration/Website/Service.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using Volo.Abp.Domain.Entities.Auditing; - -namespace Sozsoft.Platform.Entities; - -public class Service : FullAuditedEntity -{ - public string? Icon { get; set; } - public string Title { get; set; } - public string? Description { get; set; } - public string Type { get; set; } - - // JSON olarak saklanacak - public string[] Features { get; set; } = []; -} - - diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs index 066add2a..a9466fce 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs @@ -89,9 +89,6 @@ public class PlatformDbContext : public DbSet LinkUsers { get; set; } public DbSet UserDelegations { get; set; } public DbSet Sessions { get; set; } - public DbSet Abouts { get; set; } - public DbSet Homes { get; set; } - public DbSet Services { get; set; } public DbSet Products { get; set; } public DbSet PaymentMethods { get; set; } public DbSet InstallmentOptions { get; set; } @@ -100,7 +97,6 @@ public class PlatformDbContext : public DbSet BlogPosts { get; set; } public DbSet BlogCategories { get; set; } public DbSet Demos { get; set; } - public DbSet Contacts { get; set; } public DbSet Sectors { get; set; } public DbSet SkillTypes { get; set; } public DbSet DevRequests { get; set; } @@ -678,10 +674,10 @@ public class PlatformDbContext : b.ConfigureByConvention(); b.Property(x => x.Name).IsRequired().HasMaxLength(128); - b.Property(x => x.RoutePath).IsRequired().HasMaxLength(512); b.Property(x => x.Code).IsRequired(); // The visual designer document contains user-facing text. Keep it Unicode so // Turkish and other non-ASCII property values survive a database round-trip. + b.Property(x => x.RoutePath).HasMaxLength(512); b.Property(x => x.Props).HasColumnType("nvarchar(max)"); b.Property(x => x.Description).HasMaxLength(512); b.Property(x => x.Dependencies).HasMaxLength(2048); @@ -689,7 +685,9 @@ public class PlatformDbContext : b.Property(x => x.DataSources).HasColumnType("nvarchar(max)"); b.HasIndex(x => new { x.Name }).IsUnique().HasFilter("[IsDeleted] = 0"); - b.HasIndex(x => new { x.RoutePath }).IsUnique().HasFilter("[IsDeleted] = 0"); + // Rotasi olmayan bilesen (alt bilgi, ust bilgi) `RoutePath` tasimaz; tekil indeks + // yalnizca dolu degerleri kapsar, aksi halde her rotasiz bilesen digeriyle carpisir. + b.HasIndex(x => new { x.RoutePath }).IsUnique().HasFilter("[IsDeleted] = 0 AND [RoutePath] IS NOT NULL"); }); builder.Entity(b => @@ -957,64 +955,6 @@ public class PlatformDbContext : .OnDelete(DeleteBehavior.Restrict); }); - //Web Site - builder.Entity(b => - { - b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.About)), Prefix.DbSchema); - b.ConfigureByConvention(); - - b.Property(x => x.StatsJson).HasColumnType("nvarchar(max)"); - b.Property(x => x.DescriptionsJson).HasColumnType("nvarchar(max)"); - b.Property(x => x.SectionsJson).HasColumnType("nvarchar(max)"); - }); - - builder.Entity(b => - { - b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Home)), Prefix.DbSchema); - b.ConfigureByConvention(); - - b.Property(x => x.HeroBackgroundImageKey).HasMaxLength(256); - b.Property(x => x.HeroPrimaryCtaKey).HasMaxLength(256); - b.Property(x => x.HeroSecondaryCtaKey).HasMaxLength(256); - b.Property(x => x.FeaturesTitleKey).HasMaxLength(256); - b.Property(x => x.FeaturesSubtitleKey).HasMaxLength(256); - b.Property(x => x.SolutionsTitleKey).HasMaxLength(256); - b.Property(x => x.SolutionsSubtitleKey).HasMaxLength(256); - b.Property(x => x.CtaTitleKey).HasMaxLength(256); - b.Property(x => x.CtaSubtitleKey).HasMaxLength(256); - b.Property(x => x.CtaButtonLabelKey).HasMaxLength(256); - b.Property(x => x.SlidesJson).HasColumnType("nvarchar(max)"); - b.Property(x => x.FeaturesJson).HasColumnType("nvarchar(max)"); - b.Property(x => x.SolutionsJson).HasColumnType("nvarchar(max)"); - }); - - builder.Entity(b => - { - b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Service)), Prefix.DbSchema); - b.ConfigureByConvention(); - - b.Property(x => x.Title).IsRequired().HasMaxLength(128); - b.Property(x => x.Description).HasMaxLength(512); - b.Property(x => x.Icon).HasMaxLength(64); - b.Property(x => x.Type).HasMaxLength(64); - - b.Property(x => x.Features) - .HasConversion( - v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null), - v => JsonSerializer.Deserialize(v, (JsonSerializerOptions)null) - ) - .Metadata - .SetValueComparer( - new ValueComparer( - (a, b) => a.SequenceEqual(b), - a => a.Aggregate(0, (c, v) => HashCode.Combine(c, v.GetHashCode())), - a => a.ToArray() - ) - ); - - b.HasIndex(x => new { x.Title }).IsUnique().HasFilter("[IsDeleted] = 0"); - }); - builder.Entity(b => { b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Product)), Prefix.DbSchema); @@ -1165,21 +1105,6 @@ public class PlatformDbContext : b.Property(x => x.Message).IsRequired().HasMaxLength(2048); }); - builder.Entity(b => - { - b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Contact)), Prefix.DbSchema); - b.ConfigureByConvention(); - - b.Property(x => x.Address).HasMaxLength(512); - b.Property(x => x.PhoneNumber).HasMaxLength(20); - b.Property(x => x.Email).HasMaxLength(128); - b.Property(x => x.Location).HasMaxLength(256); - - b.Property(x => x.BankJson).HasColumnType("nvarchar(max)"); - b.Property(x => x.WorkHoursJson).HasColumnType("nvarchar(max)"); - b.Property(x => x.MapJson).HasColumnType("nvarchar(max)"); - }); - builder.Entity(b => { b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.LogEntry)), Prefix.DbSchema); diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260904084415_Initial.Designer.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260908125150_Initial.Designer.cs similarity index 97% rename from api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260904084415_Initial.Designer.cs rename to api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260908125150_Initial.Designer.cs index 496baf07..4ad9e28d 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260904084415_Initial.Designer.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260908125150_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace Sozsoft.Platform.Migrations { [DbContext(typeof(PlatformDbContext))] - [Migration("20260904084415_Initial")] + [Migration("20260908125150_Initial")] partial class Initial { /// @@ -630,55 +630,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Sas_H_NotificationType", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.About", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("DescriptionsJson") - .HasColumnType("nvarchar(max)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("SectionsJson") - .HasColumnType("nvarchar(max)"); - - b.Property("StatsJson") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Sas_H_About", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Agenda", b => { b.Property("Id") @@ -1505,74 +1456,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Sas_H_City", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Contact", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("Address") - .HasMaxLength(512) - .HasColumnType("nvarchar(512)"); - - b.Property("BankJson") - .HasColumnType("nvarchar(max)"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("Email") - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("Location") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("MapJson") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - b.Property("TaxNumber") - .HasColumnType("bigint"); - - b.Property("WorkHoursJson") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Sas_H_Contact", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.ContactTitle", b => { b.Property("Id") @@ -1962,7 +1845,6 @@ namespace Sozsoft.Platform.Migrations .HasColumnType("nvarchar(max)"); b.Property("RoutePath") - .IsRequired() .HasMaxLength(512) .HasColumnType("nvarchar(512)"); @@ -1974,7 +1856,7 @@ namespace Sozsoft.Platform.Migrations b.HasIndex("RoutePath") .IsUnique() - .HasFilter("[IsDeleted] = 0"); + .HasFilter("[IsDeleted] = 0 AND [RoutePath] IS NOT NULL"); b.ToTable("Sas_H_CustomComponent", (string)null); }); @@ -2957,95 +2839,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Sas_T_GlobalSearch", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Home", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("CtaButtonLabelKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("CtaSubtitleKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("CtaTitleKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("FeaturesJson") - .HasColumnType("nvarchar(max)"); - - b.Property("FeaturesSubtitleKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("FeaturesTitleKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("HeroBackgroundImageKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("HeroPrimaryCtaKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("HeroSecondaryCtaKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("SlidesJson") - .HasColumnType("nvarchar(max)"); - - b.Property("SolutionsJson") - .HasColumnType("nvarchar(max)"); - - b.Property("SolutionsSubtitleKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("SolutionsTitleKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.ToTable("Sas_H_Home", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.InstallmentOption", b => { b.Property("Id") @@ -5087,70 +4880,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Adm_T_Sector", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Service", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("Description") - .HasMaxLength(512) - .HasColumnType("nvarchar(512)"); - - b.Property("Features") - .HasColumnType("nvarchar(max)"); - - b.Property("Icon") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("Type") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("Id"); - - b.HasIndex("Title") - .IsUnique() - .HasFilter("[IsDeleted] = 0"); - - b.ToTable("Sas_H_Service", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Skill", b => { b.Property("Id") diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260904084415_Initial.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260908125150_Initial.cs similarity index 97% rename from api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260904084415_Initial.cs rename to api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260908125150_Initial.cs index 91facf3f..d880ff02 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260904084415_Initial.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260908125150_Initial.cs @@ -922,27 +922,6 @@ namespace Sozsoft.Platform.Migrations table.PrimaryKey("PK_OpenIddictScopes", x => x.Id); }); - migrationBuilder.CreateTable( - name: "Sas_H_About", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - StatsJson = table.Column(type: "nvarchar(max)", nullable: true), - DescriptionsJson = table.Column(type: "nvarchar(max)", nullable: true), - SectionsJson = table.Column(type: "nvarchar(max)", nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Sas_H_About", x => x.Id); - }); - migrationBuilder.CreateTable( name: "Sas_H_AiBot", columns: table => new @@ -1059,32 +1038,6 @@ namespace Sozsoft.Platform.Migrations table.PrimaryKey("PK_Sas_H_ChangeLog", x => x.Id); }); - migrationBuilder.CreateTable( - name: "Sas_H_Contact", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Address = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), - PhoneNumber = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: true), - Email = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: true), - Location = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - TaxNumber = table.Column(type: "bigint", nullable: true), - BankJson = table.Column(type: "nvarchar(max)", nullable: true), - WorkHoursJson = table.Column(type: "nvarchar(max)", nullable: true), - MapJson = table.Column(type: "nvarchar(max)", nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Sas_H_Contact", x => x.Id); - }); - migrationBuilder.CreateTable( name: "Sas_H_ContactTitle", columns: table => new @@ -1178,7 +1131,7 @@ namespace Sozsoft.Platform.Migrations { Id = table.Column(type: "uniqueidentifier", nullable: false), Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - RoutePath = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: false), + RoutePath = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), Code = table.Column(type: "nvarchar(max)", nullable: false), Props = table.Column(type: "nvarchar(max)", nullable: true), Description = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), @@ -1361,37 +1314,6 @@ namespace Sozsoft.Platform.Migrations table.PrimaryKey("PK_Sas_H_DynamicService", x => x.Id); }); - migrationBuilder.CreateTable( - name: "Sas_H_Home", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - HeroBackgroundImageKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - HeroPrimaryCtaKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - HeroSecondaryCtaKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - FeaturesTitleKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - FeaturesSubtitleKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - SolutionsTitleKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - SolutionsSubtitleKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - CtaTitleKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - CtaSubtitleKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - CtaButtonLabelKey = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - SlidesJson = table.Column(type: "nvarchar(max)", nullable: true), - FeaturesJson = table.Column(type: "nvarchar(max)", nullable: true), - SolutionsJson = table.Column(type: "nvarchar(max)", nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Sas_H_Home", x => x.Id); - }); - migrationBuilder.CreateTable( name: "Sas_H_InstallmentOption", columns: table => new @@ -1725,29 +1647,6 @@ namespace Sozsoft.Platform.Migrations table.PrimaryKey("PK_Sas_H_Route", x => x.Id); }); - migrationBuilder.CreateTable( - name: "Sas_H_Service", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Icon = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - Title = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - Description = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), - Type = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - Features = table.Column(type: "nvarchar(max)", nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Sas_H_Service", x => x.Id); - }); - migrationBuilder.CreateTable( name: "Sas_H_SettingDefinition", columns: table => new @@ -4167,7 +4066,7 @@ namespace Sozsoft.Platform.Migrations table: "Sas_H_CustomComponent", column: "RoutePath", unique: true, - filter: "[IsDeleted] = 0"); + filter: "[IsDeleted] = 0 AND [RoutePath] IS NOT NULL"); migrationBuilder.CreateIndex( name: "IX_Sas_H_CustomEndpoint_Name", @@ -4322,13 +4221,6 @@ namespace Sozsoft.Platform.Migrations unique: true, filter: "[IsDeleted] = 0"); - migrationBuilder.CreateIndex( - name: "IX_Sas_H_Service_Title", - table: "Sas_H_Service", - column: "Title", - unique: true, - filter: "[IsDeleted] = 0"); - migrationBuilder.CreateIndex( name: "IX_Sas_H_Skill_SkillTypeId_Name", table: "Sas_H_Skill", @@ -4562,9 +4454,6 @@ namespace Sozsoft.Platform.Migrations migrationBuilder.DropTable( name: "OpenIddictTokens"); - migrationBuilder.DropTable( - name: "Sas_H_About"); - migrationBuilder.DropTable( name: "Sas_H_AiBot"); @@ -4580,9 +4469,6 @@ namespace Sozsoft.Platform.Migrations migrationBuilder.DropTable( name: "Sas_H_ChangeLog"); - migrationBuilder.DropTable( - name: "Sas_H_Contact"); - migrationBuilder.DropTable( name: "Sas_H_ContactTitle"); @@ -4616,9 +4502,6 @@ namespace Sozsoft.Platform.Migrations migrationBuilder.DropTable( name: "Sas_H_DynamicService"); - migrationBuilder.DropTable( - name: "Sas_H_Home"); - migrationBuilder.DropTable( name: "Sas_H_InstallmentOption"); @@ -4658,9 +4541,6 @@ namespace Sozsoft.Platform.Migrations migrationBuilder.DropTable( name: "Sas_H_Route"); - migrationBuilder.DropTable( - name: "Sas_H_Service"); - migrationBuilder.DropTable( name: "Sas_H_SettingDefinition"); diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs index 3bbab29a..b192088a 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs @@ -627,55 +627,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Sas_H_NotificationType", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.About", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("DescriptionsJson") - .HasColumnType("nvarchar(max)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("SectionsJson") - .HasColumnType("nvarchar(max)"); - - b.Property("StatsJson") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Sas_H_About", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Agenda", b => { b.Property("Id") @@ -1502,74 +1453,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Sas_H_City", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Contact", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("Address") - .HasMaxLength(512) - .HasColumnType("nvarchar(512)"); - - b.Property("BankJson") - .HasColumnType("nvarchar(max)"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("Email") - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("Location") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("MapJson") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - b.Property("TaxNumber") - .HasColumnType("bigint"); - - b.Property("WorkHoursJson") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Sas_H_Contact", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.ContactTitle", b => { b.Property("Id") @@ -1959,7 +1842,6 @@ namespace Sozsoft.Platform.Migrations .HasColumnType("nvarchar(max)"); b.Property("RoutePath") - .IsRequired() .HasMaxLength(512) .HasColumnType("nvarchar(512)"); @@ -1971,7 +1853,7 @@ namespace Sozsoft.Platform.Migrations b.HasIndex("RoutePath") .IsUnique() - .HasFilter("[IsDeleted] = 0"); + .HasFilter("[IsDeleted] = 0 AND [RoutePath] IS NOT NULL"); b.ToTable("Sas_H_CustomComponent", (string)null); }); @@ -2954,95 +2836,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Sas_T_GlobalSearch", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Home", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("CtaButtonLabelKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("CtaSubtitleKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("CtaTitleKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("FeaturesJson") - .HasColumnType("nvarchar(max)"); - - b.Property("FeaturesSubtitleKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("FeaturesTitleKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("HeroBackgroundImageKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("HeroPrimaryCtaKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("HeroSecondaryCtaKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("SlidesJson") - .HasColumnType("nvarchar(max)"); - - b.Property("SolutionsJson") - .HasColumnType("nvarchar(max)"); - - b.Property("SolutionsSubtitleKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("SolutionsTitleKey") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.ToTable("Sas_H_Home", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.InstallmentOption", b => { b.Property("Id") @@ -5084,70 +4877,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Adm_T_Sector", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Service", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("Description") - .HasMaxLength(512) - .HasColumnType("nvarchar(512)"); - - b.Property("Features") - .HasColumnType("nvarchar(max)"); - - b.Property("Icon") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("nvarchar(128)"); - - b.Property("Type") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.HasKey("Id"); - - b.HasIndex("Title") - .IsUnique() - .HasFilter("[IsDeleted] = 0"); - - b.ToTable("Sas_H_Service", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.Skill", b => { b.Property("Id") diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.Dev.json b/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.Dev.json deleted file mode 100644 index 0f530c14..00000000 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.Dev.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.Production.json b/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.Production.json deleted file mode 100644 index 0f530c14..00000000 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.json b/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.json deleted file mode 100644 index 44968c73..00000000 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.json +++ /dev/null @@ -1,360 +0,0 @@ -{ - "Homes": [ - { - "heroBackgroundImageKey": "Public.home.hero.backgroundImage", - "heroPrimaryCtaKey": "App.HeroCta.Consultation", - "heroSecondaryCtaKey": "App.HeroCta.Discover", - "featuresTitleKey": "App.PublicFeatures.FeaturesTitle", - "featuresSubtitleKey": "App.PublicFeatures.Subtitle", - "solutionsTitleKey": "App.PublicSolutions.SolutionsTitle", - "solutionsSubtitleKey": "App.PublicSolutions.Subtitle", - "ctaTitleKey": "App.PublicCommon.GetStarted", - "ctaSubtitleKey": "App.PublicCommon.Contact", - "ctaButtonLabelKey": "App.PublicCommon.LearnMore", - "slides": [ - { - "titleKey": "App.HeroSlide1.Slide1Title", - "subtitleKey": "App.HeroSlide1.Subtitle", - "services": [ - { - "icon": "FaCalendarAlt", - "titleKey": "App.Slide1Service1.Service1Title", - "descriptionKey": "App.Slide1Service1.Service1Desc" - }, - { - "icon": "FaUsers", - "titleKey": "App.Slide1Service2.Service2Title", - "descriptionKey": "App.Slide1Service2.Service2Desc" - }, - { - "icon": "FaShieldAlt", - "titleKey": "App.Slide1Service3.Service3Title", - "descriptionKey": "App.Slide1Service3.Service3Desc" - } - ] - }, - { - "titleKey": "App.HeroSlide2.Slide2Title", - "subtitleKey": "App.HeroSlide2.Subtitle", - "services": [ - { - "icon": "FaChartBar", - "titleKey": "App.Slide2Service1.Service1Title", - "descriptionKey": "App.Slide2Service1.Service1Desc" - }, - { - "icon": "FaCreditCard", - "titleKey": "App.Slide2Service2.Service2Title", - "descriptionKey": "App.Slide2Service2.Service2Desc" - }, - { - "icon": "FaDatabase", - "titleKey": "App.Slide2Service3.Service3Title", - "descriptionKey": "App.Slide2Service3.Service3Desc" - } - ] - }, - { - "titleKey": "App.HeroSlide3.Slide3Title", - "subtitleKey": "App.HeroSlide3.Subtitle", - "services": [ - { - "icon": "FaDesktop", - "titleKey": "App.Slide3Service1.Service1Title", - "descriptionKey": "App.Slide3Service1.Service1Desc" - }, - { - "icon": "FaServer", - "titleKey": "App.Slide3Service2.Service2Title", - "descriptionKey": "App.Slide3Service2.Service2Desc" - }, - { - "icon": "FaMobileAlt", - "titleKey": "App.Slide3Service3.Service3Title", - "descriptionKey": "App.Slide3Service3.Service3Desc" - } - ] - } - ], - "features": [ - { - "icon": "FaUsers", - "titleKey": "App.PublicFeatures.Reliable", - "descriptionKey": "App.FeaturesReliable.ReliableDesc" - }, - { - "icon": "FaCalendarAlt", - "titleKey": "App.VideoRoom.Planning", - "descriptionKey": "App.FeaturesRapid.RapidDesc" - }, - { - "icon": "FaBookOpen", - "titleKey": "App.PublicFeatures.Expert", - "descriptionKey": "App.FeaturesExpert.ExpertDesc" - }, - { - "icon": "FaCreditCard", - "titleKey": "App.PublicFeatures.Muhasebe", - "descriptionKey": "App.FeaturesMuhasebe.MuhasebeDesc" - }, - { - "icon": "FaRegComment", - "titleKey": "App.PublicFeatures.Iletisim", - "descriptionKey": "App.FeaturesIletisim.IletisimDesc" - }, - { - "icon": "FaPhone", - "titleKey": "App.PublicFeatures.Mobil", - "descriptionKey": "App.FeaturesMobil.MobilDesc" - }, - { - "icon": "FaChartBar", - "titleKey": "App.PublicFeatures.Scalable", - "descriptionKey": "App.FeaturesScalable.ScalableDesc" - }, - { - "icon": "FaShieldAlt", - "titleKey": "App.Listform.ListformField.Security", - "descriptionKey": "App.FeaturesGuvenlik.GuvenlikDesc" - } - ], - "solutions": [ - { - "icon": "FaDesktop", - "colorClass": "bg-blue-600", - "titleKey": "App.BlogCategories.Webdev", - "descriptionKey": "App.SolutionsWeb.WebDesc" - }, - { - "icon": "FaMobileAlt", - "colorClass": "bg-purple-600", - "titleKey": "App.ServicesMobile.MobileTitle", - "descriptionKey": "App.SolutionsMobile.MobileDesc" - }, - { - "icon": "FaServer", - "colorClass": "bg-green-600", - "titleKey": "App.SolutionsCustom.CustomTitle", - "descriptionKey": "App.SolutionsCustom.CustomDesc" - }, - { - "icon": "FaDatabase", - "colorClass": "bg-red-600", - "titleKey": "App.SolutionsDatabase.DatabaseTitle", - "descriptionKey": "App.SolutionsDatabase.DatabaseDesc" - } - ] - } - ], - "Abouts": [ - { - "stats": [ - { - "Icon": "FaUser", - "Value": "740\u002B", - "LabelKey": "App.AboutStats.Users", - "UseCounter": true, - "CounterEnd": "740\u002B", - "CounterSuffix": "\u002B", - "CounterDuration": 3000 - }, - { - "icon": "FaUsers", - "value": "310+", - "labelKey": "App.AboutStats.Clients", - "useCounter": true, - "counterEnd": "310+", - "counterSuffix": "+", - "counterDuration": 2500 - }, - { - "icon": "FaAward", - "value": "25", - "labelKey": "App.AboutStats.Experience", - "useCounter": true, - "counterEnd": "20", - "counterSuffix": "+", - "counterDuration": 2000 - }, - { - "icon": "FaClock", - "value": "7/24", - "labelKey": "App.AboutStats.Support", - "useCounter": false - }, - { - "icon": "FaGlobe", - "value": "3", - "labelKey": "App.Definitions.Country", - "useCounter": true, - "counterEnd": "3", - "counterDuration": 1500 - } - ], - "descriptions": [ - "App.AboutDescription.Part1", - "App.AboutDescription.Motto", - "App.AboutDescription.Part2", - "App.AboutDescription.Closing" - ], - "sections": [ - { - "key": "App.PublicAbout.Mission", - "descKey": "App.AboutMission.MissionDesc" - }, - { - "key": "App.PublicAbout.Vision", - "descKey": "App.AboutVision.VisionDesc" - } - ] - } - ], - "Services": [ - { - "icon": "FaCode", - "title": "App.ServicesSoftware.SoftwareTitle", - "description": "App.ServicesSoftware.SoftwareDesc", - "type": "service", - "features": [ - "App.SoftwareFeatures.Analysis", - "App.SoftwareFeatures.Design", - "App.SoftwareFeatures.Development", - "App.SoftwareFeatures.Testing", - "App.SoftwareFeatures.Maintenance" - ] - }, - { - "icon": "FaUsers", - "title": "App.BlogCategories.Webdev", - "description": "App.ServicesWeb.WebDesc", - "type": "service", - "features": [ - "App.WebFeatures.Frontend", - "App.WebFeatures.Backend", - "App.WebFeatures.Api", - "App.WebFeatures.Seo", - "App.WebFeatures.Performance" - ] - }, - { - "icon": "FaShieldAlt", - "title": "App.ServicesMobile.MobileTitle", - "description": "App.ServicesMobile.MobileDesc", - "type": "service", - "features": [ - "App.MobileFeatures.Design", - "App.MobileFeatures.Native", - "App.MobileFeatures.Cross", - "App.MobileFeatures.Push", - "App.MobileFeatures.Store" - ] - }, - { - "icon": "FaServer", - "title": "App.ServicesDatabase.DatabaseTitle", - "description": "App.ServicesDatabase.DatabaseDesc", - "type": "service", - "features": [ - "App.DatabaseFeatures.Design", - "App.DatabaseFeatures.Optimization", - "App.DatabaseFeatures.Migration", - "App.DatabaseFeatures.Backup", - "App.DatabaseFeatures.Recovery" - ] - }, - { - "icon": "FaGlobe", - "title": "App.ServicesIntegration.IntegrationTitle", - "description": "App.ServicesIntegration.IntegrationDesc", - "type": "service", - "features": [ - "App.IntegrationFeatures.Api", - "App.IntegrationFeatures.Middleware", - "App.IntegrationFeatures.Legacy", - "App.IntegrationFeatures.Realtime", - "App.IntegrationFeatures.Monitoring" - ] - }, - { - "icon": "FaCog", - "title": "App.ServicesConsulting.ConsultingTitle", - "description": "App.ServicesConsulting.ConsultingDesc", - "type": "service", - "features": [ - "App.ConsultingFeatures.Tech", - "App.ConsultingFeatures.Project", - "App.ConsultingFeatures.Digital", - "App.ConsultingFeatures.Risk", - "App.ConsultingFeatures.Training" - ] - }, - { - "icon": "FaUsers", - "title": "App.SupportBranchRemote.BranchRemoteTitle", - "description": null, - "type": "support", - "features": [ - "App.BranchRemoteFeatures.Priority", - "App.BranchRemoteFeatures.Remote", - "App.BranchRemoteFeatures.Optimization", - "App.BranchRemoteFeatures.Maintenance", - "App.BranchRemoteFeatures.Consulting" - ] - }, - { - "icon": "FaServer", - "title": "App.SupportBackup.BackupTitle", - "description": null, - "type": "support", - "features": [ - "App.BackupFeatures.Daily", - "App.BackupFeatures.Encrypted", - "App.BackupFeatures.Recovery", - "App.BackupFeatures.Verification", - "App.BackupFeatures.Access" - ] - }, - { - "icon": "FaGlobe", - "title": "App.SupportSms.SmsTitle", - "description": null, - "type": "support", - "features": [ - "App.SmsFeatures.Packages", - "App.SmsFeatures.Bulk", - "App.SmsFeatures.Template", - "App.SmsFeatures.Reporting", - "App.SmsFeatures.Api" - ] - } - ], - "Contacts": [ - { - "address": "App.ContactAddress.Full", - "phoneNumber": "5447697638", - "email": "destek@sozsoft.com", - "location": "Sarıgazi", - "taxNumber": "32374982750", - "bank": { - "accountHolder": "Özlem Öztürk", - "branch": "03663 / Enpara", - "accountNumber": "73941177", - "iban": "TR11 0015 7000 0000 0073 9411 77" - }, - "workHour": { - "weekday": "App.ContactWorkHours.Weekday", - "weekend": "App.ContactWorkHours.Weekend", - "whatsapp": "App.ContactWorkHours.Whatsapp" - }, - "map": { - "title": "App.PublicContact.Location", - "src": "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3006.209566407676!2d28.757000999999992!3d41.10811400000001!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x0%3A0x8a0bbbdfcfd3fd24!2zU8O2enNvZnQ!5e0!3m2!1str!2str!4v1450816303558", - "width": "100%", - "height": "700", - "allowFullScreen": true, - "loading": "lazy", - "referrerPolicy": "no-referrer-when-downgrade" - } - } - ] -} diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantDataSeeder.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantDataSeeder.cs deleted file mode 100644 index 8c72c1f7..00000000 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantDataSeeder.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.Json; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Sozsoft.Platform.Entities; -using Sozsoft.Platform.Enums; -using Sozsoft.Platform.Forum; -using Sozsoft.Platform.Public; -using Volo.Abp.Data; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Domain.Repositories; -using Volo.Abp.Identity; - -namespace Sozsoft.Platform.Data.Seeds; - -public class TenantSeederDto -{ - //Saas - - //Definitions - - //Public - public List Homes { get; set; } - public List Abouts { get; set; } - public List Services { get; set; } - public List Contacts { get; set; } - - - //Forum - - //Intranet -} - -public class ServiceSeedDto -{ - public string Icon { get; set; } - public string Title { get; set; } - public string Description { get; set; } - public string Type { get; set; } - public string[] Features { get; set; } -} - -public class AboutSeedDto -{ - public List Stats { get; set; } - public List Descriptions { get; set; } - public List Sections { get; set; } -} - -public class HomeSeedDto -{ - public string HeroBackgroundImageKey { get; set; } - public string HeroPrimaryCtaKey { get; set; } - public string HeroSecondaryCtaKey { get; set; } - public string FeaturesTitleKey { get; set; } - public string FeaturesSubtitleKey { get; set; } - public string SolutionsTitleKey { get; set; } - public string SolutionsSubtitleKey { get; set; } - public string CtaTitleKey { get; set; } - public string CtaSubtitleKey { get; set; } - public string CtaButtonLabelKey { get; set; } - public List Slides { get; set; } - public List Features { get; set; } - public List Solutions { get; set; } -} - -public class HomeSlideSeedDto -{ - public string TitleKey { get; set; } - public string SubtitleKey { get; set; } - public string StyleClass { get; set; } - public List Services { get; set; } -} - -public class HomeSlideServiceSeedDto -{ - public string Icon { get; set; } - public string TitleKey { get; set; } - public string DescriptionKey { get; set; } - public string StyleClass { get; set; } -} - -public class HomeFeatureSeedDto -{ - public string Icon { get; set; } - public string TitleKey { get; set; } - public string DescriptionKey { get; set; } - public string StyleClass { get; set; } -} - -public class HomeSolutionSeedDto -{ - public string Icon { get; set; } - public string ColorClass { get; set; } - public string TitleKey { get; set; } - public string DescriptionKey { get; set; } - public string StyleClass { get; set; } -} - -public class ContactSeedDto -{ - public string Address { get; set; } - public string PhoneNumber { get; set; } - public string Email { get; set; } - public string Location { get; set; } - public long TaxNumber { get; set; } - public BankDto Bank { get; set; } - public WorkHoursDto WorkHour { get; set; } - public MapDto Map { get; set; } -} - -public class TenantDataSeeder : IDataSeedContributor, ITransientDependency -{ - private readonly ILookupNormalizer _lookupNormalizer; - private readonly IRepository _branchRepository; - private readonly IRepository _homeRepository; - private readonly IRepository _aboutRepository; - private readonly IRepository _servicesRepository; - private readonly IRepository _contactRepository; - private readonly IIdentityUserRepository _repositoryUser; - - - - public TenantDataSeeder( - ILookupNormalizer lookupNormalizer, - IIdentityUserRepository repositoryUser, - IRepository branchRepository, - IRepository homeRepository, - IRepository aboutRepository, - IRepository servicesRepository, - IRepository contactRepository - - ) - { - _lookupNormalizer = lookupNormalizer; - _repositoryUser = repositoryUser; - _branchRepository = branchRepository; - _homeRepository = homeRepository; - _servicesRepository = servicesRepository; - _aboutRepository = aboutRepository; - _contactRepository = contactRepository; - } - - public async Task SeedAsync(DataSeedContext context) - { - var assemblyLocation = Path.GetDirectoryName(typeof(TenantDataSeeder).Assembly.Location)!; - - var configuration = new ConfigurationBuilder() - .SetBasePath(assemblyLocation) - .AddJsonFile(Path.Combine("Seeds", "TenantData.json"), optional: false, reloadOnChange: false) - .AddJsonFile(Path.Combine("Seeds", $"TenantData.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? ""}.json"), optional: true) - .Build(); - - var items = configuration.Get(); - - var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; - - foreach (var item in items.Services) - { - var exists = await _servicesRepository.AnyAsync(x => x.Title == item.Title); - - if (!exists) - { - await _servicesRepository.InsertAsync(new Service - { - Icon = item.Icon, - Title = item.Title, - Description = item.Description, - Type = item.Type, - Features = item.Features - }); - } - } - - foreach (var item in items.Abouts) - { - var exists = await _aboutRepository.FirstOrDefaultAsync(); - if (exists == null) - { - await _aboutRepository.InsertAsync(new About - { - StatsJson = JsonSerializer.Serialize(item.Stats), - DescriptionsJson = JsonSerializer.Serialize(item.Descriptions), - SectionsJson = JsonSerializer.Serialize(item.Sections) - }); - } - } - - foreach (var item in items.Homes) - { - var exists = await _homeRepository.FirstOrDefaultAsync(); - if (exists == null) - { - await _homeRepository.InsertAsync(new Home - { - HeroBackgroundImageKey = item.HeroBackgroundImageKey, - HeroPrimaryCtaKey = item.HeroPrimaryCtaKey, - HeroSecondaryCtaKey = item.HeroSecondaryCtaKey, - FeaturesTitleKey = item.FeaturesTitleKey, - FeaturesSubtitleKey = item.FeaturesSubtitleKey, - SolutionsTitleKey = item.SolutionsTitleKey, - SolutionsSubtitleKey = item.SolutionsSubtitleKey, - CtaTitleKey = item.CtaTitleKey, - CtaSubtitleKey = item.CtaSubtitleKey, - CtaButtonLabelKey = item.CtaButtonLabelKey, - SlidesJson = JsonSerializer.Serialize(item.Slides), - FeaturesJson = JsonSerializer.Serialize(item.Features), - SolutionsJson = JsonSerializer.Serialize(item.Solutions) - }); - } - } - - foreach (var item in items.Contacts) - { - var exists = await _contactRepository.FirstOrDefaultAsync(); - if (exists == null) - { - await _contactRepository.InsertAsync(new Contact - { - Address = item.Address, - PhoneNumber = item.PhoneNumber, - Email = item.Email, - Location = item.Location, - TaxNumber = item.TaxNumber, - BankJson = JsonSerializer.Serialize(item.Bank), - WorkHoursJson = JsonSerializer.Serialize(item.WorkHour), - MapJson = JsonSerializer.Serialize(item.Map) - }); - } - } - - } -} diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Sozsoft.Platform.EntityFrameworkCore.csproj b/api/src/Sozsoft.Platform.EntityFrameworkCore/Sozsoft.Platform.EntityFrameworkCore.csproj index cd79736e..698c0e33 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Sozsoft.Platform.EntityFrameworkCore.csproj +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Sozsoft.Platform.EntityFrameworkCore.csproj @@ -7,22 +7,6 @@ Sozsoft.Platform - - - - PreserveNewest - Always - - - PreserveNewest - Always - - - PreserveNewest - Always - - - diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/Extensions/PlatformApplicationConfigurationContributor.cs b/api/src/Sozsoft.Platform.HttpApi.Host/Extensions/PlatformApplicationConfigurationContributor.cs index ab6f70a5..0ff03bee 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/Extensions/PlatformApplicationConfigurationContributor.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/Extensions/PlatformApplicationConfigurationContributor.cs @@ -53,6 +53,28 @@ public class PlatformApplicationConfigurationContributor : IApplicationConfigura context.ApplicationConfiguration.SetProperty( "customComponents", await customComponentRepository.GetListAsync()); + // Public site menusu: ziyaretci menu ucunu cagiramaz ([Authorize]), bu yuzden + // rotalar ve custom component'ler gibi anonim erisilen konfigurasyonla tasinir. + var menuRepository = context.ServiceProvider.GetRequiredService>(); + var publicMenus = await menuRepository.GetListAsync( + menu => menu.ModuleId == PlatformConsts.PublicSite.ModuleId && !menu.IsDisabled); + + context.ApplicationConfiguration.SetProperty("publicMenus", publicMenus + .OrderBy(menu => menu.Order) + .Select(menu => new + { + code = menu.Code, + displayName = menu.DisplayName, + url = menu.Url, + icon = menu.Icon, + order = menu.Order, + parentCode = menu.ParentCode, + cssClass = menu.CssClass, + target = menu.Target, + requiredPermissionName = menu.RequiredPermissionName + }) + .ToList()); + var currentUser = context.ServiceProvider.GetRequiredService(); if (currentUser.Id.HasValue) { diff --git a/claude.md b/claude.md index c7a6513b..abd3d71f 100644 --- a/claude.md +++ b/claude.md @@ -184,6 +184,10 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` → `permission.constant.ts` + `LanguagesData.json`. - **Yeni bir dil anahtarı** → `LanguagesData.json`; eklemeden önce anahtarın zaten var olup olmadığını kontrol et. +- **Custom component sınıfları** → bileşen kodu veritabanında yaşar, Tailwind derlemede yalnızca + 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 @@ -234,6 +238,23 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` → kullanıcının runtime revizyonunu ezmez. Yeni bir seeder yazarken bu kuralı taklit et: **migrate varsayılanı eklemektir, ezmek değil.** - **`MenusData.json`** → `Routes` bölümüne dokunma; yalnızca `Modules` ve `Menus`. +- **Public site** → menü `Menu` tablosunda `ModuleId = "Pub"` altında (`PlatformConsts.PublicSite`); + `App.Public.Header` hem üst menüyü hem alt bilgideki hızlı bağlantıları besler (ikisi de + `PublicNav` düğümü) ve ziyaretçiye uygulama konfigürasyonunun `publicMenus` alanıyla taşınır + (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. + Ü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, + yerleşimi tasarımcıdadır. + **Public bileşenlerin hepsi tasarımcı belgesidir** (`Props.visualDesigner`, `sourceMode: visual`); + kod belgeden üretilir (`generateDesignerCode`), elle yazılmış koda dönülmez. Runtime kapsamında + `react-icons`/`react-router` yoktur — ikon `PlatformIcon` düğümü, gezinme `a` düğümü. Kanvasın + doğrudan çizdiği katalog düğümleri tek sözlüktedir (`componentEditor/runtimeNodes.ts`); yeni bir + düğüm eklerken bileşen + bu sözlük + `catalog.ts` tanımı + dil anahtarı birlikte yazılır. Elle + yazılmış bir bileşen tasarımcıda kod modunda açılır, kanvasa çevrilmez. Ayrıntı: `README.md` §10.4. - **Çok değerli alan (`dxTagBox` / çoklu `dxGridBox`)** → tek metin kolonunda `|` ile saklanır. Ekran varsayılan `list-form-data/*` ucundan kaydediyorsa birleştirmeyi `QueryHelper` yapar; tipli DTO alan bir uca (`list-form-dynamic-api/...`, Custom Endpoint, Dynamic Service) diff --git a/configs/seeds/host/data/App.DeveloperKit.CustomComponents.json b/configs/seeds/host/data/App.DeveloperKit.CustomComponents.json index 17c72cbd..3eee97a4 100644 --- a/configs/seeds/host/data/App.DeveloperKit.CustomComponents.json +++ b/configs/seeds/host/data/App.DeveloperKit.CustomComponents.json @@ -1,7 +1,7 @@ { "ListFormCode": "App.DeveloperKit.CustomComponents", "KeyFieldName": "Id", - "GeneratedAt": "2026-09-07T14:42:42.8639631Z", + "GeneratedAt": "2026-09-08T13:11:35.4554636Z", "Order": 0, "Rows": [ { @@ -133,6 +133,61 @@ "SeedFile": "crud/AbpUsers.json" } ] + }, + { + "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 + ) +} + +export default CanvasFrame diff --git a/ui/src/components/visualDesigner/VisualCanvas.tsx b/ui/src/components/visualDesigner/VisualCanvas.tsx index f3eec294..9d884b8d 100644 --- a/ui/src/components/visualDesigner/VisualCanvas.tsx +++ b/ui/src/components/visualDesigner/VisualCanvas.tsx @@ -1,6 +1,7 @@ import React from 'react' import * as UiKit from '@/components/ui' import PlatformIcon from '@/components/componentEditor/PlatformIcon' +import { RUNTIME_NODE_COMPONENTS } from '@/components/componentEditor/runtimeNodes' import PlatformSelectComponent from '@/components/componentEditor/selectComponents' import PlatformViewHost, { type PlatformViewName, @@ -8,7 +9,7 @@ import PlatformViewHost, { import apiService from '@/services/api.service' import { useLocalization } from '@/utils/hooks/useLocalization' import { formatLocaleValue } from '@/utils/localeFormat' -import { FaArrowDown, FaArrowUp, FaClone, FaGripVertical, FaTrash } from 'react-icons/fa' +import { FaGripVertical } from 'react-icons/fa' import { beginDesignerDragSoon, endDesignerDrag, useDesignerDrag } from './designerDrag' import Input from '@/components/ui/Input' import { @@ -167,10 +168,7 @@ interface VisualCanvasProps { ) => void /** Moves an existing node into a container/table cell, or to the root. */ onMoveIntoContainer?: (nodeId: string, parentId: string | null, slot?: string) => void - onMove?: (id: string, direction: -1 | 1) => void onReorder?: (sourceId: string, targetId: string, placement: 'before' | 'after') => void - onDuplicate?: (id: string) => void - onDelete?: (id: string) => void onNodePropChange?: (id: string, propertyName: string, value: unknown) => void renderCustomComponent?: (name: string, props?: Record) => React.ReactNode dataValues?: Record @@ -1289,6 +1287,18 @@ const renderElement = ( refs, refOverrides, ) + // Platform düğümleri (ikon, public site parçaları) kataloğun bir parçasıdır ama + // derlenmiş custom component listesinde değildir; kanvas onları doğrudan çizer, + // üretilen kod da aynı bileşenleri çağırır. + const RuntimeNode = RUNTIME_NODE_COMPONENTS[node.type] + if (RuntimeNode) { + const { children: runtimeChildren, ...runtimeProps } = props + return React.createElement( + RuntimeNode as React.ComponentType>, + runtimeProps, + content ?? (runtimeChildren as React.ReactNode), + ) + } if (node.kind === 'custom') { return renderCustomComponent?.(node.type, { ...props, children: content }) || null } @@ -1429,21 +1439,97 @@ const HTML_PLACEHOLDER_ELEMENTS = new Set([ 'h6', ]) +/** + * Kanvasta her dugum kendi sarmalayici div'i icinde cizilir ve ust ogenin grid/flex + * duzeninde asil oge o sarmalayici olur. Dugumun kendi yerlesim siniflari sarmalayiciya + * tasinmazsa (`md:col-span-4`, `flex-1`, `absolute inset-0`, kenar bosluklari) tasarim + * zamani gorunumu canlidan farkli cikar; asagidaki ayrim bunu engeller. + */ +const HOISTED_LAYOUT_PATTERN = + /^(-?m[trblxyse]?-|w-(?!full$|auto$)|basis-|order-|self-|justify-self-|(col|row)-(span|start|end|auto)|\[grid-(column|row):)/ +const HOISTED_LAYOUT_EXACT = new Set([ + 'flex-1', + 'flex-auto', + 'flex-initial', + 'flex-none', + 'grow', + 'grow-0', + 'shrink', + 'shrink-0', +]) +/** Sarmalayici da tasir ama dugumden silinmez: ikisi de ayni kutuyu kaplar. */ +const SHARED_LAYOUT_PATTERN = /^(absolute|fixed|sticky|w-full|h-full)$|^-?(inset|top|right|bottom|left)-|^z-|^(min|max)-w-/ +const NODE_POSITION_PATTERN = /^(absolute|fixed|sticky)$/ + +/** `md:`, `dark:hover:` gibi varyant onlerini atar; `[grid-column:1/-1]` bozulmadan kalir. */ +const stripVariants = (token: string) => token.replace(/^(?:(?:[\w-]+|\[[^\]]+\]):)+/, '') + +/** + * Kanvas kendi paneline cizer; `fixed` bir dugum tarayici penceresine yapisip + * tasarim yuzeyinden tasar. Tasarim zamaninda akista kalmasi icin akisa donusturulur. + */ +const canvasPositionClass = (token: string) => + stripVariants(token) === 'fixed' ? token.replace(/fixed$/, 'relative') : token + +/** + * Gorunurluk sinifi dugumde kalirsa sarmalayici yine de bir grid/flex hucresi isgal eder: + * mobilde gizlenmesi gereken masaustu seridi yer kaplar ve komsulari yanlis hucreye iter. + * Sarmalayiciya `hidden` ile birlikte gorunur hallerinin blok karsiligi verilir; dugumun + * kendi `display` degeri (flex/grid) el degmeden kalir, ic duzeni bozulmaz. + */ +const DISPLAY_CLASSES = new Set([ + 'hidden', + 'block', + 'inline-block', + 'inline', + 'flex', + 'inline-flex', + 'grid', + 'inline-grid', + 'table', + 'flow-root', + 'contents', + 'list-item', +]) + +const wrapperDisplayClass = (token: string, base: string) => + `${token.slice(0, token.length - base.length)}${base === 'hidden' ? 'hidden' : 'block'}` + +const splitLayoutClasses = (className: unknown) => { + const hoisted: string[] = [] + const remaining: string[] = [] + let positioned = false + + for (const token of String(className ?? '') + .split(/\s+/) + .filter(Boolean)) { + const base = stripVariants(token) + if (SHARED_LAYOUT_PATTERN.test(base)) { + hoisted.push(canvasPositionClass(token)) + remaining.push(canvasPositionClass(token)) + positioned ||= NODE_POSITION_PATTERN.test(base) + } else if (DISPLAY_CLASSES.has(base)) { + hoisted.push(wrapperDisplayClass(token, base)) + remaining.push(token) + } else if (HOISTED_LAYOUT_PATTERN.test(base) || HOISTED_LAYOUT_EXACT.has(base)) { + hoisted.push(token) + } else { + remaining.push(token) + } + } + + return { hoisted: hoisted.join(' '), remaining: remaining.join(' '), positioned } +} + const NodeView = ({ node, - index, - siblingCount = 1, - isRoot = false, selectedId, interactive, onSelect, onDropComponent, onDropComponentBeside, onMoveIntoContainer, - onMove, onReorder, - onDuplicate, - onDelete, onNodePropChange, renderCustomComponent, dataValues, @@ -1451,9 +1537,6 @@ const NodeView = ({ formScope, }: { node: DesignerNode - index: number - siblingCount?: number - isRoot?: boolean selectedId: string | null interactive: boolean onSelect?: (id: string) => void @@ -1464,10 +1547,7 @@ const NodeView = ({ placement: 'before' | 'after', ) => void onMoveIntoContainer?: (nodeId: string, parentId: string | null, slot?: string) => void - onMove?: (id: string, direction: -1 | 1) => void onReorder?: (sourceId: string, targetId: string, placement: 'before' | 'after') => void - onDuplicate?: (id: string) => void - onDelete?: (id: string) => void onNodePropChange?: (id: string, propertyName: string, value: unknown) => void renderCustomComponent?: (name: string, props?: Record) => React.ReactNode dataValues: Record @@ -1483,10 +1563,13 @@ const NodeView = ({ const refOverrides = refOverride?.props || {} const refHidden = Boolean(refOverride?.hidden) const selected = interactive && selectedId === node.id + const layout = splitLayoutClasses(node.props.className) + // Tasinan siniflar dugumden dusurulur, yoksa genislik/bosluk iki kez uygulanir. + const renderedNode = layout.hoisted + ? { ...node, props: { ...node.props, className: layout.remaining } } + : node // A nested node can always move: at the edge of its container it is lifted out. // Only the first/last node at the root has nowhere left to go. - const canMoveUp = !isRoot || index > 0 - const canMoveDown = !isRoot || index < siblingCount - 1 const acceptsDroppedChildren = [ 'PageContainer', 'FlexRow', @@ -1633,8 +1716,6 @@ const NodeView = ({ )), @@ -1729,8 +1807,6 @@ const NodeView = ({ ))} @@ -1824,8 +1897,6 @@ const NodeView = ({ ))} @@ -1983,7 +2051,9 @@ const NodeView = ({ return (
{ + onClickCapture={(event) => { + // Kanvas gercek sayfa degildir: bir baglantiya tiklamak tasarimciyi terk etmemeli. + // Yakalama evresinde durdurulur; react-router'in Link'i de `defaultPrevented` + // gorunce gezinmez, tiklama yalnizca secim anlamina gelir. + if ((event.target as HTMLElement).closest?.('a[href]')) event.preventDefault() if (interactive) onSelect?.(node.id) }} onClick={(event) => { @@ -2045,12 +2119,6 @@ const NodeView = ({
)} - {/* - Taşıma tutamağı. Bir Input ya da Select'in gövdesi tamamen kontrolle - kaplıdır ve tarayıcı oradan üst öğenin sürüklenmesini başlatmaz; tutamak - her bileşen için garanti bir tutma noktası verir. Sürükleme sürerken - gizlenir, aksi halde komşu düğümün bırakma alanını kapatırdı. - */} {/* Yanına bırakma çizgisi: bileşen tam olarak buraya girer. */} {dropHint === 'before' && (
@@ -2079,79 +2147,26 @@ const NodeView = ({ {translate('::App.VisualDesignerCanvas.CannotMoveHere')} )} + {/* + Tasima tutamagi. Bir Input ya da Select'in govdesi tamamen canli kontrolle + kaplidir ve tarayici oradan ust ogenin suruklenmesini baslatmaz; bu tutamak + her bilesen icin garanti bir tutma noktasi verir. Eskiden burada tasima ve + sil/kopyala dugmelerini de tasiyan genis bir serit vardi; kucuk bir ikonun ya + da ayirici cizginin ustunu tamamen ortuyordu. Dugmeler sag paneldeki secili + bilesen basligina tasindi, kanvasta yalnizca kose tutamagi kaldi. + */} {interactive && ( -
endDesignerDrag()} + onDragStart={startNodeDrag} > - {/* - Taşıma tutamağı. Bir Input ya da Select'in gövdesi tamamen canlı - kontrolle kaplıdır ve tarayıcı oradan üst öğenin sürüklenmesini - başlatmaz; bu rozet her bileşen için garanti bir tutma noktası verir. - */} - endDesignerDrag()} - > - {node.type} - - - - - -
+ + )} {interactive && refHidden && ( @@ -2163,7 +2178,7 @@ const NodeView = ({ resetKey={JSON.stringify([node.props, node.bindings, node.events])} > {renderElement( - node, + renderedNode, contentChildren, dataValues, currentItem, @@ -2178,6 +2193,10 @@ const NodeView = ({ {/* Tabs has a drop zone inside every tab, so it needs no outer placeholder. */} {interactive && + // Bos bir konteynerin "buraya birak" kutusu her zaman gorunurse ayirici cizgi + // gibi kucuk ogeler tasarim yuzeyinde sisip duruyor; yalnizca surukleme + // sirasinda ya da dugum seciliyken gosterilir. + (dragging || selected) && !hasVisibleChildren && acceptsDroppedChildren && node.type !== 'Card' && @@ -2191,7 +2210,6 @@ const NodeView = ({ {translate('::App.VisualDesignerCanvas.DropComponentHere')}
)} - {index}
) @@ -2205,10 +2223,7 @@ const VisualCanvas = ({ onDropComponent, onDropComponentBeside, onMoveIntoContainer, - onMove, onReorder, - onDuplicate, - onDelete, onNodePropChange, renderCustomComponent, dataValues = {}, @@ -2277,9 +2292,11 @@ const VisualCanvas = ({
interactive && onSelect?.('')} onDragOver={(event) => interactive && event.preventDefault()} onDrop={(event) => { @@ -2307,15 +2324,22 @@ const VisualCanvas = ({
)} + {/* + Transform sayfa kutusunu `position: fixed` icin kapsayan blok yapar, boylece + sabitlenen bir dugum tarayici penceresine degil sayfa genisligine yayilir. + Dugumun kendi `fixed` sinifi akisa cevriliyor (`canvasPositionClass`) ama bir + bilesen bunu baska bir proptan getirebilir — PublicHeaderBar kaydirilinca + `scrolledClassName` ile `fixed` olur ve serit ozellik panelinin ustune binerdi. + */} {nodes.length ? ( -
- {nodes.map((node, index) => ( +
+ {nodes.map((node) => ( ))} diff --git a/ui/src/components/visualDesigner/catalog.ts b/ui/src/components/visualDesigner/catalog.ts index 704ee8e9..58f2f7ed 100644 --- a/ui/src/components/visualDesigner/catalog.ts +++ b/ui/src/components/visualDesigner/catalog.ts @@ -30,7 +30,7 @@ interface GeneratedComponentMetadata { } const generatedMetadata = generatedComponentProps as Record -const CONTAINER_NAMES = new Set(['div', 'Card', 'FormContainer', 'Table', 'Tabs', 'Timeline']) +const CONTAINER_NAMES = new Set(['a', 'div', 'Card', 'FormContainer', 'Table', 'Tabs', 'Timeline']) const getToolboxGroup = (name: string): DesignerComponentDefinition['toolboxGroup'] => name === 'Table' ? 'layout' : DESIGNER_DATA_COMPONENT_NAMES.has(name) ? 'data' : 'ui' /** Components intentionally kept out of the toolbox. */ @@ -875,6 +875,122 @@ const platformDefinition = ( hooks: [], }) +/** + * Public site basliginin parcalari. Menu, tema, dil ve demo davranisi bilesenin + * icinde yasar; tasarimci yerlesimi ve siniflari belirler. Kanvas ve uretilen kod + * ayni bilesenleri kullanir (`components/publicSite`). + */ +export const PUBLIC_SITE_COMPONENTS: DesignerComponentDefinition[] = [ + { + name: 'PublicHeaderBar', + icon: 'PanelTop', + category: 'layout', + kind: 'custom', + toolboxGroup: 'platform', + description: 'App.CatalogPublicHeaderBar.PublicHeaderBarDescription', + acceptsChildren: true, + properties: [ + { name: 'className', type: 'string', value: '', category: 'styling' }, + { name: 'scrolledClassName', type: 'string', value: '', category: 'styling' }, + { name: 'scrollThreshold', type: 'number', value: 10, category: 'properties' }, + ], + hooks: [], + }, + { + name: 'PublicNav', + icon: 'Menu', + category: 'basic', + kind: 'custom', + toolboxGroup: 'platform', + description: 'App.CatalogPublicNav.PublicNavDescription', + properties: [ + { + name: 'source', + type: 'select', + value: 'navigation', + options: ['navigation', 'actions', 'all'], + category: 'properties', + }, + { + name: 'variant', + type: 'select', + value: 'horizontal', + options: ['horizontal', 'vertical'], + category: 'properties', + }, + { name: 'showIcons', type: 'boolean', value: true, category: 'properties' }, + { name: 'iconSize', type: 'number', value: 0, category: 'styling' }, + { name: 'className', type: 'string', value: '', category: 'styling' }, + { name: 'itemClassName', type: 'string', value: '', category: 'styling' }, + { name: 'activeItemClassName', type: 'string', value: '', category: 'styling' }, + ], + hooks: [], + }, + { + name: 'PublicMobileMenu', + icon: 'PanelLeft', + category: 'layout', + kind: 'custom', + toolboxGroup: 'platform', + description: 'App.CatalogPublicMobileMenu.PublicMobileMenuDescription', + acceptsChildren: true, + properties: [ + { name: 'className', type: 'string', value: '', category: 'styling' }, + { name: 'buttonClassName', type: 'string', value: '', category: 'styling' }, + { name: 'panelClassName', type: 'string', value: '', category: 'styling' }, + ], + hooks: [], + }, + { + name: 'PublicLogo', + icon: 'Image', + category: 'basic', + kind: 'custom', + toolboxGroup: 'platform', + description: 'App.CatalogPublicLogo.PublicLogoDescription', + properties: [ + { name: 'className', type: 'string', value: '', category: 'styling' }, + { name: 'imgClassName', type: 'string', value: '', category: 'styling' }, + ], + hooks: [], + }, + { + name: 'PublicThemeToggle', + icon: 'Sun', + category: 'basic', + kind: 'custom', + toolboxGroup: 'platform', + description: 'App.CatalogPublicThemeToggle.PublicThemeToggleDescription', + properties: [ + { name: 'className', type: 'string', value: '', category: 'styling' }, + { name: 'buttonClassName', type: 'string', value: '', category: 'styling' }, + { name: 'activeClassName', type: 'string', value: '', category: 'styling' }, + { name: 'iconSize', type: 'number', value: 15, category: 'styling' }, + ], + hooks: [], + }, + { + name: 'PublicLanguageSelector', + icon: 'Globe', + category: 'basic', + kind: 'custom', + toolboxGroup: 'platform', + description: 'App.CatalogPublicLanguageSelector.PublicLanguageSelectorDescription', + properties: [{ name: 'className', type: 'string', value: '', category: 'styling' }], + hooks: [], + }, + { + name: 'PublicDemoButton', + icon: 'Play', + category: 'basic', + kind: 'custom', + toolboxGroup: 'platform', + description: 'App.CatalogPublicDemoButton.PublicDemoButtonDescription', + properties: [{ name: 'className', type: 'string', value: '', category: 'styling' }], + hooks: [], + }, +] + export const PLATFORM_COMPONENTS: DesignerComponentDefinition[] = [ platformDefinition('ListView', 'List', 'App.CatalogListView.ListViewDescription'), platformDefinition('DataGridView', 'Grid', 'App.CatalogDataGridView.DataGridViewDescription'), @@ -1085,6 +1201,22 @@ export const DESIGNER_EXTRAS: DesignerComponentDefinition[] = [ ], hooks: [], }, + { + // Ikonlar ada gore cizilir (menu ikonlariyla ayni sozluk); kanvas ve uretilen + // kod ayni `PlatformIcon` bilesenini kullanir. + name: 'PlatformIcon', + icon: 'Image', + category: 'basic', + kind: 'custom', + toolboxGroup: 'ui', + description: 'App.CatalogPlatformIcon.PlatformIconDescription', + properties: [ + { name: 'name', type: 'string', value: 'FaUser', category: 'properties' }, + { name: 'size', type: 'number', value: 24, category: 'styling' }, + { name: 'className', type: 'string', value: '', category: 'styling' }, + ], + hooks: [], + }, { name: 'Spacer', icon: 'Space', @@ -1146,6 +1278,7 @@ export const getDesignerCatalog = (customNames: string[] = []): DesignerComponen ...DESIGNER_EXTRAS, FORM_DEFINITION, ...PLATFORM_COMPONENTS, + ...PUBLIC_SITE_COMPONENTS, ...html, ...ui, ...generatedOnlyUi, diff --git a/ui/src/constants/permission.constant.ts b/ui/src/constants/permission.constant.ts index c2785654..f3fe4bc6 100644 --- a/ui/src/constants/permission.constant.ts +++ b/ui/src/constants/permission.constant.ts @@ -4,18 +4,6 @@ export const AI_ASSISTANT = 'App.Definitions.AiBot.Asistant' /** Veritabanı migration + seed tetikleme yetkisi (host tarafı). */ export const DB_MIGRATE = 'App.Setup.Migrate' -/** - * Public site sayfalarının tasarım modu yetkileri. - * Sunucuda `PermissionsData.json` içinde tanımlıdır ve seeder ile yönetilir; - * asıl kontrol `PublicAppService.Save*PageAsync` üzerindedir. - */ -export const PUBLIC_PAGE_DESIGN = { - HOME: 'App.Home.Design', - ABOUT: 'App.About.Design', - SERVICES: 'App.Services.Design', - CONTACT: 'App.Contact.Design', -} as const - /** * Wizard dosya yöneticisi yetkileri. `PermissionsData.json` içinde tanımlıdır ve seeder ile * yönetilir; buradaki kontroller yalnızca butonları gizler, asıl kontrol diff --git a/ui/src/contexts/ComponentContext.tsx b/ui/src/contexts/ComponentContext.tsx index 9b2de4df..967ade9c 100644 --- a/ui/src/contexts/ComponentContext.tsx +++ b/ui/src/contexts/ComponentContext.tsx @@ -222,11 +222,20 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi try { // Babel and the UI kit are only needed when an active runtime component // exists. Keep them out of the application startup bundle. - const [Babel, ui, { default: axios }, { default: PlatformViewHost }] = await Promise.all([ + const [ + Babel, + ui, + { default: axios }, + { default: PlatformViewHost }, + { RUNTIME_NODE_COMPONENTS }, + ] = await Promise.all([ import('@babel/standalone'), import('../components/ui'), import('axios'), import('../components/componentEditor/PlatformViewHost'), + // Katalog dugumleri: react-icons ve public site parcalari runtime + // kapsaminda degildir, kanvasin cizdigi bilesenler buradan gecirilir. + import('../components/componentEditor/runtimeNodes'), ]) if (cancelled) return @@ -235,6 +244,7 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi ...ui, UiKit: ui, PlatformViewHost, + ...RUNTIME_NODE_COMPONENTS, apiService, DOMPurify, axios, diff --git a/ui/src/contexts/DemoContext.tsx b/ui/src/contexts/DemoContext.tsx index a62dfbf7..525aef98 100644 --- a/ui/src/contexts/DemoContext.tsx +++ b/ui/src/contexts/DemoContext.tsx @@ -8,16 +8,10 @@ const DemoContext = createContext(undefined) export const DemoProvider = DemoContext.Provider -export const useDemo = () => { - const context = useContext(DemoContext) - - if (!context) { - throw new Error('useDemo must be used within a DemoProvider') - } - - return context -} - +/** + * Demo penceresini acan cagri. Saglayici disinda cizilen bileşen (tasarimci kanvasi) + * de ayni dugumu kullanabilsin diye eksik context bir hataya degil, bos cagriya doner. + */ export const useOptionalDemo = () => { const context = useContext(DemoContext) diff --git a/ui/src/contexts/ScrollContext.tsx b/ui/src/contexts/ScrollContext.tsx deleted file mode 100644 index ec65d323..00000000 --- a/ui/src/contexts/ScrollContext.tsx +++ /dev/null @@ -1,6 +0,0 @@ -// ScrollContext.tsx -import { createContext, useContext } from 'react' - -export const ScrollContext = createContext(false) - -export const useScroll = () => useContext(ScrollContext) diff --git a/ui/src/proxy/about/models.ts b/ui/src/proxy/about/models.ts deleted file mode 100644 index d167ea18..00000000 --- a/ui/src/proxy/about/models.ts +++ /dev/null @@ -1,22 +0,0 @@ -export interface AboutDto { - id: string - tenantId?: string - statsDto: StatDto[] - descriptionsDto: string[] - sectionsDto: SectionDto[] -} - -export interface StatDto { - icon: string - value: string - labelKey: string - useCounter?: boolean - counterEnd?: string - counterSuffix?: string - counterDuration?: number -} - -export interface SectionDto { - key: string - descKey: string -} diff --git a/ui/src/proxy/contact/models.ts b/ui/src/proxy/contact/models.ts deleted file mode 100644 index df0c02d5..00000000 --- a/ui/src/proxy/contact/models.ts +++ /dev/null @@ -1,39 +0,0 @@ -export interface ContactDto { - id: string - address: string - phoneNumber: string - email: string - location: string - taxNumber: string - bankDto: BankDto - workHoursDto: WorkHoursDto - mapDto: MapDto -} - -export interface BankDto { - accountHolder: string - branch: string - accountNumber: string - iban: string - styleClass?: string -} - -export interface WorkHoursDto { - weekday: string - weekend: string - whatsapp: string - styleClass?: string -} - -export interface MapDto { - title: string - src: string - width: string - height: string - style?: string - containerStyleClass?: string - frameStyleClass?: string - allowFullScreen?: boolean - loading: string - referrerPolicy: string -} diff --git a/ui/src/routes/dynamicRouteLoader.tsx b/ui/src/routes/dynamicRouteLoader.tsx index 1f69911e..5c132d44 100644 --- a/ui/src/routes/dynamicRouteLoader.tsx +++ b/ui/src/routes/dynamicRouteLoader.tsx @@ -25,6 +25,15 @@ const runtimeComponentCache = new Map>() // time instead. const runtimeRenderers = new Map React.ReactNode>() +/** + * Rota kaydı, karşılığı olan dosya kaldırıldıktan sonra da veritabanında kalabilir + * (seeder yalnızca ekler). Böyle bir kayıt router'a alınmaz; yolu bir custom component + * devraldıysa o çizilir, devralmadıysa NotFound gösterilir. + */ +export function hasPhysicalComponent(componentPath: string) { + return `../${componentPath.replace(/^@\//, '')}.tsx` in modules +} + // Fiziksel komponent yükleme (mevcut mantık) function loadPhysicalComponent(componentPath: string) { const cleanedPath = componentPath.replace(/^@\//, '') @@ -124,22 +133,24 @@ export interface DynamicReactRoute { // API'den gelen route objesini, React Router için uygun hale getirir export function mapDynamicRoutes(routes: RouteDto[]): DynamicReactRoute[] { - return routes.map((route) => ({ - key: route.path, - path: route.path, - getComponent: (registeredComponents, renderComponent, isComponentRegistered) => - loadComponent( - route.componentType, - route.componentPath, - registeredComponents, - renderComponent, - isComponentRegistered, - ), - routeType: route.routeType, - authority: route.authority, - componentType: route.componentType, - componentPath: route.componentPath, - })) + return routes + .filter((route) => route.componentType === 'dynamic' || hasPhysicalComponent(route.componentPath)) + .map((route) => ({ + key: route.path, + path: route.path, + getComponent: (registeredComponents, renderComponent, isComponentRegistered) => + loadComponent( + route.componentType, + route.componentPath, + registeredComponents, + renderComponent, + isComponentRegistered, + ), + routeType: route.routeType, + authority: route.authority, + componentType: route.componentType, + componentPath: route.componentPath, + })) } // Custom components are the single source of truth for runtime routes. diff --git a/ui/src/routes/dynamicRouter.tsx b/ui/src/routes/dynamicRouter.tsx index 690a5dfb..0c4595bf 100644 --- a/ui/src/routes/dynamicRouter.tsx +++ b/ui/src/routes/dynamicRouter.tsx @@ -62,10 +62,18 @@ export const DynamicRouter: React.FC = () => { useComponents() const location = useLocation() - const dynamicRoutes = React.useMemo( - () => [...mapDynamicRoutes(routes), ...mapCustomComponentRoutes(components)], - [routes, components], - ) + const dynamicRoutes = React.useMemo(() => { + const customComponentRoutes = mapCustomComponentRoutes(components) + const claimedPaths = new Set(customComponentRoutes.map((route) => route.path)) + + // Ayni yolu iki route paylasirsa custom component kazanir: bir ekranin fiziksel + // sayfadan runtime bilesene devri (ve bileseni pasife alarak geri donusu) yalnizca + // Custom Components ekranindan yonetilir, rota kaydini silmeyi gerektirmez. + return [ + ...mapDynamicRoutes(routes).filter((route) => !claimedPaths.has(route.path)), + ...customComponentRoutes, + ] + }, [routes, components]) // /setup path'inde loading bekleme — setup route her zaman erişilebilir olmalı. // bootstrapFailed durumu Layouts'ta, layout seçilmeden önce ele alınır. diff --git a/ui/src/routes/route.constant.ts b/ui/src/routes/route.constant.ts index 04076b4a..49376709 100644 --- a/ui/src/routes/route.constant.ts +++ b/ui/src/routes/route.constant.ts @@ -2,18 +2,11 @@ setup: '/setup', public: { home: '/home', - about: '/about', - aboutDesigner: '/about/designer', products: '/products', checkout: '/checkout', payment: '/payment', success: '/success', - services: '/services', - servicesDesigner: '/services/designer', blog: '/blog', - blogDetail: '/blog/:id', - demo: '/demo', - contact: '/contact', accessDenied: '/access-denied', }, authenticated: { diff --git a/ui/src/services/about.ts b/ui/src/services/about.ts deleted file mode 100644 index 06e98f89..00000000 --- a/ui/src/services/about.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { AboutDto } from '@/proxy/about/models' -import apiService from './api.service' - -export interface SaveLocalizedTextInput { - key: string - value: string -} - -export interface SaveAboutStatInput { - icon: string - value: string - labelKey: string - labelValue: string - useCounter?: boolean - counterEnd?: string - counterSuffix?: string - counterDuration?: number -} - -export interface SaveAboutSectionInput { - titleKey: string - titleValue: string - descriptionKey: string - descriptionValue: string -} - -export interface SaveAboutPageInput { - cultureName: string - heroTitleKey: string - heroTitleValue: string - heroSubtitleKey: string - heroSubtitleValue: string - heroImageKey: string - heroImageValue: string - stats: SaveAboutStatInput[] - descriptions: SaveLocalizedTextInput[] - sections: SaveAboutSectionInput[] - styleTexts: SaveLocalizedTextInput[] -} - -export function getAbout() { - return apiService.fetchData( - { - method: 'GET', - url: '/api/app/public/about', - }, - { apiName: 'Default' }, - ) -} - -export function saveAboutPage(input: SaveAboutPageInput) { - return apiService.fetchData( - { - method: 'POST', - url: '/api/app/public/save-about-page', - data: input, - }, - { apiName: 'Default' }, - ) -} diff --git a/ui/src/services/contact.service.ts b/ui/src/services/contact.service.ts deleted file mode 100644 index d8a3a75b..00000000 --- a/ui/src/services/contact.service.ts +++ /dev/null @@ -1,73 +0,0 @@ -import apiService from './api.service' -import { ContactDto } from '@/proxy/contact/models' - -export interface SaveLocalizedTextInput { - key: string - value: string -} - -export function getContact() { - return apiService.fetchData( - { - method: 'GET', - url: '/api/app/public/contact', - }, - { apiName: 'Default' }, - ) -} - -export interface SaveContactPageInput extends Record { - cultureName: string - heroTitleKey: string - heroTitleValue: string - heroSubtitleKey: string - heroSubtitleValue: string - heroImageKey: string - heroImageValue: string - contactInfoTitleKey: string - contactInfoTitleValue: string - addressKey: string - addressValue: string - phoneNumber: string - email: string - location: string - taxNumber: string - bankTitleKey: string - bankTitleValue: string - bankAccountHolder: string - bankBranch: string - bankAccountNumber: string - bankIban: string - bankStyleClass: string - workHoursTitleKey: string - workHoursTitleValue: string - workWeekdayKey: string - workWeekdayValue: string - workWeekendKey: string - workWeekendValue: string - workWhatsappKey: string - workWhatsappValue: string - workHoursStyleClass: string - mapTitleKey: string - mapTitleValue: string - mapSrc: string - mapWidth: string - mapHeight: string - mapAllowFullScreen: boolean - mapLoading: string - mapReferrerPolicy: string - mapContainerStyleClass: string - mapFrameStyleClass: string - styleTexts: SaveLocalizedTextInput[] -} - -export function saveContactPage(input: SaveContactPageInput) { - return apiService.fetchData( - { - method: 'POST', - url: '/api/app/public/save-contact-page', - data: input, - }, - { apiName: 'Default' }, - ) -} diff --git a/ui/src/services/country.service.ts b/ui/src/services/country.service.ts new file mode 100644 index 00000000..87e64d12 --- /dev/null +++ b/ui/src/services/country.service.ts @@ -0,0 +1,30 @@ +import apiService from './api.service' + +export interface CountryDto { + id: string; + + name: string; + groupName: string; + currency: string; + phoneCode: number; + taxLabel: string; + zipRequired: boolean; + stateRequired: boolean; + + cities: CityDto[]; +} + +export interface CityDto { + id: string; + name: string; +} + +export function getCountry() { + return apiService.fetchData( + { + method: 'GET', + url: '/api/app/public/country', + }, + { apiName: 'Default' }, + ) +} diff --git a/ui/src/services/home.service.ts b/ui/src/services/home.service.ts deleted file mode 100644 index 79667764..00000000 --- a/ui/src/services/home.service.ts +++ /dev/null @@ -1,179 +0,0 @@ -import apiService from './api.service' - -export interface HomeSlideServiceDto { - icon: string - titleKey: string - descriptionKey: string - styleClass?: string -} - -export interface HomeSlideDto { - titleKey: string - subtitleKey: string - styleClass?: string - services: HomeSlideServiceDto[] -} - -export interface HomeFeatureDto { - icon: string - titleKey: string - descriptionKey: string - styleClass?: string -} - -export interface HomeSolutionDto { - icon: string - colorClass: string - titleKey: string - descriptionKey: string - styleClass?: string -} - -export interface HomeDto { - id: string - heroBackgroundImageKey: string - heroPrimaryCtaKey: string - heroSecondaryCtaKey: string - featuresTitleKey: string - featuresSubtitleKey: string - solutionsTitleKey: string - solutionsSubtitleKey: string - ctaTitleKey: string - ctaSubtitleKey: string - ctaButtonLabelKey: string - slidesDto: HomeSlideDto[] - featuresDto: HomeFeatureDto[] - solutionsDto: HomeSolutionDto[] -} - -export interface SaveHomeSlideServiceInput { - icon: string - titleKey: string - titleValue: string - descriptionKey: string - descriptionValue: string - styleClass: string -} - -export interface SaveHomeSlideInput { - titleKey: string - titleValue: string - subtitleKey: string - subtitleValue: string - styleClass: string - services: SaveHomeSlideServiceInput[] -} - -export interface SaveHomeFeatureInput { - icon: string - titleKey: string - titleValue: string - descriptionKey: string - descriptionValue: string - styleClass: string -} - -export interface SaveHomeSolutionInput { - icon: string - colorClass: string - titleKey: string - titleValue: string - descriptionKey: string - descriptionValue: string - styleClass: string -} - -export interface SaveHomePageInput { - cultureName: string - heroBackgroundImageKey: string - heroBackgroundImageValue: string - heroPrimaryCtaKey: string - heroPrimaryCtaValue: string - heroPrimaryCtaStyleKey: string - heroPrimaryCtaStyleValue: string - heroSecondaryCtaKey: string - heroSecondaryCtaValue: string - heroSecondaryCtaStyleKey: string - heroSecondaryCtaStyleValue: string - featuresTitleKey: string - featuresTitleValue: string - featuresTitleStyleKey: string - featuresTitleStyleValue: string - featuresSubtitleKey: string - featuresSubtitleValue: string - featuresSubtitleStyleKey: string - featuresSubtitleStyleValue: string - solutionsTitleKey: string - solutionsTitleValue: string - solutionsTitleStyleKey: string - solutionsTitleStyleValue: string - solutionsSubtitleKey: string - solutionsSubtitleValue: string - solutionsSubtitleStyleKey: string - solutionsSubtitleStyleValue: string - ctaTitleKey: string - ctaTitleValue: string - ctaTitleStyleKey: string - ctaTitleStyleValue: string - ctaSubtitleKey: string - ctaSubtitleValue: string - ctaSubtitleStyleKey: string - ctaSubtitleStyleValue: string - ctaButtonLabelKey: string - ctaButtonLabelValue: string - ctaButtonStyleKey: string - ctaButtonStyleValue: string - slides: SaveHomeSlideInput[] - features: SaveHomeFeatureInput[] - solutions: SaveHomeSolutionInput[] -} - -export interface CountryDto { - id: string; - - name: string; - groupName: string; - currency: string; - phoneCode: number; - taxLabel: string; - zipRequired: boolean; - stateRequired: boolean; - - cities: CityDto[]; -} - -export interface CityDto { - id: string; - name: string; -} - -export function getHome() { - return apiService.fetchData( - { - method: 'GET', - url: '/api/app/public/home', - }, - { apiName: 'Default' }, - ) -} - -export function getCountry() { - return apiService.fetchData( - { - method: 'GET', - url: '/api/app/public/country', - }, - { apiName: 'Default' }, - ) -} - -export function saveHomePage(input: SaveHomePageInput) { - return apiService.fetchData( - { - method: 'POST', - url: '/api/app/public/save-home-page', - data: input, - }, - { apiName: 'Default' }, - ) -} diff --git a/ui/src/services/service.service.ts b/ui/src/services/service.service.ts deleted file mode 100644 index 86c5af06..00000000 --- a/ui/src/services/service.service.ts +++ /dev/null @@ -1,61 +0,0 @@ -import apiService from './api.service' -import { ServiceDto } from '@/proxy/services/models' - -export interface SaveLocalizedTextInput { - key: string - value: string -} - -export interface SaveServiceItemInput { - icon?: string - titleKey: string - titleValue: string - descriptionKey?: string - descriptionValue?: string - type: 'service' | 'support' - features: SaveLocalizedTextInput[] -} - -export interface SaveServicesPageInput { - cultureName: string - heroTitleKey: string - heroTitleValue: string - heroSubtitleKey: string - heroSubtitleValue: string - heroImageKey: string - heroImageValue: string - supportTitleKey: string - supportTitleValue: string - supportButtonLabelKey: string - supportButtonLabelValue: string - ctaTitleKey: string - ctaTitleValue: string - ctaDescriptionKey: string - ctaDescriptionValue: string - ctaButtonLabelKey: string - ctaButtonLabelValue: string - serviceItems: SaveServiceItemInput[] - supportItems: SaveServiceItemInput[] - styleTexts: SaveLocalizedTextInput[] -} - -export function getServices() { - return apiService.fetchData( - { - method: 'GET', - url: '/api/app/public/services-list', - }, - { apiName: 'Default' }, - ) -} - -export function saveServicesPage(input: SaveServicesPageInput) { - return apiService.fetchData( - { - method: 'POST', - url: '/api/app/public/save-services-page', - data: input, - }, - { apiName: 'Default' }, - ) -} diff --git a/ui/src/store/abpConfig.model.ts b/ui/src/store/abpConfig.model.ts index ccbc8def..97843144 100644 --- a/ui/src/store/abpConfig.model.ts +++ b/ui/src/store/abpConfig.model.ts @@ -6,6 +6,7 @@ import appConfig from '../proxy/configs/app.config' import { NavigationTree } from '../proxy/menus/navigation' import { MenuDto } from '../proxy/menus/models' import getChildren from '../utils/navigation' +import { PUBLIC_MENU_MODULE_ID } from '../utils/hooks/usePublicMenu' import { ApplicationConfigurationDto, ApplicationLocalizationRequestDto, @@ -108,12 +109,17 @@ export const abpConfigModel: AbpConfigModel = { const texts = getState().texts const defaultResourceName = getState().config?.localization.defaultResourceName - result.data.items?.forEach((menu: MenuDto) => { + // Public site menusu ziyaretci duzenine aittir; yonetim navigasyonunda yer almaz. + const items = (result.data.items ?? []).filter( + (menu: MenuDto) => menu.moduleId !== PUBLIC_MENU_MODULE_ID, + ) + + items.forEach((menu: MenuDto) => { const displayName = '::' + menu.displayName menu.displayName = getLocalization(texts || {}, defaultResourceName, displayName) }) - const menu = getChildren(result.data.items ?? [], null) + const menu = getChildren(items, null) actions.setMenu(menu) }), } diff --git a/ui/src/utils/hooks/usePublicMenu.ts b/ui/src/utils/hooks/usePublicMenu.ts new file mode 100644 index 00000000..a929d645 --- /dev/null +++ b/ui/src/utils/hooks/usePublicMenu.ts @@ -0,0 +1,65 @@ +import { useMemo } from 'react' +import { useStoreState } from '@/store' + +/** + * Public site menu kaydi. Menuler `Pub` modulu altinda tutulur ve uygulama + * konfigurasyonunun `publicMenus` alaniyla tasinir; menu ucu `[Authorize]` + * oldugu icin ziyaretci ayri bir istek atamaz. + */ +export interface PublicMenuItem { + code: string + displayName?: string + url?: string + icon?: string + order: number + parentCode?: string + cssClass?: string + target?: string + requiredPermissionName?: string +} + +export interface PublicMenuNode extends PublicMenuItem { + children: PublicMenuNode[] +} + +/** Ust menude bu sinifi tasiyan kayit duz baglanti yerine vurgulu buton olarak cizilir. */ +export const PUBLIC_MENU_CTA_CLASS = 'cta' + +/** Public site menulerinin modulu; yonetim navigasyonu bu modulu disarida birakir. */ +export const PUBLIC_MENU_MODULE_ID = 'Pub' + +/** Ust ve alt bilgiyi cizen Custom Component'lerin adlari. */ +export const PUBLIC_HEADER_COMPONENT = 'PublicHeader' +export const PUBLIC_FOOTER_COMPONENT = 'PublicFooter' + +export const PUBLIC_MENU_HEADER_CODE = 'App.Public.Header' + +function buildChildren( + items: PublicMenuItem[], + parentCode: string, + isVisible: (item: PublicMenuItem) => boolean, +): PublicMenuNode[] { + return items + .filter((item) => item.parentCode === parentCode && isVisible(item)) + .sort((left, right) => left.order - right.order) + .map((item) => ({ ...item, children: buildChildren(items, item.code, isVisible) })) +} + +/** + * Ust menu baglantilarini menu agacindan turetir. Alt bilgi kendi Custom Component'inde + * tasarlanir, menuden beslenmez. + */ +export function usePublicMenu() { + const publicMenus = useStoreState( + (state) => state.abpConfig.config?.extraProperties?.['publicMenus'], + ) as PublicMenuItem[] | undefined + const grantedPolicies = useStoreState((state) => state.abpConfig.config?.auth?.grantedPolicies) + + return useMemo(() => { + const items = Array.isArray(publicMenus) ? publicMenus : [] + const isVisible = (item: PublicMenuItem) => + !item.requiredPermissionName || grantedPolicies?.[item.requiredPermissionName] === true + + return { headerLinks: buildChildren(items, PUBLIC_MENU_HEADER_CODE, isVisible) } + }, [publicMenus, grantedPolicies]) +} diff --git a/ui/src/utils/hooks/useScrolled.ts b/ui/src/utils/hooks/useScrolled.ts new file mode 100644 index 00000000..d1f4f84f --- /dev/null +++ b/ui/src/utils/hooks/useScrolled.ts @@ -0,0 +1,20 @@ +import { useEffect, useState } from 'react' + +/** + * Sayfa esigin otesine kaydirildi mi. Kabuk artik tasarlanabilir bir Custom + * Component oldugu icin durum bir context'ten degil, ihtiyaci olan her bilesenin + * kendi dinleyicisinden gelir. + */ +export const useScrolled = (threshold = 10) => { + const [scrolled, setScrolled] = useState(false) + + useEffect(() => { + const handleScroll = () => setScrolled(window.scrollY > threshold) + + handleScroll() + window.addEventListener('scroll', handleScroll, { passive: true }) + return () => window.removeEventListener('scroll', handleScroll) + }, [threshold]) + + return scrolled +} diff --git a/ui/src/views/admin/listForm/edit/options.ts b/ui/src/views/admin/listForm/edit/options.ts index 01d7f91f..4c6d8367 100644 --- a/ui/src/views/admin/listForm/edit/options.ts +++ b/ui/src/views/admin/listForm/edit/options.ts @@ -1,5 +1,4 @@ import { - AuthorizationTypeEnum, ColumnCascadeFilterOperatorEnum, ColumnRowTypeListOptions, ColumnSortDirectionEnum, @@ -29,19 +28,6 @@ import { enumToList } from '../../../../utils/enumUtils' import { PREFIX } from '@/constants/theme.constant' import { SelectBoxOption } from '@/types/shared' -export enum WidgetColorEnum { - 'Blue' = 'blue', - 'Green' = 'green', - 'Purple' = 'purple', - 'Gray' = 'gray', - 'Red' = 'red', - 'Yellow' = 'yellow', - 'Pink' = 'pink', - 'Indigo' = 'indigo', - 'Teal' = 'teal', - 'Orange' = 'orange', -} - export const overflowWrapOptions = [ { value: 'normal', label: 'Normal' }, { value: 'break-word', label: 'Break Word' }, @@ -150,21 +136,6 @@ export const customValueTypeOptions = [ { value: 5, label: 'Sequence' }, ] -export const gridColumnCopyFieldTypeOptions = [ - { value: 0, label: 'All' }, - { value: 1, label: 'General' }, - { value: 2, label: 'Filter' }, - { value: 3, label: 'Header' }, - { value: 4, label: 'Group' }, - { value: 5, label: 'Customization' }, - { value: 6, label: 'GroupSummary' }, - { value: 7, label: 'TotalSummary' }, - { value: 8, label: 'Editing' }, - { value: 9, label: 'Lookup' }, - { value: 10, label: 'Validation' }, - { value: 11, label: 'Styling' }, -] - export const itemTypeOptions = [ { value: 'button', label: 'Button' }, { value: 'empty', label: 'Empty' }, @@ -180,18 +151,6 @@ export const urlOptions = [ { value: '_top', label: 'Top' }, ] -export const joinConditionOptions = [ - { value: 'AND', label: 'AND' }, - { value: 'OR', label: 'OR' }, -] - -export type listFormCustomizationType = 'customization' | 'detail' | '' - -export const columnLookupTagBoxApplyValueModeListOptions = [ - { value: 'instantly', label: 'Instantly' }, - { value: 'useButtons', label: 'Use Buttons' }, -] - export const columnEditorTypeListOptions = [ { value: 'dxAutocomplete', label: 'dxAutocomplete' }, { value: 'dxCalendar', label: 'dxCalendar' }, @@ -527,7 +486,6 @@ export const dbSourceTypeOptions = enumToList(DbTypeEnum) export const dataSourceTypeOptions = enumToList(DataSourceTypeEnum) export const selectCommandTypeOptions = enumToList(SelectCommandTypeEnum) export const listFormCustomizationOptions = enumToList(ListFormCustomizationTypeEnum) -export const authorizationTypeOptions = enumToList(AuthorizationTypeEnum) export const sortDirectionOptions = enumToList(SortDirectionEnum) export const fixedPositionOptions = enumToList(FixedPositionEnum) export const columnSummaryTypeListOptions = enumToList(SummaryTypeEnum) @@ -546,7 +504,6 @@ export const cascadeFilterOperator = enumToList(ColumnCascadeFilterOpera export const pivotSettingsAreaOptions = enumToList(PivotSettingsAreaEnum) export const pivotSettingsGroupIntervalOptions = enumToList(PivotSettingsGroupIntervalEnum) export const pivotSortDirectionOptions = enumToList(ColumnSortDirectionEnum) -export const widgetColorOptions = enumToList(WidgetColorEnum) // Tema renk haritası (border ve fill) const styleColorMap: Record< string, diff --git a/ui/src/views/admin/listForm/edit/types.ts b/ui/src/views/admin/listForm/edit/types.ts index b51e2e97..8a15c17f 100644 --- a/ui/src/views/admin/listForm/edit/types.ts +++ b/ui/src/views/admin/listForm/edit/types.ts @@ -1,5 +1,3 @@ -export type ChartOperation = '' | 'select' | 'insert' | 'update' | 'delete' -export type ChartDialogType = '' | 'pane' | 'serie' | 'annotation' | 'axis' export type ListViewLayoutType = | 'grid' | 'card' diff --git a/ui/src/views/admin/profile/components/General.tsx b/ui/src/views/admin/profile/components/General.tsx index 63854317..d3d310bf 100644 --- a/ui/src/views/admin/profile/components/General.tsx +++ b/ui/src/views/admin/profile/components/General.tsx @@ -32,7 +32,7 @@ import { import * as Yup from 'yup' import { ProfileDto, UpdateProfileDto } from '@/proxy/account/models' import { getProfile, updateProfile } from '@/services/account.service' -import { CountryDto, getCountry } from '@/services/home.service' +import { CountryDto, getCountry } from '@/services/country.service' import { SelectBoxOption } from '@/types/shared' import { useSetting } from '@/utils/hooks/useSetting' diff --git a/ui/src/views/admin/user-management/Details.tsx b/ui/src/views/admin/user-management/Details.tsx index 2e527ba2..b773ec16 100644 --- a/ui/src/views/admin/user-management/Details.tsx +++ b/ui/src/views/admin/user-management/Details.tsx @@ -31,7 +31,7 @@ import { putUserLookout, putUserPermission, } from '@/services/identity.service' -import { CountryDto, getCountry } from '@/services/home.service' +import { CountryDto, getCountry } from '@/services/country.service' import { useLocalization } from '@/utils/hooks/useLocalization' import dayjs from 'dayjs' import { Field, FieldArray, FieldProps, Form, Formik, FormikHelpers } from 'formik' diff --git a/ui/src/views/developerKit/VisualComponentDesigner.tsx b/ui/src/views/developerKit/VisualComponentDesigner.tsx index 58411923..6c282ac5 100644 --- a/ui/src/views/developerKit/VisualComponentDesigner.tsx +++ b/ui/src/views/developerKit/VisualComponentDesigner.tsx @@ -7,6 +7,7 @@ import { FaArrowLeft, FaBolt, FaCheckCircle, + FaClone, FaArrowDown, FaArrowUp, FaChevronDown, @@ -41,6 +42,8 @@ import { getListForms } from '@/services/admin/list-form.service' import { developerKitService } from '@/services/developerKit.service' import { Button, Notification, Select, toast } from '@/components/ui' import StyleModal from '@/components/codeLayout/StyleModal' +import { RUNTIME_NODE_COMPONENTS } from '@/components/componentEditor/runtimeNodes' +import CanvasFrame from '@/components/visualDesigner/CanvasFrame' import VisualCanvas, { DESIGNER_DRAG_TYPE } from '@/components/visualDesigner/VisualCanvas' import { beginDesignerDragSoon, @@ -843,6 +846,20 @@ const parseGeneratedJsxProps = ( return props } +/** + * Tasarimci ile uretilmis kodu tanir. Elle yazilmis bir bilesenin kodu dugum agacina + * cevrilemez: donusum yalnizca tanidigi etiketleri toplar, gerisini (duz HTML, metin, + * yardimci bilesenler) sessizce duserdi ve kaydedildiginde kodun kendisi kaybolurdu. + * Bu yuzden imzasi olmayan kod "kod modu" olarak acilir. + */ +const isDesignerGeneratedCode = (code: string | undefined) => + Boolean( + code && + (code.includes('__SOZSOFT_VISUAL_DESIGNER__') || + /const \[data_[A-Za-z0-9_$]+, setData_/.test(code) || + code.includes('const designerRefs')), + ) + const migrateGeneratedCodeToDesignerDocument = ( code: string | undefined, definitions: Map, @@ -1475,7 +1492,7 @@ const VisualComponentDesigner = () => { ? null : parseDesignerDocumentFromCode(component.code) const migratedCodeDocument = - hasStoredDesignerNodes || codeBackupDocument + hasStoredDesignerNodes || codeBackupDocument || !isDesignerGeneratedCode(component.code) ? null : migrateGeneratedCodeToDesignerDocument(component.code, catalogByName) const visualDocument = hasStoredDesignerNodes @@ -1488,7 +1505,8 @@ const VisualComponentDesigner = () => { setManualCode(component.code || '') setSavedCode(component.code || '') setSavedDesignerFingerprint(JSON.stringify(initialDocument)) - setWorkspaceTab('design') + // Elle yazilmis bilesen kod sekmesiyle acilir; tasarim kanvasi onun icin bos olurdu. + setWorkspaceTab(initialDocument.sourceMode === 'visual' ? 'design' : 'code') setLoadedId(id) undoStack.current = [] redoStack.current = [] @@ -1587,6 +1605,22 @@ const VisualComponentDesigner = () => { [document.nodes, selectedId], ) const selectedDefinition = selectedNode ? catalogByName.get(selectedNode.type) : undefined + /** + * Ok tuslari kardesler arasinda yer degistirir; sinira gelindiginde dugum bir ust + * seviyeye tasinir. Yalnizca kok seviyedeki ilk/son dugumun gidecegi yer yoktur. + */ + const selectedMoveRange = useMemo(() => { + if (!selectedNode) return { up: false, down: false } + const ancestors = findDesignerAncestors(document.nodes, selectedNode.id) + const parent = ancestors?.[ancestors.length - 1] + const siblings = parent ? parent.children : document.nodes + const index = siblings.findIndex((node) => node.id === selectedNode.id) + + return { + up: Boolean(parent) || index > 0, + down: Boolean(parent) || index < siblings.length - 1, + } + }, [document.nodes, selectedNode]) /** Every addressable component of the page, keyed by ref. */ const documentRefs = useMemo(() => collectDesignerRefs(document.nodes), [document.nodes]) @@ -2804,10 +2838,14 @@ const VisualComponentDesigner = () => { [commitDocument, selectDesignerNode], ) + // Bagimlilik listesi yalnizca baska Custom Component'leri sayar; export bu listeyi izleyerek + // bilesenin zincirini toplar. Katalog dugumleri (PlatformIcon, public site parcalari) runtime + // kapsamindan gelir, veritabaninda karsiligi yoktur — listeye girerse export cozulemeyen bir + // referans arar. const collectDependencies = useCallback(() => { const names = new Set() walkDesignerNodes(document.nodes, (node) => { - if (node.kind === 'custom') names.add(node.type) + if (node.kind === 'custom' && !(node.type in RUNTIME_NODE_COMPONENTS)) names.add(node.type) }) return [...names] }, [document.nodes]) @@ -5269,10 +5307,7 @@ const VisualComponentDesigner = () => { onDropComponent={addComponent} onDropComponentBeside={addComponentBeside} onMoveIntoContainer={moveNodeIntoContainerNode} - onMove={moveNode} onReorder={reorderNode} - onDuplicate={duplicateNode} - onDelete={deleteNode} /> ) @@ -5372,7 +5407,7 @@ const VisualComponentDesigner = () => { )}

- Visual Component Designer · {component.routePath} + Visual Component Designer{component.routePath ? ` · ${component.routePath}` : ''}

@@ -5406,21 +5441,25 @@ const VisualComponentDesigner = () => { > {translate('::App.Platform.Reset')} - + {/* Rotasi olmayan bilesen (baska bir ekranin icinde cizilen alt bilgi gibi) + tek basina acilamaz; onizleme dugmesi yalnizca rotasi olanda cikar. */} + {component.routePath?.trim() ? ( + + ) : null}
) : workspaceTab === 'data' ? (
@@ -5932,18 +5977,59 @@ const VisualComponentDesigner = () => {
Selected component
-
- + {/* + Tasi/kopyala/sil kanvastaki secim seridinden buraya tasindi: serit + kucuk bir ikonun ya da ayirici cizginin ustunu tamamen ortuyordu. + Kanvasta yalnizca kose tutamagi kaldi. + */} +
+ {selectedNode.type} - +
+ + + + +
{selectedNode.id}
diff --git a/ui/src/views/public/About.tsx b/ui/src/views/public/About.tsx deleted file mode 100644 index 88e1b0b8..00000000 --- a/ui/src/views/public/About.tsx +++ /dev/null @@ -1,691 +0,0 @@ -import { useEffect, useMemo, useRef, useState } from 'react' -import type { FC } from 'react' -import PageTitle from '@/components/shared/PageTitle' -import { AboutDto } from '@/proxy/about/models' -import { getAbout, saveAboutPage } from '@/services/about' -import { useLocalization } from '@/utils/hooks/useLocalization' -import { PUBLIC_PAGE_DESIGN } from '@/constants/permission.constant' -import { - DesignerLayer, - DesignerSelection, - SelectableBlock, - TranslateFn, - iconField, - imageField, - isLikelyLocalizationKey, - numberField, - booleanField, - resolveLocalizedValue, - styleField, - textAreaField, - textField, - translateLabel, - usePageDesigner, -} from './designer' -import { designerPageClass, getIconColor } from './pageStyles' -import { PageIcon, PublicPageLoader } from './shared' - -interface AboutStatContent { - icon: string - value: string - label: string - labelKey: string - styleClassKey: string - styleClass: string - valueStyleClassKey: string - valueStyleClass: string - labelStyleClassKey: string - labelStyleClass: string - useCounter: boolean - counterEnd: string - counterSuffix: string - counterDuration: number -} - -interface AboutDescriptionContent { - key: string - text: string - styleClassKey: string - styleClass: string -} - -interface AboutSectionContent { - title: string - description: string - titleKey: string - descriptionKey: string - cardStyleClassKey: string - cardStyleClass: string - titleStyleClassKey: string - titleStyleClass: string - descriptionStyleClassKey: string - descriptionStyleClass: string -} - -interface AboutContent { - heroTitle: string - heroTitleKey: string - heroTitleStyleClassKey: string - heroTitleStyleClass: string - heroSubtitle: string - heroSubtitleKey: string - heroSubtitleStyleClassKey: string - heroSubtitleStyleClass: string - heroImage: string - heroImageKey: string - heroSectionStyleClassKey: string - heroSectionStyleClass: string - descriptionsContainerStyleClassKey: string - descriptionsContainerStyleClass: string - stats: AboutStatContent[] - descriptions: AboutDescriptionContent[] - sections: AboutSectionContent[] -} - -const ABOUT_HERO_IMAGE = - 'https://images.pexels.com/photos/3183183/pexels-photo-3183183.jpeg?auto=compress&cs=tinysrgb&w=1920' -const ABOUT_HERO_TITLE_KEY = 'App.About' -const ABOUT_HERO_SUBTITLE_KEY = 'App.PublicAbout.Subtitle' -const ABOUT_HERO_IMAGE_KEY = 'Public.about.heroImage' -const ABOUT_HERO_SECTION_STYLE_KEY = 'Public.about.hero.sectionStyleClass' -const ABOUT_HERO_TITLE_STYLE_KEY = 'Public.about.hero.titleStyleClass' -const ABOUT_HERO_SUBTITLE_STYLE_KEY = 'Public.about.hero.subtitleStyleClass' -const ABOUT_DESCRIPTIONS_CONTAINER_STYLE_KEY = 'Public.about.descriptions.containerStyleClass' - -const HERO_SECTION_STYLE_DEFAULT = 'relative bg-blue-900 text-white py-12' -const HERO_TITLE_STYLE_DEFAULT = 'text-5xl font-bold ml-4 mt-3 mb-2 text-white' -const HERO_SUBTITLE_STYLE_DEFAULT = 'text-xl max-w-3xl ml-4' -const DESCRIPTIONS_CONTAINER_STYLE_DEFAULT = - 'p-5 mx-auto text-gray-800 dark:text-gray-200 text-lg leading-relaxed shadow-md bg-white dark:bg-gray-900 border-l-4 border-blue-600' -const STAT_STYLE_DEFAULT = 'text-center rounded-xl px-4 py-6' -const STAT_VALUE_STYLE_DEFAULT = 'text-4xl font-bold text-gray-900 dark:text-gray-100 mb-2' -const STAT_LABEL_STYLE_DEFAULT = 'text-gray-600 dark:text-gray-300' -const SECTION_CARD_STYLE_DEFAULT = - 'bg-white dark:bg-gray-900 p-8 rounded-xl shadow-lg dark:shadow-gray-950/40' -const SECTION_TITLE_STYLE_DEFAULT = 'text-2xl font-bold text-gray-900 dark:text-gray-100 mb-4' -const SECTION_DESCRIPTION_STYLE_DEFAULT = 'text-gray-700 dark:text-gray-300' - -function getCounterParts(value: string, suffix: string) { - const numericMatch = (value || '0').replace(',', '.').match(/\d+(?:\.\d+)?/) - - if (!numericMatch) { - return null - } - - const endValue = Number(numericMatch[0]) - - if (!Number.isFinite(endValue)) { - return null - } - - const matchIndex = numericMatch.index ?? 0 - - return { - endValue, - prefix: value.slice(0, matchIndex), - suffix: suffix || value.slice(matchIndex + numericMatch[0].length), - hasDecimals: numericMatch[0].includes('.'), - } -} - -/** - * Sayac animasyonu yalnizca deger ekrana girdiginde bir kez calisir; - * boylece sayfanin altindaki istatistikler kullanici oraya gelmeden bitmis olmaz. - */ -const AnimatedStatValue: FC<{ stat: AboutStatContent }> = ({ stat }) => { - const counterParts = useMemo( - () => getCounterParts(stat.counterEnd || stat.value, stat.counterSuffix), - [stat.counterEnd, stat.counterSuffix, stat.value], - ) - const [currentValue, setCurrentValue] = useState(0) - const [isVisible, setIsVisible] = useState(false) - const containerRef = useRef(null) - - useEffect(() => { - const element = containerRef.current - - if (!stat.useCounter || !element || typeof IntersectionObserver === 'undefined') { - setIsVisible(true) - return - } - - const observer = new IntersectionObserver( - (entries) => { - if (entries.some((entry) => entry.isIntersecting)) { - setIsVisible(true) - observer.disconnect() - } - }, - { threshold: 0.3 }, - ) - - observer.observe(element) - - return () => observer.disconnect() - }, [stat.useCounter]) - - useEffect(() => { - if (!stat.useCounter || !counterParts || !isVisible) { - return - } - - const duration = Math.max(stat.counterDuration || 2000, 0) - let animationFrameId = 0 - let startTime: number | null = null - - const animate = (timestamp: number) => { - startTime ??= timestamp - - const progress = duration === 0 ? 1 : Math.min((timestamp - startTime) / duration, 1) - setCurrentValue(counterParts.endValue * (1 - Math.pow(1 - progress, 3))) - - if (progress < 1) { - animationFrameId = requestAnimationFrame(animate) - } - } - - setCurrentValue(0) - animationFrameId = requestAnimationFrame(animate) - - return () => cancelAnimationFrame(animationFrameId) - }, [counterParts, isVisible, stat.counterDuration, stat.useCounter]) - - if (!stat.useCounter || !counterParts) { - return <>{stat.value} - } - - return ( - - {counterParts.prefix} - {counterParts.hasDecimals ? currentValue.toFixed(1) : Math.floor(currentValue)} - {counterParts.suffix} - - ) -} - -function buildAboutContent(about: AboutDto | undefined, translate: TranslateFn): AboutContent { - return { - heroTitle: resolveLocalizedValue(translate, ABOUT_HERO_TITLE_KEY, 'About'), - heroTitleKey: ABOUT_HERO_TITLE_KEY, - heroTitleStyleClassKey: ABOUT_HERO_TITLE_STYLE_KEY, - heroTitleStyleClass: resolveLocalizedValue( - translate, - ABOUT_HERO_TITLE_STYLE_KEY, - HERO_TITLE_STYLE_DEFAULT, - ), - heroSubtitle: resolveLocalizedValue(translate, ABOUT_HERO_SUBTITLE_KEY), - heroSubtitleKey: ABOUT_HERO_SUBTITLE_KEY, - heroSubtitleStyleClassKey: ABOUT_HERO_SUBTITLE_STYLE_KEY, - heroSubtitleStyleClass: resolveLocalizedValue( - translate, - ABOUT_HERO_SUBTITLE_STYLE_KEY, - HERO_SUBTITLE_STYLE_DEFAULT, - ), - heroImage: resolveLocalizedValue(translate, ABOUT_HERO_IMAGE_KEY, ABOUT_HERO_IMAGE), - heroImageKey: ABOUT_HERO_IMAGE_KEY, - heroSectionStyleClassKey: ABOUT_HERO_SECTION_STYLE_KEY, - heroSectionStyleClass: resolveLocalizedValue( - translate, - ABOUT_HERO_SECTION_STYLE_KEY, - HERO_SECTION_STYLE_DEFAULT, - ), - descriptionsContainerStyleClassKey: ABOUT_DESCRIPTIONS_CONTAINER_STYLE_KEY, - descriptionsContainerStyleClass: resolveLocalizedValue( - translate, - ABOUT_DESCRIPTIONS_CONTAINER_STYLE_KEY, - DESCRIPTIONS_CONTAINER_STYLE_DEFAULT, - ), - stats: (about?.statsDto ?? []).map((stat, index) => { - const prefix = `Public.about.dynamic.stat.${index + 1}` - - return { - icon: stat.icon || '', - value: stat.value, - label: resolveLocalizedValue(translate, stat.labelKey, stat.labelKey), - labelKey: - (isLikelyLocalizationKey(stat.labelKey) ? stat.labelKey : '') || `${prefix}.label`, - styleClassKey: `${prefix}.styleClass`, - styleClass: resolveLocalizedValue(translate, `${prefix}.styleClass`, STAT_STYLE_DEFAULT), - valueStyleClassKey: `${prefix}.valueStyleClass`, - valueStyleClass: resolveLocalizedValue( - translate, - `${prefix}.valueStyleClass`, - STAT_VALUE_STYLE_DEFAULT, - ), - labelStyleClassKey: `${prefix}.labelStyleClass`, - labelStyleClass: resolveLocalizedValue( - translate, - `${prefix}.labelStyleClass`, - STAT_LABEL_STYLE_DEFAULT, - ), - useCounter: stat.useCounter ?? false, - counterEnd: stat.counterEnd ?? '', - counterSuffix: stat.counterSuffix ?? '', - counterDuration: stat.counterDuration ?? 2000, - } - }), - descriptions: (about?.descriptionsDto ?? []).map((item, index) => { - const prefix = `Public.about.dynamic.description.${index + 1}` - - return { - key: (isLikelyLocalizationKey(item) ? item : '') || prefix, - text: resolveLocalizedValue(translate, item, item), - styleClassKey: `${prefix}.styleClass`, - styleClass: resolveLocalizedValue( - translate, - `${prefix}.styleClass`, - index % 2 === 0 ? '' : 'text-center p-5 text-blue-800 dark:text-white', - ), - } - }), - sections: (about?.sectionsDto ?? []).map((section) => { - const prefix = `Public.about.dynamic.section.${section.key}` - - return { - title: resolveLocalizedValue(translate, section.key, section.key), - description: resolveLocalizedValue(translate, section.descKey, section.descKey), - titleKey: (isLikelyLocalizationKey(section.key) ? section.key : '') || `${prefix}.title`, - descriptionKey: - (isLikelyLocalizationKey(section.descKey) ? section.descKey : '') || - `${prefix}.description`, - cardStyleClassKey: `${prefix}.cardStyleClass`, - cardStyleClass: resolveLocalizedValue( - translate, - `${prefix}.cardStyleClass`, - SECTION_CARD_STYLE_DEFAULT, - ), - titleStyleClassKey: `${prefix}.titleStyleClass`, - titleStyleClass: resolveLocalizedValue( - translate, - `${prefix}.titleStyleClass`, - SECTION_TITLE_STYLE_DEFAULT, - ), - descriptionStyleClassKey: `${prefix}.descriptionStyleClass`, - descriptionStyleClass: resolveLocalizedValue( - translate, - `${prefix}.descriptionStyleClass`, - SECTION_DESCRIPTION_STYLE_DEFAULT, - ), - } - }), - } -} - -function toSaveInput(content: AboutContent, cultureName: string) { - return { - cultureName, - heroTitleKey: content.heroTitleKey, - heroTitleValue: content.heroTitle, - heroSubtitleKey: content.heroSubtitleKey, - heroSubtitleValue: content.heroSubtitle, - heroImageKey: content.heroImageKey, - heroImageValue: content.heroImage, - stats: content.stats.map((stat, index) => ({ - icon: stat.icon, - value: stat.value, - labelKey: stat.labelKey || `Public.about.dynamic.stat.${index + 1}.label`, - labelValue: stat.label, - useCounter: stat.useCounter, - counterEnd: stat.counterEnd, - counterSuffix: stat.counterSuffix, - counterDuration: stat.counterDuration, - })), - descriptions: content.descriptions.map((item, index) => ({ - key: item.key || `Public.about.dynamic.description.${index + 1}`, - value: item.text, - })), - sections: content.sections.map((section, index) => ({ - titleKey: section.titleKey || `Public.about.dynamic.section.${index + 1}.title`, - titleValue: section.title, - descriptionKey: - section.descriptionKey || `Public.about.dynamic.section.${index + 1}.description`, - descriptionValue: section.description, - })), - styleTexts: [ - { key: content.heroSectionStyleClassKey, value: content.heroSectionStyleClass }, - { key: content.heroTitleStyleClassKey, value: content.heroTitleStyleClass }, - { key: content.heroSubtitleStyleClassKey, value: content.heroSubtitleStyleClass }, - { - key: content.descriptionsContainerStyleClassKey, - value: content.descriptionsContainerStyleClass, - }, - ...content.stats.flatMap((stat) => [ - { key: stat.styleClassKey, value: stat.styleClass }, - { key: stat.valueStyleClassKey, value: stat.valueStyleClass }, - { key: stat.labelStyleClassKey, value: stat.labelStyleClass }, - ]), - ...content.descriptions.map((item) => ({ - key: item.styleClassKey, - value: item.styleClass, - })), - ...content.sections.flatMap((section) => [ - { key: section.cardStyleClassKey, value: section.cardStyleClass }, - { key: section.titleStyleClassKey, value: section.titleStyleClass }, - { key: section.descriptionStyleClassKey, value: section.descriptionStyleClass }, - ]), - ], - } -} - -const About: FC = () => { - const { translate } = useLocalization() - const [loading, setLoading] = useState(true) - const [about, setAbout] = useState() - - useEffect(() => { - const fetchAbout = async () => { - setLoading(true) - - try { - const result = await getAbout() - setAbout(result.data) - } catch (error) { - console.error('About alinirken hata olustu:', error) - } finally { - setLoading(false) - } - } - - fetchAbout() - }, []) - - // Referansi kararli tutmak icin memoize edilir; aksi halde tasarim state'i her render'da yeniden tohumlanir. - const initialContent = useMemo( - () => (loading ? null : buildAboutContent(about, translate)), - [about, loading, translate], - ) - - const designer = usePageDesigner({ - pageKey: 'About', - permission: PUBLIC_PAGE_DESIGN.ABOUT, - initialContent, - onSave: (content, cultureName) => saveAboutPage(toSaveInput(content, cultureName)), - }) - - const { content, isDesignMode, isPanelVisible, selectedBlockId, selectBlock } = designer - - const selection: DesignerSelection | null = useMemo(() => { - if (!content || !selectedBlockId) { - return null - } - - if (selectedBlockId === 'hero') { - return { - id: 'hero', - title: content.heroTitleKey, - description: translateLabel(translate, 'App.PublicDesigner.DesignerDesc4'), - fields: [ - textField('heroTitle', content.heroTitleKey, content.heroTitle), - textAreaField('heroSubtitle', content.heroSubtitleKey, content.heroSubtitle), - imageField('heroImage', content.heroImageKey, content.heroImage, { group: 'media' }), - styleField( - 'heroTitleStyleClass', - content.heroTitleStyleClassKey, - content.heroTitleStyleClass, - ), - styleField( - 'heroSubtitleStyleClass', - content.heroSubtitleStyleClassKey, - content.heroSubtitleStyleClass, - ), - styleField( - 'heroSectionStyleClass', - content.heroSectionStyleClassKey, - content.heroSectionStyleClass, - ), - ], - } - } - - if (selectedBlockId === 'descriptions') { - return { - id: 'descriptions', - title: 'Public.about.description.*', - description: translateLabel(translate, 'Public.designer.descriptionsHint'), - fields: [ - ...content.descriptions.map((item, index) => - textAreaField(`descriptions.${index}.text`, item.key, item.text, { - rows: index % 2 === 0 ? 4 : 3, - }), - ), - styleField( - 'descriptionsContainerStyleClass', - content.descriptionsContainerStyleClassKey, - content.descriptionsContainerStyleClass, - ), - ...content.descriptions.map((item, index) => - styleField(`descriptions.${index}.styleClass`, item.styleClassKey, item.styleClass), - ), - ], - } - } - - if (selectedBlockId.startsWith('stat-')) { - const index = Number(selectedBlockId.replace('stat-', '')) - const stat = content.stats[index] - - if (!stat) { - return null - } - - return { - id: selectedBlockId, - title: stat.labelKey, - description: translateLabel(translate, 'App.PublicDesigner.DesignerDesc1'), - fields: [ - iconField( - `stats.${index}.icon`, - translateLabel(translate, 'App.PublicDesigner.IkonAnahtari'), - stat.icon, - { placeholder: translate('::App.PublicDesigner.IconPlaceholder', { icon: 'FaUsers' }) }, - ), - textField( - `stats.${index}.value`, - translate('::App.Listform.ListformField.Value'), - stat.value, - ), - textField(`stats.${index}.label`, stat.labelKey, stat.label), - booleanField(`stats.${index}.useCounter`, 'useCounter', stat.useCounter, { - group: 'advanced', - placeholder: translateLabel(translate, 'Public.designer.useCounter'), - }), - textField(`stats.${index}.counterEnd`, 'counterEnd', stat.counterEnd, { - group: 'advanced', - helpText: translateLabel(translate, 'Public.designer.counterEndHint'), - }), - textField(`stats.${index}.counterSuffix`, 'counterSuffix', stat.counterSuffix, { - group: 'advanced', - }), - numberField( - `stats.${index}.counterDuration`, - 'counterDuration (ms)', - stat.counterDuration, - { - group: 'advanced', - }, - ), - styleField(`stats.${index}.styleClass`, stat.styleClassKey, stat.styleClass), - styleField( - `stats.${index}.valueStyleClass`, - stat.valueStyleClassKey, - stat.valueStyleClass, - ), - styleField( - `stats.${index}.labelStyleClass`, - stat.labelStyleClassKey, - stat.labelStyleClass, - ), - ], - } - } - - if (selectedBlockId.startsWith('section-')) { - const index = Number(selectedBlockId.replace('section-', '')) - const section = content.sections[index] - - if (!section) { - return null - } - - return { - id: selectedBlockId, - title: section.titleKey, - description: translateLabel(translate, 'Public.designer.sectionHint'), - fields: [ - textField(`sections.${index}.title`, section.titleKey, section.title), - textAreaField( - `sections.${index}.description`, - section.descriptionKey, - section.description, - ), - styleField( - `sections.${index}.cardStyleClass`, - section.cardStyleClassKey, - section.cardStyleClass, - ), - styleField( - `sections.${index}.titleStyleClass`, - section.titleStyleClassKey, - section.titleStyleClass, - ), - styleField( - `sections.${index}.descriptionStyleClass`, - section.descriptionStyleClassKey, - section.descriptionStyleClass, - ), - ], - } - } - - return null - }, [content, selectedBlockId, translate]) - - if (loading) { - return - } - - return ( - <> - - -
- {/* Hero */} - -
-
-
-
-

- {content?.heroTitle} -

-

- {content?.heroSubtitle} -

-
-
- - - {/* Stats */} -
-
-
- {content?.stats.map((stat, index) => ( - -
- -
- -
-
- {stat.label} -
-
-
- ))} -
-
-
- - {/* Descriptions + sections */} -
-
-
- -
- {content?.descriptions.map((item, index) => ( -

- {item.text} -

- ))} -
-
-
- -
- {content?.sections.map((section, index) => ( - -
-

- {section.title} -

-

- {section.description} -

-
-
- ))} -
-
-
- - -
- - ) -} - -export default About diff --git a/ui/src/views/public/Blog.tsx b/ui/src/views/public/Blog.tsx index 30595078..36285422 100644 --- a/ui/src/views/public/Blog.tsx +++ b/ui/src/views/public/Blog.tsx @@ -7,7 +7,7 @@ import { BlogCategory, BlogPost } from '@/proxy/blog/blog' import { blogService } from '@/services/blog.service' import { useLocalization } from '@/utils/hooks/useLocalization' import { Button } from '@/components/ui' -import { translateLabel } from './designer' +import { translateLabel } from './localization' import { PublicPageLoader } from './shared' import Input from '@/components/ui/Input' diff --git a/ui/src/views/public/BlogDetail.tsx b/ui/src/views/public/BlogDetail.tsx index 7321b27e..b574fa67 100644 --- a/ui/src/views/public/BlogDetail.tsx +++ b/ui/src/views/public/BlogDetail.tsx @@ -7,7 +7,7 @@ import { blogService } from '@/services/blog.service' import { useLocalization } from '@/utils/hooks/useLocalization' import { useStoreState } from '@/store/store' import { ROUTES_ENUM } from '@/routes/route.constant' -import { translateLabel } from './designer' +import { translateLabel } from './localization' import { PublicPageLoader } from './shared' const CenteredMessage: React.FC<{ children: React.ReactNode }> = ({ children }) => ( diff --git a/ui/src/views/public/Contact.tsx b/ui/src/views/public/Contact.tsx deleted file mode 100644 index 98fc968e..00000000 --- a/ui/src/views/public/Contact.tsx +++ /dev/null @@ -1,608 +0,0 @@ -import React, { useEffect, useMemo, useState } from 'react' -import { - FaPhone, - FaBuilding, - FaCalendarAlt, - FaCalendarCheck, - FaRegComment, - FaMapMarkerAlt, - FaEnvelope, - FaIdCard, -} from 'react-icons/fa' -import PageTitle from '@/components/shared/PageTitle' -import { ContactDto } from '@/proxy/contact/models' -import { getContact, saveContactPage } from '@/services/contact.service' -import { useLocalization } from '@/utils/hooks/useLocalization' -import { PUBLIC_PAGE_DESIGN } from '@/constants/permission.constant' -import { - DesignerLayer, - DesignerSelection, - SelectableBlock, - TranslateFn, - booleanField, - imageField, - resolveLocalizedValue, - styleField, - textAreaField, - textField, - translateLabel, - usePageDesigner, -} from './designer' -import { designerPageClass } from './pageStyles' -import { PublicPageLoader } from './shared' - -interface ContactContent { - heroTitle: string - heroTitleKey: string - heroSectionStyleClass: string - heroSectionStyleClassKey: string - heroTitleStyleClass: string - heroTitleStyleClassKey: string - heroSubtitle: string - heroSubtitleKey: string - heroSubtitleStyleClass: string - heroSubtitleStyleClassKey: string - heroImage: string - heroImageKey: string - contactInfoTitle: string - contactInfoTitleKey: string - contactInfoCardStyleClass: string - contactInfoCardStyleClassKey: string - contactInfoTitleStyleClass: string - contactInfoTitleStyleClassKey: string - address: string - addressKey: string - phoneNumber: string - email: string - location: string - taxNumber: string - bankTitle: string - bankTitleKey: string - bankAccountHolder: string - bankBranch: string - bankAccountNumber: string - bankIban: string - bankStyleClass: string - workHoursTitle: string - workHoursTitleKey: string - workWeekday: string - workWeekdayKey: string - workWeekend: string - workWeekendKey: string - workWhatsapp: string - workWhatsappKey: string - workHoursStyleClass: string - mapTitle: string - mapTitleKey: string - mapSrc: string - mapWidth: string - mapHeight: string - mapAllowFullScreen: boolean - mapLoading: string - mapReferrerPolicy: string - mapContainerStyleClass: string - mapFrameStyleClass: string -} - -const CONTACT_HERO_TITLE_KEY = 'App.Contact' -const CONTACT_HERO_SUBTITLE_KEY = 'App.PublicContact.Subtitle' -const CONTACT_HERO_IMAGE_KEY = 'Public.contact.heroImage' -const CONTACT_HERO_IMAGE_DEFAULT = - 'https://images.pexels.com/photos/3183171/pexels-photo-3183171.jpeg?auto=compress&cs=tinysrgb&w=1920' -const CONTACT_INFO_TITLE_KEY = 'Abp.Identity.User.UserInformation.ContactInformation' -const CONTACT_BANK_TITLE_KEY = 'App.ContactBank.BankTitle' -const CONTACT_WORK_HOURS_TITLE_KEY = 'App.Definitions.WorkHour' -const CONTACT_HERO_SECTION_STYLE_KEY = 'Public.contact.hero.sectionStyleClass' -const CONTACT_HERO_TITLE_STYLE_KEY = 'Public.contact.hero.titleStyleClass' -const CONTACT_HERO_SUBTITLE_STYLE_KEY = 'Public.contact.hero.subtitleStyleClass' -const CONTACT_INFO_CARD_STYLE_KEY = 'Public.contact.info.cardStyleClass' -const CONTACT_INFO_TITLE_STYLE_KEY = 'Public.contact.info.titleStyleClass' - -const HERO_SECTION_STYLE_DEFAULT = 'relative bg-blue-900 py-12 text-white dark:bg-gray-950' -const HERO_TITLE_STYLE_DEFAULT = 'ml-4 mb-2 mt-3 text-5xl font-bold text-white' -const HERO_SUBTITLE_STYLE_DEFAULT = 'ml-4 max-w-3xl text-xl' -const CARD_STYLE_DEFAULT = - 'rounded-xl bg-white p-8 shadow-lg dark:bg-gray-900 dark:shadow-gray-950/40' -const INFO_TITLE_STYLE_DEFAULT = 'mb-6 text-2xl font-bold text-gray-900 dark:text-gray-100' -const MAP_FRAME_STYLE_DEFAULT = - 'aspect-w-16 aspect-h-9 overflow-hidden rounded-xl bg-gray-200 dark:bg-gray-800' - -function buildContactContent( - contact: ContactDto | undefined, - translate: TranslateFn, -): ContactContent { - return { - heroTitle: resolveLocalizedValue(translate, CONTACT_HERO_TITLE_KEY, 'Contact'), - heroTitleKey: CONTACT_HERO_TITLE_KEY, - heroSectionStyleClass: resolveLocalizedValue( - translate, - CONTACT_HERO_SECTION_STYLE_KEY, - HERO_SECTION_STYLE_DEFAULT, - ), - heroSectionStyleClassKey: CONTACT_HERO_SECTION_STYLE_KEY, - heroTitleStyleClass: resolveLocalizedValue( - translate, - CONTACT_HERO_TITLE_STYLE_KEY, - HERO_TITLE_STYLE_DEFAULT, - ), - heroTitleStyleClassKey: CONTACT_HERO_TITLE_STYLE_KEY, - heroSubtitle: resolveLocalizedValue(translate, CONTACT_HERO_SUBTITLE_KEY), - heroSubtitleKey: CONTACT_HERO_SUBTITLE_KEY, - heroSubtitleStyleClass: resolveLocalizedValue( - translate, - CONTACT_HERO_SUBTITLE_STYLE_KEY, - HERO_SUBTITLE_STYLE_DEFAULT, - ), - heroSubtitleStyleClassKey: CONTACT_HERO_SUBTITLE_STYLE_KEY, - heroImage: resolveLocalizedValue(translate, CONTACT_HERO_IMAGE_KEY, CONTACT_HERO_IMAGE_DEFAULT), - heroImageKey: CONTACT_HERO_IMAGE_KEY, - contactInfoTitle: resolveLocalizedValue(translate, CONTACT_INFO_TITLE_KEY), - contactInfoTitleKey: CONTACT_INFO_TITLE_KEY, - contactInfoCardStyleClass: resolveLocalizedValue( - translate, - CONTACT_INFO_CARD_STYLE_KEY, - CARD_STYLE_DEFAULT, - ), - contactInfoCardStyleClassKey: CONTACT_INFO_CARD_STYLE_KEY, - contactInfoTitleStyleClass: resolveLocalizedValue( - translate, - CONTACT_INFO_TITLE_STYLE_KEY, - INFO_TITLE_STYLE_DEFAULT, - ), - contactInfoTitleStyleClassKey: CONTACT_INFO_TITLE_STYLE_KEY, - address: resolveLocalizedValue(translate, contact?.address, ''), - addressKey: contact?.address || '', - phoneNumber: contact?.phoneNumber || '', - email: contact?.email || '', - location: contact?.location || '', - taxNumber: contact?.taxNumber || '', - bankTitle: resolveLocalizedValue(translate, CONTACT_BANK_TITLE_KEY), - bankTitleKey: CONTACT_BANK_TITLE_KEY, - bankAccountHolder: contact?.bankDto?.accountHolder || '', - bankBranch: contact?.bankDto?.branch || '', - bankAccountNumber: contact?.bankDto?.accountNumber || '', - bankIban: contact?.bankDto?.iban || '', - bankStyleClass: contact?.bankDto?.styleClass || CARD_STYLE_DEFAULT, - workHoursTitle: resolveLocalizedValue(translate, CONTACT_WORK_HOURS_TITLE_KEY), - workHoursTitleKey: CONTACT_WORK_HOURS_TITLE_KEY, - workWeekday: resolveLocalizedValue(translate, contact?.workHoursDto?.weekday, ''), - workWeekdayKey: contact?.workHoursDto?.weekday || '', - workWeekend: resolveLocalizedValue(translate, contact?.workHoursDto?.weekend, ''), - workWeekendKey: contact?.workHoursDto?.weekend || '', - workWhatsapp: resolveLocalizedValue(translate, contact?.workHoursDto?.whatsapp, ''), - workWhatsappKey: contact?.workHoursDto?.whatsapp || '', - workHoursStyleClass: contact?.workHoursDto?.styleClass || CARD_STYLE_DEFAULT, - mapTitle: resolveLocalizedValue(translate, contact?.mapDto?.title, ''), - mapTitleKey: contact?.mapDto?.title || '', - mapSrc: contact?.mapDto?.src || '', - mapWidth: contact?.mapDto?.width || '100%', - mapHeight: contact?.mapDto?.height || '700', - mapAllowFullScreen: contact?.mapDto?.allowFullScreen ?? true, - mapLoading: contact?.mapDto?.loading || 'lazy', - mapReferrerPolicy: contact?.mapDto?.referrerPolicy || 'no-referrer-when-downgrade', - mapContainerStyleClass: contact?.mapDto?.containerStyleClass || CARD_STYLE_DEFAULT, - mapFrameStyleClass: contact?.mapDto?.frameStyleClass || MAP_FRAME_STYLE_DEFAULT, - } -} - -function toSaveInput(content: ContactContent, cultureName: string) { - return { - cultureName, - heroTitleKey: content.heroTitleKey, - heroTitleValue: content.heroTitle, - heroSubtitleKey: content.heroSubtitleKey, - heroSubtitleValue: content.heroSubtitle, - heroImageKey: content.heroImageKey, - heroImageValue: content.heroImage, - contactInfoTitleKey: content.contactInfoTitleKey, - contactInfoTitleValue: content.contactInfoTitle, - addressKey: content.addressKey, - addressValue: content.address, - phoneNumber: content.phoneNumber, - email: content.email, - location: content.location, - taxNumber: content.taxNumber, - bankTitleKey: content.bankTitleKey, - bankTitleValue: content.bankTitle, - bankAccountHolder: content.bankAccountHolder, - bankBranch: content.bankBranch, - bankAccountNumber: content.bankAccountNumber, - bankIban: content.bankIban, - bankStyleClass: content.bankStyleClass, - workHoursTitleKey: content.workHoursTitleKey, - workHoursTitleValue: content.workHoursTitle, - workWeekdayKey: content.workWeekdayKey, - workWeekdayValue: content.workWeekday, - workWeekendKey: content.workWeekendKey, - workWeekendValue: content.workWeekend, - workWhatsappKey: content.workWhatsappKey, - workWhatsappValue: content.workWhatsapp, - workHoursStyleClass: content.workHoursStyleClass, - mapTitleKey: content.mapTitleKey, - mapTitleValue: content.mapTitle, - mapSrc: content.mapSrc, - mapWidth: content.mapWidth, - mapHeight: content.mapHeight, - mapAllowFullScreen: content.mapAllowFullScreen, - mapLoading: content.mapLoading, - mapReferrerPolicy: content.mapReferrerPolicy, - mapContainerStyleClass: content.mapContainerStyleClass, - mapFrameStyleClass: content.mapFrameStyleClass, - styleTexts: [ - { key: content.heroSectionStyleClassKey, value: content.heroSectionStyleClass }, - { key: content.heroTitleStyleClassKey, value: content.heroTitleStyleClass }, - { key: content.heroSubtitleStyleClassKey, value: content.heroSubtitleStyleClass }, - { key: content.contactInfoCardStyleClassKey, value: content.contactInfoCardStyleClass }, - { key: content.contactInfoTitleStyleClassKey, value: content.contactInfoTitleStyleClass }, - ], - } -} - -const InfoRow: React.FC<{ icon: React.ReactNode; children: React.ReactNode }> = ({ - icon, - children, -}) => ( -
- {icon} -

{children}

-
-) - -const Contact: React.FC = () => { - const { translate } = useLocalization() - const [loading, setLoading] = useState(true) - const [contact, setContact] = useState() - - useEffect(() => { - const fetchContact = async () => { - setLoading(true) - - try { - const result = await getContact() - setContact(result.data) - } catch (error) { - console.error('Contact alinirken hata olustu:', error) - } finally { - setLoading(false) - } - } - - fetchContact() - }, []) - - const initialContent = useMemo( - () => (loading ? null : buildContactContent(contact, translate)), - [contact, loading, translate], - ) - - const designer = usePageDesigner({ - pageKey: 'Contact', - permission: PUBLIC_PAGE_DESIGN.CONTACT, - initialContent, - onSave: (content, cultureName) => saveContactPage(toSaveInput(content, cultureName)), - }) - - const { content, isDesignMode, isPanelVisible, selectedBlockId, selectBlock } = designer - - const selection: DesignerSelection | null = useMemo(() => { - if (!content || !selectedBlockId) { - return null - } - - if (selectedBlockId === 'hero') { - return { - id: 'hero', - title: 'Public.contact.hero.*', - description: translateLabel(translate, 'App.PublicDesigner.DesignerDesc4'), - fields: [ - textField('heroTitle', content.heroTitleKey, content.heroTitle), - textAreaField('heroSubtitle', content.heroSubtitleKey, content.heroSubtitle), - imageField('heroImage', content.heroImageKey, content.heroImage, { group: 'media' }), - styleField( - 'heroTitleStyleClass', - content.heroTitleStyleClassKey, - content.heroTitleStyleClass, - ), - styleField( - 'heroSubtitleStyleClass', - content.heroSubtitleStyleClassKey, - content.heroSubtitleStyleClass, - ), - styleField( - 'heroSectionStyleClass', - content.heroSectionStyleClassKey, - content.heroSectionStyleClass, - ), - ], - } - } - - if (selectedBlockId === 'contact-info') { - return { - id: selectedBlockId, - title: content.contactInfoTitleKey, - description: translateLabel(translate, 'App.PublicDesigner.DesignerDesc5'), - fields: [ - textField('contactInfoTitle', content.contactInfoTitleKey, content.contactInfoTitle), - textAreaField('address', content.addressKey || 'Public.contact.address', content.address), - textField('phoneNumber', 'Public.contact.phone', content.phoneNumber), - textField('email', 'Public.contact.email', content.email), - textField('location', 'Public.contact.location.plain', content.location), - textField('taxNumber', 'App.PublicContact.TaxNumber', content.taxNumber), - styleField( - 'contactInfoTitleStyleClass', - content.contactInfoTitleStyleClassKey, - content.contactInfoTitleStyleClass, - ), - styleField( - 'contactInfoCardStyleClass', - content.contactInfoCardStyleClassKey, - content.contactInfoCardStyleClass, - ), - ], - } - } - - if (selectedBlockId === 'bank') { - return { - id: selectedBlockId, - title: content.bankTitleKey, - description: translateLabel(translate, 'App.PublicDesigner.DesignerDesc6'), - fields: [ - textField('bankTitle', content.bankTitleKey, content.bankTitle), - textField( - 'bankAccountHolder', - 'Public.contact.bank.accountHolder', - content.bankAccountHolder, - ), - textField('bankBranch', 'Public.contact.bank.branch', content.bankBranch), - textField( - 'bankAccountNumber', - 'Public.contact.bank.accountNumber', - content.bankAccountNumber, - ), - textField('bankIban', 'Public.contact.bank.iban', content.bankIban), - styleField('bankStyleClass', 'Public.contact.bank.styleClass', content.bankStyleClass), - ], - } - } - - if (selectedBlockId === 'work-hours') { - return { - id: selectedBlockId, - title: content.workHoursTitleKey, - description: translateLabel(translate, 'App.PublicDesigner.DesignerDesc7'), - fields: [ - textField('workHoursTitle', content.workHoursTitleKey, content.workHoursTitle), - textField('workWeekday', content.workWeekdayKey || 'weekday', content.workWeekday), - textField('workWeekend', content.workWeekendKey || 'weekend', content.workWeekend), - textField('workWhatsapp', content.workWhatsappKey || 'whatsapp', content.workWhatsapp), - styleField( - 'workHoursStyleClass', - 'Public.contact.workHours.styleClass', - content.workHoursStyleClass, - ), - ], - } - } - - if (selectedBlockId === 'map') { - return { - id: selectedBlockId, - title: content.mapTitleKey || 'Public.contact.map', - description: translateLabel(translate, 'App.PublicDesigner.DesignerDesc8'), - fields: [ - textField( - 'mapTitle', - content.mapTitleKey || 'Public.contact.map.title', - content.mapTitle, - ), - textAreaField('mapSrc', 'Public.contact.map.src', content.mapSrc, { group: 'media' }), - textField('mapWidth', 'Public.contact.map.width', content.mapWidth, { - group: 'advanced', - }), - textField('mapHeight', 'Public.contact.map.height', content.mapHeight, { - group: 'advanced', - }), - booleanField( - 'mapAllowFullScreen', - 'Public.contact.map.allowFullScreen', - content.mapAllowFullScreen, - { group: 'advanced' }, - ), - textField('mapLoading', 'Public.contact.map.loading', content.mapLoading, { - group: 'advanced', - placeholder: 'lazy | eager', - }), - textField( - 'mapReferrerPolicy', - 'Public.contact.map.referrerPolicy', - content.mapReferrerPolicy, - { - group: 'advanced', - }, - ), - styleField( - 'mapContainerStyleClass', - 'Public.contact.map.containerStyleClass', - content.mapContainerStyleClass, - ), - styleField( - 'mapFrameStyleClass', - 'Public.contact.map.frameStyleClass', - content.mapFrameStyleClass, - ), - ], - } - } - - return null - }, [content, selectedBlockId, translate]) - - if (loading) { - return - } - - return ( -
- - - -
-
-
-
-

- {content?.heroTitle} -

-

- {content?.heroSubtitle} -

-
-
- - -
-
-
-
- -
-

- {content?.contactInfoTitle} -

-
- }> - {content?.address} - - }>{content?.phoneNumber} - }> - - {content?.email} - - - }>{content?.location} - }>{content?.taxNumber} -
-
-
- - -
-

- {content?.bankTitle} -

- {content?.bankAccountHolder -

- {content?.bankAccountHolder} -

-

- {content?.bankBranch} -

-

- {content?.bankAccountNumber} -

-

{content?.bankIban}

-
-
- - -
-

- {content?.workHoursTitle} -

-
-
- -

{content?.workWeekday}

-
-
- -

{content?.workWeekend}

-
-
- -

{content?.workWhatsapp}

-
-
-
-
-
- - -
-

- {content?.mapTitle} -

-
- {content?.mapSrc && ( -