WizardStep güncellemeleri
This commit is contained in:
parent
f2652dbb44
commit
4b2fceb404
15 changed files with 833 additions and 242 deletions
|
|
@ -1,6 +1,9 @@
|
||||||
namespace Sozsoft.Platform.ListForms;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
public interface IListFormWizardAppService
|
public interface IListFormWizardAppService
|
||||||
{
|
{
|
||||||
|
Task Create(WizardCreateInputDto input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
|
public class WizardColumnGroupInputDto
|
||||||
|
{
|
||||||
|
public string Caption { get; set; }
|
||||||
|
public int ColCount { get; set; } = 2;
|
||||||
|
public List<WizardColumnItemInputDto> Items { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
|
public class WizardColumnItemInputDto
|
||||||
|
{
|
||||||
|
public string DataField { get; set; }
|
||||||
|
public string EditorType { get; set; }
|
||||||
|
public string EditorOptions { get; set; }
|
||||||
|
public string EditorScript { get; set; }
|
||||||
|
public int ColSpan { get; set; } = 1;
|
||||||
|
public bool IsRequired { get; set; }
|
||||||
|
public DbType DbSourceType { get; set; } = DbType.String;
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.ListForms;
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
@ -27,5 +28,7 @@ public class WizardCreateInputDto
|
||||||
public string SelectCommand { get; set; }
|
public string SelectCommand { get; set; }
|
||||||
public string KeyFieldName { get; set; }
|
public string KeyFieldName { get; set; }
|
||||||
public DbType KeyFieldDbSourceType { get; set; }
|
public DbType KeyFieldDbSourceType { get; set; }
|
||||||
|
|
||||||
|
public List<WizardColumnGroupInputDto> Groups { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -12,11 +13,15 @@ using Volo.Abp.Identity;
|
||||||
using Volo.Abp.MultiTenancy;
|
using Volo.Abp.MultiTenancy;
|
||||||
using Volo.Abp.PermissionManagement;
|
using Volo.Abp.PermissionManagement;
|
||||||
using Volo.Abp.Uow;
|
using Volo.Abp.Uow;
|
||||||
|
using static Sozsoft.Platform.PlatformConsts;
|
||||||
|
using System.Data;
|
||||||
|
using Sozsoft.Platform.Data.Seeds;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.ListForms;
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
||||||
public class ListFormWizardAppService(
|
public class ListFormWizardAppService(
|
||||||
IRepository<ListForm, Guid> repoListForm,
|
IRepository<ListForm, Guid> repoListForm,
|
||||||
|
IRepository<ListFormField, Guid> repoListFormField,
|
||||||
IRepository<DataSource, Guid> repoDataSource,
|
IRepository<DataSource, Guid> repoDataSource,
|
||||||
IRepository<LanguageKey, Guid> repoLangKey,
|
IRepository<LanguageKey, Guid> repoLangKey,
|
||||||
IRepository<LanguageText, Guid> repoLangText,
|
IRepository<LanguageText, Guid> repoLangText,
|
||||||
|
|
@ -30,6 +35,7 @@ public class ListFormWizardAppService(
|
||||||
) : PlatformAppService(), IListFormWizardAppService
|
) : PlatformAppService(), IListFormWizardAppService
|
||||||
{
|
{
|
||||||
private readonly IRepository<ListForm, Guid> repoListForm = repoListForm;
|
private readonly IRepository<ListForm, Guid> repoListForm = repoListForm;
|
||||||
|
private readonly IRepository<ListFormField, Guid> repoListFormField = repoListFormField;
|
||||||
private readonly IRepository<DataSource, Guid> repoDataSource = repoDataSource;
|
private readonly IRepository<DataSource, Guid> repoDataSource = repoDataSource;
|
||||||
private readonly IRepository<LanguageKey, Guid> repoLangKey = repoLangKey;
|
private readonly IRepository<LanguageKey, Guid> repoLangKey = repoLangKey;
|
||||||
private readonly IRepository<LanguageText, Guid> repoLangText = repoLangText;
|
private readonly IRepository<LanguageText, Guid> repoLangText = repoLangText;
|
||||||
|
|
@ -71,24 +77,30 @@ public class ListFormWizardAppService(
|
||||||
|
|
||||||
var permRead = existingPerms.FirstOrDefault(a => a.Name == code) ??
|
var permRead = existingPerms.FirstOrDefault(a => a.Name == code) ??
|
||||||
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, code, null, nameLangKey, true, MultiTenancySides.Both), autoSave: false);
|
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, code, null, nameLangKey, true, MultiTenancySides.Both), autoSave: false);
|
||||||
|
|
||||||
var permCreate = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermCreate(listFormCode)) ??
|
var permCreate = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermCreate(listFormCode)) ??
|
||||||
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermCreate(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyCreate, true, MultiTenancySides.Both), autoSave: false);
|
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermCreate(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyCreate, true, MultiTenancySides.Both), autoSave: false);
|
||||||
|
|
||||||
var permUpdate = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermUpdate(listFormCode)) ??
|
var permUpdate = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermUpdate(listFormCode)) ??
|
||||||
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermUpdate(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: false);
|
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermUpdate(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyUpdate, true, MultiTenancySides.Both), autoSave: false);
|
||||||
|
|
||||||
var permDelete = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermDelete(listFormCode)) ??
|
var permDelete = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermDelete(listFormCode)) ??
|
||||||
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermDelete(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyDelete, true, MultiTenancySides.Both), autoSave: false);
|
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermDelete(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyDelete, true, MultiTenancySides.Both), autoSave: false);
|
||||||
|
|
||||||
var permExport = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermExport(listFormCode)) ??
|
var permExport = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermExport(listFormCode)) ??
|
||||||
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermExport(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyExport, true, MultiTenancySides.Both), autoSave: false);
|
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermExport(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyExport, true, MultiTenancySides.Both), autoSave: false);
|
||||||
|
|
||||||
|
var permImport = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermImport(listFormCode)) ??
|
||||||
|
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermImport(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyImport, true, MultiTenancySides.Both), autoSave: false);
|
||||||
|
|
||||||
|
var PermNote = existingPerms.FirstOrDefault(a => a.Name == PlatformConsts.Wizard.PermNote(listFormCode)) ??
|
||||||
|
await repoPerm.InsertAsync(new PermissionDefinitionRecord(Guid.NewGuid(), groupName, PlatformConsts.Wizard.PermNote(listFormCode), permRead.Name, PlatformConsts.Wizard.LangKeyNote, true, MultiTenancySides.Both), autoSave: false);
|
||||||
|
|
||||||
// Permission Grants - Bulk Insert
|
// Permission Grants - Bulk Insert
|
||||||
var adminUserName = PlatformConsts.AbpIdentity.User.AdminEmailDefaultValue;
|
var adminUserName = PlatformConsts.AbpIdentity.User.AdminEmailDefaultValue;
|
||||||
var adminUser = await userRepository.FindByNormalizedUserNameAsync(lookupNormalizer.NormalizeName(adminUserName));
|
var adminUser = await userRepository.FindByNormalizedUserNameAsync(lookupNormalizer.NormalizeName(adminUserName));
|
||||||
var adminRole = await roleRepository.FindByNormalizedNameAsync(lookupNormalizer.NormalizeName(PlatformConsts.AbpIdentity.User.AdminRoleName));
|
var adminRole = await roleRepository.FindByNormalizedNameAsync(lookupNormalizer.NormalizeName(PlatformConsts.AbpIdentity.User.AdminRoleName));
|
||||||
|
|
||||||
await permissionGrantRepository.InsertManyAsync(
|
await permissionGrantRepository.InsertManyAsync(
|
||||||
[
|
[
|
||||||
new PermissionGrant(Guid.NewGuid(), permRead.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName),
|
new PermissionGrant(Guid.NewGuid(), permRead.Name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName),
|
||||||
|
|
@ -111,7 +123,7 @@ public class ListFormWizardAppService(
|
||||||
IsDisabled = false,
|
IsDisabled = false,
|
||||||
}, autoSave: false);
|
}, autoSave: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Menu
|
//Menu
|
||||||
var menu = await AsyncExecuter.FirstOrDefaultAsync(menuQueryable.Where(a => a.Code == code)) ??
|
var menu = await AsyncExecuter.FirstOrDefaultAsync(menuQueryable.Where(a => a.Code == code)) ??
|
||||||
await repoMenu.InsertAsync(new Menu
|
await repoMenu.InsertAsync(new Menu
|
||||||
|
|
@ -128,7 +140,7 @@ public class ListFormWizardAppService(
|
||||||
RequiredPermissionName = permRead.Name
|
RequiredPermissionName = permRead.Name
|
||||||
}, autoSave: false);
|
}, autoSave: false);
|
||||||
|
|
||||||
//Data Source
|
//DataSource kodu ile iligli kod blogu
|
||||||
var dataSourceQueryable = await repoDataSource.GetQueryableAsync();
|
var dataSourceQueryable = await repoDataSource.GetQueryableAsync();
|
||||||
var dataSource = await AsyncExecuter.FirstOrDefaultAsync(dataSourceQueryable.Where(a => a.Code == input.DataSourceCode));
|
var dataSource = await AsyncExecuter.FirstOrDefaultAsync(dataSourceQueryable.Where(a => a.Code == input.DataSourceCode));
|
||||||
if (dataSource is null)
|
if (dataSource is null)
|
||||||
|
|
@ -141,37 +153,98 @@ public class ListFormWizardAppService(
|
||||||
}, autoSave: false);
|
}, autoSave: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build EditingFormJson from wizard groups
|
||||||
|
var editingFormDtos = input.Groups
|
||||||
|
.Select((g, gi) => new EditingFormDto
|
||||||
|
{
|
||||||
|
Order = gi + 1,
|
||||||
|
Caption = g.Caption,
|
||||||
|
ColCount = g.ColCount,
|
||||||
|
ColSpan = g.ColCount,
|
||||||
|
ItemType = "group",
|
||||||
|
Items = g.Items
|
||||||
|
.Select((it, ii) => new EditingFormItemDto
|
||||||
|
{
|
||||||
|
Order = ii + 1,
|
||||||
|
DataField = it.DataField,
|
||||||
|
EditorType2 = it.EditorType,
|
||||||
|
ColSpan = it.ColSpan,
|
||||||
|
EditorOptions = string.IsNullOrWhiteSpace(it.EditorOptions) ? null : it.EditorOptions,
|
||||||
|
EditorScript = string.IsNullOrWhiteSpace(it.EditorScript) ? null : it.EditorScript,
|
||||||
|
IsRequired = it.IsRequired,
|
||||||
|
})
|
||||||
|
.ToArray()
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
//ListForm
|
//ListForm
|
||||||
var listForm = await repoListForm.InsertAsync(new ListForm
|
var listForm = await repoListForm.InsertAsync(new ListForm
|
||||||
{
|
{
|
||||||
|
ListFormType = ListFormTypeEnum.List,
|
||||||
|
PageSize = 10,
|
||||||
|
ExportJson = Wizard.DefaultExportJson,
|
||||||
|
IsSubForm = false,
|
||||||
|
ShowNote = true,
|
||||||
|
LayoutJson = Wizard.DefaultLayoutJson(),
|
||||||
|
CultureName = LanguageCodes.En,
|
||||||
ListFormCode = listFormCode,
|
ListFormCode = listFormCode,
|
||||||
DataSourceCode = input.DataSourceCode,
|
|
||||||
Name = nameLangKey,
|
Name = nameLangKey,
|
||||||
Title = titleLangKey,
|
Title = titleLangKey,
|
||||||
CultureName = PlatformConsts.DefaultLanguage,
|
DataSourceCode = input.DataSourceCode,
|
||||||
Description = PlatformConsts.Wizard.WizardKeyDesc(listFormCode),
|
IsTenant = false,
|
||||||
|
IsBranch = false,
|
||||||
|
IsOrganizationUnit = false,
|
||||||
|
Description = Wizard.WizardKeyDesc(listFormCode),
|
||||||
SelectCommandType = input.SelectCommandType,
|
SelectCommandType = input.SelectCommandType,
|
||||||
SelectCommand = input.SelectCommand,
|
SelectCommand = input.SelectCommand,
|
||||||
KeyFieldName = input.KeyFieldName,
|
KeyFieldName = input.KeyFieldName,
|
||||||
KeyFieldDbSourceType = input.KeyFieldDbSourceType,
|
KeyFieldDbSourceType = input.KeyFieldDbSourceType,
|
||||||
PermissionJson = JsonSerializer.Serialize(new PermissionCrudDto
|
DefaultFilter = Wizard.DefaultFilterJson,
|
||||||
{
|
SortMode = GridOptions.SortModeSingle,
|
||||||
C = permCreate.Name,
|
FilterRowJson = Wizard.DefaultFilterRowJson,
|
||||||
R = permRead.Name,
|
HeaderFilterJson = Wizard.DefaultHeaderFilterJson,
|
||||||
U = permUpdate.Name,
|
SearchPanelJson = Wizard.DefaultSearchPanelJson,
|
||||||
D = permDelete.Name
|
GroupPanelJson = JsonSerializer.Serialize(new { Visible = false }),
|
||||||
}),
|
SelectionJson = Wizard.DefaultSelectionSingleJson,
|
||||||
|
ColumnOptionJson = Wizard.DefaultColumnOptionJson(),
|
||||||
|
PermissionJson = Wizard.DefaultPermissionJson(listFormCode),
|
||||||
|
DeleteCommand = Wizard.DefaultDeleteCommand(nameof(TableNameEnum.Country)),
|
||||||
|
DeleteFieldsDefaultValueJson = Wizard.DefaultDeleteFieldsDefaultValueJson(),
|
||||||
|
PagerOptionJson = Wizard.DefaultPagerOptionJson,
|
||||||
|
EditingOptionJson = Wizard.DefaultEditingOptionJson(listFormCode, 600, 500, true, true, true, true, false),
|
||||||
|
EditingFormJson = editingFormDtos.Count > 0 ? JsonSerializer.Serialize(editingFormDtos) : null,
|
||||||
}, autoSave: true);
|
}, autoSave: true);
|
||||||
|
|
||||||
|
// ListFormField - each item in each group becomes a visible field record
|
||||||
|
var fieldOrder = 0;
|
||||||
|
foreach (var group in input.Groups)
|
||||||
|
{
|
||||||
|
foreach (var item in group.Items)
|
||||||
|
{
|
||||||
|
fieldOrder++;
|
||||||
|
await repoListFormField.InsertAsync(new ListFormField
|
||||||
|
{
|
||||||
|
ListFormCode = listFormCode,
|
||||||
|
FieldName = item.DataField,
|
||||||
|
CaptionName = item.DataField,
|
||||||
|
Visible = true,
|
||||||
|
IsActive = true,
|
||||||
|
ListOrderNo = fieldOrder,
|
||||||
|
SourceDbType = item.DbSourceType,
|
||||||
|
CultureName = PlatformConsts.DefaultLanguage,
|
||||||
|
}, autoSave: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<LanguageKey> CreateLangKey(string key, string textEn, string textTr)
|
private async Task<LanguageKey> CreateLangKey(string key, string textEn, string textTr)
|
||||||
{
|
{
|
||||||
var res = PlatformConsts.AppName;
|
var res = PlatformConsts.AppName;
|
||||||
|
|
||||||
var keyQueryable = await repoLangKey.GetQueryableAsync();
|
var keyQueryable = await repoLangKey.GetQueryableAsync();
|
||||||
var langKey = await AsyncExecuter.FirstOrDefaultAsync(keyQueryable.Where(a => a.ResourceName == res && a.Key == key))
|
var langKey = await AsyncExecuter.FirstOrDefaultAsync(keyQueryable.Where(a => a.ResourceName == res && a.Key == key))
|
||||||
?? await repoLangKey.InsertAsync(new LanguageKey { ResourceName = res, Key = key }, autoSave: false);
|
?? await repoLangKey.InsertAsync(new LanguageKey { ResourceName = res, Key = key }, autoSave: false);
|
||||||
|
|
||||||
var textQueryable = await repoLangText.GetQueryableAsync();
|
var textQueryable = await repoLangText.GetQueryableAsync();
|
||||||
var existingTexts = await AsyncExecuter.ToListAsync(
|
var existingTexts = await AsyncExecuter.ToListAsync(
|
||||||
textQueryable.Where(a => a.ResourceName == res && a.Key == langKey.Key)
|
textQueryable.Where(a => a.ResourceName == res && a.Key == langKey.Key)
|
||||||
|
|
@ -179,7 +252,7 @@ public class ListFormWizardAppService(
|
||||||
|
|
||||||
var langTextEn = existingTexts.FirstOrDefault(a => a.CultureName == cultureNameDefault)
|
var langTextEn = existingTexts.FirstOrDefault(a => a.CultureName == cultureNameDefault)
|
||||||
?? await repoLangText.InsertAsync(new LanguageText { ResourceName = res, Key = langKey.Key, CultureName = cultureNameDefault, Value = textEn }, autoSave: false);
|
?? await repoLangText.InsertAsync(new LanguageText { ResourceName = res, Key = langKey.Key, CultureName = cultureNameDefault, Value = textEn }, autoSave: false);
|
||||||
|
|
||||||
var langTextTr = existingTexts.FirstOrDefault(a => a.CultureName == LanguageCodes.Tr)
|
var langTextTr = existingTexts.FirstOrDefault(a => a.CultureName == LanguageCodes.Tr)
|
||||||
?? await repoLangText.InsertAsync(new LanguageText { ResourceName = res, Key = langKey.Key, CultureName = LanguageCodes.Tr, Value = textTr }, autoSave: false);
|
?? await repoLangText.InsertAsync(new LanguageText { ResourceName = res, Key = langKey.Key, CultureName = LanguageCodes.Tr, Value = textTr }, autoSave: false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Text.Json;
|
||||||
using Sozsoft.Languages.Languages;
|
using Sozsoft.Languages.Languages;
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
using Volo.Abp.Reflection;
|
using Volo.Abp.Reflection;
|
||||||
|
|
@ -414,14 +416,122 @@ public static class PlatformConsts
|
||||||
public static string PermUpdate(string code) => $"{WizardKey(code)}.Update";
|
public static string PermUpdate(string code) => $"{WizardKey(code)}.Update";
|
||||||
public static string PermDelete(string code) => $"{WizardKey(code)}.Delete";
|
public static string PermDelete(string code) => $"{WizardKey(code)}.Delete";
|
||||||
public static string PermExport(string code) => $"{WizardKey(code)}.Export";
|
public static string PermExport(string code) => $"{WizardKey(code)}.Export";
|
||||||
|
public static string PermImport(string code) => $"{WizardKey(code)}.Import";
|
||||||
|
public static string PermNote(string code) => $"{WizardKey(code)}.Note";
|
||||||
public static string LangKeyCreate => $"{Prefix.App}.Create";
|
public static string LangKeyCreate => $"{Prefix.App}.Create";
|
||||||
public static string LangKeyUpdate => $"{Prefix.App}.Update";
|
public static string LangKeyUpdate => $"{Prefix.App}.Update";
|
||||||
public static string LangKeyDelete => $"{Prefix.App}.Delete";
|
public static string LangKeyDelete => $"{Prefix.App}.Delete";
|
||||||
public static string LangKeyExport => $"{Prefix.App}.Export";
|
public static string LangKeyExport => $"{Prefix.App}.Export";
|
||||||
|
public static string LangKeyImport => $"{Prefix.App}.Import";
|
||||||
|
public static string LangKeyNote => $"{Prefix.App}.Note";
|
||||||
|
|
||||||
public static string MenuUrl(string code) => $"/list/{code}";
|
public static string MenuUrl(string code) => $"/admin/list/{code}";
|
||||||
public static string MenuIcon => "FcList";
|
public static string MenuIcon => "FcList";
|
||||||
|
|
||||||
|
public static readonly string DefaultExportJson = JsonSerializer.Serialize(new
|
||||||
|
{
|
||||||
|
Enabled = true,
|
||||||
|
AllowExportSelectedData = false,
|
||||||
|
PrintingEnabled = true,
|
||||||
|
BackgroundColor = "#FFFFFF",
|
||||||
|
Margin = 10
|
||||||
|
});
|
||||||
|
|
||||||
|
public static string DefaultLayoutJson(string DefaultLayout = "grid") => JsonSerializer.Serialize(new
|
||||||
|
{
|
||||||
|
Grid = true,
|
||||||
|
Pivot = true,
|
||||||
|
Chart = true,
|
||||||
|
Tree = true,
|
||||||
|
Gantt = true,
|
||||||
|
Scheduler = true,
|
||||||
|
DefaultLayout = DefaultLayout,
|
||||||
|
});
|
||||||
|
|
||||||
|
public static readonly string DefaultFilterJson = "\"IsDeleted\" = 'false'";
|
||||||
|
public static readonly string DefaultFilterRowJson = JsonSerializer.Serialize(new { Visible = true });
|
||||||
|
public static readonly string DefaultHeaderFilterJson = JsonSerializer.Serialize(new { Visible = true });
|
||||||
|
public static readonly string DefaultSearchPanelJson = JsonSerializer.Serialize(new { Visible = true });
|
||||||
|
public static readonly string DefaultGroupPanelJson = JsonSerializer.Serialize(new { Visible = true });
|
||||||
|
|
||||||
|
public static readonly string DefaultSelectionSingleJson = JsonSerializer.Serialize(new
|
||||||
|
{
|
||||||
|
Mode = GridOptions.SelectionModeNone,
|
||||||
|
AllowSelectAll = false
|
||||||
|
});
|
||||||
|
|
||||||
|
public static string DefaultColumnOptionJson(bool FocusedRowEnabled = true) => JsonSerializer.Serialize(new
|
||||||
|
{
|
||||||
|
ColumnFixingEnabled = true,
|
||||||
|
ColumnAutoWidth = true,
|
||||||
|
ColumnChooserEnabled = true,
|
||||||
|
AllowColumnResizing = true,
|
||||||
|
AllowColumnReordering = true,
|
||||||
|
ColumnResizingMode = "widget",
|
||||||
|
FocusedRowEnabled = FocusedRowEnabled,
|
||||||
|
});
|
||||||
|
|
||||||
|
public static string DefaultPermissionJson(string permissionName)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(new
|
||||||
|
{
|
||||||
|
C = permissionName + ".Create",
|
||||||
|
R = permissionName,
|
||||||
|
U = permissionName + ".Update",
|
||||||
|
D = permissionName + ".Delete",
|
||||||
|
E = permissionName + ".Export",
|
||||||
|
I = permissionName + ".Import",
|
||||||
|
N = permissionName + ".Note",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string DefaultDeleteCommand(string tableName)
|
||||||
|
{
|
||||||
|
return $"UPDATE \"{TableNameResolver.GetFullTableName(tableName)}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string DefaultDeleteFieldsDefaultValueJson(DbType dbType = DbType.Guid)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(new[]
|
||||||
|
{
|
||||||
|
new { FieldName = "DeleterId", FieldDbType = DbType.Guid.ToString(), Value = "@USERID", CustomValueType = FieldCustomValueTypeEnum.CustomKey },
|
||||||
|
new { FieldName = "Id", FieldDbType = dbType.ToString(), Value = "@ID", CustomValueType = FieldCustomValueTypeEnum.CustomKey }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly string DefaultPagerOptionJson = JsonSerializer.Serialize(new
|
||||||
|
{
|
||||||
|
Visible = true,
|
||||||
|
AllowedPageSizes = "10,20,50,100",
|
||||||
|
ShowPageSizeSelector = true,
|
||||||
|
ShowNavigationButtons = true,
|
||||||
|
ShowInfo = false,
|
||||||
|
InfoText = "Page {0} of {1} ({2} items)",
|
||||||
|
DisplayMode = GridColumnOptions.PagerDisplayModeAdaptive,
|
||||||
|
ScrollingMode = GridColumnOptions.ScrollingModeStandard,
|
||||||
|
LoadPanelEnabled = "auto",
|
||||||
|
LoadPanelText = "Loading..."
|
||||||
|
});
|
||||||
|
|
||||||
|
public static string DefaultEditingOptionJson(
|
||||||
|
string Title,
|
||||||
|
int Width,
|
||||||
|
int Height,
|
||||||
|
bool AllowDeleting,
|
||||||
|
bool AllowAdding,
|
||||||
|
bool AllowEditing,
|
||||||
|
bool ConfirmDelete,
|
||||||
|
bool SendOnlyChangedFormValuesUpdate,
|
||||||
|
bool AllowDetail = false) => JsonSerializer.Serialize(new
|
||||||
|
{
|
||||||
|
Popup = new { Title = Title, Width = Width, Height = Height },
|
||||||
|
AllowDeleting = AllowDeleting,
|
||||||
|
AllowAdding = AllowAdding,
|
||||||
|
AllowEditing = AllowEditing,
|
||||||
|
ConfirmDelete = ConfirmDelete,
|
||||||
|
SendOnlyChangedFormValuesUpdate = SendOnlyChangedFormValuesUpdate,
|
||||||
|
AllowDetail = AllowDetail
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AppErrorCodes
|
public static class AppErrorCodes
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,22 @@ import {
|
||||||
ChartValueAxisDto,
|
ChartValueAxisDto,
|
||||||
} from '../charts/models'
|
} from '../charts/models'
|
||||||
|
|
||||||
|
export interface ListFormWizardColumnItemDto {
|
||||||
|
dataField: string
|
||||||
|
editorType: string
|
||||||
|
editorOptions: string
|
||||||
|
editorScript: string
|
||||||
|
colSpan: number
|
||||||
|
isRequired: boolean
|
||||||
|
dbSourceType: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ListFormWizardColumnGroupDto {
|
||||||
|
caption: string
|
||||||
|
colCount: number
|
||||||
|
items: ListFormWizardColumnItemDto[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface ListFormWizardDto {
|
export interface ListFormWizardDto {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
menuCode: string
|
menuCode: string
|
||||||
|
|
@ -35,6 +51,7 @@ export interface ListFormWizardDto {
|
||||||
selectCommand: string
|
selectCommand: string
|
||||||
keyFieldName: string
|
keyFieldName: string
|
||||||
keyFieldDbSourceType: number
|
keyFieldDbSourceType: number
|
||||||
|
groups?: ListFormWizardColumnGroupDto[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ListFormJsonRowDto {
|
export interface ListFormJsonRowDto {
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,11 @@ import WizardStep1, {
|
||||||
filterNonLinkNodes,
|
filterNonLinkNodes,
|
||||||
findRootCode,
|
findRootCode,
|
||||||
} from './WizardStep1'
|
} from './WizardStep1'
|
||||||
import WizardStep2, { sqlDataTypeToDbType } from './WizardStep2'
|
import WizardStep2 from './WizardStep2'
|
||||||
import WizardStep3, { WizardGroup } from './WizardStep3'
|
import WizardStep3, { WizardGroup } from './WizardStep3'
|
||||||
import WizardStep4 from './WizardStep4'
|
import WizardStep4 from './WizardStep4'
|
||||||
import { Container } from '@/components/shared'
|
import { Container } from '@/components/shared'
|
||||||
|
import { sqlDataTypeToDbType } from './edit/options'
|
||||||
|
|
||||||
// ─── Formik initial values & validation ──────────────────────────────────────
|
// ─── Formik initial values & validation ──────────────────────────────────────
|
||||||
const initialValues: ListFormWizardDto = {
|
const initialValues: ListFormWizardDto = {
|
||||||
|
|
@ -296,7 +297,25 @@ const Wizard = () => {
|
||||||
const handleDeploy = async () => {
|
const handleDeploy = async () => {
|
||||||
if (!formikRef.current) throw new Error('Form bulunamadı')
|
if (!formikRef.current) throw new Error('Form bulunamadı')
|
||||||
const values = formikRef.current.values
|
const values = formikRef.current.values
|
||||||
await postListFormWizard({ ...values })
|
await postListFormWizard({
|
||||||
|
...values,
|
||||||
|
groups: editingGroups.map((g) => ({
|
||||||
|
caption: g.caption,
|
||||||
|
colCount: g.colCount,
|
||||||
|
items: g.items.map((item) => {
|
||||||
|
const col = selectCommandColumns.find((c) => c.columnName === item.dataField)
|
||||||
|
return {
|
||||||
|
dataField: item.dataField,
|
||||||
|
editorType: item.editorType,
|
||||||
|
editorOptions: item.editorOptions ?? '',
|
||||||
|
editorScript: item.editorScript ?? '',
|
||||||
|
colSpan: item.colSpan,
|
||||||
|
isRequired: item.isRequired,
|
||||||
|
dbSourceType: col ? sqlDataTypeToDbType(col.dataType) : 12, // 12 = DbType.String
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
})),
|
||||||
|
})
|
||||||
toast.push(
|
toast.push(
|
||||||
<Notification type="success" duration={2000}>
|
<Notification type="success" duration={2000}>
|
||||||
{translate('::ListForms.FormBilgileriKaydedildi')}
|
{translate('::ListForms.FormBilgileriKaydedildi')}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { Field, FieldProps, FormikErrors, FormikTouched } from 'formik'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import CreatableSelect from 'react-select/creatable'
|
import CreatableSelect from 'react-select/creatable'
|
||||||
import {
|
import {
|
||||||
|
FaArrowRight,
|
||||||
FaChevronDown,
|
FaChevronDown,
|
||||||
FaChevronRight,
|
FaChevronRight,
|
||||||
FaEdit,
|
FaEdit,
|
||||||
|
|
@ -670,6 +671,15 @@ const WizardStep1 = ({
|
||||||
const [menuDialogParentCode, setMenuDialogParentCode] = useState('')
|
const [menuDialogParentCode, setMenuDialogParentCode] = useState('')
|
||||||
const [menuDialogInitialOrder, setMenuDialogInitialOrder] = useState(999)
|
const [menuDialogInitialOrder, setMenuDialogInitialOrder] = useState(999)
|
||||||
|
|
||||||
|
const step1Missing = [
|
||||||
|
!wizardName && 'Wizard Adı',
|
||||||
|
!values.menuParentCode && 'Menu Parent',
|
||||||
|
!values.permissionGroupName && 'İzin Grubu',
|
||||||
|
!values.languageTextMenuEn && 'Menü (EN)',
|
||||||
|
!values.languageTextMenuTr && 'Menü (TR)',
|
||||||
|
].filter(Boolean) as string[]
|
||||||
|
const step1CanGo = step1Missing.length === 0
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pb-20">
|
<div className="pb-20">
|
||||||
{/* Wizard Name */}
|
{/* Wizard Name */}
|
||||||
|
|
@ -848,9 +858,16 @@ const WizardStep1 = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ─── Fixed Footer ─────────────────────────────── */}
|
{/* ─── Fixed Footer ─────────────────────────────── */}
|
||||||
<div className="fixed bottom-0 left-0 right-0 z-10 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 px-6 py-3">
|
<div className="fixed bottom-0 left-0 right-0 z-10 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 px-6 py-0 h-16 flex items-center">
|
||||||
<div className="max-w-sm mx-auto">
|
<div className="flex items-center gap-3 w-full">
|
||||||
<Button block variant="solid" type="button" onClick={onNext}>
|
<div className="flex-1 flex items-center gap-2">
|
||||||
|
{!step1CanGo && (
|
||||||
|
<span className="text-xs text-amber-600 dark:text-amber-400 font-medium">
|
||||||
|
⚠ Zorunlu: {step1Missing.join(', ')}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Button variant="solid" type="button" icon={<FaArrowRight />} disabled={!step1CanGo} onClick={onNext}>
|
||||||
{translate('::Next') || 'Next'}
|
{translate('::Next') || 'Next'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,12 @@
|
||||||
import { Button, FormItem, Input, Select } from '@/components/ui'
|
import { Button, FormItem, Input, Select } from '@/components/ui'
|
||||||
import { ListFormWizardDto } from '@/proxy/admin/list-form/models'
|
import { ListFormWizardDto } from '@/proxy/admin/list-form/models'
|
||||||
import { DbTypeEnum, SelectCommandTypeEnum } from '@/proxy/form/models'
|
import { SelectCommandTypeEnum } from '@/proxy/form/models'
|
||||||
import type { DatabaseColumnDto, SqlObjectExplorerDto } from '@/proxy/sql-query-manager/models'
|
import type { DatabaseColumnDto, SqlObjectExplorerDto } from '@/proxy/sql-query-manager/models'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { dbSourceTypeOptions, selectCommandTypeOptions } from './edit/options'
|
import { dbSourceTypeOptions, selectCommandTypeOptions, sqlDataTypeToDbType } from './edit/options'
|
||||||
import { Field, FieldProps, FormikErrors, FormikTouched } from 'formik'
|
import { Field, FieldProps, FormikErrors, FormikTouched } from 'formik'
|
||||||
import CreatableSelect from 'react-select/creatable'
|
import CreatableSelect from 'react-select/creatable'
|
||||||
|
import { FaArrowLeft, FaArrowRight } from 'react-icons/fa'
|
||||||
// ─── SQL dataType → DbTypeEnum mapper ────────────────────────────────────────
|
|
||||||
|
|
||||||
export function sqlDataTypeToDbType(sqlType: string): DbTypeEnum {
|
|
||||||
const t = sqlType
|
|
||||||
.toLowerCase()
|
|
||||||
.replace(/\s*\(.*\)/, '')
|
|
||||||
.trim()
|
|
||||||
if (['int', 'integer', 'int32'].includes(t)) return DbTypeEnum.Int32
|
|
||||||
if (['bigint', 'int64'].includes(t)) return DbTypeEnum.Int64
|
|
||||||
if (['smallint', 'int16'].includes(t)) return DbTypeEnum.Int16
|
|
||||||
if (['tinyint', 'byte'].includes(t)) return DbTypeEnum.Byte
|
|
||||||
if (['bit', 'boolean', 'bool'].includes(t)) return DbTypeEnum.Boolean
|
|
||||||
if (['float', 'real', 'double', 'double precision'].includes(t)) return DbTypeEnum.Double
|
|
||||||
if (['decimal', 'numeric', 'money', 'smallmoney'].includes(t)) return DbTypeEnum.Decimal
|
|
||||||
if (['uniqueidentifier'].includes(t)) return DbTypeEnum.Guid
|
|
||||||
if (['datetime2', 'smalldatetime', 'datetime'].includes(t)) return DbTypeEnum.DateTime
|
|
||||||
if (['date'].includes(t)) return DbTypeEnum.Date
|
|
||||||
if (['time'].includes(t)) return DbTypeEnum.Time
|
|
||||||
if (['datetimeoffset'].includes(t)) return DbTypeEnum.DateTimeOffset
|
|
||||||
if (['nvarchar', 'varchar', 'char', 'nchar', 'text', 'ntext', 'string'].includes(t))
|
|
||||||
return DbTypeEnum.String
|
|
||||||
if (['xml'].includes(t)) return DbTypeEnum.Xml
|
|
||||||
if (['binary', 'varbinary', 'image'].includes(t)) return DbTypeEnum.Binary
|
|
||||||
return DbTypeEnum.String
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Props ────────────────────────────────────────────────────────────────────
|
// ─── Props ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -86,6 +61,15 @@ const WizardStep2 = ({
|
||||||
onBack,
|
onBack,
|
||||||
onNext,
|
onNext,
|
||||||
}: WizardStep2Props) => {
|
}: WizardStep2Props) => {
|
||||||
|
const step2Missing = [
|
||||||
|
!values.listFormCode && 'ListForm Code',
|
||||||
|
!values.dataSourceCode && 'Veri Kaynağı',
|
||||||
|
!values.selectCommand && 'Select Command',
|
||||||
|
!values.keyFieldName && 'Key Field',
|
||||||
|
selectedColumns.size === 0 && 'Sütun seçimi',
|
||||||
|
].filter(Boolean) as string[]
|
||||||
|
const step2CanGo = step2Missing.length === 0
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pb-20">
|
<div className="pb-20">
|
||||||
{/* ListForm Code + Data Source */}
|
{/* ListForm Code + Data Source */}
|
||||||
|
|
@ -445,14 +429,21 @@ const WizardStep2 = ({
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
{/* ─── Fixed Footer ─────────────────────────────── */}
|
{/* ─── Fixed Footer ─────────────────────────────── */}
|
||||||
<div className="fixed bottom-0 left-0 right-0 z-10 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 px-6 py-3">
|
<div className="fixed bottom-0 left-0 right-0 z-10 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 px-6 py-0 h-16 flex items-center">
|
||||||
<div className="max-w-sm mx-auto flex gap-3">
|
<div className="flex items-center gap-3 w-full">
|
||||||
<Button block variant="default" type="button" onClick={onBack}>
|
<Button variant="default" type="button" icon={<FaArrowLeft />} onClick={onBack}>
|
||||||
{translate('::Back') || 'Back'}
|
{translate('::Back') || 'Back'}
|
||||||
</Button>
|
</Button>
|
||||||
<Button block variant="solid" type="button" onClick={onNext}>
|
<div className="flex-1 flex items-center justify-end gap-3">
|
||||||
{translate('::Next') || 'Next'}
|
{!step2CanGo && (
|
||||||
</Button>
|
<span className="text-xs text-amber-600 dark:text-amber-400 font-medium">
|
||||||
|
⚠ Zorunlu: {step2Missing.join(', ')}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<Button variant="solid" type="button" icon={<FaArrowRight />} disabled={!step2CanGo} onClick={onNext}>
|
||||||
|
{translate('::Next') || 'Next'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import {
|
||||||
import { SortableContext, arrayMove, useSortable, rectSortingStrategy } from '@dnd-kit/sortable'
|
import { SortableContext, arrayMove, useSortable, rectSortingStrategy } from '@dnd-kit/sortable'
|
||||||
import { CSS } from '@dnd-kit/utilities'
|
import { CSS } from '@dnd-kit/utilities'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { FaGripVertical, FaPlus, FaTimes, FaTrash, FaArrowRight, FaCode } from 'react-icons/fa'
|
import { FaArrowLeft, FaGripVertical, FaPlus, FaTimes, FaTrash, FaArrowRight, FaCode } from 'react-icons/fa'
|
||||||
|
|
||||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||||
export interface WizardGroupItem {
|
export interface WizardGroupItem {
|
||||||
|
|
@ -612,6 +612,15 @@ const WizardStep3 = ({
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasNoGroups = groups.length === 0
|
||||||
|
const hasEmptyGroup = groups.some((g) => g.items.length === 0)
|
||||||
|
const canProceed = !hasNoGroups && !hasEmptyGroup
|
||||||
|
const validationMsg = hasNoGroups
|
||||||
|
? 'En az bir grup eklemelisiniz.'
|
||||||
|
: hasEmptyGroup
|
||||||
|
? 'Her gruba en az bir sütun eklemelisiniz.'
|
||||||
|
: ''
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DndContext
|
<DndContext
|
||||||
sensors={sensors}
|
sensors={sensors}
|
||||||
|
|
@ -740,23 +749,22 @@ const WizardStep3 = ({
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
{/* ── Fixed Footer ─────────────────────────────────────────────────── */}
|
{/* ── Fixed Footer ─────────────────────────────────────────────────── */}
|
||||||
<div className="fixed bottom-0 left-0 right-0 z-10 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 px-6 py-3">
|
<div className="fixed bottom-0 left-0 right-0 z-10 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 px-6 py-0 h-16 flex items-center">
|
||||||
<div className="max-w-sm mx-auto flex gap-3">
|
<div className="flex items-center gap-3 w-full">
|
||||||
<Button
|
<Button variant="default" type="button" icon={<FaCode />} onClick={() => setIsHelperOpen(true)}>
|
||||||
block
|
|
||||||
variant="default"
|
|
||||||
type="button"
|
|
||||||
onClick={() => setIsHelperOpen(true)}
|
|
||||||
title="Helper Codes"
|
|
||||||
>
|
|
||||||
{translate('::Helper Codes') || 'Helper Codes'}
|
{translate('::Helper Codes') || 'Helper Codes'}
|
||||||
</Button>
|
</Button>
|
||||||
<Button block variant="default" type="button" onClick={onBack}>
|
<Button variant="default" type="button" icon={<FaArrowLeft />} onClick={onBack}>
|
||||||
{translate('::Back') || 'Back'}
|
{translate('::Back') || 'Back'}
|
||||||
</Button>
|
</Button>
|
||||||
<Button block variant="solid" type="button" onClick={onNext}>
|
<div className="flex-1 flex items-center justify-end gap-3">
|
||||||
{translate('::Next') || 'Next'}
|
{!canProceed && (
|
||||||
</Button>
|
<span className="text-xs text-amber-600 dark:text-amber-400 font-medium">⚠ {validationMsg}</span>
|
||||||
|
)}
|
||||||
|
<Button variant="solid" type="button" icon={<FaArrowRight />} disabled={!canProceed} onClick={onNext}>
|
||||||
|
{translate('::Next') || 'Next'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import type { DatabaseColumnDto } from '@/proxy/sql-query-manager/models'
|
||||||
import type { WizardGroup } from './WizardStep3'
|
import type { WizardGroup } from './WizardStep3'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import {
|
import {
|
||||||
|
FaArrowLeft,
|
||||||
FaCheckCircle,
|
FaCheckCircle,
|
||||||
FaChevronDown,
|
FaChevronDown,
|
||||||
FaChevronRight,
|
FaChevronRight,
|
||||||
|
|
@ -12,6 +13,7 @@ import {
|
||||||
FaRocket,
|
FaRocket,
|
||||||
FaSpinner,
|
FaSpinner,
|
||||||
} from 'react-icons/fa'
|
} from 'react-icons/fa'
|
||||||
|
import { selectCommandTypeOptions } from './edit/options'
|
||||||
|
|
||||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -37,15 +39,30 @@ interface LogEntry {
|
||||||
|
|
||||||
// ─── Deploy log steps ─────────────────────────────────────────────────────────
|
// ─── Deploy log steps ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function buildLogSteps(values: ListFormWizardDto, groups: WizardGroup[]): Omit<LogEntry, 'status'>[] {
|
function buildLogSteps(
|
||||||
|
values: ListFormWizardDto,
|
||||||
|
groups: WizardGroup[],
|
||||||
|
): Omit<LogEntry, 'status'>[] {
|
||||||
const totalFields = groups.reduce((acc, g) => acc + g.items.length, 0)
|
const totalFields = groups.reduce((acc, g) => acc + g.items.length, 0)
|
||||||
return [
|
return [
|
||||||
{ id: 1, label: 'Konfigürasyon doğrulanıyor…' },
|
{ id: 1, label: 'Konfigürasyon doğrulanıyor…' },
|
||||||
{ id: 2, label: `Menü oluşturuluyor: ${values.menuCode}`, detail: `Parent: ${values.menuParentCode}` },
|
{
|
||||||
{ id: 3, label: 'Dil metinleri kaydediliyor', detail: `EN: ${values.languageTextMenuEn} / TR: ${values.languageTextMenuTr}` },
|
id: 2,
|
||||||
|
label: `Menü oluşturuluyor: ${values.menuCode}`,
|
||||||
|
detail: `Parent: ${values.menuParentCode}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
label: 'Dil metinleri kaydediliyor',
|
||||||
|
detail: `EN: ${values.languageTextMenuEn} / TR: ${values.languageTextMenuTr}`,
|
||||||
|
},
|
||||||
{ id: 4, label: `İzin grubu yapılandırılıyor: ${values.permissionGroupName}` },
|
{ id: 4, label: `İzin grubu yapılandırılıyor: ${values.permissionGroupName}` },
|
||||||
{ id: 5, label: `Veri kaynağı bağlanıyor: ${values.dataSourceCode}` },
|
{ id: 5, label: `Veri kaynağı bağlanıyor: ${values.dataSourceCode}` },
|
||||||
{ id: 6, label: `ListForm oluşturuluyor: ${values.listFormCode}`, detail: `Key: ${values.keyFieldName}` },
|
{
|
||||||
|
id: 6,
|
||||||
|
label: `ListForm oluşturuluyor: ${values.listFormCode}`,
|
||||||
|
detail: `Key: ${values.keyFieldName}`,
|
||||||
|
},
|
||||||
{ id: 7, label: `Form grupları kaydediliyor (${groups.length} grup, ${totalFields} alan)` },
|
{ id: 7, label: `Form grupları kaydediliyor (${groups.length} grup, ${totalFields} alan)` },
|
||||||
{ id: 8, label: 'Sunucuya deploy ediliyor…' },
|
{ id: 8, label: 'Sunucuya deploy ediliyor…' },
|
||||||
{ id: 9, label: 'Tamamlandı ✓' },
|
{ id: 9, label: 'Tamamlandı ✓' },
|
||||||
|
|
@ -101,13 +118,14 @@ function Row({ label, value }: { label: string; value?: string | number }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-2 py-1 border-b border-gray-100 dark:border-gray-800 last:border-0">
|
<div className="flex gap-2 py-1 border-b border-gray-100 dark:border-gray-800 last:border-0">
|
||||||
<span className="text-xs text-gray-400 w-40 shrink-0">{label}</span>
|
<span className="text-xs text-gray-400 w-40 shrink-0">{label}</span>
|
||||||
<span className="text-xs text-gray-700 dark:text-gray-200 font-medium break-all">{value}</span>
|
<span className="text-xs text-gray-700 dark:text-gray-200 font-medium break-all">
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── WizardStep4 ──────────────────────────────────────────────────────────────
|
// ─── WizardStep4 ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const WizardStep4 = ({
|
const WizardStep4 = ({
|
||||||
values,
|
values,
|
||||||
wizardName,
|
wizardName,
|
||||||
|
|
@ -179,42 +197,43 @@ const WizardStep4 = ({
|
||||||
const totalFields = groups.reduce((acc, g) => acc + g.items.length, 0)
|
const totalFields = groups.reduce((acc, g) => acc + g.items.length, 0)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-6 pb-24">
|
<div className="grid grid-cols-[3fr_2fr] gap-5 pb-24 items-start">
|
||||||
{/* ── Left: Summary ────────────────────────────────────────────── */}
|
{/* ── Left: Summary ──────────────────────────────────────────── */}
|
||||||
<div className="flex-1 flex flex-col gap-3 overflow-y-auto max-h-[calc(100vh-220px)]">
|
<div className="flex flex-col gap-3">
|
||||||
<h6 className="text-sm font-bold text-gray-600 dark:text-gray-300 uppercase tracking-wide mb-1">
|
<div className="grid grid-cols-2 gap-3 items-start">
|
||||||
Özet
|
<Section title="Menü Bilgileri">
|
||||||
</h6>
|
<Row label="Wizard Adı" value={wizardName} />
|
||||||
|
<Row label="Menu Code" value={values.menuCode} />
|
||||||
|
<Row label="Menu Parent" value={values.menuParentCode} />
|
||||||
|
<Row label="İzin Grubu" value={values.permissionGroupName} />
|
||||||
|
<Row label="İkon" value={values.menuIcon} />
|
||||||
|
<Row label="Menü (TR)" value={values.languageTextMenuTr} />
|
||||||
|
<Row label="Menü (EN)" value={values.languageTextMenuEn} />
|
||||||
|
<Row label="Menü Parent (TR)" value={values.languageTextMenuParentTr} />
|
||||||
|
<Row label="Menü Parent (EN)" value={values.languageTextMenuParentEn} />
|
||||||
|
</Section>
|
||||||
|
|
||||||
{/* Step 1 Summary */}
|
<Section title="ListForm Ayarları">
|
||||||
<Section title="Menü Bilgileri">
|
<Row label="ListForm Code" value={values.listFormCode} />
|
||||||
<Row label="Wizard Adı" value={wizardName} />
|
<Row label="Başlık (TR)" value={values.languageTextTitleTr} />
|
||||||
<Row label="Menu Code" value={values.menuCode} />
|
<Row label="Başlık (EN)" value={values.languageTextTitleEn} />
|
||||||
<Row label="Menu Parent" value={values.menuParentCode} />
|
<Row label="Açıklama (TR)" value={values.languageTextDescTr} />
|
||||||
<Row label="İzin Grubu" value={values.permissionGroupName} />
|
<Row label="Açıklama (EN)" value={values.languageTextDescEn} />
|
||||||
<Row label="İkon" value={values.menuIcon} />
|
<Row label="Veri Kaynağı" value={values.dataSourceCode} />
|
||||||
<Row label="Menü (TR)" value={values.languageTextMenuTr} />
|
<Row label="Connection String" value={values.dataSourceConnectionString} />
|
||||||
<Row label="Menü (EN)" value={values.languageTextMenuEn} />
|
<Row
|
||||||
<Row label="Menü Parent (TR)" value={values.languageTextMenuParentTr} />
|
label="Komut Tipi"
|
||||||
<Row label="Menü Parent (EN)" value={values.languageTextMenuParentEn} />
|
value={
|
||||||
</Section>
|
selectCommandTypeOptions.find((o) => o.value === values.selectCommandType)?.label ||
|
||||||
|
values.selectCommandType
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Row label="Select Command" value={values.selectCommand} />
|
||||||
|
<Row label="Key Field" value={values.keyFieldName} />
|
||||||
|
<Row label="Key Field Tipi" value={String(values.keyFieldDbSourceType)} />
|
||||||
|
</Section>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Step 2 Summary */}
|
|
||||||
<Section title="ListForm Ayarları">
|
|
||||||
<Row label="ListForm Code" value={values.listFormCode} />
|
|
||||||
<Row label="Başlık (TR)" value={values.languageTextTitleTr} />
|
|
||||||
<Row label="Başlık (EN)" value={values.languageTextTitleEn} />
|
|
||||||
<Row label="Açıklama (TR)" value={values.languageTextDescTr} />
|
|
||||||
<Row label="Açıklama (EN)" value={values.languageTextDescEn} />
|
|
||||||
<Row label="Veri Kaynağı" value={values.dataSourceCode} />
|
|
||||||
<Row label="Connection String" value={values.dataSourceConnectionString} />
|
|
||||||
<Row label="Komut Tipi" value={values.selectCommandType} />
|
|
||||||
<Row label="Select Command" value={values.selectCommand} />
|
|
||||||
<Row label="Key Field" value={values.keyFieldName} />
|
|
||||||
<Row label="Key Field Tipi" value={String(values.keyFieldDbSourceType)} />
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
{/* Columns */}
|
|
||||||
<Section title="Seçili Sütunlar" badge={selectedColumns.size}>
|
<Section title="Seçili Sütunlar" badge={selectedColumns.size}>
|
||||||
<div className="flex flex-wrap gap-1.5">
|
<div className="flex flex-wrap gap-1.5">
|
||||||
{[...selectedColumns].map((col) => {
|
{[...selectedColumns].map((col) => {
|
||||||
|
|
@ -226,7 +245,7 @@ const WizardStep4 = ({
|
||||||
>
|
>
|
||||||
{col}
|
{col}
|
||||||
{meta?.dataType && (
|
{meta?.dataType && (
|
||||||
<span className="text-[10px] text-indigo-400">{meta.dataType}</span>
|
<span className="text-[10px] text-indigo-400 opacity-70">{meta.dataType}</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
|
@ -234,11 +253,15 @@ const WizardStep4 = ({
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
{/* Step 3 – Groups */}
|
<Section title="Form Grupları" badge={groups.length}>
|
||||||
<Section title="Form Grupları" badge={groups.length} defaultOpen={true}>
|
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
{groups.map((g) => (
|
{groups.map((g) => (
|
||||||
<Section key={g.id} title={g.caption || '(Grup)'} badge={`${g.items.length} alan · ${g.colCount} sütun`} defaultOpen={false}>
|
<Section
|
||||||
|
key={g.id}
|
||||||
|
title={g.caption || '(Grup)'}
|
||||||
|
badge={`${g.items.length} alan · ${g.colCount} sütun`}
|
||||||
|
defaultOpen={false}
|
||||||
|
>
|
||||||
<div className="flex flex-col gap-0.5">
|
<div className="flex flex-col gap-0.5">
|
||||||
{g.items.length === 0 ? (
|
{g.items.length === 0 ? (
|
||||||
<span className="text-xs text-gray-300 italic">Alan yok</span>
|
<span className="text-xs text-gray-300 italic">Alan yok</span>
|
||||||
|
|
@ -254,7 +277,7 @@ const WizardStep4 = ({
|
||||||
<span className="text-[10px] text-gray-400 bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded">
|
<span className="text-[10px] text-gray-400 bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded">
|
||||||
{item.editorType}
|
{item.editorType}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-[10px] text-gray-400 ml-auto">
|
<span className="text-[10px] text-gray-400 ml-auto shrink-0">
|
||||||
span:{item.colSpan}
|
span:{item.colSpan}
|
||||||
{item.isRequired && (
|
{item.isRequired && (
|
||||||
<span className="ml-1 text-red-400 font-semibold">*</span>
|
<span className="ml-1 text-red-400 font-semibold">*</span>
|
||||||
|
|
@ -270,9 +293,9 @@ const WizardStep4 = ({
|
||||||
</Section>
|
</Section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Right: Deploy ────────────────────────────────────────────── */}
|
{/* ── Right: Deploy ──────────────────────────────────────────── */}
|
||||||
<div className="w-96 shrink-0 flex flex-col gap-4">
|
<div className="sticky top-4 flex flex-col gap-3 max-h-[calc(100vh-200px)]">
|
||||||
{/* Stats bar */}
|
{/* Stats */}
|
||||||
<div className="grid grid-cols-3 gap-2">
|
<div className="grid grid-cols-3 gap-2">
|
||||||
{[
|
{[
|
||||||
{ label: 'Grup', value: groups.length },
|
{ label: 'Grup', value: groups.length },
|
||||||
|
|
@ -281,18 +304,23 @@ const WizardStep4 = ({
|
||||||
].map((s) => (
|
].map((s) => (
|
||||||
<div
|
<div
|
||||||
key={s.label}
|
key={s.label}
|
||||||
className="rounded-xl border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 p-3 text-center"
|
className="rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-3 text-center shadow-sm"
|
||||||
>
|
>
|
||||||
<div className="text-xl font-bold text-indigo-600 dark:text-indigo-400">{s.value}</div>
|
<div className="text-2xl font-bold text-indigo-600 dark:text-indigo-400">
|
||||||
|
{s.value}
|
||||||
|
</div>
|
||||||
<div className="text-xs text-gray-400 mt-0.5">{s.label}</div>
|
<div className="text-xs text-gray-400 mt-0.5">{s.label}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Log panel */}
|
{/* Log panel — grows to fill remaining height */}
|
||||||
<div className="rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden flex flex-col flex-1">
|
<div className="rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden flex flex-col flex-1 min-h-0">
|
||||||
<div className="px-4 py-2.5 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
<div className="px-4 py-2.5 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between shrink-0">
|
||||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">Deploy Log</span>
|
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200 flex items-center gap-2">
|
||||||
|
<FaRocket className="text-indigo-400 text-xs" />
|
||||||
|
Deploy Log
|
||||||
|
</span>
|
||||||
{isDone && (
|
{isDone && (
|
||||||
<span className="text-xs text-emerald-500 font-semibold flex items-center gap-1">
|
<span className="text-xs text-emerald-500 font-semibold flex items-center gap-1">
|
||||||
<FaCheckCircle /> Başarılı
|
<FaCheckCircle /> Başarılı
|
||||||
|
|
@ -305,26 +333,33 @@ const WizardStep4 = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 min-h-[280px] max-h-[calc(100vh-380px)] overflow-y-auto p-3 bg-gray-950 dark:bg-black font-mono">
|
<div className="flex-1 overflow-y-auto p-4 bg-[#0d1117] dark:bg-black font-mono min-h-[360px]">
|
||||||
{logs.length === 0 ? (
|
{logs.length === 0 ? (
|
||||||
<div className="text-xs text-gray-500 italic py-4 text-center select-none">
|
<div className="flex flex-col items-center justify-center h-full gap-3 py-10 select-none">
|
||||||
Deploy başlatmak için butona tıklayın
|
<FaRocket className="text-gray-700 text-3xl" />
|
||||||
|
<span className="text-xs text-gray-600 italic text-center">
|
||||||
|
Tüm bilgiler hazır.
|
||||||
|
<br />
|
||||||
|
Deploy başlatmak için butona tıklayın.
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col gap-1.5">
|
<div className="flex flex-col gap-2">
|
||||||
{logs.map((log) => (
|
{logs.map((log) => (
|
||||||
<div key={log.id} className="flex items-start gap-2">
|
<div key={log.id} className="flex items-start gap-2.5">
|
||||||
<LogIcon status={log.status} />
|
<span className="mt-0.5 shrink-0">
|
||||||
|
<LogIcon status={log.status} />
|
||||||
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
className={`text-xs ${
|
className={`text-xs leading-relaxed ${
|
||||||
log.status === 'success'
|
log.status === 'success'
|
||||||
? 'text-emerald-400'
|
? 'text-emerald-400'
|
||||||
: log.status === 'error'
|
: log.status === 'error'
|
||||||
? 'text-red-400'
|
? 'text-red-400'
|
||||||
: log.status === 'running'
|
: log.status === 'running'
|
||||||
? 'text-indigo-300'
|
? 'text-yellow-300'
|
||||||
: 'text-gray-500'
|
: 'text-gray-600'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{log.label}
|
{log.label}
|
||||||
|
|
@ -335,44 +370,35 @@ const WizardStep4 = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
{isDone && (
|
||||||
|
<div className="mt-4 rounded-lg border border-emerald-800 bg-emerald-950/40 px-4 py-2.5 text-xs text-emerald-400 text-center font-semibold">
|
||||||
|
🎉 ListForm başarıyla oluşturuldu ve deploy edildi!
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isDone && (
|
|
||||||
<div className="rounded-xl border border-emerald-200 dark:border-emerald-800 bg-emerald-50 dark:bg-emerald-900/20 p-4 text-sm text-emerald-700 dark:text-emerald-300 text-center font-medium">
|
|
||||||
🎉 ListForm başarıyla oluşturuldu ve deploy edildi!
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Fixed Footer ─────────────────────────────────────────────── */}
|
{/* ── Fixed Footer ─────────────────────────────────────────────── */}
|
||||||
<div className="fixed bottom-0 left-0 right-0 z-10 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 px-6 py-3">
|
<div className="fixed bottom-0 left-0 right-0 z-10 bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700 px-6 py-0 h-16 flex items-center">
|
||||||
<div className="max-w-5xl mx-auto flex gap-3">
|
<div className="flex items-center gap-3 w-full">
|
||||||
<Button
|
<Button variant="default" type="button" icon={<FaArrowLeft />} onClick={onBack} disabled={isDeploying}>
|
||||||
variant="default"
|
|
||||||
type="button"
|
|
||||||
onClick={onBack}
|
|
||||||
disabled={isDeploying}
|
|
||||||
>
|
|
||||||
{translate('::Back') || 'Back'}
|
{translate('::Back') || 'Back'}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<div className="flex-1 flex items-center justify-end">
|
||||||
block
|
<Button
|
||||||
variant="solid"
|
variant="solid"
|
||||||
type="button"
|
type="button"
|
||||||
icon={<FaRocket />}
|
icon={<FaRocket />}
|
||||||
loading={isDeploying}
|
loading={isDeploying}
|
||||||
disabled={isDeploying || isDone}
|
disabled={isDeploying || isDone}
|
||||||
onClick={runDeploy}
|
onClick={runDeploy}
|
||||||
>
|
>
|
||||||
{isDeploying
|
{isDeploying ? 'Deploy ediliyor…' : isDone ? '✓ Tamamlandı' : 'Deploy & Kaydet'}
|
||||||
? 'Deploy ediliyor…'
|
</Button>
|
||||||
: isDone
|
</div>
|
||||||
? 'Tamamlandı'
|
|
||||||
: translate('::Save') || 'Deploy & Save'}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,12 @@ import { Field, FieldProps, Form, Formik } from 'formik'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { FormEditProps } from './FormEdit'
|
import { FormEditProps } from './FormEdit'
|
||||||
import { dbSourceTypeOptions, selectCommandTypeOptions } from './options'
|
import { dbSourceTypeOptions, selectCommandTypeOptions, sqlDataTypeToDbType } from './options'
|
||||||
import { DataSourceTypeEnum, SelectCommandTypeEnum } from '@/proxy/form/models'
|
import { DataSourceTypeEnum, SelectCommandTypeEnum } from '@/proxy/form/models'
|
||||||
import { getDataSources } from '@/services/data-source.service'
|
import { getDataSources } from '@/services/data-source.service'
|
||||||
|
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
||||||
|
import type { DatabaseColumnDto, SqlObjectExplorerDto } from '@/proxy/sql-query-manager/models'
|
||||||
|
import CreatableSelect from 'react-select/creatable'
|
||||||
|
|
||||||
const schema = Yup.object().shape({
|
const schema = Yup.object().shape({
|
||||||
isOrganizationUnit: Yup.bool(),
|
isOrganizationUnit: Yup.bool(),
|
||||||
|
|
@ -25,6 +28,10 @@ const schema = Yup.object().shape({
|
||||||
})
|
})
|
||||||
|
|
||||||
function FormTabDatabaseDataSource(props: FormEditProps) {
|
function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
|
const initialValues = useStoreState((s) => s.admin.lists.values)
|
||||||
|
const { translate } = useLocalization()
|
||||||
|
|
||||||
|
// ── Data Source List ────────────────────────────────────────────────────────
|
||||||
const [dataSourceList, setDataSourceList] = useState<SelectBoxOption[]>([])
|
const [dataSourceList, setDataSourceList] = useState<SelectBoxOption[]>([])
|
||||||
const getDataSourceList = async () => {
|
const getDataSourceList = async () => {
|
||||||
const response = await getDataSources()
|
const response = await getDataSources()
|
||||||
|
|
@ -41,11 +48,74 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
getDataSourceList()
|
getDataSourceList()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const initialValues = useStoreState((s) => s.admin.lists.values)
|
// ── DB Objects ──────────────────────────────────────────────────────────────
|
||||||
|
const [dbObjects, setDbObjects] = useState<SqlObjectExplorerDto | null>(null)
|
||||||
|
const [isLoadingDbObjects, setIsLoadingDbObjects] = useState(false)
|
||||||
|
// Mevcut kayıttaki dataSourceCode ile başlat
|
||||||
|
const [currentDataSource, setCurrentDataSource] = useState(initialValues?.dataSourceCode ?? '')
|
||||||
|
|
||||||
|
const loadDbObjects = async (dsCode: string) => {
|
||||||
|
if (!dsCode) {
|
||||||
|
setDbObjects(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setIsLoadingDbObjects(true)
|
||||||
|
try {
|
||||||
|
const res = await sqlObjectManagerService.getAllObjects(dsCode)
|
||||||
|
setDbObjects(res.data)
|
||||||
|
} catch {
|
||||||
|
setDbObjects(null)
|
||||||
|
} finally {
|
||||||
|
setIsLoadingDbObjects(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadDbObjects(currentDataSource)
|
||||||
|
}, [currentDataSource])
|
||||||
|
|
||||||
|
// ── Columns ─────────────────────────────────────────────────────────────────
|
||||||
|
const [selectCommandColumns, setSelectCommandColumns] = useState<DatabaseColumnDto[]>([])
|
||||||
|
const [isLoadingColumns, setIsLoadingColumns] = useState(false)
|
||||||
|
|
||||||
|
const loadColumns = async (dsCode: string, schema: string, name: string) => {
|
||||||
|
if (!dsCode || !name) {
|
||||||
|
setSelectCommandColumns([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setIsLoadingColumns(true)
|
||||||
|
try {
|
||||||
|
const res = await sqlObjectManagerService.getTableColumns(dsCode, schema, name)
|
||||||
|
setSelectCommandColumns(res.data ?? [])
|
||||||
|
} catch {
|
||||||
|
setSelectCommandColumns([])
|
||||||
|
} finally {
|
||||||
|
setIsLoadingColumns(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mevcut selectCommand'a göre sütunları otomatik yükle (dbObjects hazır olunca)
|
||||||
|
useEffect(() => {
|
||||||
|
const dsCode = initialValues?.dataSourceCode
|
||||||
|
const cmd = initialValues?.selectCommand
|
||||||
|
if (!dbObjects || !dsCode || !cmd) return
|
||||||
|
// Daha önce yüklenmiş sütun varsa tekrar yükleme
|
||||||
|
if (selectCommandColumns.length > 0) return
|
||||||
|
|
||||||
|
const table = dbObjects.tables.find((t) => t.tableName === cmd)
|
||||||
|
if (table) { loadColumns(dsCode, table.schemaName, table.tableName); return }
|
||||||
|
const view = dbObjects.views.find((v) => v.viewName === cmd)
|
||||||
|
if (view) { loadColumns(dsCode, view.schemaName, view.viewName); return }
|
||||||
|
const fn = dbObjects.functions.find((f) => f.functionName === cmd)
|
||||||
|
if (fn) { loadColumns(dsCode, fn.schemaName, fn.functionName); return }
|
||||||
|
const sp = dbObjects.storedProcedures.find((p) => p.procedureName === cmd)
|
||||||
|
if (sp) { loadColumns(dsCode, sp.schemaName, sp.procedureName); return }
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [dbObjects])
|
||||||
|
|
||||||
if (!initialValues) {
|
if (!initialValues) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const { translate } = useLocalization()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
|
@ -116,34 +186,17 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
value={dataSourceList?.filter(
|
value={dataSourceList?.filter(
|
||||||
(option) => option.value === values.dataSourceCode,
|
(option) => option.value === values.dataSourceCode,
|
||||||
)}
|
)}
|
||||||
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
onChange={(option) => {
|
||||||
/>
|
const val = option?.value ?? ''
|
||||||
)}
|
form.setFieldValue(field.name, val)
|
||||||
</Field>
|
setCurrentDataSource(val)
|
||||||
</FormItem>
|
// reset dependent fields
|
||||||
<FormItem
|
form.setFieldValue('selectCommand', '')
|
||||||
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceSelectCommandType')}
|
form.setFieldValue('selectCommandType', undefined)
|
||||||
invalid={errors.selectCommandType && touched.selectCommandType}
|
form.setFieldValue('keyFieldName', '')
|
||||||
errorMessage={errors.selectCommandType}
|
form.setFieldValue('keyFieldDbSourceType', undefined)
|
||||||
>
|
setSelectCommandColumns([])
|
||||||
<Field
|
}}
|
||||||
type="text"
|
|
||||||
autoComplete="off"
|
|
||||||
name="selectCommandType"
|
|
||||||
placeholder={translate(
|
|
||||||
'::ListForms.ListFormEdit.DatabaseDataSourceSelectCommandType',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{({ field, form }: FieldProps<SelectCommandTypeEnum>) => (
|
|
||||||
<Select
|
|
||||||
field={field}
|
|
||||||
form={form}
|
|
||||||
isClearable={true}
|
|
||||||
options={selectCommandTypeOptions}
|
|
||||||
value={selectCommandTypeOptions?.filter(
|
|
||||||
(option: any) => option.value === values.selectCommandType,
|
|
||||||
)}
|
|
||||||
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
|
@ -152,16 +205,111 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceSelectCommand')}
|
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceSelectCommand')}
|
||||||
invalid={errors.selectCommand && touched.selectCommand}
|
invalid={errors.selectCommand && touched.selectCommand}
|
||||||
errorMessage={errors.selectCommand}
|
errorMessage={errors.selectCommand}
|
||||||
|
extra={
|
||||||
|
values.selectCommandType != null ? (
|
||||||
|
<span className="ml-2 text-xs px-2 py-0.5 rounded bg-indigo-100 text-indigo-600 dark:bg-indigo-900/40 dark:text-indigo-300">
|
||||||
|
{selectCommandTypeOptions.find(
|
||||||
|
(o: any) => o.value === values.selectCommandType,
|
||||||
|
)?.label ?? String(values.selectCommandType)}
|
||||||
|
</span>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Field
|
<Field type="text" autoComplete="off" name="selectCommand">
|
||||||
type="text"
|
{({ field, form }: FieldProps<string>) => {
|
||||||
autoComplete="off"
|
const grouped = dbObjects
|
||||||
name="selectCommand"
|
? [
|
||||||
placeholder={translate(
|
{
|
||||||
'::ListForms.ListFormEdit.DatabaseDataSourceSelectCommand',
|
label: 'Tables',
|
||||||
)}
|
options: dbObjects.tables.map((t) => ({
|
||||||
component={Input}
|
label: t.tableName,
|
||||||
/>
|
value: t.tableName,
|
||||||
|
__type: SelectCommandTypeEnum.Table,
|
||||||
|
__schema: t.schemaName,
|
||||||
|
__rawName: t.tableName,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Views',
|
||||||
|
options: dbObjects.views.map((v) => ({
|
||||||
|
label: v.viewName,
|
||||||
|
value: v.viewName,
|
||||||
|
__type: SelectCommandTypeEnum.View,
|
||||||
|
__schema: v.schemaName,
|
||||||
|
__rawName: v.viewName,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Functions',
|
||||||
|
options: dbObjects.functions.map((f) => ({
|
||||||
|
label: f.functionName,
|
||||||
|
value: f.functionName,
|
||||||
|
__type: SelectCommandTypeEnum.TableValuedFunction,
|
||||||
|
__schema: f.schemaName,
|
||||||
|
__rawName: f.functionName,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Stored Procedures',
|
||||||
|
options: dbObjects.storedProcedures.map((p) => ({
|
||||||
|
label: p.procedureName,
|
||||||
|
value: p.procedureName,
|
||||||
|
__type: SelectCommandTypeEnum.StoredProcedure,
|
||||||
|
__schema: p.schemaName,
|
||||||
|
__rawName: p.procedureName,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
componentAs={CreatableSelect}
|
||||||
|
field={field}
|
||||||
|
form={form}
|
||||||
|
isClearable
|
||||||
|
isLoading={isLoadingDbObjects}
|
||||||
|
options={grouped}
|
||||||
|
placeholder={
|
||||||
|
isLoadingDbObjects
|
||||||
|
? translate('::Loading')
|
||||||
|
: translate(
|
||||||
|
'::ListForms.ListFormEdit.DatabaseDataSourceSelectCommand',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
value={field.value ? { label: field.value, value: field.value } : null}
|
||||||
|
onChange={(option: any) => {
|
||||||
|
if (!option) {
|
||||||
|
form.setFieldValue(field.name, '')
|
||||||
|
form.setFieldValue('selectCommandType', undefined)
|
||||||
|
form.setFieldValue('keyFieldName', '')
|
||||||
|
form.setFieldValue('keyFieldDbSourceType', undefined)
|
||||||
|
setSelectCommandColumns([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
form.setFieldValue(field.name, option.value)
|
||||||
|
const type = option.__isNew__
|
||||||
|
? SelectCommandTypeEnum.Query
|
||||||
|
: (option.__type ?? SelectCommandTypeEnum.Query)
|
||||||
|
form.setFieldValue('selectCommandType', type)
|
||||||
|
form.setFieldValue('keyFieldName', '')
|
||||||
|
form.setFieldValue('keyFieldDbSourceType', undefined)
|
||||||
|
if (!option.__isNew__ && option.__schema != null && option.__rawName) {
|
||||||
|
loadColumns(values.dataSourceCode ?? '', option.__schema, option.__rawName)
|
||||||
|
} else {
|
||||||
|
setSelectCommandColumns([])
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onCreateOption={(inputValue: string) => {
|
||||||
|
form.setFieldValue(field.name, inputValue)
|
||||||
|
form.setFieldValue('selectCommandType', SelectCommandTypeEnum.Query)
|
||||||
|
form.setFieldValue('keyFieldName', '')
|
||||||
|
form.setFieldValue('keyFieldDbSourceType', undefined)
|
||||||
|
setSelectCommandColumns([])
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</Field>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceTableName')}
|
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceTableName')}
|
||||||
|
|
@ -175,47 +323,64 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
placeholder={translate('::ListForms.ListFormEdit.DatabaseDataSourceTableName')}
|
placeholder={translate('::ListForms.ListFormEdit.DatabaseDataSourceTableName')}
|
||||||
component={Input}
|
component={Input}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName')}
|
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName')}
|
||||||
invalid={errors.keyFieldName && touched.keyFieldName}
|
invalid={errors.keyFieldName && touched.keyFieldName}
|
||||||
errorMessage={errors.keyFieldName}
|
errorMessage={errors.keyFieldName}
|
||||||
|
extra={
|
||||||
|
values.keyFieldName && values.keyFieldDbSourceType != null ? (
|
||||||
|
<span className="ml-2 text-xs px-2 py-0.5 rounded bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300">
|
||||||
|
{dbSourceTypeOptions.find(
|
||||||
|
(o: any) => o.value === values.keyFieldDbSourceType,
|
||||||
|
)?.label ?? String(values.keyFieldDbSourceType)}
|
||||||
|
</span>
|
||||||
|
) : selectCommandColumns.length === 0 && !isLoadingColumns ? (
|
||||||
|
<span className="text-xs ml-2 text-gray-400">
|
||||||
|
{translate('::ListForms.ListFormEdit.DatabaseDataSourceSelectCommand') +
|
||||||
|
' seçince sütunlar yüklenir'}
|
||||||
|
</span>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Field
|
<Field type="text" autoComplete="off" name="keyFieldName">
|
||||||
type="text"
|
{({ field, form }: FieldProps<string>) => (
|
||||||
autoComplete="off"
|
|
||||||
name="keyFieldName"
|
|
||||||
placeholder={translate(
|
|
||||||
'::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName',
|
|
||||||
)}
|
|
||||||
component={Input}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
|
||||||
label={translate(
|
|
||||||
'::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldDbSourceType',
|
|
||||||
)}
|
|
||||||
// invalid={errors.keyFieldDbSourceType && touched.keyFieldDbSourceType}
|
|
||||||
// errorMessage={errors.keyFieldDbSourceType}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
type="text"
|
|
||||||
autoComplete="off"
|
|
||||||
name="keyFieldDbSourceType"
|
|
||||||
placeholder={translate(
|
|
||||||
'::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldDbSourceType',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{({ field, form }: FieldProps<any>) => (
|
|
||||||
<Select
|
<Select
|
||||||
|
componentAs={CreatableSelect}
|
||||||
field={field}
|
field={field}
|
||||||
form={form}
|
form={form}
|
||||||
isClearable={true}
|
isClearable
|
||||||
options={dbSourceTypeOptions}
|
isLoading={isLoadingColumns}
|
||||||
value={dbSourceTypeOptions?.filter(
|
placeholder={
|
||||||
(option: any) => option.value === values.keyFieldDbSourceType,
|
isLoadingColumns
|
||||||
)}
|
? translate('::Loading')
|
||||||
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
: translate(
|
||||||
|
'::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
options={selectCommandColumns.map((c) => ({
|
||||||
|
label: `${c.columnName} (${c.dataType})`,
|
||||||
|
value: c.columnName,
|
||||||
|
__dataType: c.dataType,
|
||||||
|
}))}
|
||||||
|
value={field.value ? { label: field.value, value: field.value } : null}
|
||||||
|
onChange={(option: any) => {
|
||||||
|
if (!option) {
|
||||||
|
form.setFieldValue(field.name, '')
|
||||||
|
form.setFieldValue('keyFieldDbSourceType', undefined)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
form.setFieldValue(field.name, option.value)
|
||||||
|
if (!option.__isNew__ && option.__dataType) {
|
||||||
|
form.setFieldValue(
|
||||||
|
'keyFieldDbSourceType',
|
||||||
|
sqlDataTypeToDbType(option.__dataType),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onCreateOption={(inputValue: string) => {
|
||||||
|
form.setFieldValue(field.name, inputValue)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,13 @@ import setNull from '@/utils/setNull'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { Field, FieldProps, Form, Formik, FormikHelpers } from 'formik'
|
import { Field, FieldProps, Form, Formik, FormikHelpers } from 'formik'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { FaFileMedical, FaCopy, FaEyeSlash, FaMinus, FaTimes } from 'react-icons/fa'
|
import { FaFileMedical, FaCopy, FaEyeSlash, FaMinus, FaTimes, FaTable } from 'react-icons/fa'
|
||||||
import { number, object, string } from 'yup'
|
import { number, object, string } from 'yup'
|
||||||
import FormFieldEdit from './FormFieldEdit'
|
import FormFieldEdit from './FormFieldEdit'
|
||||||
import { dbSourceTypeOptions } from '../options'
|
import { dbSourceTypeOptions, sqlDataTypeToDbType } from '../options'
|
||||||
import { IdentityRoleDto, IdentityUserDto } from '@/proxy/admin/models'
|
import { IdentityRoleDto, IdentityUserDto } from '@/proxy/admin/models'
|
||||||
|
import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
||||||
|
import type { DatabaseColumnDto } from '@/proxy/sql-query-manager/models'
|
||||||
|
|
||||||
export interface FormFieldEditProps {
|
export interface FormFieldEditProps {
|
||||||
onSubmit: (
|
onSubmit: (
|
||||||
|
|
@ -87,6 +89,103 @@ function FormFields({
|
||||||
const [fieldList, setFieldList] = useState<SelectBoxOption[]>([])
|
const [fieldList, setFieldList] = useState<SelectBoxOption[]>([])
|
||||||
const cultureName = useStoreState((state) => state.locale.currentLang)
|
const cultureName = useStoreState((state) => state.locale.currentLang)
|
||||||
|
|
||||||
|
// ── All DB Columns (for "Tüm Sütunları Ekle") ───────────────────────────────
|
||||||
|
const listFormValues = useStoreState((s) => s.admin.lists.values)
|
||||||
|
const [allDbColumns, setAllDbColumns] = useState<DatabaseColumnDto[]>([])
|
||||||
|
const [isAddingAllColumns, setIsAddingAllColumns] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const dsCode = listFormValues?.dataSourceCode
|
||||||
|
const cmd = listFormValues?.selectCommand
|
||||||
|
if (!dsCode || !cmd) {
|
||||||
|
setAllDbColumns([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const load = async () => {
|
||||||
|
try {
|
||||||
|
const objRes = await sqlObjectManagerService.getAllObjects(dsCode)
|
||||||
|
const dbObjects = objRes.data
|
||||||
|
if (!dbObjects) return
|
||||||
|
let schema = ''
|
||||||
|
let name = ''
|
||||||
|
const table = dbObjects.tables.find((t) => t.tableName === cmd)
|
||||||
|
if (table) {
|
||||||
|
schema = table.schemaName
|
||||||
|
name = table.tableName
|
||||||
|
} else {
|
||||||
|
const view = dbObjects.views.find((v) => v.viewName === cmd)
|
||||||
|
if (view) {
|
||||||
|
schema = view.schemaName
|
||||||
|
name = view.viewName
|
||||||
|
} else {
|
||||||
|
const fn = dbObjects.functions.find((f) => f.functionName === cmd)
|
||||||
|
if (fn) {
|
||||||
|
schema = fn.schemaName
|
||||||
|
name = fn.functionName
|
||||||
|
} else {
|
||||||
|
const sp = dbObjects.storedProcedures.find((p) => p.procedureName === cmd)
|
||||||
|
if (sp) {
|
||||||
|
schema = sp.schemaName
|
||||||
|
name = sp.procedureName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!name) {
|
||||||
|
setAllDbColumns([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const colRes = await sqlObjectManagerService.getTableColumns(dsCode, schema, name)
|
||||||
|
setAllDbColumns(colRes.data ?? [])
|
||||||
|
} catch {
|
||||||
|
setAllDbColumns([])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
load()
|
||||||
|
}, [listFormValues?.dataSourceCode, listFormValues?.selectCommand])
|
||||||
|
|
||||||
|
const addAllColumns = async () => {
|
||||||
|
if (!allDbColumns.length || !listFormCode) return
|
||||||
|
const existingNames = new Set((fields ?? []).map((f) => f.fieldName))
|
||||||
|
const newCols = allDbColumns.filter((c) => !existingNames.has(c.columnName))
|
||||||
|
if (newCols.length === 0) {
|
||||||
|
toast.push(
|
||||||
|
<Notification type="info" duration={2000}>
|
||||||
|
Tüm sütunlar zaten eklenmiş.
|
||||||
|
</Notification>,
|
||||||
|
{ placement: 'top-end' },
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setIsAddingAllColumns(true)
|
||||||
|
try {
|
||||||
|
for (const col of newCols) {
|
||||||
|
await postListFormField({
|
||||||
|
listFormCode,
|
||||||
|
fieldName: col.columnName,
|
||||||
|
sourceDbType: sqlDataTypeToDbType(col.dataType) as any,
|
||||||
|
cultureName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
toast.push(
|
||||||
|
<Notification type="success" duration={2000}>
|
||||||
|
{newCols.length} sütun eklendi.
|
||||||
|
</Notification>,
|
||||||
|
{ placement: 'top-end' },
|
||||||
|
)
|
||||||
|
await getFields()
|
||||||
|
} catch (error: any) {
|
||||||
|
toast.push(
|
||||||
|
<Notification type="danger">
|
||||||
|
Hata: <code>{error.toString()}</code>
|
||||||
|
</Notification>,
|
||||||
|
{ placement: 'top-end' },
|
||||||
|
)
|
||||||
|
} finally {
|
||||||
|
setIsAddingAllColumns(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const NewField: Partial<ColumnFormatEditDto> = {
|
const NewField: Partial<ColumnFormatEditDto> = {
|
||||||
fieldName: '',
|
fieldName: '',
|
||||||
cultureName,
|
cultureName,
|
||||||
|
|
@ -324,6 +423,18 @@ function FormFields({
|
||||||
setIsCopyField(true)
|
setIsCopyField(true)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{allDbColumns.length > 0 && (
|
||||||
|
<Button
|
||||||
|
shape="circle"
|
||||||
|
variant="plain"
|
||||||
|
type="button"
|
||||||
|
size="xs"
|
||||||
|
title="Tüm Sütunları Ekle"
|
||||||
|
icon={<FaTable />}
|
||||||
|
loading={isAddingAllColumns}
|
||||||
|
onClick={addAllColumns}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Th>
|
</Th>
|
||||||
<Th>Field</Th>
|
<Th>Field</Th>
|
||||||
|
|
|
||||||
|
|
@ -433,6 +433,30 @@ export const firstDayOfWeekOptions = [
|
||||||
{ value: 6, label: 'Saturday' },
|
{ value: 6, label: 'Saturday' },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const sqlDataTypeToDbType = (sqlType: string): DbTypeEnum => {
|
||||||
|
const t = sqlType
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s*\(.*\)/, '')
|
||||||
|
.trim()
|
||||||
|
if (['int', 'integer', 'int32'].includes(t)) return DbTypeEnum.Int32
|
||||||
|
if (['bigint', 'int64'].includes(t)) return DbTypeEnum.Int64
|
||||||
|
if (['smallint', 'int16'].includes(t)) return DbTypeEnum.Int16
|
||||||
|
if (['tinyint', 'byte'].includes(t)) return DbTypeEnum.Byte
|
||||||
|
if (['bit', 'boolean', 'bool'].includes(t)) return DbTypeEnum.Boolean
|
||||||
|
if (['float', 'real', 'double', 'double precision'].includes(t)) return DbTypeEnum.Double
|
||||||
|
if (['decimal', 'numeric', 'money', 'smallmoney'].includes(t)) return DbTypeEnum.Decimal
|
||||||
|
if (['uniqueidentifier'].includes(t)) return DbTypeEnum.Guid
|
||||||
|
if (['datetime2', 'smalldatetime', 'datetime'].includes(t)) return DbTypeEnum.DateTime
|
||||||
|
if (['date'].includes(t)) return DbTypeEnum.Date
|
||||||
|
if (['time'].includes(t)) return DbTypeEnum.Time
|
||||||
|
if (['datetimeoffset'].includes(t)) return DbTypeEnum.DateTimeOffset
|
||||||
|
if (['nvarchar', 'varchar', 'char', 'nchar', 'text', 'ntext', 'string'].includes(t))
|
||||||
|
return DbTypeEnum.String
|
||||||
|
if (['xml'].includes(t)) return DbTypeEnum.Xml
|
||||||
|
if (['binary', 'varbinary', 'image'].includes(t)) return DbTypeEnum.Binary
|
||||||
|
return DbTypeEnum.String
|
||||||
|
}
|
||||||
|
|
||||||
export const dbSourceTypeOptions = enumToList(DbTypeEnum)
|
export const dbSourceTypeOptions = enumToList(DbTypeEnum)
|
||||||
export const dataSourceTypeOptions = enumToList(DataSourceTypeEnum)
|
export const dataSourceTypeOptions = enumToList(DataSourceTypeEnum)
|
||||||
export const selectCommandTypeOptions = enumToList(SelectCommandTypeEnum)
|
export const selectCommandTypeOptions = enumToList(SelectCommandTypeEnum)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue