From c993b77b5d6abee572b0af51856c0ab13bd72d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Mon, 17 Aug 2026 08:56:52 +0300 Subject: [PATCH] =?UTF-8?q?Seeds=20klas=C3=B6r=C3=BC=20path=20de=C4=9Fi?= =?UTF-8?q?=C5=9Ftirildi.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 19 ++- .../ISqlObjectManagerAppService.cs | 8 +- .../SqlExecutionDto.cs | 4 +- .../SqlObjectManagerAppService.cs | 59 ++++--- .../FileManagementAppService.cs | 47 ++---- .../ListForms/ListFormWizardAppService.cs | 32 +--- .../ListForms/ListFormWorkflowAppService.cs | 109 ++++++++++--- .../AfterAllMigrationsSqlExecutor.cs | 20 ++- .../Migrations/LanguagesData.json | 150 ++++++++++++++++++ .../Migrations/SqlDataSeeder.cs | 86 +++++++--- .../Migrations/WizardDataSeeder.cs | 8 +- .../Sozsoft.Platform.DbMigrator.csproj | 44 ----- .../appsettings.Dev.json | 3 +- .../appsettings.Production.json | 3 +- .../Data/SeedPathResolver.cs | 104 ++++++++++++ .../DbStartup/SetupMigrationRunner.cs | 12 +- .../appsettings.Dev.json | 4 +- .../appsettings.Production.json | 4 +- configs/deployment/docker-compose-app.yml | 12 +- ...ieldsDictionaryJson_SaveDbTableColumns.sql | 0 .../ObjectData}/Adm_T_DatabaseBackupAll.sql | 0 .../Adm_T_DatabaseBackupFilesDeleteAll.sql | 0 .../Sas_H_BackgroundWorker_JobFlow.sql | 0 .../Sas_H_ListFormFieldsDictionaryJson.sql | 0 .../Sas_H_OrderItem_ByTenantId.sql | 0 ...ieldsDictionaryJson_SaveDbTableColumns.sql | 0 .../ObjectData}/Adm_T_DatabaseBackupAll.sql | 0 .../Adm_T_DatabaseBackupFilesDeleteAll.sql | 0 .../Sas_H_BackgroundWorker_JobFlow.sql | 0 .../Sas_H_ListFormFieldsDictionaryJson.sql | 0 .../Sas_H_OrderItem_ByTenantId.sql | 0 ui/src/utils/workflow/workflowConstants.ts | 4 +- ui/src/utils/workflow/workflowHelpers.ts | 33 +++- ui/src/views/admin/files/FileManager.tsx | 4 +- .../listForm/workflow/WorkflowCanvas.tsx | 14 +- .../views/developerKit/SqlObjectExplorer.tsx | 2 +- ui/src/views/developerKit/SqlQueryManager.tsx | 2 +- .../developerKit/SqlTableDesignerDialog.tsx | 2 +- ui/src/views/form/notes/NoteList.tsx | 16 +- ui/src/views/list/useListFormColumns.ts | 14 ++ ui/src/views/list/useToolbar.tsx | 33 ++-- ui/src/views/public/Blog.tsx | 4 +- 42 files changed, 621 insertions(+), 235 deletions(-) create mode 100644 api/src/Sozsoft.Platform.Domain/Data/SeedPathResolver.cs rename {api/src/Sozsoft.Platform.DbMigrator/Seeds => configs/seeds}/PostgresData/HostData/ExecuteData/Sas_H_ListFormFieldsDictionaryJson_SaveDbTableColumns.sql (100%) rename {api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData => configs/seeds/PostgresData/HostData/ObjectData}/Adm_T_DatabaseBackupAll.sql (100%) rename {api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData => configs/seeds/PostgresData/HostData/ObjectData}/Adm_T_DatabaseBackupFilesDeleteAll.sql (100%) rename {api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData => configs/seeds/PostgresData/HostData/ObjectData}/Sas_H_BackgroundWorker_JobFlow.sql (100%) rename {api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData => configs/seeds/PostgresData/HostData/ObjectData}/Sas_H_ListFormFieldsDictionaryJson.sql (100%) rename {api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData => configs/seeds/PostgresData/HostData/ObjectData}/Sas_H_OrderItem_ByTenantId.sql (100%) rename {api/src/Sozsoft.Platform.DbMigrator/Seeds => configs/seeds}/SqlData/HostData/ExecuteData/Sas_H_ListFormFieldsDictionaryJson_SaveDbTableColumns.sql (100%) rename {api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData => configs/seeds/SqlData/HostData/ObjectData}/Adm_T_DatabaseBackupAll.sql (100%) rename {api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData => configs/seeds/SqlData/HostData/ObjectData}/Adm_T_DatabaseBackupFilesDeleteAll.sql (100%) rename {api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData => configs/seeds/SqlData/HostData/ObjectData}/Sas_H_BackgroundWorker_JobFlow.sql (100%) rename {api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData => configs/seeds/SqlData/HostData/ObjectData}/Sas_H_ListFormFieldsDictionaryJson.sql (100%) rename {api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData => configs/seeds/SqlData/HostData/ObjectData}/Sas_H_OrderItem_ByTenantId.sql (100%) diff --git a/README.md b/README.md index 852245d7..69e80b6f 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,8 @@ sozsoft-platform/ ├── configs/ │ ├── docker/ # Yerel geliştirme için veri katmanı compose dosyaları ve scriptler │ ├── deployment/ # Dev/Prod compose dosyaları, nginx/redis/turn konfigürasyonu, deploy scriptleri +│ ├── seeds/ # Runtime'da düzenlenebilen seed'ler: SqlData, PostgresData, WizardData +│ │ # {SqlData|PostgresData}/{HostData|TenantData}/{ObjectData|ExecuteData} │ └── ai/ # AI entegrasyonu (n8n akış tanımları) ├── .github/instructions/ # ai.instructions.md (bağlayıcı platform kuralları), list.instructions.md ├── CLAUDE.md # Claude Code için operasyon kuralları @@ -233,7 +235,20 @@ dotnet run --project api/src/Sozsoft.Platform.DbMigrator DbMigrator; şema migration'larını uygular ve ardından menü, route, permission, dil, host ayarları, ülke/şehir/ilçe verileri ile seed edilmiş ListForm ekranlarını yükler -(`api/src/Sozsoft.Platform.DbMigrator/Seeds/`). +(kodla versiyonlanan sabit seed'ler `api/src/Sozsoft.Platform.DbMigrator/Migrations/`, +runtime'da düzenlenebilen seed'ler `configs/seeds/` altındadır — bkz. `App:SeedsPath`). + +`configs/seeds/{SqlData|PostgresData}` altındaki her `.sql` dosyası, önce veritabanı +kapsamına (`HostData` / `TenantData`), sonra amacına göre bir klasörde durur; klasör +köklerinde dosya bulunmaz: + +| Klasör | Ne zaman çalışır | +| --- | --- | +| `{HostData\|TenantData}/ObjectData` | Seed sırasında nesneyi oluşturur/günceller. SqlTableDesigner ve SqlQueryManager, deploy ettiği script'i çalıştığı kapsamın bu klasörüne yazar (host bağlamı → `HostData`, tenant seçiliyken → `TenantData`). | +| `{HostData\|TenantData}/ExecuteData` | Nesneyi oluşturur; ayrıca tüm migration ve seed'ler bittikten sonra `AfterAllMigrationsSqlExecutor` dosya adından türettiği procedure'ü çalıştırır (dosya adı = procedure adı, `schema.Procedure.sql` de olur). | + +Klasör kökünde kalmış eski `.sql` dosyaları geriye dönük uyumluluk için hâlâ işlenir, +fakat `SqlDataSeeder` bunları uyarı ile loglar — ilgili klasöre taşınmaları beklenir. ### 5.4. Frontend @@ -829,7 +844,7 @@ Yeni ABP modülü eklemek için: Yeni bir modülün nasıl kurulacağını öğrenmek için başvurulacak dosyalar: -- `api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Saas.cs` +- `api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Saas.cs` - `.../Migrations/ListFormSeeder_Administration.cs` - `.../Migrations/MenusData.json` - `.../Migrations/PermissionsData.json` diff --git a/api/modules/Sozsoft.SqlQueryManager/Sozsoft.SqlQueryManager.Application.Contracts/ISqlObjectManagerAppService.cs b/api/modules/Sozsoft.SqlQueryManager/Sozsoft.SqlQueryManager.Application.Contracts/ISqlObjectManagerAppService.cs index d514557e..8b84af5d 100644 --- a/api/modules/Sozsoft.SqlQueryManager/Sozsoft.SqlQueryManager.Application.Contracts/ISqlObjectManagerAppService.cs +++ b/api/modules/Sozsoft.SqlQueryManager/Sozsoft.SqlQueryManager.Application.Contracts/ISqlObjectManagerAppService.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.Application.Services; @@ -30,13 +30,15 @@ public interface ISqlObjectManagerAppService : IApplicationService Task GetNativeObjectDefinitionAsync(string dataSourceCode, string schemaName, string objectName); /// - /// Saves the T-SQL script to Seeds/SqlData/{fileName}.sql in the DbMigrator project. + /// Saves the T-SQL script to {SeedsRoot}/{SqlData|PostgresData}/{HostData|TenantData}/ObjectData/{fileName}.sql + /// (repo: configs/seeds). The scope folder follows the current tenant: host context writes to HostData. /// Called automatically after a successful SqlTableDesigner deploy so the script can be re-seeded. /// Task SaveTableScriptAsync(SaveTableScriptDto input); /// - /// Deletes matching SQL seed files from Seeds/SqlData when objects are dropped from the UI. + /// Deletes matching SQL seed files from the current scope's ObjectData folder when objects are + /// dropped from the UI. /// Non-existing files are ignored. /// Task DeleteSqlDataFilesAsync(DeleteSqlDataFilesDto input); diff --git a/api/modules/Sozsoft.SqlQueryManager/Sozsoft.SqlQueryManager.Application.Contracts/SqlExecutionDto.cs b/api/modules/Sozsoft.SqlQueryManager/Sozsoft.SqlQueryManager.Application.Contracts/SqlExecutionDto.cs index 83f44171..7927dffc 100644 --- a/api/modules/Sozsoft.SqlQueryManager/Sozsoft.SqlQueryManager.Application.Contracts/SqlExecutionDto.cs +++ b/api/modules/Sozsoft.SqlQueryManager/Sozsoft.SqlQueryManager.Application.Contracts/SqlExecutionDto.cs @@ -20,7 +20,7 @@ public class SqlQueryExecutionResultDto } /// -/// Input for saving a T-SQL script file to DbMigrator Seeds/SqlData. +/// Input for saving a T-SQL script file to the seeds folder ({App:SeedsPath}/SqlData). /// public class SaveTableScriptDto { @@ -36,7 +36,7 @@ public class SaveTableScriptDto } /// -/// Input for deleting seed files from DbMigrator Seeds/SqlData. +/// Input for deleting seed files from the seeds folder ({App:SeedsPath}/SqlData). /// public class DeleteSqlDataFilesDto { diff --git a/api/modules/Sozsoft.SqlQueryManager/Sozsoft.SqlQueryManager.Application/SqlObjectManagerAppService.cs b/api/modules/Sozsoft.SqlQueryManager/Sozsoft.SqlQueryManager.Application/SqlObjectManagerAppService.cs index b02b6621..8d210168 100644 --- a/api/modules/Sozsoft.SqlQueryManager/Sozsoft.SqlQueryManager.Application/SqlObjectManagerAppService.cs +++ b/api/modules/Sozsoft.SqlQueryManager/Sozsoft.SqlQueryManager.Application/SqlObjectManagerAppService.cs @@ -8,13 +8,15 @@ using Sozsoft.SqlQueryManager.Domain.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Volo.Abp.Application.Services; using Volo.Abp.MultiTenancy; using System.Text.RegularExpressions; +using Sozsoft.Platform.Data.Seeds; using Sozsoft.Platform.Enums; using Sozsoft.Platform.Queries; +using Sozsoft.Settings; namespace Sozsoft.SqlQueryManager.Application; @@ -48,7 +50,7 @@ public class SqlObjectManagerAppService : ApplicationService, ISqlObjectManagerA private readonly IDataSourceManager _dataSourceManager; private readonly ICurrentTenant _currentTenant; private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IHostEnvironment _hostEnvironment; + private readonly IConfiguration _configuration; private readonly ILogger _logger; public SqlObjectManagerAppService( @@ -57,7 +59,7 @@ public class SqlObjectManagerAppService : ApplicationService, ISqlObjectManagerA IDataSourceManager dataSourceManager, ICurrentTenant currentTenant, IHttpContextAccessor httpContextAccessor, - IHostEnvironment hostEnvironment, + IConfiguration configuration, ILogger logger) { _sqlExecutorService = sqlExecutorService; @@ -65,7 +67,7 @@ public class SqlObjectManagerAppService : ApplicationService, ISqlObjectManagerA _dataSourceManager = dataSourceManager; _currentTenant = currentTenant; _httpContextAccessor = httpContextAccessor; - _hostEnvironment = hostEnvironment; + _configuration = configuration; _logger = logger; } @@ -955,38 +957,31 @@ FROM ( } + /// + /// Script dosyalarinin yazilacagi klasor. SqlDataSeeder ile ayni cozumleyiciyi kullanir + /// (App:SeedsPath -> configs/seeds), aksi halde deploy edilen nesne seed olarak geri okunmaz. + /// + /// Yol, islemin yapildigi veritabani kapsamini izler: host (varsayilan) baglaminda + /// {SqlData|PostgresData}/HostData/ObjectData, bir tenant secilmisken + /// {SqlData|PostgresData}/TenantData/ObjectData. Boylece nesne, seed sirasinda + /// olusturuldugu veritabaninda yeniden olusturulur. + /// + /// private string ResolveSqlDataOutputPath() { - return ResolveSqlDataOutputPath("SqlData"); - } + var dataDirectoryName = SettingsConsts.DefaultDatabaseProvider == SettingsConsts.DatabaseProvider.PostgreSql + ? SeedPathResolver.PostgresDataFolder + : SeedPathResolver.SqlDataFolder; - private string ResolveSqlDataOutputPath(string dataDirectoryName) - { - const string dbMigratorName = "Sozsoft.Platform.DbMigrator"; - var safeDirectoryName = NormalizeSqlDataDirectoryName(dataDirectoryName); - var dir = new DirectoryInfo(_hostEnvironment.ContentRootPath); + var scopeDirectoryName = _currentTenant.Id == null + ? SeedPathResolver.HostDataFolder + : SeedPathResolver.TenantDataFolder; - while (dir != null) - { - var candidate = Path.Combine(dir.FullName, "src", dbMigratorName, "Seeds"); - if (Directory.Exists(candidate)) - return Path.Combine(candidate, safeDirectoryName); - - candidate = Path.Combine(dir.FullName, dbMigratorName, "Seeds"); - if (Directory.Exists(candidate)) - return Path.Combine(candidate, safeDirectoryName); - - dir = dir.Parent; - } - - return Path.Combine(_hostEnvironment.ContentRootPath, "Seeds", safeDirectoryName); - } - - private static string NormalizeSqlDataDirectoryName(string dataDirectoryName) - { - return string.Equals(dataDirectoryName, "PostgresData", StringComparison.OrdinalIgnoreCase) - ? "PostgresData" - : "SqlData"; + return SeedPathResolver.GetPath( + _configuration, + dataDirectoryName, + scopeDirectoryName, + SeedPathResolver.ObjectDataFolder); } } diff --git a/api/src/Sozsoft.Platform.Application/FileManagement/FileManagementAppService.cs b/api/src/Sozsoft.Platform.Application/FileManagement/FileManagementAppService.cs index cf9690da..97d347ed 100644 --- a/api/src/Sozsoft.Platform.Application/FileManagement/FileManagementAppService.cs +++ b/api/src/Sozsoft.Platform.Application/FileManagement/FileManagementAppService.cs @@ -8,6 +8,7 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; using Sozsoft.Platform.BlobStoring; +using Sozsoft.Platform.Data.Seeds; using Sozsoft.Platform.Permissions; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; @@ -32,13 +33,11 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe private const string IndexFileName = "index.json"; /// - /// CDN altinda fiziksel karsiligi olmayan, DbMigrator projesinin Seeds klasorune baglanan - /// sanal kok klasor. Sadece host baglaminda ve + /// CDN altinda fiziksel karsiligi olmayan, mount edilen seed klasorune baglanan sanal kok + /// klasor. Sadece host baglaminda ve /// yetkisi olan kullaniciya gorunur. /// - private const string SeedsFolderName = "Seeds"; - - private const string DbMigratorProjectName = "Sozsoft.Platform.DbMigrator"; + private const string SeedsFolderName = "seeds"; private static readonly HashSet ProtectedFolders = new(StringComparer.OrdinalIgnoreCase) { @@ -93,42 +92,20 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe return string.Equals(firstPart, SeedsFolderName, StringComparison.OrdinalIgnoreCase); } - /// Host + migration yetkisi olmadan DbMigrator Seeds klasoru ne listelenir ne de degistirilir. + /// Host + migration yetkisi olmadan seed klasoru ne listelenir ne de degistirilir. private async Task CanAccessSeedsAsync(string tenantId) { return IsHostTenant(tenantId) && await AuthorizationService.IsGrantedAsync(PlatformPermissions.Setup.Migrate); } - /// DbMigrator projesinin Seeds klasorunu bulur (config ile override edilebilir). + /// + /// Seed klasorunu bulur. Yol cozumu seeder'lar ile ortaktir; boylece File Manager'dan + /// duzenlenen dosyayi migrate isleminde ayni klasorden okuruz. + /// private string ResolveSeedsRootPath() { - var configuredPath = _configuration["App:DbMigratorSeedsPath"]; - if (!string.IsNullOrWhiteSpace(configuredPath)) - { - return Path.GetFullPath(configuredPath); - } - - var dir = new DirectoryInfo(_hostEnvironment.ContentRootPath); - - while (dir != null) - { - var candidate = Path.Combine(dir.FullName, "src", DbMigratorProjectName, "Seeds"); - if (Directory.Exists(candidate)) - { - return candidate; - } - - candidate = Path.Combine(dir.FullName, DbMigratorProjectName, "Seeds"); - if (Directory.Exists(candidate)) - { - return candidate; - } - - dir = dir.Parent; - } - - return Path.Combine(_hostEnvironment.ContentRootPath, "Seeds"); + return SeedPathResolver.GetRootPath(_configuration); } /// @@ -394,13 +371,13 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe } } - /// Kok listeye DbMigrator Seeds klasorunu sanal klasor olarak ekler. + /// Kok listeye seed klasorunu sanal klasor olarak ekler. private void AddSeedsFolderItem(List items, string tenantId) { var seedsRootPath = ResolveSeedsRootPath(); if (!Directory.Exists(seedsRootPath)) { - Logger.LogWarning("DbMigrator seeds directory was not found: {SeedsPath}", seedsRootPath); + Logger.LogWarning("Seeds directory was not found: {SeedsPath}", seedsRootPath); return; } diff --git a/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs b/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs index 4c68c3b4..6116c54e 100644 --- a/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs +++ b/api/src/Sozsoft.Platform.Application/ListForms/ListFormWizardAppService.cs @@ -13,8 +13,10 @@ using Volo.Abp.Domain.Repositories; using Volo.Abp.MultiTenancy; using Volo.Abp.PermissionManagement; using Volo.Abp.Uow; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Sozsoft.Languages; +using Sozsoft.Platform.Data.Seeds; using Sozsoft.Platform.DynamicData; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging; @@ -39,6 +41,7 @@ public class ListFormWizardAppService( IRepository repoMenu, IPermissionGrantRepository permissionGrantRepository, IHostEnvironment hostEnvironment, + IConfiguration configuration, LanguageTextAppService languageTextAppService, IDynamicDataManager dynamicDataManager, ILogger logger @@ -55,6 +58,7 @@ public class ListFormWizardAppService( private readonly IRepository repoMenu = repoMenu; private readonly IPermissionGrantRepository permissionGrantRepository = permissionGrantRepository; private readonly IHostEnvironment _hostEnvironment = hostEnvironment; + private readonly IConfiguration _configuration = configuration; private readonly LanguageTextAppService _languageTextAppService = languageTextAppService; private readonly IDynamicDataManager _dynamicDataManager = dynamicDataManager; private readonly ILogger logger = logger; @@ -531,8 +535,7 @@ public class ListFormWizardAppService( /// /// Wizard konfigürasyonunu JSON dosyası olarak kaydeder. - /// Önce ContentRootPath'ten yukarı çıkarak Sozsoft.Platform.DbMigrator/Seeds/WizardData dizinini arar. - /// Bulamazsa ContentRootPath/Seeds/WizardData altına yazar. + /// Dosya, seeder'lar ve File Manager ile ortak olan seed klasörünün WizardData dizinine yazılır. /// Veritabanı silinip yeniden oluşturulduğunda WizardDataSeeder bu dosyaları okuyarak konfigürasyonu geri yükler. /// private async Task SaveWizardSeedFileAsync(ListFormWizardDto input, bool isDeletedField, bool isCreatedField, WizardInsertedRecordsDto inserted) @@ -755,31 +758,12 @@ public class ListFormWizardAppService( } /// - /// DbMigrator projesinin Seeds/WizardData dizinini ContentRootPath'ten yukarı traversal ile bulur. - /// Tüm işletim sistemlerinde Path.Combine kullanır, separator karakteri içermez. + /// Wizard seed dosyalarinin yazilacagi WizardData dizinini dondurur. + /// Yol cozumu uzerinden seeder'lar ve File Manager ile ortaktir. /// private string ResolveWizardSeedOutputPath() { - const string dbMigratorName = "Sozsoft.Platform.DbMigrator"; - var dir = new DirectoryInfo(_hostEnvironment.ContentRootPath); - - while (dir != null) - { - // src/Sozsoft.Platform.DbMigrator/Seeds altında ara - var candidate = Path.Combine(dir.FullName, "src", dbMigratorName, "Seeds"); - if (Directory.Exists(candidate)) - return Path.Combine(candidate, "WizardData"); - - // Sozsoft.Platform.DbMigrator/Seeds doğrudan altında ara - candidate = Path.Combine(dir.FullName, dbMigratorName, "Seeds"); - if (Directory.Exists(candidate)) - return Path.Combine(candidate, "WizardData"); - - dir = dir.Parent; - } - - // Fallback: çalışan API'nin yanında Seeds/WizardData - return Path.Combine(_hostEnvironment.ContentRootPath, "Seeds", "WizardData"); + return SeedPathResolver.GetPath(_configuration, "WizardData"); } private async Task> GetTableColumnNamesAsync(string dataSourceCode, SelectCommandTypeEnum commandType, string selectCommand) diff --git a/api/src/Sozsoft.Platform.Application/ListForms/ListFormWorkflowAppService.cs b/api/src/Sozsoft.Platform.Application/ListForms/ListFormWorkflowAppService.cs index 755e2db1..6c86e7cc 100644 --- a/api/src/Sozsoft.Platform.Application/ListForms/ListFormWorkflowAppService.cs +++ b/api/src/Sozsoft.Platform.Application/ListForms/ListFormWorkflowAppService.cs @@ -308,11 +308,11 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA var start = context.Criteria.FirstOrDefault(x => x.Kind == "Start") ?? throw new UserFriendlyException("Workflow başlangıç adımı bulunamadı."); - context.WorkflowNoteRows.Add(("Started By: ", ResolveCurrentUserDisplayName())); + context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.StartedBy", "Started By"), ResolveCurrentUserDisplayName())); var result = await RunUntilWaitAsync(context, start); await InsertWorkflowNoteAsync( context, - $"Workflow Started: {start.Title}", + $"{Localize("App.WorkflowNote.SubjectStarted", "Workflow Started")}: {LocalizeNodeTitle(start.Title)}", BuildWorkflowNoteContent(context.WorkflowNoteRows)); return result; @@ -362,16 +362,16 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA await UpdateRowAsync(context, clear); MergeRowValues(context.Row, clear); - context.WorkflowNoteRows.Add(("Reset By: ", ResolveCurrentUserDisplayName())); + context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.ResetBy", "Reset By"), ResolveCurrentUserDisplayName())); if (!previousStatus.IsNullOrWhiteSpace()) { - context.WorkflowNoteRows.Add(("Reset Step: ", previousStatus)); + context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.ResetStep", "Reset Step"), LocalizeNodeTitle(previousStatus))); } var result = await RunUntilWaitAsync(context, start); await InsertWorkflowNoteAsync( context, - $"Workflow Reset: {start.Title}", + $"{Localize("App.WorkflowNote.SubjectReset", "Workflow Reset")}: {LocalizeNodeTitle(start.Title)}", BuildWorkflowNoteContent(context.WorkflowNoteRows)); return result; @@ -443,12 +443,15 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA } var next = FindNextCriteria(context.Criteria, input.Approved ? current.NextOnApprove : current.NextOnReject); - context.WorkflowNoteRows.Add(("Decision By: ", ResolveCurrentUserDisplayName())); + context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.DecisionBy", "Decision By"), ResolveCurrentUserDisplayName())); AddWorkflowDecisionRows(context, current, input.Approved, input.Note ?? string.Empty); var result = await RunUntilWaitAsync(context, next); + var decisionSubject = input.Approved + ? Localize("App.WorkflowNote.SubjectApproved", "Workflow Approved") + : Localize("App.WorkflowNote.SubjectRejected", "Workflow Rejected"); await InsertWorkflowNoteAsync( context, - $"Workflow {(input.Approved ? "Approved" : "Rejected")}: {current.Title}", + $"{decisionSubject}: {LocalizeNodeTitle(current.Title)}", BuildWorkflowNoteContent(context.WorkflowNoteRows)); return result; @@ -790,36 +793,41 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA ?? "System"; } - private static void AddWorkflowDecisionRows( + private void AddWorkflowDecisionRows( WorkflowRunContext context, ListFormWorkflow current, bool approved, string description) { - var action = approved ? "Approved" : "Rejected"; - context.WorkflowNoteRows.Add(("Decision: ", $"{action}: {FormatNode(current)}")); - context.WorkflowNoteRows.Add(("Description: ", description ?? string.Empty)); + var action = approved + ? Localize("App.WorkflowNote.Approved", "Approved") + : Localize("App.WorkflowNote.Rejected", "Rejected"); + context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.Decision", "Decision"), $"{action}: {FormatNode(current)}")); + context.WorkflowNoteRows.Add((NoteLabel("App.WorkflowNote.Description", "Description"), description ?? string.Empty)); } - private static void AddWorkflowNodeRows( + private void AddWorkflowNodeRows( WorkflowRunContext context, ListFormWorkflow node) { var action = node.Kind switch { - "Start" => "Started", - "Compare" => "Evaluated", - "Approval" => "Waiting Approval", - "Inform" => "Informed", - "End" => "Completed", - _ => "Processed" + "Start" => Localize("App.WorkflowNote.Started", "Started"), + "Compare" => Localize("App.WorkflowNote.Evaluated", "Evaluated"), + "Approval" => Localize("App.WorkflowNote.WaitingApproval", "Waiting Approval"), + "Inform" => Localize("App.WorkflowNote.Informed", "Informed"), + "End" => Localize("App.WorkflowNote.Completed", "Completed"), + _ => Localize("App.WorkflowNote.Processed", "Processed") }; context.WorkflowNoteRows.Add(($"{action}: ", $"{FormatNode(node)}")); if (!node.Approver.IsNullOrWhiteSpace()) { - context.WorkflowNoteRows.Add((node.Kind == "Inform" ? "Inform: " : "Approver: ", FormatWorkflowApprovers(node.Approver))); + var approverLabel = node.Kind == "Inform" + ? NoteLabel("App.WorkflowNote.Inform", "Inform") + : NoteLabel("App.WorkflowNote.Approver", "Approver"); + context.WorkflowNoteRows.Add((approverLabel, FormatWorkflowApprovers(node.Approver))); } } @@ -955,16 +963,71 @@ public class ListFormWorkflowAppService : PlatformAppService, IListFormWorkflowA return $"{string.Join(string.Empty, tableRows)}
"; } - private static string FormatNode(ListFormWorkflow node) + private string FormatNode(ListFormWorkflow node) { if (node == null) { return string.Empty; } - var title = node.Title ?? string.Empty; - var kind = node.Kind ?? string.Empty; - return $"{title} ({kind} - {node.Id})"; + var title = LocalizeNodeTitle(node.Title); + var kind = node.Kind.IsNullOrWhiteSpace() + ? string.Empty + : Localize($"App.WorkflowKind.{node.Kind.Trim()}", node.Kind.Trim()); + + return kind.IsNullOrWhiteSpace() ? title : $"{title} ({kind})"; + } + + private string Localize(string key, string fallback) + { + if (key.IsNullOrWhiteSpace()) + { + return fallback; + } + + var value = localizer[key]; + return value.ResourceNotFound ? fallback : value.Value; + } + + private string NoteLabel(string key, string fallback) + { + return $"{Localize(key, fallback)}: "; + } + + /// + /// Workflow adım başlıkları localization key olarak saklanabilir ("App.WorkflowKind.Approval") + /// ve aynı formda tekrar ederse sonuna sıra numarası eklenir ("App.WorkflowKind.Approval1"). + /// + private string LocalizeNodeTitle(string title) + { + if (title.IsNullOrWhiteSpace()) + { + return string.Empty; + } + + var text = title.Trim(); + var direct = localizer[text]; + if (!direct.ResourceNotFound) + { + return direct.Value; + } + + var digitCount = 0; + while (digitCount < text.Length && char.IsDigit(text[text.Length - 1 - digitCount])) + { + digitCount++; + } + + if (digitCount == 0 || digitCount == text.Length) + { + return text; + } + + var baseKey = text[..^digitCount]; + var baseValue = localizer[baseKey]; + return baseValue.ResourceNotFound + ? text + : $"{baseValue.Value} {text[^digitCount..]}"; } private static string Encode(string value) diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/AfterAllMigrationsSqlExecutor.cs b/api/src/Sozsoft.Platform.DbMigrator/Migrations/AfterAllMigrationsSqlExecutor.cs index 71f43786..f8bb145a 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/AfterAllMigrationsSqlExecutor.cs +++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/AfterAllMigrationsSqlExecutor.cs @@ -1,10 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Sozsoft.Platform.EntityFrameworkCore; using Volo.Abp.DependencyInjection; @@ -26,28 +27,31 @@ namespace Sozsoft.Platform.Data.Seeds; public class AfterAllMigrationsSqlExecutor : IAfterAllMigrationsSqlExecutor, ITransientDependency { /// HostData/TenantData altındaki, migration sonrası da çalıştırılacak script klasörünün adı. - public const string DirectoryName = "ExecuteData"; + public const string DirectoryName = SeedPathResolver.ExecuteDataFolder; /// Host kapsamındaki script klasörü. - public const string HostDataDirectoryName = "HostData"; + public const string HostDataDirectoryName = SeedPathResolver.HostDataFolder; /// Tenant kapsamındaki script klasörü (henüz kullanılmıyorsa sessizce atlanır). - public const string TenantDataDirectoryName = "TenantData"; + public const string TenantDataDirectoryName = SeedPathResolver.TenantDataFolder; private static readonly Regex SafeIdentifierRegex = new(@"^[A-Za-z_][A-Za-z0-9_]{0,126}$", RegexOptions.Compiled); private readonly IDbContextProvider _dbContextProvider; private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly IConfiguration _configuration; private readonly ILogger _logger; public AfterAllMigrationsSqlExecutor( IDbContextProvider dbContextProvider, IUnitOfWorkManager unitOfWorkManager, + IConfiguration configuration, ILogger logger) { _dbContextProvider = dbContextProvider; _unitOfWorkManager = unitOfWorkManager; + _configuration = configuration; _logger = logger; } @@ -131,13 +135,13 @@ public class AfterAllMigrationsSqlExecutor : IAfterAllMigrationsSqlExecutor, ITr } } - private static string GetScriptDirectoryPath(string scopeDirectoryName) + private string GetScriptDirectoryPath(string scopeDirectoryName) { var dataDirectoryName = DefaultDatabaseProvider == DatabaseProvider.PostgreSql - ? "PostgresData" - : "SqlData"; + ? SeedPathResolver.PostgresDataFolder + : SeedPathResolver.SqlDataFolder; - return Path.Combine(Directory.GetCurrentDirectory(), "Seeds", dataDirectoryName, scopeDirectoryName, DirectoryName); + return SeedPathResolver.GetPath(_configuration, dataDirectoryName, scopeDirectoryName, DirectoryName); } /// diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json b/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json index 113abbad..f9ba6613 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json @@ -3588,6 +3588,12 @@ "en": "Workflow has already been started for the selected record", "tr": "Seçili kayıt için iş akışı zaten başlamış." }, + { + "resourceName": "Platform", + "key": "App.ListForm.WorkflowStart", + "en": "Workflow Start", + "tr": "İş Akışı Başlat" + }, { "resourceName": "Platform", "key": "App.ListForm.WorkflowReset", @@ -6852,6 +6858,12 @@ "en": "Search blog posts...", "tr": "Blog yazıları ara..." }, + { + "resourceName": "Platform", + "key": "App.PublicBlog.Empty", + "en": "No blog posts found.", + "tr": "Blog yazısı bulunamadı." + }, { "resourceName": "Platform", "key": "App.BlogSubscribe.SubscribeDesc", @@ -18276,6 +18288,126 @@ "en": "Undefined", "tr": "Tanımsız" }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.SubjectStarted", + "en": "Workflow Started", + "tr": "Workflow Başlatıldı" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.SubjectReset", + "en": "Workflow Reset", + "tr": "Workflow Sıfırlandı" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.SubjectApproved", + "en": "Workflow Approved", + "tr": "Workflow Onaylandı" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.SubjectRejected", + "en": "Workflow Rejected", + "tr": "Workflow Reddedildi" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.StartedBy", + "en": "Started By", + "tr": "Başlatan" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.ResetBy", + "en": "Reset By", + "tr": "Sıfırlayan" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.ResetStep", + "en": "Reset Step", + "tr": "Sıfırlanan Adım" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.DecisionBy", + "en": "Decision By", + "tr": "Karar Veren" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.Decision", + "en": "Decision", + "tr": "Karar" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.Description", + "en": "Description", + "tr": "Açıklama" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.Approver", + "en": "Approver", + "tr": "Onaylayacak" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.Inform", + "en": "Inform", + "tr": "Bilgilendirilen" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.Approved", + "en": "Approved", + "tr": "Onaylandı" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.Rejected", + "en": "Rejected", + "tr": "Reddedildi" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.Started", + "en": "Started", + "tr": "Başlatıldı" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.Evaluated", + "en": "Evaluated", + "tr": "Değerlendirildi" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.WaitingApproval", + "en": "Waiting Approval", + "tr": "Onay Bekliyor" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.Informed", + "en": "Informed", + "tr": "Bilgilendirildi" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.Completed", + "en": "Completed", + "tr": "Tamamlandı" + }, + { + "resourceName": "Platform", + "key": "App.WorkflowNote.Processed", + "en": "Processed", + "tr": "İşlendi" + }, { "resourceName": "Platform", "key": "App.MessengerWidget.Title", @@ -25050,6 +25182,12 @@ "en": "Approver", "tr": "Onaylanacak kişi" }, + { + "resourceName": "Platform", + "key": "App.WorkflowKind.Compare", + "en": "Comparison", + "tr": "Karşılaştırma" + }, { "resourceName": "Platform", "key": "App.WorkflowKind.End", @@ -25062,6 +25200,12 @@ "en": "Inform", "tr": "Bilgilendirme" }, + { + "resourceName": "Platform", + "key": "App.WorkflowKind.Start", + "en": "Start", + "tr": "Başlangıç" + }, { "resourceName": "Platform", "key": "App.WorkflowKind.Step", @@ -25074,6 +25218,12 @@ "en": "No rule", "tr": "Kural yok" }, + { + "resourceName": "Platform", + "key": "App.WorkflowOutcome.Next", + "en": "Next", + "tr": "Sonraki" + }, { "resourceName": "Platform", "key": "App.WorkflowOutcome.CompareState", diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/SqlDataSeeder.cs b/api/src/Sozsoft.Platform.DbMigrator/Migrations/SqlDataSeeder.cs index 9012237c..4d416dec 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/SqlDataSeeder.cs +++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/SqlDataSeeder.cs @@ -1,9 +1,11 @@ -using System; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Sozsoft.Platform.EntityFrameworkCore; using Volo.Abp.Data; @@ -14,27 +16,50 @@ using static Sozsoft.Settings.SettingsConsts; namespace Sozsoft.Platform.Data.Seeds; /// -/// SqlTableDesigner üzerinden deploy edilen tabloları Seeds/SqlData/*.sql dosyalarından okuyarak veritabanına uygular. -/// Her dosya, tek bir tabloya (veya ilgili ALTER/INDEX script'lerine) ait IF OBJECT_ID kontrolü içeren T-SQL batch'leri içermelidir. -/// Veritabanı silinip yeniden oluşturulduğunda bu seeder tüm tablo scriptlerini yeniden çalıştırır. +/// SqlTableDesigner ve SqlQueryManager üzerinden deploy edilen nesneleri +/// {SqlData|PostgresData} klasöründeki .sql dosyalarından okuyarak veritabanına uygular. +/// Her dosya, tek bir nesneye (ve ilgili ALTER/INDEX script'lerine) ait varlık kontrolü içeren +/// batch'lerden oluşmalıdır. Veritabanı silinip yeniden oluşturulduğunda tüm scriptler yeniden çalışır. +/// +/// Klasör düzeni — her .sql dosyası amacını belirten bir klasörün altındadır, kökte dosya durmaz: +/// +/// {SqlData|PostgresData}/ +/// HostData/ → host (varsayılan) veritabanı kapsamı +/// ObjectData/ → nesne oluşturulur/güncellenir. SqlTableDesigner ve +/// SqlQueryManager, host bağlamındayken buraya yazar. +/// ExecuteData/ → nesne oluşturulur, ayrıca tüm migration'lar bittikten sonra +/// tarafından çalıştırılır. +/// TenantData/ → tenant veritabanı kapsamı (bir tenant seçiliyken üretilenler) +/// ObjectData/ +/// ExecuteData/ +/// +/// Geriye dönük uyumluluk: klasör köklerinde kalmış eski .sql dosyaları da işlenir, ancak +/// uyarı ile loglanır — taşınmaları beklenir. +/// /// public class SqlDataSeeder : IDataSeedContributor, ITransientDependency { + /// Yalnızca oluşturulan/güncellenen nesne scriptlerinin klasörü. + public const string ObjectDataDirectoryName = SeedPathResolver.ObjectDataFolder; + private readonly IDbContextProvider _dbContextProvider; + private readonly IConfiguration _configuration; private readonly ILogger _logger; public SqlDataSeeder( IDbContextProvider dbContextProvider, + IConfiguration configuration, ILogger logger) { _dbContextProvider = dbContextProvider; + _configuration = configuration; _logger = logger; } public async Task SeedAsync(DataSeedContext context) { var dataDirectoryName = GetDataDirectoryName(); - var sqlDataPath = Path.Combine(Directory.GetCurrentDirectory(), "Seeds", dataDirectoryName); + var sqlDataPath = SeedPathResolver.GetPath(_configuration, dataDirectoryName); if (!Directory.Exists(sqlDataPath)) { _logger.LogInformation("Seeds/{DirectoryName} directory not found, skipping SqlDataSeeder.", dataDirectoryName); @@ -107,15 +132,16 @@ public class SqlDataSeeder : IDataSeedContributor, ITransientDependency private static string GetDataDirectoryName() { return DefaultDatabaseProvider == DatabaseProvider.PostgreSql - ? "PostgresData" - : "SqlData"; + ? SeedPathResolver.PostgresDataFolder + : SeedPathResolver.SqlDataFolder; } - private static string[] GetSqlFiles(string dataDirectoryPath, bool isHostSeed) + private string[] GetSqlFiles(string dataDirectoryPath, bool isHostSeed) { - var sqlFiles = Directory.GetFiles(dataDirectoryPath, "*.sql") - .OrderBy(f => Path.GetFileName(f)) - .ToList(); + var sqlFiles = new List(); + + // Klasor kokunde dosya beklenmez; eskiden kalanlar uyari ile islenir. + sqlFiles.AddRange(GetLegacyRootSqlFiles(dataDirectoryPath)); var scopeDirectoryName = isHostSeed ? AfterAllMigrationsSqlExecutor.HostDataDirectoryName @@ -124,22 +150,44 @@ public class SqlDataSeeder : IDataSeedContributor, ITransientDependency var scopePath = Path.Combine(dataDirectoryPath, scopeDirectoryName); if (Directory.Exists(scopePath)) { - sqlFiles.AddRange(Directory.GetFiles(scopePath, "*.sql") - .OrderBy(f => Path.GetFileName(f))); + sqlFiles.AddRange(GetOrderedSqlFiles(Path.Combine(scopePath, ObjectDataDirectoryName))); + sqlFiles.AddRange(GetLegacyRootSqlFiles(scopePath)); // {HostData|TenantData}/ExecuteData: nesneler burada da oluşturulur, ayrıca tüm // migration bittikten sonra AfterAllMigrationsSqlExecutor dosya adı üzerinden çalıştırır. - var executeDataPath = Path.Combine(scopePath, AfterAllMigrationsSqlExecutor.DirectoryName); - if (Directory.Exists(executeDataPath)) - { - sqlFiles.AddRange(Directory.GetFiles(executeDataPath, "*.sql") - .OrderBy(f => Path.GetFileName(f))); - } + sqlFiles.AddRange(GetOrderedSqlFiles( + Path.Combine(scopePath, AfterAllMigrationsSqlExecutor.DirectoryName))); } return sqlFiles.ToArray(); } + private static string[] GetOrderedSqlFiles(string directoryPath) + { + return Directory.Exists(directoryPath) + ? Directory.GetFiles(directoryPath, "*.sql").OrderBy(Path.GetFileName).ToArray() + : []; + } + + /// + /// Klasor kokunde kalmis eski scriptler. Artik her .sql bir amac klasorunun altinda + /// olmali; calistirmaya devam edilir ancak tasinmalari icin uyarilir. + /// + private string[] GetLegacyRootSqlFiles(string directoryPath) + { + var files = GetOrderedSqlFiles(directoryPath); + if (files.Length > 0) + { + _logger.LogWarning( + "{Count} .sql file(s) found directly under '{Directory}'. Move them into the '{ObjectData}' folder; root-level files are deprecated.", + files.Length, + directoryPath, + ObjectDataDirectoryName); + } + + return files; + } + private static (string Action, string? ObjectName, string? ObjectType) ExtractSqlInfo(string sql) { var patterns = new[] diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/WizardDataSeeder.cs b/api/src/Sozsoft.Platform.DbMigrator/Migrations/WizardDataSeeder.cs index b228d79f..4ce5373d 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/WizardDataSeeder.cs +++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/WizardDataSeeder.cs @@ -1,10 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; using System.Text.Json; using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Sozsoft.Languages.Entities; using Sozsoft.Languages.Languages; @@ -36,6 +37,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency private readonly IRepository _repoListForm; private readonly IRepository _repoListFormField; private readonly IRepository _repoListFormWorkflow; + private readonly IConfiguration _configuration; private readonly ILogger _logger; private readonly string _cultureNameDefault = PlatformConsts.DefaultLanguage; @@ -51,6 +53,7 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency IRepository repoListForm, IRepository repoListFormField, IRepository repoListFormWorkflow, + IConfiguration configuration, ILogger logger) { _repoLangKey = repoLangKey; @@ -62,12 +65,13 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency _repoListForm = repoListForm; _repoListFormField = repoListFormField; _repoListFormWorkflow = repoListFormWorkflow; + _configuration = configuration; _logger = logger; } public async Task SeedAsync(DataSeedContext context) { - var wizardDataPath = Path.Combine(Directory.GetCurrentDirectory(), "Seeds", "WizardData"); + var wizardDataPath = SeedPathResolver.GetPath(_configuration, "WizardData"); if (!Directory.Exists(wizardDataPath)) { _logger.LogInformation("WizardData directory not found, skipping."); diff --git a/api/src/Sozsoft.Platform.DbMigrator/Sozsoft.Platform.DbMigrator.csproj b/api/src/Sozsoft.Platform.DbMigrator/Sozsoft.Platform.DbMigrator.csproj index 31c47646..dc3283c0 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Sozsoft.Platform.DbMigrator.csproj +++ b/api/src/Sozsoft.Platform.DbMigrator/Sozsoft.Platform.DbMigrator.csproj @@ -77,50 +77,6 @@ PreserveNewest Always - - PreserveNewest - Always - - - PreserveNewest - Always - - - PreserveNewest - Always - - - PreserveNewest - Always - - - PreserveNewest - Always - - - PreserveNewest - Always - - - PreserveNewest - Always - - - PreserveNewest - Always - - - PreserveNewest - Always - - - PreserveNewest - Always - - - PreserveNewest - Always - diff --git a/api/src/Sozsoft.Platform.DbMigrator/appsettings.Dev.json b/api/src/Sozsoft.Platform.DbMigrator/appsettings.Dev.json index 0dac35a6..08e2b014 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/appsettings.Dev.json +++ b/api/src/Sozsoft.Platform.DbMigrator/appsettings.Dev.json @@ -1,5 +1,6 @@ -{ +{ "App": { + "SeedsPath": "/etc/api/seeds", "CdnUrl": "https://cdn-api.sozsoft.com" }, "ConnectionStrings": { diff --git a/api/src/Sozsoft.Platform.DbMigrator/appsettings.Production.json b/api/src/Sozsoft.Platform.DbMigrator/appsettings.Production.json index b4f70a1d..22ec8b3f 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/appsettings.Production.json +++ b/api/src/Sozsoft.Platform.DbMigrator/appsettings.Production.json @@ -1,5 +1,6 @@ -{ +{ "App": { + "SeedsPath": "/etc/api/seeds", "CdnUrl": "https://cdn.sozsoft.com" }, "ConnectionStrings": { diff --git a/api/src/Sozsoft.Platform.Domain/Data/SeedPathResolver.cs b/api/src/Sozsoft.Platform.Domain/Data/SeedPathResolver.cs new file mode 100644 index 00000000..4114fe11 --- /dev/null +++ b/api/src/Sozsoft.Platform.Domain/Data/SeedPathResolver.cs @@ -0,0 +1,104 @@ +using System; +using System.IO; +using Microsoft.Extensions.Configuration; + +namespace Sozsoft.Platform.Data.Seeds; + +/// +/// Runtime'da duzenlenebilen seed dosyalarinin (SqlData / PostgresData / WizardData) klasorunu cozer. +/// +/// Kod ile birlikte versiyonlanan sabit seed'ler (ornegin TenantDataSeeder'in okudugu +/// TenantData.json) buraya dahil degildir; onlar kendi projelerinin ciktisinda kalir. +/// +/// +/// Klasor bilincli olarak hicbir projenin icinde degildir: depoda configs/seeds altinda durur ve +/// konteynere bind mount ile baglanir. Boylece bir seed dosyasi degistiginde ne api ne de migrator +/// imaji yeniden derlenir; deploy sirasindaki git pull yeterlidir. Ayni klasor File Manager +/// uzerinden de duzenlenebildigi icin yapilan degisiklik dogrudan host diskinde kalici olur. +/// +/// +/// Cozum sirasi: +/// 1) App:SeedsPath ayari (konteynerde mount noktasi), +/// 2) calisma dizininden yukari dogru configs/seeds aramasi (local gelistirme), +/// 3) calisma dizininin altindaki Seeds klasoru (son care). +/// +/// +public static class SeedPathResolver +{ + public const string ConfigurationKey = "App:SeedsPath"; + + /// SQL Server saglayicisinin script klasoru. + public const string SqlDataFolder = "SqlData"; + + /// PostgreSQL saglayicisinin script klasoru. + public const string PostgresDataFolder = "PostgresData"; + + /// Host veritabani kapsamindaki scriptler. + public const string HostDataFolder = "HostData"; + + /// Tenant veritabani kapsamindaki scriptler. + public const string TenantDataFolder = "TenantData"; + + /// Yalnizca olusturulan/guncellenen nesne scriptleri. + public const string ObjectDataFolder = "ObjectData"; + + /// Olusturulan, ayrica tum migration'lar bittikten sonra calistirilan scriptler. + public const string ExecuteDataFolder = "ExecuteData"; + + /// Wizard'in urettigi konfigurasyon dosyalari. + public const string WizardDataFolder = "WizardData"; + + private const string RepositorySeedsFolder = "seeds"; + private const string RepositoryConfigsFolder = "configs"; + private const string FallbackFolderName = "Seeds"; + + /// Seed kok klasorunun tam yolunu dondurur. Klasorun var olmasi garanti edilmez. + public static string GetRootPath(IConfiguration? configuration) + { + var configuredPath = configuration?[ConfigurationKey]; + if (!string.IsNullOrWhiteSpace(configuredPath)) + { + return Path.GetFullPath(configuredPath); + } + + var repositoryPath = FindRepositorySeedsPath(); + if (repositoryPath != null) + { + return repositoryPath; + } + + return Path.Combine(Directory.GetCurrentDirectory(), FallbackFolderName); + } + + /// Seed kok klasorunun altindaki bir alt yolu dondurur. + public static string GetPath(IConfiguration? configuration, params string[] subPaths) + { + var path = GetRootPath(configuration); + + foreach (var subPath in subPaths) + { + path = Path.Combine(path, subPath); + } + + return path; + } + + /// Calisma dizininden yukari dogru cikarak depodaki configs/seeds klasorunu arar. + private static string? FindRepositorySeedsPath() + { + var dir = new DirectoryInfo(Directory.GetCurrentDirectory()); + + while (dir != null) + { + var candidate = Path.Combine(dir.FullName, RepositoryConfigsFolder, RepositorySeedsFolder); + if (Directory.Exists(candidate)) + { + return candidate; + } + + dir = dir.Parent; + } + + return null; + } +} diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/DbStartup/SetupMigrationRunner.cs b/api/src/Sozsoft.Platform.HttpApi.Host/DbStartup/SetupMigrationRunner.cs index 637ace79..5638dd33 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/DbStartup/SetupMigrationRunner.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/DbStartup/SetupMigrationRunner.cs @@ -1,9 +1,10 @@ -using System; +using System; using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; +using Sozsoft.Platform.Data.Seeds; namespace Sozsoft.Platform.DbStartup; @@ -33,6 +34,9 @@ public static class SetupMigrationRunner private const int DefaultTimeoutMinutes = 30; + /// Alt surece seed klasorunu bildiren ortam degiskeni (App:SeedsPath karsiligi). + private const string SeedsPathEnvironmentVariable = "App__SeedsPath"; + /// /// Sürecin yaşam süresini kontrol eden token. Bu istemci isteğinin token'ı DEĞİL, /// uygulamanın ApplicationStopping token'ı olmalıdır. Aksi halde kullanıcı sekmeyi @@ -102,6 +106,12 @@ public static class SetupMigrationRunner } }; + // Seed klasoru artik migrator publish ciktisinin icinde degil, mount edilen ortak + // klasorde. Alt surec API'nin cozdugu yolu birebir kullansin ki uygulamadan + // tetiklenen migrate, File Manager'da gorunen dosyalari islesin. + process.StartInfo.Environment[SeedsPathEnvironmentVariable] = + SeedPathResolver.GetRootPath(configuration); + process.Start(); async Task PumpAsync(StreamReader reader, string level) diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Dev.json b/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Dev.json index c6499720..18bcb4ee 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Dev.json +++ b/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Dev.json @@ -1,4 +1,4 @@ -{ +{ "App": { "SelfUrl": "https://dev-api.sozsoft.com", "ClientUrl": "https://dev.sozsoft.com", @@ -7,7 +7,7 @@ "AttachmentsPath": "/etc/api/mail-queue/attachments", "CdnUrl": "https://dev-cdn.sozsoft.com", "CdnPath": "/etc/api/cdn", - "DbMigratorSeedsPath": "/etc/api/seeds", + "SeedsPath": "/etc/api/seeds", "BaseDomain": "sozsoft.com", "BackupPath": "/var/opt/mssql/backup" }, diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Production.json b/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Production.json index 70e54a1d..83939862 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Production.json +++ b/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Production.json @@ -1,4 +1,4 @@ -{ +{ "App": { "SelfUrl": "https://api.sozsoft.com", "ClientUrl": "https://sozsoft.com", @@ -7,7 +7,7 @@ "AttachmentsPath": "/etc/api/mail-queue/attachments", "CdnUrl": "https://cdn.sozsoft.com", "CdnPath": "/etc/api/cdn", - "DbMigratorSeedsPath": "/etc/api/seeds", + "SeedsPath": "/etc/api/seeds", "BaseDomain": "sozsoft.com", "BackupPath": "/var/opt/mssql/backup" }, diff --git a/configs/deployment/docker-compose-app.yml b/configs/deployment/docker-compose-app.yml index db818e37..5127e323 100644 --- a/configs/deployment/docker-compose-app.yml +++ b/configs/deployment/docker-compose-app.yml @@ -8,9 +8,6 @@ networks: volumes: cdn: api-keys: - # DbMigrator imajindaki Seeds klasoru ilk calistirmada bu volume'e kopyalanir; - # api konteyneri ayni volume'u /etc/api/seeds altinda okuyup yazar. - seeds: sql-backups: external: true name: sozsoft-platform-data_sql-backups @@ -25,7 +22,10 @@ services: - ASPNETCORE_ENVIRONMENT=Dev - SEED=${SEED} volumes: - - seeds:/srv/app/Seeds + # Seed dosyalari imaja gomulmez; depodaki configs/seeds klasorunden mount edilir + # (App:SeedsPath). Boylece bir seed degistiginde imaj build etmek gerekmez, + # deploy sirasindaki git pull yeterlidir. + - ../seeds:/etc/api/seeds networks: - db @@ -40,7 +40,9 @@ services: volumes: - cdn:/etc/api/cdn - sql-backups:/etc/api/cdn/host/backup - - seeds:/etc/api/seeds + # Seed dosyalari: migrator ile ayni depo klasoru. File Manager uzerinden yapilan + # duzenleme dogrudan host diskine yazilir ve deploy'lar arasinda kalici olur. + - ../seeds:/etc/api/seeds - api-keys:/root/.aspnet/DataProtection-Keys # Surum notlari imaja gomulmez; deploy sirasinda git tag'lerinden uretilip # buradan okunur (ReleaseNoteStartupService). Boylece yeni tag API imajini diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData/ExecuteData/Sas_H_ListFormFieldsDictionaryJson_SaveDbTableColumns.sql b/configs/seeds/PostgresData/HostData/ExecuteData/Sas_H_ListFormFieldsDictionaryJson_SaveDbTableColumns.sql similarity index 100% rename from api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData/ExecuteData/Sas_H_ListFormFieldsDictionaryJson_SaveDbTableColumns.sql rename to configs/seeds/PostgresData/HostData/ExecuteData/Sas_H_ListFormFieldsDictionaryJson_SaveDbTableColumns.sql diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData/Adm_T_DatabaseBackupAll.sql b/configs/seeds/PostgresData/HostData/ObjectData/Adm_T_DatabaseBackupAll.sql similarity index 100% rename from api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData/Adm_T_DatabaseBackupAll.sql rename to configs/seeds/PostgresData/HostData/ObjectData/Adm_T_DatabaseBackupAll.sql diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData/Adm_T_DatabaseBackupFilesDeleteAll.sql b/configs/seeds/PostgresData/HostData/ObjectData/Adm_T_DatabaseBackupFilesDeleteAll.sql similarity index 100% rename from api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData/Adm_T_DatabaseBackupFilesDeleteAll.sql rename to configs/seeds/PostgresData/HostData/ObjectData/Adm_T_DatabaseBackupFilesDeleteAll.sql diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData/Sas_H_BackgroundWorker_JobFlow.sql b/configs/seeds/PostgresData/HostData/ObjectData/Sas_H_BackgroundWorker_JobFlow.sql similarity index 100% rename from api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData/Sas_H_BackgroundWorker_JobFlow.sql rename to configs/seeds/PostgresData/HostData/ObjectData/Sas_H_BackgroundWorker_JobFlow.sql diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData/Sas_H_ListFormFieldsDictionaryJson.sql b/configs/seeds/PostgresData/HostData/ObjectData/Sas_H_ListFormFieldsDictionaryJson.sql similarity index 100% rename from api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData/Sas_H_ListFormFieldsDictionaryJson.sql rename to configs/seeds/PostgresData/HostData/ObjectData/Sas_H_ListFormFieldsDictionaryJson.sql diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData/Sas_H_OrderItem_ByTenantId.sql b/configs/seeds/PostgresData/HostData/ObjectData/Sas_H_OrderItem_ByTenantId.sql similarity index 100% rename from api/src/Sozsoft.Platform.DbMigrator/Seeds/PostgresData/HostData/Sas_H_OrderItem_ByTenantId.sql rename to configs/seeds/PostgresData/HostData/ObjectData/Sas_H_OrderItem_ByTenantId.sql diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData/ExecuteData/Sas_H_ListFormFieldsDictionaryJson_SaveDbTableColumns.sql b/configs/seeds/SqlData/HostData/ExecuteData/Sas_H_ListFormFieldsDictionaryJson_SaveDbTableColumns.sql similarity index 100% rename from api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData/ExecuteData/Sas_H_ListFormFieldsDictionaryJson_SaveDbTableColumns.sql rename to configs/seeds/SqlData/HostData/ExecuteData/Sas_H_ListFormFieldsDictionaryJson_SaveDbTableColumns.sql diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData/Adm_T_DatabaseBackupAll.sql b/configs/seeds/SqlData/HostData/ObjectData/Adm_T_DatabaseBackupAll.sql similarity index 100% rename from api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData/Adm_T_DatabaseBackupAll.sql rename to configs/seeds/SqlData/HostData/ObjectData/Adm_T_DatabaseBackupAll.sql diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData/Adm_T_DatabaseBackupFilesDeleteAll.sql b/configs/seeds/SqlData/HostData/ObjectData/Adm_T_DatabaseBackupFilesDeleteAll.sql similarity index 100% rename from api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData/Adm_T_DatabaseBackupFilesDeleteAll.sql rename to configs/seeds/SqlData/HostData/ObjectData/Adm_T_DatabaseBackupFilesDeleteAll.sql diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData/Sas_H_BackgroundWorker_JobFlow.sql b/configs/seeds/SqlData/HostData/ObjectData/Sas_H_BackgroundWorker_JobFlow.sql similarity index 100% rename from api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData/Sas_H_BackgroundWorker_JobFlow.sql rename to configs/seeds/SqlData/HostData/ObjectData/Sas_H_BackgroundWorker_JobFlow.sql diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData/Sas_H_ListFormFieldsDictionaryJson.sql b/configs/seeds/SqlData/HostData/ObjectData/Sas_H_ListFormFieldsDictionaryJson.sql similarity index 100% rename from api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData/Sas_H_ListFormFieldsDictionaryJson.sql rename to configs/seeds/SqlData/HostData/ObjectData/Sas_H_ListFormFieldsDictionaryJson.sql diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData/Sas_H_OrderItem_ByTenantId.sql b/configs/seeds/SqlData/HostData/ObjectData/Sas_H_OrderItem_ByTenantId.sql similarity index 100% rename from api/src/Sozsoft.Platform.DbMigrator/Seeds/SqlData/HostData/Sas_H_OrderItem_ByTenantId.sql rename to configs/seeds/SqlData/HostData/ObjectData/Sas_H_OrderItem_ByTenantId.sql diff --git a/ui/src/utils/workflow/workflowConstants.ts b/ui/src/utils/workflow/workflowConstants.ts index 3aff5931..e44d6330 100644 --- a/ui/src/utils/workflow/workflowConstants.ts +++ b/ui/src/utils/workflow/workflowConstants.ts @@ -1,8 +1,8 @@ import { FiBell, FiCheck, FiGitBranch, FiPlay, FiSlash } from 'react-icons/fi' export const kindOptions = [ - { value: 'Start', label: 'App.VideoRoom.Start' }, - { value: 'Compare', label: 'App.ScriptBuilder.Comparison' }, + { value: 'Start', label: 'App.WorkflowKind.Start' }, + { value: 'Compare', label: 'App.WorkflowKind.Compare' }, { value: 'Approval', label: 'App.WorkflowKind.Approval' }, { value: 'Inform', label: 'App.WorkflowKind.Inform' }, { value: 'End', label: 'App.WorkflowKind.End' }, diff --git a/ui/src/utils/workflow/workflowHelpers.ts b/ui/src/utils/workflow/workflowHelpers.ts index 09e732fb..34b3a9db 100644 --- a/ui/src/utils/workflow/workflowHelpers.ts +++ b/ui/src/utils/workflow/workflowHelpers.ts @@ -244,7 +244,7 @@ function sideToward(from: WorkflowCriteriaDto, to: WorkflowCriteriaDto) { /** Çıkış etiketleri lokalizasyon anahtarı olarak taşınır; ekranda `translate` ile çözülür. */ export const OUTCOME_KEYS = { - nextOnStart: 'App.Platform.Next2', + nextOnStart: 'App.WorkflowOutcome.Next', nextOnTrue: 'App.WorkflowOutcome.True', nextOnFalse: 'App.WorkflowOutcome.False', nextOnApprove: 'App.ListFormWorkflow.Approve', @@ -476,8 +476,8 @@ export function normalizeCriteria(item: WorkflowCriteriaForm): SaveCriteriaInput export function defaultTitle(kind: string) { return ( { - Start: 'App.VideoRoom.Start', - Compare: 'App.ScriptBuilder.Comparison', + Start: 'App.WorkflowKind.Start', + Compare: 'App.WorkflowKind.Compare', Approval: 'App.WorkflowKind.Approval', Inform: 'App.WorkflowKind.Inform', End: 'App.WorkflowKind.End', @@ -485,6 +485,33 @@ export function defaultTitle(kind: string) { ) } +/** + * Node basliklari lokalizasyon anahtari olarak uretilir ve benzersizlik icin + * sonuna sira numarasi eklenir (`App.WorkflowKind.Approval1`). Bu yuzden ham + * baslik dogrudan `translate` edilemez; once numara ayrilir, anahtar cevrilir, + * numara tekrar eklenir. Anahtar bulunamazsa ham baslik gosterilir. + */ +const LOCALIZATION_KEY_PATTERN = /^[A-Za-z0-9_]+(?:\.[A-Za-z0-9_]+)+$/u + +export function translateWorkflowLabel( + title: string | null | undefined, + translate: (key: string) => string, +) { + const raw = String(title ?? '').trim() + if (!raw) return '' + + const match = /^(.*?)(\d*)$/u.exec(raw) + const base = (match?.[1] || raw).trim() + const suffix = match?.[2] || '' + + if (!LOCALIZATION_KEY_PATTERN.test(base)) return raw + + const translated = translate('::' + base) + if (!translated || translated === base) return raw + + return `${translated}${suffix}` +} + export function emptyCompareOutcome1(label = 'Durum', compareColumn = 'Price'): CompareOutcomeDto { return { label, diff --git a/ui/src/views/admin/files/FileManager.tsx b/ui/src/views/admin/files/FileManager.tsx index 31bdc860..aaf1ddb9 100644 --- a/ui/src/views/admin/files/FileManager.tsx +++ b/ui/src/views/admin/files/FileManager.tsx @@ -72,7 +72,7 @@ const buildDownloadUrl = (item: FileItemType, fallbackTenantId?: string): string return `${VITE_CDN_URL}/${tenantSegment}/${segments.map(encodeURIComponent).join('/')}` } -/** DbMigrator Seeds klasoru CDN altinda degil, backend sanal kok olarak sunar. */ +/** seeds klasoru CDN altinda degil, backend sanal kok olarak sunar (configs/seeds). */ const isSeedsItem = (item: FileItemType): boolean => { const relativePath = item.path || item.id.replace(/\|/g, '/') return relativePath.split('/')[0]?.toLowerCase() === 'seeds' @@ -419,7 +419,7 @@ const FileManager = () => { const handleDownload = useCallback( async (item: FileItemType) => { - // Sanal Seeds klasoru CDN altinda olmadigi icin dosya API uzerinden stream edilir. + // Sanal seeds klasoru CDN altinda olmadigi icin dosya API uzerinden stream edilir. if (isSeedsItem(item)) { try { const blob = await fileManagementService.downloadFile(item.id, currentTenantId) diff --git a/ui/src/views/admin/listForm/workflow/WorkflowCanvas.tsx b/ui/src/views/admin/listForm/workflow/WorkflowCanvas.tsx index 5c5b3e3b..6a2eedae 100644 --- a/ui/src/views/admin/listForm/workflow/WorkflowCanvas.tsx +++ b/ui/src/views/admin/listForm/workflow/WorkflowCanvas.tsx @@ -7,6 +7,7 @@ import { collectLinks, getNodeOutcomes, outcomeLabel, + translateWorkflowLabel, OUTCOME_KEYS, type WorkflowLink, type WorkflowOutcome, @@ -342,7 +343,7 @@ function FlowNode({ {translate('::' + kindOptions.find((option) => option.value === item.kind)?.label)} - {item.title} + {translateWorkflowLabel(item.title, translate)} - {outcome.label.startsWith('ListForms.') - ? translate('::' + outcome.label) - : outcome.label} + {translateWorkflowLabel(outcome.label, translate)} ))} @@ -505,12 +504,15 @@ function Arrow({ } function ArrowLabel({ link, pendingLink }: { link: WorkflowLink; pendingLink: PendingLink }) { + const { translate } = useLocalization() + if (!link.label) return null const route = buildArrowRoute(link.source, link.target, link.sourcePort) const labelPoint = route.labelPoint const tone = linkTone(link) - const labelWidth = Math.max(38, (link.label || '').length * 6 + 14) + const label = translateWorkflowLabel(link.label, translate) + const labelWidth = Math.max(38, label.length * 6 + 14) const isActive = pendingLink?.sourceId === link.source.id && pendingLink?.outcome === link.sourcePort?.field @@ -536,7 +538,7 @@ function ArrowLabel({ link, pendingLink }: { link: WorkflowLink; pendingLink: Pe y={labelPoint.y} textAnchor="middle" > - {link.label} + {label} ) diff --git a/ui/src/views/developerKit/SqlObjectExplorer.tsx b/ui/src/views/developerKit/SqlObjectExplorer.tsx index d943e49c..b8a10a65 100644 --- a/ui/src/views/developerKit/SqlObjectExplorer.tsx +++ b/ui/src/views/developerKit/SqlObjectExplorer.tsx @@ -306,7 +306,7 @@ const SqlObjectExplorer = ({ dataSourceCode: dataSource, }) - // If a matching seed file exists under DbMigrator/Seeds/SqlData, delete it too. + // If a matching seed file exists under the seeds folder (configs/seeds/SqlData), delete it too. try { const fileNames = [...new Set(getSqlDataFileCandidates(dropConfirm.node).filter(Boolean))] if (fileNames.length > 0) { diff --git a/ui/src/views/developerKit/SqlQueryManager.tsx b/ui/src/views/developerKit/SqlQueryManager.tsx index 948a11c4..7bbee24f 100644 --- a/ui/src/views/developerKit/SqlQueryManager.tsx +++ b/ui/src/views/developerKit/SqlQueryManager.tsx @@ -958,7 +958,7 @@ GO`, ))} - {/* Seed dosyalari (DbMigrator/Seeds) File Manager uzerinden yonetilir. */} + {/* Seed dosyalari (configs/seeds) File Manager uzerinden yonetilir. */}