Compare commits
No commits in common. "main" and "1.0.3" have entirely different histories.
109 changed files with 2554 additions and 2015 deletions
|
|
@ -44,7 +44,6 @@ COPY "src/Sozsoft.Platform.EntityFrameworkCore/Sozsoft.Platform.EntityFrameworkC
|
||||||
COPY "src/Sozsoft.Platform.HttpApi/Sozsoft.Platform.HttpApi.csproj" "src/Sozsoft.Platform.HttpApi/"
|
COPY "src/Sozsoft.Platform.HttpApi/Sozsoft.Platform.HttpApi.csproj" "src/Sozsoft.Platform.HttpApi/"
|
||||||
COPY "src/Sozsoft.Platform.HttpApi.Client/Sozsoft.Platform.HttpApi.Client.csproj" "src/Sozsoft.Platform.HttpApi.Client/"
|
COPY "src/Sozsoft.Platform.HttpApi.Client/Sozsoft.Platform.HttpApi.Client.csproj" "src/Sozsoft.Platform.HttpApi.Client/"
|
||||||
COPY "src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.Host.csproj" "src/Sozsoft.Platform.HttpApi.Host/"
|
COPY "src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.Host.csproj" "src/Sozsoft.Platform.HttpApi.Host/"
|
||||||
COPY "src/Sozsoft.Platform.DbMigrator/Sozsoft.Platform.DbMigrator.csproj" "src/Sozsoft.Platform.DbMigrator/"
|
|
||||||
COPY "test/Sozsoft.Platform.EntityFrameworkCore.Tests/Sozsoft.Platform.EntityFrameworkCore.Tests.csproj" "test/Sozsoft.Platform.EntityFrameworkCore.Tests/"
|
COPY "test/Sozsoft.Platform.EntityFrameworkCore.Tests/Sozsoft.Platform.EntityFrameworkCore.Tests.csproj" "test/Sozsoft.Platform.EntityFrameworkCore.Tests/"
|
||||||
COPY "test/Sozsoft.Platform.TestBase/Sozsoft.Platform.TestBase.csproj" "test/Sozsoft.Platform.TestBase/"
|
COPY "test/Sozsoft.Platform.TestBase/Sozsoft.Platform.TestBase.csproj" "test/Sozsoft.Platform.TestBase/"
|
||||||
RUN dotnet restore "src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.Host.csproj"
|
RUN dotnet restore "src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.Host.csproj"
|
||||||
|
|
@ -52,7 +51,6 @@ RUN dotnet restore "src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.H
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN mkdir -p publish
|
RUN mkdir -p publish
|
||||||
RUN dotnet publish "src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.Host.csproj" -c Release -o /app/publish --no-restore
|
RUN dotnet publish "src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.Host.csproj" -c Release -o /app/publish --no-restore
|
||||||
RUN dotnet publish "src/Sozsoft.Platform.DbMigrator/Sozsoft.Platform.DbMigrator.csproj" -c Release -o /app/migrator
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
|
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final
|
||||||
|
|
||||||
|
|
@ -99,8 +97,4 @@ EXPOSE 443
|
||||||
|
|
||||||
WORKDIR /srv/app
|
WORKDIR /srv/app
|
||||||
COPY --from=build /app/publish .
|
COPY --from=build /app/publish .
|
||||||
|
|
||||||
# Migrator publish çıktısını Setup modunun çağırabilmesi için kopyala
|
|
||||||
COPY --from=build /app/migrator /srv/Sozsoft.Platform.DbMigrator
|
|
||||||
|
|
||||||
ENTRYPOINT ["./Sozsoft.Platform.HttpApi.Host"]
|
ENTRYPOINT ["./Sozsoft.Platform.HttpApi.Host"]
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
using Sozsoft.Platform.Enums;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.ListForms;
|
namespace Sozsoft.Platform.ListForms;
|
||||||
|
|
@ -13,7 +14,6 @@ public class ColumnFormatDto : AuditedEntityDto<Guid>
|
||||||
|
|
||||||
public string FieldName { get; set; }
|
public string FieldName { get; set; }
|
||||||
public string CaptionName { get; set; }
|
public string CaptionName { get; set; }
|
||||||
public string PlaceHolder { get; set; }
|
|
||||||
public bool ReadOnly { get; set; }
|
public bool ReadOnly { get; set; }
|
||||||
public bool Visible { get; set; } // select sorgusuna dahildir fakat ekranda gosterilmez, kolon secicinin icerisinde bulunur
|
public bool Visible { get; set; } // select sorgusuna dahildir fakat ekranda gosterilmez, kolon secicinin icerisinde bulunur
|
||||||
public bool IsActive { get; set; } // sadece isActive olan alanlar sorguya dahil edilir
|
public bool IsActive { get; set; } // sadece isActive olan alanlar sorguya dahil edilir
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ public class GridEditingDto
|
||||||
public bool AllowDeleting { get; set; } = false;
|
public bool AllowDeleting { get; set; } = false;
|
||||||
public bool AllowAllDeleting { get; set; } = false;
|
public bool AllowAllDeleting { get; set; } = false;
|
||||||
public bool AllowAdding { get; set; } = false;
|
public bool AllowAdding { get; set; } = false;
|
||||||
public bool AllowDuplicate { get; set; } = false;
|
|
||||||
public bool UseIcons { get; set; } = false;
|
public bool UseIcons { get; set; } = false;
|
||||||
public bool ConfirmDelete { get; set; } = true;
|
public bool ConfirmDelete { get; set; } = true;
|
||||||
/// <summary>Accepted Values: 'first' | 'last' | 'pageBottom' | 'pageTop' | 'viewportBottom' | 'viewportTop'
|
/// <summary>Accepted Values: 'first' | 'last' | 'pageBottom' | 'pageTop' | 'viewportBottom' | 'viewportTop'
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ public interface IAuditLogAppService
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(AppCodes.IdentityManagement.AuditLogs)]
|
[Authorize(AppCodes.AuditLogs)]
|
||||||
public class AuditLogAppService
|
public class AuditLogAppService
|
||||||
: CrudAppService<AuditLog, AuditLogDto, Guid>
|
: CrudAppService<AuditLog, AuditLogDto, Guid>
|
||||||
, IAuditLogAppService
|
, IAuditLogAppService
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ using Volo.Abp.Domain.Repositories;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.GlobalSearchs;
|
namespace Sozsoft.Platform.GlobalSearchs;
|
||||||
|
|
||||||
[Authorize(PlatformConsts.AppCodes.Definitions.GlobalSearch)]
|
[Authorize(PlatformConsts.AppCodes.Settings.GlobalSearch)]
|
||||||
public class GlobalSearchAppService : PlatformAppService
|
public class GlobalSearchAppService : PlatformAppService
|
||||||
{
|
{
|
||||||
private readonly IRepository<GlobalSearch, int> repo;
|
private readonly IRepository<GlobalSearch, int> repo;
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@ public class ListFormFieldsAppService : CrudAppService<
|
||||||
entity.FieldName = updateInput.FieldName;
|
entity.FieldName = updateInput.FieldName;
|
||||||
entity.CultureName = updateInput.CultureName;
|
entity.CultureName = updateInput.CultureName;
|
||||||
entity.CaptionName = updateInput.CaptionName;
|
entity.CaptionName = updateInput.CaptionName;
|
||||||
entity.PlaceHolder = updateInput.PlaceHolder;
|
|
||||||
entity.BandName = updateInput.BandName;
|
entity.BandName = updateInput.BandName;
|
||||||
entity.IsActive = updateInput.IsActive;
|
entity.IsActive = updateInput.IsActive;
|
||||||
entity.Visible = updateInput.Visible;
|
entity.Visible = updateInput.Visible;
|
||||||
|
|
@ -129,7 +128,6 @@ public class ListFormFieldsAppService : CrudAppService<
|
||||||
{
|
{
|
||||||
item.FieldName = input.FieldName;
|
item.FieldName = input.FieldName;
|
||||||
item.CaptionName = input.CaptionName;
|
item.CaptionName = input.CaptionName;
|
||||||
item.PlaceHolder = input.PlaceHolder;
|
|
||||||
item.BandName = input.BandName;
|
item.BandName = input.BandName;
|
||||||
item.SourceDbType = input.SourceDbType;
|
item.SourceDbType = input.SourceDbType;
|
||||||
item.Alignment = input.Alignment;
|
item.Alignment = input.Alignment;
|
||||||
|
|
@ -253,7 +251,6 @@ public class ListFormFieldsAppService : CrudAppService<
|
||||||
{
|
{
|
||||||
field.BandName = sourceField.BandName;
|
field.BandName = sourceField.BandName;
|
||||||
field.CaptionName = sourceField.CaptionName;
|
field.CaptionName = sourceField.CaptionName;
|
||||||
field.PlaceHolder = sourceField.PlaceHolder;
|
|
||||||
field.SourceDbType = sourceField.SourceDbType;
|
field.SourceDbType = sourceField.SourceDbType;
|
||||||
}
|
}
|
||||||
if (input.CopiedFields.All || input.CopiedFields.Options)
|
if (input.CopiedFields.All || input.CopiedFields.Options)
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,6 @@ public class ListFormQueryPreviewAppService : PlatformAppService
|
||||||
{
|
{
|
||||||
var authType = op switch
|
var authType = op switch
|
||||||
{
|
{
|
||||||
OperationEnum.Duplicate => AuthorizationTypeEnum.Create,
|
|
||||||
OperationEnum.Insert => AuthorizationTypeEnum.Create,
|
OperationEnum.Insert => AuthorizationTypeEnum.Create,
|
||||||
OperationEnum.Update => AuthorizationTypeEnum.Update,
|
OperationEnum.Update => AuthorizationTypeEnum.Update,
|
||||||
OperationEnum.Delete => AuthorizationTypeEnum.Delete,
|
OperationEnum.Delete => AuthorizationTypeEnum.Delete,
|
||||||
|
|
@ -105,7 +104,7 @@ public class ListFormQueryPreviewAppService : PlatformAppService
|
||||||
|
|
||||||
var (_, _, dataSourceType) = await dynamicDataManager.GetAsync(listForm.IsTenant, listForm.DataSourceCode);
|
var (_, _, dataSourceType) = await dynamicDataManager.GetAsync(listForm.IsTenant, listForm.DataSourceCode);
|
||||||
|
|
||||||
return qManager.GenerateQuery(listForm, listFormFields, parameters, op, dataSourceType);
|
return qManager.GenerateQuery(listForm, parameters, op, dataSourceType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ using Sozsoft.Platform.Localization;
|
||||||
using Sozsoft.Platform.Queries;
|
using Sozsoft.Platform.Queries;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using static Sozsoft.Platform.PlatformConsts;
|
using static Sozsoft.Platform.PlatformConsts;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace Sozsoft.Platform.ListForms.Select;
|
namespace Sozsoft.Platform.ListForms.Select;
|
||||||
|
|
||||||
|
|
@ -16,18 +14,15 @@ public class ListFormDataAppService : PlatformAppService
|
||||||
private readonly IListFormAuthorizationManager authManager;
|
private readonly IListFormAuthorizationManager authManager;
|
||||||
private readonly IQueryManager qManager;
|
private readonly IQueryManager qManager;
|
||||||
private readonly IHttpContextAccessor httpContextAccessor;
|
private readonly IHttpContextAccessor httpContextAccessor;
|
||||||
private readonly IListFormSelectAppService listFormSelectAppService;
|
|
||||||
|
|
||||||
public ListFormDataAppService(
|
public ListFormDataAppService(
|
||||||
IListFormAuthorizationManager authManager,
|
IListFormAuthorizationManager authManager,
|
||||||
IQueryManager qManager,
|
IQueryManager qManager,
|
||||||
IHttpContextAccessor httpContextAccessor,
|
IHttpContextAccessor httpContextAccessor)
|
||||||
IListFormSelectAppService listFormSelectAppService)
|
|
||||||
{
|
{
|
||||||
this.authManager = authManager;
|
this.authManager = authManager;
|
||||||
this.qManager = qManager;
|
this.qManager = qManager;
|
||||||
this.httpContextAccessor = httpContextAccessor;
|
this.httpContextAccessor = httpContextAccessor;
|
||||||
this.listFormSelectAppService = listFormSelectAppService;
|
|
||||||
LocalizationResource = typeof(PlatformResource);
|
LocalizationResource = typeof(PlatformResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,39 +38,6 @@ public class ListFormDataAppService : PlatformAppService
|
||||||
var queryParameters = httpContext.Request.Query.ToDictionary(x => x.Key, x => x.Value);
|
var queryParameters = httpContext.Request.Query.ToDictionary(x => x.Key, x => x.Value);
|
||||||
return await qManager.GenerateAndRunQueryAsync<dynamic>(input.ListFormCode, OperationEnum.Insert, input.Data, queryParameters: queryParameters);
|
return await qManager.GenerateAndRunQueryAsync<dynamic>(input.ListFormCode, OperationEnum.Insert, input.Data, queryParameters: queryParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<dynamic> PostDuplicateAsync(DataRequestDto input)
|
|
||||||
{
|
|
||||||
// Izin logic process
|
|
||||||
if (!await authManager.CanAccess(input.ListFormCode, AuthorizationTypeEnum.Create))
|
|
||||||
throw new Volo.Abp.UserFriendlyException(L[AppErrorCodes.NoAuth]);
|
|
||||||
|
|
||||||
var httpContext = httpContextAccessor.HttpContext
|
|
||||||
?? throw new InvalidOperationException("HTTP Context bulunamadı.");
|
|
||||||
|
|
||||||
var queryParameters = httpContext.Request.Query.ToDictionary(x => x.Key, x => x.Value);
|
|
||||||
|
|
||||||
object filter = new object[] { input.Data[0], "=", input.Keys[0] };
|
|
||||||
|
|
||||||
var selectRequest = new SelectRequestDto
|
|
||||||
{
|
|
||||||
ListFormCode = input.ListFormCode,
|
|
||||||
Filter = filter.ToString(),
|
|
||||||
Skip = 0,
|
|
||||||
Take = 1,
|
|
||||||
RequireTotalCount = false,
|
|
||||||
RequireGroupCount = false,
|
|
||||||
};
|
|
||||||
var selectResult = await listFormSelectAppService.GetSelectAsync(selectRequest);
|
|
||||||
var record = ((selectResult?.Data as System.Collections.IEnumerable)?.Cast<object>()?.FirstOrDefault()) ?? throw new Volo.Abp.UserFriendlyException("Kopyalanacak kayıt bulunamadı.");
|
|
||||||
|
|
||||||
if (record is not IDictionary<string, object> dict)
|
|
||||||
throw new Exception("DapperRow IDictionary'e çevrilemedi.");
|
|
||||||
|
|
||||||
input.Data = JsonSerializer.Serialize(dict);
|
|
||||||
|
|
||||||
return await qManager.GenerateAndRunQueryAsync<dynamic>(input.ListFormCode, OperationEnum.Duplicate, input.Data, null, queryParameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> PostUpdateAsync(DataRequestDto input)
|
public async Task<int> PostUpdateAsync(DataRequestDto input)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,7 @@
|
||||||
{
|
{
|
||||||
"code": "App.Sender.Sms.PostaGuvercini.Url",
|
"code": "App.Sender.Sms.PostaGuvercini.Url",
|
||||||
"nameKey": "App.Sender.Sms.PostaGuvercini.Url",
|
"nameKey": "App.Sender.Sms.PostaGuvercini.Url",
|
||||||
"descriptionKey": "App.Sender.Url.Description",
|
"descriptionKey": "App.Sender.Sms.PostaGuvercini.Url.Description",
|
||||||
"defaultValue": "https://www.postaguvercini.com/api_http",
|
"defaultValue": "https://www.postaguvercini.com/api_http",
|
||||||
"isVisibleToClients": false,
|
"isVisibleToClients": false,
|
||||||
"providers": "T|G|D",
|
"providers": "T|G|D",
|
||||||
|
|
@ -337,7 +337,7 @@
|
||||||
{
|
{
|
||||||
"code": "App.Sender.WhatsApp.Url",
|
"code": "App.Sender.WhatsApp.Url",
|
||||||
"nameKey": "App.Sender.WhatsApp.Url",
|
"nameKey": "App.Sender.WhatsApp.Url",
|
||||||
"descriptionKey": "App.Sender.Url.Description",
|
"descriptionKey": "App.Sender.WhatsApp.Url.Description",
|
||||||
"defaultValue": "https://graph.facebook.com/v21.0",
|
"defaultValue": "https://graph.facebook.com/v21.0",
|
||||||
"isVisibleToClients": false,
|
"isVisibleToClients": false,
|
||||||
"providers": "T|G|D",
|
"providers": "T|G|D",
|
||||||
|
|
@ -401,7 +401,7 @@
|
||||||
{
|
{
|
||||||
"code": "App.Sender.Rocket.Url",
|
"code": "App.Sender.Rocket.Url",
|
||||||
"nameKey": "App.Sender.Rocket.Url",
|
"nameKey": "App.Sender.Rocket.Url",
|
||||||
"descriptionKey": "App.Sender.Url.Description",
|
"descriptionKey": "App.Sender.Rocket.Url.Description",
|
||||||
"defaultValue": "https://chat.sozsoft.com/api/v1",
|
"defaultValue": "https://chat.sozsoft.com/api/v1",
|
||||||
"isVisibleToClients": false,
|
"isVisibleToClients": false,
|
||||||
"providers": "G|D",
|
"providers": "G|D",
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -903,7 +903,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "Email",
|
FieldName = "Email",
|
||||||
CaptionName = "Abp.Account.EmailAddress",
|
CaptionName = "App.Listform.ListformField.Email",
|
||||||
Width = 300,
|
Width = 300,
|
||||||
ListOrderNo = 2,
|
ListOrderNo = 2,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
|
|
@ -963,7 +963,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "PhoneNumber",
|
FieldName = "PhoneNumber",
|
||||||
CaptionName = "Abp.Identity.User.UserInformation.PhoneNumber",
|
CaptionName = "App.Listform.ListformField.PhoneNumber",
|
||||||
Width = 150,
|
Width = 150,
|
||||||
ListOrderNo = 5,
|
ListOrderNo = 5,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
|
|
@ -1016,8 +1016,152 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Ip Restriction
|
||||||
|
listFormName = AppCodes.IdentityManagement.IpRestrictions;
|
||||||
|
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
||||||
|
{
|
||||||
|
var listForm = await _listFormRepository.InsertAsync(
|
||||||
|
new ListForm()
|
||||||
|
{
|
||||||
|
ListFormType = ListFormTypeEnum.List,
|
||||||
|
PageSize = 10,
|
||||||
|
ExportJson = DefaultExportJson,
|
||||||
|
IsSubForm = false,
|
||||||
|
ShowNote = true,
|
||||||
|
LayoutJson = DefaultLayoutJson(),
|
||||||
|
CultureName = LanguageCodes.En,
|
||||||
|
ListFormCode = listFormName,
|
||||||
|
Name = listFormName,
|
||||||
|
Title = listFormName,
|
||||||
|
DataSourceCode = SeedConsts.DataSources.DefaultCode,
|
||||||
|
IsTenant = true,
|
||||||
|
IsBranch = false,
|
||||||
|
IsOrganizationUnit = false,
|
||||||
|
Description = listFormName,
|
||||||
|
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||||
|
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.IpRestriction)),
|
||||||
|
KeyFieldName = "Id",
|
||||||
|
KeyFieldDbSourceType = DbType.Guid,
|
||||||
|
DefaultFilter = DefaultFilterJson,
|
||||||
|
SortMode = GridOptions.SortModeSingle,
|
||||||
|
FilterRowJson = DefaultFilterRowJson,
|
||||||
|
HeaderFilterJson = DefaultHeaderFilterJson,
|
||||||
|
SearchPanelJson = DefaultSearchPanelJson,
|
||||||
|
GroupPanelJson = DefaultGroupPanelJson,
|
||||||
|
SelectionJson = DefaultSelectionSingleJson,
|
||||||
|
ColumnOptionJson = DefaultColumnOptionJson(),
|
||||||
|
PermissionJson = DefaultPermissionJson(listFormName),
|
||||||
|
DeleteCommand = $"UPDATE \"{FullNameTable(TableNameEnum.IpRestriction)}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id",
|
||||||
|
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
||||||
|
PagerOptionJson = DefaultPagerOptionJson,
|
||||||
|
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 350, true, true, true, true, false),
|
||||||
|
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() {
|
||||||
|
new() {
|
||||||
|
Order=1, ColCount=1, ColSpan=1, ItemType="group", Items=
|
||||||
|
[
|
||||||
|
new EditingFormItemDto { Order = 1, DataField = "ResourceType", ColSpan = 1, IsRequired = true, EditorType2=EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
||||||
|
new EditingFormItemDto { Order = 2, DataField = "ResourceId", ColSpan = 1, EditorType2=EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
||||||
|
new EditingFormItemDto { Order = 3, DataField = "IP", ColSpan = 1, IsRequired = true, EditorType2=EditorTypes.dxTextBox },
|
||||||
|
]}
|
||||||
|
}),
|
||||||
|
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#region Ip Restriction Fields
|
||||||
|
await _listFormFieldRepository.InsertManyAsync([
|
||||||
|
new() {
|
||||||
|
ListFormCode = listForm.ListFormCode,
|
||||||
|
CultureName = LanguageCodes.En,
|
||||||
|
SourceDbType = DbType.Guid,
|
||||||
|
FieldName = "Id",
|
||||||
|
CaptionName = "App.Listform.ListformField.Id",
|
||||||
|
Width = 100,
|
||||||
|
ListOrderNo = 1,
|
||||||
|
Visible = false,
|
||||||
|
IsActive = true,
|
||||||
|
IsDeleted = false,
|
||||||
|
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||||
|
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||||
|
PivotSettingsJson = DefaultPivotSettingsJson
|
||||||
|
},
|
||||||
|
new() {
|
||||||
|
ListFormCode = listForm.ListFormCode,
|
||||||
|
CultureName = LanguageCodes.En,
|
||||||
|
SourceDbType = DbType.String,
|
||||||
|
FieldName = "ResourceType",
|
||||||
|
CaptionName = "App.Listform.ListformField.ResourceType",
|
||||||
|
Width = 400,
|
||||||
|
ListOrderNo = 2,
|
||||||
|
Visible = true,
|
||||||
|
IsActive = true,
|
||||||
|
IsDeleted = false,
|
||||||
|
SortIndex = 1,
|
||||||
|
SortDirection = GridColumnOptions.SortOrderAsc,
|
||||||
|
AllowSearch = true,
|
||||||
|
LookupJson = JsonSerializer.Serialize(new LookupDto
|
||||||
|
{
|
||||||
|
DataSourceType = UiLookupDataSourceTypeEnum.StaticData,
|
||||||
|
DisplayExpr = "name",
|
||||||
|
ValueExpr = "key",
|
||||||
|
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] {
|
||||||
|
new () { Key="User", Name="User" },
|
||||||
|
new () { Key="Role", Name="Role" },
|
||||||
|
new () { Key="Global", Name="Global" },
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
||||||
|
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||||
|
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||||
|
PivotSettingsJson = DefaultPivotSettingsJson
|
||||||
|
},
|
||||||
|
new() {
|
||||||
|
ListFormCode = listForm.ListFormCode,
|
||||||
|
CultureName = LanguageCodes.En,
|
||||||
|
SourceDbType = DbType.String,
|
||||||
|
FieldName = "ResourceId",
|
||||||
|
CaptionName = "App.Listform.ListformField.ResourceId",
|
||||||
|
Width = 400,
|
||||||
|
ListOrderNo = 3,
|
||||||
|
Visible = true,
|
||||||
|
IsActive = true,
|
||||||
|
IsDeleted = false,
|
||||||
|
AllowSearch = true,
|
||||||
|
LookupJson = JsonSerializer.Serialize(new LookupDto {
|
||||||
|
DataSourceType = UiLookupDataSourceTypeEnum.Query,
|
||||||
|
DisplayExpr = "Name",
|
||||||
|
ValueExpr = "Key",
|
||||||
|
LookupQuery = $"SELECT \"UserName\" AS \"Key\", \"UserName\" AS \"Name\" FROM \"AbpUsers\" UNION SELECT \"Name\" AS \"Key\", \"Name\" AS \"Name\" FROM \"AbpRoles\"",
|
||||||
|
}),
|
||||||
|
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||||
|
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||||
|
PivotSettingsJson = DefaultPivotSettingsJson
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
ListFormCode = listForm.ListFormCode,
|
||||||
|
CultureName = LanguageCodes.En,
|
||||||
|
SourceDbType = DbType.String,
|
||||||
|
FieldName = "IP",
|
||||||
|
CaptionName = "App.Listform.ListformField.IP",
|
||||||
|
Width = 100,
|
||||||
|
ListOrderNo = 4,
|
||||||
|
Visible = true,
|
||||||
|
IsActive = true,
|
||||||
|
IsDeleted = false,
|
||||||
|
AllowSearch = true,
|
||||||
|
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
||||||
|
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
||||||
|
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
||||||
|
PivotSettingsJson = DefaultPivotSettingsJson
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Audit Logs
|
#region Audit Logs
|
||||||
listFormName = AppCodes.IdentityManagement.AuditLogs;
|
listFormName = AppCodes.AuditLogs;
|
||||||
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
||||||
{
|
{
|
||||||
var listForm = await _listFormRepository.InsertAsync(
|
var listForm = await _listFormRepository.InsertAsync(
|
||||||
|
|
@ -1565,7 +1709,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Sector)),
|
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Sector)),
|
||||||
KeyFieldName = "Id",
|
KeyFieldName = "Id",
|
||||||
KeyFieldDbSourceType = DbType.Guid,
|
KeyFieldDbSourceType = DbType.String,
|
||||||
DefaultFilter = DefaultFilterJson,
|
DefaultFilter = DefaultFilterJson,
|
||||||
SortMode = GridOptions.SortModeSingle,
|
SortMode = GridOptions.SortModeSingle,
|
||||||
FilterRowJson = DefaultFilterRowJson,
|
FilterRowJson = DefaultFilterRowJson,
|
||||||
|
|
@ -1576,9 +1720,9 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
ColumnOptionJson = DefaultColumnOptionJson(),
|
ColumnOptionJson = DefaultColumnOptionJson(),
|
||||||
PermissionJson = DefaultPermissionJson(listFormName),
|
PermissionJson = DefaultPermissionJson(listFormName),
|
||||||
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.Sector)),
|
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.Sector)),
|
||||||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(DbType.String),
|
||||||
PagerOptionJson = DefaultPagerOptionJson,
|
PagerOptionJson = DefaultPagerOptionJson,
|
||||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(DbType.String, "Name"),
|
||||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 400, 200, true, true, true, true, false),
|
EditingOptionJson = DefaultEditingOptionJson(listFormName, 400, 200, true, true, true, true, false),
|
||||||
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
|
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>
|
||||||
{
|
{
|
||||||
|
|
@ -1635,7 +1779,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region WorkHour
|
#region WorkHour
|
||||||
listFormName = AppCodes.Restrictions.WorkHour;
|
listFormName = AppCodes.Definitions.WorkHour;
|
||||||
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
||||||
{
|
{
|
||||||
var listForm = await _listFormRepository.InsertAsync(
|
var listForm = await _listFormRepository.InsertAsync(
|
||||||
|
|
@ -1659,7 +1803,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
SelectCommandType = SelectCommandTypeEnum.Table,
|
||||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.WorkHour)),
|
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.WorkHour)),
|
||||||
KeyFieldName = "Id",
|
KeyFieldName = "Id",
|
||||||
KeyFieldDbSourceType = DbType.Guid,
|
KeyFieldDbSourceType = DbType.String,
|
||||||
DefaultFilter = DefaultFilterJson,
|
DefaultFilter = DefaultFilterJson,
|
||||||
SortMode = GridOptions.SortModeSingle,
|
SortMode = GridOptions.SortModeSingle,
|
||||||
FilterRowJson = DefaultFilterRowJson,
|
FilterRowJson = DefaultFilterRowJson,
|
||||||
|
|
@ -1670,8 +1814,8 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
ColumnOptionJson = DefaultColumnOptionJson(),
|
ColumnOptionJson = DefaultColumnOptionJson(),
|
||||||
PermissionJson = DefaultPermissionJson(listFormName),
|
PermissionJson = DefaultPermissionJson(listFormName),
|
||||||
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.WorkHour)),
|
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.WorkHour)),
|
||||||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(DbType.String),
|
||||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(DbType.String, "Name"),
|
||||||
PagerOptionJson = DefaultPagerOptionJson,
|
PagerOptionJson = DefaultPagerOptionJson,
|
||||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 600, 600, true, true, true, true, false),
|
EditingOptionJson = DefaultEditingOptionJson(listFormName, 600, 600, true, true, true, true, false),
|
||||||
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() {
|
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() {
|
||||||
|
|
@ -1890,150 +2034,6 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Ip Restriction
|
|
||||||
listFormName = AppCodes.Restrictions.IpRestrictions;
|
|
||||||
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
|
||||||
{
|
|
||||||
var listForm = await _listFormRepository.InsertAsync(
|
|
||||||
new ListForm()
|
|
||||||
{
|
|
||||||
ListFormType = ListFormTypeEnum.List,
|
|
||||||
PageSize = 10,
|
|
||||||
ExportJson = DefaultExportJson,
|
|
||||||
IsSubForm = false,
|
|
||||||
ShowNote = true,
|
|
||||||
LayoutJson = DefaultLayoutJson(),
|
|
||||||
CultureName = LanguageCodes.En,
|
|
||||||
ListFormCode = listFormName,
|
|
||||||
Name = listFormName,
|
|
||||||
Title = listFormName,
|
|
||||||
DataSourceCode = SeedConsts.DataSources.DefaultCode,
|
|
||||||
IsTenant = true,
|
|
||||||
IsBranch = false,
|
|
||||||
IsOrganizationUnit = false,
|
|
||||||
Description = listFormName,
|
|
||||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
|
||||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.IpRestriction)),
|
|
||||||
KeyFieldName = "Id",
|
|
||||||
KeyFieldDbSourceType = DbType.Guid,
|
|
||||||
DefaultFilter = DefaultFilterJson,
|
|
||||||
SortMode = GridOptions.SortModeSingle,
|
|
||||||
FilterRowJson = DefaultFilterRowJson,
|
|
||||||
HeaderFilterJson = DefaultHeaderFilterJson,
|
|
||||||
SearchPanelJson = DefaultSearchPanelJson,
|
|
||||||
GroupPanelJson = DefaultGroupPanelJson,
|
|
||||||
SelectionJson = DefaultSelectionSingleJson,
|
|
||||||
ColumnOptionJson = DefaultColumnOptionJson(),
|
|
||||||
PermissionJson = DefaultPermissionJson(listFormName),
|
|
||||||
DeleteCommand = $"UPDATE \"{FullNameTable(TableNameEnum.IpRestriction)}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id",
|
|
||||||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
|
||||||
PagerOptionJson = DefaultPagerOptionJson,
|
|
||||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 350, true, true, true, true, false),
|
|
||||||
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>() {
|
|
||||||
new() {
|
|
||||||
Order=1, ColCount=1, ColSpan=1, ItemType="group", Items=
|
|
||||||
[
|
|
||||||
new EditingFormItemDto { Order = 1, DataField = "ResourceType", ColSpan = 1, IsRequired = true, EditorType2=EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
|
||||||
new EditingFormItemDto { Order = 2, DataField = "ResourceId", ColSpan = 1, EditorType2=EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
|
||||||
new EditingFormItemDto { Order = 3, DataField = "IP", ColSpan = 1, IsRequired = true, EditorType2=EditorTypes.dxTextBox },
|
|
||||||
]}
|
|
||||||
}),
|
|
||||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#region Ip Restriction Fields
|
|
||||||
await _listFormFieldRepository.InsertManyAsync([
|
|
||||||
new() {
|
|
||||||
ListFormCode = listForm.ListFormCode,
|
|
||||||
CultureName = LanguageCodes.En,
|
|
||||||
SourceDbType = DbType.Guid,
|
|
||||||
FieldName = "Id",
|
|
||||||
CaptionName = "App.Listform.ListformField.Id",
|
|
||||||
Width = 100,
|
|
||||||
ListOrderNo = 1,
|
|
||||||
Visible = false,
|
|
||||||
IsActive = true,
|
|
||||||
IsDeleted = false,
|
|
||||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
|
||||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
|
||||||
PivotSettingsJson = DefaultPivotSettingsJson
|
|
||||||
},
|
|
||||||
new() {
|
|
||||||
ListFormCode = listForm.ListFormCode,
|
|
||||||
CultureName = LanguageCodes.En,
|
|
||||||
SourceDbType = DbType.String,
|
|
||||||
FieldName = "ResourceType",
|
|
||||||
CaptionName = "App.Listform.ListformField.ResourceType",
|
|
||||||
Width = 400,
|
|
||||||
ListOrderNo = 2,
|
|
||||||
Visible = true,
|
|
||||||
IsActive = true,
|
|
||||||
IsDeleted = false,
|
|
||||||
SortIndex = 1,
|
|
||||||
SortDirection = GridColumnOptions.SortOrderAsc,
|
|
||||||
AllowSearch = true,
|
|
||||||
LookupJson = JsonSerializer.Serialize(new LookupDto
|
|
||||||
{
|
|
||||||
DataSourceType = UiLookupDataSourceTypeEnum.StaticData,
|
|
||||||
DisplayExpr = "name",
|
|
||||||
ValueExpr = "key",
|
|
||||||
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] {
|
|
||||||
new () { Key="User", Name="User" },
|
|
||||||
new () { Key="Role", Name="Role" },
|
|
||||||
new () { Key="Global", Name="Global" },
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
|
||||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
|
||||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
|
||||||
PivotSettingsJson = DefaultPivotSettingsJson
|
|
||||||
},
|
|
||||||
new() {
|
|
||||||
ListFormCode = listForm.ListFormCode,
|
|
||||||
CultureName = LanguageCodes.En,
|
|
||||||
SourceDbType = DbType.String,
|
|
||||||
FieldName = "ResourceId",
|
|
||||||
CaptionName = "App.Listform.ListformField.ResourceId",
|
|
||||||
Width = 400,
|
|
||||||
ListOrderNo = 3,
|
|
||||||
Visible = true,
|
|
||||||
IsActive = true,
|
|
||||||
IsDeleted = false,
|
|
||||||
AllowSearch = true,
|
|
||||||
LookupJson = JsonSerializer.Serialize(new LookupDto {
|
|
||||||
DataSourceType = UiLookupDataSourceTypeEnum.Query,
|
|
||||||
DisplayExpr = "Name",
|
|
||||||
ValueExpr = "Key",
|
|
||||||
LookupQuery = $"SELECT \"UserName\" AS \"Key\", \"UserName\" AS \"Name\" FROM \"AbpUsers\" UNION SELECT \"Name\" AS \"Key\", \"Name\" AS \"Name\" FROM \"AbpRoles\"",
|
|
||||||
}),
|
|
||||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
|
||||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
|
||||||
PivotSettingsJson = DefaultPivotSettingsJson
|
|
||||||
},
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
ListFormCode = listForm.ListFormCode,
|
|
||||||
CultureName = LanguageCodes.En,
|
|
||||||
SourceDbType = DbType.String,
|
|
||||||
FieldName = "IP",
|
|
||||||
CaptionName = "App.Listform.ListformField.IP",
|
|
||||||
Width = 100,
|
|
||||||
ListOrderNo = 4,
|
|
||||||
Visible = true,
|
|
||||||
IsActive = true,
|
|
||||||
IsDeleted = false,
|
|
||||||
AllowSearch = true,
|
|
||||||
ValidationRuleJson = DefaultValidationRuleRequiredJson,
|
|
||||||
ColumnCustomizationJson = DefaultColumnCustomizationJson,
|
|
||||||
PermissionJson = DefaultFieldPermissionJson(listForm.Name),
|
|
||||||
PivotSettingsJson = DefaultPivotSettingsJson
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -426,7 +426,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "Email",
|
FieldName = "Email",
|
||||||
CaptionName = "Abp.Account.EmailAddress",
|
CaptionName = "App.Listform.ListformField.Email",
|
||||||
Width = 170,
|
Width = 170,
|
||||||
ListOrderNo = 14,
|
ListOrderNo = 14,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
|
|
@ -479,7 +479,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "PhoneNumber",
|
FieldName = "PhoneNumber",
|
||||||
CaptionName = "Abp.Identity.User.UserInformation.PhoneNumber",
|
CaptionName = "App.Listform.ListformField.PhoneNumber",
|
||||||
Width = 100,
|
Width = 100,
|
||||||
ListOrderNo = 17,
|
ListOrderNo = 17,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
|
|
@ -684,7 +684,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "Code",
|
FieldName = "Code",
|
||||||
CaptionName = "App.Platform.Code",
|
CaptionName = "App.Listform.ListformField.Code",
|
||||||
Width = 100,
|
Width = 100,
|
||||||
ListOrderNo = 2,
|
ListOrderNo = 2,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
|
|
@ -911,7 +911,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "Email",
|
FieldName = "Email",
|
||||||
CaptionName = "Abp.Account.EmailAddress",
|
CaptionName = "App.Listform.ListformField.Email",
|
||||||
Width = 170,
|
Width = 170,
|
||||||
ListOrderNo = 13,
|
ListOrderNo = 13,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
|
|
@ -964,7 +964,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "PhoneNumber",
|
FieldName = "PhoneNumber",
|
||||||
CaptionName = "Abp.Identity.User.UserInformation.PhoneNumber",
|
CaptionName = "App.Listform.ListformField.PhoneNumber",
|
||||||
Width = 100,
|
Width = 100,
|
||||||
ListOrderNo = 16,
|
ListOrderNo = 16,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
|
|
@ -1016,7 +1016,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Global Search
|
#region Global Search
|
||||||
listFormName = AppCodes.Definitions.GlobalSearch;
|
listFormName = AppCodes.Settings.GlobalSearch;
|
||||||
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
||||||
{
|
{
|
||||||
var listForm = await _listFormRepository.InsertAsync(
|
var listForm = await _listFormRepository.InsertAsync(
|
||||||
|
|
@ -1182,7 +1182,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region AiBot
|
#region AiBot
|
||||||
listFormName = AppCodes.Definitions.AiBot;
|
listFormName = AppCodes.AiBot;
|
||||||
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
||||||
{
|
{
|
||||||
var listForm = await _listFormRepository.InsertAsync(
|
var listForm = await _listFormRepository.InsertAsync(
|
||||||
|
|
@ -1218,10 +1218,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
PagerOptionJson = DefaultPagerOptionJson,
|
PagerOptionJson = DefaultPagerOptionJson,
|
||||||
DeleteCommand = $"DELETE FROM \"{TableNameResolver.GetFullTableName(nameof(TableNameEnum.AiBot))}\" WHERE \"Id\"=@Id",
|
DeleteCommand = $"DELETE FROM \"{TableNameResolver.GetFullTableName(nameof(TableNameEnum.AiBot))}\" WHERE \"Id\"=@Id",
|
||||||
DeleteFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
DeleteFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
||||||
new() { FieldName = "Id", FieldDbType = DbType.Guid, Value = "@ID", CustomValueType = FieldCustomValueTypeEnum.CustomKey }
|
new() { FieldName = "Id", FieldDbType = DbType.Int32, Value = "@ID", CustomValueType = FieldCustomValueTypeEnum.CustomKey }
|
||||||
}),
|
|
||||||
InsertFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[] {
|
|
||||||
new() { FieldName = "Id", FieldDbType = DbType.Guid, Value = "@NEWID", CustomValueType = FieldCustomValueTypeEnum.CustomKey }
|
|
||||||
}),
|
}),
|
||||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 450, true, true, true, true, false),
|
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 450, true, true, true, true, false),
|
||||||
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
|
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
|
||||||
|
|
@ -2857,7 +2854,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region SettingDefinition
|
#region SettingDefinition
|
||||||
listFormName = AppCodes.SettingDefinitions;
|
listFormName = AppCodes.Settings.SettingDefinitions;
|
||||||
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
||||||
{
|
{
|
||||||
var listForm = await _listFormRepository.InsertAsync(
|
var listForm = await _listFormRepository.InsertAsync(
|
||||||
|
|
@ -2946,7 +2943,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "Code",
|
FieldName = "Code",
|
||||||
CaptionName = "App.Platform.Code",
|
CaptionName = "App.Listform.ListformField.Code",
|
||||||
Width = 400,
|
Width = 400,
|
||||||
ListOrderNo = 4,
|
ListOrderNo = 4,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
|
|
@ -3584,7 +3581,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Data Source
|
#region Data Source
|
||||||
listFormName = AppCodes.DataSource;
|
listFormName = AppCodes.Listforms.DataSource;
|
||||||
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
||||||
{
|
{
|
||||||
var listForm = await _listFormRepository.InsertAsync(
|
var listForm = await _listFormRepository.InsertAsync(
|
||||||
|
|
@ -3658,7 +3655,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "Code",
|
FieldName = "Code",
|
||||||
CaptionName = "App.Platform.Code",
|
CaptionName = "App.Listform.ListformField.Code",
|
||||||
Width = 300,
|
Width = 300,
|
||||||
ListOrderNo = 2,
|
ListOrderNo = 2,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
|
|
@ -4890,7 +4887,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Route
|
#region Route
|
||||||
listFormName = AppCodes.Menus.Routes;
|
listFormName = AppCodes.Routes;
|
||||||
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == listFormName))
|
||||||
{
|
{
|
||||||
var listForm = await _listFormRepository.InsertAsync(
|
var listForm = await _listFormRepository.InsertAsync(
|
||||||
|
|
@ -5251,7 +5248,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "Code",
|
FieldName = "Code",
|
||||||
CaptionName = "App.Platform.Code",
|
CaptionName = "App.Listform.ListformField.Code",
|
||||||
Width = 300,
|
Width = 300,
|
||||||
ListOrderNo = 2,
|
ListOrderNo = 2,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
|
|
@ -6508,7 +6505,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "PhoneNumber",
|
FieldName = "PhoneNumber",
|
||||||
CaptionName = "Abp.Identity.User.UserInformation.PhoneNumber",
|
CaptionName = "App.Listform.ListformField.PhoneNumber",
|
||||||
Width = 100,
|
Width = 100,
|
||||||
ListOrderNo = 11,
|
ListOrderNo = 11,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
|
|
@ -7375,7 +7372,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "Email",
|
FieldName = "Email",
|
||||||
CaptionName = "Abp.Account.EmailAddress",
|
CaptionName = "App.Listform.ListformField.Email",
|
||||||
Width = 250,
|
Width = 250,
|
||||||
ListOrderNo = 4,
|
ListOrderNo = 4,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
|
|
@ -7393,7 +7390,7 @@ public class ListFormSeeder_Saas : IDataSeedContributor, ITransientDependency
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.String,
|
||||||
FieldName = "PhoneNumber",
|
FieldName = "PhoneNumber",
|
||||||
CaptionName = "Abp.Identity.User.UserInformation.PhoneNumber",
|
CaptionName = "App.Listform.ListformField.PhoneNumber",
|
||||||
Width = 100,
|
Width = 100,
|
||||||
ListOrderNo = 5,
|
ListOrderNo = 5,
|
||||||
Visible = true,
|
Visible = true,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,13 @@
|
||||||
"routeType": "public",
|
"routeType": "public",
|
||||||
"authority": []
|
"authority": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "about",
|
||||||
|
"path": "/about",
|
||||||
|
"componentPath": "@/views/public/About",
|
||||||
|
"routeType": "public",
|
||||||
|
"authority": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "products",
|
"key": "products",
|
||||||
"path": "/products",
|
"path": "/products",
|
||||||
|
|
@ -180,7 +187,7 @@
|
||||||
"path": "/admin/ai",
|
"path": "/admin/ai",
|
||||||
"componentPath": "@/views/ai/Assistant",
|
"componentPath": "@/views/ai/Assistant",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": ["App.Definitions.AiBot.Asistant"]
|
"authority": ["App.AiBot.Asistant"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.profile.general",
|
"key": "admin.profile.general",
|
||||||
|
|
@ -445,22 +452,22 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ParentCode": "App.Saas.Definitions",
|
"ParentCode": "App.Saas.Definitions",
|
||||||
"Code": "App.Definitions.AiBot",
|
"Code": "App.AiBot",
|
||||||
"DisplayName": "App.Definitions.AiBot",
|
"DisplayName": "App.AiBot",
|
||||||
"Order": 1,
|
"Order": 1,
|
||||||
"Url": "/admin/list/App.Definitions.AiBot",
|
"Url": "/admin/list/App.AiBot",
|
||||||
"Icon": "FcMindMap",
|
"Icon": "FcMindMap",
|
||||||
"RequiredPermissionName": "App.Definitions.AiBot",
|
"RequiredPermissionName": "App.AiBot",
|
||||||
"IsDisabled": false
|
"IsDisabled": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ParentCode": "App.Saas.Definitions",
|
"ParentCode": "App.Saas.Definitions",
|
||||||
"Code": "App.Definitions.GlobalSearch",
|
"Code": "App.Settings.GlobalSearch",
|
||||||
"DisplayName": "App.Definitions.GlobalSearch",
|
"DisplayName": "App.Settings.GlobalSearch",
|
||||||
"Order": 2,
|
"Order": 2,
|
||||||
"Url": "/admin/list/App.Definitions.GlobalSearch",
|
"Url": "/admin/list/App.Settings.GlobalSearch",
|
||||||
"Icon": "FcSearch",
|
"Icon": "FcSearch",
|
||||||
"RequiredPermissionName": "App.Definitions.GlobalSearch",
|
"RequiredPermissionName": "App.Settings.GlobalSearch",
|
||||||
"IsDisabled": false
|
"IsDisabled": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -545,12 +552,12 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ParentCode": "App.Saas",
|
"ParentCode": "App.Saas",
|
||||||
"Code": "App.SettingDefinitions",
|
"Code": "App.Settings.SettingDefinitions",
|
||||||
"DisplayName": "App.SettingDefinitions",
|
"DisplayName": "App.Settings.SettingDefinitions",
|
||||||
"Order": 5,
|
"Order": 5,
|
||||||
"Url": "/admin/list/App.SettingDefinitions",
|
"Url": "/admin/list/App.Settings.SettingDefinitions",
|
||||||
"Icon": "FcSupport",
|
"Icon": "FcSupport",
|
||||||
"RequiredPermissionName": "App.SettingDefinitions",
|
"RequiredPermissionName": "App.Settings.SettingDefinitions",
|
||||||
"IsDisabled": false
|
"IsDisabled": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -585,12 +592,12 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ParentCode": "App.Saas",
|
"ParentCode": "App.Saas",
|
||||||
"Code": "App.DataSource",
|
"Code": "App.Listforms.DataSource",
|
||||||
"DisplayName": "App.DataSource",
|
"DisplayName": "App.Listforms.DataSource",
|
||||||
"Order": 7,
|
"Order": 7,
|
||||||
"Url": "/admin/list/App.DataSource",
|
"Url": "/admin/list/App.Listforms.DataSource",
|
||||||
"Icon": "FcAcceptDatabase",
|
"Icon": "FcAcceptDatabase",
|
||||||
"RequiredPermissionName": "App.DataSource",
|
"RequiredPermissionName": "App.Listforms.DataSource",
|
||||||
"IsDisabled": false
|
"IsDisabled": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -777,12 +784,12 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ParentCode": "App.Menus",
|
"ParentCode": "App.Menus",
|
||||||
"Code": "App.Menus.Routes",
|
"Code": "App.Routes",
|
||||||
"DisplayName": "App.Menus.Routes",
|
"DisplayName": "App.Routes",
|
||||||
"Order": 1,
|
"Order": 1,
|
||||||
"Url": "/admin/list/App.Menus.Routes",
|
"Url": "/admin/list/App.Routes",
|
||||||
"Icon": "FaSynagogue",
|
"Icon": "FaSynagogue",
|
||||||
"RequiredPermissionName": "App.Menus.Routes",
|
"RequiredPermissionName": "App.Routes",
|
||||||
"IsDisabled": false
|
"IsDisabled": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -938,40 +945,30 @@
|
||||||
"IsDisabled": false
|
"IsDisabled": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ParentCode": "App.Administration",
|
"ParentCode": "App.Administration.Definitions",
|
||||||
"Code": "App.Administration.Restrictions",
|
"Code": "App.Definitions.WorkHour",
|
||||||
"DisplayName": "App.Restrictions",
|
"DisplayName": "App.Definitions.WorkHour",
|
||||||
"Order": 3,
|
|
||||||
"Url": null,
|
|
||||||
"Icon": "FaLock",
|
|
||||||
"RequiredPermissionName": null,
|
|
||||||
"IsDisabled": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ParentCode": "App.Administration.Restrictions",
|
|
||||||
"Code": "App.Restrictions.WorkHour",
|
|
||||||
"DisplayName": "App.Restrictions.WorkHour",
|
|
||||||
"Order": 1,
|
|
||||||
"Url": "/admin/list/App.Restrictions.WorkHour",
|
|
||||||
"Icon": "FcClock",
|
|
||||||
"RequiredPermissionName": "App.Restrictions.WorkHour",
|
|
||||||
"IsDisabled": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ParentCode": "App.Administration.Restrictions",
|
|
||||||
"Code": "App.Restrictions.IpRestrictions",
|
|
||||||
"DisplayName": "App.Restrictions.IpRestrictions",
|
|
||||||
"Order": 2,
|
"Order": 2,
|
||||||
"Url": "/admin/list/App.Restrictions.IpRestrictions",
|
"Url": "/admin/list/App.Definitions.WorkHour",
|
||||||
|
"Icon": "FcClock",
|
||||||
|
"RequiredPermissionName": "App.Definitions.WorkHour",
|
||||||
|
"IsDisabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ParentCode": "App.Administration.Definitions",
|
||||||
|
"Code": "App.IpRestrictions",
|
||||||
|
"DisplayName": "App.IpRestrictions",
|
||||||
|
"Order": 3,
|
||||||
|
"Url": "/admin/list/App.IpRestrictions",
|
||||||
"Icon": "FcNfcSign",
|
"Icon": "FcNfcSign",
|
||||||
"RequiredPermissionName": "App.Restrictions.IpRestrictions",
|
"RequiredPermissionName": "App.IpRestrictions",
|
||||||
"IsDisabled": false
|
"IsDisabled": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ParentCode": "App.Administration",
|
"ParentCode": "App.Administration",
|
||||||
"Code": "Abp.Identity",
|
"Code": "Abp.Identity",
|
||||||
"DisplayName": "Abp.Identity",
|
"DisplayName": "Abp.Identity",
|
||||||
"Order": 4,
|
"Order": 3,
|
||||||
"Url": null,
|
"Url": null,
|
||||||
"Icon": "FcConferenceCall",
|
"Icon": "FcConferenceCall",
|
||||||
"RequiredPermissionName": null,
|
"RequiredPermissionName": null,
|
||||||
|
|
@ -1039,19 +1036,19 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ParentCode": "Abp.Identity",
|
"ParentCode": "Abp.Identity",
|
||||||
"Code": "App.IdentityManagement.AuditLogs",
|
"Code": "App.AuditLogs",
|
||||||
"DisplayName": "App.IdentityManagement.AuditLogs",
|
"DisplayName": "App.AuditLogs",
|
||||||
"Order": 7,
|
"Order": 7,
|
||||||
"Url": "/admin/list/App.IdentityManagement.AuditLogs",
|
"Url": "/admin/list/App.AuditLogs",
|
||||||
"Icon": "FcMultipleInputs",
|
"Icon": "FcMultipleInputs",
|
||||||
"RequiredPermissionName": "App.IdentityManagement.AuditLogs",
|
"RequiredPermissionName": "App.AuditLogs",
|
||||||
"IsDisabled": false
|
"IsDisabled": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ParentCode": "App.Administration",
|
"ParentCode": "App.Administration",
|
||||||
"Code": "App.Reports.Management",
|
"Code": "App.Reports.Management",
|
||||||
"DisplayName": "App.Reports.Management",
|
"DisplayName": "App.Reports.Management",
|
||||||
"Order": 5,
|
"Order": 4,
|
||||||
"Url": null,
|
"Url": null,
|
||||||
"Icon": "FcDocument",
|
"Icon": "FcDocument",
|
||||||
"RequiredPermissionName": null,
|
"RequiredPermissionName": null,
|
||||||
|
|
@ -1081,7 +1078,7 @@
|
||||||
"ParentCode": "App.Administration",
|
"ParentCode": "App.Administration",
|
||||||
"Code": "App.Files",
|
"Code": "App.Files",
|
||||||
"DisplayName": "App.Files",
|
"DisplayName": "App.Files",
|
||||||
"Order": 6,
|
"Order": 5,
|
||||||
"Url": "/admin/files",
|
"Url": "/admin/files",
|
||||||
"Icon": "FcFolder",
|
"Icon": "FcFolder",
|
||||||
"RequiredPermissionName": "App.Files",
|
"RequiredPermissionName": "App.Files",
|
||||||
|
|
@ -1091,7 +1088,7 @@
|
||||||
"ParentCode": "App.Administration",
|
"ParentCode": "App.Administration",
|
||||||
"Code": "App.Forum",
|
"Code": "App.Forum",
|
||||||
"DisplayName": "App.Forum",
|
"DisplayName": "App.Forum",
|
||||||
"Order": 7,
|
"Order": 6,
|
||||||
"Url": "/admin/forum",
|
"Url": "/admin/forum",
|
||||||
"Icon": "FcLink",
|
"Icon": "FcLink",
|
||||||
"RequiredPermissionName": "App.ForumManagement.Publish",
|
"RequiredPermissionName": "App.ForumManagement.Publish",
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -11,7 +11,6 @@ public enum OperationEnum
|
||||||
Delete,
|
Delete,
|
||||||
DeleteBefore,
|
DeleteBefore,
|
||||||
DeleteAfter,
|
DeleteAfter,
|
||||||
Select,
|
Select
|
||||||
Duplicate,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -290,80 +290,42 @@ public static class PlatformConsts
|
||||||
|
|
||||||
public static class AppCodes
|
public static class AppCodes
|
||||||
{
|
{
|
||||||
|
public const string Home = Prefix.App + ".Home";
|
||||||
public const string Saas = Prefix.App + ".Saas";
|
public const string Saas = Prefix.App + ".Saas";
|
||||||
|
|
||||||
public const string Branches = Prefix.App + ".Branches";
|
public const string Branches = Prefix.App + ".Branches";
|
||||||
public static class Definitions
|
public static class Settings
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".Definitions";
|
public const string Default = Prefix.App + ".Settings";
|
||||||
|
|
||||||
public const string AiBot = Default + ".AiBot";
|
|
||||||
public const string GlobalSearch = Default + ".GlobalSearch";
|
public const string GlobalSearch = Default + ".GlobalSearch";
|
||||||
public const string ContactTitle = Default + ".ContactTitle";
|
public const string SettingDefinitions = Default + ".SettingDefinitions";
|
||||||
public const string Currency = Default + ".Currency";
|
|
||||||
public const string CountryGroup = Default + ".CountryGroup";
|
|
||||||
public const string Country = Default + ".Country";
|
|
||||||
public const string City = Default + ".City";
|
|
||||||
public const string District = Default + ".District";
|
|
||||||
public const string SkillType = Default + ".SkillType";
|
|
||||||
public const string UomCategory = Default + ".UomCategory";
|
|
||||||
|
|
||||||
public const string Sector = Default + ".Sector";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Restrictions
|
|
||||||
{
|
|
||||||
public const string Default = Prefix.App + ".Restrictions";
|
|
||||||
|
|
||||||
public const string WorkHour = Default + ".WorkHour";
|
|
||||||
public const string IpRestrictions = Default + ".IpRestrictions";
|
|
||||||
}
|
|
||||||
|
|
||||||
public const string SettingDefinitions = Prefix.App + ".SettingDefinitions";
|
|
||||||
|
|
||||||
public static class Languages
|
public static class Languages
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".Languages";
|
public const string Default = Prefix.App + ".Languages";
|
||||||
|
|
||||||
public const string Language = Default + ".Language";
|
public const string Language = Default + ".Language";
|
||||||
public const string LanguageText = Default + ".LanguageText";
|
public const string LanguageText = Default + ".LanguageText";
|
||||||
}
|
}
|
||||||
|
public const string Menus = Prefix.App + ".Menus";
|
||||||
public const string DataSource = Prefix.App + ".DataSource";
|
|
||||||
public static class Listforms
|
public static class Listforms
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".Listforms";
|
public const string Default = Prefix.App + ".Listforms";
|
||||||
|
|
||||||
public const string Wizard = Default + ".Wizard";
|
public const string Wizard = Default + ".Wizard";
|
||||||
|
public const string DataSource = Default + ".DataSource";
|
||||||
public const string Listform = Default + ".Listform";
|
public const string Listform = Default + ".Listform";
|
||||||
public const string ListformField = Default + ".ListformField";
|
public const string ListformField = Default + ".ListformField";
|
||||||
public const string Chart = Default + ".Chart";
|
public const string Chart = Default + ".Chart";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Notifications
|
public static class Notifications
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".Notifications";
|
public const string Default = Prefix.App + ".Notifications";
|
||||||
|
|
||||||
public const string NotificationRules = Default + ".NotificationRules";
|
public const string NotificationRules = Default + ".NotificationRules";
|
||||||
public const string Notification = Default + ".Notification";
|
public const string Notification = Default + ".Notification";
|
||||||
}
|
}
|
||||||
|
|
||||||
public const string BackgroundWorkers = Prefix.App + ".BackgroundWorkers";
|
public const string BackgroundWorkers = Prefix.App + ".BackgroundWorkers";
|
||||||
|
|
||||||
public static class Menus
|
|
||||||
{
|
|
||||||
public const string Default = Prefix.App + ".Menus";
|
|
||||||
|
|
||||||
public const string Routes = Default + ".Routes";
|
|
||||||
public const string MenuGroup = Default + ".MenuGroup";
|
|
||||||
public const string Menu = Default + ".Menu";
|
|
||||||
public const string Manager = Default + ".Manager";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DeveloperKits
|
public static class DeveloperKits
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".DeveloperKit";
|
public const string Default = Prefix.App + ".DeveloperKit";
|
||||||
|
|
||||||
public const string CustomEndpoints = Default + ".CustomEndpoints";
|
public const string CustomEndpoints = Default + ".CustomEndpoints";
|
||||||
|
|
||||||
public const string Get = CustomEndpoints + ".Get";
|
public const string Get = CustomEndpoints + ".Get";
|
||||||
|
|
@ -384,48 +346,63 @@ public static class PlatformConsts
|
||||||
public const string ViewCode = DynamicService + ".ViewCode";
|
public const string ViewCode = DynamicService + ".ViewCode";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public const string Blog = Prefix.App + ".Blog";
|
||||||
//Web Site
|
public const string Forum = Prefix.App + ".Forum";
|
||||||
public const string Home = Prefix.App + ".Home";
|
|
||||||
public const string About = Prefix.App + ".About";
|
|
||||||
public const string Services = Prefix.App + ".Services";
|
|
||||||
public static class Orders
|
|
||||||
{
|
|
||||||
public const string Default = Prefix.App + ".Orders";
|
|
||||||
|
|
||||||
public const string Products = Default + ".Products";
|
|
||||||
public const string PaymentMethods = Default + ".PaymentMethods";
|
|
||||||
public const string InstallmentOptions = Default + ".InstallmentOptions";
|
|
||||||
public const string SalesOrders = Default + ".SalesOrders";
|
|
||||||
}
|
|
||||||
public static class BlogManagement
|
|
||||||
{
|
|
||||||
public const string Default = Prefix.App + ".BlogManagement";
|
|
||||||
|
|
||||||
public const string BlogPosts = Default + ".Posts";
|
|
||||||
public const string BlogCategory = Default + ".Category";
|
|
||||||
}
|
|
||||||
public const string Demos = Prefix.App + ".Demos";
|
|
||||||
public const string Contact = Prefix.App + ".Contact";
|
|
||||||
|
|
||||||
//Administration
|
|
||||||
public const string Administration = Prefix.App + ".Administration";
|
public const string Administration = Prefix.App + ".Administration";
|
||||||
|
|
||||||
public const string Setting = Prefix.App + ".Setting";
|
public const string Setting = Prefix.App + ".Setting";
|
||||||
public static class IdentityManagement
|
public static class IdentityManagement
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".IdentityManagement";
|
public const string ClaimTypes = Prefix.App + ".ClaimType";
|
||||||
|
public const string IpRestrictions = Prefix.App + ".IpRestrictions";
|
||||||
public const string ClaimTypes = Default + ".ClaimType";
|
|
||||||
public const string AuditLogs = Default + ".AuditLogs";
|
|
||||||
}
|
}
|
||||||
|
public const string AuditLogs = Prefix.App + ".AuditLogs";
|
||||||
public static class Reports
|
public static class Definitions
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".Reports";
|
public const string ContactTag = Default + ".ContactTag";
|
||||||
|
public const string ContactTitle = Default + ".ContactTitle";
|
||||||
public const string Categories = Default + ".Categories";
|
public const string Currency = Default + ".Currency";
|
||||||
public const string ReportTemplates = Default + ".ReportTemplates";
|
public const string CountryGroup = Default + ".CountryGroup";
|
||||||
|
public const string Country = Default + ".Country";
|
||||||
|
public const string City = Default + ".City";
|
||||||
|
public const string District = Default + ".District";
|
||||||
|
public const string Default = Prefix.App + ".Definitions";
|
||||||
|
public const string Sector = Default + ".Sector";
|
||||||
|
public const string SkillType = Default + ".SkillType";
|
||||||
|
public const string UomCategory = Default + ".UomCategory";
|
||||||
|
public const string Bank = Default + ".Bank";
|
||||||
|
public const string Behavior = Default + ".Behavior";
|
||||||
|
public const string Disease = Default + ".Disease";
|
||||||
|
public const string Document = Default + ".Document";
|
||||||
|
public const string EducationStatus = Default + ".EducationStatus";
|
||||||
|
public const string MeetingMethod = Default + ".MeetingMethod";
|
||||||
|
public const string MeetingResult = Default + ".MeetingResult";
|
||||||
|
public const string Program = Default + ".Program";
|
||||||
|
public const string Interesting = Default + ".Interesting";
|
||||||
|
public const string SalesRejectionReason = Default + ".SalesRejectionReason";
|
||||||
|
public const string ClassCancellationReason = Default + ".ClassCancellationReason";
|
||||||
|
public const string Source = Default + ".Source";
|
||||||
|
public const string Vaccine = Default + ".Vaccine";
|
||||||
|
public const string NoteType = Default + ".NoteType";
|
||||||
|
public const string WorkHour = Default + ".WorkHour";
|
||||||
|
public const string Vehicle = Default + ".Vehicle";
|
||||||
|
public const string Schedule = Default + ".Schedule";
|
||||||
|
public const string ScheduleLesson = Default + ".ScheduleLesson";
|
||||||
|
public const string Psychologist = Default + ".Psychologist";
|
||||||
|
public const string Meal = Default + ".Meal";
|
||||||
|
public const string Lawyer = Default + ".Lawyer";
|
||||||
|
public const string LessonPeriod = Default + ".LessonPeriod";
|
||||||
|
public const string RegistrationType = Default + ".RegistrationType";
|
||||||
|
public const string RegistrationMethod = Default + ".RegistrationMethod";
|
||||||
|
public const string ClassType = Default + ".ClassType";
|
||||||
|
public const string Class = Default + ".Class";
|
||||||
|
public const string Level = Default + ".Level";
|
||||||
|
}
|
||||||
|
public static class Hr
|
||||||
|
{
|
||||||
|
public const string Default = Prefix.App + ".Hr";
|
||||||
|
public const string EventType = Default + ".EventType";
|
||||||
|
public const string EventCategory = Default + ".EventCategory";
|
||||||
|
public const string Event = Default + ".Event";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -318,77 +318,70 @@ public static class SeedConsts
|
||||||
|
|
||||||
public static class AppCodes
|
public static class AppCodes
|
||||||
{
|
{
|
||||||
|
public const string Home = Prefix.App + ".Home";
|
||||||
|
|
||||||
|
//Saas
|
||||||
public const string Saas = Prefix.App + ".Saas";
|
public const string Saas = Prefix.App + ".Saas";
|
||||||
|
|
||||||
public const string Branches = Prefix.App + ".Branches";
|
public const string Branches = Prefix.App + ".Branches";
|
||||||
|
public static class Settings
|
||||||
public static class Definitions
|
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".Definitions";
|
public const string Default = Prefix.App + ".Settings";
|
||||||
|
|
||||||
public const string AiBot = Default + ".AiBot";
|
|
||||||
public const string GlobalSearch = Default + ".GlobalSearch";
|
public const string GlobalSearch = Default + ".GlobalSearch";
|
||||||
public const string ContactTitle = Default + ".ContactTitle";
|
public const string SettingDefinitions = Default + ".SettingDefinitions";
|
||||||
public const string Currency = Default + ".Currency";
|
|
||||||
public const string CountryGroup = Default + ".CountryGroup";
|
|
||||||
public const string Country = Default + ".Country";
|
|
||||||
public const string City = Default + ".City";
|
|
||||||
public const string District = Default + ".District";
|
|
||||||
public const string SkillType = Default + ".SkillType";
|
|
||||||
public const string SkillLevel = Default + ".SkillLevel";
|
|
||||||
public const string Skill = Default + ".Skill";
|
|
||||||
public const string UomCategory = Default + ".UomCategory";
|
|
||||||
public const string Uom = Default + ".Uom";
|
|
||||||
|
|
||||||
public const string Sector = Default + ".Sector";
|
|
||||||
}
|
}
|
||||||
|
public const string AiBot = Prefix.App + ".AiBot";
|
||||||
public static class Restrictions
|
|
||||||
{
|
|
||||||
public const string Default = Prefix.App + ".Restrictions";
|
|
||||||
|
|
||||||
public const string WorkHour = Default + ".WorkHour";
|
|
||||||
public const string IpRestrictions = Default + ".IpRestrictions";
|
|
||||||
}
|
|
||||||
|
|
||||||
public const string SettingDefinitions = Prefix.App + ".SettingDefinitions";
|
|
||||||
|
|
||||||
public static class Languages
|
public static class Languages
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".Languages";
|
public const string Default = Prefix.App + ".Languages";
|
||||||
public const string Language = Default + ".Language";
|
public const string Language = Default + ".Language";
|
||||||
public const string LanguageText = Default + ".LanguageText";
|
public const string LanguageText = Default + ".LanguageText";
|
||||||
}
|
}
|
||||||
|
public const string Routes = Prefix.App + ".Routes";
|
||||||
|
public static class Menus
|
||||||
|
{
|
||||||
|
public const string Default = Prefix.App + ".Menus";
|
||||||
|
|
||||||
public const string DataSource = Prefix.App + ".DataSource";
|
public const string MenuGroup = Default + ".MenuGroup";
|
||||||
|
public const string Menu = Default + ".Menu";
|
||||||
|
public const string Manager = Default + ".Manager";
|
||||||
|
}
|
||||||
public static class Listforms
|
public static class Listforms
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".Listforms";
|
public const string Default = Prefix.App + ".Listforms";
|
||||||
|
|
||||||
|
public const string DataSource = Default + ".DataSource";
|
||||||
public const string Wizard = Default + ".Wizard";
|
public const string Wizard = Default + ".Wizard";
|
||||||
public const string Listform = Default + ".Listform";
|
public const string Listform = Default + ".Listform";
|
||||||
public const string ListformField = Default + ".ListformField";
|
public const string ListformField = Default + ".ListformField";
|
||||||
public const string Chart = Default + ".Chart";
|
public const string Chart = Default + ".Chart";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Notifications
|
public static class Notifications
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".Notifications";
|
public const string Default = Prefix.App + ".Notifications";
|
||||||
|
|
||||||
public const string NotificationRules = Default + ".NotificationRules";
|
public const string NotificationRules = Default + ".NotificationRules";
|
||||||
public const string Notification = Default + ".Notification";
|
public const string Notification = Default + ".Notification";
|
||||||
}
|
}
|
||||||
|
|
||||||
public const string BackgroundWorkers = Prefix.App + ".BackgroundWorkers";
|
public const string BackgroundWorkers = Prefix.App + ".BackgroundWorkers";
|
||||||
|
public const string Forum = Prefix.App + ".Forum";
|
||||||
|
|
||||||
|
public static class DeveloperKits
|
||||||
|
{
|
||||||
|
public const string Default = Prefix.App + ".DeveloperKit";
|
||||||
|
public const string CustomEndpoints = Default + ".CustomEndpoints";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Reports
|
||||||
|
{
|
||||||
|
public const string Default = Prefix.App + ".Reports";
|
||||||
|
public const string Categories = Default + ".Categories";
|
||||||
|
public const string ReportTemplates = Default + ".ReportTemplates";
|
||||||
|
}
|
||||||
//Web Site
|
//Web Site
|
||||||
public const string Home = Prefix.App + ".Home";
|
|
||||||
public const string About = Prefix.App + ".About";
|
public const string About = Prefix.App + ".About";
|
||||||
public const string Services = Prefix.App + ".Services";
|
public const string Services = Prefix.App + ".Services";
|
||||||
public static class Orders
|
public static class Orders
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".Orders";
|
public const string Default = Prefix.App + ".Orders";
|
||||||
|
|
||||||
public const string Products = Default + ".Products";
|
public const string Products = Default + ".Products";
|
||||||
public const string PaymentMethods = Default + ".PaymentMethods";
|
public const string PaymentMethods = Default + ".PaymentMethods";
|
||||||
public const string InstallmentOptions = Default + ".InstallmentOptions";
|
public const string InstallmentOptions = Default + ".InstallmentOptions";
|
||||||
|
|
@ -404,43 +397,32 @@ public static class SeedConsts
|
||||||
public const string Demos = Prefix.App + ".Demos";
|
public const string Demos = Prefix.App + ".Demos";
|
||||||
public const string Contact = Prefix.App + ".Contact";
|
public const string Contact = Prefix.App + ".Contact";
|
||||||
|
|
||||||
public static class Menus
|
|
||||||
{
|
|
||||||
public const string Default = Prefix.App + ".Menus";
|
|
||||||
|
|
||||||
public const string Routes = Default + ".Routes";
|
|
||||||
public const string MenuGroup = Default + ".MenuGroup";
|
|
||||||
public const string Menu = Default + ".Menu";
|
|
||||||
public const string Manager = Default + ".Manager";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DeveloperKits
|
|
||||||
{
|
|
||||||
public const string Default = Prefix.App + ".DeveloperKit";
|
|
||||||
|
|
||||||
public const string CustomEndpoints = Default + ".CustomEndpoints";
|
|
||||||
}
|
|
||||||
|
|
||||||
public const string Forum = Prefix.App + ".Forum";
|
|
||||||
|
|
||||||
//Administration
|
//Administration
|
||||||
public const string Administration = Prefix.App + ".Administration";
|
public const string Administration = Prefix.App + ".Administration";
|
||||||
|
|
||||||
public const string Setting = Prefix.App + ".Setting";
|
public const string Setting = Prefix.App + ".Setting";
|
||||||
public static class IdentityManagement
|
public static class IdentityManagement
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".IdentityManagement";
|
public const string ClaimTypes = Prefix.App + ".ClaimType";
|
||||||
|
public const string IpRestrictions = Prefix.App + ".IpRestrictions";
|
||||||
public const string ClaimTypes = Default + ".ClaimType";
|
|
||||||
public const string AuditLogs = Default + ".AuditLogs";
|
|
||||||
}
|
}
|
||||||
|
public const string AuditLogs = Prefix.App + ".AuditLogs";
|
||||||
public static class Reports
|
public static class Definitions
|
||||||
{
|
{
|
||||||
public const string Default = Prefix.App + ".Reports";
|
public const string Default = Prefix.App + ".Definitions";
|
||||||
|
|
||||||
public const string Categories = Default + ".Categories";
|
public const string ContactTitle = Default + ".ContactTitle";
|
||||||
public const string ReportTemplates = Default + ".ReportTemplates";
|
public const string Currency = Default + ".Currency";
|
||||||
|
public const string CountryGroup = Default + ".CountryGroup";
|
||||||
|
public const string Country = Default + ".Country";
|
||||||
|
public const string City = Default + ".City";
|
||||||
|
public const string District = Default + ".District";
|
||||||
|
public const string Sector = Default + ".Sector";
|
||||||
|
public const string SkillType = Default + ".SkillType";
|
||||||
|
public const string SkillLevel = Default + ".SkillLevel";
|
||||||
|
public const string Skill = Default + ".Skill";
|
||||||
|
public const string UomCategory = Default + ".UomCategory";
|
||||||
|
public const string Uom = Default + ".Uom";
|
||||||
|
public const string WorkHour = Default + ".WorkHour";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ public class ListFormField : FullAuditedEntity<Guid>
|
||||||
public string CultureName { get; set; } // Bu tanım hangi dil için (“tr”, “en”)
|
public string CultureName { get; set; } // Bu tanım hangi dil için (“tr”, “en”)
|
||||||
public string FieldName { get; set; } // Kaynaktaki sutun adi
|
public string FieldName { get; set; } // Kaynaktaki sutun adi
|
||||||
public string CaptionName { get; set; } // Sutun basligi
|
public string CaptionName { get; set; } // Sutun basligi
|
||||||
public string PlaceHolder { get; set; } // Sutun placeholder'i
|
|
||||||
public bool? Visible { get; set; } // Liste üzerinde gösterilecek mi? Yoksa eklenebilir sütunların arasında mı duracak. select sorgusuna dahildir
|
public bool? Visible { get; set; } // Liste üzerinde gösterilecek mi? Yoksa eklenebilir sütunların arasında mı duracak. select sorgusuna dahildir
|
||||||
public bool? IsActive { get; set; } = true; // Sadece IsActive olan alanlar sorguya dahil edilir
|
public bool? IsActive { get; set; } = true; // Sadece IsActive olan alanlar sorguya dahil edilir
|
||||||
public int? Width { get; set; } // Sütunun listedeki genişliği
|
public int? Width { get; set; } // Sütunun listedeki genişliği
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using Microsoft.Extensions.Localization;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
@ -94,7 +95,6 @@ public class ListFormManager : PlatformDomainService, IListFormManager
|
||||||
var field = listFormFields.FirstOrDefault(c => c.FieldName == item.Key);
|
var field = listFormFields.FirstOrDefault(c => c.FieldName == item.Key);
|
||||||
if (field == null
|
if (field == null
|
||||||
|| (op == OperationEnum.Insert && !field.CanCreate)
|
|| (op == OperationEnum.Insert && !field.CanCreate)
|
||||||
|| (op == OperationEnum.Duplicate && !field.CanCreate)
|
|
||||||
|| (op == OperationEnum.Update && !field.CanUpdate)
|
|| (op == OperationEnum.Update && !field.CanUpdate)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@ public class DefaultValueManager : PlatformDomainService, IDefaultValueManager
|
||||||
var defaultFieldsJson = op switch
|
var defaultFieldsJson = op switch
|
||||||
{
|
{
|
||||||
OperationEnum.Insert => listForm.InsertFieldsDefaultValueJson,
|
OperationEnum.Insert => listForm.InsertFieldsDefaultValueJson,
|
||||||
OperationEnum.Duplicate => listForm.InsertFieldsDefaultValueJson,
|
|
||||||
OperationEnum.Update => listForm.UpdateFieldsDefaultValueJson,
|
OperationEnum.Update => listForm.UpdateFieldsDefaultValueJson,
|
||||||
OperationEnum.Delete => listForm.DeleteFieldsDefaultValueJson,
|
OperationEnum.Delete => listForm.DeleteFieldsDefaultValueJson,
|
||||||
OperationEnum.Select => listForm.FormFieldsDefaultValueJson,
|
OperationEnum.Select => listForm.FormFieldsDefaultValueJson,
|
||||||
|
|
@ -104,7 +103,6 @@ public class DefaultValueManager : PlatformDomainService, IDefaultValueManager
|
||||||
var field = listFormFields.FirstOrDefault(c => c.FieldName == item.Key);
|
var field = listFormFields.FirstOrDefault(c => c.FieldName == item.Key);
|
||||||
if (field == null
|
if (field == null
|
||||||
|| (op == OperationEnum.Insert && !field.CanCreate)
|
|| (op == OperationEnum.Insert && !field.CanCreate)
|
||||||
|| (op == OperationEnum.Duplicate && !field.CanCreate)
|
|
||||||
|| (op == OperationEnum.Update && !field.CanUpdate)
|
|| (op == OperationEnum.Update && !field.CanUpdate)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ public interface IQueryManager
|
||||||
|
|
||||||
string GenerateQuery(
|
string GenerateQuery(
|
||||||
ListForm listForm,
|
ListForm listForm,
|
||||||
List<ListFormField> listFormFields,
|
|
||||||
Dictionary<string, object> parameters,
|
Dictionary<string, object> parameters,
|
||||||
OperationEnum op,
|
OperationEnum op,
|
||||||
DataSourceTypeEnum dataSourceType,
|
DataSourceTypeEnum dataSourceType,
|
||||||
|
|
@ -78,20 +77,24 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
||||||
var listFormFields = await listFormFieldManager.GetUserListFormFields(listFormCode);
|
var listFormFields = await listFormFieldManager.GetUserListFormFields(listFormCode);
|
||||||
|
|
||||||
var parameters = await listFormManager.GetParametersAsync(listForm, listFormFields, inputParams, op, keys, queryParameters);
|
var parameters = await listFormManager.GetParametersAsync(listForm, listFormFields, inputParams, op, keys, queryParameters);
|
||||||
|
// if (parameters == null || parameters.Count == 0)
|
||||||
|
// {
|
||||||
|
// throw new UserFriendlyException(localizer[AppErrorCodes.ParameterNotValid]);
|
||||||
|
// }
|
||||||
|
|
||||||
var (dynamicDataRepository, connectionString, dataSourceType) = await dynamicDataManager.GetAsync(listForm.IsTenant, listForm.DataSourceCode);
|
var (dynamicDataRepository, connectionString, dataSourceType) = await dynamicDataManager.GetAsync(listForm.IsTenant, listForm.DataSourceCode);
|
||||||
|
|
||||||
var sql = GenerateQuery(listForm, listFormFields, parameters, op, dataSourceType, keys);
|
var sql = GenerateQuery(listForm, parameters, op, dataSourceType, keys);
|
||||||
|
|
||||||
// Sorguyu calistir
|
// Sorguyu calistir
|
||||||
if (!string.IsNullOrEmpty(sql))
|
if (!string.IsNullOrEmpty(sql))
|
||||||
{
|
{
|
||||||
// TODO: Log
|
// TODO: Log
|
||||||
if (op == OperationEnum.Insert || op == OperationEnum.Duplicate)
|
if (op == OperationEnum.Insert)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(listForm.InsertBeforeCommand))
|
if (!string.IsNullOrEmpty(listForm.InsertBeforeCommand))
|
||||||
{
|
{
|
||||||
var beforeSql = GenerateQuery(listForm, listFormFields, parameters, OperationEnum.InsertBefore, dataSourceType, keys);
|
var beforeSql = GenerateQuery(listForm, parameters, OperationEnum.InsertBefore, dataSourceType, keys);
|
||||||
await dynamicDataRepository.ExecuteAsync(beforeSql, connectionString, parameters);
|
await dynamicDataRepository.ExecuteAsync(beforeSql, connectionString, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,7 +102,7 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(listForm.InsertAfterCommand))
|
if (!string.IsNullOrEmpty(listForm.InsertAfterCommand))
|
||||||
{
|
{
|
||||||
var afterSql = GenerateQuery(listForm, listFormFields, parameters, OperationEnum.InsertAfter, dataSourceType, keys);
|
var afterSql = GenerateQuery(listForm, parameters, OperationEnum.InsertAfter, dataSourceType, keys);
|
||||||
await dynamicDataRepository.ExecuteAsync(afterSql, connectionString, parameters);
|
await dynamicDataRepository.ExecuteAsync(afterSql, connectionString, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,12 +113,12 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
||||||
// Before komutlari varsa calistir
|
// Before komutlari varsa calistir
|
||||||
if (op == OperationEnum.Update && !string.IsNullOrEmpty(listForm.UpdateBeforeCommand))
|
if (op == OperationEnum.Update && !string.IsNullOrEmpty(listForm.UpdateBeforeCommand))
|
||||||
{
|
{
|
||||||
var beforeSql = GenerateQuery(listForm, listFormFields, parameters, OperationEnum.UpdateBefore, dataSourceType, keys);
|
var beforeSql = GenerateQuery(listForm, parameters, OperationEnum.UpdateBefore, dataSourceType, keys);
|
||||||
await dynamicDataRepository.ExecuteAsync(beforeSql, connectionString, parameters);
|
await dynamicDataRepository.ExecuteAsync(beforeSql, connectionString, parameters);
|
||||||
}
|
}
|
||||||
else if (op == OperationEnum.Delete && !string.IsNullOrEmpty(listForm.DeleteBeforeCommand))
|
else if (op == OperationEnum.Delete && !string.IsNullOrEmpty(listForm.DeleteBeforeCommand))
|
||||||
{
|
{
|
||||||
var beforeSql = GenerateQuery(listForm, listFormFields, parameters, OperationEnum.DeleteBefore, dataSourceType, keys);
|
var beforeSql = GenerateQuery(listForm, parameters, OperationEnum.DeleteBefore, dataSourceType, keys);
|
||||||
await dynamicDataRepository.ExecuteAsync(beforeSql, connectionString, parameters);
|
await dynamicDataRepository.ExecuteAsync(beforeSql, connectionString, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,12 +128,12 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
||||||
// After komutlari varsa calistir
|
// After komutlari varsa calistir
|
||||||
if (op == OperationEnum.Update && !string.IsNullOrEmpty(listForm.UpdateAfterCommand))
|
if (op == OperationEnum.Update && !string.IsNullOrEmpty(listForm.UpdateAfterCommand))
|
||||||
{
|
{
|
||||||
var afterSql = GenerateQuery(listForm, listFormFields, parameters, OperationEnum.UpdateAfter, dataSourceType, keys);
|
var afterSql = GenerateQuery(listForm, parameters, OperationEnum.UpdateAfter, dataSourceType, keys);
|
||||||
await dynamicDataRepository.ExecuteAsync(afterSql, connectionString, parameters);
|
await dynamicDataRepository.ExecuteAsync(afterSql, connectionString, parameters);
|
||||||
}
|
}
|
||||||
else if (op == OperationEnum.Delete && !string.IsNullOrEmpty(listForm.DeleteAfterCommand))
|
else if (op == OperationEnum.Delete && !string.IsNullOrEmpty(listForm.DeleteAfterCommand))
|
||||||
{
|
{
|
||||||
var afterSql = GenerateQuery(listForm, listFormFields, parameters, OperationEnum.DeleteAfter, dataSourceType, keys);
|
var afterSql = GenerateQuery(listForm, parameters, OperationEnum.DeleteAfter, dataSourceType, keys);
|
||||||
await dynamicDataRepository.ExecuteAsync(afterSql, connectionString, parameters);
|
await dynamicDataRepository.ExecuteAsync(afterSql, connectionString, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +146,6 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
||||||
|
|
||||||
public string GenerateQuery(
|
public string GenerateQuery(
|
||||||
ListForm listForm,
|
ListForm listForm,
|
||||||
List<ListFormField> listFormField,
|
|
||||||
Dictionary<string, object> parameters,
|
Dictionary<string, object> parameters,
|
||||||
OperationEnum op,
|
OperationEnum op,
|
||||||
DataSourceTypeEnum dataSourceType,
|
DataSourceTypeEnum dataSourceType,
|
||||||
|
|
@ -151,7 +153,6 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
||||||
{
|
{
|
||||||
var command = op switch
|
var command = op switch
|
||||||
{
|
{
|
||||||
OperationEnum.Duplicate => listForm.InsertCommand,
|
|
||||||
OperationEnum.Insert => listForm.InsertCommand,
|
OperationEnum.Insert => listForm.InsertCommand,
|
||||||
OperationEnum.InsertBefore => listForm.InsertBeforeCommand,
|
OperationEnum.InsertBefore => listForm.InsertBeforeCommand,
|
||||||
OperationEnum.InsertAfter => listForm.InsertAfterCommand,
|
OperationEnum.InsertAfter => listForm.InsertAfterCommand,
|
||||||
|
|
@ -176,7 +177,7 @@ public class QueryManager : PlatformDomainService, IQueryManager
|
||||||
var fieldString = string.Join(',', parameters.Keys.Select(a => $"\"{a}\"").ToList());
|
var fieldString = string.Join(',', parameters.Keys.Select(a => $"\"{a}\"").ToList());
|
||||||
var fieldParams = string.Join(',', parameters.Keys.Select(a => $"@{a}"));
|
var fieldParams = string.Join(',', parameters.Keys.Select(a => $"@{a}"));
|
||||||
|
|
||||||
if (op == OperationEnum.Insert || op == OperationEnum.Duplicate)
|
if (op == OperationEnum.Insert)
|
||||||
{
|
{
|
||||||
sql = dataSourceType switch
|
sql = dataSourceType switch
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -365,7 +365,6 @@ public class PlatformDbContext :
|
||||||
b.Property(a => a.CultureName).HasMaxLength(10).IsRequired();
|
b.Property(a => a.CultureName).HasMaxLength(10).IsRequired();
|
||||||
b.Property(a => a.FieldName).IsRequired().HasMaxLength(128);
|
b.Property(a => a.FieldName).IsRequired().HasMaxLength(128);
|
||||||
b.Property(a => a.CaptionName).HasMaxLength(256);
|
b.Property(a => a.CaptionName).HasMaxLength(256);
|
||||||
b.Property(a => a.PlaceHolder).HasMaxLength(256);
|
|
||||||
|
|
||||||
// Varsayılan değerler
|
// Varsayılan değerler
|
||||||
b.Property(a => a.AllowSearch).HasDefaultValue(false);
|
b.Property(a => a.AllowSearch).HasDefaultValue(false);
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
||||||
namespace Sozsoft.Platform.Migrations
|
namespace Sozsoft.Platform.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PlatformDbContext))]
|
[DbContext(typeof(PlatformDbContext))]
|
||||||
[Migration("20260330120142_Initial")]
|
[Migration("20260317181749_Initial")]
|
||||||
partial class Initial
|
partial class Initial
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -2656,10 +2656,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
b.Property<string>("PivotSettingsJson")
|
b.Property<string>("PivotSettingsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("PlaceHolder")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<string>("RoleId")
|
b.Property<string>("RoleId")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
@ -2031,7 +2031,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
CultureName = table.Column<string>(type: "nvarchar(10)", maxLength: 10, nullable: false),
|
CultureName = table.Column<string>(type: "nvarchar(10)", maxLength: 10, nullable: false),
|
||||||
FieldName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
FieldName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
||||||
CaptionName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
CaptionName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
PlaceHolder = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
|
||||||
Visible = table.Column<bool>(type: "bit", nullable: true, defaultValue: true),
|
Visible = table.Column<bool>(type: "bit", nullable: true, defaultValue: true),
|
||||||
IsActive = table.Column<bool>(type: "bit", nullable: true, defaultValue: true),
|
IsActive = table.Column<bool>(type: "bit", nullable: true, defaultValue: true),
|
||||||
Width = table.Column<int>(type: "int", nullable: true, defaultValue: 100),
|
Width = table.Column<int>(type: "int", nullable: true, defaultValue: 100),
|
||||||
|
|
@ -2653,10 +2653,6 @@ namespace Sozsoft.Platform.Migrations
|
||||||
b.Property<string>("PivotSettingsJson")
|
b.Property<string>("PivotSettingsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("PlaceHolder")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<string>("RoleId")
|
b.Property<string>("RoleId")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
|
||||||
|
|
@ -1,281 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Data.SqlClient;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Serilog;
|
|
||||||
using static Sozsoft.Settings.SettingsConsts;
|
|
||||||
|
|
||||||
namespace Sozsoft.Platform;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Veritabanı henüz hazır değilken çalışan minimal kurulum uygulaması.
|
|
||||||
/// Tam ABP stack yüklemez; sadece /api/setup/* endpointlerini sunar.
|
|
||||||
/// </summary>
|
|
||||||
internal static class SetupAppRunner
|
|
||||||
{
|
|
||||||
// Veritabanı Hazırlık Kontrolü
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DB var mı ve AbpRoles tablosu oluşmuş mu diye kontrol eder.
|
|
||||||
/// Boş DB veya bağlantı hatası durumunda false döner.
|
|
||||||
/// </summary>
|
|
||||||
public static bool DatabaseIsReady(IConfiguration configuration)
|
|
||||||
{
|
|
||||||
var connectionString = configuration.GetConnectionString(DefaultDatabaseProvider);
|
|
||||||
if (string.IsNullOrWhiteSpace(connectionString))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (DefaultDatabaseProvider == DatabaseProvider.SqlServer)
|
|
||||||
return SqlServerIsReady(connectionString);
|
|
||||||
|
|
||||||
#pragma warning disable CS0162
|
|
||||||
return true; // Diğer sağlayıcılar için geçici — ileride PostgreSQL desteği eklenecek
|
|
||||||
#pragma warning restore CS0162
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Warning("Veritabanı hazırlık kontrolü başarısız: {Error}", ex.Message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool SqlServerIsReady(string connectionString)
|
|
||||||
{
|
|
||||||
var csb = new SqlConnectionStringBuilder(connectionString);
|
|
||||||
var dbName = csb.InitialCatalog;
|
|
||||||
if (string.IsNullOrEmpty(dbName))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 1) master'a bağlan — DB varlığını kontrol et
|
|
||||||
var masterCsb = new SqlConnectionStringBuilder(connectionString)
|
|
||||||
{
|
|
||||||
InitialCatalog = "master",
|
|
||||||
ConnectTimeout = 8
|
|
||||||
};
|
|
||||||
|
|
||||||
using var masterConn = new SqlConnection(masterCsb.ConnectionString);
|
|
||||||
masterConn.Open();
|
|
||||||
|
|
||||||
using var dbCheck = new SqlCommand(
|
|
||||||
"SELECT COUNT(1) FROM sys.databases WHERE name = @n", masterConn);
|
|
||||||
dbCheck.Parameters.AddWithValue("@n", dbName);
|
|
||||||
if ((int)dbCheck.ExecuteScalar() == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 2) Hedef DB'ye bağlan — AbpRoles tablosunun varlığını kontrol et
|
|
||||||
csb.ConnectTimeout = 8;
|
|
||||||
using var dbConn = new SqlConnection(csb.ConnectionString);
|
|
||||||
dbConn.Open();
|
|
||||||
|
|
||||||
using var tableCheck = new SqlCommand(
|
|
||||||
"SELECT COUNT(1) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'AbpRoles'",
|
|
||||||
dbConn);
|
|
||||||
return (int)tableCheck.ExecuteScalar() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Minimal Kurulum Uygulaması
|
|
||||||
|
|
||||||
public static async Task<int> RunAsync(string[] args, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
Log.Warning("Veritabanı hazır değil — kurulum modu başlatılıyor.");
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
|
||||||
|
|
||||||
var extraOrigins = (configuration["App:CorsOrigins"] ?? "")
|
|
||||||
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
|
||||||
var baseDomain = configuration["App:BaseDomain"]?.Trim();
|
|
||||||
|
|
||||||
builder.Services.AddCors(o => o.AddPolicy("Setup", policy =>
|
|
||||||
policy.AllowAnyHeader()
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowCredentials()
|
|
||||||
.SetIsOriginAllowed(origin =>
|
|
||||||
{
|
|
||||||
if (!Uri.TryCreate(origin, UriKind.Absolute, out var uri)) return false;
|
|
||||||
var host = uri.Host.ToLowerInvariant();
|
|
||||||
|
|
||||||
if (host is "localhost" or "127.0.0.1" or "[::1]")
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(baseDomain))
|
|
||||||
{
|
|
||||||
var bd = baseDomain.ToLowerInvariant();
|
|
||||||
if (host == bd || host.EndsWith("." + bd))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var o in extraOrigins)
|
|
||||||
if (Uri.TryCreate(o, UriKind.Absolute, out var eo) &&
|
|
||||||
eo.Host.Equals(host, StringComparison.OrdinalIgnoreCase))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
})));
|
|
||||||
|
|
||||||
builder.Host.UseSerilog();
|
|
||||||
|
|
||||||
var app = builder.Build();
|
|
||||||
app.UseCors("Setup");
|
|
||||||
|
|
||||||
app.MapGet("/api/setup/status", (IConfiguration cfg) =>
|
|
||||||
Results.Ok(new { dbExists = DatabaseIsReady(cfg) }));
|
|
||||||
|
|
||||||
app.MapGet("/api/setup/migrate", async (IConfiguration cfg, IHostEnvironment env,
|
|
||||||
IHostApplicationLifetime lifetime, HttpContext ctx, CancellationToken ct) =>
|
|
||||||
{
|
|
||||||
ctx.Response.ContentType = "text/event-stream; charset=utf-8";
|
|
||||||
ctx.Response.Headers["Cache-Control"] = "no-cache, no-store";
|
|
||||||
ctx.Response.Headers["X-Accel-Buffering"] = "no";
|
|
||||||
await ctx.Response.Body.FlushAsync(ct);
|
|
||||||
|
|
||||||
async Task Send(string level, string message)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var payload = JsonSerializer.Serialize(new { level, message });
|
|
||||||
await ctx.Response.WriteAsync($"data: {payload}\n\n", ct);
|
|
||||||
await ctx.Response.Body.FlushAsync(ct);
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DatabaseIsReady(cfg))
|
|
||||||
{
|
|
||||||
await Send("warn", "Veritabanı zaten hazır. Migration atlanıyor.");
|
|
||||||
await Send("done", "Tamamlandı.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var migratorPath = cfg["Setup:MigratorPath"]
|
|
||||||
?? Path.GetFullPath(Path.Combine(env.ContentRootPath, "..", "Sozsoft.Platform.DbMigrator"));
|
|
||||||
|
|
||||||
await Send("info", "Veritabanı migration ve seed başlatılıyor...");
|
|
||||||
await Send("info", $"Migrator yolu: {migratorPath}");
|
|
||||||
|
|
||||||
var extraArgs = cfg["Setup:MigratorArgs"] ?? "--Seed=true";
|
|
||||||
|
|
||||||
string fileName;
|
|
||||||
string arguments;
|
|
||||||
string workingDirectory;
|
|
||||||
|
|
||||||
if (migratorPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && File.Exists(migratorPath))
|
|
||||||
{
|
|
||||||
// Doğrudan DLL yolu verilmiş — "--" separator YOK, doğrudan argüman
|
|
||||||
fileName = "dotnet";
|
|
||||||
arguments = $"\"{migratorPath}\" {extraArgs}";
|
|
||||||
workingDirectory = Path.GetDirectoryName(migratorPath)!;
|
|
||||||
}
|
|
||||||
else if (Directory.Exists(migratorPath))
|
|
||||||
{
|
|
||||||
// Klasör verilmiş — içinde publish edilmiş DLL var mı?
|
|
||||||
var dllFiles = Directory.GetFiles(migratorPath, "*.DbMigrator.dll", SearchOption.TopDirectoryOnly);
|
|
||||||
if (dllFiles.Length == 0)
|
|
||||||
dllFiles = Directory.GetFiles(migratorPath, "*Migrator*.dll", SearchOption.TopDirectoryOnly);
|
|
||||||
|
|
||||||
if (dllFiles.Length > 0)
|
|
||||||
{
|
|
||||||
// Publish çıktısı — SDK gerekmez, "--" separator YOK
|
|
||||||
fileName = "dotnet";
|
|
||||||
arguments = $"\"{dllFiles[0]}\" {extraArgs}";
|
|
||||||
workingDirectory = migratorPath;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Kaynak proje klasörü — geliştirme ortamı, "--" gerekli
|
|
||||||
fileName = "dotnet";
|
|
||||||
arguments = $"run --project \"{migratorPath}\" -- {extraArgs}";
|
|
||||||
workingDirectory = migratorPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await Send("error", $"Migrator yolu bulunamadı veya geçersiz: {migratorPath}");
|
|
||||||
await Send("done", "Hata ile sonlandı.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Send("info", $"Çalıştırılıyor: {fileName} {arguments}");
|
|
||||||
|
|
||||||
Process? process = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
process = new Process
|
|
||||||
{
|
|
||||||
StartInfo = new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = fileName,
|
|
||||||
Arguments = arguments,
|
|
||||||
RedirectStandardOutput = true,
|
|
||||||
RedirectStandardError = true,
|
|
||||||
UseShellExecute = false,
|
|
||||||
CreateNoWindow = true,
|
|
||||||
WorkingDirectory = workingDirectory,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
process.Start();
|
|
||||||
|
|
||||||
async Task ReadStream(StreamReader reader, string level)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (!reader.EndOfStream)
|
|
||||||
{
|
|
||||||
var line = await reader.ReadLineAsync(ct);
|
|
||||||
if (line != null) await Send(level, line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.WhenAll(
|
|
||||||
ReadStream(process.StandardOutput, "info"),
|
|
||||||
ReadStream(process.StandardError, "warn"));
|
|
||||||
|
|
||||||
await process.WaitForExitAsync(ct);
|
|
||||||
|
|
||||||
if (process.ExitCode == 0)
|
|
||||||
{
|
|
||||||
await Send("success", "Migration ve seed başarıyla tamamlandı.");
|
|
||||||
await Send("restart", "Uygulama sunucusu yeniden başlatılıyor...");
|
|
||||||
await Send("done", "Tamamlandı.");
|
|
||||||
|
|
||||||
_ = Task.Delay(1500).ContinueWith(_ => lifetime.StopApplication());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await Send("error", $"Migration başarısız. Çıkış kodu: {process.ExitCode}");
|
|
||||||
await Send("done", "Hata ile sonlandı.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
await Send("warn", "Migration isteği iptal edildi.");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await Send("error", $"Migration hatası: {ex.Message}");
|
|
||||||
await Send("done", "Hata ile sonlandı.");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
process?.Dispose();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.MapFallback(() => Results.StatusCode(503));
|
|
||||||
|
|
||||||
await app.RunAsync();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
using Sozsoft.Platform.DynamicServices;
|
using Sozsoft.Platform.DynamicServices;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
@ -25,24 +26,6 @@ public class Program
|
||||||
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? ""}.json", true)
|
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? ""}.json", true)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// ── Veritabanı varlık kontrolü ────────────────────────────────────────
|
|
||||||
// Serilog SQL sink kurulmadan ve ABP modülleri yüklenmeden önce yapılmalı.
|
|
||||||
// DB yoksa minimal setup uygulaması çalıştırılır.
|
|
||||||
Log.Logger = new LoggerConfiguration()
|
|
||||||
.MinimumLevel.Warning()
|
|
||||||
.WriteTo.Console()
|
|
||||||
.CreateLogger();
|
|
||||||
|
|
||||||
if (!SetupAppRunner.DatabaseIsReady(configuration))
|
|
||||||
{
|
|
||||||
var setupResult = await SetupAppRunner.RunAsync(args, configuration);
|
|
||||||
if (setupResult != 0)
|
|
||||||
return setupResult;
|
|
||||||
// Migration başarılı — DB artık hazır, tam ABP başlatmasına geç
|
|
||||||
Log.Warning("Migration tamamlandı — tam uygulama başlatılıyor.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Veritabanı mevcut — tam ABP başlatma ─────────────────────────────
|
|
||||||
|
|
||||||
var columnWriters = new Dictionary<string, ColumnWriterBase>
|
var columnWriters = new Dictionary<string, ColumnWriterBase>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,6 @@
|
||||||
"StringEncryption": {
|
"StringEncryption": {
|
||||||
"DefaultPassPhrase": "UQpiYfT79zRZ3yYH"
|
"DefaultPassPhrase": "UQpiYfT79zRZ3yYH"
|
||||||
},
|
},
|
||||||
"Setup": {
|
|
||||||
"MigratorPath": "/srv/Sozsoft.Platform.DbMigrator",
|
|
||||||
"MigratorArgs": "--Seed=true"
|
|
||||||
},
|
|
||||||
"Serilog": {
|
"Serilog": {
|
||||||
"MinimumLevel": {
|
"MinimumLevel": {
|
||||||
"Default": "Information"
|
"Default": "Information"
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Volo.Abp.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace Sozsoft.Platform.Controllers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tam ABP uygulaması çalışırken setup durumunu döner.
|
|
||||||
/// SetupAppRunner'ın aynı endpoint'i DB hazır olmadığında dbExists=false döner.
|
|
||||||
/// </summary>
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/setup")]
|
|
||||||
public class SetupController : AbpControllerBase
|
|
||||||
{
|
|
||||||
[HttpGet("status")]
|
|
||||||
public IActionResult Status() => Ok(new { dbExists = true });
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ComponentInfo } from '../../proxy/developerKit/componentInfo';
|
import { ComponentInfo } from '../../proxy/developerKit/componentInfo';
|
||||||
import { Button } from '../ui';
|
|
||||||
|
|
||||||
interface ComponentSelectorProps {
|
interface ComponentSelectorProps {
|
||||||
components: ComponentInfo[];
|
components: ComponentInfo[];
|
||||||
|
|
@ -21,15 +20,13 @@ const ComponentSelector: React.FC<ComponentSelectorProps> = ({
|
||||||
<label className="block text-sm font-medium text-gray-700">
|
<label className="block text-sm font-medium text-gray-700">
|
||||||
Select Component
|
Select Component
|
||||||
</label>
|
</label>
|
||||||
<Button
|
<button
|
||||||
variant='solid'
|
|
||||||
size="xs"
|
|
||||||
onClick={onRefresh}
|
onClick={onRefresh}
|
||||||
className="px-3 py-1 bg-blue-500 text-white text-xs rounded hover:bg-blue-600 transition-colors"
|
className="px-3 py-1 bg-blue-500 text-white text-xs rounded hover:bg-blue-600 transition-colors"
|
||||||
title="Refresh component list"
|
title="Refresh component list"
|
||||||
>
|
>
|
||||||
Refresh
|
Refresh
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<select
|
<select
|
||||||
value={selectedComponentId || ''}
|
value={selectedComponentId || ''}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { useState, useEffect } from "react";
|
||||||
import TailwindModal from "./TailwindModal";
|
import TailwindModal from "./TailwindModal";
|
||||||
import { ComponentInfo, HookInfo, PropertyInfo } from "../../proxy/developerKit/componentInfo";
|
import { ComponentInfo, HookInfo, PropertyInfo } from "../../proxy/developerKit/componentInfo";
|
||||||
import { getComponentDefinition } from "./data/componentDefinitions";
|
import { getComponentDefinition } from "./data/componentDefinitions";
|
||||||
import { Button } from "../ui";
|
|
||||||
|
|
||||||
interface PropertyPanelProps {
|
interface PropertyPanelProps {
|
||||||
selectedComponent: ComponentInfo | null;
|
selectedComponent: ComponentInfo | null;
|
||||||
|
|
@ -526,9 +525,7 @@ const PropertyPanel: React.FC<PropertyPanelProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Sil Butonu */}
|
{/* Sil Butonu */}
|
||||||
<Button
|
<button
|
||||||
variant="solid"
|
|
||||||
size="xs"
|
|
||||||
className="mr-2 px-3 py-1 rounded bg-red-500 text-white hover:bg-red-600 transition-colors text-sm"
|
className="mr-2 px-3 py-1 rounded bg-red-500 text-white hover:bg-red-600 transition-colors text-sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (selectedComponent) {
|
if (selectedComponent) {
|
||||||
|
|
@ -544,32 +541,28 @@ const PropertyPanel: React.FC<PropertyPanelProps> = ({
|
||||||
title="Komponenti Sil"
|
title="Komponenti Sil"
|
||||||
>
|
>
|
||||||
Sil
|
Sil
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer Action Buttons - her iki tabda da sabit */}
|
{/* Footer Action Buttons - her iki tabda da sabit */}
|
||||||
<div className="p-4 border-t">
|
<div className="p-4 border-t">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<button
|
||||||
size="xs"
|
|
||||||
variant="solid"
|
|
||||||
onClick={
|
onClick={
|
||||||
activeTab === "props"
|
activeTab === "props"
|
||||||
? handleApplyPropChanges
|
? handleApplyPropChanges
|
||||||
: handleApplyHookChanges
|
: handleApplyHookChanges
|
||||||
}
|
}
|
||||||
disabled={activeTab === "props" ? !hasChanges : !hasHookChanges}
|
disabled={activeTab === "props" ? !hasChanges : !hasHookChanges}
|
||||||
className={`flex-1 rounded-md font-medium transition-colors ${
|
className={`flex-1 px-4 py-2 rounded-md font-medium transition-colors ${
|
||||||
(activeTab === "props" ? hasChanges : hasHookChanges)
|
(activeTab === "props" ? hasChanges : hasHookChanges)
|
||||||
? "bg-green-500 text-white hover:bg-green-600"
|
? "bg-green-500 text-white hover:bg-green-600"
|
||||||
: "bg-gray-300 text-gray-500 cursor-not-allowed"
|
: "bg-gray-300 text-gray-500 cursor-not-allowed"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
Uygula
|
Uygula
|
||||||
</Button>
|
</button>
|
||||||
<Button
|
<button
|
||||||
size="xs"
|
|
||||||
variant="default"
|
|
||||||
onClick={
|
onClick={
|
||||||
activeTab === "props"
|
activeTab === "props"
|
||||||
? handleResetChanges
|
? handleResetChanges
|
||||||
|
|
@ -579,20 +572,20 @@ const PropertyPanel: React.FC<PropertyPanelProps> = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
disabled={activeTab === "props" ? !hasChanges : !hasHookChanges}
|
disabled={activeTab === "props" ? !hasChanges : !hasHookChanges}
|
||||||
className={`flex-1 rounded-md font-medium transition-colors ${
|
className={`flex-1 px-4 py-2 rounded-md font-medium transition-colors ${
|
||||||
(activeTab === "props" ? hasChanges : hasHookChanges)
|
(activeTab === "props" ? hasChanges : hasHookChanges)
|
||||||
? "bg-red-500 text-white hover:bg-red-600"
|
? "bg-red-500 text-white hover:bg-red-600"
|
||||||
: "bg-gray-300 text-gray-500 cursor-not-allowed"
|
: "bg-gray-300 text-gray-500 cursor-not-allowed"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
İptal
|
İptal
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
{activeTab === "props" && (
|
{activeTab === "props" && (
|
||||||
<div className="flex-1 text-black overflow-y-auto p-4 max-h-[calc(100vh-200px)]">
|
<div className="flex-1 overflow-y-auto p-4 max-h-[calc(100vh-200px)]">
|
||||||
<h3 className="text-md font-medium text-gray-800 mb-4">Properties</h3>
|
<h3 className="text-md font-medium text-gray-800 mb-4">Properties</h3>
|
||||||
{/* Properties */}
|
{/* Properties */}
|
||||||
{properties.length > 0 && (
|
{properties.length > 0 && (
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { searchTailwindClasses, TAILWIND_CLASSES } from './data/tailwindClasses';
|
import { searchTailwindClasses, TAILWIND_CLASSES } from './data/tailwindClasses';
|
||||||
import { Button } from '../ui';
|
|
||||||
|
|
||||||
interface TailwindModalProps {
|
interface TailwindModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
|
@ -62,7 +61,7 @@ const TailwindModal: React.FC<TailwindModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Search and Filter */}
|
{/* Search and Filter */}
|
||||||
<div className="p-4 border-b bg-gray-50 text-black">
|
<div className="p-4 border-b bg-gray-50">
|
||||||
<div className="flex gap-4 mb-4">
|
<div className="flex gap-4 mb-4">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -107,15 +106,25 @@ const TailwindModal: React.FC<TailwindModalProps> = ({
|
||||||
key={`${className}-${index}`}
|
key={`${className}-${index}`}
|
||||||
className="group relative"
|
className="group relative"
|
||||||
>
|
>
|
||||||
<Button
|
<button
|
||||||
variant='default'
|
|
||||||
onClick={() => handleClassSelect(className)}
|
onClick={() => handleClassSelect(className)}
|
||||||
className="w-full text-left px-3 py-2 text-sm bg-gray-100 hover:bg-blue-100 rounded border hover:border-blue-300 transition-colors"
|
className="w-full text-left px-3 py-2 text-sm bg-gray-100 hover:bg-blue-100 rounded border hover:border-blue-300 transition-colors"
|
||||||
>
|
>
|
||||||
<span className="font-mono text-xs text-gray-600">
|
<span className="font-mono text-xs text-gray-600">
|
||||||
{className}
|
{className}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
|
{/* Add to current value button */}
|
||||||
|
{currentValue && (
|
||||||
|
<button
|
||||||
|
onClick={() => handleAddToCurrentValue(className)}
|
||||||
|
className="absolute top-1 right-1 w-6 h-6 bg-blue-500 text-white rounded-full text-xs opacity-0 group-hover:opacity-100 transition-opacity hover:bg-blue-600"
|
||||||
|
title="Add to current value"
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -128,20 +137,18 @@ const TailwindModal: React.FC<TailwindModalProps> = ({
|
||||||
{filteredClasses.length} classes found
|
{filteredClasses.length} classes found
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<button
|
||||||
variant='default'
|
|
||||||
onClick={() => onSelectClass('')}
|
onClick={() => onSelectClass('')}
|
||||||
className="px-4 py-2 bg-gray-200 text-gray-700 rounded hover:bg-gray-300 transition-colors"
|
className="px-4 py-2 bg-gray-200 text-gray-700 rounded hover:bg-gray-300 transition-colors"
|
||||||
>
|
>
|
||||||
Clear
|
Clear
|
||||||
</Button>
|
</button>
|
||||||
<Button
|
<button
|
||||||
variant='solid'
|
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors"
|
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors"
|
||||||
>
|
>
|
||||||
Close
|
Close
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -283,7 +283,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
|
||||||
<thead className="bg-slate-100 sticky top-0">
|
<thead className="bg-slate-100 sticky top-0">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 uppercase">
|
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 uppercase">
|
||||||
{translate('::App.Listform.ListformField.Column')}
|
{translate('::App.Listforms.ImportManager.Column')}
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 uppercase">
|
<th className="px-4 py-2 text-left text-xs font-medium text-slate-500 uppercase">
|
||||||
{translate('::ListForms.ListFormEdit.Type')}
|
{translate('::ListForms.ListFormEdit.Type')}
|
||||||
|
|
|
||||||
|
|
@ -60,11 +60,6 @@ const Layout = () => {
|
||||||
}, [routes, currentPath])
|
}, [routes, currentPath])
|
||||||
|
|
||||||
const AppLayout = useMemo(() => {
|
const AppLayout = useMemo(() => {
|
||||||
// 0) Setup path ise minimal blank layout
|
|
||||||
if (currentPath === '/setup') {
|
|
||||||
return layouts[LAYOUT_TYPE_BLANK]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1) Admin path ise, route bulunmasa bile admin layout'u göster
|
// 1) Admin path ise, route bulunmasa bile admin layout'u göster
|
||||||
if (isAdminPath) {
|
if (isAdminPath) {
|
||||||
return layouts[layoutType]
|
return layouts[layoutType]
|
||||||
|
|
@ -83,7 +78,7 @@ const Layout = () => {
|
||||||
return AuthLayout
|
return AuthLayout
|
||||||
}
|
}
|
||||||
return PublicLayout
|
return PublicLayout
|
||||||
}, [isAdminPath, route, layoutType, authenticated, currentPath])
|
}, [isAdminPath, route, layoutType, authenticated])
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ export const Cart: React.FC<CartProps> = ({
|
||||||
<div className="border-t border-gray-200 p-6">
|
<div className="border-t border-gray-200 p-6">
|
||||||
<div className="flex justify-between items-center mb-4">
|
<div className="flex justify-between items-center mb-4">
|
||||||
<span className="text-lg font-semibold text-gray-900">
|
<span className="text-lg font-semibold text-gray-900">
|
||||||
{translate('::App.Listform.ListformField.Total')}
|
{translate('::Public.payment.summary.total')}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xl font-bold text-blue-600">
|
<span className="text-xl font-bold text-blue-600">
|
||||||
{formatPrice(cartState.total)}
|
{formatPrice(cartState.total)}
|
||||||
|
|
|
||||||
|
|
@ -436,7 +436,7 @@ export const PaymentForm: React.FC<PaymentFormProps> = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex justify-between text-base font-bold pt-2 text-gray-900">
|
<div className="flex justify-between text-base font-bold pt-2 text-gray-900">
|
||||||
<span>{translate('::App.Listform.ListformField.Total')}</span>
|
<span>{translate('::Public.payment.summary.total')}</span>
|
||||||
<span className="text-blue-600">{formatPrice(finalTotal)}</span>
|
<span className="text-blue-600">{formatPrice(finalTotal)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -449,7 +449,7 @@ export const PaymentForm: React.FC<PaymentFormProps> = ({
|
||||||
className="flex items-center px-6 py-3 border border-gray-300 text-gray-700 rounded-lg"
|
className="flex items-center px-6 py-3 border border-gray-300 text-gray-700 rounded-lg"
|
||||||
>
|
>
|
||||||
<FaArrowLeft className="w-4 h-4 mr-2" />
|
<FaArrowLeft className="w-4 h-4 mr-2" />
|
||||||
{translate('::Back')}
|
{translate('::Public.payment.buttons.back')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ export const ProductCard: React.FC<ProductCardProps> = ({
|
||||||
</div>
|
</div>
|
||||||
{globalPeriod > 1 && (
|
{globalPeriod > 1 && (
|
||||||
<div className="text-lg font-semibold text-blue-600 mt-1">
|
<div className="text-lg font-semibold text-blue-600 mt-1">
|
||||||
{translate('::App.Listform.ListformField.Total')} {formatPrice(getTotalPrice())}
|
{translate('::Public.payment.summary.total')} {formatPrice(getTotalPrice())}
|
||||||
<span className="text-sm font-normal text-gray-500 ml-1">{getPeriodText()}</span>
|
<span className="text-sm font-normal text-gray-500 ml-1">{getPeriodText()}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -479,7 +479,7 @@ export const TenantForm: React.FC<TenantFormProps> = ({ onSubmit }) => {
|
||||||
className="flex items-center px-6 py-3 border border-gray-300 text-gray-700 rounded-lg"
|
className="flex items-center px-6 py-3 border border-gray-300 text-gray-700 rounded-lg"
|
||||||
>
|
>
|
||||||
<FaArrowLeft className="w-4 h-4 mr-2" />
|
<FaArrowLeft className="w-4 h-4 mr-2" />
|
||||||
{translate('::Back')}
|
{translate('::Public.payment.buttons.back')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import Tooltip from '@/components/ui/Tooltip'
|
import Tooltip from '@/components/ui/Tooltip'
|
||||||
import { AI_ASSISTANT } from '@/constants/permission.constant'
|
|
||||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { usePermission } from '@/utils/hooks/usePermission'
|
import { usePermission } from '@/utils/hooks/usePermission'
|
||||||
|
|
@ -11,14 +10,14 @@ const AiAssistant = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { checkPermissions } = usePermission()
|
const { checkPermissions } = usePermission()
|
||||||
|
|
||||||
const canViewAi = checkPermissions([AI_ASSISTANT])
|
const canViewAi = checkPermissions(['App.AiBot.Asistant'])
|
||||||
|
|
||||||
if (!canViewAi) {
|
if (!canViewAi) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip title={translate('::' + AI_ASSISTANT)}>
|
<Tooltip title={translate('::App.AiBot.Asistant')}>
|
||||||
<div
|
<div
|
||||||
onClick={() => navigate(ROUTES_ENUM.protected.admin.ai)}
|
onClick={() => navigate(ROUTES_ENUM.protected.admin.ai)}
|
||||||
className="flex items-center justify-center text-2xl m-1 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer transition-colors duration-200"
|
className="flex items-center justify-center text-2xl m-1 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer transition-colors duration-200"
|
||||||
|
|
|
||||||
|
|
@ -8,42 +8,17 @@ import { useSetting } from '@/utils/hooks/useSetting'
|
||||||
import useTabFocus from '@/utils/hooks/useTabFocus'
|
import useTabFocus from '@/utils/hooks/useTabFocus'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { useNavigate, useLocation } from 'react-router-dom'
|
|
||||||
import { getSetupStatus } from '@/services/setup.service'
|
|
||||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
|
||||||
|
|
||||||
let didInit = false
|
let didInit = false
|
||||||
|
|
||||||
const Theme = (props: CommonProps) => {
|
const Theme = (props: CommonProps) => {
|
||||||
// ABP App Config'i uygulama acilirken al
|
// ABP App Config'i uygulama acilirken al
|
||||||
const { getConfig } = useStoreActions((a) => a.abpConfig)
|
const { getConfig } = useStoreActions((a) => a.abpConfig)
|
||||||
const { setSetupMode } = useStoreActions((a) => a.base.common)
|
|
||||||
const navigate = useNavigate()
|
|
||||||
const location = useLocation()
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!didInit) {
|
if (!didInit) {
|
||||||
didInit = true
|
didInit = true
|
||||||
|
getConfig(false)
|
||||||
// Direkt /setup'a gelindiyse — hemen setupMode=true yap (loading gate açılsın)
|
|
||||||
if (location.pathname === ROUTES_ENUM.setup) {
|
|
||||||
setSetupMode(true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Veritabanı var mı kontrol et; yoksa setup sayfasına yönlendir
|
|
||||||
getSetupStatus()
|
|
||||||
.then((res) => {
|
|
||||||
if (!res.data.dbExists) {
|
|
||||||
setSetupMode(true)
|
|
||||||
navigate(ROUTES_ENUM.setup, { replace: true })
|
|
||||||
} else {
|
|
||||||
getConfig(false)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
getConfig(false)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1 @@
|
||||||
export const GLOBAL_SEARCH = 'App.Definitions.GlobalSearch'
|
export const GLOBAL_SEARCH = 'App.Settings.GlobalSearch'
|
||||||
export const AI_ASSISTANT = 'App.Definitions.AiBot.Asistant'
|
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,6 @@ export interface ColumnFilterDto {
|
||||||
export interface ColumnFormatDto extends AuditedEntityDto<string> {
|
export interface ColumnFormatDto extends AuditedEntityDto<string> {
|
||||||
fieldName?: string
|
fieldName?: string
|
||||||
captionName?: string
|
captionName?: string
|
||||||
placeHolder?: string
|
|
||||||
readOnly: boolean
|
readOnly: boolean
|
||||||
visible: boolean
|
visible: boolean
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
|
|
@ -445,7 +444,6 @@ export interface GridEditingDto {
|
||||||
allowDeleting: boolean
|
allowDeleting: boolean
|
||||||
allowAllDeleting: boolean
|
allowAllDeleting: boolean
|
||||||
allowAdding: boolean
|
allowAdding: boolean
|
||||||
allowDuplicate: boolean
|
|
||||||
useIcons: boolean
|
useIcons: boolean
|
||||||
confirmDelete: boolean
|
confirmDelete: boolean
|
||||||
newRowPosition?: NewRowPosition
|
newRowPosition?: NewRowPosition
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// DynamicRouter.tsx
|
// DynamicRouter.tsx
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Routes, Route, Navigate, useLocation } from 'react-router-dom'
|
import { Routes, Route, Navigate } from 'react-router-dom'
|
||||||
import { mapDynamicRoutes } from './dynamicRouteLoader'
|
import { mapDynamicRoutes } from './dynamicRouteLoader'
|
||||||
import { useDynamicRoutes } from './dynamicRoutesContext'
|
import { useDynamicRoutes } from './dynamicRoutesContext'
|
||||||
import { useComponents } from '@/contexts/ComponentContext'
|
import { useComponents } from '@/contexts/ComponentContext'
|
||||||
|
|
@ -13,18 +13,15 @@ import { hasSubdomain } from '@/utils/subdomain'
|
||||||
// AccessDenied ve NotFound'u dinamiklikten çıkarıyoruz
|
// AccessDenied ve NotFound'u dinamiklikten çıkarıyoruz
|
||||||
const AccessDenied = React.lazy(() => import('@/views/AccessDenied'))
|
const AccessDenied = React.lazy(() => import('@/views/AccessDenied'))
|
||||||
const NotFound = React.lazy(() => import('@/views/NotFound'))
|
const NotFound = React.lazy(() => import('@/views/NotFound'))
|
||||||
const DatabaseSetup = React.lazy(() => import('@/views/setup/DatabaseSetup'))
|
|
||||||
|
|
||||||
export const DynamicRouter: React.FC = () => {
|
export const DynamicRouter: React.FC = () => {
|
||||||
const { routes, loading, error } = useDynamicRoutes()
|
const { routes, loading, error } = useDynamicRoutes()
|
||||||
const { registeredComponents, renderComponent, isComponentRegistered } = useComponents()
|
const { registeredComponents, renderComponent, isComponentRegistered } = useComponents()
|
||||||
const location = useLocation()
|
|
||||||
|
|
||||||
const dynamicRoutes = React.useMemo(() => mapDynamicRoutes(routes), [routes])
|
const dynamicRoutes = React.useMemo(() => mapDynamicRoutes(routes), [routes])
|
||||||
|
|
||||||
// /setup path'inde loading bekleme — setup route her zaman erişilebilir olmalı
|
if (loading) return <div>Loading...</div>
|
||||||
if (loading && location.pathname !== '/setup') return <div>Loading...</div>
|
if (error) return <div>Hata: {error}</div>
|
||||||
if (error && location.pathname !== '/setup') return <div>Hata: {error}</div>
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
|
|
@ -129,16 +126,6 @@ export const DynamicRouter: React.FC = () => {
|
||||||
</React.Suspense>
|
</React.Suspense>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* İlk kurulum — veritabanı mevcut değilse gösterilir */}
|
|
||||||
<Route
|
|
||||||
path={ROUTES_ENUM.setup}
|
|
||||||
element={
|
|
||||||
<React.Suspense fallback={<div>Loading...</div>}>
|
|
||||||
<DatabaseSetup />
|
|
||||||
</React.Suspense>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Routes>
|
</Routes>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ export const useDynamicRoutes = () => {
|
||||||
|
|
||||||
export const DynamicRoutesProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
export const DynamicRoutesProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
const extraProperties = useStoreState((state) => state.abpConfig?.config?.extraProperties)
|
const extraProperties = useStoreState((state) => state.abpConfig?.config?.extraProperties)
|
||||||
const setupMode = useStoreState((state) => state.base.common.setupMode)
|
|
||||||
const [routes, setRoutes] = useState<RouteDto[]>([])
|
const [routes, setRoutes] = useState<RouteDto[]>([])
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|
@ -49,12 +48,8 @@ export const DynamicRoutesProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (extraProperties) {
|
if (extraProperties) {
|
||||||
loadRoutesFromConfig()
|
loadRoutesFromConfig()
|
||||||
} else if (setupMode) {
|
|
||||||
// Veritabanı mevcut değil — setup modunda loading'i kapat
|
|
||||||
setLoading(false)
|
|
||||||
setRoutes([])
|
|
||||||
}
|
}
|
||||||
}, [extraProperties, setupMode])
|
}, [extraProperties])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicRoutesContext.Provider value={{ routes, loading, error, reload: loadRoutesFromConfig }}>
|
<DynamicRoutesContext.Provider value={{ routes, loading, error, reload: loadRoutesFromConfig }}>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
export const ROUTES_ENUM = {
|
export const ROUTES_ENUM = {
|
||||||
setup: '/setup',
|
|
||||||
public: {
|
public: {
|
||||||
home: '/home',
|
home: '/home',
|
||||||
about: '/about',
|
about: '/about',
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,10 @@ import {
|
||||||
} from '../proxy/config/models'
|
} from '../proxy/config/models'
|
||||||
import apiService from './api.service'
|
import apiService from './api.service'
|
||||||
|
|
||||||
export const applicationConfigurationUrl = (includeLocalizationResources: boolean) =>
|
|
||||||
`/api/abp/application-configuration?includeLocalizationResources=${includeLocalizationResources}`
|
|
||||||
|
|
||||||
export const getAppConfig = (includeLocalizationResources: boolean) =>
|
export const getAppConfig = (includeLocalizationResources: boolean) =>
|
||||||
apiService.fetchData<ApplicationConfigurationDto>({
|
apiService.fetchData<ApplicationConfigurationDto>({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: applicationConfigurationUrl(includeLocalizationResources),
|
url: `/api/abp/application-configuration?includeLocalizationResources=${includeLocalizationResources}`,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getLocalizations = ({
|
export const getLocalizations = ({
|
||||||
|
|
|
||||||
573
ui/src/services/classroom/signalr.tsx
Normal file
573
ui/src/services/classroom/signalr.tsx
Normal file
|
|
@ -0,0 +1,573 @@
|
||||||
|
import { ClassroomAttendanceDto, ClassroomChatDto, HandRaiseDto } from '@/proxy/classroom/models'
|
||||||
|
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||||
|
import { store } from '@/store/store'
|
||||||
|
import * as signalR from '@microsoft/signalr'
|
||||||
|
import { toast } from '@/components/ui'
|
||||||
|
import Notification from '@/components/ui/Notification'
|
||||||
|
|
||||||
|
export class SignalRService {
|
||||||
|
private connection!: signalR.HubConnection
|
||||||
|
private isConnected: boolean = false
|
||||||
|
private currentSessionId?: string
|
||||||
|
private isKicked: boolean = false
|
||||||
|
|
||||||
|
private onAttendanceUpdate?: (record: ClassroomAttendanceDto) => void
|
||||||
|
private onParticipantJoined?: (
|
||||||
|
userId: string,
|
||||||
|
name: string,
|
||||||
|
isTeacher: boolean,
|
||||||
|
isActive: boolean,
|
||||||
|
) => void
|
||||||
|
private onParticipantLeft?: (payload: {
|
||||||
|
userId: string
|
||||||
|
sessionId: string
|
||||||
|
userName: string
|
||||||
|
}) => void
|
||||||
|
private onChatMessage?: (message: ClassroomChatDto) => void
|
||||||
|
private onParticipantMuted?: (userId: string, isMuted: boolean) => void
|
||||||
|
private onHandRaiseReceived?: (studentId: string) => void
|
||||||
|
private onHandRaiseDismissed?: (studentId: string) => void
|
||||||
|
private onOfferReceived?: (fromUserId: string, offer: RTCSessionDescriptionInit) => void
|
||||||
|
private onAnswerReceived?: (fromUserId: string, answer: RTCSessionDescriptionInit) => void
|
||||||
|
private onIceCandidateReceived?: (fromUserId: string, candidate: RTCIceCandidateInit) => void
|
||||||
|
private onForceCleanup?: () => void
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
const { auth } = store.getState()
|
||||||
|
|
||||||
|
this.connection = new signalR.HubConnectionBuilder()
|
||||||
|
.withUrl(`${import.meta.env.VITE_API_URL}/classroomhub`, {
|
||||||
|
accessTokenFactory: () => auth.session.token || '',
|
||||||
|
})
|
||||||
|
.configureLogging(signalR.LogLevel.Information)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
this.setupEventHandlers()
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupEventHandlers() {
|
||||||
|
if (!this.connection) return
|
||||||
|
|
||||||
|
this.connection.on('AttendanceUpdated', (record: ClassroomAttendanceDto) => {
|
||||||
|
this.onAttendanceUpdate?.(record)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.on(
|
||||||
|
'ParticipantJoined',
|
||||||
|
(userId: string, name: string, isTeacher: boolean, isActive: boolean) => {
|
||||||
|
this.onParticipantJoined?.(userId, name, isTeacher, isActive)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
this.connection.on(
|
||||||
|
'ParticipantLeft',
|
||||||
|
(payload: { userId: string; sessionId: string; userName: string }) => {
|
||||||
|
this.onParticipantLeft?.(payload)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
this.connection.on('ChatMessage', (message: any) => {
|
||||||
|
this.onChatMessage?.(message)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.on('ParticipantMuted', (userId: string, isMuted: boolean) => {
|
||||||
|
this.onParticipantMuted?.(userId, isMuted)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.on('HandRaiseReceived', (payload: any) => {
|
||||||
|
this.onHandRaiseReceived?.(payload.studentId)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.on('HandRaiseDismissed', (payload: any) => {
|
||||||
|
this.onHandRaiseDismissed?.(payload.studentId)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.on('ReceiveOffer', (fromUserId: string, offer: RTCSessionDescriptionInit) => {
|
||||||
|
this.onOfferReceived?.(fromUserId, offer)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.on('ReceiveAnswer', (fromUserId: string, answer: RTCSessionDescriptionInit) => {
|
||||||
|
this.onAnswerReceived?.(fromUserId, answer)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.on(
|
||||||
|
'ReceiveIceCandidate',
|
||||||
|
(fromUserId: string, candidate: RTCIceCandidateInit) => {
|
||||||
|
this.onIceCandidateReceived?.(fromUserId, candidate)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
this.connection.onreconnected(async () => {
|
||||||
|
this.isConnected = true
|
||||||
|
toast.push(<Notification title="🔄 Bağlantı tekrar kuruldu" type="success" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.currentSessionId && store.getState().auth.user) {
|
||||||
|
const u = store.getState().auth.user
|
||||||
|
await this.joinClass(this.currentSessionId, u.id, u.name, u.role === 'teacher', true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.onclose(async () => {
|
||||||
|
if (this.isKicked) {
|
||||||
|
toast.push(
|
||||||
|
<Notification title="⚠️ Bağlantı koptu, yeniden bağlanılıyor..." type="warning" />,
|
||||||
|
{ placement: 'top-end' },
|
||||||
|
)
|
||||||
|
this.isConnected = false
|
||||||
|
this.currentSessionId = undefined
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isConnected = false
|
||||||
|
try {
|
||||||
|
if (this.currentSessionId) {
|
||||||
|
await this.connection.invoke('LeaveClass', this.currentSessionId)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.currentSessionId = undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.on('Error', (message: string) => {
|
||||||
|
toast.push(<Notification title={`❌ Hata: ${message}`} type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.on('Warning', (message: string) => {
|
||||||
|
toast.push(<Notification title={`⚠️ Uyarı: ${message}`} type="warning" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.on('Info', (message: string) => {
|
||||||
|
toast.push(<Notification title={`ℹ️ Bilgi: ${message}`} type="info" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.onreconnecting(() => {
|
||||||
|
if (this.isKicked) {
|
||||||
|
toast.push(
|
||||||
|
<Notification
|
||||||
|
title="❌ Sınıftan çıkarıldığınız için yeniden bağlanma engellendi"
|
||||||
|
type="danger"
|
||||||
|
/>,
|
||||||
|
)
|
||||||
|
this.connection.stop()
|
||||||
|
throw new Error('Reconnect blocked after kick')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.on('ForceDisconnect', async (message: string) => {
|
||||||
|
this.isKicked = true
|
||||||
|
toast.push(<Notification title={`❌ Sınıftan çıkarıldınız: ${message}`} type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.onForceCleanup) {
|
||||||
|
this.onForceCleanup()
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.connection.stop()
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
this.isConnected = false
|
||||||
|
|
||||||
|
if (this.currentSessionId && store.getState().auth.user) {
|
||||||
|
this.onParticipantLeft?.({
|
||||||
|
userId: store.getState().auth.user.id,
|
||||||
|
sessionId: this.currentSessionId,
|
||||||
|
userName: store.getState().auth.user.name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentSessionId = undefined
|
||||||
|
window.location.href = ROUTES_ENUM.protected.coordinator.classroom.classes
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const startPromise = this.connection.start()
|
||||||
|
const timeout = new Promise((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error('Bağlantı zaman aşımına uğradı')), 10000),
|
||||||
|
)
|
||||||
|
|
||||||
|
await Promise.race([startPromise, timeout])
|
||||||
|
this.isConnected = true
|
||||||
|
toast.push(<Notification title="✅ Bağlantı kuruldu" type="success" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
toast.push(
|
||||||
|
<Notification
|
||||||
|
title="⚠️ Sunucuya bağlanılamadı. Lütfen sayfayı yenileyin veya internet bağlantınızı kontrol edin."
|
||||||
|
type="danger"
|
||||||
|
/>,
|
||||||
|
{ placement: 'top-end' },
|
||||||
|
)
|
||||||
|
this.isConnected = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async joinClass(
|
||||||
|
sessionId: string,
|
||||||
|
userId: string,
|
||||||
|
userName: string,
|
||||||
|
isTeacher: boolean,
|
||||||
|
isActive: boolean,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
toast.push(
|
||||||
|
<Notification
|
||||||
|
title="⚠️ Bağlantı yok. Sınıfa katılmadan önce bağlantıyı kontrol edin."
|
||||||
|
type="warning"
|
||||||
|
/>,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentSessionId = sessionId
|
||||||
|
try {
|
||||||
|
await this.connection.invoke('JoinClass', sessionId, userId, userName, isTeacher, isActive)
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="❌ Sınıfa katılamadı" type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async leaveClass(sessionId: string): Promise<void> {
|
||||||
|
const { auth } = store.getState()
|
||||||
|
|
||||||
|
if (!this.isConnected) {
|
||||||
|
this.onParticipantLeft?.({ userId: auth.user.id, sessionId, userName: auth.user.name })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.connection.invoke('LeaveClass', sessionId)
|
||||||
|
this.currentSessionId = undefined
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="⚠️ Çıkış başarısız" type="warning" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendChatMessage(
|
||||||
|
sessionId: string,
|
||||||
|
senderId: string,
|
||||||
|
senderName: string,
|
||||||
|
message: string,
|
||||||
|
isTeacher: boolean,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
const chatMessage: ClassroomChatDto = {
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
sessionId,
|
||||||
|
senderId,
|
||||||
|
senderName,
|
||||||
|
message,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
isTeacher,
|
||||||
|
messageType: 'public',
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
this.onChatMessage?.(chatMessage)
|
||||||
|
}, 100)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.connection.invoke(
|
||||||
|
'SendChatMessage',
|
||||||
|
sessionId,
|
||||||
|
senderId,
|
||||||
|
senderName,
|
||||||
|
message,
|
||||||
|
isTeacher,
|
||||||
|
'public',
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="❌ Mesaj gönderilemedi" type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendPrivateMessage(
|
||||||
|
sessionId: string,
|
||||||
|
senderId: string,
|
||||||
|
senderName: string,
|
||||||
|
message: string,
|
||||||
|
recipientId: string,
|
||||||
|
recipientName: string,
|
||||||
|
isTeacher: boolean,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
const chatMessage: ClassroomChatDto = {
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
sessionId,
|
||||||
|
senderId,
|
||||||
|
senderName,
|
||||||
|
message,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
isTeacher,
|
||||||
|
recipientId,
|
||||||
|
recipientName,
|
||||||
|
messageType: 'private',
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
this.onChatMessage?.(chatMessage)
|
||||||
|
}, 100)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.connection.invoke(
|
||||||
|
'SendPrivateMessage',
|
||||||
|
sessionId,
|
||||||
|
senderId,
|
||||||
|
senderName,
|
||||||
|
message,
|
||||||
|
recipientId,
|
||||||
|
recipientName,
|
||||||
|
isTeacher,
|
||||||
|
'private',
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="❌ Özel mesaj gönderilemedi" type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendAnnouncement(
|
||||||
|
sessionId: string,
|
||||||
|
senderId: string,
|
||||||
|
senderName: string,
|
||||||
|
message: string,
|
||||||
|
isTeacher: boolean,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
const chatMessage: ClassroomChatDto = {
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
sessionId,
|
||||||
|
senderId,
|
||||||
|
senderName,
|
||||||
|
message,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
isTeacher,
|
||||||
|
messageType: 'announcement',
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
this.onChatMessage?.(chatMessage)
|
||||||
|
}, 100)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.connection.invoke(
|
||||||
|
'SendAnnouncement',
|
||||||
|
sessionId,
|
||||||
|
senderId,
|
||||||
|
senderName,
|
||||||
|
message,
|
||||||
|
isTeacher,
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="❌ Duyuru gönderilemedi" type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async muteParticipant(
|
||||||
|
sessionId: string,
|
||||||
|
userId: string,
|
||||||
|
isMuted: boolean,
|
||||||
|
isTeacher: boolean,
|
||||||
|
): Promise<void> {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.onParticipantMuted?.(userId, isMuted)
|
||||||
|
}, 100)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.connection.invoke('MuteParticipant', sessionId, userId, isMuted, isTeacher)
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="⚠️ Katılımcı susturulamadı" type="warning" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async raiseHand(sessionId: string, studentId: string, studentName: string): Promise<void> {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.onHandRaiseReceived?.(studentId)
|
||||||
|
}, 100)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.connection.invoke('RaiseHand', sessionId, studentId, studentName)
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="❌ El kaldırma başarısız" type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async kickParticipant(sessionId: string, participantId: string, userName: string): Promise<void> {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.onParticipantLeft?.({ userId: participantId, sessionId, userName })
|
||||||
|
}, 100)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.connection.invoke('KickParticipant', sessionId, participantId)
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="❌ Katılımcı atılamadı" type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async approveHandRaise(sessionId: string, studentId: string): Promise<void> {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.onHandRaiseDismissed?.(studentId)
|
||||||
|
}, 100)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.connection.invoke('ApproveHandRaise', sessionId, studentId)
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="⚠️ El kaldırma onayı başarısız" type="warning" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async dismissHandRaise(sessionId: string, studentId: string): Promise<void> {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.onHandRaiseDismissed?.(studentId)
|
||||||
|
}, 100)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.connection.invoke('DismissHandRaise', sessionId, studentId)
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="⚠️ El indirme başarısız" type="warning" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendOffer(sessionId: string, targetUserId: string, offer: RTCSessionDescriptionInit) {
|
||||||
|
if (!this.isConnected) return
|
||||||
|
await this.connection.invoke('SendOffer', sessionId, targetUserId, offer)
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendAnswer(sessionId: string, targetUserId: string, answer: RTCSessionDescriptionInit) {
|
||||||
|
if (!this.isConnected) return
|
||||||
|
await this.connection.invoke('SendAnswer', sessionId, targetUserId, answer)
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendIceCandidate(sessionId: string, targetUserId: string, candidate: RTCIceCandidateInit) {
|
||||||
|
if (!this.isConnected) return
|
||||||
|
await this.connection.invoke('SendIceCandidate', sessionId, targetUserId, candidate)
|
||||||
|
}
|
||||||
|
|
||||||
|
setExistingParticipantsHandler(callback: (participants: any[]) => void) {
|
||||||
|
this.connection.on('ExistingParticipants', callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
setAttendanceUpdatedHandler(callback: (record: ClassroomAttendanceDto) => void) {
|
||||||
|
this.onAttendanceUpdate = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
setParticipantJoinHandler(
|
||||||
|
callback: (userId: string, name: string, isTeacher: boolean, isActive: boolean) => void,
|
||||||
|
) {
|
||||||
|
this.onParticipantJoined = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
setParticipantLeaveHandler(
|
||||||
|
callback: (payload: { userId: string; sessionId: string; userName: string }) => void,
|
||||||
|
) {
|
||||||
|
this.onParticipantLeft = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
setChatMessageReceivedHandler(callback: (message: ClassroomChatDto) => void) {
|
||||||
|
this.onChatMessage = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
setParticipantMutedHandler(callback: (userId: string, isMuted: boolean) => void) {
|
||||||
|
this.onParticipantMuted = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
setHandRaiseReceivedHandler(callback: (studentId: string) => void) {
|
||||||
|
this.onHandRaiseReceived = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
setHandRaiseDismissedHandler(callback: (studentId: string) => void) {
|
||||||
|
this.onHandRaiseDismissed = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
setOfferReceivedHandler(
|
||||||
|
callback: (fromUserId: string, offer: RTCSessionDescriptionInit) => void,
|
||||||
|
) {
|
||||||
|
this.onOfferReceived = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
setAnswerReceivedHandler(
|
||||||
|
callback: (fromUserId: string, answer: RTCSessionDescriptionInit) => void,
|
||||||
|
) {
|
||||||
|
this.onAnswerReceived = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
setIceCandidateReceivedHandler(
|
||||||
|
callback: (fromUserId: string, candidate: RTCIceCandidateInit) => void,
|
||||||
|
) {
|
||||||
|
this.onIceCandidateReceived = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
async disconnect(): Promise<void> {
|
||||||
|
if (this.isConnected && this.currentSessionId) {
|
||||||
|
try {
|
||||||
|
await this.connection.invoke('LeaveClass', this.currentSessionId)
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="⚠️ Bağlantı koparılırken hata" type="warning" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.connection) {
|
||||||
|
await this.connection.stop()
|
||||||
|
}
|
||||||
|
this.isConnected = false
|
||||||
|
this.currentSessionId = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
getConnectionState(): boolean {
|
||||||
|
return this.isConnected
|
||||||
|
}
|
||||||
|
|
||||||
|
setForceCleanupHandler(callback: () => void) {
|
||||||
|
this.onForceCleanup = callback
|
||||||
|
}
|
||||||
|
}
|
||||||
358
ui/src/services/classroom/webrtc.tsx
Normal file
358
ui/src/services/classroom/webrtc.tsx
Normal file
|
|
@ -0,0 +1,358 @@
|
||||||
|
import { toast } from '@/components/ui'
|
||||||
|
import Notification from '@/components/ui/Notification'
|
||||||
|
|
||||||
|
export class WebRTCService {
|
||||||
|
private peerConnections: Map<string, RTCPeerConnection> = new Map()
|
||||||
|
private retryCounts: Map<string, number> = new Map()
|
||||||
|
private maxRetries = 3
|
||||||
|
private signalRService: any
|
||||||
|
private sessionId: string = ''
|
||||||
|
|
||||||
|
private localStream: MediaStream | null = null
|
||||||
|
private onRemoteStream?: (userId: string, stream: MediaStream) => void
|
||||||
|
private onIceCandidate?: (userId: string, candidate: RTCIceCandidateInit) => void
|
||||||
|
private candidateBuffer: Map<string, RTCIceCandidateInit[]> = new Map()
|
||||||
|
|
||||||
|
private rtcConfiguration: RTCConfiguration = {
|
||||||
|
iceServers: [
|
||||||
|
{
|
||||||
|
urls: [
|
||||||
|
'stun:turn.sozsoft.com:3478',
|
||||||
|
'turn:turn.sozsoft.com:3478?transport=udp',
|
||||||
|
'turn:turn.sozsoft.com:3478?transport=tcp',
|
||||||
|
'turns:turn.sozsoft.com:5349?transport=tcp',
|
||||||
|
],
|
||||||
|
username: 'webrtc',
|
||||||
|
credential: 'strongpassword123',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
async initializeLocalStream(enableAudio: boolean, enableVideo: boolean): Promise<MediaStream> {
|
||||||
|
try {
|
||||||
|
this.localStream = await navigator.mediaDevices.getUserMedia({
|
||||||
|
video: {
|
||||||
|
width: { ideal: 1280 },
|
||||||
|
height: { ideal: 720 },
|
||||||
|
frameRate: { ideal: 30 },
|
||||||
|
},
|
||||||
|
audio: {
|
||||||
|
echoCancellation: true,
|
||||||
|
noiseSuppression: true,
|
||||||
|
autoGainControl: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
this.localStream.getAudioTracks().forEach((track) => (track.enabled = enableAudio))
|
||||||
|
this.localStream.getVideoTracks().forEach((track) => (track.enabled = enableVideo))
|
||||||
|
|
||||||
|
return this.localStream
|
||||||
|
} catch {
|
||||||
|
toast.push(
|
||||||
|
<Notification
|
||||||
|
title="❌ Kamera/Mikrofon erişilemedi. Tarayıcı ayarlarınızı veya izinleri kontrol edin."
|
||||||
|
type="danger"
|
||||||
|
/>,
|
||||||
|
{ placement: 'top-end' },
|
||||||
|
)
|
||||||
|
throw new Error('Media devices access failed')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPeerConnection(userId: string): Promise<RTCPeerConnection> {
|
||||||
|
const peerConnection = new RTCPeerConnection(this.rtcConfiguration)
|
||||||
|
this.peerConnections.set(userId, peerConnection)
|
||||||
|
this.retryCounts.set(userId, 0)
|
||||||
|
|
||||||
|
if (this.localStream) {
|
||||||
|
this.localStream.getTracks().forEach((track) => {
|
||||||
|
peerConnection.addTrack(track, this.localStream!)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
peerConnection.ontrack = (event) => {
|
||||||
|
const [remoteStream] = event.streams
|
||||||
|
this.onRemoteStream?.(userId, remoteStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
peerConnection.onicecandidate = (event) => {
|
||||||
|
if (event.candidate) {
|
||||||
|
this.onIceCandidate?.(userId, event.candidate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
peerConnection.onconnectionstatechange = async () => {
|
||||||
|
const state = peerConnection.connectionState
|
||||||
|
|
||||||
|
if (state === 'closed') {
|
||||||
|
this.closePeerConnection(userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state === 'failed') {
|
||||||
|
let retries = this.retryCounts.get(userId) ?? 0
|
||||||
|
if (retries < this.maxRetries) {
|
||||||
|
toast.push(
|
||||||
|
<Notification
|
||||||
|
title={`⚠️ Bağlantı başarısız, yeniden deneniyor (${retries + 1}/${this.maxRetries})`}
|
||||||
|
type="warning"
|
||||||
|
/>,
|
||||||
|
)
|
||||||
|
this.retryCounts.set(userId, retries + 1)
|
||||||
|
await this.restartIce(peerConnection, userId)
|
||||||
|
} else {
|
||||||
|
toast.push(
|
||||||
|
<Notification
|
||||||
|
title={`❌ Bağlantı kurulamadı (${this.maxRetries} deneme başarısız).`}
|
||||||
|
type="danger"
|
||||||
|
/>,
|
||||||
|
{ placement: 'top-end' },
|
||||||
|
)
|
||||||
|
this.closePeerConnection(userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.candidateBuffer.has(userId)) {
|
||||||
|
for (const cand of this.candidateBuffer.get(userId)!) {
|
||||||
|
try {
|
||||||
|
await peerConnection.addIceCandidate(cand)
|
||||||
|
} catch {
|
||||||
|
toast.push(
|
||||||
|
<Notification
|
||||||
|
title={`⚠️ ICE candidate eklenemedi. Kullanıcı: ${userId}`}
|
||||||
|
type="warning"
|
||||||
|
/>,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.candidateBuffer.delete(userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return peerConnection
|
||||||
|
}
|
||||||
|
|
||||||
|
setSignalRService(signalRService: any, sessionId: string) {
|
||||||
|
this.signalRService = signalRService
|
||||||
|
this.sessionId = sessionId
|
||||||
|
}
|
||||||
|
|
||||||
|
setIceCandidateHandler(callback: (userId: string, candidate: RTCIceCandidateInit) => void) {
|
||||||
|
this.onIceCandidate = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
async createOffer(userId: string): Promise<RTCSessionDescriptionInit> {
|
||||||
|
const pc = this.peerConnections.get(userId)
|
||||||
|
if (!pc) throw new Error('Peer connection not found')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const offer = await pc.createOffer()
|
||||||
|
await pc.setLocalDescription(offer)
|
||||||
|
return offer
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="❌ Offer oluşturulamadı" type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
throw new Error('Offer creation failed')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createAnswer(
|
||||||
|
userId: string,
|
||||||
|
offer: RTCSessionDescriptionInit,
|
||||||
|
): Promise<RTCSessionDescriptionInit> {
|
||||||
|
const pc = this.peerConnections.get(userId)
|
||||||
|
if (!pc) throw new Error('Peer connection not found')
|
||||||
|
|
||||||
|
try {
|
||||||
|
await pc.setRemoteDescription(offer)
|
||||||
|
const answer = await pc.createAnswer()
|
||||||
|
await pc.setLocalDescription(answer)
|
||||||
|
return answer
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="❌ Answer oluşturulamadı" type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
throw new Error('Answer creation failed')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleAnswer(userId: string, answer: RTCSessionDescriptionInit): Promise<void> {
|
||||||
|
const peerConnection = this.peerConnections.get(userId)
|
||||||
|
if (!peerConnection) throw new Error('Peer connection not found')
|
||||||
|
await peerConnection.setRemoteDescription(answer)
|
||||||
|
}
|
||||||
|
|
||||||
|
async addIceCandidate(userId: string, candidate: RTCIceCandidateInit): Promise<void> {
|
||||||
|
const pc = this.peerConnections.get(userId)
|
||||||
|
if (!pc) {
|
||||||
|
if (!this.candidateBuffer.has(userId)) {
|
||||||
|
this.candidateBuffer.set(userId, [])
|
||||||
|
}
|
||||||
|
this.candidateBuffer.get(userId)!.push(candidate)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pc.signalingState === 'stable' || pc.signalingState === 'have-remote-offer') {
|
||||||
|
try {
|
||||||
|
await pc.addIceCandidate(candidate)
|
||||||
|
} catch {
|
||||||
|
toast.push(
|
||||||
|
<Notification
|
||||||
|
title={`⚠️ ICE candidate eklenemedi. Kullanıcı: ${userId}`}
|
||||||
|
type="warning"
|
||||||
|
/>,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!this.candidateBuffer.has(userId)) {
|
||||||
|
this.candidateBuffer.set(userId, [])
|
||||||
|
}
|
||||||
|
this.candidateBuffer.get(userId)!.push(candidate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemoteStreamReceived(callback: (userId: string, stream: MediaStream) => void) {
|
||||||
|
this.onRemoteStream = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleVideo(enabled: boolean): Promise<void> {
|
||||||
|
if (!this.localStream) return
|
||||||
|
let videoTrack = this.localStream.getVideoTracks()[0]
|
||||||
|
|
||||||
|
if (videoTrack) {
|
||||||
|
videoTrack.enabled = enabled
|
||||||
|
} else if (enabled) {
|
||||||
|
try {
|
||||||
|
const stream = await navigator.mediaDevices.getUserMedia({ video: true })
|
||||||
|
const newTrack = stream.getVideoTracks()[0]
|
||||||
|
if (newTrack) {
|
||||||
|
this.localStream!.addTrack(newTrack)
|
||||||
|
this.peerConnections.forEach((pc) => {
|
||||||
|
const sender = pc.getSenders().find((s) => s.track?.kind === newTrack.kind)
|
||||||
|
if (sender) {
|
||||||
|
sender.replaceTrack(newTrack)
|
||||||
|
} else {
|
||||||
|
pc.addTrack(newTrack, this.localStream!)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="❌ Kamera açılamadı" type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleAudio(enabled: boolean): Promise<void> {
|
||||||
|
if (!this.localStream) return
|
||||||
|
let audioTrack = this.localStream.getAudioTracks()[0]
|
||||||
|
|
||||||
|
if (audioTrack) {
|
||||||
|
audioTrack.enabled = enabled
|
||||||
|
} else if (enabled) {
|
||||||
|
try {
|
||||||
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
|
||||||
|
const newTrack = stream.getAudioTracks()[0]
|
||||||
|
if (newTrack) {
|
||||||
|
this.localStream!.addTrack(newTrack)
|
||||||
|
this.peerConnections.forEach((pc) => {
|
||||||
|
const sender = pc.getSenders().find((s) => s.track?.kind === newTrack.kind)
|
||||||
|
if (sender) {
|
||||||
|
sender.replaceTrack(newTrack)
|
||||||
|
} else {
|
||||||
|
pc.addTrack(newTrack, this.localStream!)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="❌ Mikrofon açılamadı" type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getLocalStream(): MediaStream | null {
|
||||||
|
return this.localStream
|
||||||
|
}
|
||||||
|
|
||||||
|
private async restartIce(peerConnection: RTCPeerConnection, userId: string) {
|
||||||
|
try {
|
||||||
|
const offer = await peerConnection.createOffer({ iceRestart: true })
|
||||||
|
await peerConnection.setLocalDescription(offer)
|
||||||
|
|
||||||
|
if (this.signalRService) {
|
||||||
|
await this.signalRService.sendOffer(this.sessionId, userId, offer)
|
||||||
|
} else {
|
||||||
|
toast.push(<Notification title="⚠️ Tekrar bağlanma başarısız" type="warning" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="❌ ICE restart başarısız" type="danger" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closePeerConnection(userId: string): void {
|
||||||
|
const peerConnection = this.peerConnections.get(userId)
|
||||||
|
if (peerConnection) {
|
||||||
|
peerConnection.getSenders().forEach((sender) => sender.track?.stop())
|
||||||
|
peerConnection.close()
|
||||||
|
this.peerConnections.delete(userId)
|
||||||
|
this.retryCounts.delete(userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPeerConnection(userId: string): RTCPeerConnection | undefined {
|
||||||
|
return this.peerConnections.get(userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
closeAllConnections(): void {
|
||||||
|
this.peerConnections.forEach((pc) => {
|
||||||
|
pc.getSenders().forEach((sender) => sender.track?.stop())
|
||||||
|
pc.close()
|
||||||
|
})
|
||||||
|
this.peerConnections.clear()
|
||||||
|
|
||||||
|
if (this.localStream) {
|
||||||
|
this.localStream.getTracks().forEach((track) => track.stop())
|
||||||
|
this.localStream = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addStreamToPeers(stream: MediaStream) {
|
||||||
|
this.peerConnections.forEach((pc) => {
|
||||||
|
stream.getTracks().forEach((track) => {
|
||||||
|
const alreadyHas = pc.getSenders().some((s) => s.track?.id === track.id)
|
||||||
|
if (!alreadyHas) {
|
||||||
|
pc.addTrack(track, stream)
|
||||||
|
track.onended = () => {
|
||||||
|
this.removeTrackFromPeers(track)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTrackFromPeers(track: MediaStreamTrack) {
|
||||||
|
this.peerConnections.forEach((pc) => {
|
||||||
|
pc.getSenders().forEach((sender) => {
|
||||||
|
if (sender.track === track) {
|
||||||
|
try {
|
||||||
|
pc.removeTrack(sender)
|
||||||
|
} catch {
|
||||||
|
toast.push(<Notification title="⚠️ Track silinemedi" type="warning" />, {
|
||||||
|
placement: 'top-end',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (sender.track?.readyState !== 'ended') {
|
||||||
|
sender.track?.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import apiService from './api.service'
|
|
||||||
|
|
||||||
export interface SetupStatusDto {
|
|
||||||
dbExists: boolean
|
|
||||||
error?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getSetupStatus = () =>
|
|
||||||
apiService.fetchData<SetupStatusDto>({
|
|
||||||
method: 'GET',
|
|
||||||
url: '/api/setup/status',
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the SSE URL for migration streaming.
|
|
||||||
* Usage: new EventSource(getMigrateUrl())
|
|
||||||
*/
|
|
||||||
export const getMigrateUrl = (): string => {
|
|
||||||
const base = import.meta.env.VITE_API_URL ?? ''
|
|
||||||
return `${base}/api/setup/migrate`
|
|
||||||
}
|
|
||||||
|
|
@ -13,7 +13,6 @@ export interface BaseStoreModel {
|
||||||
common: {
|
common: {
|
||||||
currentRouteKey: string
|
currentRouteKey: string
|
||||||
tabHasFocus: boolean
|
tabHasFocus: boolean
|
||||||
setupMode: boolean /** Veritabanı mevcut değilse true — setup sayfasına yönlendirme için */
|
|
||||||
}
|
}
|
||||||
messages: {
|
messages: {
|
||||||
errors: StoreError[]
|
errors: StoreError[]
|
||||||
|
|
@ -25,7 +24,6 @@ export interface BaseStoreActions {
|
||||||
common: {
|
common: {
|
||||||
setCurrentRouteKey: Action<BaseStoreModel['common'], string>
|
setCurrentRouteKey: Action<BaseStoreModel['common'], string>
|
||||||
setTabHasFocus: Action<BaseStoreModel['common'], boolean>
|
setTabHasFocus: Action<BaseStoreModel['common'], boolean>
|
||||||
setSetupMode: Action<BaseStoreModel['common'], boolean>
|
|
||||||
}
|
}
|
||||||
messages: {
|
messages: {
|
||||||
addError: Action<BaseStoreModel['messages'], StoreError>
|
addError: Action<BaseStoreModel['messages'], StoreError>
|
||||||
|
|
@ -38,12 +36,12 @@ export interface BaseStoreActions {
|
||||||
export type BaseModel = BaseStoreModel & BaseStoreActions
|
export type BaseModel = BaseStoreModel & BaseStoreActions
|
||||||
|
|
||||||
const initialState: BaseStoreModel = {
|
const initialState: BaseStoreModel = {
|
||||||
common: { currentRouteKey: '', tabHasFocus: false, setupMode: false },
|
common: { currentRouteKey: '', tabHasFocus: false },
|
||||||
messages: {
|
messages: {
|
||||||
errors: [],
|
errors: [],
|
||||||
// success: [],
|
// success: [],
|
||||||
warning: [],
|
warning: [],
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const baseModel: BaseModel = {
|
export const baseModel: BaseModel = {
|
||||||
|
|
@ -55,9 +53,6 @@ export const baseModel: BaseModel = {
|
||||||
setTabHasFocus: action((state, payload) => {
|
setTabHasFocus: action((state, payload) => {
|
||||||
state.tabHasFocus = payload
|
state.tabHasFocus = payload
|
||||||
}),
|
}),
|
||||||
setSetupMode: action((state, payload) => {
|
|
||||||
state.setupMode = payload
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
messages: {
|
messages: {
|
||||||
...initialState.messages,
|
...initialState.messages,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { Button } from '@/components/ui'
|
|
||||||
import { APP_NAME } from '@/constants/app.constant'
|
import { APP_NAME } from '@/constants/app.constant'
|
||||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
|
@ -28,15 +27,14 @@ const NotFoundPage = () => {
|
||||||
{translate('::Public.notFound.message')}
|
{translate('::Public.notFound.message')}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center justify-center font-inter">
|
<div className="flex items-center justify-center font-inter">
|
||||||
<Button
|
<button
|
||||||
variant='solid'
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
navigate(isAdminPath ? ROUTES_ENUM.protected.dashboard : ROUTES_ENUM.public.home)
|
navigate(isAdminPath ? ROUTES_ENUM.protected.dashboard : ROUTES_ENUM.public.home)
|
||||||
}
|
}
|
||||||
className="px-6 py-3 bg-blue-500 rounded-xl shadow hover:bg-blue-600 transition"
|
className="px-6 py-3 bg-blue-500 rounded-xl shadow hover:bg-blue-600 transition"
|
||||||
>
|
>
|
||||||
{translate('::Public.notFound.button')}
|
{translate('::Public.notFound.button')}
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -671,14 +671,14 @@ const FileManager = () => {
|
||||||
await fileManagementService.copyItems(itemIds, currentFolderId, selectedTenant?.id)
|
await fileManagementService.copyItems(itemIds, currentFolderId, selectedTenant?.id)
|
||||||
await fetchItems(currentFolderId)
|
await fetchItems(currentFolderId)
|
||||||
toast.push(
|
toast.push(
|
||||||
<Notification title={translate('::App.Platform.Success')} type="success">
|
<Notification title="Success" type="success">
|
||||||
{itemIds.length} item(s) copied successfully
|
{itemIds.length} item(s) copied successfully
|
||||||
</Notification>,
|
</Notification>,
|
||||||
)
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Copy failed:', error)
|
console.error('Copy failed:', error)
|
||||||
toast.push(
|
toast.push(
|
||||||
<Notification title={translate('::App.Platform.Error')} type="danger">
|
<Notification title="Error" type="danger">
|
||||||
Failed to copy items
|
Failed to copy items
|
||||||
</Notification>,
|
</Notification>,
|
||||||
)
|
)
|
||||||
|
|
@ -704,14 +704,14 @@ const FileManager = () => {
|
||||||
localStorage.removeItem('fileManager_clipboard')
|
localStorage.removeItem('fileManager_clipboard')
|
||||||
setHasClipboardData(false)
|
setHasClipboardData(false)
|
||||||
toast.push(
|
toast.push(
|
||||||
<Notification title={translate('::App.Platform.Success')} type="success">
|
<Notification title="Success" type="success">
|
||||||
{itemIds.length} item(s) moved successfully
|
{itemIds.length} item(s) moved successfully
|
||||||
</Notification>,
|
</Notification>,
|
||||||
)
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Move failed:', error)
|
console.error('Move failed:', error)
|
||||||
toast.push(
|
toast.push(
|
||||||
<Notification title={translate('::App.Platform.Error')} type="danger">
|
<Notification title="Error" type="danger">
|
||||||
Failed to move items
|
Failed to move items
|
||||||
</Notification>,
|
</Notification>,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { forwardRef } from 'react'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { FaChevronRight, FaFolder, FaHome } from 'react-icons/fa'
|
import { FaChevronRight, FaFolder, FaHome } from 'react-icons/fa'
|
||||||
import type { BreadcrumbItem } from '@/types/fileManagement'
|
import type { BreadcrumbItem } from '@/types/fileManagement'
|
||||||
import { Button } from '@/components/ui'
|
|
||||||
|
|
||||||
export interface BreadcrumbProps {
|
export interface BreadcrumbProps {
|
||||||
items: BreadcrumbItem[]
|
items: BreadcrumbItem[]
|
||||||
|
|
@ -18,8 +17,7 @@ const Breadcrumb = forwardRef<HTMLDivElement, BreadcrumbProps>((props, ref) => {
|
||||||
{items.map((item, index) => (
|
{items.map((item, index) => (
|
||||||
<div key={item.path} className="flex items-center">
|
<div key={item.path} className="flex items-center">
|
||||||
{index > 0 && <FaChevronRight className="mx-2 h-4 w-4 text-gray-400" />}
|
{index > 0 && <FaChevronRight className="mx-2 h-4 w-4 text-gray-400" />}
|
||||||
<Button
|
<button
|
||||||
size="xs"
|
|
||||||
onClick={() => onNavigate(item)}
|
onClick={() => onNavigate(item)}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'flex items-center px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors',
|
'flex items-center px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors',
|
||||||
|
|
@ -35,7 +33,7 @@ const Breadcrumb = forwardRef<HTMLDivElement, BreadcrumbProps>((props, ref) => {
|
||||||
<FaFolder className="h-4 w-4 mr-1" />
|
<FaFolder className="h-4 w-4 mr-1" />
|
||||||
)}
|
)}
|
||||||
<span className="truncate max-w-32">{item.name}</span>
|
<span className="truncate max-w-32">{item.name}</span>
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -415,7 +415,7 @@ const Wizard = () => {
|
||||||
<Steps.Item
|
<Steps.Item
|
||||||
title={translate('::ListForms.Wizard.ListFormFields') || 'List Form Fields'}
|
title={translate('::ListForms.Wizard.ListFormFields') || 'List Form Fields'}
|
||||||
/>
|
/>
|
||||||
<Steps.Item title={translate('::App.Platform.Deploy') || 'Deploy'} />
|
<Steps.Item title={translate('::ListForms.Wizard.Deploy') || 'Deploy'} />
|
||||||
</Steps>
|
</Steps>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ const WizardStep2 = ({
|
||||||
onNext,
|
onNext,
|
||||||
}: WizardStep2Props) => {
|
}: WizardStep2Props) => {
|
||||||
const step2Missing = [
|
const step2Missing = [
|
||||||
!values.listFormCode && translate('::App.Listform.ListformField.ListFormCode'),
|
!values.listFormCode && translate('::ListForms.Wizard.Step2.ListFormCode'),
|
||||||
!values.dataSourceCode && translate('::ListForms.Wizard.Step4.DataSource'),
|
!values.dataSourceCode && translate('::ListForms.Wizard.Step4.DataSource'),
|
||||||
!values.selectCommand && translate('::ListForms.Wizard.Step2.SelectCommand'),
|
!values.selectCommand && translate('::ListForms.Wizard.Step2.SelectCommand'),
|
||||||
!values.keyFieldName && translate('::ListForms.Wizard.Step4.KeyField'),
|
!values.keyFieldName && translate('::ListForms.Wizard.Step4.KeyField'),
|
||||||
|
|
@ -80,7 +80,7 @@ const WizardStep2 = ({
|
||||||
{/* ListForm Code + Data Source */}
|
{/* ListForm Code + Data Source */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-6">
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.ListFormCode')}
|
label={translate('::ListForms.Wizard.Step2.ListFormCode')}
|
||||||
invalid={!!(errors.listFormCode && touched.listFormCode)}
|
invalid={!!(errors.listFormCode && touched.listFormCode)}
|
||||||
errorMessage={errors.listFormCode}
|
errorMessage={errors.listFormCode}
|
||||||
asterisk={true}
|
asterisk={true}
|
||||||
|
|
@ -102,7 +102,7 @@ const WizardStep2 = ({
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.DataSourceCode')}
|
label={translate('::ListForms.Wizard.Step2.DataSourceCode')}
|
||||||
asterisk={true}
|
asterisk={true}
|
||||||
invalid={!!(errors.dataSourceCode && touched.dataSourceCode)}
|
invalid={!!(errors.dataSourceCode && touched.dataSourceCode)}
|
||||||
errorMessage={errors.dataSourceCode}
|
errorMessage={errors.dataSourceCode}
|
||||||
|
|
@ -112,7 +112,7 @@ const WizardStep2 = ({
|
||||||
<Select
|
<Select
|
||||||
field={field}
|
field={field}
|
||||||
form={form}
|
form={form}
|
||||||
placeholder={translate('::App.Listform.ListformField.DataSourceCode')}
|
placeholder={translate('::ListForms.Wizard.Step2.DataSourceCode')}
|
||||||
isClearable={true}
|
isClearable={true}
|
||||||
isLoading={isLoadingDataSource}
|
isLoading={isLoadingDataSource}
|
||||||
options={dataSourceList}
|
options={dataSourceList}
|
||||||
|
|
@ -137,7 +137,7 @@ const WizardStep2 = ({
|
||||||
|
|
||||||
{isDataSourceNew && (
|
{isDataSourceNew && (
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.ConnectionString')}
|
label={translate('::ListForms.Wizard.Step2.ConnectionString')}
|
||||||
invalid={!!(errors.dataSourceConnectionString && touched.dataSourceConnectionString)}
|
invalid={!!(errors.dataSourceConnectionString && touched.dataSourceConnectionString)}
|
||||||
errorMessage={errors.dataSourceConnectionString}
|
errorMessage={errors.dataSourceConnectionString}
|
||||||
>
|
>
|
||||||
|
|
@ -145,7 +145,7 @@ const WizardStep2 = ({
|
||||||
type="text"
|
type="text"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
name="dataSourceConnectionString"
|
name="dataSourceConnectionString"
|
||||||
placeholder={translate('::App.Listform.ListformField.ConnectionString')}
|
placeholder={translate('::ListForms.Wizard.Step2.ConnectionString')}
|
||||||
component={Input}
|
component={Input}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
@ -195,7 +195,7 @@ const WizardStep2 = ({
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: translate('::App.Platform.Views') || 'Views',
|
label: translate('::ListForms.Wizard.Step2.Views') || 'Views',
|
||||||
options: dbObjects.views.map((v) => ({
|
options: dbObjects.views.map((v) => ({
|
||||||
label: v.objectName,
|
label: v.objectName,
|
||||||
value: v.objectName,
|
value: v.objectName,
|
||||||
|
|
@ -362,7 +362,7 @@ const WizardStep2 = ({
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::ListForms.ListFormEdit.AllowEditing')}
|
label={translate('::ListForms.Wizard.Step2.AllowEditing')}
|
||||||
invalid={!!(errors.allowEditing && touched.allowEditing)}
|
invalid={!!(errors.allowEditing && touched.allowEditing)}
|
||||||
errorMessage={errors.allowEditing}
|
errorMessage={errors.allowEditing}
|
||||||
>
|
>
|
||||||
|
|
@ -473,23 +473,23 @@ const WizardStep2 = ({
|
||||||
extra={
|
extra={
|
||||||
selectCommandColumns.length > 0 ? (
|
selectCommandColumns.length > 0 ? (
|
||||||
<div className="flex items-center gap-2 ml-3">
|
<div className="flex items-center gap-2 ml-3">
|
||||||
<Button
|
<button
|
||||||
variant='solid'
|
type="button"
|
||||||
onClick={() => onToggleAllColumns(true)}
|
onClick={() => onToggleAllColumns(true)}
|
||||||
className="text-xs px-2 py-0.5 rounded bg-indigo-500 text-white hover:bg-indigo-600"
|
className="text-xs px-2 py-0.5 rounded bg-indigo-500 text-white hover:bg-indigo-600"
|
||||||
>
|
>
|
||||||
{translate('::ListForms.Wizard.Step2.SelectAll') || 'Tümünü Seç'}
|
{translate('::ListForms.Wizard.Step2.SelectAll') || 'Tümünü Seç'}
|
||||||
</Button>
|
</button>
|
||||||
<Button
|
<button
|
||||||
variant='default'
|
type="button"
|
||||||
onClick={() => onToggleAllColumns(false)}
|
onClick={() => onToggleAllColumns(false)}
|
||||||
className="text-xs px-2 py-0.5 rounded border border-gray-300 dark:border-gray-600 text-gray-500 hover:text-red-500 hover:border-red-400"
|
className="text-xs px-2 py-0.5 rounded border border-gray-300 dark:border-gray-600 text-gray-500 hover:text-red-500 hover:border-red-400"
|
||||||
>
|
>
|
||||||
{translate('::ListForms.Wizard.Step2.ClearAll') || 'Tümünü Kaldır'}
|
{translate('::ListForms.Wizard.Step2.ClearAll') || 'Tümünü Kaldır'}
|
||||||
</Button>
|
</button>
|
||||||
<span className="text-xs text-gray-400">
|
<span className="text-xs text-gray-400">
|
||||||
{selectedColumns.size}/{selectCommandColumns.length}{' '}
|
{selectedColumns.size}/{selectCommandColumns.length}{' '}
|
||||||
{translate('::App.Listform.ListformField.Column')}
|
{translate('::ListForms.Wizard.Step4.StatColumn')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : null
|
) : null
|
||||||
|
|
|
||||||
|
|
@ -679,7 +679,7 @@ const WizardStep3 = ({
|
||||||
<div className="sticky top-4">
|
<div className="sticky top-4">
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<span className="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide">
|
<span className="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide">
|
||||||
{translate('::App.Listform.ListformField.Column')}
|
{translate('::ListForms.Wizard.Step4.StatColumn')}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-gray-400">
|
<span className="text-xs text-gray-400">
|
||||||
{availableColumns.length}/{selectedColumns.size}
|
{availableColumns.length}/{selectedColumns.size}
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ const WizardStep4 = ({
|
||||||
<Row label={translate('::ListForms.Wizard.Step4.MenuCode')} value={values.menuCode} />
|
<Row label={translate('::ListForms.Wizard.Step4.MenuCode')} value={values.menuCode} />
|
||||||
<Row label={translate('::ListForms.Wizard.Step4.MenuParent')} value={values.menuParentCode} />
|
<Row label={translate('::ListForms.Wizard.Step4.MenuParent')} value={values.menuParentCode} />
|
||||||
<Row label={translate('::ListForms.Wizard.Step4.PermissionGroup')} value={values.permissionGroupName} />
|
<Row label={translate('::ListForms.Wizard.Step4.PermissionGroup')} value={values.permissionGroupName} />
|
||||||
<Row label={translate('::App.Listform.ListformField.Icon')} value={values.menuIcon} />
|
<Row label={translate('::ListForms.Wizard.Step4.Icon')} value={values.menuIcon} />
|
||||||
<Row label={translate('::ListForms.Wizard.Step4.MenuTr')} value={values.languageTextMenuTr} />
|
<Row label={translate('::ListForms.Wizard.Step4.MenuTr')} value={values.languageTextMenuTr} />
|
||||||
<Row label={translate('::ListForms.Wizard.Step4.MenuEn')} value={values.languageTextMenuEn} />
|
<Row label={translate('::ListForms.Wizard.Step4.MenuEn')} value={values.languageTextMenuEn} />
|
||||||
<Row label={translate('::ListForms.Wizard.Step4.MenuParentTr')} value={values.languageTextMenuParentTr} />
|
<Row label={translate('::ListForms.Wizard.Step4.MenuParentTr')} value={values.languageTextMenuParentTr} />
|
||||||
|
|
@ -221,7 +221,7 @@ const WizardStep4 = ({
|
||||||
<Row label={translate('::ListForms.Wizard.Step4.DescTr')} value={values.languageTextDescTr} />
|
<Row label={translate('::ListForms.Wizard.Step4.DescTr')} value={values.languageTextDescTr} />
|
||||||
<Row label={translate('::ListForms.Wizard.Step4.DescEn')} value={values.languageTextDescEn} />
|
<Row label={translate('::ListForms.Wizard.Step4.DescEn')} value={values.languageTextDescEn} />
|
||||||
<Row label={translate('::ListForms.Wizard.Step4.DataSource')} value={values.dataSourceCode} />
|
<Row label={translate('::ListForms.Wizard.Step4.DataSource')} value={values.dataSourceCode} />
|
||||||
<Row label={translate('::App.Listform.ListformField.ConnectionString')} value={values.dataSourceConnectionString} />
|
<Row label={translate('::ListForms.Wizard.Step4.ConnectionString')} value={values.dataSourceConnectionString} />
|
||||||
<Row
|
<Row
|
||||||
label={translate('::ListForms.Wizard.Step4.CommandType')}
|
label={translate('::ListForms.Wizard.Step4.CommandType')}
|
||||||
value={
|
value={
|
||||||
|
|
@ -266,7 +266,7 @@ const WizardStep4 = ({
|
||||||
<Section
|
<Section
|
||||||
key={g.id}
|
key={g.id}
|
||||||
title={g.caption || `(${translate('::ListForms.Wizard.Step4.StatGroup')})`}
|
title={g.caption || `(${translate('::ListForms.Wizard.Step4.StatGroup')})`}
|
||||||
badge={`${g.items.length} ${translate('::ListForms.Wizard.Step4.StatField')} · ${g.colCount} ${translate('::App.Listform.ListformField.Column')}`}
|
badge={`${g.items.length} ${translate('::ListForms.Wizard.Step4.StatField')} · ${g.colCount} ${translate('::ListForms.Wizard.Step4.StatColumn')}`}
|
||||||
defaultOpen={false}
|
defaultOpen={false}
|
||||||
>
|
>
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
|
@ -307,7 +307,7 @@ const WizardStep4 = ({
|
||||||
{[
|
{[
|
||||||
{ label: translate('::ListForms.Wizard.Step4.StatGroup'), value: groups.length },
|
{ label: translate('::ListForms.Wizard.Step4.StatGroup'), value: groups.length },
|
||||||
{ label: translate('::ListForms.Wizard.Step4.StatField'), value: totalFields },
|
{ label: translate('::ListForms.Wizard.Step4.StatField'), value: totalFields },
|
||||||
{ label: translate('::App.Listform.ListformField.Column'), value: selectedColumns.size },
|
{ label: translate('::ListForms.Wizard.Step4.StatColumn'), value: selectedColumns.size },
|
||||||
].map((s) => (
|
].map((s) => (
|
||||||
<div
|
<div
|
||||||
key={s.label}
|
key={s.label}
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ function FormTabCommands() {
|
||||||
<Th>{translate('::ListForms.ListFormEdit.CommandPosition')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.CommandPosition')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.CommandText')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.CommandText')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.CommandHint')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.CommandHint')}</Th>
|
||||||
<Th>{translate('::App.Listform.ListformField.Icon')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.CommandIcon')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.CommandAuthorizationType')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.CommandAuthorizationType')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.CommandUrlTarget')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.CommandUrlTarget')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.CommandUrl')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.CommandUrl')}</Th>
|
||||||
|
|
|
||||||
|
|
@ -104,12 +104,12 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
|
|
||||||
const table = dbObjects.tables.find((t) => t.tableName === cmd)
|
const table = dbObjects.tables.find((t) => t.tableName === cmd)
|
||||||
if (table) { loadColumns(dsCode, table.schemaName, table.tableName); return }
|
if (table) { loadColumns(dsCode, table.schemaName, table.tableName); return }
|
||||||
const view = dbObjects.views.find((v) => v.objectName === cmd)
|
const view = dbObjects.views.find((v) => v.viewName === cmd)
|
||||||
if (view) { loadColumns(dsCode, view.schemaName, view.objectName); return }
|
if (view) { loadColumns(dsCode, view.schemaName, view.viewName); return }
|
||||||
const fn = dbObjects.functions.find((f) => f.objectName === cmd)
|
const fn = dbObjects.functions.find((f) => f.functionName === cmd)
|
||||||
if (fn) { loadColumns(dsCode, fn.schemaName, fn.objectName); return }
|
if (fn) { loadColumns(dsCode, fn.schemaName, fn.functionName); return }
|
||||||
const sp = dbObjects.storedProcedures.find((p) => p.objectName === cmd)
|
const sp = dbObjects.storedProcedures.find((p) => p.procedureName === cmd)
|
||||||
if (sp) { loadColumns(dsCode, sp.schemaName, sp.objectName); return }
|
if (sp) { loadColumns(dsCode, sp.schemaName, sp.procedureName); return }
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [dbObjects])
|
}, [dbObjects])
|
||||||
|
|
||||||
|
|
@ -167,7 +167,7 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.DataSourceCode')}
|
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceCode')}
|
||||||
invalid={errors.dataSourceCode && touched.dataSourceCode}
|
invalid={errors.dataSourceCode && touched.dataSourceCode}
|
||||||
errorMessage={errors.dataSourceCode}
|
errorMessage={errors.dataSourceCode}
|
||||||
>
|
>
|
||||||
|
|
@ -175,7 +175,7 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
type="text"
|
type="text"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
name="dataSourceCode"
|
name="dataSourceCode"
|
||||||
placeholder={translate('::App.Listform.ListformField.DataSourceCode')}
|
placeholder={translate('::ListForms.ListFormEdit.DatabaseDataSourceCode')}
|
||||||
>
|
>
|
||||||
{({ field, form }: FieldProps<DataSourceTypeEnum>) => (
|
{({ field, form }: FieldProps<DataSourceTypeEnum>) => (
|
||||||
<Select
|
<Select
|
||||||
|
|
@ -232,31 +232,31 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
{
|
{
|
||||||
label: 'Views',
|
label: 'Views',
|
||||||
options: dbObjects.views.map((v) => ({
|
options: dbObjects.views.map((v) => ({
|
||||||
label: v.objectName,
|
label: v.viewName,
|
||||||
value: v.objectName,
|
value: v.viewName,
|
||||||
__type: SelectCommandTypeEnum.View,
|
__type: SelectCommandTypeEnum.View,
|
||||||
__schema: v.schemaName,
|
__schema: v.schemaName,
|
||||||
__rawName: v.objectName,
|
__rawName: v.viewName,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Functions',
|
label: 'Functions',
|
||||||
options: dbObjects.functions.map((f) => ({
|
options: dbObjects.functions.map((f) => ({
|
||||||
label: f.objectName,
|
label: f.functionName,
|
||||||
value: f.objectName,
|
value: f.functionName,
|
||||||
__type: SelectCommandTypeEnum.TableValuedFunction,
|
__type: SelectCommandTypeEnum.TableValuedFunction,
|
||||||
__schema: f.schemaName,
|
__schema: f.schemaName,
|
||||||
__rawName: f.objectName,
|
__rawName: f.functionName,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Stored Procedures',
|
label: 'Stored Procedures',
|
||||||
options: dbObjects.storedProcedures.map((p) => ({
|
options: dbObjects.storedProcedures.map((p) => ({
|
||||||
label: p.objectName,
|
label: p.procedureName,
|
||||||
value: p.objectName,
|
value: p.procedureName,
|
||||||
__type: SelectCommandTypeEnum.StoredProcedure,
|
__type: SelectCommandTypeEnum.StoredProcedure,
|
||||||
__schema: p.schemaName,
|
__schema: p.schemaName,
|
||||||
__rawName: p.objectName,
|
__rawName: p.procedureName,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
@ -312,7 +312,7 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
</Field>
|
</Field>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.TableName')}
|
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceTableName')}
|
||||||
invalid={errors.tableName && touched.tableName}
|
invalid={errors.tableName && touched.tableName}
|
||||||
errorMessage={errors.tableName}
|
errorMessage={errors.tableName}
|
||||||
>
|
>
|
||||||
|
|
@ -320,12 +320,12 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
type="text"
|
type="text"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
name="tableName"
|
name="tableName"
|
||||||
placeholder={translate('::App.Listform.ListformField.TableName')}
|
placeholder={translate('::ListForms.ListFormEdit.DatabaseDataSourceTableName')}
|
||||||
component={Input}
|
component={Input}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.KeyFieldName')}
|
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName')}
|
||||||
invalid={errors.keyFieldName && touched.keyFieldName}
|
invalid={errors.keyFieldName && touched.keyFieldName}
|
||||||
errorMessage={errors.keyFieldName}
|
errorMessage={errors.keyFieldName}
|
||||||
extra={
|
extra={
|
||||||
|
|
@ -355,7 +355,7 @@ function FormTabDatabaseDataSource(props: FormEditProps) {
|
||||||
isLoadingColumns
|
isLoadingColumns
|
||||||
? translate('::Loading')
|
? translate('::Loading')
|
||||||
: translate(
|
: translate(
|
||||||
'::App.Listform.ListformField.KeyFieldName',
|
'::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
options={selectCommandColumns.map((c) => ({
|
options={selectCommandColumns.map((c) => ({
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ function FormTabDatabaseDelete({
|
||||||
<Th>{translate('::ListForms.ListFormFieldEdit.FieldName')}</Th>
|
<Th>{translate('::ListForms.ListFormFieldEdit.FieldName')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.FieldDbType')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.FieldDbType')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.CustomValueType')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.CustomValueType')}</Th>
|
||||||
<Th>{translate('::App.Listform.ListformField.Value')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.Value')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.SqlQuery')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.SqlQuery')}</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</THead>
|
</THead>
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ function FormTabDatabaseInsert({
|
||||||
<Th>{translate('::ListForms.ListFormFieldEdit.FieldName')}</Th>
|
<Th>{translate('::ListForms.ListFormFieldEdit.FieldName')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.FieldDbType')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.FieldDbType')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.CustomValueType')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.CustomValueType')}</Th>
|
||||||
<Th>{translate('::App.Listform.ListformField.Value')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.Value')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.SqlQuery')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.SqlQuery')}</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</THead>
|
</THead>
|
||||||
|
|
@ -251,7 +251,7 @@ function FormTabDatabaseInsert({
|
||||||
<Th>{translate('::ListForms.ListFormFieldEdit.FieldName')}</Th>
|
<Th>{translate('::ListForms.ListFormFieldEdit.FieldName')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.FieldDbType')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.FieldDbType')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.CustomValueType')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.CustomValueType')}</Th>
|
||||||
<Th>{translate('::App.Listform.ListformField.Value')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.Value')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.SqlQuery')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.SqlQuery')}</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</THead>
|
</THead>
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ function FormTabDatabaseSelect({
|
||||||
<Th>{translate('::ListForms.ListFormFieldEdit.FieldName')}</Th>
|
<Th>{translate('::ListForms.ListFormFieldEdit.FieldName')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.FieldDbType')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.FieldDbType')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.CustomValueType')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.CustomValueType')}</Th>
|
||||||
<Th>{translate('::App.Listform.ListformField.Value')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.Value')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.SqlQuery')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.SqlQuery')}</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</THead>
|
</THead>
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ function FormTabDatabaseUpdate({
|
||||||
<Th>{translate('::ListForms.ListFormFieldEdit.FieldName')}</Th>
|
<Th>{translate('::ListForms.ListFormFieldEdit.FieldName')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.FieldDbType')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.FieldDbType')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.CustomValueType')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.CustomValueType')}</Th>
|
||||||
<Th>{translate('::App.Listform.ListformField.Value')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.Value')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.SqlQuery')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.SqlQuery')}</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</THead>
|
</THead>
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ function FormTabDetails(
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
<Card className="my-2" header="General">
|
<Card className="my-2" header="General">
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.CultureName')}
|
label={translate('::ListForms.ListFormEdit.DetailsCultureName')}
|
||||||
invalid={errors.cultureName && touched.cultureName}
|
invalid={errors.cultureName && touched.cultureName}
|
||||||
errorMessage={errors.cultureName}
|
errorMessage={errors.cultureName}
|
||||||
>
|
>
|
||||||
|
|
@ -84,7 +84,7 @@ function FormTabDetails(
|
||||||
type="text"
|
type="text"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
name="cultureName"
|
name="cultureName"
|
||||||
placeholder={translate('::App.Listform.ListformField.CultureName')}
|
placeholder={translate('::ListForms.ListFormEdit.DetailsCultureName')}
|
||||||
>
|
>
|
||||||
{({ field, form }: FieldProps<LanguageInfo>) => (
|
{({ field, form }: FieldProps<LanguageInfo>) => (
|
||||||
<Select
|
<Select
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ function FormTabEdit(props: FormEditProps & { listFormCode: string }) {
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::ListForms.ListFormEdit.AllowEditing')}
|
label={translate('::ListForms.ListFormEdit.EditingAllowEditing')}
|
||||||
invalid={
|
invalid={
|
||||||
errors.editingOptionDto?.allowEditing &&
|
errors.editingOptionDto?.allowEditing &&
|
||||||
touched.editingOptionDto?.allowEditing
|
touched.editingOptionDto?.allowEditing
|
||||||
|
|
@ -82,7 +82,7 @@ function FormTabEdit(props: FormEditProps & { listFormCode: string }) {
|
||||||
>
|
>
|
||||||
<Field
|
<Field
|
||||||
name="editingOptionDto.allowEditing"
|
name="editingOptionDto.allowEditing"
|
||||||
placeholder={translate('::ListForms.ListFormEdit.AllowEditing')}
|
placeholder={translate('::ListForms.ListFormEdit.EditingAllowEditing')}
|
||||||
component={Checkbox}
|
component={Checkbox}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
@ -118,20 +118,6 @@ function FormTabEdit(props: FormEditProps & { listFormCode: string }) {
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
|
||||||
label={translate('::ListForms.ListFormEdit.EditingAllowDuplicate')}
|
|
||||||
invalid={
|
|
||||||
errors.editingOptionDto?.allowDuplicate && touched.editingOptionDto?.allowDuplicate
|
|
||||||
}
|
|
||||||
errorMessage={errors.editingOptionDto?.allowDuplicate}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="editingOptionDto.allowDuplicate"
|
|
||||||
placeholder={translate('::ListForms.ListFormEdit.EditingAllowDuplicate')}
|
|
||||||
component={Checkbox}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::ListForms.ListFormEdit.EditingAllowDeleting')}
|
label={translate('::ListForms.ListFormEdit.EditingAllowDeleting')}
|
||||||
invalid={
|
invalid={
|
||||||
|
|
@ -206,7 +192,7 @@ function FormTabEdit(props: FormEditProps & { listFormCode: string }) {
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::ListForms.ListFormEdit.EditingMode')}
|
label={translate('::SidePanel.Mode')}
|
||||||
invalid={errors.editingOptionDto?.mode && touched.editingOptionDto?.mode}
|
invalid={errors.editingOptionDto?.mode && touched.editingOptionDto?.mode}
|
||||||
errorMessage={errors.editingOptionDto?.mode}
|
errorMessage={errors.editingOptionDto?.mode}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ function FormTabEditForm(props: { listFormCode: string }) {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Th>
|
</Th>
|
||||||
<Th>{translate('::App.Listform.ListformField.Order')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.EditingFormOrder')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.EditingFormItemType')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.EditingFormItemType')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.DetailsTitle')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.DetailsTitle')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.EditingFormColumnCount')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.EditingFormColumnCount')}</Th>
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ function FormTabGantt(props: FormEditProps) {
|
||||||
<Card className="mt-4">
|
<Card className="mt-4">
|
||||||
<h5 className="mb-4">{translate('::ListForms.SchedulerOptions.BasicSettings')}</h5>
|
<h5 className="mb-4">{translate('::ListForms.SchedulerOptions.BasicSettings')}</h5>
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.KeyFieldName')}
|
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName')}
|
||||||
invalid={errors.ganttOptionDto?.keyExpr && touched.ganttOptionDto?.keyExpr}
|
invalid={errors.ganttOptionDto?.keyExpr && touched.ganttOptionDto?.keyExpr}
|
||||||
errorMessage={errors.ganttOptionDto?.keyExpr}
|
errorMessage={errors.ganttOptionDto?.keyExpr}
|
||||||
>
|
>
|
||||||
|
|
@ -95,7 +95,7 @@ function FormTabGantt(props: FormEditProps) {
|
||||||
type="text"
|
type="text"
|
||||||
name="ganttOptionDto.keyExpr"
|
name="ganttOptionDto.keyExpr"
|
||||||
placeholder={translate(
|
placeholder={translate(
|
||||||
'::App.Listform.ListformField.KeyFieldName',
|
'::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
||||||
|
|
|
||||||
|
|
@ -354,7 +354,7 @@ function FormTabScheduler(props: FormEditProps) {
|
||||||
<Field name="schedulerOptionDto.allowAdding" component={Checkbox} />
|
<Field name="schedulerOptionDto.allowAdding" component={Checkbox} />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem label={translate('::ListForms.ListFormEdit.AllowEditing')}>
|
<FormItem label={translate('::ListForms.SchedulerOptions.AllowEditing')}>
|
||||||
<Field name="schedulerOptionDto.allowEditing" component={Checkbox} />
|
<Field name="schedulerOptionDto.allowEditing" component={Checkbox} />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ function FormTabSubForm() {
|
||||||
</Th>
|
</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.SubFormsTabTitle')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.SubFormsTabTitle')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.SubFormsTabType')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.SubFormsTabType')}</Th>
|
||||||
<Th>{translate('::App.Listform.ListformField.ListFormCode')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.SubFormsCode')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.SubFormsIsRefresh')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.SubFormsIsRefresh')}</Th>
|
||||||
<Th>{translate('::ListForms.ListFormEdit.SubFormsRelation')}</Th>
|
<Th>{translate('::ListForms.ListFormEdit.SubFormsRelation')}</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
|
|
|
||||||
|
|
@ -82,14 +82,14 @@ function FormTabTree(props: FormEditProps) {
|
||||||
<Form>
|
<Form>
|
||||||
<FormContainer size="sm">
|
<FormContainer size="sm">
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.KeyFieldName')}
|
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName')}
|
||||||
invalid={errors.treeOptionDto?.keyExpr && touched.treeOptionDto?.keyExpr}
|
invalid={errors.treeOptionDto?.keyExpr && touched.treeOptionDto?.keyExpr}
|
||||||
errorMessage={errors.treeOptionDto?.keyExpr}
|
errorMessage={errors.treeOptionDto?.keyExpr}
|
||||||
>
|
>
|
||||||
<Field
|
<Field
|
||||||
type="text"
|
type="text"
|
||||||
name="treeOptionDto.keyExpr"
|
name="treeOptionDto.keyExpr"
|
||||||
placeholder={translate('::App.Listform.ListformField.KeyFieldName')}
|
placeholder={translate('::ListForms.ListFormEdit.DatabaseDataSourceKeyFieldName')}
|
||||||
>
|
>
|
||||||
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
||||||
<Select
|
<Select
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import { tooltipFormatListOptions } from '@/proxy/admin/list-form/options'
|
||||||
const schema = object().shape({
|
const schema = object().shape({
|
||||||
fieldName: string().required().max(100),
|
fieldName: string().required().max(100),
|
||||||
captionName: string(),
|
captionName: string(),
|
||||||
placeHolder: string(),
|
|
||||||
bandName: string(),
|
bandName: string(),
|
||||||
sourceDbType: number().required(),
|
sourceDbType: number().required(),
|
||||||
})
|
})
|
||||||
|
|
@ -54,13 +53,6 @@ function FormFieldTabDetails({
|
||||||
>
|
>
|
||||||
<Field type="text" name="captionName" component={Input} />
|
<Field type="text" name="captionName" component={Input} />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem
|
|
||||||
label={translate('::ListForms.ListFormFieldEdit.PlaceHolder')}
|
|
||||||
invalid={errors.placeHolder && touched.placeHolder}
|
|
||||||
errorMessage={errors.placeHolder}
|
|
||||||
>
|
|
||||||
<Field type="text" name="placeHolder" component={Input} />
|
|
||||||
</FormItem>
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::ListForms.ListFormFieldEdit.DetailsBandName')}
|
label={translate('::ListForms.ListFormFieldEdit.DetailsBandName')}
|
||||||
invalid={errors.bandName && touched.bandName}
|
invalid={errors.bandName && touched.bandName}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ function FormFieldTabJoinOptions({
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.TableName')}
|
label={translate('::ListForms.ListFormEdit.DatabaseDataSourceTableName')}
|
||||||
invalid={
|
invalid={
|
||||||
errors.columnJoinTableDto?.tableName && touched.columnJoinTableDto?.tableName
|
errors.columnJoinTableDto?.tableName && touched.columnJoinTableDto?.tableName
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +88,7 @@ function FormFieldTabJoinOptions({
|
||||||
type="text"
|
type="text"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
name="columnJoinTableDto.tableName"
|
name="columnJoinTableDto.tableName"
|
||||||
placeholder={translate('::App.Listform.ListformField.TableName')}
|
placeholder={translate('::ListForms.ListFormEdit.DatabaseDataSourceTableName')}
|
||||||
component={Input}
|
component={Input}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ function FormFieldTabOptions({
|
||||||
<FormItem label={translate('::ListForms.ListFormFieldEdit.HeaderingAllowSearch')}>
|
<FormItem label={translate('::ListForms.ListFormFieldEdit.HeaderingAllowSearch')}>
|
||||||
<Field name="allowSearch" component={Checkbox} />
|
<Field name="allowSearch" component={Checkbox} />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label={translate('::ListForms.ListFormEdit.AllowEditing')}>
|
<FormItem label={translate('::ListForms.ListFormEdit.EditingAllowEditing')}>
|
||||||
<Field name="allowEditing" component={Checkbox} />
|
<Field name="allowEditing" component={Checkbox} />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -340,7 +340,7 @@ function FormFields({
|
||||||
<Field type="text" name="cultureName">
|
<Field type="text" name="cultureName">
|
||||||
{({ field, form }: FieldProps<LanguageInfo>) => (
|
{({ field, form }: FieldProps<LanguageInfo>) => (
|
||||||
<Select
|
<Select
|
||||||
placeholder={translate('::App.Listform.ListformField.CultureName')}
|
placeholder={translate('::ListForms.ListFormField.FilterCultureName')}
|
||||||
field={field}
|
field={field}
|
||||||
form={form}
|
form={form}
|
||||||
options={langOptions}
|
options={langOptions}
|
||||||
|
|
@ -593,7 +593,7 @@ function FormFields({
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.CultureName')}
|
label={translate('::ListForms.ListFormEdit.DetailsCultureName')}
|
||||||
invalid={errors.cultureName && touched.cultureName}
|
invalid={errors.cultureName && touched.cultureName}
|
||||||
errorMessage={errors.cultureName}
|
errorMessage={errors.cultureName}
|
||||||
asterisk={true}
|
asterisk={true}
|
||||||
|
|
@ -602,7 +602,7 @@ function FormFields({
|
||||||
type="text"
|
type="text"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
name="cultureName"
|
name="cultureName"
|
||||||
placeholder={translate('::App.Listform.ListformField.CultureName')}
|
placeholder={translate('::ListForms.ListFormEdit.DetailsCultureName')}
|
||||||
>
|
>
|
||||||
{({ field, form }: FieldProps<LanguageInfo>) => (
|
{({ field, form }: FieldProps<LanguageInfo>) => (
|
||||||
<Select
|
<Select
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ function JsonRowOpDialogDatabase({
|
||||||
</Field>
|
</Field>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.Value')}
|
label={translate('::ListForms.ListFormEdit.Value')}
|
||||||
invalid={errors.value && touched.value}
|
invalid={errors.value && touched.value}
|
||||||
errorMessage={errors.value}
|
errorMessage={errors.value}
|
||||||
>
|
>
|
||||||
|
|
@ -212,7 +212,7 @@ function JsonRowOpDialogDatabase({
|
||||||
type="text"
|
type="text"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
name="value"
|
name="value"
|
||||||
placeholder={translate('::App.Listform.ListformField.Value')}
|
placeholder={translate('::ListForms.ListFormEdit.Value')}
|
||||||
component={Input}
|
component={Input}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
|
||||||
|
|
@ -430,24 +430,24 @@ const OrganizationUnits = () => {
|
||||||
|
|
||||||
<div className="file-actions">
|
<div className="file-actions">
|
||||||
<div className="flex gap-1 folderFileActions">
|
<div className="flex gap-1 folderFileActions">
|
||||||
<Button
|
<button
|
||||||
onClick={() => setIsMoveAllUsersOpen(true)}
|
onClick={() => setIsMoveAllUsersOpen(true)}
|
||||||
title={translate('::Abp.Identity.OrganizationUnit.MoveAllUsers')}
|
title={translate('::Abp.Identity.OrganizationUnit.MoveAllUsers')}
|
||||||
>
|
>
|
||||||
<FaUserPlus size="20" color="#2d6da3" />
|
<FaUserPlus size="20" color="#2d6da3" />
|
||||||
</Button>
|
</button>
|
||||||
<Button
|
<button
|
||||||
onClick={() => node.edit()}
|
onClick={() => node.edit()}
|
||||||
title={translate('::Abp.Identity.OrganizationUnit.Rename')}
|
title={translate('::Abp.Identity.OrganizationUnit.Rename')}
|
||||||
>
|
>
|
||||||
<FaEdit size="20" className="text-teal-900" />
|
<FaEdit size="20" className="text-teal-900" />
|
||||||
</Button>
|
</button>
|
||||||
<Button
|
<button
|
||||||
onClick={() => setDeleteRow({ id: node.data.id, name: 'Organization Unit' })}
|
onClick={() => setDeleteRow({ id: node.data.id, name: 'Organization Unit' })}
|
||||||
title={translate('::Delete')}
|
title={translate('::Delete')}
|
||||||
>
|
>
|
||||||
<FaTrashAlt size="20" className="text-red-500" />
|
<FaTrashAlt size="20" className="text-red-500" />
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { FaChevronRight, FaChevronDown } from 'react-icons/fa'
|
|
||||||
import Container from '@/components/shared/Container'
|
import Container from '@/components/shared/Container'
|
||||||
import { Button, Checkbox, Dialog, Input, Menu, toast } from '@/components/ui'
|
import { Button, Checkbox, Dialog, Input, Menu, toast } from '@/components/ui'
|
||||||
import { useConfig } from '@/components/ui/ConfigProvider'
|
import { useConfig } from '@/components/ui/ConfigProvider'
|
||||||
|
|
@ -9,14 +8,13 @@ import {
|
||||||
PermissionGroupDto,
|
PermissionGroupDto,
|
||||||
PermissionWithGroupName,
|
PermissionWithGroupName,
|
||||||
PermissionWithStyle,
|
PermissionWithStyle,
|
||||||
|
UpdatePermissionDto,
|
||||||
} from '@/proxy/admin/models'
|
} from '@/proxy/admin/models'
|
||||||
import { getPermissions, getRoles, updatePermissions } from '@/services/identity.service'
|
import { getPermissions, updatePermissions } from '@/services/identity.service'
|
||||||
import { useStoreActions, useStoreState } from '@/store'
|
import { useStoreActions, useStoreState } from '@/store'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { ChangeEvent, useEffect, useMemo, useState } from 'react'
|
import { ChangeEvent, useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
type OpenState = Record<string, boolean>
|
|
||||||
|
|
||||||
function RolesPermission({
|
function RolesPermission({
|
||||||
open,
|
open,
|
||||||
onDialogClose,
|
onDialogClose,
|
||||||
|
|
@ -26,24 +24,6 @@ function RolesPermission({
|
||||||
onDialogClose: () => void
|
onDialogClose: () => void
|
||||||
name: string
|
name: string
|
||||||
}) {
|
}) {
|
||||||
// Collapse/expand state
|
|
||||||
const [openPermissions, setOpenPermissions] = useState<OpenState>({})
|
|
||||||
|
|
||||||
// Parent izinleri aç/kapat
|
|
||||||
const togglePermission = (permName: string) => {
|
|
||||||
setOpenPermissions((prev) => ({ ...prev, [permName]: !prev[permName] }))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parent izin mi?
|
|
||||||
function isParent(permission: PermissionWithStyle) {
|
|
||||||
return selectedGroupPermissions.some((p) => p.parentName === permission.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Belirli parent'ın alt izinleri
|
|
||||||
function getChildren(parentName: string) {
|
|
||||||
if (!parentName) return []
|
|
||||||
return selectedGroupPermissions.filter((p) => p.parentName === parentName)
|
|
||||||
}
|
|
||||||
const providerName = 'R'
|
const providerName = 'R'
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
const { getConfig } = useStoreActions((a) => a.abpConfig)
|
const { getConfig } = useStoreActions((a) => a.abpConfig)
|
||||||
|
|
@ -155,28 +135,6 @@ function RolesPermission({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Expand/Collapse All fonksiyonları (tüm state ve yardımcı fonksiyonlardan sonra) ---
|
|
||||||
const handleExpandAll = () => {
|
|
||||||
const expanded: OpenState = {}
|
|
||||||
function expandRecursively(perms: PermissionWithStyle[]) {
|
|
||||||
perms.forEach((perm) => {
|
|
||||||
if (isParent(perm)) {
|
|
||||||
expanded[perm.name || ''] = true
|
|
||||||
// Alt parent'ları da recursive aç
|
|
||||||
const children = getChildren(perm.name || '')
|
|
||||||
expandRecursively(children)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// Tüm parent ve alt parent'ları recursive olarak aç
|
|
||||||
expandRecursively(selectedGroupPermissions)
|
|
||||||
setOpenPermissions(expanded)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCollapseAll = () => {
|
|
||||||
setOpenPermissions({})
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPermissionsWithGroupName(groups: PermissionGroupDto[]): PermissionWithGroupName[] {
|
function getPermissionsWithGroupName(groups: PermissionGroupDto[]): PermissionWithGroupName[] {
|
||||||
return groups.reduce(
|
return groups.reduce(
|
||||||
(acc, val) => [
|
(acc, val) => [
|
||||||
|
|
@ -216,43 +174,34 @@ function RolesPermission({
|
||||||
[selectedGroupPermissions],
|
[selectedGroupPermissions],
|
||||||
)
|
)
|
||||||
|
|
||||||
// Collapse/expand için recursive düzende izinleri döndür
|
|
||||||
const filteredPermissions = useMemo(() => {
|
const filteredPermissions = useMemo(() => {
|
||||||
const lowerTerm = searchTerm.toLowerCase()
|
if (!searchTerm) {
|
||||||
|
return selectedGroupPermissions
|
||||||
// Filtreli veya tüm parent izinler
|
|
||||||
const parents = selectedGroupPermissions.filter(
|
|
||||||
(p) =>
|
|
||||||
!p.parentName &&
|
|
||||||
(searchTerm === '' ||
|
|
||||||
translate('::' + p.displayName)
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(lowerTerm)),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Recursive children builder (her seviye için doğru level)
|
|
||||||
function buildTree(
|
|
||||||
parent: PermissionWithStyle,
|
|
||||||
level: number,
|
|
||||||
): (PermissionWithStyle & { level: number })[] {
|
|
||||||
const arr = [{ ...parent, level }]
|
|
||||||
const parentKey = parent.name || ''
|
|
||||||
if (openPermissions[parentKey] || searchTerm) {
|
|
||||||
const children = getChildren(parentKey)
|
|
||||||
children.forEach((child) => {
|
|
||||||
arr.push(...buildTree(child, level + 1))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tüm parentlar ve altlarını sırayla ekle
|
const lowerTerm = searchTerm.toLowerCase()
|
||||||
let result: (PermissionWithStyle & { level: number })[] = []
|
|
||||||
parents.forEach((parent) => {
|
// 1️⃣ Sadece 1. kırınımdakileri (parent olmayanları değil) ara
|
||||||
result = result.concat(buildTree(parent, 0))
|
const topLevelMatches = selectedGroupPermissions.filter(
|
||||||
|
(p) =>
|
||||||
|
!p.parentName && // parentName yoksa bu 1. kırınım demektir
|
||||||
|
translate('::' + p.displayName)
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(lowerTerm),
|
||||||
|
)
|
||||||
|
|
||||||
|
// 2️⃣ Her bulunan parent’ın altındaki child izinleri ekle
|
||||||
|
const result: PermissionWithStyle[] = []
|
||||||
|
topLevelMatches.forEach((parent) => {
|
||||||
|
result.push(parent)
|
||||||
|
|
||||||
|
// Alt kırılımları bul
|
||||||
|
const children = selectedGroupPermissions.filter((child) => child.parentName === parent.name)
|
||||||
|
result.push(...children)
|
||||||
})
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}, [selectedGroupPermissions, searchTerm, translate, openPermissions])
|
}, [selectedGroupPermissions, searchTerm, translate])
|
||||||
|
|
||||||
const onSelectAll = (value: boolean, e: ChangeEvent<HTMLInputElement>) => {
|
const onSelectAll = (value: boolean, e: ChangeEvent<HTMLInputElement>) => {
|
||||||
if (!permissionList) {
|
if (!permissionList) {
|
||||||
|
|
@ -311,55 +260,6 @@ function RolesPermission({
|
||||||
fetchDataPermissions()
|
fetchDataPermissions()
|
||||||
}, [name])
|
}, [name])
|
||||||
|
|
||||||
// --- Copy Permissions State and Logic ---
|
|
||||||
const [roleList, setRoleList] = useState<string[]>([])
|
|
||||||
|
|
||||||
// --- Copy Permissions Dialog State ---
|
|
||||||
const [copyDialogOpen, setCopyDialogOpen] = useState(false)
|
|
||||||
const [copyDialogRole, setCopyDialogRole] = useState('')
|
|
||||||
|
|
||||||
// Fetch all roles for select (except current)
|
|
||||||
useEffect(() => {
|
|
||||||
async function fetchRoles() {
|
|
||||||
try {
|
|
||||||
const res = await getRoles()
|
|
||||||
setRoleList(res.data.items?.map((r: any) => r.name) || [])
|
|
||||||
} catch (e) {
|
|
||||||
setRoleList([])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fetchRoles()
|
|
||||||
}, [copyDialogOpen])
|
|
||||||
|
|
||||||
// Dialog üzerinden kopyalama işlemi
|
|
||||||
const handleCopyDialogConfirm = async () => {
|
|
||||||
if (!copyDialogRole || !permissionList) return
|
|
||||||
setIsLoading(true)
|
|
||||||
try {
|
|
||||||
const res = await getPermissions(providerName, copyDialogRole)
|
|
||||||
const sourcePerms = getPermissionsWithGroupName(res.data?.groups)
|
|
||||||
const grantedNames = new Set(sourcePerms.filter((p) => p.isGranted).map((p) => p.name))
|
|
||||||
permissionList.groups.forEach((group) => {
|
|
||||||
group.permissions.forEach((perm) => {
|
|
||||||
perm.isGranted = grantedNames.has(perm.name)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
setPermissionList({ ...permissionList })
|
|
||||||
changeGroup(selectedGroup?.name)
|
|
||||||
toast.push(<Notification title={translate('::PermissionsCopied')} type="success" />, {
|
|
||||||
placement: 'top-end',
|
|
||||||
})
|
|
||||||
setCopyDialogOpen(false)
|
|
||||||
setCopyDialogRole('')
|
|
||||||
} catch (e) {
|
|
||||||
toast.push(<Notification title={translate('::CopyFailed')} type="danger" />, {
|
|
||||||
placement: 'top-end',
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return permissionList ? (
|
return permissionList ? (
|
||||||
<Container>
|
<Container>
|
||||||
<Dialog
|
<Dialog
|
||||||
|
|
@ -384,13 +284,6 @@ function RolesPermission({
|
||||||
<Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}>
|
<Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}>
|
||||||
{translate('AbpPermissionManagement::SelectAllInThisTab')}
|
{translate('AbpPermissionManagement::SelectAllInThisTab')}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
|
|
||||||
<Button size="sm" variant="plain" onClick={handleExpandAll} icon={<FaChevronRight />}>
|
|
||||||
{translate('::ListForms.ListFormEdit.ExpandAll')}
|
|
||||||
</Button>
|
|
||||||
<Button size="sm" variant="plain" onClick={handleCollapseAll} icon={<FaChevronDown />}>
|
|
||||||
{translate('::ListForms.ListFormEdit.CollapseAll')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -417,110 +310,35 @@ function RolesPermission({
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full md:w-2/3 max-h-[450px] overflow-y-auto">
|
<div className="w-full md:w-2/3 max-h-[450px] overflow-y-auto">
|
||||||
<hr className="mb-2"></hr>
|
<hr className="mb-2"></hr>
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<Input
|
||||||
<Input
|
size="sm"
|
||||||
size="sm"
|
className="mb-2"
|
||||||
className=""
|
placeholder={translate('::Search')}
|
||||||
placeholder={translate('::Search')}
|
value={searchTerm}
|
||||||
value={searchTerm}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="my-1">
|
<div className="my-1">
|
||||||
{filteredPermissions.map((permission) => {
|
{filteredPermissions.map((permission) => (
|
||||||
const isParentPerm = isParent(permission)
|
<div key={permission.name}>
|
||||||
const permKey = permission.name || ''
|
<Checkbox
|
||||||
const children = getChildren(permKey)
|
name={permission.name}
|
||||||
return (
|
className={permission.class}
|
||||||
<div key={permission.name} className={`ml-${permission.level * 4} group`}>
|
checked={permission.isGranted}
|
||||||
<div className="flex items-center gap-2 px-2 py-0.5 rounded-md hover:bg-gray-50 transition-all">
|
onChange={() => onClickCheckbox(permission)}
|
||||||
{/* Expand Icon */}
|
>
|
||||||
{isParentPerm ? (
|
{translate('::' + permission.displayName)}
|
||||||
<button
|
</Checkbox>
|
||||||
onClick={(e) => {
|
</div>
|
||||||
e.stopPropagation()
|
))}
|
||||||
togglePermission(permKey)
|
|
||||||
}}
|
|
||||||
className="w-5 h-5 flex items-center justify-center rounded hover:bg-gray-200 transition"
|
|
||||||
>
|
|
||||||
{openPermissions[permKey] || searchTerm ? (
|
|
||||||
<FaChevronDown className="text-gray-500 text-xs" />
|
|
||||||
) : (
|
|
||||||
<FaChevronRight className="text-gray-500 text-xs" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<div className="w-5" />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Checkbox */}
|
|
||||||
<Checkbox
|
|
||||||
name={permission.name}
|
|
||||||
checked={permission.isGranted}
|
|
||||||
onChange={() => onClickCheckbox(permission)}
|
|
||||||
>
|
|
||||||
<span className="text-sm text-gray-700 group-hover:text-gray-900 transition">
|
|
||||||
{translate('::' + permission.displayName)}
|
|
||||||
</span>
|
|
||||||
</Checkbox>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center mt-6">
|
<div className="text-right mt-6">
|
||||||
<div className="flex items-center">
|
<Button className="ltr:mr-2 rtl:ml-2" variant="plain" onClick={onDialogClose}>
|
||||||
<Button variant="solid" color="sky-500" onClick={() => setCopyDialogOpen(true)}>
|
|
||||||
{translate('::AbpIdentity.Roles.CopyPermissions')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="text-right">
|
|
||||||
<Button className="ltr:mr-2 rtl:ml-2" variant="plain" onClick={onDialogClose}>
|
|
||||||
{translate('::Cancel')}
|
|
||||||
</Button>
|
|
||||||
<Button variant="solid" loading={isLoading} onClick={onDialogOk}>
|
|
||||||
{isLoading ? translate('::SavingWithThreeDot') : translate('::Save')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
{/* Copy Permissions Dialog */}
|
|
||||||
<Dialog
|
|
||||||
isOpen={copyDialogOpen}
|
|
||||||
width="min(400px, 95vw)"
|
|
||||||
onClose={() => setCopyDialogOpen(false)}
|
|
||||||
>
|
|
||||||
<h5 className="mb-2">{translate('::AbpIdentity.Roles.CopyPermissions')}</h5>
|
|
||||||
<div className="mb-4">
|
|
||||||
<select
|
|
||||||
className="border rounded px-2 py-1 w-full"
|
|
||||||
value={copyDialogRole}
|
|
||||||
onChange={(e) => setCopyDialogRole(e.target.value)}
|
|
||||||
>
|
|
||||||
<option value="">{translate('::App.Select')}</option>
|
|
||||||
{roleList
|
|
||||||
.filter((role) => role !== name)
|
|
||||||
.map((role) => (
|
|
||||||
<option key={role} value={role}>
|
|
||||||
{role}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-end gap-2">
|
|
||||||
<Button variant="plain" onClick={() => setCopyDialogOpen(false)}>
|
|
||||||
{translate('::Cancel')}
|
{translate('::Cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button variant="solid" loading={isLoading} onClick={onDialogOk}>
|
||||||
variant="solid"
|
{isLoading ? translate('::SavingWithThreeDot') : translate('::Save')}
|
||||||
onClick={handleCopyDialogConfirm}
|
|
||||||
disabled={!copyDialogRole || isLoading}
|
|
||||||
loading={isLoading}
|
|
||||||
>
|
|
||||||
{translate('::Copy')}
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
|
||||||
|
|
@ -318,7 +318,7 @@ function TenantConnectionString({
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.ConnectionString')}
|
label="Connection String"
|
||||||
invalid={errors.value && touched.value}
|
invalid={errors.value && touched.value}
|
||||||
errorMessage={errors.value}
|
errorMessage={errors.value}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -271,7 +271,7 @@ function UserDetails() {
|
||||||
<Field
|
<Field
|
||||||
type="text"
|
type="text"
|
||||||
name="phoneNumber"
|
name="phoneNumber"
|
||||||
placeholder={translate('::Abp.Identity.User.UserInformation.PhoneNumber')}
|
placeholder="Phone Number"
|
||||||
component={Input}
|
component={Input}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { FaChevronRight, FaChevronDown } from 'react-icons/fa'
|
import AdaptableCard from '@/components/shared/AdaptableCard'
|
||||||
import { Badge, Button, Checkbox, Dialog, Input, Menu, toast } from '@/components/ui'
|
import Container from '@/components/shared/Container'
|
||||||
type OpenState = Record<string, boolean>
|
import { Badge, Button, Checkbox, Dialog, Menu, toast } from '@/components/ui'
|
||||||
import { useConfig } from '@/components/ui/ConfigProvider'
|
import { useConfig } from '@/components/ui/ConfigProvider'
|
||||||
import Notification from '@/components/ui/Notification'
|
import Notification from '@/components/ui/Notification'
|
||||||
import {
|
import {
|
||||||
|
|
@ -10,6 +10,7 @@ import {
|
||||||
PermissionWithGroupName,
|
PermissionWithGroupName,
|
||||||
PermissionWithStyle,
|
PermissionWithStyle,
|
||||||
ProviderInfoDto,
|
ProviderInfoDto,
|
||||||
|
UpdatePermissionDto,
|
||||||
} from '@/proxy/admin/models'
|
} from '@/proxy/admin/models'
|
||||||
import { getPermissions, updatePermissions } from '@/services/identity.service'
|
import { getPermissions, updatePermissions } from '@/services/identity.service'
|
||||||
import { useStoreActions, useStoreState } from '@/store'
|
import { useStoreActions, useStoreState } from '@/store'
|
||||||
|
|
@ -38,44 +39,6 @@ function UsersPermission({
|
||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
|
|
||||||
// Collapse/expand state
|
|
||||||
const [openPermissions, setOpenPermissions] = useState<OpenState>({})
|
|
||||||
|
|
||||||
// Parent izinleri aç/kapat
|
|
||||||
const togglePermission = (permName: string) => {
|
|
||||||
setOpenPermissions((prev) => ({ ...prev, [permName]: !prev[permName] }))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parent izin mi?
|
|
||||||
function isParent(permission: PermissionWithStyle) {
|
|
||||||
return selectedGroupPermissions.some((p) => p.parentName === permission.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Belirli parent'ın alt izinleri
|
|
||||||
function getChildren(parentName: string) {
|
|
||||||
if (!parentName) return []
|
|
||||||
return selectedGroupPermissions.filter((p) => p.parentName === parentName)
|
|
||||||
}
|
|
||||||
// --- Expand/Collapse All fonksiyonları ---
|
|
||||||
const handleExpandAll = () => {
|
|
||||||
const expanded: OpenState = {}
|
|
||||||
function expandRecursively(perms: PermissionWithStyle[]) {
|
|
||||||
perms.forEach((perm) => {
|
|
||||||
if (isParent(perm)) {
|
|
||||||
expanded[perm.name || ''] = true
|
|
||||||
const children = getChildren(perm.name || '')
|
|
||||||
expandRecursively(children)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
expandRecursively(selectedGroupPermissions)
|
|
||||||
setOpenPermissions(expanded)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCollapseAll = () => {
|
|
||||||
setOpenPermissions({})
|
|
||||||
}
|
|
||||||
|
|
||||||
const mode = useStoreState((state) => state.theme.mode)
|
const mode = useStoreState((state) => state.theme.mode)
|
||||||
|
|
||||||
const { direction } = useConfig()
|
const { direction } = useConfig()
|
||||||
|
|
@ -253,42 +216,6 @@ function UsersPermission({
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Search ve recursive treeview ---
|
|
||||||
// Collapse/expand için recursive düzende izinleri döndür
|
|
||||||
const [searchTerm, setSearchTerm] = useState('')
|
|
||||||
const filteredPermissions = useMemo(() => {
|
|
||||||
const lowerTerm = searchTerm.toLowerCase()
|
|
||||||
// Filtreli veya tüm parent izinler
|
|
||||||
const parents = selectedGroupPermissions.filter(
|
|
||||||
(p) =>
|
|
||||||
!p.parentName &&
|
|
||||||
(searchTerm === '' ||
|
|
||||||
translate('::' + p.displayName)
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(lowerTerm)),
|
|
||||||
)
|
|
||||||
// Recursive children builder (her seviye için doğru level)
|
|
||||||
function buildTree(
|
|
||||||
parent: PermissionWithStyle,
|
|
||||||
level: number,
|
|
||||||
): (PermissionWithStyle & { level: number })[] {
|
|
||||||
const arr = [{ ...parent, level }]
|
|
||||||
const parentKey = parent.name || ''
|
|
||||||
if (openPermissions[parentKey] || searchTerm) {
|
|
||||||
const children = getChildren(parentKey)
|
|
||||||
children.forEach((child) => {
|
|
||||||
arr.push(...buildTree(child, level + 1))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
let result: (PermissionWithStyle & { level: number })[] = []
|
|
||||||
parents.forEach((parent) => {
|
|
||||||
result = result.concat(buildTree(parent, 0))
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
}, [selectedGroupPermissions, searchTerm, translate, openPermissions])
|
|
||||||
|
|
||||||
return permissionList ? (
|
return permissionList ? (
|
||||||
<Dialog
|
<Dialog
|
||||||
width="min(900px, 95vw)"
|
width="min(900px, 95vw)"
|
||||||
|
|
@ -297,7 +224,7 @@ function UsersPermission({
|
||||||
onClose={onDialogClose}
|
onClose={onDialogClose}
|
||||||
onRequestClose={onDialogClose}
|
onRequestClose={onDialogClose}
|
||||||
>
|
>
|
||||||
<h5 className="mb-1">
|
<h5 className="mb-4">
|
||||||
{translate('::Permission')} - {name}
|
{translate('::Permission')} - {name}
|
||||||
</h5>
|
</h5>
|
||||||
<hr className="mt-1 mb-2"></hr>
|
<hr className="mt-1 mb-2"></hr>
|
||||||
|
|
@ -312,18 +239,12 @@ function UsersPermission({
|
||||||
<Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}>
|
<Checkbox name="group" checked={isAllSelectedForGroup} onChange={onSelectAll}>
|
||||||
{translate('AbpPermissionManagement::SelectAllInThisTab')}
|
{translate('AbpPermissionManagement::SelectAllInThisTab')}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
<Button size="sm" variant="plain" onClick={handleExpandAll} icon={<FaChevronRight />}>
|
|
||||||
{translate('::ListForms.ListFormEdit.ExpandAll')}
|
|
||||||
</Button>
|
|
||||||
<Button size="sm" variant="plain" onClick={handleCollapseAll} icon={<FaChevronDown />}>
|
|
||||||
{translate('::ListForms.ListFormEdit.CollapseAll')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col md:flex-row gap-4">
|
<div className="flex flex-col md:flex-row gap-4">
|
||||||
<div className="w-full md:w-1/3 max-h-[450px] overflow-y-auto">
|
<div className="w-full md:w-1/3 max-h-[450px] overflow-y-auto">
|
||||||
<hr className="mb-2"></hr>
|
<hr className="mt-2 mb-2"></hr>
|
||||||
<Menu variant={mode} defaultActiveKeys={[selectedGroup?.displayName ?? '']}>
|
<Menu variant={mode} defaultActiveKeys={[selectedGroup?.displayName ?? '']}>
|
||||||
{permissionList?.groups.map((group) => (
|
{permissionList?.groups.map((group) => (
|
||||||
<Menu.MenuItem
|
<Menu.MenuItem
|
||||||
|
|
@ -341,70 +262,31 @@ function UsersPermission({
|
||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full md:w-2/3 max-h-[450px] overflow-y-auto">
|
<div className="w-full md:w-2/3 max-h-[450px] overflow-y-auto">
|
||||||
<hr className="mb-2"></hr>
|
<hr className="mt-2 mb-2"></hr>
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="card-body">
|
||||||
<Input
|
{selectedGroupPermissions.map((permission) => (
|
||||||
size="sm"
|
<div key={permission.name}>
|
||||||
className=""
|
<Checkbox
|
||||||
placeholder={translate('::Search')}
|
name={permission.name}
|
||||||
value={searchTerm}
|
className={permission.class}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
disabled={isGrantedByOtherProviderName(permission.grantedProviders)}
|
||||||
/>
|
//disabled={permission.grantedProviders.length > 0 ? true : false}
|
||||||
</div>
|
checked={permission.isGranted}
|
||||||
<div className="my-1">
|
onChange={() => onClickCheckbox(permission)}
|
||||||
{filteredPermissions.map((permission) => {
|
>
|
||||||
const isParentPerm = isParent(permission)
|
{translate('::' + permission.displayName)}
|
||||||
const permKey = permission.name || ''
|
{permission.grantedProviders.map((provider) => (
|
||||||
const children = getChildren(permKey)
|
<Badge
|
||||||
return (
|
key={provider.providerKey}
|
||||||
<div key={permission.name} className={`ml-${permission.level * 4} group`}>
|
className="m-2"
|
||||||
<div className="flex items-center gap-2 px-2 py-0.5 rounded-md hover:bg-gray-50 transition-all">
|
content={`${provider.providerName}${
|
||||||
{/* Expand Icon */}
|
provider.providerName !== providerName ? `: ${provider.providerKey}` : ''
|
||||||
{isParentPerm ? (
|
}`}
|
||||||
<button
|
/>
|
||||||
onClick={(e) => {
|
))}
|
||||||
e.stopPropagation()
|
</Checkbox>
|
||||||
togglePermission(permKey)
|
</div>
|
||||||
}}
|
))}
|
||||||
className="w-5 h-5 flex items-center justify-center rounded hover:bg-gray-200 transition"
|
|
||||||
>
|
|
||||||
{openPermissions[permKey] || searchTerm ? (
|
|
||||||
<FaChevronDown className="text-gray-500 text-xs" />
|
|
||||||
) : (
|
|
||||||
<FaChevronRight className="text-gray-500 text-xs" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<div className="w-5" />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Checkbox */}
|
|
||||||
<Checkbox
|
|
||||||
name={permission.name}
|
|
||||||
checked={permission.isGranted}
|
|
||||||
onChange={() => onClickCheckbox(permission)}
|
|
||||||
disabled={permission.grantedProviders.some((provider) => provider.providerName === 'R')}
|
|
||||||
>
|
|
||||||
<span className="text-sm text-gray-700 group-hover:text-gray-900 transition">
|
|
||||||
{translate('::' + permission.displayName)}
|
|
||||||
{permission.grantedProviders.map((provider) => {
|
|
||||||
const badgeContent = provider.providerName !== providerName
|
|
||||||
? `${provider.providerName}: ${provider.providerKey}`
|
|
||||||
: provider.providerName;
|
|
||||||
return (
|
|
||||||
<Badge
|
|
||||||
key={provider.providerKey}
|
|
||||||
className="m-2"
|
|
||||||
content={badgeContent}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</Checkbox>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import { Container } from '@/components/shared'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { APP_NAME } from '@/constants/app.constant'
|
import { APP_NAME } from '@/constants/app.constant'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { AI_ASSISTANT } from '@/constants/permission.constant'
|
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
type ChatType = 'chat' | 'query' | 'analyze'
|
type ChatType = 'chat' | 'query' | 'analyze'
|
||||||
|
|
@ -294,7 +293,7 @@ const Assistant = () => {
|
||||||
<Container>
|
<Container>
|
||||||
<Helmet
|
<Helmet
|
||||||
titleTemplate={`%s | ${APP_NAME}`}
|
titleTemplate={`%s | ${APP_NAME}`}
|
||||||
title={translate('::' + AI_ASSISTANT)}
|
title={translate('::' + 'App.AiBot.Asistant')}
|
||||||
defaultTitle={APP_NAME}
|
defaultTitle={APP_NAME}
|
||||||
></Helmet>
|
></Helmet>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -314,7 +314,7 @@ export default ${pascalCaseName}Component;`
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Platform.Code')}
|
label={translate('::App.DeveloperKit.ComponentEditor.Code')}
|
||||||
invalid={!!(errors.code && touched.code)}
|
invalid={!!(errors.code && touched.code)}
|
||||||
errorMessage={errors.code as string}
|
errorMessage={errors.code as string}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ const DynamicServiceManager: React.FC = () => {
|
||||||
|
|
||||||
const stats = [
|
const stats = [
|
||||||
{
|
{
|
||||||
name: translate('::App.Listform.ListformField.Total'),
|
name: translate('::App.DeveloperKit.DynamicServices.Total'),
|
||||||
value: totalServices,
|
value: totalServices,
|
||||||
icon: FaCode,
|
icon: FaCode,
|
||||||
color: 'text-purple-600',
|
color: 'text-purple-600',
|
||||||
|
|
|
||||||
|
|
@ -1500,7 +1500,7 @@ const SqlTableDesignerDialog = ({
|
||||||
if (result.data.success) {
|
if (result.data.success) {
|
||||||
const deployedTable = settings.tableName || initialTableData?.tableName || ''
|
const deployedTable = settings.tableName || initialTableData?.tableName || ''
|
||||||
toast.push(
|
toast.push(
|
||||||
<Notification type="success" title={translate('::App.Platform.Success')}>
|
<Notification type="success" title={translate('::App.SqlQueryManager.Success')}>
|
||||||
{`${translate(isEditMode ? '::App.SqlQueryManager.TableUpdated' : '::App.SqlQueryManager.TableCreated')}: [dbo].[${deployedTable}]`}
|
{`${translate(isEditMode ? '::App.SqlQueryManager.TableUpdated' : '::App.SqlQueryManager.TableCreated')}: [dbo].[${deployedTable}]`}
|
||||||
</Notification>,
|
</Notification>,
|
||||||
{ placement: 'top-center' },
|
{ placement: 'top-center' },
|
||||||
|
|
@ -1509,7 +1509,7 @@ const SqlTableDesignerDialog = ({
|
||||||
handleClose()
|
handleClose()
|
||||||
} else {
|
} else {
|
||||||
toast.push(
|
toast.push(
|
||||||
<Notification type="danger" title={translate('::App.Platform.Error')}>
|
<Notification type="danger" title={translate('::App.SqlQueryManager.Error')}>
|
||||||
{result.data.message || translate('::App.SqlQueryManager.TableCreationFailed')}
|
{result.data.message || translate('::App.SqlQueryManager.TableCreationFailed')}
|
||||||
</Notification>,
|
</Notification>,
|
||||||
{ placement: 'top-center' },
|
{ placement: 'top-center' },
|
||||||
|
|
@ -1517,7 +1517,7 @@ const SqlTableDesignerDialog = ({
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.push(
|
toast.push(
|
||||||
<Notification type="danger" title={translate('::App.Platform.Error')}>
|
<Notification type="danger" title={translate('::App.SqlQueryManager.Error')}>
|
||||||
{error.response?.data?.error?.message ||
|
{error.response?.data?.error?.message ||
|
||||||
translate('::App.SqlQueryManager.TableDeployFailed')}
|
translate('::App.SqlQueryManager.TableDeployFailed')}
|
||||||
</Notification>,
|
</Notification>,
|
||||||
|
|
@ -1873,7 +1873,7 @@ const SqlTableDesignerDialog = ({
|
||||||
{/* Table Name (readonly, auto-generated) */}
|
{/* Table Name (readonly, auto-generated) */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium mb-1">
|
<label className="block text-sm font-medium mb-1">
|
||||||
{translate('::App.Listform.ListformField.TableName')}
|
{translate('::App.SqlQueryManager.TableName')}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -2057,7 +2057,7 @@ const SqlTableDesignerDialog = ({
|
||||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white focus:ring-2 focus:ring-indigo-500"
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white focus:ring-2 focus:ring-indigo-500"
|
||||||
>
|
>
|
||||||
<option value="">
|
<option value="">
|
||||||
{translate('::App.Select')}
|
{translate('::App.SqlQueryManager.SelectPlaceholder')}
|
||||||
</option>
|
</option>
|
||||||
{columns
|
{columns
|
||||||
.filter((c) => c.columnName.trim())
|
.filter((c) => c.columnName.trim())
|
||||||
|
|
@ -2082,7 +2082,7 @@ const SqlTableDesignerDialog = ({
|
||||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white focus:ring-2 focus:ring-indigo-500"
|
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-sm dark:bg-gray-700 dark:text-white focus:ring-2 focus:ring-indigo-500"
|
||||||
>
|
>
|
||||||
<option value="">
|
<option value="">
|
||||||
{translate('::App.Select')}
|
{translate('::App.SqlQueryManager.SelectPlaceholder')}
|
||||||
</option>
|
</option>
|
||||||
{dbTables.map((t) => (
|
{dbTables.map((t) => (
|
||||||
<option key={`${t.schemaName}.${t.tableName}`} value={t.tableName}>
|
<option key={`${t.schemaName}.${t.tableName}`} value={t.tableName}>
|
||||||
|
|
@ -2187,6 +2187,7 @@ const SqlTableDesignerDialog = ({
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Description */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-xs font-semibold text-gray-700 dark:text-gray-300 mb-1.5">
|
<label className="block text-xs font-semibold text-gray-700 dark:text-gray-300 mb-1.5">
|
||||||
{translate('::App.SqlQueryManager.Description')}
|
{translate('::App.SqlQueryManager.Description')}
|
||||||
|
|
@ -2207,7 +2208,7 @@ const SqlTableDesignerDialog = ({
|
||||||
onClick={() => setFkModalOpen(false)}
|
onClick={() => setFkModalOpen(false)}
|
||||||
className="px-4 py-2 text-sm text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
|
className="px-4 py-2 text-sm text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
{translate('::Cancel')}
|
{translate('::App.SqlQueryManager.Cancel')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={saveFk}
|
onClick={saveFk}
|
||||||
|
|
@ -2431,7 +2432,7 @@ const SqlTableDesignerDialog = ({
|
||||||
<div className="border border-gray-200 dark:border-gray-600 rounded-lg overflow-hidden">
|
<div className="border border-gray-200 dark:border-gray-600 rounded-lg overflow-hidden">
|
||||||
<div className="grid grid-cols-12 gap-2 px-3 py-1.5 bg-gray-50 dark:bg-gray-700 text-xs font-semibold text-gray-500">
|
<div className="grid grid-cols-12 gap-2 px-3 py-1.5 bg-gray-50 dark:bg-gray-700 text-xs font-semibold text-gray-500">
|
||||||
<div className="col-span-1" />
|
<div className="col-span-1" />
|
||||||
<div className="col-span-7">{translate('::App.Listform.ListformField.Column')}</div>
|
<div className="col-span-7">{translate('::App.SqlQueryManager.Column')}</div>
|
||||||
<div className="col-span-4">{translate('::App.SqlQueryManager.SortOrder')}</div>
|
<div className="col-span-4">{translate('::App.SqlQueryManager.SortOrder')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-h-44 overflow-y-auto divide-y divide-gray-100 dark:divide-gray-700">
|
<div className="max-h-44 overflow-y-auto divide-y divide-gray-100 dark:divide-gray-700">
|
||||||
|
|
@ -2516,6 +2517,7 @@ const SqlTableDesignerDialog = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Description */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-xs font-semibold text-gray-700 dark:text-gray-300 mb-1.5">
|
<label className="block text-xs font-semibold text-gray-700 dark:text-gray-300 mb-1.5">
|
||||||
{translate('::App.SqlQueryManager.Description')}
|
{translate('::App.SqlQueryManager.Description')}
|
||||||
|
|
@ -2538,7 +2540,7 @@ const SqlTableDesignerDialog = ({
|
||||||
onClick={() => setIndexModalOpen(false)}
|
onClick={() => setIndexModalOpen(false)}
|
||||||
className="px-4 py-2 text-sm text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
|
className="px-4 py-2 text-sm text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
{translate('::Cancel')}
|
{translate('::App.SqlQueryManager.Cancel')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={saveIndex}
|
onClick={saveIndex}
|
||||||
|
|
@ -2613,12 +2615,12 @@ const SqlTableDesignerDialog = ({
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="flex justify-between items-center border-t pt-3 mt-1">
|
<div className="flex justify-between items-center border-t pt-3 mt-1">
|
||||||
<Button variant="plain" onClick={handleClose}>
|
<Button variant="plain" onClick={handleClose}>
|
||||||
{translate('::Cancel')}
|
{translate('::App.SqlQueryManager.Cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{step > 0 && (
|
{step > 0 && (
|
||||||
<Button variant="default" onClick={handleBack}>
|
<Button variant="default" onClick={handleBack}>
|
||||||
{translate('::Back')}
|
{translate('::App.SqlQueryManager.Back')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{step < 4 ? (
|
{step < 4 ? (
|
||||||
|
|
@ -2638,7 +2640,7 @@ const SqlTableDesignerDialog = ({
|
||||||
(isEditMode && generatedSql.includes('Henüz değişiklik yapılmadı'))
|
(isEditMode && generatedSql.includes('Henüz değişiklik yapılmadı'))
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{translate('::App.Platform.Deploy')}
|
{translate('::App.SqlQueryManager.Deploy')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -177,10 +177,7 @@ const FormDevExpress = (props: {
|
||||||
}}
|
}}
|
||||||
></TagBoxEditorComponent>
|
></TagBoxEditorComponent>
|
||||||
)}
|
)}
|
||||||
label={{
|
label={{ text: translate('::' + formItem.colData?.captionName), className: 'font-semibold' }}
|
||||||
text: translate('::' + formItem.colData?.captionName),
|
|
||||||
className: 'font-semibold',
|
|
||||||
}}
|
|
||||||
></SimpleItemDx>
|
></SimpleItemDx>
|
||||||
) : formItem.editorType2 === PlatformEditorTypes.dxGridBox ? (
|
) : formItem.editorType2 === PlatformEditorTypes.dxGridBox ? (
|
||||||
<SimpleItemDx
|
<SimpleItemDx
|
||||||
|
|
@ -202,10 +199,7 @@ const FormDevExpress = (props: {
|
||||||
}}
|
}}
|
||||||
></GridBoxEditorComponent>
|
></GridBoxEditorComponent>
|
||||||
)}
|
)}
|
||||||
label={{
|
label={{ text: translate('::' + formItem.colData?.captionName), className: 'font-semibold' }}
|
||||||
text: translate('::' + formItem.colData?.captionName),
|
|
||||||
className: 'font-semibold',
|
|
||||||
}}
|
|
||||||
></SimpleItemDx>
|
></SimpleItemDx>
|
||||||
) : (
|
) : (
|
||||||
<SimpleItemDx
|
<SimpleItemDx
|
||||||
|
|
@ -222,9 +216,6 @@ const FormDevExpress = (props: {
|
||||||
showClearButton: true,
|
showClearButton: true,
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(formItem.colData?.placeHolder
|
|
||||||
? { placeholder: translate('::' + formItem.colData.placeHolder) }
|
|
||||||
: {}),
|
|
||||||
buttons: (formItem.editorOptions?.buttons || []).map((btn: any) => {
|
buttons: (formItem.editorOptions?.buttons || []).map((btn: any) => {
|
||||||
if (btn?.options?.onClick && typeof btn.options.onClick === 'string') {
|
if (btn?.options?.onClick && typeof btn.options.onClick === 'string') {
|
||||||
const origClick = eval(`(${btn.options.onClick})`)
|
const origClick = eval(`(${btn.options.onClick})`)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,12 @@ import React, { useState, useRef, useEffect } from 'react'
|
||||||
import { NoteModal } from './NoteModal'
|
import { NoteModal } from './NoteModal'
|
||||||
import { NoteList } from './NoteList'
|
import { NoteList } from './NoteList'
|
||||||
import { Button, Badge } from '@/components/ui'
|
import { Button, Badge } from '@/components/ui'
|
||||||
import { FaChevronLeft, FaChevronRight, FaTimes, FaGripVertical } from 'react-icons/fa'
|
import {
|
||||||
|
FaChevronLeft,
|
||||||
|
FaChevronRight,
|
||||||
|
FaTimes,
|
||||||
|
FaGripVertical,
|
||||||
|
} from 'react-icons/fa'
|
||||||
import { noteService } from '@/services/note.service'
|
import { noteService } from '@/services/note.service'
|
||||||
import { NoteDto } from '@/proxy/note/models'
|
import { NoteDto } from '@/proxy/note/models'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
|
@ -46,7 +51,11 @@ export const NotePanel: React.FC<NotePanelProps> = ({
|
||||||
const handleDownloadFile = async (fileData: any) => {
|
const handleDownloadFile = async (fileData: any) => {
|
||||||
if (!fileData?.SavedFileName) return
|
if (!fileData?.SavedFileName) return
|
||||||
try {
|
try {
|
||||||
await noteService.downloadFile(fileData.SavedFileName, fileData.FileName, fileData.FileType)
|
await noteService.downloadFile(
|
||||||
|
fileData.SavedFileName,
|
||||||
|
fileData.FileName,
|
||||||
|
fileData.FileType,
|
||||||
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(translate('::ListForms.ListForm.NotesPanel.DownloadFailed'), err)
|
console.error(translate('::ListForms.ListForm.NotesPanel.DownloadFailed'), err)
|
||||||
}
|
}
|
||||||
|
|
@ -126,11 +135,7 @@ export const NotePanel: React.FC<NotePanelProps> = ({
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
onToggle()
|
onToggle()
|
||||||
}}
|
}}
|
||||||
title={
|
title={isVisible ? translate('::ListForms.ListForm.NotesPanel.ClosePanel') : translate('::ListForms.ListForm.NotesPanel.OpenPanel')}
|
||||||
isVisible
|
|
||||||
? translate('::ListForms.ListForm.NotesPanel.ClosePanel')
|
|
||||||
: translate('::ListForms.ListForm.NotesPanel.OpenPanel')
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{isVisible ? <FaChevronRight /> : <FaChevronLeft />}
|
{isVisible ? <FaChevronRight /> : <FaChevronLeft />}
|
||||||
|
|
@ -152,13 +157,12 @@ export const NotePanel: React.FC<NotePanelProps> = ({
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full">
|
||||||
<div className="p-2 border-b border-gray-200 bg-gray-50">
|
<div className="p-4 border-b border-gray-200 bg-gray-50">
|
||||||
{/* Üst Satır: Başlık, Kayıt Bilgisi Toggle ve Kapat Butonu */}
|
{/* Üst Satır: Başlık, Kayıt Bilgisi Toggle ve Kapat Butonu */}
|
||||||
<div className="flex items-center justify-between mx-1 my-1">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<div className="flex flex-col gap-1 text-sm text-gray-700">
|
<div className="flex items-center gap-1 text-sm text-gray-700">
|
||||||
<span className="font-medium">{entityName}</span>
|
<span className="font-medium">{entityName}</span>
|
||||||
|
<code className="bg-gray-100 px-2 rounded text-gray-800 text-xs font-mono">
|
||||||
<code className="bg-gray-100 py-1 rounded text-gray-800 text-xs font-mono w-fit">
|
|
||||||
<Badge className="bg-blue-100 text-blue-600" content={entityId} />
|
<Badge className="bg-blue-100 text-blue-600" content={entityId} />
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -175,10 +179,13 @@ export const NotePanel: React.FC<NotePanelProps> = ({
|
||||||
className={`transition-all duration-300 overflow-hidden ${
|
className={`transition-all duration-300 overflow-hidden ${
|
||||||
showEntityInfo ? 'max-h-20 mt-2 opacity-100' : 'max-h-0 opacity-0'
|
showEntityInfo ? 'max-h-20 mt-2 opacity-100' : 'max-h-0 opacity-0'
|
||||||
}`}
|
}`}
|
||||||
></div>
|
>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 overflow-y-auto p-2">
|
<div className="flex-1 overflow-y-auto p-4">
|
||||||
<NoteList
|
<NoteList
|
||||||
notes={activities}
|
notes={activities}
|
||||||
entityName={entityName}
|
entityName={entityName}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import { Container } from '@/components/shared'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { APP_NAME } from '@/constants/app.constant'
|
import { APP_NAME } from '@/constants/app.constant'
|
||||||
import { Button } from '@/components/ui'
|
|
||||||
|
|
||||||
export function Forum() {
|
export function Forum() {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
@ -64,9 +63,9 @@ export function Forum() {
|
||||||
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative">
|
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative">
|
||||||
<strong className="font-bold">Error: </strong>
|
<strong className="font-bold">Error: </strong>
|
||||||
<span className="block sm:inline">{error}</span>
|
<span className="block sm:inline">{error}</span>
|
||||||
<Button onClick={clearError} className="absolute top-0 bottom-0 right-0 px-4 py-3">
|
<button onClick={clearError} className="absolute top-0 bottom-0 right-0 px-4 py-3">
|
||||||
<span className="sr-only">Dismiss</span>×
|
<span className="sr-only">Dismiss</span>×
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { Container } from '@/components/shared'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { APP_NAME } from '@/constants/app.constant'
|
import { APP_NAME } from '@/constants/app.constant'
|
||||||
import { Button } from '@/components/ui'
|
|
||||||
|
|
||||||
export function Management() {
|
export function Management() {
|
||||||
const {
|
const {
|
||||||
|
|
@ -70,9 +69,9 @@ export function Management() {
|
||||||
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative">
|
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative">
|
||||||
<strong className="font-bold">Error: </strong>
|
<strong className="font-bold">Error: </strong>
|
||||||
<span className="block sm:inline">{error}</span>
|
<span className="block sm:inline">{error}</span>
|
||||||
<Button onClick={clearError} className="absolute top-0 bottom-0 right-0 px-4 py-3">
|
<button onClick={clearError} className="absolute top-0 bottom-0 right-0 px-4 py-3">
|
||||||
<span className="sr-only">Dismiss</span>×
|
<span className="sr-only">Dismiss</span>×
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import { PostManagement } from './PostManagement'
|
||||||
import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum'
|
import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum'
|
||||||
import { AdminStats } from './Dashboard'
|
import { AdminStats } from './Dashboard'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { Button } from '@/components/ui'
|
|
||||||
|
|
||||||
interface AdminViewProps {
|
interface AdminViewProps {
|
||||||
categories: ForumCategory[]
|
categories: ForumCategory[]
|
||||||
|
|
@ -109,7 +108,7 @@ export function AdminView({
|
||||||
{navigationItems.map((item) => {
|
{navigationItems.map((item) => {
|
||||||
const Icon = item.icon
|
const Icon = item.icon
|
||||||
return (
|
return (
|
||||||
<Button
|
<button
|
||||||
key={item.id}
|
key={item.id}
|
||||||
onClick={() => setActiveSection(item.id)}
|
onClick={() => setActiveSection(item.id)}
|
||||||
className={`w-full flex items-center space-x-3 px-4 py-3 rounded-lg text-left transition-colors ${
|
className={`w-full flex items-center space-x-3 px-4 py-3 rounded-lg text-left transition-colors ${
|
||||||
|
|
@ -120,7 +119,7 @@ export function AdminView({
|
||||||
>
|
>
|
||||||
<Icon className="w-5 h-5" />
|
<Icon className="w-5 h-5" />
|
||||||
<span className="font-medium">{item.label}</span>
|
<span className="font-medium">{item.label}</span>
|
||||||
</Button>
|
</button>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
||||||
|
|
@ -165,15 +165,14 @@ export function CategoryManagement({
|
||||||
<h2 className="text-2xl font-bold text-gray-900">
|
<h2 className="text-2xl font-bold text-gray-900">
|
||||||
{translate('::App.Forum.CategoryManagement.Title')}
|
{translate('::App.Forum.CategoryManagement.Title')}
|
||||||
</h2>
|
</h2>
|
||||||
<Button
|
<button
|
||||||
variant="solid"
|
|
||||||
onClick={() => setShowCreateForm(true)}
|
onClick={() => setShowCreateForm(true)}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="flex items-center space-x-2 bg-blue-600 px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
|
className="flex items-center space-x-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<FaPlus className="w-4 h-4" />
|
<FaPlus className="w-4 h-4" />
|
||||||
<span>{translate('::App.Forum.CategoryManagement.AddCategory')}</span>
|
<span>{translate('::App.Forum.CategoryManagement.AddCategory')}</span>
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Create/Edit Form */}
|
{/* Create/Edit Form */}
|
||||||
|
|
@ -368,8 +367,7 @@ export function CategoryManagement({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Button
|
<button
|
||||||
size='xs'
|
|
||||||
onClick={() => handleToggleActive(category)}
|
onClick={() => handleToggleActive(category)}
|
||||||
className={`p-2 rounded-lg transition-colors ${
|
className={`p-2 rounded-lg transition-colors ${
|
||||||
category.isActive
|
category.isActive
|
||||||
|
|
@ -383,10 +381,9 @@ export function CategoryManagement({
|
||||||
) : (
|
) : (
|
||||||
<FaEyeSlash className="w-4 h-4" />
|
<FaEyeSlash className="w-4 h-4" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
<Button
|
<button
|
||||||
size='xs'
|
|
||||||
onClick={() => handleToggleLocked(category)}
|
onClick={() => handleToggleLocked(category)}
|
||||||
className={`p-2 rounded-lg transition-colors ${
|
className={`p-2 rounded-lg transition-colors ${
|
||||||
category.isLocked
|
category.isLocked
|
||||||
|
|
@ -400,25 +397,23 @@ export function CategoryManagement({
|
||||||
) : (
|
) : (
|
||||||
<FaUnlock className="w-4 h-4" />
|
<FaUnlock className="w-4 h-4" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
<Button
|
<button
|
||||||
size='xs'
|
|
||||||
onClick={() => handleEdit(category)}
|
onClick={() => handleEdit(category)}
|
||||||
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
|
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
|
||||||
title={translate('::App.Forum.CategoryManagement.EditCategory')}
|
title={translate('::App.Forum.CategoryManagement.EditCategory')}
|
||||||
>
|
>
|
||||||
<FaEdit className="w-4 h-4" />
|
<FaEdit className="w-4 h-4" />
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
<Button
|
<button
|
||||||
size='xs'
|
|
||||||
onClick={() => confirmDeleteCategory(category)}
|
onClick={() => confirmDeleteCategory(category)}
|
||||||
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
|
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
|
||||||
title={translate('::App.Forum.CategoryManagement.DeleteCategory')}
|
title={translate('::App.Forum.CategoryManagement.DeleteCategory')}
|
||||||
>
|
>
|
||||||
<FaTrash className="w-4 h-4" />
|
<FaTrash className="w-4 h-4" />
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -164,15 +164,14 @@ export function PostManagement({
|
||||||
<h2 className="text-2xl font-bold text-gray-900">
|
<h2 className="text-2xl font-bold text-gray-900">
|
||||||
{translate('::App.Forum.PostManagement.Title')}
|
{translate('::App.Forum.PostManagement.Title')}
|
||||||
</h2>
|
</h2>
|
||||||
<Button
|
<button
|
||||||
variant="solid"
|
|
||||||
onClick={() => setShowCreateForm(true)}
|
onClick={() => setShowCreateForm(true)}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="flex items-center space-x-2 bg-blue-600 px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
|
className="flex items-center space-x-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<FaPlus className="w-4 h-4" />
|
<FaPlus className="w-4 h-4" />
|
||||||
<span>{translate('::App.Forum.PostManagement.AddPost')}</span>
|
<span>{translate('::App.Forum.PostManagement.AddPost')}</span>
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Create/Edit Form */}
|
{/* Create/Edit Form */}
|
||||||
|
|
@ -398,7 +397,7 @@ export function PostManagement({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2 ml-4">
|
<div className="flex items-center space-x-2 ml-4">
|
||||||
<Button
|
<button
|
||||||
onClick={() => handleToggleAcceptedAnswer(post)}
|
onClick={() => handleToggleAcceptedAnswer(post)}
|
||||||
className={`p-2 rounded-lg transition-colors ${
|
className={`p-2 rounded-lg transition-colors ${
|
||||||
post.isAcceptedAnswer
|
post.isAcceptedAnswer
|
||||||
|
|
@ -416,23 +415,23 @@ export function PostManagement({
|
||||||
) : (
|
) : (
|
||||||
<FaCircle className="w-4 h-4" />
|
<FaCircle className="w-4 h-4" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
<Button
|
<button
|
||||||
onClick={() => handleEdit(post)}
|
onClick={() => handleEdit(post)}
|
||||||
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
|
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
|
||||||
title={translate('::App.Forum.PostManagement.EditPost')}
|
title={translate('::App.Forum.PostManagement.EditPost')}
|
||||||
>
|
>
|
||||||
<FaEdit className="w-4 h-4" />
|
<FaEdit className="w-4 h-4" />
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
<Button
|
<button
|
||||||
onClick={() => confirmDeletePost(post)}
|
onClick={() => confirmDeletePost(post)}
|
||||||
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
|
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
|
||||||
title={translate('::App.Forum.PostManagement.DeletePost')}
|
title={translate('::App.Forum.PostManagement.DeletePost')}
|
||||||
>
|
>
|
||||||
<FaTrashAlt className="w-4 h-4" />
|
<FaTrashAlt className="w-4 h-4" />
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -206,15 +206,14 @@ export function TopicManagement({
|
||||||
<h2 className="text-2xl font-bold text-gray-900">
|
<h2 className="text-2xl font-bold text-gray-900">
|
||||||
{translate('::App.Forum.TopicManagement.Title')}
|
{translate('::App.Forum.TopicManagement.Title')}
|
||||||
</h2>
|
</h2>
|
||||||
<Button
|
<button
|
||||||
variant="solid"
|
|
||||||
onClick={() => setShowCreateForm(true)}
|
onClick={() => setShowCreateForm(true)}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="flex items-center space-x-2 bg-blue-600 px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
|
className="flex items-center space-x-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<FaPlus className="w-4 h-4" />
|
<FaPlus className="w-4 h-4" />
|
||||||
<span>{translate('::App.Forum.TopicManagement.AddTopic')}</span>
|
<span>{translate('::App.Forum.TopicManagement.AddTopic')}</span>
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Create/Edit Form */}
|
{/* Create/Edit Form */}
|
||||||
|
|
@ -399,7 +398,7 @@ export function TopicManagement({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2 ml-4">
|
<div className="flex items-center space-x-2 ml-4">
|
||||||
<Button
|
<button
|
||||||
onClick={() => handlePin(topic)}
|
onClick={() => handlePin(topic)}
|
||||||
className={`p-2 rounded-lg transition-colors ${
|
className={`p-2 rounded-lg transition-colors ${
|
||||||
topic.isPinned
|
topic.isPinned
|
||||||
|
|
@ -413,9 +412,9 @@ export function TopicManagement({
|
||||||
) : (
|
) : (
|
||||||
<FaTree className="w-4 h-4" />
|
<FaTree className="w-4 h-4" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
<Button
|
<button
|
||||||
onClick={() => handleLock(topic)}
|
onClick={() => handleLock(topic)}
|
||||||
className={`p-2 rounded-lg transition-colors ${
|
className={`p-2 rounded-lg transition-colors ${
|
||||||
topic.isLocked
|
topic.isLocked
|
||||||
|
|
@ -429,9 +428,9 @@ export function TopicManagement({
|
||||||
) : (
|
) : (
|
||||||
<FaUnlock className="w-4 h-4" />
|
<FaUnlock className="w-4 h-4" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
<Button
|
<button
|
||||||
onClick={() => handleSolved(topic)}
|
onClick={() => handleSolved(topic)}
|
||||||
className={`p-2 rounded-lg transition-colors ${
|
className={`p-2 rounded-lg transition-colors ${
|
||||||
topic.isSolved
|
topic.isSolved
|
||||||
|
|
@ -445,23 +444,23 @@ export function TopicManagement({
|
||||||
) : (
|
) : (
|
||||||
<FaCircle className="w-4 h-4" />
|
<FaCircle className="w-4 h-4" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
<Button
|
<button
|
||||||
onClick={() => handleEdit(topic)}
|
onClick={() => handleEdit(topic)}
|
||||||
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
|
className="p-2 text-blue-600 hover:bg-blue-100 rounded-lg transition-colors"
|
||||||
title={translate('::App.Forum.TopicManagement.EditTopic')}
|
title={translate('::App.Forum.TopicManagement.EditTopic')}
|
||||||
>
|
>
|
||||||
<FaEdit className="w-4 h-4" />
|
<FaEdit className="w-4 h-4" />
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
<Button
|
<button
|
||||||
onClick={() => confirmDeleteTopic(topic)}
|
onClick={() => confirmDeleteTopic(topic)}
|
||||||
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
|
className="p-2 text-red-600 hover:bg-red-100 rounded-lg transition-colors"
|
||||||
title={translate('::App.Forum.TopicManagement.DeleteTopic')}
|
title={translate('::App.Forum.TopicManagement.DeleteTopic')}
|
||||||
>
|
>
|
||||||
<FaTrashAlt className="w-4 h-4" />
|
<FaTrashAlt className="w-4 h-4" />
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ export function CreatePostModal({ onClose, onSubmit, parentPostId }: CreatePostM
|
||||||
label={
|
label={
|
||||||
parentPostId
|
parentPostId
|
||||||
? translate('::App.Forum.PostManagement.BaslikEdit')
|
? translate('::App.Forum.PostManagement.BaslikEdit')
|
||||||
: translate('::App.Listform.ListformField.Content')
|
: translate('::App.Forum.PostManagement.BaslikNew')
|
||||||
}
|
}
|
||||||
invalid={!!errors.content && !!touched.content}
|
invalid={!!errors.content && !!touched.content}
|
||||||
errorMessage={errors.content}
|
errorMessage={errors.content}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ export function CreateTopicModal({ onClose, onSubmit }: CreateTopicModalProps) {
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.Listform.ListformField.Content')}
|
label={translate('::App.Forum.TopicManagement.Content')}
|
||||||
invalid={errors.content && touched.content}
|
invalid={errors.content && touched.content}
|
||||||
errorMessage={errors.content}
|
errorMessage={errors.content}
|
||||||
asterisk
|
asterisk
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ export function ForumTopicCard({ topic, onClick }: TopicCardProps) {
|
||||||
<p className="text-gray-600 text-sm mb-4 line-clamp-2">{topic.content}</p>
|
<p className="text-gray-600 text-sm mb-4 line-clamp-2">{topic.content}</p>
|
||||||
|
|
||||||
<div className="flex items-center space-x-4 text-sm text-gray-500">
|
<div className="flex items-center space-x-4 text-sm text-gray-500">
|
||||||
<div className="flex items-center space-x-1" title={translate('::App.Platform.Views')}>
|
<div className="flex items-center space-x-1" title="Views">
|
||||||
<FaEye className="w-4 h-4" />
|
<FaEye className="w-4 h-4" />
|
||||||
<span>{topic.viewCount}</span>
|
<span>{topic.viewCount}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import { ForumTopicCard } from './ForumTopicCard'
|
||||||
import { useStoreState } from '@/store/store'
|
import { useStoreState } from '@/store/store'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { forumService } from '@/services/forum.service'
|
import { forumService } from '@/services/forum.service'
|
||||||
import { Button } from '@/components/ui'
|
|
||||||
|
|
||||||
interface ForumViewProps {
|
interface ForumViewProps {
|
||||||
categories: ForumCategory[]
|
categories: ForumCategory[]
|
||||||
|
|
@ -353,45 +352,41 @@ export function ForumView({
|
||||||
{/* Right Side: Actions + Search */}
|
{/* Right Side: Actions + Search */}
|
||||||
<div className="flex items-center space-x-2 ml-auto">
|
<div className="flex items-center space-x-2 ml-auto">
|
||||||
{viewState === 'topics' && selectedCategory && !selectedCategory.isLocked && (
|
{viewState === 'topics' && selectedCategory && !selectedCategory.isLocked && (
|
||||||
<Button
|
<button
|
||||||
variant='solid'
|
|
||||||
onClick={() => setShowCreateTopic(true)}
|
onClick={() => setShowCreateTopic(true)}
|
||||||
className="flex items-center space-x-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
|
className="flex items-center space-x-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
|
||||||
>
|
>
|
||||||
<FaPlus className="w-4 h-4" />
|
<FaPlus className="w-4 h-4" />
|
||||||
<span>{translate('::App.Forum.TopicManagement.NewTopic')}</span>
|
<span>{translate('::App.Forum.TopicManagement.NewTopic')}</span>
|
||||||
</Button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{viewState === 'posts' && selectedTopic && !selectedTopic.isLocked && (
|
{viewState === 'posts' && selectedTopic && !selectedTopic.isLocked && (
|
||||||
<Button
|
<button
|
||||||
variant='solid'
|
|
||||||
onClick={() => setShowCreatePost(true)}
|
onClick={() => setShowCreatePost(true)}
|
||||||
className="flex items-center space-x-2 bg-emerald-600 text-white px-4 py-2 rounded-lg hover:bg-emerald-700 transition-colors"
|
className="flex items-center space-x-2 bg-emerald-600 text-white px-4 py-2 rounded-lg hover:bg-emerald-700 transition-colors"
|
||||||
>
|
>
|
||||||
<FaPlus className="w-4 h-4" />
|
<FaPlus className="w-4 h-4" />
|
||||||
<span>{translate('::App.Forum.PostManagement.NewPost')}</span>
|
<span>{translate('::App.Forum.PostManagement.NewPost')}</span>
|
||||||
</Button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Search */}
|
{/* Search */}
|
||||||
<Button
|
<button
|
||||||
onClick={() => setIsSearchModalOpen(true)}
|
onClick={() => setIsSearchModalOpen(true)}
|
||||||
variant='default'
|
|
||||||
className="hidden md:flex items-center space-x-2 px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
className="hidden md:flex items-center space-x-2 px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
||||||
>
|
>
|
||||||
<FaSearch className="w-4 h-4 text-gray-400" />
|
<FaSearch className="w-4 h-4 text-gray-400" />
|
||||||
<span className="text-gray-500">
|
<span className="text-gray-500">
|
||||||
{translate('::App.Forum.TopicManagement.Searchtopics')}
|
{translate('::App.Forum.TopicManagement.Searchtopics')}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
<Button
|
<button
|
||||||
onClick={() => setIsSearchModalOpen(true)}
|
onClick={() => setIsSearchModalOpen(true)}
|
||||||
variant='default'
|
|
||||||
className="md:hidden p-2 text-gray-400 hover:text-gray-600 transition-colors"
|
className="md:hidden p-2 text-gray-400 hover:text-gray-600 transition-colors"
|
||||||
>
|
>
|
||||||
<FaSearch className="w-5 h-5" />
|
<FaSearch className="w-5 h-5" />
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ const ChartDrawer = ({
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.push(
|
toast.push(
|
||||||
<Notification type="danger">
|
<Notification type="danger">
|
||||||
{translate('::App.Platform.Error')}
|
{translate('::App.Platform.ChartDrawer.Error')}
|
||||||
<code>{error}</code>
|
<code>{error}</code>
|
||||||
</Notification>,
|
</Notification>,
|
||||||
{ placement: 'top-end' },
|
{ placement: 'top-end' },
|
||||||
|
|
@ -143,13 +143,13 @@ const ChartDrawer = ({
|
||||||
<span>📊</span>
|
<span>📊</span>
|
||||||
{translate('::App.Platform.ChartDrawer.ChartSeries')}
|
{translate('::App.Platform.ChartDrawer.ChartSeries')}
|
||||||
</h2>
|
</h2>
|
||||||
<Button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="p-2 hover:bg-gray-200 rounded-full transition-colors"
|
className="p-2 hover:bg-gray-200 rounded-full transition-colors"
|
||||||
>
|
>
|
||||||
<FaTimes />
|
<FaTimes />
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormContainer size="sm" className="flex flex-col flex-1 overflow-hidden">
|
<FormContainer size="sm" className="flex flex-col flex-1 overflow-hidden">
|
||||||
|
|
@ -205,7 +205,7 @@ const ChartDrawer = ({
|
||||||
<Field name={`series[${index}].type`}>
|
<Field name={`series[${index}].type`}>
|
||||||
{({ field, form }: FieldProps) => (
|
{({ field, form }: FieldProps) => (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setSelectedSeriesIndex(
|
setSelectedSeriesIndex(
|
||||||
|
|
@ -222,12 +222,12 @@ const ChartDrawer = ({
|
||||||
{chartSeriesTypeOptions.find((t) => t.label === field.value)
|
{chartSeriesTypeOptions.find((t) => t.label === field.value)
|
||||||
?.label || field.value}
|
?.label || field.value}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</button>
|
||||||
{selectedSeriesIndex === index && (
|
{selectedSeriesIndex === index && (
|
||||||
<div className="absolute z-50 mt-1 w-full bg-white border rounded-lg shadow-xl p-2">
|
<div className="absolute z-50 mt-1 w-full bg-white border rounded-lg shadow-xl p-2">
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-2 gap-2">
|
||||||
{chartSeriesTypeOptions.map((chartType) => (
|
{chartSeriesTypeOptions.map((chartType) => (
|
||||||
<Button
|
<button
|
||||||
key={chartType.label}
|
key={chartType.label}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
@ -244,7 +244,7 @@ const ChartDrawer = ({
|
||||||
<span className="text-xs font-medium">
|
<span className="text-xs font-medium">
|
||||||
{chartType.label}
|
{chartType.label}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -257,7 +257,7 @@ const ChartDrawer = ({
|
||||||
{/* Name */}
|
{/* Name */}
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="text-xs font-medium mb-1 block">
|
<label className="text-xs font-medium mb-1 block">
|
||||||
{translate('::App.Listform.ListformField.Name')}
|
{translate('::App.Platform.ChartDrawer.Name')}
|
||||||
</label>
|
</label>
|
||||||
<Field
|
<Field
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,6 @@ import { useListFormCustomDataSource } from './useListFormCustomDataSource'
|
||||||
import { useListFormColumns } from './useListFormColumns'
|
import { useListFormColumns } from './useListFormColumns'
|
||||||
import { Loading } from '@/components/shared'
|
import { Loading } from '@/components/shared'
|
||||||
import { useStoreState } from '@/store'
|
import { useStoreState } from '@/store'
|
||||||
import { placeholder } from '@babel/types'
|
|
||||||
|
|
||||||
interface GridProps {
|
interface GridProps {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -925,14 +924,6 @@ const Grid = (props: GridProps) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Her item'a placeholder olarak captionName ekle
|
|
||||||
if (listFormField?.placeHolder) {
|
|
||||||
editorOptions = {
|
|
||||||
...editorOptions,
|
|
||||||
placeholder: translate('::' + listFormField.placeHolder),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set defaultValue for @AUTONUMBER fields
|
// Set defaultValue for @AUTONUMBER fields
|
||||||
if (
|
if (
|
||||||
typeof listFormField?.defaultValue === 'string' &&
|
typeof listFormField?.defaultValue === 'string' &&
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ const GridFilterDialogs = (props: {
|
||||||
<h5 className="mb-4">Delete Filter</h5>
|
<h5 className="mb-4">Delete Filter</h5>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
|
placeholder="Please Select"
|
||||||
options={filtersForSelectBox}
|
options={filtersForSelectBox}
|
||||||
onChange={(option) => {
|
onChange={(option) => {
|
||||||
setNewFilterId(option?.value)
|
setNewFilterId(option?.value)
|
||||||
|
|
|
||||||
|
|
@ -621,7 +621,7 @@ const SchedulerView = (props: SchedulerViewProps) => {
|
||||||
allowDragging={gridDto.gridOptions.schedulerOptionDto?.allowDragging ?? false}
|
allowDragging={gridDto.gridOptions.schedulerOptionDto?.allowDragging ?? false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View type="day" name={translate('::App.Listform.ListformField.Day')} />
|
<View type="day" name={translate('::ListForms.SchedulerOptions.Day')} />
|
||||||
<View type="week" name={translate('::ListForms.SchedulerOptions.Week')} />
|
<View type="week" name={translate('::ListForms.SchedulerOptions.Week')} />
|
||||||
<View type="workWeek" name={translate('::ListForms.SchedulerOptions.WorkWeek')} />
|
<View type="workWeek" name={translate('::ListForms.SchedulerOptions.WorkWeek')} />
|
||||||
<View type="month" name={translate('::ListForms.SchedulerOptions.Month')} />
|
<View type="month" name={translate('::ListForms.SchedulerOptions.Month')} />
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue