Dinamik Route sistemi geliştirildi.

This commit is contained in:
Sedat Öztürk 2025-06-29 00:34:28 +03:00
parent b6527e7b36
commit 954555c375
64 changed files with 6626 additions and 536 deletions

View file

@ -0,0 +1,14 @@
using System;
using Volo.Abp.Application.Dtos;
namespace Kurs.Platform.Routes
{
public class RouteDto : EntityDto<Guid>
{
public string Key { get; set; }
public string Path { get; set; }
public string ComponentPath { get; set; }
public string RouteType { get; set; }
public string[] Authority { get; set; }
}
}

View file

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Kurs.Platform.Entities;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
namespace Kurs.Platform.Routes
{
public class RouteAppService : PlatformAppService
{
private readonly IRepository<Route, Guid> _routeRepository;
public RouteAppService(IRepository<Route, Guid> routeRepository)
{
_routeRepository = routeRepository;
}
public async Task<List<RouteDto>> GetListAsync()
{
var items = await _routeRepository.GetListAsync();
if (items is null)
{
throw new EntityNotFoundException(L["RecordNotFound"]);
}
return ObjectMapper.Map<List<Route>, List<RouteDto>>(items);
}
}
}

View file

@ -0,0 +1,14 @@
using AutoMapper;
using Kurs.Platform.Entities;
using Kurs.Platform.Routes;
namespace Kurs.Platform;
public class RouteAutoMapperProfile : Profile
{
public RouteAutoMapperProfile()
{
// Blog mappings
CreateMap<Route, RouteDto>();
}
}

View file

@ -1609,7 +1609,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
Text ="Detail",
UrlTarget="_blank",
AuthName=PlatformConsts.IdentityPermissions.Users.Update,
Url="/admin/identity/users/detail/@Id"
Url="/admin/users/detail/@Id"
},
new CommandColumnDto() {
Hint = "Permission",
@ -1970,7 +1970,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
Text ="Manage",
UrlTarget="_blank",
AuthName = AppCodes.Languages.Language + ".Update",
Url=$"/form/{ListFormCodes.Forms.FormLanguage}/@Id"
Url=$"/admin/form/{ListFormCodes.Forms.FormLanguage}/@Id"
},
}),
}
@ -3139,14 +3139,14 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
Text ="Manage",
UrlTarget="_blank",
AuthName=AppCodes.Listforms.Listform + ".Update",
Url="/admin/listform/edit/@ListFormCode"
Url="/saas/listform/edit/@ListFormCode"
},
new() {
Hint = "Göster",
Text ="Göster",
UrlTarget="_blank",
AuthName=AppCodes.Listforms.Listform,
Url="/list/@ListFormCode"
Url="/admin/list/@ListFormCode"
}
}),
StateStoringJson = JsonSerializer.Serialize(new StateStoringDto
@ -4632,14 +4632,14 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
Text ="Manage",
UrlTarget="_blank",
AuthName=AppCodes.Listforms.Chart + ".Update",
Url="/admin/chart/edit/@ChartCode"
Url="/saas/chart/edit/@ChartCode"
},
new() {
Hint = "Göster",
Text ="Göster",
UrlTarget="_blank",
AuthName=AppCodes.Listforms.Chart,
Url="/chart/@ChartCode"
Url="/admin/chart/@ChartCode"
}
}),
}
@ -10457,7 +10457,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
Text ="Manage",
UrlTarget="_blank",
AuthName = AppCodes.Definitions.SkillType + ".Update",
Url=$"/form/{ListFormCodes.Forms.FormUomCategory}/@Id"
Url=$"/admin/form/{ListFormCodes.Forms.FormUomCategory}/@Id"
},
}),
});
@ -10644,7 +10644,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
Text ="Manage",
UrlTarget="_blank",
AuthName = AppCodes.Definitions.UomCategory + ".Update",
Url=$"/form/{ListFormCodes.Forms.FormUomCategory}/@Id"
Url=$"/admin/form/{ListFormCodes.Forms.FormUomCategory}/@Id"
},
}),
});
@ -10717,7 +10717,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
IsOrganizationUnit = false,
Description = AppCodes.BlogManagement.BlogCategory,
SelectCommandType = SelectCommandTypeEnum.Table,
SelectCommand = SelectCommandByTableName("BlogCategories"),
SelectCommand = SelectCommandByTableName("BlogCategory"),
KeyFieldName = "Id",
KeyFieldDbSourceType = DbType.Guid,
DefaultFilter = "\"IsDeleted\" = 'false'",
@ -11015,7 +11015,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
}
#endregion
#region BlogPosts
#region BlogPost
if (!await _listFormRepository.AnyAsync(a => a.ListFormCode == ListFormCodes.Lists.BlogPosts))
{
var listFormBlogPosts = await _listFormRepository.InsertAsync(
@ -11031,7 +11031,7 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
IsOrganizationUnit = false,
Description = AppCodes.BlogManagement.BlogPosts,
SelectCommandType = SelectCommandTypeEnum.Table,
SelectCommand = SelectCommandByTableName("BlogPosts"),
SelectCommand = SelectCommandByTableName("BlogPost"),
KeyFieldName = "Id",
KeyFieldDbSourceType = DbType.Guid,
DefaultFilter = "\"IsDeleted\" = 'false'",

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
@ -11,6 +12,7 @@ using Kurs.Platform.Entities;
using Kurs.Platform.Enums;
using Kurs.Platform.Forum;
using Kurs.Platform.ListForms;
using Kurs.Platform.Routes;
using Kurs.Platform.Seeds;
using Kurs.Settings.Entities;
using Microsoft.Extensions.Configuration;
@ -53,6 +55,7 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
private readonly IRepository<BlogPost, Guid> _blogPostsRepository;
private readonly IRepository<ForumCategory, Guid> _forumCategoryRepository;
private readonly IRepository<AiBot, Guid> _aiBotRepository;
private readonly IRepository<Route, Guid> _RouteRepository;
public PlatformDataSeeder(
IRepository<Language, Guid> languages,
@ -82,7 +85,8 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
IRepository<BlogCategory, Guid> blogCategoryRepository,
IRepository<BlogPost, Guid> blogPostsRepository,
IRepository<ForumCategory, Guid> forumCategoryRepository,
IRepository<AiBot, Guid> aiBotRepository
IRepository<AiBot, Guid> aiBotRepository,
IRepository<Route, Guid> RouteRepository
)
{
_languages = languages;
@ -113,6 +117,7 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
_blogPostsRepository = blogPostsRepository;
_forumCategoryRepository = forumCategoryRepository;
_aiBotRepository = aiBotRepository;
_RouteRepository = RouteRepository;
}
private static IConfigurationRoot BuildConfiguration()
@ -631,5 +636,21 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
));
}
}
foreach (var item in items.Routes)
{
var exists = await _RouteRepository.AnyAsync(x => x.Key == item.Key);
if (!exists)
{
await _RouteRepository.InsertAsync(new Route(
item.Key,
item.Path,
item.ComponentPath,
item.RouteType,
item.Authority ?? []
));
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -37,6 +37,7 @@ public class SeederDto
public List<BlogPostSeedDto> BlogPosts { get; set; }
public List<ForumCategorySeedDto> ForumCategories { get; set; }
public List<AiBotSeedDto> AiBots { get; set; }
public List<RouteSeedDto> Routes { get; set; }
}
public class ChartsSeedDto
@ -244,3 +245,12 @@ public class AiBotSeedDto
public Guid Id { get; set; }
public string BotName { get; set; }
}
public class RouteSeedDto
{
public string Key { get; set; }
public string Path { get; set; }
public string ComponentPath { get; set; }
public string RouteType { get; set; }
public string[] Authority { get; set; }
}

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Kurs.Platform.Entities;

View file

@ -1,6 +1,5 @@
using System;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Kurs.Platform.Entities;

View file

@ -0,0 +1,22 @@
using System;
using Volo.Abp.Domain.Entities.Auditing;
namespace Kurs.Platform.Entities;
public class Route : FullAuditedAggregateRoot<Guid>
{
public string Key { get; set; }
public string Path { get; set; }
public string ComponentPath { get; set; }
public string RouteType { get; set; }
public string[] Authority { get; set; }
public Route(string key, string path, string componentPath, string routeType, string[] authority)
{
Key = key;
Path = path;
ComponentPath = componentPath;
RouteType = routeType;
Authority = authority;
}
}

View file

@ -19,6 +19,9 @@ using Volo.Abp.TenantManagement.EntityFrameworkCore;
using Kurs.Notifications.EntityFrameworkCore;
using static Kurs.Settings.SettingsConsts;
using Kurs.Platform.Forum;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using System;
using System.Linq;
namespace Kurs.Platform.EntityFrameworkCore;
@ -59,6 +62,7 @@ public class PlatformDbContext :
public DbSet<ContactTitle> ContactTitles { get; set; }
public DbSet<BlogPost> BlogPosts { get; set; }
public DbSet<BlogCategory> BlogCategories { get; set; }
public DbSet<Route> Routes { get; set; }
// Forum Entities
public DbSet<ForumCategory> ForumCategories { get; set; }
@ -402,7 +406,7 @@ public class PlatformDbContext :
// Blog Entity Configurations
builder.Entity<BlogCategory>(b =>
{
b.ToTable(PlatformConsts.DbTablePrefix + "BlogCategories", PlatformConsts.DbSchema);
b.ToTable(PlatformConsts.DbTablePrefix + nameof(BlogCategory), PlatformConsts.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
@ -414,7 +418,7 @@ public class PlatformDbContext :
builder.Entity<BlogPost>(b =>
{
b.ToTable(PlatformConsts.DbTablePrefix + "BlogPosts", PlatformConsts.DbSchema);
b.ToTable(PlatformConsts.DbTablePrefix + nameof(BlogPost), PlatformConsts.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Title).IsRequired().HasMaxLength(256);
@ -438,7 +442,7 @@ public class PlatformDbContext :
// ForumCategory
builder.Entity<ForumCategory>(b =>
{
b.ToTable(PlatformConsts.DbTablePrefix + "ForumCategories", PlatformConsts.DbSchema);
b.ToTable(PlatformConsts.DbTablePrefix + nameof(ForumCategory), PlatformConsts.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
@ -455,7 +459,7 @@ public class PlatformDbContext :
// ForumTopic
builder.Entity<ForumTopic>(b =>
{
b.ToTable(PlatformConsts.DbTablePrefix + "ForumTopics", PlatformConsts.DbSchema);
b.ToTable(PlatformConsts.DbTablePrefix + nameof(ForumTopic), PlatformConsts.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Title).IsRequired().HasMaxLength(256);
@ -474,7 +478,7 @@ public class PlatformDbContext :
// ForumPost
builder.Entity<ForumPost>(b =>
{
b.ToTable(PlatformConsts.DbTablePrefix + "ForumPosts", PlatformConsts.DbSchema);
b.ToTable(PlatformConsts.DbTablePrefix + nameof(ForumPost), PlatformConsts.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Content).IsRequired();
@ -492,5 +496,24 @@ public class PlatformDbContext :
.OnDelete(DeleteBehavior.Restrict);
});
builder.Entity<Route>(b =>
{
b.ToTable(PlatformConsts.DbTablePrefix + nameof(Route), PlatformConsts.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Key).IsRequired().HasMaxLength(128);
b.Property(x => x.Path).IsRequired().HasMaxLength(256);
b.Property(x => x.ComponentPath).IsRequired().HasMaxLength(256);
b.Property(x => x.Authority).HasConversion(
v => string.Join(",", v),
v => v.Split(',', StringSplitOptions.RemoveEmptyEntries)
).Metadata.SetValueComparer(
new ValueComparer<string[]>(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => c.ToArray()
)
);
});
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,383 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Kurs.Platform.Migrations
{
/// <inheritdoc />
public partial class AddRoutes : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_PBlogPosts_PBlogCategories_CategoryId",
table: "PBlogPosts");
migrationBuilder.DropForeignKey(
name: "FK_PForumPosts_PForumPosts_ParentPostId",
table: "PForumPosts");
migrationBuilder.DropForeignKey(
name: "FK_PForumPosts_PForumTopics_TopicId",
table: "PForumPosts");
migrationBuilder.DropForeignKey(
name: "FK_PForumTopics_PForumCategories_CategoryId",
table: "PForumTopics");
migrationBuilder.DropPrimaryKey(
name: "PK_PForumTopics",
table: "PForumTopics");
migrationBuilder.DropPrimaryKey(
name: "PK_PForumPosts",
table: "PForumPosts");
migrationBuilder.DropPrimaryKey(
name: "PK_PForumCategories",
table: "PForumCategories");
migrationBuilder.DropPrimaryKey(
name: "PK_PBlogPosts",
table: "PBlogPosts");
migrationBuilder.DropPrimaryKey(
name: "PK_PBlogCategories",
table: "PBlogCategories");
migrationBuilder.RenameTable(
name: "PForumTopics",
newName: "PForumTopic");
migrationBuilder.RenameTable(
name: "PForumPosts",
newName: "PForumPost");
migrationBuilder.RenameTable(
name: "PForumCategories",
newName: "PForumCategory");
migrationBuilder.RenameTable(
name: "PBlogPosts",
newName: "PBlogPost");
migrationBuilder.RenameTable(
name: "PBlogCategories",
newName: "PBlogCategory");
migrationBuilder.RenameIndex(
name: "IX_PForumTopics_LastPostDate",
table: "PForumTopic",
newName: "IX_PForumTopic_LastPostDate");
migrationBuilder.RenameIndex(
name: "IX_PForumTopics_IsPinned",
table: "PForumTopic",
newName: "IX_PForumTopic_IsPinned");
migrationBuilder.RenameIndex(
name: "IX_PForumTopics_CategoryId",
table: "PForumTopic",
newName: "IX_PForumTopic_CategoryId");
migrationBuilder.RenameIndex(
name: "IX_PForumPosts_TopicId",
table: "PForumPost",
newName: "IX_PForumPost_TopicId");
migrationBuilder.RenameIndex(
name: "IX_PForumPosts_ParentPostId",
table: "PForumPost",
newName: "IX_PForumPost_ParentPostId");
migrationBuilder.RenameIndex(
name: "IX_PForumCategories_DisplayOrder",
table: "PForumCategory",
newName: "IX_PForumCategory_DisplayOrder");
migrationBuilder.RenameIndex(
name: "IX_PBlogPosts_Slug",
table: "PBlogPost",
newName: "IX_PBlogPost_Slug");
migrationBuilder.RenameIndex(
name: "IX_PBlogPosts_PublishedAt",
table: "PBlogPost",
newName: "IX_PBlogPost_PublishedAt");
migrationBuilder.RenameIndex(
name: "IX_PBlogPosts_IsPublished",
table: "PBlogPost",
newName: "IX_PBlogPost_IsPublished");
migrationBuilder.RenameIndex(
name: "IX_PBlogPosts_CategoryId",
table: "PBlogPost",
newName: "IX_PBlogPost_CategoryId");
migrationBuilder.RenameIndex(
name: "IX_PBlogCategories_Slug",
table: "PBlogCategory",
newName: "IX_PBlogCategory_Slug");
migrationBuilder.AddPrimaryKey(
name: "PK_PForumTopic",
table: "PForumTopic",
column: "Id");
migrationBuilder.AddPrimaryKey(
name: "PK_PForumPost",
table: "PForumPost",
column: "Id");
migrationBuilder.AddPrimaryKey(
name: "PK_PForumCategory",
table: "PForumCategory",
column: "Id");
migrationBuilder.AddPrimaryKey(
name: "PK_PBlogPost",
table: "PBlogPost",
column: "Id");
migrationBuilder.AddPrimaryKey(
name: "PK_PBlogCategory",
table: "PBlogCategory",
column: "Id");
migrationBuilder.CreateTable(
name: "PRoute",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Key = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Path = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
ComponentPath = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
RouteType = table.Column<string>(type: "nvarchar(max)", nullable: true),
Authority = table.Column<string>(type: "nvarchar(max)", nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: false),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: false),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PRoute", x => x.Id);
});
migrationBuilder.AddForeignKey(
name: "FK_PBlogPost_PBlogCategory_CategoryId",
table: "PBlogPost",
column: "CategoryId",
principalTable: "PBlogCategory",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_PForumPost_PForumPost_ParentPostId",
table: "PForumPost",
column: "ParentPostId",
principalTable: "PForumPost",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_PForumPost_PForumTopic_TopicId",
table: "PForumPost",
column: "TopicId",
principalTable: "PForumTopic",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_PForumTopic_PForumCategory_CategoryId",
table: "PForumTopic",
column: "CategoryId",
principalTable: "PForumCategory",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_PBlogPost_PBlogCategory_CategoryId",
table: "PBlogPost");
migrationBuilder.DropForeignKey(
name: "FK_PForumPost_PForumPost_ParentPostId",
table: "PForumPost");
migrationBuilder.DropForeignKey(
name: "FK_PForumPost_PForumTopic_TopicId",
table: "PForumPost");
migrationBuilder.DropForeignKey(
name: "FK_PForumTopic_PForumCategory_CategoryId",
table: "PForumTopic");
migrationBuilder.DropTable(
name: "PRoute");
migrationBuilder.DropPrimaryKey(
name: "PK_PForumTopic",
table: "PForumTopic");
migrationBuilder.DropPrimaryKey(
name: "PK_PForumPost",
table: "PForumPost");
migrationBuilder.DropPrimaryKey(
name: "PK_PForumCategory",
table: "PForumCategory");
migrationBuilder.DropPrimaryKey(
name: "PK_PBlogPost",
table: "PBlogPost");
migrationBuilder.DropPrimaryKey(
name: "PK_PBlogCategory",
table: "PBlogCategory");
migrationBuilder.RenameTable(
name: "PForumTopic",
newName: "PForumTopics");
migrationBuilder.RenameTable(
name: "PForumPost",
newName: "PForumPosts");
migrationBuilder.RenameTable(
name: "PForumCategory",
newName: "PForumCategories");
migrationBuilder.RenameTable(
name: "PBlogPost",
newName: "PBlogPosts");
migrationBuilder.RenameTable(
name: "PBlogCategory",
newName: "PBlogCategories");
migrationBuilder.RenameIndex(
name: "IX_PForumTopic_LastPostDate",
table: "PForumTopics",
newName: "IX_PForumTopics_LastPostDate");
migrationBuilder.RenameIndex(
name: "IX_PForumTopic_IsPinned",
table: "PForumTopics",
newName: "IX_PForumTopics_IsPinned");
migrationBuilder.RenameIndex(
name: "IX_PForumTopic_CategoryId",
table: "PForumTopics",
newName: "IX_PForumTopics_CategoryId");
migrationBuilder.RenameIndex(
name: "IX_PForumPost_TopicId",
table: "PForumPosts",
newName: "IX_PForumPosts_TopicId");
migrationBuilder.RenameIndex(
name: "IX_PForumPost_ParentPostId",
table: "PForumPosts",
newName: "IX_PForumPosts_ParentPostId");
migrationBuilder.RenameIndex(
name: "IX_PForumCategory_DisplayOrder",
table: "PForumCategories",
newName: "IX_PForumCategories_DisplayOrder");
migrationBuilder.RenameIndex(
name: "IX_PBlogPost_Slug",
table: "PBlogPosts",
newName: "IX_PBlogPosts_Slug");
migrationBuilder.RenameIndex(
name: "IX_PBlogPost_PublishedAt",
table: "PBlogPosts",
newName: "IX_PBlogPosts_PublishedAt");
migrationBuilder.RenameIndex(
name: "IX_PBlogPost_IsPublished",
table: "PBlogPosts",
newName: "IX_PBlogPosts_IsPublished");
migrationBuilder.RenameIndex(
name: "IX_PBlogPost_CategoryId",
table: "PBlogPosts",
newName: "IX_PBlogPosts_CategoryId");
migrationBuilder.RenameIndex(
name: "IX_PBlogCategory_Slug",
table: "PBlogCategories",
newName: "IX_PBlogCategories_Slug");
migrationBuilder.AddPrimaryKey(
name: "PK_PForumTopics",
table: "PForumTopics",
column: "Id");
migrationBuilder.AddPrimaryKey(
name: "PK_PForumPosts",
table: "PForumPosts",
column: "Id");
migrationBuilder.AddPrimaryKey(
name: "PK_PForumCategories",
table: "PForumCategories",
column: "Id");
migrationBuilder.AddPrimaryKey(
name: "PK_PBlogPosts",
table: "PBlogPosts",
column: "Id");
migrationBuilder.AddPrimaryKey(
name: "PK_PBlogCategories",
table: "PBlogCategories",
column: "Id");
migrationBuilder.AddForeignKey(
name: "FK_PBlogPosts_PBlogCategories_CategoryId",
table: "PBlogPosts",
column: "CategoryId",
principalTable: "PBlogCategories",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_PForumPosts_PForumPosts_ParentPostId",
table: "PForumPosts",
column: "ParentPostId",
principalTable: "PForumPosts",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_PForumPosts_PForumTopics_TopicId",
table: "PForumPosts",
column: "TopicId",
principalTable: "PForumTopics",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
migrationBuilder.AddForeignKey(
name: "FK_PForumTopics_PForumCategories_CategoryId",
table: "PForumTopics",
column: "CategoryId",
principalTable: "PForumCategories",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
}
}

View file

@ -946,7 +946,7 @@ namespace Kurs.Platform.Migrations
b.HasIndex("Slug");
b.ToTable("PBlogCategories", (string)null);
b.ToTable("PBlogCategory", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.BlogPost", b =>
@ -1060,7 +1060,7 @@ namespace Kurs.Platform.Migrations
b.HasIndex("Slug");
b.ToTable("PBlogPosts", (string)null);
b.ToTable("PBlogPost", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.Branch", b =>
@ -2269,6 +2269,79 @@ namespace Kurs.Platform.Migrations
b.ToTable("PPublicApi", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.Route", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<string>("Authority")
.HasColumnType("nvarchar(max)");
b.Property<string>("ComponentPath")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<string>("Key")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Path")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("RouteType")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("PRoute", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.Sector", b =>
{
b.Property<Guid>("Id")
@ -2519,7 +2592,7 @@ namespace Kurs.Platform.Migrations
b.HasIndex("DisplayOrder");
b.ToTable("PForumCategories", (string)null);
b.ToTable("PForumCategory", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Forum.ForumPost", b =>
@ -2588,7 +2661,7 @@ namespace Kurs.Platform.Migrations
b.HasIndex("TopicId");
b.ToTable("PForumPosts", (string)null);
b.ToTable("PForumPost", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Forum.ForumTopic", b =>
@ -2685,7 +2758,7 @@ namespace Kurs.Platform.Migrations
b.HasIndex("LastPostDate");
b.ToTable("PForumTopics", (string)null);
b.ToTable("PForumTopic", (string)null);
});
modelBuilder.Entity("Kurs.Settings.Entities.SettingDefinition", b =>

View file

@ -82,7 +82,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812"
}, {
"url": "index.html",
"revision": "0.kii9phg4rp8"
"revision": "0.vvrlobooaco"
}], {});
workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {

View file

@ -3,21 +3,23 @@ import Theme from '@/components/template/Theme'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { StoreProvider } from 'easy-peasy'
import { BrowserRouter } from 'react-router-dom'
import history from './history'
import './locales'
import { store } from './store'
import { DynamicRoutesProvider } from './routes/dynamicRoutesContext'
const queryClient = new QueryClient()
function App() {
return (
<StoreProvider store={store}>
<BrowserRouter history={history}>
<QueryClientProvider client={queryClient}>
<Theme>
<Layout />
</Theme>
</QueryClientProvider>
<BrowserRouter>
<DynamicRoutesProvider>
<QueryClientProvider client={queryClient}>
<Theme>
<Layout />
</Theme>
</QueryClientProvider>
</DynamicRoutesProvider>
</BrowserRouter>
</StoreProvider>
)

View file

@ -4,7 +4,6 @@ import Simple from './Simple'
import View from '@/views'
import { useStoreState } from '@/store'
import { LAYOUT_TYPE_BLANK } from '@/constants/theme.constant'
import { HiArrowLeft } from 'react-icons/hi'
const AuthLayout = () => {
const layoutType = useStoreState((state) => state.theme.layout.type)

View file

@ -6,11 +6,11 @@ import SideNavToggle from '@/components/template/SideNavToggle'
import SidePanel from '@/components/template/SidePanel'
import UserDropdown from '@/components/template/UserDropdown'
import { useStoreState } from '@/store'
import View from '@/views'
import LanguageSelector from '../template/LanguageSelector'
import Search from '../template/Search'
import StackedSideNav from '../template/StackedSideNav'
import AiAssistant from '../template/AiAssistant'
import { DynamicRouter } from '@/routes/dynamicRouter'
const HeaderActionsStart = () => {
return (
@ -48,7 +48,7 @@ const ClassicLayout = () => {
headerEnd={<HeaderActionsEnd />}
/>
<div className="h-full flex flex-auto flex-col">
<View />
<DynamicRouter />
</div>
</div>
</div>

View file

@ -1,4 +1,5 @@
import Tooltip from '@/components/ui/Tooltip'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { Helmet } from 'react-helmet'
import { FcAssistant } from 'react-icons/fc'
@ -17,7 +18,7 @@ const AiAssistant = () => {
></Helmet>
<Tooltip title="AI Asistan">
<div
onClick={() => navigate('/ai')}
onClick={() => navigate(ROUTES_ENUM.protected.admin.ai)}
className="flex items-center justify-center w-9 h-9 m-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer transition-colors duration-200"
>
<FcAssistant size={28} />

View file

@ -4,7 +4,7 @@ import { APP_NAME } from '@/constants/app.constant'
import { PAGE_CONTAINER_GUTTER_X } from '@/constants/theme.constant'
import { useStoreActions, useStoreState } from '@/store'
import { Link, useNavigate } from 'react-router-dom'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { ROUTES_ENUM } from '@/routes/route.constant'
import UiDialog from '@/views/shared/UiDialog'
export type FooterPageContainerType = 'gutterless' | 'contained'
@ -30,7 +30,7 @@ const FooterContent = () => {
<span>Copyright &copy; {new Date().getFullYear()}</span>
<span className="font-semibold">{APP_NAME}</span>
</div>
<Link to={ROUTES_ENUM.docs.changelog}>
<Link to={ROUTES_ENUM.protected.admin.changeLog}>
<div className="text-muted capitalize">
<span>
<b>UI: </b>
@ -53,7 +53,7 @@ const FooterContent = () => {
type="info"
onConfirm={() => {
setUiVersion(reactAppVersion )
navigate(ROUTES_ENUM.docs.changelog)
navigate(ROUTES_ENUM.protected.admin.changeLog)
}}
title="🎉 Yeni Güncelleme"
>

View file

@ -7,7 +7,7 @@ import Spinner from '@/components/ui/Spinner'
import Tooltip from '@/components/ui/Tooltip'
import { AVATAR_URL } from '@/constants/app.constant'
import NotificationChannels from '@/constants/notification-channel.enum'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { ROUTES_ENUM } from '@/routes/route.constant'
import {
getList,
updateRead,
@ -329,7 +329,7 @@ const _Notification = ({ className }: { className?: string }) => {
<Dropdown.Item variant="header">
<div className="flex justify-center border-t border-gray-200 dark:border-gray-600 px-4 py-2">
<Link
to={ROUTES_ENUM.admin.activityLogs}
to={ROUTES_ENUM.protected.admin.activityLog}
className="font-semibold cursor-pointer p-2 px-3 text-gray-600 hover:text-gray-900 dark:text-gray-200 dark:hover:text-white"
>
{translate('::Abp.Identity.ActivityLogs.ViewAllActivity')}

View file

@ -1,6 +1,6 @@
import type { CommonProps } from '@/@types/common'
import Dropdown from '@/components/ui/Dropdown'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { useStoreState } from '@/store'
import withHeaderItem from '@/utils/hoc/withHeaderItem'
import useAuth from '@/utils/hooks/useAuth'
@ -26,12 +26,12 @@ const _UserDropdown = ({ className }: CommonProps) => {
const dropdownItemList: DropdownList[] = [
{
label: translate('::Abp.Identity.Profile'),
path: ROUTES_ENUM.account.profile,
path: ROUTES_ENUM.protected.admin.profile.general,
icon: <HiOutlineUser />,
},
{
label: translate('::Abp.Identity.ActivityLogs'),
path: ROUTES_ENUM.admin.activityLogs,
path: ROUTES_ENUM.protected.admin.activityLog,
icon: <FiActivity />,
},
]

View file

@ -1,4 +1,4 @@
import { ROUTES_ENUM } from '@/constants/route.constant'
import { ROUTES_ENUM } from "@/routes/route.constant"
export type AppConfig = {
baseUrl: string
@ -13,8 +13,8 @@ export type AppConfig = {
const appConfig: AppConfig = {
baseUrl: import.meta.env.VITE_API_URL,
apiPrefix: '/api',
authenticatedEntryPath: '/home',
unAuthenticatedEntryPath: ROUTES_ENUM.account.login,
authenticatedEntryPath: ROUTES_ENUM.protected.admin.home,
unAuthenticatedEntryPath: ROUTES_ENUM.public.login,
tourPath: '/',
locale: 'en',
uiVersion : undefined

View file

@ -1,74 +0,0 @@
import type { Routes } from '@/@types/routes'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { lazy } from 'react'
const adminRoutes: Routes = [
{
key: ROUTES_ENUM.admin.tenantmanagement,
path: ROUTES_ENUM.admin.tenantmanagement,
component: lazy(() => import('@/views/admin/tenant-management')),
authority: [], //TODO
},
{
key: ROUTES_ENUM.admin.identity.roles,
path: ROUTES_ENUM.admin.identity.roles,
component: lazy(() => import('@/views/admin/identity/Roles')),
authority: [], //TODO
},
{
key: ROUTES_ENUM.admin.identity.users.view,
path: ROUTES_ENUM.admin.identity.users.view,
component: lazy(() => import('@/views/admin/identity/Users')),
authority: [], //TODO
},
{
key: ROUTES_ENUM.admin.identity.users.details,
path: ROUTES_ENUM.admin.identity.users.details,
component: lazy(() => import('@/views/admin/identity/Users/Details')),
authority: [], //TODO
},
{
key: ROUTES_ENUM.admin.organizationUnits,
path: ROUTES_ENUM.admin.organizationUnits,
component: lazy(() => import('@/views/admin/organization-unit/OrganizationUnits')),
authority: [],
},
{
key: ROUTES_ENUM.admin.activityLogs,
path: ROUTES_ENUM.admin.activityLogs,
component: lazy(() => import('@/views/admin/activityLog')),
authority: [],
},
{
key: ROUTES_ENUM.admin.listFormManagement.wizard,
path: ROUTES_ENUM.admin.listFormManagement.wizard,
component: lazy(() => import('@/views/admin/listForm/Wizard')),
authority: [],
},
{
key: ROUTES_ENUM.admin.listFormManagement.edit,
path: ROUTES_ENUM.admin.listFormManagement.edit,
component: lazy(() => import('@/views/admin/listForm/edit/FormEdit')),
authority: [],
},
{
key: ROUTES_ENUM.admin.chart,
path: ROUTES_ENUM.admin.chart,
component: lazy(() => import('@/views/admin/chart/ChartEdit')),
authority: [],
},
{
key: ROUTES_ENUM.admin.forum.management,
path: ROUTES_ENUM.admin.forum.management,
component: lazy(() => import('@/views/forum/Management')),
authority: [],
},
{
key: ROUTES_ENUM.admin.forum.view,
path: ROUTES_ENUM.admin.forum.view,
component: lazy(() => import('@/views/forum/Forum')),
authority: [],
},
]
export { adminRoutes }

View file

@ -1,82 +0,0 @@
import type { Routes } from '@/@types/routes'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { lazy } from 'react'
const publicAuthRoutes: Routes = [
{
key: ROUTES_ENUM.account.login,
path: ROUTES_ENUM.account.login,
component: lazy(() => import('@/views/auth/Login')),
authority: [],
},
{
key: ROUTES_ENUM.account.register,
path: ROUTES_ENUM.account.register,
component: lazy(() => import('@/views/auth/Register')),
authority: [],
},
{
key: ROUTES_ENUM.account.forgotPassword,
path: ROUTES_ENUM.account.forgotPassword,
component: lazy(() => import('@/views/auth/ForgotPassword')),
authority: [],
},
{
key: ROUTES_ENUM.account.resetPassword,
path: ROUTES_ENUM.account.resetPassword,
component: lazy(() => import('@/views/auth/ResetPassword')),
authority: [],
},
{
key: ROUTES_ENUM.account.sendConfirmationCode,
path: ROUTES_ENUM.account.sendConfirmationCode,
component: lazy(() => import('@/views/auth/SendConfirmationCode')),
authority: [],
},
{
key: ROUTES_ENUM.account.verifyConfirmationCode,
path: ROUTES_ENUM.account.verifyConfirmationCode,
component: lazy(() => import('@/views/auth/VerifyConfirmationCode')),
authority: [],
},
{
key: ROUTES_ENUM.account.sendExtendLogin,
path: ROUTES_ENUM.account.sendExtendLogin,
component: lazy(() => import('@/views/auth/ExtendLogin')),
authority: [],
},
]
const protectedAuthRoutes: Routes = [
{
key: ROUTES_ENUM.account.changePassword,
path: ROUTES_ENUM.account.changePassword,
component: lazy(() => import('@/views/auth/Settings')),
authority: [],
},
{
key: ROUTES_ENUM.account.profile,
path: ROUTES_ENUM.account.profile,
component: lazy(() => import('@/views/auth/Settings')),
authority: [],
},
{
key: ROUTES_ENUM.account.notificationSettings,
path: ROUTES_ENUM.account.notificationSettings,
component: lazy(() => import('@/views/auth/Settings')),
authority: [],
},
]
export { protectedAuthRoutes, publicAuthRoutes }
// {
// key: ROUTES_ENUM.account.login,
// path: `${APP_PREFIX_PATH}/account/settings/:tab`,
// component: lazy(() => import('@/views/account/Settings')),
// authority: [ADMIN, USER],
// meta: {
// header: 'Settings',
// headerContainer: true,
// },
// },

View file

@ -1 +0,0 @@
export { protectedRoutes, publicRoutes } from './routes.config'

View file

@ -1,84 +0,0 @@
import type { Routes } from '@/@types/routes'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { lazy } from 'react'
import { adminRoutes } from './adminRoutes'
import { protectedAuthRoutes, publicAuthRoutes } from './authRoute'
export const publicRoutes: Routes = [...publicAuthRoutes]
export const protectedRoutes: Routes = [
{
key: '/',
path: '/home',
component: lazy(() => import('@/views/Home')),
authority: [],
},
{
key: '/ai',
path: '/ai',
component: lazy(() => import('@/views/ai/Assistant')),
authority: [],
},
{
key: '/access-denied',
path: '/access-denied',
component: lazy(() => import('@/views/AccessDenied')),
authority: [],
},
...protectedAuthRoutes,
...adminRoutes,
{
key: ROUTES_ENUM.settings,
path: ROUTES_ENUM.settings,
component: lazy(() => import('@/views/settings/Settings')),
authority: [],
},
{
key: ROUTES_ENUM.list,
path: ROUTES_ENUM.list,
component: lazy(() => import('@/views/list/List')),
authority: [],
},
{
key: ROUTES_ENUM.formNew,
path: ROUTES_ENUM.formNew,
component: lazy(() => import('@/views/form/FormNew')),
authority: [],
},
{
key: ROUTES_ENUM.formView,
path: ROUTES_ENUM.formView,
component: lazy(() => import('@/views/form/FormView')),
authority: [],
},
{
key: ROUTES_ENUM.formEdit,
path: ROUTES_ENUM.formEdit,
component: lazy(() => import('@/views/form/FormEdit')),
authority: [],
},
{
key: ROUTES_ENUM.chart,
path: ROUTES_ENUM.chart,
component: lazy(() => import('@/views/chart/Chart')),
authority: [],
},
{
key: ROUTES_ENUM.pivot,
path: ROUTES_ENUM.pivot,
component: lazy(() => import('@/views/list/ListPivot')),
authority: [],
},
{
key: ROUTES_ENUM.docs.changelog,
path: ROUTES_ENUM.docs.changelog,
component: lazy(() => import('@/views/docs/ChangeLog')),
authority: [],
},
{
key: ROUTES_ENUM.menumanager,
path: ROUTES_ENUM.menumanager,
component: lazy(() => import('@/views/menu/MenuManager')),
authority: [],
},
]

View file

@ -1,54 +0,0 @@
export const ROOT = '/'
export const APP_PREFIX_PATH = '/app'
export const AUTH_PREFIX_PATH = '/auth'
export const UI_COMPONENTS_PREFIX_PATH = '/ui-components'
export const PAGES_PREFIX_PATH = '/pages'
export const DOCS_PREFIX_PATH = '/docs'
export const ROUTES_ENUM = {
account: {
login: '/account/login',
register: '/account/register',
forgotPassword: '/account/forgot-password',
resetPassword: '/account/reset-password',
sendConfirmationCode: '/account/confirm',
sendExtendLogin: '/account/extend-login',
verifyConfirmationCode: '/account/confirm/:userId/:token',
settings: '/account/settings',
changePassword: '/account/settings/password',
profile: '/account/settings/profile',
notificationSettings: '/account/settings/notification-settings',
},
admin: {
tenantmanagement: '/admin/tenant-management',
identity: {
roles: '/admin/identity/roles',
users: {
view: '/admin/identity/users',
details: '/admin/identity/users/detail/:userId',
},
},
activityLogs: '/admin/activityLog',
organizationUnits: '/admin/ous',
listFormManagement: {
wizard: '/admin/listform/wizard',
edit: '/admin/listform/edit/:listFormCode',
},
chart: '/admin/chart/edit/:chartCode',
forum: {
view: '/admin/forum',
management: '/admin/forummanagement',
},
},
settings: '/settings',
list: '/list/:listFormCode',
formNew: '/form/:listFormCode',
formView: '/form/:listFormCode/:id',
formEdit: '/form/:listFormCode/:id/edit',
chart: '/chart/:chartCode',
pivot: '/pivot/:listFormCode',
docs: {
changelog: '/docs/changelog'
},
menumanager: '/menumanager',
}

View file

@ -0,0 +1 @@
export * from './models'

View file

@ -0,0 +1,10 @@
import { FullAuditedEntityDto } from '../abp'
export interface RouteDto extends FullAuditedEntityDto<string> {
id: string;
key: string;
path: string;
componentPath: string;
routeType: string;
authority: string[];
}

View file

@ -0,0 +1,46 @@
import { RouteDto } from '@/proxy/routes'
import { lazy } from 'react'
// Tüm view bileşenlerini import et (vite özel)
const modules = import.meta.glob('../views/**/*.tsx')
const lazyComponentCache = new Map<string, React.LazyExoticComponent<React.ComponentType<any>>>()
export function loadComponent(componentPath: string) {
const cleanedPath = componentPath.replace(/^@\//, '')
const fullPath = `../${cleanedPath}.tsx`
if (lazyComponentCache.has(fullPath)) {
return lazyComponentCache.get(fullPath)!
}
const loader = modules[fullPath]
if (!loader) {
console.error(`Component not found for path: ${fullPath}`)
throw new Error(`Component not found for path: ${fullPath}`)
}
const LazyComponent = lazy(loader as () => Promise<{ default: React.ComponentType<any> }>)
lazyComponentCache.set(fullPath, LazyComponent)
return LazyComponent
}
// React Router için uygun bir route tipi
export interface DynamicReactRoute {
key: string;
path: string;
getComponent: () => React.LazyExoticComponent<React.ComponentType<any>>;
routeType: string;
authority?: string[];
}
// API'den gelen route objesini, React Router için uygun hale getirir
export function mapDynamicRoutes(routes: RouteDto[]): DynamicReactRoute[] {
return routes.map((route) => ({
key: route.path,
path: route.path,
getComponent: () => loadComponent(route.componentPath),
routeType: route.routeType,
authority: route.authority,
}));
}

View file

@ -0,0 +1,69 @@
import React, { useMemo } from 'react'
import { Routes, Route, Navigate } from 'react-router-dom'
import { mapDynamicRoutes } from './dynamicRouteLoader'
import { useDynamicRoutes } from './dynamicRoutesContext'
import NotFound from '@/views/NotFound'
import ProtectedRoute from '@/components/route/ProtectedRoute'
import PermissionGuard from '@/components/route/PermissionGuard'
import PageContainer from '@/components/template/PageContainer'
import { ROUTES_ENUM } from './route.constant'
export const DynamicRouter: React.FC = () => {
const { routes, loading, error } = useDynamicRoutes()
const dynamicRoutes = useMemo(() => mapDynamicRoutes(routes), [routes])
if (loading) return <div>Loading...</div>
if (error) return <div>Hata: {error}</div>
return (
<Routes>
{/* Protected Routes */}
<Route path="/" element={<ProtectedRoute />}>
{dynamicRoutes
.filter((r) => r.routeType === 'protected')
.map((route) => {
const Component = route.getComponent()
return (
<Route
key={route.key}
path={route.path}
element={
<PermissionGuard permissions={route.authority}>
<PageContainer>
<React.Suspense fallback={<div>Loading {route.path}...</div>}>
<Component />
</React.Suspense>
</PageContainer>
</PermissionGuard>
}
/>
)
})}
<Route path="/" element={<Navigate to={ROUTES_ENUM.protected.admin.home} replace />} />
</Route>
{/* Public Routes */}
{dynamicRoutes
.filter((r) => r.routeType === 'public')
.map((route) => {
const Component = route.getComponent()
return (
<Route
key={route.key}
path={route.path}
element={
<React.Suspense fallback={<div>Loading {route.path}...</div>}>
<Component />
</React.Suspense>
}
/>
)
})}
{/* Not Found */}
<Route path="*" element={<NotFound />} />
</Routes>
)
}

View file

@ -0,0 +1,54 @@
import { RouteDto } from '@/proxy/routes'
import { RouteService } from '@/services/route.service'
import React, { createContext, useContext, useEffect, useState } from 'react'
interface DynamicRoutesContextProps {
routes: RouteDto[]
loading: boolean
error: string | null
reload: () => void
}
const DynamicRoutesContext = createContext<DynamicRoutesContextProps | undefined>(undefined)
export const useDynamicRoutes = () => {
const ctx = useContext(DynamicRoutesContext)
if (!ctx) throw new Error('useDynamicRoutes must be used within DynamicRoutesProvider')
return ctx
}
export const DynamicRoutesProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [routes, setRoutes] = useState<RouteDto[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const fetchRoutes = async () => {
setLoading(true)
setError(null)
try {
const routeService = new RouteService()
const res = await routeService.getRoutes()
if (res.data) {
setRoutes(res.data)
} else {
throw new Error('No routes found')
}
} catch (e: any) {
setError(e.message || 'Unknown error')
} finally {
setLoading(false)
}
}
useEffect(() => {
fetchRoutes()
}, [])
return (
<DynamicRoutesContext.Provider value={{ routes, loading, error, reload: fetchRoutes }}>
{children}
</DynamicRoutesContext.Provider>
)
}

View file

@ -0,0 +1,53 @@
export const ROUTES_ENUM = {
public: {
login: '/login',
register: '/register',
forgotPassword: '/forgot-password',
resetPassword: '/reset-password',
sendConfirmationCode: '/confirm',
sendExtendLogin: '/extend-login',
verifyConfirmationCode: '/confirm/:userId/:token',
},
protected: {
root: '/home',
saas: {
menuManager: '/saas/menuManager',
listFormManagement: {
wizard: '/saas/listform/wizard',
edit: '/saas/listform/edit/:listFormCode',
},
chartManagement: {
edit: '/saas/chart/edit/:chartCode',
},
forumManagement: '/admin/forumManagement',
},
admin: {
home: '/home',
ai: '/admin/ai',
profile: {
general: '/admin/settings/general',
password: '/admin/settings/password',
notificationSettings: '/admin/settings/notification-settings',
},
activityLog: '/admin/activityLog',
changeLog: '/admin/changeLog',
settings: '/admin/settings',
identity: {
user: {
detail: '/admin/users/detail/:userId',
},
ous: '/admin/ous',
},
forum: '/admin/forum',
list: '/admin/list/:listFormCode',
formNew: '/admin/form/:listFormCode',
formView: '/admin/form/:listFormCode/:id',
formEdit: '/admin/form/:listFormCode/:id/edit',
chart: '/admin/chart/:chartCode',
pivot: '/admin/pivot/:listFormCode',
},
},
}

View file

@ -0,0 +1,18 @@
import { RouteDto } from '@/proxy/routes'
import apiService, { Config } from '@/services/api.service'
export class RouteService {
apiName = 'Default'
getRoutes = (config?: Partial<Config>) =>
apiService.fetchData<RouteDto[]>(
{
method: 'GET',
url: '/api/app/route',
},
{ apiName: this.apiName, ...config },
)
}
const routeService = new RouteService()
export default routeService

View file

@ -94,7 +94,7 @@ export const baseModel: BaseModel = {
}),
addAiPost: action((state, payload) => {
state.aiPosts = [...state.aiPosts, payload]
localStorage.setItem('aiPosts', JSON.stringify(state.aiPosts))
localStorage.setItem('AiPosts', JSON.stringify(state.aiPosts))
}),
setAiPosts: action((state, payload) => {

View file

@ -65,9 +65,7 @@ function useAuth() {
handleToken({ token, refreshToken, expiresIn })
const redirectUrl = query.get(REDIRECT_URL_KEY)
setTimeout(() => {
navigate(redirectUrl ?? appConfig.authenticatedEntryPath)
}, 2000)
navigate(redirectUrl ?? appConfig.authenticatedEntryPath)
return {
status: 'success',

13
ui/src/views/NotFound.tsx Normal file
View file

@ -0,0 +1,13 @@
import React, { useEffect, useRef } from 'react'
const NotFoundPage = () => {
return (
<div className="flex flex-col items-center justify-center min-h-[90vh] bg-white font-inter">
<div className="text-[8rem] sm:text-[10rem] md:text-[12rem] font-bold mb-6 bg-gradient-to-br from-primary to-secondary bg-clip-text animate-pulse">
404
</div>
</div>
)
}
export default NotFoundPage

View file

@ -1,64 +1,21 @@
import type { LayoutType } from '@/@types/theme'
import AppRoute from '@/components/route/AppRoute'
import PermissionGuard from '@/components/route/PermissionGuard'
import ProtectedRoute from '@/components/route/ProtectedRoute'
import PublicRoute from '@/components/route/PublicRoute'
import Loading from '@/components/shared/Loading'
import PageContainer from '@/components/template/PageContainer'
import { Alert, Button } from '@/components/ui'
import appConfig from '@/configs/app.config'
import { protectedRoutes, publicRoutes } from '@/configs/routes.config'
import { store, useStoreState } from '@/store'
import { Suspense, useEffect } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import { MdArrowBack } from 'react-icons/md'
import { Navigate, Route, Routes, useLocation } from 'react-router-dom'
import { useLocation } from 'react-router-dom'
import DialogProvider from './shared/DialogContext'
import DialogShowComponent from './shared/DialogContext/DialogShowComponent'
import UiDialog from './shared/UiDialog'
import { DynamicRouter } from '@/routes/dynamicRouter'
interface ViewsProps {
pageContainerType?: 'default' | 'gutterless' | 'contained'
layout?: LayoutType
}
type AllRoutesProps = ViewsProps
const { authenticatedEntryPath } = appConfig
const AllRoutes = (props: AllRoutesProps) => {
return (
<Routes>
<Route path="/" element={<ProtectedRoute />}>
<Route path="/" element={<Navigate replace to={authenticatedEntryPath} />} />
{protectedRoutes.map((route, index) => (
<Route
key={route.key + index}
path={route.path}
element={
<PermissionGuard permissions={route.authority}>
<PageContainer {...props} {...route.meta}>
<AppRoute routeKey={route.key} component={route.component} {...route.meta} />
</PageContainer>
</PermissionGuard>
}
/>
))}
<Route path="*" element={<Navigate replace to="/" />} />
</Route>
<Route path="/" element={<PublicRoute />}>
{publicRoutes.map((route) => (
<Route
key={route.path}
path={route.path}
element={<AppRoute routeKey={route.key} component={route.component} {...route.meta} />}
/>
))}
</Route>
</Routes>
)
}
function fallbackRender({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: any }) {
return (
<Alert showIcon className="mb-4" type="danger">
@ -110,7 +67,7 @@ const Views = (props: ViewsProps) => {
))}
<DialogProvider>
<DialogShowComponent></DialogShowComponent>
<AllRoutes {...props} />
<DynamicRouter />
</DialogProvider>
</Suspense>
</ErrorBoundary>

View file

@ -1,3 +0,0 @@
import ActivityLog from './ActivityLog'
export default ActivityLog

View file

@ -1,3 +0,0 @@
import Roles from './Roles'
export default Roles

View file

@ -1,3 +0,0 @@
import Users from './Users'
export default Users

View file

@ -8,7 +8,7 @@ import {
Select,
toast,
} from '@/components/ui'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { getPermissions } from '@/proxy/admin/identity.service'
import { postListFormWizard } from '@/proxy/admin/list-form/list-form.service'
import { ListFormWizardDto } from '@/proxy/admin/list-form/models'
@ -156,7 +156,7 @@ const Wizard = () => {
setSubmitting(false)
setTimeout(() => {
navigate(
ROUTES_ENUM.admin.listFormManagement.edit.replace(
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(
':listFormCode',
values.listFormCode,
),

View file

@ -1,3 +0,0 @@
import Tenants from './Tenants'
export default Tenants

View file

@ -5,7 +5,7 @@ const LoadAiPostsFromLocalStorage = () => {
const setAiPosts = useStoreActions((actions) => actions.base.messages.setAiPosts)
useEffect(() => {
const saved = localStorage.getItem('aiPosts')
const saved = localStorage.getItem('AiPosts')
if (saved) {
try {
const parsed = JSON.parse(saved)

View file

@ -4,7 +4,7 @@ import Alert from '@/components/ui/Alert'
import Button from '@/components/ui/Button'
import { FormContainer, FormItem } from '@/components/ui/Form'
import Input from '@/components/ui/Input'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { sendExtendLoginRequest } from '@/proxy/account/account.service'
import { store } from '@/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
@ -26,7 +26,7 @@ const validationSchema = Yup.object().shape({
const ExtendLogin = () => {
const { userName } = store.getState().auth.user
const disableSubmit = false
const signInUrl = ROUTES_ENUM.account.login
const signInUrl = ROUTES_ENUM.public.login
const [emailSent, setEmailSent] = useState(false)

View file

@ -4,7 +4,7 @@ import Alert from '@/components/ui/Alert'
import Button from '@/components/ui/Button'
import { FormContainer, FormItem } from '@/components/ui/Form'
import Input from '@/components/ui/Input'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { sendPasswordResetCode } from '@/proxy/account/account.service'
import { store } from '@/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
@ -28,7 +28,7 @@ const validationSchema = Yup.object().shape({
const ForgotPassword = () => {
const { userName } = store.getState().auth.user
const disableSubmit = false
const signInUrl = ROUTES_ENUM.account.login
const signInUrl = ROUTES_ENUM.public.login
const [emailSent, setEmailSent] = useState(false)

View file

@ -8,7 +8,7 @@ import Checkbox from '@/components/ui/Checkbox'
import { FormContainer, FormItem } from '@/components/ui/Form'
import Input from '@/components/ui/Input'
import PlatformLoginResultType from '@/constants/login.result.enum'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { getTenantByName } from '@/proxy/admin/tenant/tenant.service'
import { useStoreActions, useStoreState } from '@/store'
import useAuth from '@/utils/hooks/useAuth'
@ -115,7 +115,7 @@ const Login = () => {
if (data.pResult === PlatformLoginResultType.NotAllowed) {
setWarningTimeout(data.description)
navigate(ROUTES_ENUM.account.sendConfirmationCode)
navigate(ROUTES_ENUM.public.sendConfirmationCode)
} else {
setWarning('')
}
@ -149,14 +149,14 @@ const Login = () => {
data.pResult === PlatformLoginResultType.ShouldChangePasswordPeriodic
) {
setWarningTimeout(data.description)
navigate(ROUTES_ENUM.account.forgotPassword)
navigate(ROUTES_ENUM.public.forgotPassword)
} else {
setWarning('')
}
if (data.pResult === PlatformLoginResultType.LoginEndDateDue) {
setWarningTimeout(data.description)
navigate(ROUTES_ENUM.account.sendExtendLogin)
navigate(ROUTES_ENUM.public.sendExtendLogin)
} else {
setWarning('')
}
@ -301,7 +301,7 @@ const Login = () => {
<Field className="mb-0" name="rememberMe" component={Checkbox}>
{translate('::Abp.Account.RememberMe')}
</Field>
<ActionLink to={ROUTES_ENUM.account.forgotPassword}>
<ActionLink to={ROUTES_ENUM.public.forgotPassword}>
{translate('::Abp.Account.ForgotPassword')}
</ActionLink>
</div>
@ -330,7 +330,7 @@ const Login = () => {
</Button>
<div className="mt-4 text-center">
<span>{translate('::Abp.Account.SignUp.Message')} </span>
<ActionLink to={ROUTES_ENUM.account.register}>
<ActionLink to={ROUTES_ENUM.public.register}>
{translate('::Abp.Account.Register')}
</ActionLink>
</div>

View file

@ -4,7 +4,7 @@ import Alert from '@/components/ui/Alert'
import Button from '@/components/ui/Button'
import { FormContainer, FormItem } from '@/components/ui/Form'
import Input from '@/components/ui/Input'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { ROUTES_ENUM } from '@/routes/route.constant'
import useAuth from '@/utils/hooks/useAuth'
import useTimeOutMessage from '@/utils/hooks/useTimeOutMessage'
import Captcha from '@/components/shared/Captcha'
@ -31,7 +31,7 @@ const validationSchema = Yup.object().shape({
const Register = () => {
const disableSubmit = false
const signInUrl = ROUTES_ENUM.account.login
const signInUrl = ROUTES_ENUM.public.login
const { signUp } = useAuth()
const { translate } = useLocalization()

View file

@ -3,7 +3,7 @@ import PasswordInput from '@/components/shared/PasswordInput'
import Alert from '@/components/ui/Alert'
import Button from '@/components/ui/Button'
import { FormContainer, FormItem } from '@/components/ui/Form'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { resetPassword } from '@/proxy/account/account.service'
import { useLocalization } from '@/utils/hooks/useLocalization'
import useTimeOutMessage from '@/utils/hooks/useTimeOutMessage'
@ -25,7 +25,7 @@ const validationSchema = Yup.object().shape({
const ResetPassword = () => {
const disableSubmit = false
const signInUrl = ROUTES_ENUM.account.login
const signInUrl = ROUTES_ENUM.public.login
const [searchParams] = useSearchParams()
const userId = searchParams.get('userId')
@ -72,7 +72,7 @@ const ResetPassword = () => {
}
const onContinue = () => {
navigate(ROUTES_ENUM.account.login)
navigate(ROUTES_ENUM.public.login)
}
return (

View file

@ -3,7 +3,7 @@ import { Alert, Button, FormContainer, FormItem, Input } from '@/components/ui'
import { Field, Form, Formik } from 'formik'
import * as Yup from 'yup'
import { ActionLink } from '@/components/shared'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { store } from '@/store'
import Captcha from '@/components/shared/Captcha'
import { useLocalization } from '@/utils/hooks/useLocalization'
@ -85,7 +85,7 @@ const SendConfirmationCode = () => {
</Button>
<div className="mt-4 text-center">
<span>{translate('::Abp.Account.Backto')} </span>
<ActionLink to={ROUTES_ENUM.account.login}>{translate('::Abp.Account.SignIn')}</ActionLink>
<ActionLink to={ROUTES_ENUM.public.login}>{translate('::Abp.Account.SignIn')}</ActionLink>
</div>
</FormContainer>
</Form>

View file

@ -1,7 +1,7 @@
import AdaptableCard from '@/components/shared/AdaptableCard'
import Container from '@/components/shared/Container'
import Tabs from '@/components/ui/Tabs'
import { ROUTES_ENUM } from '@/constants/route.constant'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { Suspense, lazy, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
@ -21,9 +21,9 @@ type AccountSetting = {
}[]
}
const Profile = lazy(() => import('./components/Profile'))
const Password = lazy(() => import('./components/Password'))
const NotificationSettings = lazy(() => import('./components/NotificationSettings'))
const General = lazy(() => import('./settings/General'))
const Password = lazy(() => import('./settings/Password'))
const NotificationSettings = lazy(() => import('./settings/NotificationSettings'))
const { TabNav, TabList } = Tabs
@ -36,9 +36,9 @@ const Settings = () => {
path: string
}
> = {
profile: {
label: translate('::Abp.Identity.Profile'),
path: 'profile',
general: {
label: translate('::Abp.Identity.Profile.General'),
path: 'general',
},
password: {
label: translate('::Abp.Identity.Password'),
@ -59,7 +59,7 @@ const Settings = () => {
const onTabChange = (val: string) => {
if (settingsMenu[val]) {
setCurrentTab(settingsMenu[val].path)
navigate(`${ROUTES_ENUM.account.settings}/${settingsMenu[val].path}`)
navigate(`${ROUTES_ENUM.protected.admin.settings}/${settingsMenu[val].path}`)
}
}
@ -77,7 +77,7 @@ const Settings = () => {
</Tabs>
<div className="px-4 py-6">
<Suspense fallback={<></>}>
{currentTab === 'profile' && <Profile />}
{currentTab === 'general' && <General />}
{currentTab === 'password' && <Password data={data.loginHistory} />}
{currentTab === 'notification-settings' && <NotificationSettings />}
</Suspense>

View file

@ -1,3 +0,0 @@
import Settings from './Settings'
export default Settings

View file

@ -24,16 +24,16 @@ import 'react-advanced-cropper/dist/style.css'
import { BsWhatsapp } from 'react-icons/bs'
import { HiMail, HiOutlineTrash, HiOutlineUserCircle, HiPhone, HiPlus } from 'react-icons/hi'
import * as Yup from 'yup'
import FormDesription from './FormDesription'
import FormRow from './FormRow'
import isEmpty from 'lodash/isEmpty'
import FormRow from '@/views/shared/FormRow'
import FormDesription from '@/views/shared/FormDesription'
const schema = Yup.object().shape({
name: Yup.string().min(3).max(50).required(),
surname: Yup.string().min(3).max(50).required(),
})
const Profile = () => {
const General = () => {
const [loading, setLoading] = useState(true)
const [profileData, setProfileData] = useState<ProfileDto>()
const [formData, setFormData] = useState<UpdateProfileDto>()
@ -303,4 +303,4 @@ const Profile = () => {
)
}
export default Profile
export default General

View file

@ -5,6 +5,8 @@ import Notification from '@/components/ui/Notification'
import toast from '@/components/ui/toast'
import { changePassword } from '@/proxy/account/account.service'
import { useLocalization } from '@/utils/hooks/useLocalization'
import FormDesription from '@/views/shared/FormDesription'
import FormRow from '@/views/shared/FormRow'
import { Field, Form, Formik } from 'formik'
import {
HiOutlineDesktopComputer,
@ -12,8 +14,6 @@ import {
HiOutlineDeviceTablet,
} from 'react-icons/hi'
import * as Yup from 'yup'
import FormDesription from './FormDesription'
import FormRow from './FormRow'
type LoginHistory = {
type: string

View file

@ -1,9 +1,9 @@
import RolesPermission from '@/views/admin/identity/Roles/RolesPermission'
import UsersPermission from '@/views/admin/identity/Users/UsersPermission'
import TenantsConnectionString from '@/views/admin/tenant-management/TenantsConnectionString'
import { useDialogContext } from './DialogProvider'
import CreateNotification from '@/views/admin/notification/CreateNotification'
import AuditLogDetail from '@/views/admin/auditLog/AuditLogDetail'
import RolesPermission from '@/views/admin/role-management/RolesPermission'
import UsersPermission from '@/views/admin/user-management/UsersPermission'
const DialogShowComponent = (): JSX.Element => {
const dialogContext: any = useDialogContext()