From 4d127032aff786af94e8260788976c46531747e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Mon, 23 Jun 2025 17:58:13 +0300 Subject: [PATCH] =?UTF-8?q?forum=20g=C3=BCncellemeleri?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Forum/ForumDtos.cs | 189 + .../Forum/IForumAppService.cs | 39 + .../Forum/ForumAppService.cs | 524 ++ .../Forum/ForumAutoMapperProfile.cs | 15 + .../Seeds/PlatformDataSeeder.cs | 27 +- .../Seeds/SeederData.json | 218 +- .../Seeds/SeederDto.cs | 30 +- .../Kurs.Platform.Domain/Entities/Forum.cs | 109 + .../EntityFrameworkCore/PlatformDbContext.cs | 65 + .../20250623140407_AddForum.Designer.cs | 5109 +++++++++++++++++ .../Migrations/20250623140407_AddForum.cs | 165 + .../PlatformDbContextModelSnapshot.cs | 289 + ui/dev-dist/sw.js | 2 +- ui/src/configs/routes.config/adminRoutes.tsx | 12 + ui/src/constants/route.constant.ts | 5 +- ui/src/proxy/forum/forum.ts | 50 + ui/src/services/forumService.ts | 347 ++ ui/src/utils/hooks/useForumSearch.ts | 80 + ui/src/views/forum/Forum.tsx | 83 + ui/src/views/forum/Management.tsx | 102 + ui/src/views/forum/admin/AdminStats.tsx | 105 + ui/src/views/forum/admin/AdminView.tsx | 160 + .../views/forum/admin/CategoryManagement.tsx | 321 ++ ui/src/views/forum/admin/PostManagement.tsx | 282 + ui/src/views/forum/admin/TopicManagement.tsx | 374 ++ ui/src/views/forum/forum/CategoryCard.tsx | 61 + ui/src/views/forum/forum/CreatePostModal.tsx | 69 + ui/src/views/forum/forum/CreateTopicModal.tsx | 85 + ui/src/views/forum/forum/ForumView.tsx | 493 ++ ui/src/views/forum/forum/PostCard.tsx | 81 + ui/src/views/forum/forum/SearchModal.tsx | 268 + ui/src/views/forum/forum/TopicCard.tsx | 75 + ui/src/views/forum/forum/utils.tsx | 24 + ui/src/views/forum/useForumData.ts | 438 ++ 34 files changed, 10231 insertions(+), 65 deletions(-) create mode 100644 api/src/Kurs.Platform.Application.Contracts/Forum/ForumDtos.cs create mode 100644 api/src/Kurs.Platform.Application.Contracts/Forum/IForumAppService.cs create mode 100644 api/src/Kurs.Platform.Application/Forum/ForumAppService.cs create mode 100644 api/src/Kurs.Platform.Application/Forum/ForumAutoMapperProfile.cs create mode 100644 api/src/Kurs.Platform.Domain/Entities/Forum.cs create mode 100644 api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20250623140407_AddForum.Designer.cs create mode 100644 api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20250623140407_AddForum.cs create mode 100644 ui/src/proxy/forum/forum.ts create mode 100644 ui/src/services/forumService.ts create mode 100644 ui/src/utils/hooks/useForumSearch.ts create mode 100644 ui/src/views/forum/Forum.tsx create mode 100644 ui/src/views/forum/Management.tsx create mode 100644 ui/src/views/forum/admin/AdminStats.tsx create mode 100644 ui/src/views/forum/admin/AdminView.tsx create mode 100644 ui/src/views/forum/admin/CategoryManagement.tsx create mode 100644 ui/src/views/forum/admin/PostManagement.tsx create mode 100644 ui/src/views/forum/admin/TopicManagement.tsx create mode 100644 ui/src/views/forum/forum/CategoryCard.tsx create mode 100644 ui/src/views/forum/forum/CreatePostModal.tsx create mode 100644 ui/src/views/forum/forum/CreateTopicModal.tsx create mode 100644 ui/src/views/forum/forum/ForumView.tsx create mode 100644 ui/src/views/forum/forum/PostCard.tsx create mode 100644 ui/src/views/forum/forum/SearchModal.tsx create mode 100644 ui/src/views/forum/forum/TopicCard.tsx create mode 100644 ui/src/views/forum/forum/utils.tsx create mode 100644 ui/src/views/forum/useForumData.ts diff --git a/api/src/Kurs.Platform.Application.Contracts/Forum/ForumDtos.cs b/api/src/Kurs.Platform.Application.Contracts/Forum/ForumDtos.cs new file mode 100644 index 00000000..bee65b7f --- /dev/null +++ b/api/src/Kurs.Platform.Application.Contracts/Forum/ForumDtos.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Volo.Abp.Application.Dtos; + +namespace Kurs.Platform.Forum; + +// Search DTOs +public class SearchForumInput +{ + [Required] + public string Query { get; set; } + public Guid? CategoryId { get; set; } + public Guid? TopicId { get; set; } + public bool SearchInCategories { get; set; } = true; + public bool SearchInTopics { get; set; } = true; + public bool SearchInPosts { get; set; } = true; + public int Page { get; set; } = 1; + public int PageSize { get; set; } = 20; +} + +public class ForumSearchResultDto +{ + public List Categories { get; set; } = new(); + public List Topics { get; set; } = new(); + public List Posts { get; set; } = new(); + public int TotalCount { get; set; } +} + +// Category DTOs +public class ForumCategoryDto : EntityDto +{ + public string Name { get; set; } + public string Slug { get; set; } + public string Description { get; set; } + public string Icon { get; set; } + public int DisplayOrder { get; set; } + public bool IsActive { get; set; } + public bool IsLocked { get; set; } + public int TopicCount { get; set; } + public int PostCount { get; set; } + public DateTime? LastPostDate { get; set; } + public Guid? LastPostUserId { get; set; } +} + +public class CreateForumCategoryDto +{ + [Required] + [StringLength(100)] + public string Name { get; set; } + + [Required] + [StringLength(100)] + public string Slug { get; set; } + + [Required] + [StringLength(500)] + public string Description { get; set; } + + [StringLength(10)] + public string Icon { get; set; } + + public int DisplayOrder { get; set; } + public bool IsActive { get; set; } = true; + public bool IsLocked { get; set; } = false; +} + +public class UpdateForumCategoryDto : CreateForumCategoryDto +{ +} + +public class GetCategoriesInput : PagedAndSortedResultRequestDto +{ + public bool? IsActive { get; set; } + public string Search { get; set; } + + public GetCategoriesInput() + { + MaxResultCount = 10; + SkipCount = 0; + } +} + +// Topic DTOs +public class ForumTopicDto : EntityDto +{ + public string Title { get; set; } + public string Content { get; set; } + public Guid CategoryId { get; set; } + public Guid AuthorId { get; set; } + public string AuthorName { get; set; } + public int ViewCount { get; set; } + public int ReplyCount { get; set; } + public int LikeCount { get; set; } + public bool IsPinned { get; set; } + public bool IsLocked { get; set; } + public bool IsSolved { get; set; } + public Guid? LastPostId { get; set; } + public DateTime? LastPostDate { get; set; } + public Guid? LastPostUserId { get; set; } + public string LastPostUserName { get; set; } +} + +public class CreateForumTopicDto +{ + [Required] + [StringLength(200)] + public string Title { get; set; } + + [Required] + public string Content { get; set; } + + [Required] + public Guid CategoryId { get; set; } + + public bool IsPinned { get; set; } = false; + public bool IsLocked { get; set; } = false; +} + +public class UpdateForumTopicDto +{ + [Required] + [StringLength(200)] + public string Title { get; set; } + + [Required] + public string Content { get; set; } + + public bool IsPinned { get; set; } + public bool IsLocked { get; set; } + public bool IsSolved { get; set; } +} + +public class GetTopicsInput : PagedAndSortedResultRequestDto +{ + public Guid? CategoryId { get; set; } + public bool? IsPinned { get; set; } + public bool? IsSolved { get; set; } + public string Search { get; set; } +} + +// Post DTOs +public class ForumPostDto : EntityDto +{ + public Guid TopicId { get; set; } + public string Content { get; set; } + public Guid AuthorId { get; set; } + public string AuthorName { get; set; } + public int LikeCount { get; set; } + public bool IsAcceptedAnswer { get; set; } + + public Guid? ParentPostId { get; set; } +} + +public class CreateForumPostDto +{ + [Required] + public Guid TopicId { get; set; } + + [Required] + public string Content { get; set; } + + public Guid? ParentPostId { get; set; } +} + +public class UpdateForumPostDto +{ + [Required] + public string Content { get; set; } + + public bool IsAcceptedAnswer { get; set; } +} + +public class GetPostsInput : PagedAndSortedResultRequestDto +{ + public Guid? TopicId { get; set; } + public bool? IsAcceptedAnswer { get; set; } + public string Search { get; set; } +} + +// Statistics DTO +public class ForumStatsDto +{ + public int TotalCategories { get; set; } + public int TotalTopics { get; set; } + public int TotalPosts { get; set; } + public long TotalUsers { get; set; } + public long ActiveUsers { get; set; } +} diff --git a/api/src/Kurs.Platform.Application.Contracts/Forum/IForumAppService.cs b/api/src/Kurs.Platform.Application.Contracts/Forum/IForumAppService.cs new file mode 100644 index 00000000..1d74687f --- /dev/null +++ b/api/src/Kurs.Platform.Application.Contracts/Forum/IForumAppService.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Kurs.Platform.Forum; + +public interface IForumAppService : IApplicationService +{ + // Search + Task SearchAsync(SearchForumInput input); + + // Categories + Task> GetCategoriesAsync(GetCategoriesInput input); + Task GetCategoryAsync(Guid id); + Task GetCategoryBySlugAsync(string slug); + Task CreateCategoryAsync(CreateForumCategoryDto input); + Task UpdateCategoryAsync(Guid id, UpdateForumCategoryDto input); + Task DeleteCategoryAsync(Guid id); + + // Topics + Task> GetTopicsAsync(GetTopicsInput input); + Task GetTopicAsync(Guid id); + Task CreateTopicAsync(CreateForumTopicDto input); + Task UpdateTopicAsync(Guid id, UpdateForumTopicDto input); + Task DeleteTopicAsync(Guid id); + + // Posts + Task> GetPostsAsync(GetPostsInput input); + Task GetPostAsync(Guid id); + Task CreatePostAsync(CreateForumPostDto input); + Task UpdatePostAsync(Guid id, UpdateForumPostDto input); + Task DeletePostAsync(Guid id); + Task LikePostAsync(Guid id); + Task UnlikePostAsync(Guid id); + + // Statistics + Task GetForumStatsAsync(); +} diff --git a/api/src/Kurs.Platform.Application/Forum/ForumAppService.cs b/api/src/Kurs.Platform.Application/Forum/ForumAppService.cs new file mode 100644 index 00000000..9de3c5ae --- /dev/null +++ b/api/src/Kurs.Platform.Application/Forum/ForumAppService.cs @@ -0,0 +1,524 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Authorization; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Identity; + +namespace Kurs.Platform.Forum; + +[Authorize] +public class ForumAppService : PlatformAppService, IForumAppService +{ + private readonly IRepository _categoryRepository; + private readonly IRepository _topicRepository; + private readonly IRepository _postRepository; + private readonly IIdentityUserRepository _identityUserRepository; + + + public ForumAppService( + IRepository categoryRepository, + IRepository topicRepository, + IRepository postRepository, + IIdentityUserRepository identityUserRepository) + { + _categoryRepository = categoryRepository; + _topicRepository = topicRepository; + _postRepository = postRepository; + _identityUserRepository = identityUserRepository; + + } + + // Search functionality + public async Task SearchAsync(SearchForumInput input) + { + var result = new ForumSearchResultDto + { + Categories = [], + Topics = [], + Posts = [], + TotalCount = 0 + }; + + if (string.IsNullOrWhiteSpace(input.Query)) + return result; + + var query = input.Query.ToLower(); + + // Search in categories + if (input.SearchInCategories) + { + var categoryQuery = await _categoryRepository.GetQueryableAsync(); + var categories = await AsyncExecuter.ToListAsync( + categoryQuery.Where(c => c.IsActive && + (c.Name.ToLower().Contains(query) || + c.Description.ToLower().Contains(query))) + .Take(10) + ); + + result.Categories = ObjectMapper.Map, List>(categories); + } + + // Search in topics + if (input.SearchInTopics) + { + var topicQuery = await _topicRepository.GetQueryableAsync(); + var topics = await AsyncExecuter.ToListAsync( + topicQuery.Where(t => + t.Title.ToLower().Contains(query) || + t.Content.ToLower().Contains(query) || + t.AuthorName.ToLower().Contains(query)) + .OrderByDescending(t => t.CreationTime) + .Take(20) + ); + + result.Topics = ObjectMapper.Map, List>(topics); + } + + // Search in posts + if (input.SearchInPosts) + { + var postQuery = await _postRepository.GetQueryableAsync(); + var posts = await AsyncExecuter.ToListAsync( + postQuery.Where(p => + p.Content.ToLower().Contains(query) || + p.AuthorName.ToLower().Contains(query)) + .OrderByDescending(p => p.CreationTime) + .Take(30) + ); + + result.Posts = ObjectMapper.Map, List>(posts); + } + + result.TotalCount = result.Categories.Count + result.Topics.Count + result.Posts.Count; + return result; + } + + // Category management + public async Task> GetCategoriesAsync(GetCategoriesInput input) + { + var queryable = await _categoryRepository.GetQueryableAsync(); + + if (input.IsActive.HasValue) + { + queryable = queryable.Where(c => c.IsActive == input.IsActive.Value); + } + + if (!string.IsNullOrWhiteSpace(input.Search)) + { + var search = input.Search.ToLower(); + queryable = queryable.Where(c => + c.Name.ToLower().Contains(search) || + c.Description.ToLower().Contains(search)); + } + + queryable = queryable.OrderBy(c => c.DisplayOrder); + + var totalCount = await AsyncExecuter.CountAsync(queryable); + + var skipCount = input.SkipCount >= 0 ? input.SkipCount : 0; + var maxResultCount = input.MaxResultCount > 0 ? input.MaxResultCount : 10; + + var categories = await AsyncExecuter.ToListAsync( + queryable.Skip(input.SkipCount).Take(input.MaxResultCount) + ); + + return new PagedResultDto( + totalCount, + ObjectMapper.Map, List>(categories) + ); + } + + public async Task GetCategoryAsync(Guid id) + { + var category = await _categoryRepository.GetAsync(id); + + return ObjectMapper.Map(category); + } + + public async Task GetCategoryBySlugAsync(string slug) + { + var category = await _categoryRepository.FirstOrDefaultAsync(c => c.Slug == slug); + if (category == null) + throw new EntityNotFoundException(typeof(ForumCategory), slug); + + return ObjectMapper.Map(category); + } + + [Authorize("App.ForumManagement.Create")] + public async Task CreateCategoryAsync(CreateForumCategoryDto input) + { + var category = new ForumCategory( + GuidGenerator.Create(), + input.Name, + input.Slug, + input.Description, + input.Icon, + input.DisplayOrder + ) + { + IsActive = input.IsActive, + IsLocked = input.IsLocked + }; + + await _categoryRepository.InsertAsync(category); + return ObjectMapper.Map(category); + } + + [Authorize("App.ForumManagement.Update")] + public async Task UpdateCategoryAsync(Guid id, UpdateForumCategoryDto input) + { + var category = await _categoryRepository.GetAsync(id); + + category.Name = input.Name; + category.Slug = input.Slug; + category.Description = input.Description; + category.Icon = input.Icon; + category.DisplayOrder = input.DisplayOrder; + category.IsActive = input.IsActive; + category.IsLocked = input.IsLocked; + + await _categoryRepository.UpdateAsync(category); + return ObjectMapper.Map(category); + } + + [Authorize("App.ForumManagement.Delete")] + public async Task DeleteCategoryAsync(Guid id) + { + // Delete all topics and posts in this category + var topics = await _topicRepository.GetListAsync(t => t.CategoryId == id); + var topicIds = topics.Select(t => t.Id).ToList(); + + if (topicIds.Any()) + { + await _postRepository.DeleteAsync(p => topicIds.Contains(p.TopicId)); + await _topicRepository.DeleteAsync(t => t.CategoryId == id); + } + + await _categoryRepository.DeleteAsync(id); + } + + // Topic management + public async Task> GetTopicsAsync(GetTopicsInput input) + { + var queryable = await _topicRepository.GetQueryableAsync(); + + if (input.CategoryId.HasValue) + { + queryable = queryable.Where(t => t.CategoryId == input.CategoryId.Value); + } + + if (input.IsPinned.HasValue) + { + queryable = queryable.Where(t => t.IsPinned == input.IsPinned.Value); + } + + if (input.IsSolved.HasValue) + { + queryable = queryable.Where(t => t.IsSolved == input.IsSolved.Value); + } + + if (!string.IsNullOrWhiteSpace(input.Search)) + { + var search = input.Search.ToLower(); + queryable = queryable.Where(t => + t.Title.ToLower().Contains(search) || + t.Content.ToLower().Contains(search)); + } + + queryable = queryable.OrderByDescending(t => t.IsPinned) + .ThenByDescending(t => t.CreationTime); + + var totalCount = await AsyncExecuter.CountAsync(queryable); + var topics = await AsyncExecuter.ToListAsync( + queryable.Skip(input.SkipCount).Take(input.MaxResultCount) + ); + + return new PagedResultDto( + totalCount, + ObjectMapper.Map, List>(topics) + ); + } + + public async Task GetTopicAsync(Guid id) + { + var topic = await _topicRepository.GetAsync(id); + + topic.ViewCount++; + await _topicRepository.UpdateAsync(topic); + + return ObjectMapper.Map(topic); + } + + public async Task CreateTopicAsync(CreateForumTopicDto input) + { + var topic = new ForumTopic( + GuidGenerator.Create(), + input.Title, + input.Content, + input.CategoryId, + CurrentUser.Id.Value, + CurrentUser.Name + ) + { + IsPinned = input.IsPinned, + IsLocked = input.IsLocked + }; + + await _topicRepository.InsertAsync(topic); + + // Update category topic count + var category = await _categoryRepository.GetAsync(input.CategoryId); + category.TopicCount++; + await _categoryRepository.UpdateAsync(category); + + return ObjectMapper.Map(topic); + } + + public async Task UpdateTopicAsync(Guid id, UpdateForumTopicDto input) + { + var topic = await _topicRepository.GetAsync(id); + + topic.Title = input.Title; + topic.Content = input.Content; + topic.IsPinned = input.IsPinned; + topic.IsLocked = input.IsLocked; + topic.IsSolved = input.IsSolved; + + await _topicRepository.UpdateAsync(topic); + return ObjectMapper.Map(topic); + } + + public async Task DeleteTopicAsync(Guid id) + { + var topic = await _topicRepository.GetAsync(id); + + // Delete all posts in this topic + await _postRepository.DeleteAsync(p => p.TopicId == id); + + // Update category counts + var category = await _categoryRepository.GetAsync(topic.CategoryId); + category.TopicCount = Math.Max(0, category.TopicCount - 1); + var postCount = await _postRepository.CountAsync(p => p.TopicId == id); + category.PostCount = Math.Max(0, category.PostCount - postCount); + await _categoryRepository.UpdateAsync(category); + + await _topicRepository.DeleteAsync(id); + } + + // Post management + public async Task> GetPostsAsync(GetPostsInput input) + { + var queryable = await _postRepository.GetQueryableAsync(); + + if (input.TopicId.HasValue) + { + queryable = queryable.Where(p => p.TopicId == input.TopicId.Value); + + // Increment view count + var topic = await _topicRepository.GetAsync(input.TopicId.Value); + } + + if (input.IsAcceptedAnswer.HasValue) + { + queryable = queryable.Where(p => p.IsAcceptedAnswer == input.IsAcceptedAnswer.Value); + } + + if (!string.IsNullOrWhiteSpace(input.Search)) + { + var search = input.Search.ToLower(); + queryable = queryable.Where(p => p.Content.ToLower().Contains(search)); + } + + queryable = queryable.OrderBy(p => p.CreationTime); + + var totalCount = await AsyncExecuter.CountAsync(queryable); + var posts = await AsyncExecuter.ToListAsync( + queryable.Skip(input.SkipCount).Take(input.MaxResultCount) + ); + + return new PagedResultDto( + totalCount, + ObjectMapper.Map, List>(posts) + ); + } + + public async Task GetPostAsync(Guid id) + { + var post = await _postRepository.GetAsync(id); + return ObjectMapper.Map(post); + } + + public async Task CreatePostAsync(CreateForumPostDto input) + { + var post = new ForumPost( + GuidGenerator.Create(), + input.TopicId, + input.Content, + CurrentUser.Id.Value, + CurrentUser.Name, + input.ParentPostId + ); + + await _postRepository.InsertAsync(post, autoSave: true); + + // 🔽 Update topic + var topic = await _topicRepository.GetAsync(input.TopicId); + topic.ReplyCount++; + topic.LastPostId = post.Id; + topic.LastPostDate = post.CreationTime; + topic.LastPostUserId = post.AuthorId; + topic.LastPostUserName = post.AuthorName; + await _topicRepository.UpdateAsync(topic); + + // 🔽 Update category + var category = await _categoryRepository.GetAsync(topic.CategoryId); + category.PostCount++; + category.LastPostId = post.Id; + category.LastPostDate = post.CreationTime; + category.LastPostUserId = post.AuthorId; + category.LastPostUserName = post.AuthorName; + await _categoryRepository.UpdateAsync(category); + + return ObjectMapper.Map(post); + } + + public async Task UpdatePostAsync(Guid id, UpdateForumPostDto input) + { + var post = await _postRepository.GetAsync(id); + + // Check if user can edit this post + if (post.AuthorId != CurrentUser.Id && !await AuthorizationService.IsGrantedAsync("Forum.Posts.Edit")) + { + throw new AbpAuthorizationException(); + } + + post.Content = input.Content; + post.IsAcceptedAnswer = input.IsAcceptedAnswer; + + await _postRepository.UpdateAsync(post); + return ObjectMapper.Map(post); + } + + public async Task DeletePostAsync(Guid id) + { + var post = await _postRepository.GetAsync(id); + var topic = await _topicRepository.GetAsync(post.TopicId); + var category = await _categoryRepository.GetAsync(topic.CategoryId); + + await _postRepository.DeleteAsync(id); + + topic.ReplyCount = Math.Max(0, topic.ReplyCount - 1); + category.PostCount = Math.Max(0, category.PostCount - 1); + + // 🔁 Last post değişti mi kontrol et + var latestPost = await _postRepository + .GetQueryableAsync() + .ContinueWith(q => q.Result + .Where(p => p.TopicId == topic.Id) + .OrderByDescending(p => p.CreationTime) + .FirstOrDefault() + ); + + if (latestPost != null) + { + topic.LastPostId = latestPost.Id; + topic.LastPostDate = latestPost.CreationTime; + topic.LastPostUserId = latestPost.AuthorId; + topic.LastPostUserName = latestPost.AuthorName; + + category.LastPostId = latestPost.Id; + category.LastPostDate = latestPost.CreationTime; + category.LastPostUserId = latestPost.AuthorId; + category.LastPostUserName = latestPost.AuthorName; + } + else + { + // Tüm postlar silindiyse + topic.LastPostId = null; + topic.LastPostDate = null; + topic.LastPostUserId = null; + topic.LastPostUserName = null; + + category.LastPostId = null; + category.LastPostDate = null; + category.LastPostUserId = null; + category.LastPostUserName = null; + } + + await _topicRepository.UpdateAsync(topic); + await _categoryRepository.UpdateAsync(category); + } + + // Like/Unlike topic + public async Task LikeTopicAsync(Guid id) + { + var topic = await _topicRepository.GetAsync(id); + topic.LikeCount++; + await _topicRepository.UpdateAsync(topic); + return ObjectMapper.Map(topic); + } + + public async Task UnlikeTopicAsync(Guid id) + { + var topic = await _topicRepository.GetAsync(id); + topic.LikeCount = Math.Max(0, topic.LikeCount - 1); + await _topicRepository.UpdateAsync(topic); + return ObjectMapper.Map(topic); + } + + // Like/Unlike posts + public async Task LikePostAsync(Guid id) + { + var post = await _postRepository.GetAsync(id); + post.LikeCount++; + await _postRepository.UpdateAsync(post); + + var topic = await _topicRepository.GetAsync(post.TopicId); + var postsInTopic = await _postRepository.GetListAsync(p => p.TopicId == topic.Id); + + topic.LikeCount = postsInTopic.Sum(p => p.LikeCount); + await _topicRepository.UpdateAsync(topic); + + return ObjectMapper.Map(post); + } + + public async Task UnlikePostAsync(Guid id) + { + var post = await _postRepository.GetAsync(id); + post.LikeCount = Math.Max(0, post.LikeCount - 1); + await _postRepository.UpdateAsync(post); + + // 🔽 Topic'in toplam beğeni sayısını güncelle + var topic = await _topicRepository.GetAsync(post.TopicId); + + var postsInTopic = await _postRepository.GetListAsync(p => p.TopicId == topic.Id); + topic.LikeCount = postsInTopic.Sum(p => p.LikeCount); + + await _topicRepository.UpdateAsync(topic); + + return ObjectMapper.Map(post); + } + + // Statistics + public async Task GetForumStatsAsync() + { + var totalCategories = await _categoryRepository.CountAsync(); + var totalTopics = await _topicRepository.CountAsync(); + var totalPosts = await _postRepository.CountAsync(); + var totalUsers = await _identityUserRepository.GetCountAsync(); + + return new ForumStatsDto + { + TotalCategories = totalCategories, + TotalTopics = totalTopics, + TotalPosts = totalPosts, + TotalUsers = totalUsers, + ActiveUsers = totalUsers // This could be calculated based on recent activity + }; + } +} diff --git a/api/src/Kurs.Platform.Application/Forum/ForumAutoMapperProfile.cs b/api/src/Kurs.Platform.Application/Forum/ForumAutoMapperProfile.cs new file mode 100644 index 00000000..4a2a96d3 --- /dev/null +++ b/api/src/Kurs.Platform.Application/Forum/ForumAutoMapperProfile.cs @@ -0,0 +1,15 @@ +using AutoMapper; +using Kurs.Platform.Forum; + +namespace Kurs.Platform; + +public class ForumAutoMapperProfile : Profile +{ + public ForumAutoMapperProfile() + { + // Blog mappings + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + } +} diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/PlatformDataSeeder.cs b/api/src/Kurs.Platform.DbMigrator/Seeds/PlatformDataSeeder.cs index 3f2fa9de..b53a9dec 100644 --- a/api/src/Kurs.Platform.DbMigrator/Seeds/PlatformDataSeeder.cs +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/PlatformDataSeeder.cs @@ -10,6 +10,7 @@ using Kurs.Platform.Blog; using Kurs.Platform.Charts.Dto; using Kurs.Platform.Entities; using Kurs.Platform.Enums; +using Kurs.Platform.Forum; using Kurs.Platform.ListForms; using Kurs.Platform.Seeds; using Kurs.Settings.Entities; @@ -51,6 +52,7 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency private readonly IRepository _contactTitleRepository; private readonly IRepository _blogCategoryRepository; private readonly IRepository _blogPostsRepository; + private readonly IRepository _forumCategoryRepository; public PlatformDataSeeder( IRepository languages, @@ -78,7 +80,8 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency IRepository contactTagRepository, IRepository contactTitleRepository, IRepository blogCategoryRepository, - IRepository blogPostsRepository + IRepository blogPostsRepository, + IRepository forumCategoryRepository ) { _languages = languages; @@ -107,6 +110,7 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency _contactTitleRepository = contactTitleRepository; _blogCategoryRepository = blogCategoryRepository; _blogPostsRepository = blogPostsRepository; + _forumCategoryRepository = forumCategoryRepository; } private static IConfigurationRoot BuildConfiguration() @@ -565,7 +569,7 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency ) { DisplayOrder = item.DisplayOrder, - PostCount = item.PostCount + PostCount = item.PostCount }; await _blogCategoryRepository.InsertAsync(newCategory); @@ -592,5 +596,24 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency )); } } + + foreach (var item in items.ForumCategories) + { + var exists = await _forumCategoryRepository.AnyAsync(x => x.Name == item.Name); + + if (!exists) + { + var newCategory = new ForumCategory( + item.Id, + item.Name, + item.Slug, + item.Description, + item.Icon, + item.DisplayOrder + ); + + await _forumCategoryRepository.InsertAsync(newCategory); + } + } } } diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json b/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json index cea59d64..b34e4733 100644 --- a/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json @@ -524,13 +524,19 @@ }, { "resourceName": "Platform", - "key": "App.Blog", + "key": "App.BlogManagement", "en": "Blog Management", "tr": "Blog Yönetimi" }, { "resourceName": "Platform", "key": "App.Forum", + "en": "Forum", + "tr": "Forum" + }, + { + "resourceName": "Platform", + "key": "App.ForumManagement", "en": "Forum Management", "tr": "Forum Yönetimi" }, @@ -605,7 +611,7 @@ "key": "Cancel", "en": "Cancel", "tr": "İptal" - }, + }, { "resourceName": "Platform", "key": "Delete", @@ -4769,7 +4775,7 @@ "key": "blog.posts.post.create", "tr": "Oluştur", "en": "Create" - }, + }, { "resourceName": "Platform", "key": "blog.posts.categories", @@ -4787,7 +4793,7 @@ "key": "blog.posts.categories.edittitle", "tr": "Kategori Düzenle", "en": "Edit Category" - }, + }, { "resourceName": "Platform", "key": "blog.posts.categories.name", @@ -4811,7 +4817,7 @@ "key": "blog.posts.categories.icon", "tr": "İkon", "en": "Icon" - }, + }, { "resourceName": "Platform", "key": "blog.posts.categories.count", @@ -4823,7 +4829,7 @@ "key": "blog.posts.categories.order", "tr": "Sıralama", "en": "Order" - }, + }, { "resourceName": "Platform", "key": "blog.posts.categories.status", @@ -4841,7 +4847,7 @@ "key": "blog.posts.categories.create", "tr": "Oluştur", "en": "Create" - }, + }, { "resourceName": "Platform", "key": "blog.posts.ai.title", @@ -6224,26 +6230,6 @@ "RequiredPermissionName": "App.Menus", "IsDisabled": false }, - { - "ParentCode": null, - "Code": "App.Administration", - "DisplayName": "App.Administration", - "Order": 400, - "Url": null, - "Icon": "FcOrganization", - "RequiredPermissionName": null, - "IsDisabled": false - }, - { - "ParentCode": "App.Administration", - "Code": "App.Setting", - "DisplayName": "App.Setting", - "Order": 1, - "Url": "/settings", - "Icon": "FcSettings", - "RequiredPermissionName": "App.Setting", - "IsDisabled": false - }, { "ParentCode": "App.Saas", "Code": "App.Listforms", @@ -6346,12 +6332,42 @@ }, { "ParentCode": "App.Saas", - "Code": "App.Blog", - "DisplayName": "App.Blog", + "Code": "AApp.BlogManagement", + "DisplayName": "App.BlogManagement", "Order": 10, - "Url": "/admin/blog", + "Url": "/admin/blogmanagement", "Icon": "FcTemplate", - "RequiredPermissionName": "App.Blog", + "RequiredPermissionName": "App.BlogManagement", + "IsDisabled": false + }, + { + "ParentCode": "App.Saas", + "Code": "App.ForumManagement", + "DisplayName": "App.ForumManagement", + "Order": 11, + "Url": "/admin/forummanagement", + "Icon": "FcReading", + "RequiredPermissionName": "App.ForumManagement", + "IsDisabled": false + }, + { + "ParentCode": null, + "Code": "App.Administration", + "DisplayName": "App.Administration", + "Order": 400, + "Url": null, + "Icon": "FcOrganization", + "RequiredPermissionName": null, + "IsDisabled": false + }, + { + "ParentCode": "App.Administration", + "Code": "App.Setting", + "DisplayName": "App.Setting", + "Order": 1, + "Url": "/settings", + "Icon": "FcSettings", + "RequiredPermissionName": "App.Setting", "IsDisabled": false }, { @@ -6453,6 +6469,16 @@ "Icon": "FcMultipleInputs", "RequiredPermissionName": "App.AuditLogs", "IsDisabled": false + }, + { + "ParentCode": "App.Administration", + "Code": "App.Forum", + "DisplayName": "App.Forum", + "Order": 4, + "Url": "/admin/forum", + "Icon": "FcLink", + "RequiredPermissionName": "App.ForumManagement.Publish", + "IsDisabled": false } ], "PermissionGroupDefinitionRecords": [ @@ -6513,8 +6539,12 @@ "DisplayName": "App.AuditLogs" }, { - "Name": "App.Blog", - "DisplayName": "App.Blog" + "Name": "App.BlogManagement", + "DisplayName": "App.BlogManagement" + }, + { + "Name": "App.ForumManagement", + "DisplayName": "App.ForumManagement" } ], "PermissionDefinitionRecords": [ @@ -6711,13 +6741,21 @@ "MultiTenancySide": 2 }, { - "GroupName": "App.Blog", - "Name": "App.Blog", + "GroupName": "App.BlogManagement", + "Name": "App.BlogManagement", "ParentName": null, - "DisplayName": "App.Blog", + "DisplayName": "App.BlogManagement", "IsEnabled": true, "MultiTenancySide": 2 }, + { + "GroupName": "App.ForumManagement", + "Name": "App.ForumManagement", + "ParentName": null, + "DisplayName": "App.ForumManagement", + "IsEnabled": true, + "MultiTenancySide": 3 + }, { "GroupName": "App.Setting", "Name": "Abp.Account", @@ -7719,41 +7757,81 @@ "MultiTenancySide": 3 }, { - "GroupName": "App.Blog", - "Name": "App.Blog.Create", - "ParentName": "App.Blog", + "GroupName": "App.BlogManagement", + "Name": "App.BlogManagement.Create", + "ParentName": "App.BlogManagement", "DisplayName": "Create", "IsEnabled": true, "MultiTenancySide": 2 }, { - "GroupName": "App.Blog", - "Name": "App.Blog.Delete", - "ParentName": "App.Blog", + "GroupName": "App.BlogManagement", + "Name": "App.BlogManagement.Delete", + "ParentName": "App.BlogManagement", "DisplayName": "Delete", "IsEnabled": true, "MultiTenancySide": 2 }, { - "GroupName": "App.Blog", - "Name": "App.Blog.Export", - "ParentName": "App.Blog", + "GroupName": "App.BlogManagement", + "Name": "App.BlogManagement.Export", + "ParentName": "App.BlogManagement", "DisplayName": "Export", "IsEnabled": true, "MultiTenancySide": 2 }, { - "GroupName": "App.Blog", - "Name": "App.Blog.Publish", - "ParentName": "App.Blog", + "GroupName": "App.BlogManagement", + "Name": "App.BlogManagement.Publish", + "ParentName": "App.BlogManagement", "DisplayName": "Publish", "IsEnabled": true, "MultiTenancySide": 2 }, { - "GroupName": "App.Blog", - "Name": "App.Blog.Update", - "ParentName": "App.Blog", + "GroupName": "App.BlogManagement", + "Name": "App.BlogManagement.Update", + "ParentName": "App.BlogManagement", + "DisplayName": "Update", + "IsEnabled": true, + "MultiTenancySide": 2 + }, + { + "GroupName": "App.ForumManagement", + "Name": "App.ForumManagement.Publish", + "ParentName": "App.ForumManagement", + "DisplayName": "Publish", + "IsEnabled": true, + "MultiTenancySide": 3 + }, + { + "GroupName": "App.ForumManagement", + "Name": "App.ForumManagement.Create", + "ParentName": "App.ForumManagement", + "DisplayName": "Create", + "IsEnabled": true, + "MultiTenancySide": 2 + }, + { + "GroupName": "App.ForumManagement", + "Name": "App.ForumManagement.Delete", + "ParentName": "App.ForumManagement", + "DisplayName": "Delete", + "IsEnabled": true, + "MultiTenancySide": 2 + }, + { + "GroupName": "App.ForumManagement", + "Name": "App.ForumManagement.Export", + "ParentName": "App.ForumManagement", + "DisplayName": "Export", + "IsEnabled": true, + "MultiTenancySide": 2 + }, + { + "GroupName": "App.ForumManagement", + "Name": "App.ForumManagement.Update", + "ParentName": "App.ForumManagement", "DisplayName": "Update", "IsEnabled": true, "MultiTenancySide": 2 @@ -20284,5 +20362,43 @@ "CategoryId": "dbc8578c-1a99-594a-8997-bddd0eac8571", "AuthorId": "727ec3f0-75dd-54e2-8ae6-13d49727ff58" } + ], + "ForumCategories": [ + { + "Id": "1a79a36e-e062-4335-9ddf-0557c60f3ea9", + "Name": "Genel Tartışma", + "Slug": "genel-tartisma", + "Description": "Her türlü konunun tartışılabileceği genel forum alanı", + "Icon": "💬", + "DisplayOrder": 1, + "IsActive": true + }, + { + "Id": "e7d6f581-60ba-44d4-be37-c5d13e5c2fda", + "Name": "Teknik Destek", + "Slug": "teknik-destek", + "Description": "Teknik sorunlar ve çözümler için destek forumu", + "Icon": "🔧", + "DisplayOrder": 2, + "IsActive": true + }, + { + "Id": "54ac1095-0a95-467e-9f86-01efa8af136b", + "Name": "Öneriler", + "Slug": "oneriler", + "Description": "Platform geliştirmeleri için öneri ve istekler", + "Icon": "💡", + "DisplayOrder": 3, + "IsActive": true + }, + { + "Id": "3dfbb220-9a2d-49e4-835a-213f47c60939", + "Name": "Duyurular", + "Slug": "duyurular", + "Description": "Platform duyuruları ve güncellemeler", + "Icon": "📢", + "DisplayOrder": 4, + "IsActive": true + } ] } diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/SeederDto.cs b/api/src/Kurs.Platform.DbMigrator/Seeds/SeederDto.cs index 42217b36..5c3070c6 100644 --- a/api/src/Kurs.Platform.DbMigrator/Seeds/SeederDto.cs +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/SeederDto.cs @@ -35,6 +35,7 @@ public class SeederDto public List ContactTitles { get; set; } public List BlogCategories { get; set; } public List BlogPosts { get; set; } + public List ForumCategories { get; set; } } public class ChartsSeedDto @@ -212,14 +213,25 @@ public class BlogCategorySeedDto public class BlogPostSeedDto { - public Guid Id { get; set; } - public string Title { get; set; } - public string Slug { get; set; } - public string ContentTr { get; set; } - public string ContentEn { get; set; } - public string ReadTime { get; set; } - public string Summary { get; set; } - public string CoverImage { get; set; } + public Guid Id { get; set; } + public string Title { get; set; } + public string Slug { get; set; } + public string ContentTr { get; set; } + public string ContentEn { get; set; } + public string ReadTime { get; set; } + public string Summary { get; set; } + public string CoverImage { get; set; } public Guid CategoryId { get; set; } - public Guid AuthorId { get; set; } + public Guid AuthorId { get; set; } +} + +public class ForumCategorySeedDto +{ + public Guid Id { get; set; } + public string Name { get; set; } + public string Slug { get; set; } + public string Description { get; set; } + public string Icon { get; set; } + public int DisplayOrder { get; set; } + public bool IsActive { get; set; } } \ No newline at end of file diff --git a/api/src/Kurs.Platform.Domain/Entities/Forum.cs b/api/src/Kurs.Platform.Domain/Entities/Forum.cs new file mode 100644 index 00000000..82130865 --- /dev/null +++ b/api/src/Kurs.Platform.Domain/Entities/Forum.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Domain.Entities.Auditing; + +namespace Kurs.Platform.Forum; + +public class ForumCategory : FullAuditedEntity +{ + public string Name { get; set; } + public string Slug { get; set; } + public string Description { get; set; } + public string Icon { get; set; } + public int DisplayOrder { get; set; } + public bool IsActive { get; set; } + public bool IsLocked { get; set; } + public int TopicCount { get; set; } + public int PostCount { get; set; } + public Guid? LastPostId { get; set; } + public DateTime? LastPostDate { get; set; } + public Guid? LastPostUserId { get; set; } + public string LastPostUserName { get; set; } + + public ICollection Topics { get; set; } + + protected ForumCategory() { } + + public ForumCategory(Guid id, string name, string slug, string description, string icon, int displayOrder) : base(id) + { + Name = name; + Slug = slug; + Description = description; + Icon = icon; + DisplayOrder = displayOrder; + IsActive = true; + IsLocked = false; + TopicCount = 0; + PostCount = 0; + Topics = []; + } +} + +public class ForumTopic : FullAuditedEntity +{ + public string Title { get; set; } + public string Content { get; set; } + public Guid CategoryId { get; set; } + public Guid AuthorId { get; set; } + public string AuthorName { get; set; } + public int ViewCount { get; set; } + public int ReplyCount { get; set; } + public int LikeCount { get; set; } + public bool IsPinned { get; set; } + public bool IsLocked { get; set; } + public bool IsSolved { get; set; } + public Guid? LastPostId { get; set; } + public DateTime? LastPostDate { get; set; } + public Guid? LastPostUserId { get; set; } + public string LastPostUserName { get; set; } + + public ForumCategory Category { get; set; } + public ICollection Posts { get; set; } + + protected ForumTopic() { } + + public ForumTopic(Guid id, string title, string content, Guid categoryId, Guid authorId, string authorName) : base(id) + { + Title = title; + Content = content; + CategoryId = categoryId; + AuthorId = authorId; + AuthorName = authorName; + ViewCount = 0; + ReplyCount = 0; + LikeCount = 0; + IsPinned = false; + IsLocked = false; + IsSolved = false; + Posts = []; + } +} + +public class ForumPost : FullAuditedEntity +{ + public Guid TopicId { get; set; } + public string Content { get; set; } + public Guid AuthorId { get; set; } + public string AuthorName { get; set; } + public int LikeCount { get; set; } + public bool IsAcceptedAnswer { get; set; } + public Guid? ParentPostId { get; set; } + + public ForumTopic Topic { get; set; } + public ForumPost ParentPost { get; set; } + public ICollection Replies { get; set; } + + protected ForumPost() { } + + public ForumPost(Guid id, Guid topicId, string content, Guid authorId, string authorName, Guid? parentPostId = null) : base(id) + { + TopicId = topicId; + Content = content; + AuthorId = authorId; + AuthorName = authorName; + ParentPostId = parentPostId; + LikeCount = 0; + IsAcceptedAnswer = false; + Replies = []; + } +} \ No newline at end of file diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs b/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs index 513813ec..34d2013c 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs @@ -19,6 +19,7 @@ using Volo.Abp.TenantManagement; using Volo.Abp.TenantManagement.EntityFrameworkCore; using Kurs.Notifications.EntityFrameworkCore; using static Kurs.Settings.SettingsConsts; +using Kurs.Platform.Forum; namespace Kurs.Platform.EntityFrameworkCore; @@ -60,6 +61,11 @@ public class PlatformDbContext : public DbSet BlogPosts { get; set; } public DbSet BlogCategories { get; set; } + // Forum Entities + public DbSet ForumCategories { get; set; } + public DbSet ForumTopics { get; set; } + public DbSet ForumPosts { get; set; } + #region Entities from the modules /* Notice: We only implemented IIdentityDbContext and ITenantManagementDbContext @@ -428,5 +434,64 @@ public class PlatformDbContext : .HasForeignKey(x => x.CategoryId) .OnDelete(DeleteBehavior.Restrict); }); + + // Forum Entity Configurations + // ForumCategory + builder.Entity(b => + { + b.ToTable(PlatformConsts.DbTablePrefix + "ForumCategories", PlatformConsts.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.Name).IsRequired().HasMaxLength(128); + b.Property(x => x.Description).HasMaxLength(512); + b.Property(x => x.Icon).HasMaxLength(64); + + b.HasIndex(x => x.DisplayOrder); + b.HasMany(x => x.Topics) + .WithOne(x => x.Category) + .HasForeignKey(x => x.CategoryId) + .OnDelete(DeleteBehavior.Restrict); + }); + + // ForumTopic + builder.Entity(b => + { + b.ToTable(PlatformConsts.DbTablePrefix + "ForumTopics", PlatformConsts.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.Title).IsRequired().HasMaxLength(256); + b.Property(x => x.Content).IsRequired(); + + b.HasIndex(x => x.CategoryId); + b.HasIndex(x => x.IsPinned); + b.HasIndex(x => x.LastPostDate); + + b.HasMany(x => x.Posts) + .WithOne(x => x.Topic) + .HasForeignKey(x => x.TopicId) + .OnDelete(DeleteBehavior.Cascade); + }); + + // ForumPost + builder.Entity(b => + { + b.ToTable(PlatformConsts.DbTablePrefix + "ForumPosts", PlatformConsts.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.Content).IsRequired(); + + b.HasIndex(x => x.TopicId); + + b.HasOne(x => x.Topic) + .WithMany(x => x.Posts) + .HasForeignKey(x => x.TopicId) + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne(x => x.ParentPost) + .WithMany(x => x.Replies) + .HasForeignKey(x => x.ParentPostId) + .OnDelete(DeleteBehavior.Restrict); + }); + } } diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20250623140407_AddForum.Designer.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20250623140407_AddForum.Designer.cs new file mode 100644 index 00000000..cd64df84 --- /dev/null +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20250623140407_AddForum.Designer.cs @@ -0,0 +1,5109 @@ +// +using System; +using Kurs.Platform.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace Kurs.Platform.Migrations +{ + [DbContext(typeof(PlatformDbContext))] + [Migration("20250623140407_AddForum")] + partial class AddForum + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("ContactTag", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("PContactTag", (string)null); + }); + + modelBuilder.Entity("ContactTitle", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Abbreviation") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("PContactTitle", (string)null); + }); + + modelBuilder.Entity("Kurs.Languages.Entities.Language", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CultureName") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MultipleCultures") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoLetterISOLanguageName") + .HasColumnType("nvarchar(max)"); + + b.Property("UiCultureName") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.HasKey("Id"); + + b.ToTable("PLanguage", (string)null); + }); + + modelBuilder.Entity("Kurs.Languages.Entities.LanguageKey", b => + { + b.Property("ResourceName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Key") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.HasKey("ResourceName", "Key"); + + b.ToTable("PLanguageKey", (string)null); + }); + + modelBuilder.Entity("Kurs.Languages.Entities.LanguageText", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CultureName") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("Key") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ResourceName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.HasKey("Id"); + + b.HasIndex("ResourceName", "Key"); + + b.ToTable("PLanguageText", (string)null); + }); + + modelBuilder.Entity("Kurs.MailQueue.Domain.Entities.BackgroundWorker_MailQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Attachment") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("AttachmentParameter") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("AwsMessageId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("From") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MailParameter") + .HasMaxLength(8000) + .HasColumnType("nvarchar(max)"); + + b.Property("RelatedRecordId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("SendStatus") + .HasColumnType("bit"); + + b.Property("SendTime") + .HasColumnType("datetime2"); + + b.Property("Table") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TableParameter") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("TemplateId") + .HasColumnType("uniqueidentifier"); + + b.Property("To") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.ToTable("PBackgroundWorker_MailQueue", (string)null); + }); + + modelBuilder.Entity("Kurs.MailQueue.Domain.Entities.BackgroundWorker_MailQueueEvents", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AwsMessageId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Event") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("EventDate") + .HasColumnType("datetime2"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MailAddress") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ResponseDescription") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("PBackgroundWorker_MailQueueEvents", (string)null); + }); + + modelBuilder.Entity("Kurs.MailQueue.Domain.Entities.BackgroundWorker_MailQueueTableFormat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Caption") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ColumnName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Css") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("DataFormat") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("DataType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("FooterCss") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("HeaderCss") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("IsHidden") + .HasColumnType("bit"); + + b.Property("IsProtected") + .HasColumnType("bit"); + + b.Property("Order") + .HasColumnType("smallint"); + + b.Property("SubTotal") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("TableName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Width") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex(new[] { "TableName", "Order" }, "IX_MailQueueTableFormat") + .IsUnique(); + + b.ToTable("PBackgroundWorker_MailQueueTableFormat", (string)null); + }); + + modelBuilder.Entity("Kurs.Notifications.Entities.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Identifier") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsRead") + .HasColumnType("bit"); + + b.Property("IsSent") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Message") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("NotificationChannel") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("NotificationRuleId") + .HasColumnType("uniqueidentifier"); + + b.Property("NotificationType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ReadTime") + .HasColumnType("datetime2"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("PNotification", (string)null); + }); + + modelBuilder.Entity("Kurs.Notifications.Entities.NotificationRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Channel") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsCustomized") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsFixed") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("NotificationType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("RecipientId") + .HasColumnType("nvarchar(max)"); + + b.Property("RecipientType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("PNotificationRule", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Blog.BlogCategory", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("DisplayOrder") + .HasColumnType("int"); + + b.Property("Icon") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PostCount") + .HasColumnType("int"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Slug"); + + b.ToTable("PBlogCategories", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Blog.BlogPost", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorId") + .HasColumnType("uniqueidentifier"); + + b.Property("CategoryId") + .HasColumnType("uniqueidentifier"); + + b.Property("CommentCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContentEn") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ContentTr") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CoverImage") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsPublished") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LikeCount") + .HasColumnType("int"); + + b.Property("PublishedAt") + .HasColumnType("datetime2"); + + b.Property("ReadTime") + .HasColumnType("nvarchar(max)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Summary") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ViewCount") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("IsPublished"); + + b.HasIndex("PublishedAt"); + + b.HasIndex("Slug"); + + b.ToTable("PBlogPosts", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.AiBot", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("BotName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("PAiBot", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.BackgroundWorker", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AfterSp") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("BeforeSp") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("Cron") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("DataSourceCode") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Options") + .HasColumnType("nvarchar(max)"); + + b.Property("WorkerType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("PBackgroundWorker", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.Bank", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AddressLine1") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("AddressLine2") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("City") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Country") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("District") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Email") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("IdentifierCode") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Phone") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("PostalCode") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.HasKey("Id"); + + b.ToTable("Banks"); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.BankAccount", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AccountNumber") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("AccountOwner") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("BankId") + .HasColumnType("uniqueidentifier"); + + b.Property("CanTransferMoney") + .HasColumnType("bit"); + + b.Property("Company") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CurrencyId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.HasKey("Id"); + + b.HasIndex("BankId"); + + b.HasIndex("CurrencyId"); + + b.ToTable("BankAccounts"); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.Branch", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Address") + .HasColumnType("nvarchar(max)"); + + b.Property("Address2") + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .HasColumnType("nvarchar(max)"); + + b.Property("Code") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("District") + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("Fax") + .HasColumnType("bigint"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Mobile") + .HasColumnType("bigint"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Phone") + .HasColumnType("bigint"); + + b.Property("PostalCode") + .HasColumnType("nvarchar(max)"); + + b.Property("TaxOffice") + .HasColumnType("nvarchar(max)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("VknTckn") + .HasColumnType("bigint"); + + b.Property("Website") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("PBranch", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.BranchUsers", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("BranchId") + .HasColumnType("uniqueidentifier"); + + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "BranchId"); + + b.ToTable("PBranchUsers", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.Chart", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AdaptiveLayoutJson") + .HasColumnType("nvarchar(max)"); + + b.Property("AnimationJson") + .HasColumnType("nvarchar(max)"); + + b.Property("AnnotationsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("ArgumentAxisJson") + .HasColumnType("nvarchar(max)"); + + b.Property("ChartCode") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CommonAnnotationsSettingsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("CommonAxisSettingsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("CommonJson") + .HasColumnType("nvarchar(max)"); + + b.Property("CommonPaneSettingsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("CommonSeriesSettingsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CrosshairJson") + .HasColumnType("nvarchar(max)"); + + b.Property("CultureName") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(max)") + .HasDefaultValue("en"); + + b.Property("DataSourceCode") + .HasColumnType("nvarchar(max)"); + + b.Property("DataSourceJson") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("ExportJson") + .HasColumnType("nvarchar(max)"); + + b.Property("IsBranch") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsOrganizationUnit") + .HasColumnType("bit"); + + b.Property("IsTenant") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LegendJson") + .HasColumnType("nvarchar(max)"); + + b.Property("MarginJson") + .HasColumnType("nvarchar(max)"); + + b.Property("PanesJson") + .HasColumnType("nvarchar(max)"); + + b.Property("PermissionJson") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(max)"); + + b.Property("ScrollBarJson") + .HasColumnType("nvarchar(max)"); + + b.Property("SeriesJson") + .HasColumnType("nvarchar(max)"); + + b.Property("SizeJson") + .HasColumnType("nvarchar(max)"); + + b.Property("TitleJson") + .HasColumnType("nvarchar(max)"); + + b.Property("TooltipJson") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("nvarchar(max)"); + + b.Property("ValueAxisJson") + .HasColumnType("nvarchar(max)"); + + b.Property("ZoomAndPanJson") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("PChart", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.Country", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("nvarchar(8)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CurrencyCode") + .HasMaxLength(8) + .HasColumnType("nvarchar(8)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("GroupName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PhoneCode") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("StateRequired") + .HasColumnType("bit"); + + b.Property("TaxLabel") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ZipRequired") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("GroupName"); + + b.ToTable("PCountry", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.CountryGroup", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("PCountryGroup", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.Currency", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("nvarchar(8)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LastUpdated") + .HasColumnType("datetime2"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Rate") + .HasColumnType("decimal(18,6)"); + + b.Property("Symbol") + .HasMaxLength(8) + .HasColumnType("nvarchar(8)"); + + b.HasKey("Id"); + + b.ToTable("PCurrency", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.DataSource", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ConnectionString") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataSourceType") + .HasColumnType("int"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.HasKey("Id"); + + b.ToTable("PDataSource", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.GlobalSearch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Group") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("System") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Term") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.Property("Weight") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("PGlobalSearch", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.IpRestriction", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IP") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ResourceId") + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("ResourceType") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("PIpRestriction", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.ListForm", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ColumnOptionJson") + .HasColumnType("nvarchar(max)"); + + b.Property("CommandColumnJson") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CultureName") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomJsSourcesJson") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomStyleSourcesJson") + .HasColumnType("nvarchar(max)"); + + b.Property("DataSourceCode") + .HasColumnType("nvarchar(max)"); + + b.Property("DefaultFilter") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleteCommand") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleteFieldsDefaultValueJson") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleteServiceAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("EditingFormJson") + .HasColumnType("nvarchar(max)"); + + b.Property("EditingOptionJson") + .HasColumnType("nvarchar(max)"); + + b.Property("FilterPanelJson") + .HasColumnType("nvarchar(max)"); + + b.Property("FilterRowJson") + .HasColumnType("nvarchar(max)"); + + b.Property("FormFieldsDefaultValueJson") + .HasColumnType("nvarchar(max)"); + + b.Property("GroupPanelJson") + .HasColumnType("nvarchar(max)"); + + b.Property("HeaderFilterJson") + .HasColumnType("nvarchar(max)"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("InsertCommand") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertFieldsDefaultValueJson") + .HasColumnType("nvarchar(max)"); + + b.Property("InsertServiceAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("IsBranch") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsOrganizationUnit") + .HasColumnType("bit"); + + b.Property("IsSubForm") + .HasColumnType("bit"); + + b.Property("IsTenant") + .HasColumnType("bit"); + + b.Property("KeyFieldDbSourceType") + .HasColumnType("int"); + + b.Property("KeyFieldName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ListFormCode") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ListFormType") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("PageSize") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(10); + + b.Property("PagerOptionJson") + .HasColumnType("nvarchar(max)"); + + b.Property("PermissionJson") + .HasColumnType("nvarchar(max)"); + + b.Property("SearchPanelJson") + .HasColumnType("nvarchar(max)"); + + b.Property("SelectCommand") + .HasColumnType("nvarchar(max)"); + + b.Property("SelectCommandType") + .HasColumnType("int"); + + b.Property("SelectFieldsDefaultValueJson") + .HasColumnType("nvarchar(max)"); + + b.Property("SelectionJson") + .HasColumnType("nvarchar(max)"); + + b.Property("SortMode") + .HasColumnType("nvarchar(max)"); + + b.Property("StateStoringJson") + .HasColumnType("nvarchar(max)"); + + b.Property("SubFormsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("TableName") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateCommand") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateFieldsDefaultValueJson") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateServiceAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Width") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("PListForm", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.ListFormCustomization", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CustomizationData") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomizationType") + .HasColumnType("int"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("FilterName") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ListFormCode") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ListFormCode"); + + b.ToTable("PListFormCustomization", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.ListFormField", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Alignment") + .HasColumnType("nvarchar(max)"); + + b.Property("AllowSearch") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("BandName") + .HasColumnType("nvarchar(max)"); + + b.Property("CaptionName") + .HasColumnType("nvarchar(max)"); + + b.Property("ColumnCssClass") + .HasColumnType("nvarchar(max)"); + + b.Property("ColumnCssValue") + .HasColumnType("nvarchar(max)"); + + b.Property("ColumnCustomizationJson") + .HasColumnType("nvarchar(max)"); + + b.Property("ColumnFilterJson") + .HasColumnType("nvarchar(max)"); + + b.Property("ColumnHeaderJson") + .HasColumnType("nvarchar(max)"); + + b.Property("ColumnStylingJson") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CultureName") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("EditingJson") + .HasColumnType("nvarchar(max)"); + + b.Property("EditorOptions") + .HasColumnType("nvarchar(max)"); + + b.Property("FieldName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Format") + .HasColumnType("nvarchar(max)"); + + b.Property("GroupSummaryJson") + .HasColumnType("nvarchar(max)"); + + b.Property("GroupingJson") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("JoinTableJson") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ListFormCode") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ListOrderNo") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(30); + + b.Property("LookupJson") + .HasColumnType("nvarchar(max)"); + + b.Property("PermissionJson") + .HasColumnType("nvarchar(max)"); + + b.Property("PivotSettingsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(max)"); + + b.Property("SortDirection") + .HasColumnType("nvarchar(max)"); + + b.Property("SortIndex") + .HasColumnType("int"); + + b.Property("SourceDbType") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(16); + + b.Property("TotalSummaryJson") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("nvarchar(max)"); + + b.Property("ValidationRuleJson") + .HasColumnType("nvarchar(max)"); + + b.Property("Visible") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("Width") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(100); + + b.HasKey("Id"); + + b.HasIndex("ListFormCode"); + + b.ToTable("PListFormField", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.Menu", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("CssClass") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CultureName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ElementId") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Icon") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsDisabled") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("ParentCode") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("RequiredPermissionName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("RoleId") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Target") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Url") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("UserId") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.ToTable("PMenu", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.PublicApi", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataSourceCode") + .HasColumnType("nvarchar(max)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Method") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("ParametersJson") + .HasColumnType("nvarchar(max)"); + + b.Property("PermissionsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("Sql") + .HasColumnType("nvarchar(max)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("PPublicApi", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.Sector", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("FullName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("PSector", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.State", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("CountryCode") + .HasMaxLength(8) + .HasColumnType("nvarchar(8)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("CountryCode", "Code") + .IsUnique() + .HasFilter("[CountryCode] IS NOT NULL AND [Code] IS NOT NULL"); + + b.ToTable("PState", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.Uom", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CategoryName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Ratio") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Rounding") + .HasPrecision(18, 6) + .HasColumnType("decimal(18,6)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.HasKey("Id"); + + b.HasIndex("CategoryName"); + + b.ToTable("PUom", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.UomCategory", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("PUomCategory", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Forum.ForumCategory", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("DisplayOrder") + .HasColumnType("int"); + + b.Property("Icon") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsLocked") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LastPostDate") + .HasColumnType("datetime2"); + + b.Property("LastPostId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastPostUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastPostUserName") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PostCount") + .HasColumnType("int"); + + b.Property("Slug") + .HasColumnType("nvarchar(max)"); + + b.Property("TopicCount") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DisplayOrder"); + + b.ToTable("PForumCategories", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Forum.ForumPost", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorId") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorName") + .HasColumnType("nvarchar(max)"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsAcceptedAnswer") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LikeCount") + .HasColumnType("int"); + + b.Property("ParentPostId") + .HasColumnType("uniqueidentifier"); + + b.Property("TopicId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("ParentPostId"); + + b.HasIndex("TopicId"); + + b.ToTable("PForumPosts", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Forum.ForumTopic", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorId") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorName") + .HasColumnType("nvarchar(max)"); + + b.Property("CategoryId") + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsLocked") + .HasColumnType("bit"); + + b.Property("IsPinned") + .HasColumnType("bit"); + + b.Property("IsSolved") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LastPostDate") + .HasColumnType("datetime2"); + + b.Property("LastPostId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastPostUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastPostUserName") + .HasColumnType("nvarchar(max)"); + + b.Property("LikeCount") + .HasColumnType("int"); + + b.Property("ReplyCount") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ViewCount") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("IsPinned"); + + b.HasIndex("LastPostDate"); + + b.ToTable("PForumTopics", (string)null); + }); + + modelBuilder.Entity("Kurs.Settings.Entities.SettingDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DataType") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("DefaultValue") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DescriptionKey") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsEncrypted") + .HasColumnType("bit"); + + b.Property("IsInherited") + .HasColumnType("bit"); + + b.Property("IsVisibleToClients") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("MainGroupKey") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("NameKey") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("Providers") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("RequiredPermissionName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("SelectOptions") + .HasColumnType("nvarchar(max)"); + + b.Property("SubGroupKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("PSettingDefinition", (string)null); + }); + + modelBuilder.Entity("Skill", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("SkillTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("TypeName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("SkillTypeId"); + + b.ToTable("PSkill", (string)null); + }); + + modelBuilder.Entity("SkillLevel", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDefault") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Progress") + .HasColumnType("int"); + + b.Property("SkillTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("TypeName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("SkillTypeId"); + + b.ToTable("PSkillLevel", (string)null); + }); + + modelBuilder.Entity("SkillType", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("PSkillType", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)") + .HasColumnName("ApplicationName"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("BrowserInfo"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ClientId"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ClientIpAddress"); + + b.Property("ClientName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("ClientName"); + + b.Property("Comments") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Comments"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("CorrelationId"); + + b.Property("Exceptions") + .HasColumnType("nvarchar(max)"); + + b.Property("ExecutionDuration") + .HasColumnType("int") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("HttpMethod"); + + b.Property("HttpStatusCode") + .HasColumnType("int") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorTenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("ImpersonatorTenantName"); + + b.Property("ImpersonatorUserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("ImpersonatorUserId"); + + b.Property("ImpersonatorUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("ImpersonatorUserName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("TenantName"); + + b.Property("Url") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Url"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AuditLogId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnType("int") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("datetime2") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("MethodName"); + + b.Property("Parameters") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)") + .HasColumnName("Parameters"); + + b.Property("ServiceName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("ServiceName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AuditLogId") + .HasColumnType("uniqueidentifier") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnType("datetime2") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnType("tinyint") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("EntityId"); + + b.Property("EntityTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("EntityTypeFullName"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("EntityChangeId") + .HasColumnType("uniqueidentifier"); + + b.Property("NewValue") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("NewValue"); + + b.Property("OriginalValue") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)") + .HasColumnName("OriginalValue"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("PropertyName"); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("PropertyTypeFullName"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsAbandoned") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("JobArgs") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("nvarchar(max)"); + + b.Property("JobName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("LastTryTime") + .HasColumnType("datetime2"); + + b.Property("NextTryTime") + .HasColumnType("datetime2"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint") + .HasDefaultValue((byte)15); + + b.Property("TryCount") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((short)0); + + b.HasKey("Id"); + + b.HasIndex("IsAbandoned", "NextTryTime"); + + b.ToTable("AbpBackgroundJobs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureDefinitionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AllowedProviders") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("DefaultValue") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("IsAvailableToHost") + .HasColumnType("bit"); + + b.Property("IsVisibleToClients") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ParentName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ValueType") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpFeatures", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureGroupDefinitionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpFeatureGroups", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[ProviderName] IS NOT NULL AND [ProviderKey] IS NOT NULL"); + + b.ToTable("AbpFeatureValues", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Regex") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("RegexDescription") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Required") + .HasColumnType("bit"); + + b.Property("ValueType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("TargetTenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("TargetUserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId") + .IsUnique() + .HasFilter("[SourceTenantId] IS NOT NULL AND [TargetTenantId] IS NOT NULL"); + + b.ToTable("AbpLinkUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnType("bit") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnType("bit") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnType("bit") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Action") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreationTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Identity") + .HasMaxLength(96) + .HasColumnType("nvarchar(96)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Action"); + + b.HasIndex("TenantId", "ApplicationName"); + + b.HasIndex("TenantId", "Identity"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSecurityLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySession", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Device") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("DeviceInfo") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IpAddresses") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.Property("LastAccessed") + .HasColumnType("datetime2"); + + b.Property("SessionId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("SignedIn") + .HasColumnType("datetime2"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Device"); + + b.HasIndex("SessionId"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSessions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("bit") + .HasColumnName("IsActive"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsExternal") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsExternal"); + + b.Property("IsVerified") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LastPasswordChangeTime") + .HasColumnType("datetimeoffset"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("LockoutEnabled"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("LoginEndDate") + .HasColumnType("datetime2"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Name"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("NormalizedEmail"); + + b.Property("NormalizedUserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("NormalizedUserName"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("PasswordHash"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("nvarchar(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("RocketUsername") + .HasColumnType("nvarchar(max)"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("SecurityStamp"); + + b.Property("ShouldChangePasswordOnNextLogin") + .HasColumnType("bit"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("TwoFactorEnabled"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserDelegation", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("EndTime") + .HasColumnType("datetime2"); + + b.Property("SourceUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.Property("TargetUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AbpUserDelegations", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196) + .HasColumnType("nvarchar(196)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(95) + .HasColumnType("nvarchar(95)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)") + .HasColumnName("DisplayName"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ClientSecret") + .HasColumnType("nvarchar(max)"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ClientUri") + .HasColumnType("nvarchar(max)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayNames") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("JsonWebKeySet") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasColumnType("nvarchar(max)"); + + b.Property("Permissions") + .HasColumnType("nvarchar(max)"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedirectUris") + .HasColumnType("nvarchar(max)"); + + b.Property("Requirements") + .HasColumnType("nvarchar(max)"); + + b.Property("Settings") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("OpenIddictApplications", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Scopes") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Scopes.OpenIddictScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Descriptions") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayNames") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Resources") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("OpenIddictScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Tokens.OpenIddictToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApplicationId") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExpirationDate") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Payload") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedemptionDate") + .HasColumnType("datetime2"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionDefinitionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("GroupName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("MultiTenancySide") + .HasColumnType("tinyint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ParentName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Providers") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("StateCheckers") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("GroupName"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpPermissions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("TenantId") + .HasColumnType("uniqueidentifier") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[TenantId] IS NOT NULL"); + + b.ToTable("AbpPermissionGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGroupDefinitionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpPermissionGroups", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique() + .HasFilter("[ProviderName] IS NOT NULL AND [ProviderKey] IS NOT NULL"); + + b.ToTable("AbpSettings", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.SettingDefinitionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("DefaultValue") + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExtraProperties") + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("IsEncrypted") + .HasColumnType("bit"); + + b.Property("IsInherited") + .HasColumnType("bit"); + + b.Property("IsVisibleToClients") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Providers") + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpSettingDefinitions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Address") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Address2") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("City") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("District") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityVersion") + .HasColumnType("int"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ExtraProperties"); + + b.Property("Fax") + .HasColumnType("bigint"); + + b.Property("InstitutionName") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Mobile") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Phone") + .HasColumnType("bigint"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("TaxOffice") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("VknTckn") + .HasColumnType("bigint"); + + b.Property("Website") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpTenants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.Property("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings", (string)null); + }); + + modelBuilder.Entity("Kurs.Languages.Entities.LanguageText", b => + { + b.HasOne("Kurs.Languages.Entities.LanguageKey", null) + .WithMany("Texts") + .HasForeignKey("ResourceName", "Key") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("Kurs.Platform.Blog.BlogPost", b => + { + b.HasOne("Kurs.Platform.Blog.BlogCategory", "Category") + .WithMany("Posts") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.BankAccount", b => + { + b.HasOne("Kurs.Platform.Entities.Bank", "Bank") + .WithMany() + .HasForeignKey("BankId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kurs.Platform.Entities.Currency", "Currency") + .WithMany() + .HasForeignKey("CurrencyId"); + + b.Navigation("Bank"); + + b.Navigation("Currency"); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.Country", b => + { + b.HasOne("Kurs.Platform.Entities.CountryGroup", null) + .WithMany() + .HasForeignKey("GroupName") + .HasPrincipalKey("Name") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.ListFormCustomization", b => + { + b.HasOne("Kurs.Platform.Entities.ListForm", null) + .WithMany() + .HasForeignKey("ListFormCode") + .HasPrincipalKey("ListFormCode") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.ListFormField", b => + { + b.HasOne("Kurs.Platform.Entities.ListForm", null) + .WithMany() + .HasForeignKey("ListFormCode") + .HasPrincipalKey("ListFormCode") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.State", b => + { + b.HasOne("Kurs.Platform.Entities.Country", "Country") + .WithMany("States") + .HasForeignKey("CountryCode") + .HasPrincipalKey("Code") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Country"); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.Uom", b => + { + b.HasOne("Kurs.Platform.Entities.UomCategory", "UomCategory") + .WithMany("Units") + .HasForeignKey("CategoryName") + .HasPrincipalKey("Name") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("UomCategory"); + }); + + modelBuilder.Entity("Kurs.Platform.Forum.ForumPost", b => + { + b.HasOne("Kurs.Platform.Forum.ForumPost", "ParentPost") + .WithMany("Replies") + .HasForeignKey("ParentPostId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Kurs.Platform.Forum.ForumTopic", "Topic") + .WithMany("Posts") + .HasForeignKey("TopicId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ParentPost"); + + b.Navigation("Topic"); + }); + + modelBuilder.Entity("Kurs.Platform.Forum.ForumTopic", b => + { + b.HasOne("Kurs.Platform.Forum.ForumCategory", "Category") + .WithMany("Topics") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Skill", b => + { + b.HasOne("SkillType", null) + .WithMany("Skills") + .HasForeignKey("SkillTypeId"); + }); + + modelBuilder.Entity("SkillLevel", b => + { + b.HasOne("SkillType", null) + .WithMany("Levels") + .HasForeignKey("SkillTypeId"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange", null) + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", b => + { + b.HasOne("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", null) + .WithMany() + .HasForeignKey("ApplicationId"); + }); + + modelBuilder.Entity("Volo.Abp.OpenIddict.Tokens.OpenIddictToken", b => + { + b.HasOne("Volo.Abp.OpenIddict.Applications.OpenIddictApplication", null) + .WithMany() + .HasForeignKey("ApplicationId"); + + b.HasOne("Volo.Abp.OpenIddict.Authorizations.OpenIddictAuthorization", null) + .WithMany() + .HasForeignKey("AuthorizationId"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.HasOne("Volo.Abp.TenantManagement.Tenant", null) + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Kurs.Languages.Entities.LanguageKey", b => + { + b.Navigation("Texts"); + }); + + modelBuilder.Entity("Kurs.Platform.Blog.BlogCategory", b => + { + b.Navigation("Posts"); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.Country", b => + { + b.Navigation("States"); + }); + + modelBuilder.Entity("Kurs.Platform.Entities.UomCategory", b => + { + b.Navigation("Units"); + }); + + modelBuilder.Entity("Kurs.Platform.Forum.ForumCategory", b => + { + b.Navigation("Topics"); + }); + + modelBuilder.Entity("Kurs.Platform.Forum.ForumPost", b => + { + b.Navigation("Replies"); + }); + + modelBuilder.Entity("Kurs.Platform.Forum.ForumTopic", b => + { + b.Navigation("Posts"); + }); + + modelBuilder.Entity("SkillType", b => + { + b.Navigation("Levels"); + + b.Navigation("Skills"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Navigation("Actions"); + + b.Navigation("EntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Navigation("PropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("OrganizationUnits"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Navigation("Roles"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Navigation("ConnectionStrings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20250623140407_AddForum.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20250623140407_AddForum.cs new file mode 100644 index 00000000..78645705 --- /dev/null +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20250623140407_AddForum.cs @@ -0,0 +1,165 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Kurs.Platform.Migrations +{ + /// + public partial class AddForum : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "PForumCategories", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Slug = table.Column(type: "nvarchar(max)", nullable: true), + Description = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), + Icon = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + DisplayOrder = table.Column(type: "int", nullable: false), + IsActive = table.Column(type: "bit", nullable: false), + IsLocked = table.Column(type: "bit", nullable: false), + TopicCount = table.Column(type: "int", nullable: false), + PostCount = table.Column(type: "int", nullable: false), + LastPostId = table.Column(type: "uniqueidentifier", nullable: true), + LastPostDate = table.Column(type: "datetime2", nullable: true), + LastPostUserId = table.Column(type: "uniqueidentifier", nullable: true), + LastPostUserName = table.Column(type: "nvarchar(max)", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PForumCategories", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "PForumTopics", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Title = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Content = table.Column(type: "nvarchar(max)", nullable: false), + CategoryId = table.Column(type: "uniqueidentifier", nullable: false), + AuthorId = table.Column(type: "uniqueidentifier", nullable: false), + AuthorName = table.Column(type: "nvarchar(max)", nullable: true), + ViewCount = table.Column(type: "int", nullable: false), + ReplyCount = table.Column(type: "int", nullable: false), + LikeCount = table.Column(type: "int", nullable: false), + IsPinned = table.Column(type: "bit", nullable: false), + IsLocked = table.Column(type: "bit", nullable: false), + IsSolved = table.Column(type: "bit", nullable: false), + LastPostId = table.Column(type: "uniqueidentifier", nullable: true), + LastPostDate = table.Column(type: "datetime2", nullable: true), + LastPostUserId = table.Column(type: "uniqueidentifier", nullable: true), + LastPostUserName = table.Column(type: "nvarchar(max)", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PForumTopics", x => x.Id); + table.ForeignKey( + name: "FK_PForumTopics_PForumCategories_CategoryId", + column: x => x.CategoryId, + principalTable: "PForumCategories", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "PForumPosts", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + TopicId = table.Column(type: "uniqueidentifier", nullable: false), + Content = table.Column(type: "nvarchar(max)", nullable: false), + AuthorId = table.Column(type: "uniqueidentifier", nullable: false), + AuthorName = table.Column(type: "nvarchar(max)", nullable: true), + LikeCount = table.Column(type: "int", nullable: false), + IsAcceptedAnswer = table.Column(type: "bit", nullable: false), + ParentPostId = table.Column(type: "uniqueidentifier", nullable: true), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PForumPosts", x => x.Id); + table.ForeignKey( + name: "FK_PForumPosts_PForumPosts_ParentPostId", + column: x => x.ParentPostId, + principalTable: "PForumPosts", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_PForumPosts_PForumTopics_TopicId", + column: x => x.TopicId, + principalTable: "PForumTopics", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_PForumCategories_DisplayOrder", + table: "PForumCategories", + column: "DisplayOrder"); + + migrationBuilder.CreateIndex( + name: "IX_PForumPosts_ParentPostId", + table: "PForumPosts", + column: "ParentPostId"); + + migrationBuilder.CreateIndex( + name: "IX_PForumPosts_TopicId", + table: "PForumPosts", + column: "TopicId"); + + migrationBuilder.CreateIndex( + name: "IX_PForumTopics_CategoryId", + table: "PForumTopics", + column: "CategoryId"); + + migrationBuilder.CreateIndex( + name: "IX_PForumTopics_IsPinned", + table: "PForumTopics", + column: "IsPinned"); + + migrationBuilder.CreateIndex( + name: "IX_PForumTopics_LastPostDate", + table: "PForumTopics", + column: "LastPostDate"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "PForumPosts"); + + migrationBuilder.DropTable( + name: "PForumTopics"); + + migrationBuilder.DropTable( + name: "PForumCategories"); + } + } +} diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs index 31fc5c48..0b9be550 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs @@ -2436,6 +2436,251 @@ namespace Kurs.Platform.Migrations b.ToTable("PUomCategory", (string)null); }); + modelBuilder.Entity("Kurs.Platform.Forum.ForumCategory", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("DisplayOrder") + .HasColumnType("int"); + + b.Property("Icon") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsLocked") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LastPostDate") + .HasColumnType("datetime2"); + + b.Property("LastPostId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastPostUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastPostUserName") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PostCount") + .HasColumnType("int"); + + b.Property("Slug") + .HasColumnType("nvarchar(max)"); + + b.Property("TopicCount") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("DisplayOrder"); + + b.ToTable("PForumCategories", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Forum.ForumPost", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorId") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorName") + .HasColumnType("nvarchar(max)"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsAcceptedAnswer") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LikeCount") + .HasColumnType("int"); + + b.Property("ParentPostId") + .HasColumnType("uniqueidentifier"); + + b.Property("TopicId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("ParentPostId"); + + b.HasIndex("TopicId"); + + b.ToTable("PForumPosts", (string)null); + }); + + modelBuilder.Entity("Kurs.Platform.Forum.ForumTopic", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorId") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorName") + .HasColumnType("nvarchar(max)"); + + b.Property("CategoryId") + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsLocked") + .HasColumnType("bit"); + + b.Property("IsPinned") + .HasColumnType("bit"); + + b.Property("IsSolved") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("LastPostDate") + .HasColumnType("datetime2"); + + b.Property("LastPostId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastPostUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastPostUserName") + .HasColumnType("nvarchar(max)"); + + b.Property("LikeCount") + .HasColumnType("int"); + + b.Property("ReplyCount") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ViewCount") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("IsPinned"); + + b.HasIndex("LastPostDate"); + + b.ToTable("PForumTopics", (string)null); + }); + modelBuilder.Entity("Kurs.Settings.Entities.SettingDefinition", b => { b.Property("Id") @@ -4589,6 +4834,35 @@ namespace Kurs.Platform.Migrations b.Navigation("UomCategory"); }); + modelBuilder.Entity("Kurs.Platform.Forum.ForumPost", b => + { + b.HasOne("Kurs.Platform.Forum.ForumPost", "ParentPost") + .WithMany("Replies") + .HasForeignKey("ParentPostId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Kurs.Platform.Forum.ForumTopic", "Topic") + .WithMany("Posts") + .HasForeignKey("TopicId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ParentPost"); + + b.Navigation("Topic"); + }); + + modelBuilder.Entity("Kurs.Platform.Forum.ForumTopic", b => + { + b.HasOne("Kurs.Platform.Forum.ForumCategory", "Category") + .WithMany("Topics") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Category"); + }); + modelBuilder.Entity("Skill", b => { b.HasOne("SkillType", null) @@ -4765,6 +5039,21 @@ namespace Kurs.Platform.Migrations b.Navigation("Units"); }); + modelBuilder.Entity("Kurs.Platform.Forum.ForumCategory", b => + { + b.Navigation("Topics"); + }); + + modelBuilder.Entity("Kurs.Platform.Forum.ForumPost", b => + { + b.Navigation("Replies"); + }); + + modelBuilder.Entity("Kurs.Platform.Forum.ForumTopic", b => + { + b.Navigation("Posts"); + }); + modelBuilder.Entity("SkillType", b => { b.Navigation("Levels"); diff --git a/ui/dev-dist/sw.js b/ui/dev-dist/sw.js index 8b2dd697..304d71f6 100644 --- a/ui/dev-dist/sw.js +++ b/ui/dev-dist/sw.js @@ -82,7 +82,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict'; "revision": "3ca0b8505b4bec776b69afdba2768812" }, { "url": "index.html", - "revision": "0.pd7p0avqcno" + "revision": "0.ouca2h9ms1" }], {}); workbox.cleanupOutdatedCaches(); workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { diff --git a/ui/src/configs/routes.config/adminRoutes.tsx b/ui/src/configs/routes.config/adminRoutes.tsx index 5505d792..6d3ecbb6 100644 --- a/ui/src/configs/routes.config/adminRoutes.tsx +++ b/ui/src/configs/routes.config/adminRoutes.tsx @@ -63,6 +63,18 @@ const adminRoutes: Routes = [ component: lazy(() => import('@/views/blog/BlogManagement')), 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 } diff --git a/ui/src/constants/route.constant.ts b/ui/src/constants/route.constant.ts index 64c4a585..41c30947 100644 --- a/ui/src/constants/route.constant.ts +++ b/ui/src/constants/route.constant.ts @@ -36,10 +36,11 @@ export const ROUTES_ENUM = { }, chart: '/admin/chart/edit/:chartCode', blog: { - management: '/admin/blog', + management: '/admin/blogmanagement', }, forum: { - management: '/admin/forum', + view: '/admin/forum', + management: '/admin/forummanagement', }, }, settings: '/settings', diff --git a/ui/src/proxy/forum/forum.ts b/ui/src/proxy/forum/forum.ts new file mode 100644 index 00000000..e4b34ea9 --- /dev/null +++ b/ui/src/proxy/forum/forum.ts @@ -0,0 +1,50 @@ +export interface ForumCategory { + id: string; + name: string; + slug: string; + description: string; + icon: string; + displayOrder: number; + isActive: boolean; + isLocked: boolean; + topicCount: number; + postCount: number; + lastPostId?: string; + lastPostDate?: Date; + lastPostUserId?: string; + creationTime: Date; +} + +export interface ForumTopic { + id: string; + title: string; + content: string; + categoryId: string; + authorId: string; + authorName: string; + viewCount: number; + replyCount: number; + likeCount: number; + isPinned: boolean; + isLocked: boolean; + isSolved: boolean; + lastPostId?: string; + lastPostDate?: Date; + lastPostUserId?: string; + lastPostUserName?: string; + creationTime: Date; +} + +export interface ForumPost { + id: string; + topicId: string; + content: string; + authorId: string; + authorName: string; + likeCount: number; + isAcceptedAnswer: boolean; + parentPostId?: string; + creationTime: Date; +} + +export type ViewMode = 'forum' | 'admin'; \ No newline at end of file diff --git a/ui/src/services/forumService.ts b/ui/src/services/forumService.ts new file mode 100644 index 00000000..be10df29 --- /dev/null +++ b/ui/src/services/forumService.ts @@ -0,0 +1,347 @@ +import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum' +import apiService from '@/services/api.service' + +export interface ForumSearchResult { + categories: ForumCategory[] + topics: ForumTopic[] + posts: ForumPost[] + totalCount: number +} + +export interface SearchParams { + query: string + categoryId?: string + topicId?: string + searchInCategories?: boolean + searchInTopics?: boolean + searchInPosts?: boolean + page?: number + pageSize?: number +} + +export interface PaginatedResponse { + items: T[] + totalCount: number + pageNumber: number + pageSize: number + totalPages: number +} + +export interface CreateCategoryRequest { + name: string + slug: string + description: string + icon: string + displayOrder: number + isActive: boolean + isLocked: boolean +} + +export interface CreateTopicRequest { + title: string + content: string + categoryId: string + isPinned?: boolean + isLocked?: boolean +} + +export interface CreatePostRequest { + topicId: string + content: string + parentPostId?: string +} + +export interface CategoryListParams { + page?: number + pageSize?: number + isActive?: boolean + search?: string +} + +export interface TopicListParams { + page?: number + pageSize?: number + categoryId?: string + isPinned?: boolean + isSolved?: boolean + search?: string +} + +export interface PostListParams { + page?: number + pageSize?: number + topicId?: string + isAcceptedAnswer?: boolean + search?: string +} + +class ForumService { + // Search + async search(params: SearchParams): Promise { + const response = await apiService.fetchData({ + url: '/api/app/forum/search', + method: 'GET', + params, + }) + return response.data + } + + // Categories + async getCategories(params: CategoryListParams = {}): Promise> { + const response = await apiService.fetchData>({ + url: '/api/app/forum/categories', + method: 'GET', + params, + }) + return response.data + } + + async getCategoryById(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/categories/${id}`, + method: 'GET', + }) + return response.data + } + + async getCategoryBySlug(slug: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/categories/by-slug`, + method: 'GET', + params: { slug }, + }) + return response.data + } + + async createCategory(data: CreateCategoryRequest): Promise { + const response = await apiService.fetchData({ + url: '/api/app/forum/categories', + method: 'POST', + data, + }) + return response.data + } + + async updateCategory(id: string, data: Partial): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/categories/${id}`, + method: 'PUT', + data, + }) + return response.data + } + + async deleteCategory(id: string): Promise { + await apiService.fetchData({ + url: `/api/app/forum/categories/${id}`, + method: 'DELETE', + }) + } + + async toggleCategoryStatus(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/categories/${id}/toggle-status`, + method: 'POST', + }) + return response.data + } + + // Topics + async getTopics(params: TopicListParams = {}): Promise> { + const response = await apiService.fetchData>({ + url: '/api/app/forum/topics', + method: 'GET', + params, + }) + return response.data + } + + async getTopicById(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/${id}/topic`, + method: 'GET', + }) + return response.data + } + + async createTopic(data: CreateTopicRequest): Promise { + const response = await apiService.fetchData({ + url: '/api/app/forum/topic', + method: 'POST', + data, + }) + return response.data + } + + async updateTopic(id: string, data: Partial): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/topics/${id}`, + method: 'PUT', + data, + }) + return response.data + } + + async deleteTopic(id: string): Promise { + await apiService.fetchData({ + url: `/api/app/forum/topics/${id}`, + method: 'DELETE', + }) + } + + async pinTopic(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/topics/${id}/pin`, + method: 'POST', + }) + return response.data + } + + async unpinTopic(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/topics/${id}/unpin`, + method: 'POST', + }) + return response.data + } + + async lockTopic(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/topics/${id}/lock`, + method: 'POST', + }) + return response.data + } + + async unlockTopic(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/topics/${id}/unlock`, + method: 'POST', + }) + return response.data + } + + async markTopicAsSolved(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/topics/${id}/mark-solved`, + method: 'POST', + }) + return response.data + } + + async markTopicAsUnsolved(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/topics/${id}/mark-unsolved`, + method: 'POST', + }) + return response.data + } + + // Posts + async getPosts(params: PostListParams = {}): Promise> { + const response = await apiService.fetchData>({ + url: '/api/app/forum/posts', + method: 'GET', + params, + }) + return response.data + } + + async getPostById(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/posts/${id}`, + method: 'GET', + }) + return response.data + } + + async createPost(data: CreatePostRequest): Promise { + const response = await apiService.fetchData({ + url: '/api/app/forum/post', + method: 'POST', + data, + }) + return response.data + } + + async updatePost(id: string, data: Partial): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/posts/${id}`, + method: 'PUT', + data, + }) + return response.data + } + + async deletePost(id: string): Promise { + await apiService.fetchData({ + url: `/api/app/forum/posts/${id}`, + method: 'DELETE', + }) + } + + async likePost(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/${id}/like-post`, + method: 'POST', + }) + return response.data + } + + async unlikePost(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/${id}/unlike-post`, + method: 'POST', + }) + return response.data + } + + async likeTopic(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/${id}/like-topic`, + method: 'POST', + }) + return response.data + } + + async unlikeTopic(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/${id}/unlike-topic`, + method: 'POST', + }) + return response.data + } + + async markPostAsAcceptedAnswer(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/posts/${id}/mark-accepted`, + method: 'POST', + }) + return response.data + } + + async unmarkPostAsAcceptedAnswer(id: string): Promise { + const response = await apiService.fetchData({ + url: `/api/app/forum/posts/${id}/unmark-accepted`, + method: 'POST', + }) + return response.data + } + + // Forum statistics + async getForumStats(): Promise<{ + totalCategories: number + totalTopics: number + totalPosts: number + totalUsers: number + activeUsers: number + }> { + const response = await apiService.fetchData({ + url: '/api/app/forum/stats', + method: 'GET', + }) + return response.data + } +} + +export const forumService = new ForumService() diff --git a/ui/src/utils/hooks/useForumSearch.ts b/ui/src/utils/hooks/useForumSearch.ts new file mode 100644 index 00000000..5e860cf9 --- /dev/null +++ b/ui/src/utils/hooks/useForumSearch.ts @@ -0,0 +1,80 @@ +import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum'; +import { useState, useEffect, useMemo } from 'react'; + +interface UseSearchProps { + categories: ForumCategory[]; + topics: ForumTopic[]; + posts: ForumPost[]; +} + +interface SearchResult { + categories: ForumCategory[]; + topics: ForumTopic[]; + posts: ForumPost[]; + totalCount: number; +} + +export function useForumSearch({ categories, topics, posts }: UseSearchProps) { + const [searchQuery, setSearchQuery] = useState(''); + const [searchResults, setSearchResults] = useState({ + categories: [], + topics: [], + posts: [], + totalCount: 0 + }); + + const performSearch = useMemo(() => { + if (!searchQuery.trim()) { + return { + categories: [], + topics: [], + posts: [], + totalCount: 0 + }; + } + + const query = searchQuery.toLowerCase().trim(); + + // Search in categories + const matchedCategories = categories.filter(category => + category.name.toLowerCase().includes(query) || + category.description.toLowerCase().includes(query) + ); + + // Search in topics + const matchedTopics = topics.filter(topic => + topic.title.toLowerCase().includes(query) || + topic.content.toLowerCase().includes(query) || + topic.authorName.toLowerCase().includes(query) + ); + + // Search in posts + const matchedPosts = posts.filter(post => + post.content.toLowerCase().includes(query) || + post.authorName.toLowerCase().includes(query) + ); + + return { + categories: matchedCategories, + topics: matchedTopics, + posts: matchedPosts, + totalCount: matchedCategories.length + matchedTopics.length + matchedPosts.length + }; + }, [searchQuery, categories, topics, posts]); + + useEffect(() => { + setSearchResults(performSearch); + }, [performSearch]); + + const clearSearch = () => { + setSearchQuery(''); + }; + + return { + searchQuery, + setSearchQuery, + searchResults, + clearSearch, + hasResults: searchResults.totalCount > 0 + }; +} \ No newline at end of file diff --git a/ui/src/views/forum/Forum.tsx b/ui/src/views/forum/Forum.tsx new file mode 100644 index 00000000..11d5bacb --- /dev/null +++ b/ui/src/views/forum/Forum.tsx @@ -0,0 +1,83 @@ +import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum' +import { useStoreState } from '@/store/store' +import React, { useState, useEffect } from 'react' +import { useForumData } from './useForumData' +import { ForumView } from './forum/ForumView' + +export function Forum() { + const { user, tenantId } = useStoreState((state) => state.auth) + const { + categories, + topics, + posts, + loading, + error, + createTopic, + createPost, + likePost, + unlikePost, + clearError, + } = useForumData() + + const [selectedCategory, setSelectedCategory] = useState(null) + const [selectedTopic, setSelectedTopic] = useState(null) + const [forumViewState, setForumViewState] = useState<'categories' | 'topics' | 'posts'>( + 'categories', + ) + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if ((e.metaKey || e.ctrlKey) && e.key === 'k') { + e.preventDefault() + } + } + document.addEventListener('keydown', handleKeyDown) + return () => document.removeEventListener('keydown', handleKeyDown) + }, []) + + useEffect(() => { + if (error) { + const timer = setTimeout(() => { + clearError() + }, 5000) + return () => clearTimeout(timer) + } + }, [error, clearError]) + + return ( +
+ {error && ( +
+
+ Error: + {error} + +
+
+ )} + + createTopic(topicData).then(() => {})} + onCreatePost={(postData) => createPost(postData).then(() => {})} + onLikePost={(id) => likePost(id).then(() => {})} + onUnlikePost={(id) => unlikePost(id).then(() => {})} + currentUserId={user.id} + currentUserName={user.name} + selectedCategory={selectedCategory} + selectedTopic={selectedTopic} + viewState={forumViewState} + onCategorySelect={setSelectedCategory} + onTopicSelect={setSelectedTopic} + onViewStateChange={setForumViewState} + /> +
+ ) +} + +export default Forum diff --git a/ui/src/views/forum/Management.tsx b/ui/src/views/forum/Management.tsx new file mode 100644 index 00000000..3cf56212 --- /dev/null +++ b/ui/src/views/forum/Management.tsx @@ -0,0 +1,102 @@ +import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum' +import { useStoreState } from '@/store/store' +import React, { useState, useEffect } from 'react' +import { useForumData } from './useForumData' +import { AdminView } from './admin/AdminView' + +export function Management() { + const { user, tenantId } = useStoreState((state) => state.auth) + const { + categories, + topics, + posts, + loading, + error, + createCategory, + updateCategory, + deleteCategory, + createTopic, + updateTopic, + deleteTopic, + pinTopic, + unpinTopic, + lockTopic, + unlockTopic, + markTopicAsSolved, + markTopicAsUnsolved, + createPost, + updatePost, + deletePost, + markPostAsAcceptedAnswer, + unmarkPostAsAcceptedAnswer, + clearError, + } = useForumData() + + const [selectedCategory, setSelectedCategory] = useState(null) + const [selectedTopic, setSelectedTopic] = useState(null) + const [forumViewState, setForumViewState] = useState<'categories' | 'topics' | 'posts'>( + 'categories', + ) + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if ((e.metaKey || e.ctrlKey) && e.key === 'k') { + e.preventDefault() + // Search modal will be opened by Header component + } + } + document.addEventListener('keydown', handleKeyDown) + return () => document.removeEventListener('keydown', handleKeyDown) + }, []) + + useEffect(() => { + if (error) { + const timer = setTimeout(() => { + clearError() + }, 5000) + return () => clearTimeout(timer) + } + }, [error, clearError]) + + return ( +
+ {error && ( +
+
+ Error: + {error} + +
+
+ )} + + createCategory(data).then(() => {})} + onUpdateCategory={(id, data) => updateCategory(id, data).then(() => {})} + onDeleteCategory={(id) => deleteCategory(id).then(() => {})} + onCreateTopic={(data) => createTopic(data).then(() => {})} + onUpdateTopic={(id, data) => updateTopic(id, data).then(() => {})} + onDeleteTopic={(id) => deleteTopic(id).then(() => {})} + onPinTopic={(id) => pinTopic(id).then(() => {})} + onUnpinTopic={(id) => unpinTopic(id).then(() => {})} + onLockTopic={(id) => lockTopic(id).then(() => {})} + onUnlockTopic={(id) => unlockTopic(id).then(() => {})} + onMarkTopicAsSolved={(id) => markTopicAsSolved(id).then(() => {})} + onMarkTopicAsUnsolved={(id) => markTopicAsUnsolved(id).then(() => {})} + onCreatePost={(data) => createPost(data).then(() => {})} + onUpdatePost={(id, data) => updatePost(id, data).then(() => {})} + onDeletePost={(id) => deletePost(id).then(() => {})} + onMarkPostAsAcceptedAnswer={(id) => markPostAsAcceptedAnswer(id).then(() => {})} + onUnmarkPostAsAcceptedAnswer={(id) => unmarkPostAsAcceptedAnswer(id).then(() => {})} + /> +
+ ) +} + +export default Management diff --git a/ui/src/views/forum/admin/AdminStats.tsx b/ui/src/views/forum/admin/AdminStats.tsx new file mode 100644 index 00000000..71669858 --- /dev/null +++ b/ui/src/views/forum/admin/AdminStats.tsx @@ -0,0 +1,105 @@ +import React from 'react'; +import { Folder, MessageSquare, FileText, TrendingUp } from 'lucide-react'; +import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum'; + +interface AdminStatsProps { + categories: ForumCategory[]; + topics: ForumTopic[]; + posts: ForumPost[]; +} + +export function AdminStats({ categories, topics, posts }: AdminStatsProps) { + const totalCategories = categories.length; + const activeCategories = categories.filter(c => c.isActive).length; + const totalTopics = topics.length; + const solvedTopics = topics.filter(t => t.isSolved).length; + const totalPosts = posts.length; + const acceptedAnswers = posts.filter(p => p.isAcceptedAnswer).length; + + const stats = [ + { + title: 'Total Categories', + value: totalCategories, + subtitle: `${activeCategories} active`, + icon: Folder, + color: 'bg-blue-500', + }, + { + title: 'Total Topics', + value: totalTopics, + subtitle: `${solvedTopics} solved`, + icon: MessageSquare, + color: 'bg-emerald-500', + }, + { + title: 'Total Posts', + value: totalPosts, + subtitle: `${acceptedAnswers} accepted answers`, + icon: FileText, + color: 'bg-orange-500', + }, + { + title: 'Engagement Rate', + value: totalTopics > 0 ? Math.round((totalPosts / totalTopics) * 100) / 100 : 0, + subtitle: 'posts per topic', + icon: TrendingUp, + color: 'bg-purple-500', + }, + ]; + + return ( +
+
+

Forum Statistics

+ +
+ {stats.map((stat, index) => { + const Icon = stat.icon; + return ( +
+
+
+ +
+
+
+

{stat.value}

+

{stat.title}

+

{stat.subtitle}

+
+
+ ); + })} +
+
+ + {/* Recent Activity */} +
+

Recent Activity

+
+
+
+
+

New topic created in General Discussion

+

2 hours ago

+
+
+
+
+
+

Post marked as accepted answer

+

4 hours ago

+
+
+
+
+
+

New category created: Feature Requests

+

1 day ago

+
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/ui/src/views/forum/admin/AdminView.tsx b/ui/src/views/forum/admin/AdminView.tsx new file mode 100644 index 00000000..ef965643 --- /dev/null +++ b/ui/src/views/forum/admin/AdminView.tsx @@ -0,0 +1,160 @@ +import React, { useState } from 'react'; +import { Folder, MessageSquare, FileText, Plus, BarChart3 } from 'lucide-react'; +import { CategoryManagement } from './CategoryManagement'; +import { TopicManagement } from './TopicManagement'; +import { PostManagement } from './PostManagement'; +import { AdminStats } from './AdminStats'; +import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum'; + +interface AdminViewProps { + categories: ForumCategory[]; + topics: ForumTopic[]; + posts: ForumPost[]; + loading: boolean; + onCreateCategory: (category: { + name: string; + slug: string; + description: string; + icon: string; + displayOrder: number; + isActive: boolean; + isLocked: boolean; + }) => Promise; + onUpdateCategory: (id: string, category: Partial) => Promise; + onDeleteCategory: (id: string) => Promise; + onCreateTopic: (topic: { + title: string; + content: string; + categoryId: string; + isPinned?: boolean; + isLocked?: boolean; + }) => Promise; + onUpdateTopic: (id: string, topic: Partial) => Promise; + onDeleteTopic: (id: string) => Promise; + onPinTopic: (id: string) => Promise; + onUnpinTopic: (id: string) => Promise; + onLockTopic: (id: string) => Promise; + onUnlockTopic: (id: string) => Promise; + onMarkTopicAsSolved: (id: string) => Promise; + onMarkTopicAsUnsolved: (id: string) => Promise; + onCreatePost: (post: { + topicId: string; + content: string; + parentPostId?: string; + }) => Promise; + onUpdatePost: (id: string, post: Partial) => Promise; + onDeletePost: (id: string) => Promise; + onMarkPostAsAcceptedAnswer: (id: string) => Promise; + onUnmarkPostAsAcceptedAnswer: (id: string) => Promise; +} + +type AdminSection = 'stats' | 'categories' | 'topics' | 'posts'; + +export function AdminView({ + categories, + topics, + posts, + loading, + onCreateCategory, + onUpdateCategory, + onDeleteCategory, + onCreateTopic, + onUpdateTopic, + onDeleteTopic, + onPinTopic, + onUnpinTopic, + onLockTopic, + onUnlockTopic, + onMarkTopicAsSolved, + onMarkTopicAsUnsolved, + onCreatePost, + onUpdatePost, + onDeletePost, + onMarkPostAsAcceptedAnswer, + onUnmarkPostAsAcceptedAnswer +}: AdminViewProps) { + const [activeSection, setActiveSection] = useState('stats'); + + const navigationItems = [ + { id: 'stats' as AdminSection, label: 'Dashboard', icon: BarChart3 }, + { id: 'categories' as AdminSection, label: 'Categories', icon: Folder }, + { id: 'topics' as AdminSection, label: 'Topics', icon: MessageSquare }, + { id: 'posts' as AdminSection, label: 'Posts', icon: FileText }, + ]; + + return ( +
+
+ {/* Sidebar Navigation */} +
+ +
+ + {/* Main Content */} +
+ {activeSection === 'stats' && ( + + )} + + {activeSection === 'categories' && ( + + )} + + {activeSection === 'topics' && ( + + )} + + {activeSection === 'posts' && ( + + )} +
+
+
+ ); +} \ No newline at end of file diff --git a/ui/src/views/forum/admin/CategoryManagement.tsx b/ui/src/views/forum/admin/CategoryManagement.tsx new file mode 100644 index 00000000..c75b420a --- /dev/null +++ b/ui/src/views/forum/admin/CategoryManagement.tsx @@ -0,0 +1,321 @@ +import React, { useState } from 'react'; +import { Plus, Edit2, Trash2, Lock, Unlock, Eye, EyeOff, Loader2 } from 'lucide-react'; +import { ForumCategory } from '@/proxy/forum/forum'; + +interface CategoryManagementProps { + categories: ForumCategory[]; + loading: boolean; + onCreateCategory: (category: { + name: string; + slug: string; + description: string; + icon: string; + displayOrder: number; + isActive: boolean; + isLocked: boolean; + }) => Promise; + onUpdateCategory: (id: string, category: Partial) => Promise; + onDeleteCategory: (id: string) => Promise; +} + +export function CategoryManagement({ + categories, + loading, + onCreateCategory, + onUpdateCategory, + onDeleteCategory +}: CategoryManagementProps) { + const [showCreateForm, setShowCreateForm] = useState(false); + const [editingCategory, setEditingCategory] = useState(null); + const [formData, setFormData] = useState({ + name: '', + slug: '', + description: '', + icon: '', + displayOrder: 0, + isActive: true, + isLocked: false, + }); + const [submitting, setSubmitting] = useState(false); + + const resetForm = () => { + setFormData({ + name: '', + slug: '', + description: '', + icon: '', + displayOrder: 0, + isActive: true, + isLocked: false, + }); + setShowCreateForm(false); + setEditingCategory(null); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (submitting) return; + + try { + setSubmitting(true); + if (editingCategory) { + await onUpdateCategory(editingCategory.id, formData); + } else { + await onCreateCategory(formData); + } + resetForm(); + } catch (error) { + console.error('Error submitting form:', error); + } finally { + setSubmitting(false); + } + }; + + const handleEdit = (category: ForumCategory) => { + setEditingCategory(category); + setFormData({ + name: category.name, + slug: category.slug, + description: category.description, + icon: category.icon, + displayOrder: category.displayOrder, + isActive: category.isActive, + isLocked: category.isLocked, + }); + setShowCreateForm(true); + }; + + const handleToggleActive = async (category: ForumCategory) => { + try { + await onUpdateCategory(category.id, { isActive: !category.isActive }); + } catch (error) { + console.error('Error toggling category status:', error); + } + }; + + const handleToggleLocked = async (category: ForumCategory) => { + try { + await onUpdateCategory(category.id, { isLocked: !category.isLocked }); + } catch (error) { + console.error('Error toggling category lock:', error); + } + }; + + const handleDelete = async (id: string) => { + if (confirm('Are you sure you want to delete this category? This will also delete all topics and posts in this category.')) { + try { + await onDeleteCategory(id); + } catch (error) { + console.error('Error deleting category:', error); + } + } + }; + + return ( +
+
+

Category Management

+ +
+ + {/* Create/Edit Form */} + {showCreateForm && ( +
+

+ {editingCategory ? 'Edit Category' : 'Create New Category'} +

+
+
+
+ + setFormData({ ...formData, name: e.target.value })} + className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent" + required + /> +
+
+ + setFormData({ ...formData, slug: e.target.value })} + className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent" + required + /> +
+
+ +
+ +