Blog sistemindeki güncellemeler2

This commit is contained in:
Sedat ÖZTÜRK 2025-06-20 14:11:42 +03:00
parent 2a1f06b2f4
commit bcd0e45149
25 changed files with 593 additions and 1927 deletions

View file

@ -65,6 +65,7 @@ namespace Kurs.Platform.Blog
public string Summary { get; set; } public string Summary { get; set; }
public string ReadTime { get; set; } public string ReadTime { get; set; }
public string CoverImage { get; set; } public string CoverImage { get; set; }
public string Content { get; set; }
public BlogCategoryDto Category { get; set; } public BlogCategoryDto Category { get; set; }
public AuthorDto Author { get; set; } public AuthorDto Author { get; set; }

View file

@ -17,9 +17,6 @@ namespace Kurs.Platform.Blog
Task DeletePostAsync(Guid id); Task DeletePostAsync(Guid id);
Task<BlogPostDto> PublishPostAsync(Guid id); Task<BlogPostDto> PublishPostAsync(Guid id);
Task<BlogPostDto> UnpublishPostAsync(Guid id); Task<BlogPostDto> UnpublishPostAsync(Guid id);
Task LikePostAsync(Guid id);
Task UnlikePostAsync(Guid id);
Task IncrementViewCountAsync(Guid id);
// Blog Category methods // Blog Category methods
Task<List<BlogCategoryDto>> GetCategoriesAsync(); Task<List<BlogCategoryDto>> GetCategoriesAsync();
@ -27,28 +24,12 @@ namespace Kurs.Platform.Blog
Task<BlogCategoryDto> CreateCategoryAsync(CreateUpdateBlogCategoryDto input); Task<BlogCategoryDto> CreateCategoryAsync(CreateUpdateBlogCategoryDto input);
Task<BlogCategoryDto> UpdateCategoryAsync(Guid id, CreateUpdateBlogCategoryDto input); Task<BlogCategoryDto> UpdateCategoryAsync(Guid id, CreateUpdateBlogCategoryDto input);
Task DeleteCategoryAsync(Guid id); Task DeleteCategoryAsync(Guid id);
// Search and filters
Task<PagedResultDto<BlogPostListDto>> SearchPostsAsync(SearchBlogPostsInput input);
Task<PagedResultDto<BlogPostListDto>> GetPostsByCategoryAsync(Guid categoryId, PagedAndSortedResultRequestDto input);
Task<PagedResultDto<BlogPostListDto>> GetPostsByTagAsync(string tag, PagedAndSortedResultRequestDto input);
Task<PagedResultDto<BlogPostListDto>> GetPostsByAuthorAsync(Guid authorId, PagedAndSortedResultRequestDto input);
// Tags
Task<List<string>> GetPopularTagsAsync(int count = 20);
// Stats
Task<BlogStatsDto> GetStatsAsync();
} }
public class GetBlogPostsInput : PagedAndSortedResultRequestDto public class GetBlogPostsInput : PagedAndSortedResultRequestDto
{ {
public string Filter { get; set; } public string Search { get; set; }
public Guid? CategoryId { get; set; } public Guid? CategoryId { get; set; }
public string Tag { get; set; }
public Guid? AuthorId { get; set; }
public bool? IsPublished { get; set; }
public string SortBy { get; set; } = "latest"; // latest, popular, trending
} }
public class SearchBlogPostsInput : PagedAndSortedResultRequestDto public class SearchBlogPostsInput : PagedAndSortedResultRequestDto

View file

@ -2,9 +2,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Kurs.Platform.Localization;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Localization;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.Users; using Volo.Abp.Users;
@ -12,107 +13,81 @@ using Volo.Abp.Users;
namespace Kurs.Platform.Blog namespace Kurs.Platform.Blog
{ {
[Authorize] [Authorize]
public class BlogAppService : ApplicationService, IBlogAppService public class BlogAppService : PlatformAppService, IBlogAppService
{ {
private readonly IRepository<BlogPost, Guid> _postRepository; private readonly IRepository<BlogPost, Guid> _postRepository;
private readonly IRepository<BlogCategory, Guid> _categoryRepository; private readonly IRepository<BlogCategory, Guid> _categoryRepository;
private readonly IRepository<BlogPostTag, Guid> _tagRepository;
private readonly IRepository<BlogPostLike, Guid> _postLikeRepository;
private readonly ICurrentUser _currentUser; private readonly ICurrentUser _currentUser;
private readonly IStringLocalizer<PlatformResource> _localizer;
public BlogAppService( public BlogAppService(
IRepository<BlogPost, Guid> postRepository, IRepository<BlogPost, Guid> postRepository,
IRepository<BlogCategory, Guid> categoryRepository, IRepository<BlogCategory, Guid> categoryRepository,
IRepository<BlogPostTag, Guid> tagRepository, ICurrentUser currentUser,
IRepository<BlogPostLike, Guid> postLikeRepository, IStringLocalizer<PlatformResource> localizer)
ICurrentUser currentUser)
{ {
_postRepository = postRepository; _postRepository = postRepository;
_categoryRepository = categoryRepository; _categoryRepository = categoryRepository;
_tagRepository = tagRepository;
_postLikeRepository = postLikeRepository;
_currentUser = currentUser; _currentUser = currentUser;
_localizer = localizer;
} }
// Blog Post methods // Blog Post methods
[AllowAnonymous]
public async Task<PagedResultDto<BlogPostListDto>> GetPostsAsync(GetBlogPostsInput input) public async Task<PagedResultDto<BlogPostListDto>> GetPostsAsync(GetBlogPostsInput input)
{ {
var query = await _postRepository.GetQueryableAsync(); var allPosts = await _postRepository.GetListAsync(); // Tüm kayıtlar memory'ye alınır
if (!string.IsNullOrWhiteSpace(input.Filter)) var filtered = allPosts.Where(post =>
{ {
query = query.Where(x => x.Title.Contains(input.Filter) || x.Summary.Contains(input.Filter)); var localizedTitle = _localizer[post.Title].Value;
} var localizedSummary = _localizer[post.Summary].Value;
if (input.CategoryId.HasValue) var searchMatch = string.IsNullOrWhiteSpace(input.Search) ||
{ localizedTitle.Contains(input.Search, StringComparison.OrdinalIgnoreCase) ||
query = query.Where(x => x.CategoryId == input.CategoryId.Value); localizedSummary.Contains(input.Search, StringComparison.OrdinalIgnoreCase);
}
if (!string.IsNullOrWhiteSpace(input.Tag)) var categoryMatch = !input.CategoryId.HasValue || post.CategoryId == input.CategoryId.Value;
{
var postIds = await _tagRepository
.GetListAsync(x => x.Tag == input.Tag)
.ContinueWith(t => t.Result.Select(x => x.PostId).ToList());
query = query.Where(x => postIds.Contains(x.Id)); return searchMatch && categoryMatch;
} }).ToList();
if (input.AuthorId.HasValue) var totalCount = filtered.Count;
{
query = query.Where(x => x.AuthorId == input.AuthorId.Value);
}
if (input.IsPublished.HasValue) var pagedPosts = filtered
{ .OrderByDescending(x => x.CreationTime)
query = query.Where(x => x.IsPublished == input.IsPublished.Value); .Skip(input.SkipCount)
} .Take(input.MaxResultCount)
.ToList();
// Sorting var categoryIds = pagedPosts.Select(x => x.CategoryId).Distinct().ToList();
if (input.SortBy == "popular") var categories = await _categoryRepository.GetListAsync(x => categoryIds.Contains(x.Id));
{ var categoryDict = categories.ToDictionary(x => x.Id, x => x);
query = query.OrderByDescending(x => x.LikeCount);
}
else if (input.SortBy == "trending")
{
query = query.OrderByDescending(x => x.ViewCount);
}
else // latest
{
query = query.OrderByDescending(x => x.CreationTime);
}
var totalCount = await AsyncExecuter.CountAsync(query); var postIds = pagedPosts.Select(x => x.Id).ToList();
var posts = await AsyncExecuter.ToListAsync(
query.Skip(input.SkipCount).Take(input.MaxResultCount)
);
var postDtos = new List<BlogPostListDto>(); var postDtos = pagedPosts.Select(post =>
foreach (var post in posts)
{ {
var dto = ObjectMapper.Map<BlogPost, BlogPostListDto>(post); var dto = ObjectMapper.Map<BlogPost, BlogPostListDto>(post);
// Get category if (categoryDict.TryGetValue(post.CategoryId, out var category))
var category = await _categoryRepository.GetAsync(post.CategoryId); {
dto.Category = ObjectMapper.Map<BlogCategory, BlogCategoryDto>(category); dto.Category = ObjectMapper.Map<BlogCategory, BlogCategoryDto>(category);
}
// Get tags
var tags = await _tagRepository.GetListAsync(x => x.PostId == post.Id);
dto.Tags = tags.Select(x => x.Tag).ToList();
// Get author info
dto.Author = new AuthorDto dto.Author = new AuthorDto
{ {
Id = post.AuthorId, Id = post.AuthorId,
Name = post.CreatorId.HasValue ? "User" : "Unknown" Name = "User"
}; };
postDtos.Add(dto); return dto;
} }).ToList();
return new PagedResultDto<BlogPostListDto>(totalCount, postDtos); return new PagedResultDto<BlogPostListDto>(totalCount, postDtos);
} }
[AllowAnonymous]
public async Task<BlogPostDto> GetPostAsync(Guid id) public async Task<BlogPostDto> GetPostAsync(Guid id)
{ {
var post = await _postRepository.GetAsync(id); var post = await _postRepository.GetAsync(id);
@ -123,28 +98,17 @@ namespace Kurs.Platform.Blog
await _categoryRepository.GetAsync(post.CategoryId) await _categoryRepository.GetAsync(post.CategoryId)
); );
// Get tags
var tags = await _tagRepository.GetListAsync(x => x.PostId == post.Id);
dto.Tags = tags.Select(x => x.Tag).ToList();
// Get author info // Get author info
dto.Author = new AuthorDto dto.Author = new AuthorDto
{ {
Id = post.AuthorId, Id = post.AuthorId,
Name = post.CreatorId.HasValue ? "User" : "Unknown" Name = "User"
}; };
// Check if current user liked this post
if (_currentUser.IsAuthenticated)
{
dto.IsLiked = await _postLikeRepository.AnyAsync(
x => x.PostId == id && x.UserId == _currentUser.Id.Value
);
}
return dto; return dto;
} }
[AllowAnonymous]
public async Task<BlogPostDto> GetPostBySlugAsync(string slug) public async Task<BlogPostDto> GetPostBySlugAsync(string slug)
{ {
var post = await _postRepository.FirstOrDefaultAsync(x => x.Slug == slug); var post = await _postRepository.FirstOrDefaultAsync(x => x.Slug == slug);
@ -178,23 +142,7 @@ namespace Kurs.Platform.Blog
post.Publish(); post.Publish();
} }
await _postRepository.InsertAsync(post); await _postRepository.InsertAsync(post, autoSave: true);
// Add tags
foreach (var tag in input.Tags)
{
await _tagRepository.InsertAsync(new BlogPostTag(
GuidGenerator.Create(),
post.Id,
tag,
CurrentTenant.Id
));
}
// Update category post count
var category = await _categoryRepository.GetAsync(input.CategoryId);
category.IncrementPostCount();
await _categoryRepository.UpdateAsync(category);
return await GetPostAsync(post.Id); return await GetPostAsync(post.Id);
} }
@ -203,7 +151,6 @@ namespace Kurs.Platform.Blog
{ {
var post = await _postRepository.GetAsync(id); var post = await _postRepository.GetAsync(id);
// Check if user is author or has permission
if (post.AuthorId != _currentUser.Id && !await AuthorizationService.IsGrantedAsync("App.Blog.Update")) if (post.AuthorId != _currentUser.Id && !await AuthorizationService.IsGrantedAsync("App.Blog.Update"))
{ {
throw new Volo.Abp.Authorization.AbpAuthorizationException(); throw new Volo.Abp.Authorization.AbpAuthorizationException();
@ -211,11 +158,12 @@ namespace Kurs.Platform.Blog
post.Title = input.Title; post.Title = input.Title;
post.Slug = input.Slug; post.Slug = input.Slug;
post.Content = input.Content;
post.Summary = input.Summary; post.Summary = input.Summary;
post.CoverImage = input.CoverImage; post.CoverImage = input.CoverImage;
post.Content = input.Content;
if (input.IsPublished) post.Publish(); else post.Unpublish();
// Update category if changed
if (post.CategoryId != input.CategoryId) if (post.CategoryId != input.CategoryId)
{ {
var oldCategory = await _categoryRepository.GetAsync(post.CategoryId); var oldCategory = await _categoryRepository.GetAsync(post.CategoryId);
@ -231,19 +179,7 @@ namespace Kurs.Platform.Blog
await _postRepository.UpdateAsync(post); await _postRepository.UpdateAsync(post);
// Update tags return await GetPostAsync(post.Id); // ✅ DTO dönülüyor
await _tagRepository.DeleteAsync(x => x.PostId == id);
foreach (var tag in input.Tags)
{
await _tagRepository.InsertAsync(new BlogPostTag(
GuidGenerator.Create(),
post.Id,
tag,
CurrentTenant.Id
));
}
return await GetPostAsync(post.Id);
} }
public async Task DeletePostAsync(Guid id) public async Task DeletePostAsync(Guid id)
@ -296,61 +232,29 @@ namespace Kurs.Platform.Blog
return await GetPostAsync(id); return await GetPostAsync(id);
} }
public async Task LikePostAsync(Guid id)
{
var existingLike = await _postLikeRepository.FirstOrDefaultAsync(
x => x.PostId == id && x.UserId == _currentUser.Id.Value
);
if (existingLike == null)
{
await _postLikeRepository.InsertAsync(new BlogPostLike(
GuidGenerator.Create(),
id,
_currentUser.Id.Value,
CurrentTenant.Id
));
// Update like count
var post = await _postRepository.GetAsync(id);
var likeCount = await _postLikeRepository.CountAsync(x => x.PostId == id);
post.UpdateLikeCount((int)likeCount);
await _postRepository.UpdateAsync(post);
}
}
public async Task UnlikePostAsync(Guid id)
{
var existingLike = await _postLikeRepository.FirstOrDefaultAsync(
x => x.PostId == id && x.UserId == _currentUser.Id.Value
);
if (existingLike != null)
{
await _postLikeRepository.DeleteAsync(existingLike);
// Update like count
var post = await _postRepository.GetAsync(id);
var likeCount = await _postLikeRepository.CountAsync(x => x.PostId == id);
post.UpdateLikeCount((int)likeCount);
await _postRepository.UpdateAsync(post);
}
}
[AllowAnonymous]
public async Task IncrementViewCountAsync(Guid id)
{
var post = await _postRepository.GetAsync(id);
post.IncrementViewCount();
await _postRepository.UpdateAsync(post);
}
// Blog Category methods // Blog Category methods
[AllowAnonymous] [AllowAnonymous]
public async Task<List<BlogCategoryDto>> GetCategoriesAsync() public async Task<List<BlogCategoryDto>> GetCategoriesAsync()
{ {
var categories = await _categoryRepository.GetListAsync(x => x.IsActive); var categories = await _categoryRepository.GetListAsync();
return ObjectMapper.Map<List<BlogCategory>, List<BlogCategoryDto>>(categories);
var postQuery = await _postRepository.GetQueryableAsync();
var groupedCounts = postQuery
.Where(p => p.IsPublished) // sadece yayınlanmış yazılar
.GroupBy(p => p.CategoryId)
.Select(g => new { CategoryId = g.Key, Count = g.Count() })
.ToList();
var dtoList = ObjectMapper.Map<List<BlogCategory>, List<BlogCategoryDto>>(categories);
foreach (var dto in dtoList)
{
dto.PostCount = groupedCounts
.FirstOrDefault(x => x.CategoryId == dto.Id)?.Count ?? 0;
}
return dtoList;
} }
public async Task<BlogCategoryDto> GetCategoryAsync(Guid id) public async Task<BlogCategoryDto> GetCategoryAsync(Guid id)
@ -409,194 +313,5 @@ namespace Kurs.Platform.Blog
await _categoryRepository.DeleteAsync(id); await _categoryRepository.DeleteAsync(id);
} }
// Search and filters
public async Task<PagedResultDto<BlogPostListDto>> SearchPostsAsync(SearchBlogPostsInput input)
{
var query = await _postRepository.GetQueryableAsync();
if (!string.IsNullOrWhiteSpace(input.Query))
{
query = query.Where(x =>
x.Title.Contains(input.Query) ||
x.Content.Contains(input.Query) ||
x.Summary.Contains(input.Query)
);
}
if (input.CategoryId.HasValue)
{
query = query.Where(x => x.CategoryId == input.CategoryId.Value);
}
if (input.AuthorId.HasValue)
{
query = query.Where(x => x.AuthorId == input.AuthorId.Value);
}
if (input.IsPublished.HasValue)
{
query = query.Where(x => x.IsPublished == input.IsPublished.Value);
}
// Search by tag
if (!string.IsNullOrWhiteSpace(input.Tag))
{
var postIds = await _tagRepository
.GetListAsync(x => x.Tag == input.Tag)
.ContinueWith(t => t.Result.Select(x => x.PostId).ToList());
query = query.Where(x => postIds.Contains(x.Id));
}
var totalCount = await AsyncExecuter.CountAsync(query);
var posts = await AsyncExecuter.ToListAsync(
query.OrderByDescending(x => x.CreationTime)
.Skip(input.SkipCount)
.Take(input.MaxResultCount)
);
var postDtos = new List<BlogPostListDto>();
foreach (var post in posts)
{
var dto = ObjectMapper.Map<BlogPost, BlogPostListDto>(post);
// Get category
var category = await _categoryRepository.GetAsync(post.CategoryId);
dto.Category = ObjectMapper.Map<BlogCategory, BlogCategoryDto>(category);
// Get tags
var tags = await _tagRepository.GetListAsync(x => x.PostId == post.Id);
dto.Tags = tags.Select(x => x.Tag).ToList();
// Get author info
dto.Author = new AuthorDto
{
Id = post.AuthorId,
Name = post.CreatorId.HasValue ? "User" : "Unknown"
};
postDtos.Add(dto);
}
return new PagedResultDto<BlogPostListDto>(totalCount, postDtos);
}
public async Task<PagedResultDto<BlogPostListDto>> GetPostsByCategoryAsync(Guid categoryId, PagedAndSortedResultRequestDto input)
{
var searchInput = new GetBlogPostsInput
{
CategoryId = categoryId,
MaxResultCount = input.MaxResultCount,
SkipCount = input.SkipCount,
Sorting = input.Sorting
};
return await GetPostsAsync(searchInput);
}
public async Task<PagedResultDto<BlogPostListDto>> GetPostsByTagAsync(string tag, PagedAndSortedResultRequestDto input)
{
var searchInput = new GetBlogPostsInput
{
Tag = tag,
MaxResultCount = input.MaxResultCount,
SkipCount = input.SkipCount,
Sorting = input.Sorting
};
return await GetPostsAsync(searchInput);
}
public async Task<PagedResultDto<BlogPostListDto>> GetPostsByAuthorAsync(Guid authorId, PagedAndSortedResultRequestDto input)
{
var searchInput = new GetBlogPostsInput
{
AuthorId = authorId,
MaxResultCount = input.MaxResultCount,
SkipCount = input.SkipCount,
Sorting = input.Sorting
};
return await GetPostsAsync(searchInput);
}
// Tags
[AllowAnonymous]
public async Task<List<string>> GetPopularTagsAsync(int count = 20)
{
var tags = await _tagRepository.GetListAsync();
return tags
.GroupBy(x => x.Tag)
.OrderByDescending(g => g.Count())
.Take(count)
.Select(g => g.Key)
.ToList();
}
// Stats
[AllowAnonymous]
public async Task<BlogStatsDto> GetStatsAsync()
{
var stats = new BlogStatsDto
{
TotalPosts = (int)await _postRepository.CountAsync(),
PublishedPosts = (int)await _postRepository.CountAsync(x => x.IsPublished),
DraftPosts = (int)await _postRepository.CountAsync(x => !x.IsPublished),
TotalCategories = (int)await _categoryRepository.CountAsync(x => x.IsActive),
TotalViews = (await _postRepository.GetListAsync()).Sum(x => x.ViewCount),
TotalLikes = (await _postRepository.GetListAsync()).Sum(x => x.LikeCount),
TotalComments = 0, // You should implement comment count
PopularTags = await GetPopularTagsAsync(10)
};
// Get latest post
var latestPost = await _postRepository
.GetQueryableAsync()
.ContinueWith(async t =>
{
var query = await t;
return await AsyncExecuter.FirstOrDefaultAsync(
query.Where(x => x.IsPublished).OrderByDescending(x => x.CreationTime)
);
})
.Unwrap();
if (latestPost != null)
{
var dto = ObjectMapper.Map<BlogPost, BlogPostListDto>(latestPost);
// Get category
var category = await _categoryRepository.GetAsync(latestPost.CategoryId);
dto.Category = ObjectMapper.Map<BlogCategory, BlogCategoryDto>(category);
stats.LatestPost = dto;
}
// Get most viewed post
var mostViewedPost = await _postRepository
.GetQueryableAsync()
.ContinueWith(async t =>
{
var query = await t;
return await AsyncExecuter.FirstOrDefaultAsync(
query.Where(x => x.IsPublished).OrderByDescending(x => x.ViewCount)
);
})
.Unwrap();
if (mostViewedPost != null)
{
var dto = ObjectMapper.Map<BlogPost, BlogPostListDto>(mostViewedPost);
// Get category
var category = await _categoryRepository.GetAsync(mostViewedPost.CategoryId);
dto.Category = ObjectMapper.Map<BlogCategory, BlogCategoryDto>(category);
stats.MostViewedPost = dto;
}
return stats;
}
} }
} }

View file

@ -83,7 +83,7 @@ namespace Kurs.Platform.Forum
post.Author = new AuthorDto post.Author = new AuthorDto
{ {
Id = topic.AuthorId, Id = topic.AuthorId,
Name = topic.CreatorId.HasValue ? "User" : "Unknown" Name = "User"
}; };
// Get tags // Get tags
@ -219,7 +219,7 @@ namespace Kurs.Platform.Forum
dto.Author = new AuthorDto dto.Author = new AuthorDto
{ {
Id = topic.AuthorId, Id = topic.AuthorId,
Name = topic.CreatorId.HasValue ? "User" : "Unknown" // You should get actual user name Name = "User"
}; };
// Get tags // Get tags
@ -246,7 +246,7 @@ namespace Kurs.Platform.Forum
dto.Author = new AuthorDto dto.Author = new AuthorDto
{ {
Id = topic.AuthorId, Id = topic.AuthorId,
Name = topic.CreatorId.HasValue ? "User" : "Unknown" // You should get actual user name Name = "User"
}; };
// Get tags // Get tags
@ -514,7 +514,7 @@ namespace Kurs.Platform.Forum
dto.Author = new AuthorDto dto.Author = new AuthorDto
{ {
Id = topic.AuthorId, Id = topic.AuthorId,
Name = topic.CreatorId.HasValue ? "User" : "Unknown" Name = "User"
}; };
// Get tags // Get tags

View file

@ -565,7 +565,7 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
) )
{ {
DisplayOrder = item.DisplayOrder, DisplayOrder = item.DisplayOrder,
PostCount = 1 PostCount = item.PostCount
}; };
await _blogCategoryRepository.InsertAsync(newCategory); await _blogCategoryRepository.InsertAsync(newCategory);

View file

@ -4631,6 +4631,150 @@
"key": "AI.AnalizAnswer", "key": "AI.AnalizAnswer",
"en": "Analysis and Evaluation", "en": "Analysis and Evaluation",
"tr": "Analiz & Değerlendirme" "tr": "Analiz & Değerlendirme"
},
{
"resourceName": "Platform",
"key": "blog.posts.ai.title",
"tr": "Yapay zekanın iş süreçlerine etkileri ve geleceği...",
"en": "Artificial Intelligence and Business"
},
{
"resourceName": "Platform",
"key": "blog.posts.ai.excerpt",
"tr": "Yapay zekanın iş süreçlerine etkileri ve geleceği...",
"en": "The impacts and future of AI in business processes..."
},
{
"resourceName": "Platform",
"key": "blog.posts.ai.date",
"tr": "10 Mayıs 2024",
"en": "May 10, 2024"
},
{
"resourceName": "Platform",
"key": "blog.categories.technology",
"tr": "Teknoloji",
"en": "Technology"
},
{
"resourceName": "Platform",
"key": "blog.posts.web.title",
"tr": "Web Geliştirmede Son Trendler",
"en": "Latest Trends in Web Development"
},
{
"resourceName": "Platform",
"key": "blog.posts.web.excerpt",
"tr": "Modern web geliştirme teknikleri ve araçları...",
"en": "Modern web development techniques and tools..."
},
{
"resourceName": "Platform",
"key": "blog.posts.web.date",
"tr": "5 Mayıs 2024",
"en": "May 5, 2024"
},
{
"resourceName": "Platform",
"key": "blog.categories.webdev",
"tr": "Web Geliştirme",
"en": "Web Development"
},
{
"resourceName": "Platform",
"key": "blog.posts.security.title",
"tr": "Siber Güvenlik İpuçları",
"en": "Cybersecurity Tips"
},
{
"resourceName": "Platform",
"key": "blog.posts.security.excerpt",
"tr": "İşletmenizi siber tehditlere karşı koruyun...",
"en": "Protect your business against cyber threats..."
},
{
"resourceName": "Platform",
"key": "blog.posts.security.date",
"tr": "1 Mayıs 2024",
"en": "May 1, 2024"
},
{
"resourceName": "Platform",
"key": "blog.categories.security",
"tr": "Siber Güvenlik",
"en": "Cybersecurity"
},
{
"resourceName": "Platform",
"key": "blog.posts.mobile.title",
"tr": "Mobil Uygulama Geliştirme Rehberi",
"en": "Mobile App Development Guide"
},
{
"resourceName": "Platform",
"key": "blog.posts.mobile.excerpt",
"tr": "Başarılı bir mobil uygulama geliştirmek için ipuçları...",
"en": "Tips for building a successful mobile app..."
},
{
"resourceName": "Platform",
"key": "blog.posts.mobile.date",
"tr": "25 Nisan 2024",
"en": "April 25, 2024"
},
{
"resourceName": "Platform",
"key": "blog.categories.mobile",
"tr": "Mobil",
"en": "Mobile"
},
{
"resourceName": "Platform",
"key": "blog.posts.database.title",
"tr": "Veritabanı Yönetimi Temelleri",
"en": "Fundamentals of Database Management"
},
{
"resourceName": "Platform",
"key": "blog.posts.database.excerpt",
"tr": "Veritabanı tasarımı ve optimizasyonu...",
"en": "Database design and optimization..."
},
{
"resourceName": "Platform",
"key": "blog.posts.database.date",
"tr": "20 Nisan 2024",
"en": "April 20, 2024"
},
{
"resourceName": "Platform",
"key": "blog.categories.database",
"tr": "Veritabanı",
"en": "Database"
},
{
"resourceName": "Platform",
"key": "blog.posts.digital.title",
"tr": "Dijital Pazarlama Stratejileri",
"en": "Digital Marketing Strategies"
},
{
"resourceName": "Platform",
"key": "blog.posts.digital.excerpt",
"tr": "İşletmenizi dijital dünyada büyütün...",
"en": "Grow your business in the digital world..."
},
{
"resourceName": "Platform",
"key": "blog.posts.digital.date",
"tr": "blog.posts.digital.date",
"en": "April 15, 2024"
},
{
"resourceName": "Platform",
"key": "blog.categories.digital",
"tr": "Dijital Pazarlama",
"en": "Digital Marketing"
} }
], ],
"Settings": [ "Settings": [
@ -4640,10 +4784,7 @@
"descriptionKey": "Abp.Localization.DefaultLanguage.Description", "descriptionKey": "Abp.Localization.DefaultLanguage.Description",
"defaultValue": "en", "defaultValue": "en",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "App.SiteManagement", "mainGroupKey": "App.SiteManagement",
@ -4677,10 +4818,7 @@
"descriptionKey": "Abp.Localization.Timezone.Description", "descriptionKey": "Abp.Localization.Timezone.Description",
"defaultValue": "UTC", "defaultValue": "UTC",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "App.SiteManagement", "mainGroupKey": "App.SiteManagement",
@ -4836,11 +4974,7 @@
"descriptionKey": "App.SiteManagement.Theme.Style.Description", "descriptionKey": "App.SiteManagement.Theme.Style.Description",
"defaultValue": "dx.light.compact", "defaultValue": "dx.light.compact",
"isVisibleToClients": true, "isVisibleToClients": true,
"providers": [ "providers": ["U", "G", "D"],
"U",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "App.SiteManagement", "mainGroupKey": "App.SiteManagement",
@ -4888,10 +5022,7 @@
"descriptionKey": "App.SiteManagement.General.NewMemberNotificationEmails.Description", "descriptionKey": "App.SiteManagement.General.NewMemberNotificationEmails.Description",
"defaultValue": "system@sozsoft.com", "defaultValue": "system@sozsoft.com",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "App.SiteManagement", "mainGroupKey": "App.SiteManagement",
@ -4907,10 +5038,7 @@
"descriptionKey": "App.SiteManagement.General.TimedLoginEmails.Description", "descriptionKey": "App.SiteManagement.General.TimedLoginEmails.Description",
"defaultValue": "system@sozsoft.com", "defaultValue": "system@sozsoft.com",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "App.SiteManagement", "mainGroupKey": "App.SiteManagement",
@ -4926,11 +5054,7 @@
"descriptionKey": "App.Sender.Sms.PostaGuvercini.Url.Description", "descriptionKey": "App.Sender.Sms.PostaGuvercini.Url.Description",
"defaultValue": "https://www.postaguvercini.com/api_http", "defaultValue": "https://www.postaguvercini.com/api_http",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "App.Sender", "mainGroupKey": "App.Sender",
@ -4946,11 +5070,7 @@
"descriptionKey": "App.Sender.Sms.PostaGuvercini.Username.Description", "descriptionKey": "App.Sender.Sms.PostaGuvercini.Username.Description",
"defaultValue": "2AIlj4QlCrvlbDDBS/712A==", "defaultValue": "2AIlj4QlCrvlbDDBS/712A==",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": true, "isEncrypted": true,
"mainGroupKey": "App.Sender", "mainGroupKey": "App.Sender",
@ -4966,11 +5086,7 @@
"descriptionKey": "App.Sender.Sms.PostaGuvercini.Password.Description", "descriptionKey": "App.Sender.Sms.PostaGuvercini.Password.Description",
"defaultValue": "oTuwyZM9sxfJI+jDH5wJAw==", "defaultValue": "oTuwyZM9sxfJI+jDH5wJAw==",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": true, "isEncrypted": true,
"mainGroupKey": "App.Sender", "mainGroupKey": "App.Sender",
@ -4986,11 +5102,7 @@
"descriptionKey": "App.Sender.WhatsApp.Url.Description", "descriptionKey": "App.Sender.WhatsApp.Url.Description",
"defaultValue": "https://graph.facebook.com/v21.0", "defaultValue": "https://graph.facebook.com/v21.0",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "App.Sender", "mainGroupKey": "App.Sender",
@ -5006,11 +5118,7 @@
"descriptionKey": "App.Sender.WhatsApp.PhoneNumberId.Description", "descriptionKey": "App.Sender.WhatsApp.PhoneNumberId.Description",
"defaultValue": "442035112335974", "defaultValue": "442035112335974",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "App.Sender", "mainGroupKey": "App.Sender",
@ -5026,11 +5134,7 @@
"descriptionKey": "App.Sender.WhatsApp.Token.Description", "descriptionKey": "App.Sender.WhatsApp.Token.Description",
"defaultValue": "EAANoftqZAJ64BO5oPwXPqniUtNGF70u8TKvQVzGZBaYQh5UY8fYrgQkcXP9UbQUqT9PWRah1L7TzcBIiWQMacT8AkmZB33AP1begLoywIZCsQSdBSUz21GQaCowfVosYgBoXSyqH8irSBPQDLIjxxVxrC2n76SD9X6zPXeHgOqIPY92DqJXplstWrlhtZCAZDZD", "defaultValue": "EAANoftqZAJ64BO5oPwXPqniUtNGF70u8TKvQVzGZBaYQh5UY8fYrgQkcXP9UbQUqT9PWRah1L7TzcBIiWQMacT8AkmZB33AP1begLoywIZCsQSdBSUz21GQaCowfVosYgBoXSyqH8irSBPQDLIjxxVxrC2n76SD9X6zPXeHgOqIPY92DqJXplstWrlhtZCAZDZD",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "App.Sender", "mainGroupKey": "App.Sender",
@ -5046,11 +5150,7 @@
"descriptionKey": "App.Sender.WhatsApp.TemplateName.Description", "descriptionKey": "App.Sender.WhatsApp.TemplateName.Description",
"defaultValue": "kurs_platform_notification", "defaultValue": "kurs_platform_notification",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "App.Sender", "mainGroupKey": "App.Sender",
@ -5066,10 +5166,7 @@
"descriptionKey": "App.Sender.Rocket.Url.Description", "descriptionKey": "App.Sender.Rocket.Url.Description",
"defaultValue": "https://chat.sozsoft.com/api/v1", "defaultValue": "https://chat.sozsoft.com/api/v1",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "App.Sender", "mainGroupKey": "App.Sender",
@ -5085,10 +5182,7 @@
"descriptionKey": "App.Sender.Rocket.UserId.Description", "descriptionKey": "App.Sender.Rocket.UserId.Description",
"defaultValue": "LfpzPjzag4QJXm84N", "defaultValue": "LfpzPjzag4QJXm84N",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "App.Sender", "mainGroupKey": "App.Sender",
@ -5104,10 +5198,7 @@
"descriptionKey": "App.Sender.Rocket.Token.Description", "descriptionKey": "App.Sender.Rocket.Token.Description",
"defaultValue": "jvqALawvXn0Q7c6FfHJV3h58DCHDfQLgFF5y7oIc7oc", "defaultValue": "jvqALawvXn0Q7c6FfHJV3h58DCHDfQLgFF5y7oIc7oc",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "App.Sender", "mainGroupKey": "App.Sender",
@ -5123,11 +5214,7 @@
"descriptionKey": "Abp.Mailing.DefaultFromDisplayName.Description", "descriptionKey": "Abp.Mailing.DefaultFromDisplayName.Description",
"defaultValue": "Kurs", "defaultValue": "Kurs",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Mailing", "mainGroupKey": "Abp.Mailing",
@ -5143,11 +5230,7 @@
"descriptionKey": "Abp.Mailing.DefaultFromAddress.Description", "descriptionKey": "Abp.Mailing.DefaultFromAddress.Description",
"defaultValue": "system@sozsoft.com", "defaultValue": "system@sozsoft.com",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Mailing", "mainGroupKey": "Abp.Mailing",
@ -5163,11 +5246,7 @@
"descriptionKey": "Abp.Mailing.Smtp.UserName.Description", "descriptionKey": "Abp.Mailing.Smtp.UserName.Description",
"defaultValue": "system@sozsoft.com", "defaultValue": "system@sozsoft.com",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Mailing", "mainGroupKey": "Abp.Mailing",
@ -5183,11 +5262,7 @@
"descriptionKey": "Abp.Mailing.Smtp.Password.Description", "descriptionKey": "Abp.Mailing.Smtp.Password.Description",
"defaultValue": "QT9L7BCl1CT/1Hq19HoSlQ==", "defaultValue": "QT9L7BCl1CT/1Hq19HoSlQ==",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": true, "isEncrypted": true,
"mainGroupKey": "Abp.Mailing", "mainGroupKey": "Abp.Mailing",
@ -5203,11 +5278,7 @@
"descriptionKey": "Abp.Mailing.Smtp.Host.Description", "descriptionKey": "Abp.Mailing.Smtp.Host.Description",
"defaultValue": "127.0.0.1", "defaultValue": "127.0.0.1",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Mailing", "mainGroupKey": "Abp.Mailing",
@ -5223,11 +5294,7 @@
"descriptionKey": "Abp.Mailing.Smtp.Port.Description", "descriptionKey": "Abp.Mailing.Smtp.Port.Description",
"defaultValue": "25", "defaultValue": "25",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Mailing", "mainGroupKey": "Abp.Mailing",
@ -5243,11 +5310,7 @@
"descriptionKey": "Abp.Mailing.Smtp.Domain.Description", "descriptionKey": "Abp.Mailing.Smtp.Domain.Description",
"defaultValue": "sozsoft.com", "defaultValue": "sozsoft.com",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Mailing", "mainGroupKey": "Abp.Mailing",
@ -5263,11 +5326,7 @@
"descriptionKey": "Abp.Mailing.Smtp.EnableSsl.Description", "descriptionKey": "Abp.Mailing.Smtp.EnableSsl.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Mailing", "mainGroupKey": "Abp.Mailing",
@ -5283,11 +5342,7 @@
"descriptionKey": "Abp.Mailing.AWS.Profile.Description", "descriptionKey": "Abp.Mailing.AWS.Profile.Description",
"defaultValue": "mail-sdk-user", "defaultValue": "mail-sdk-user",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Mailing", "mainGroupKey": "Abp.Mailing",
@ -5303,11 +5358,7 @@
"descriptionKey": "Abp.Mailing.AWS.Region.Description", "descriptionKey": "Abp.Mailing.AWS.Region.Description",
"defaultValue": "eu-central-1", "defaultValue": "eu-central-1",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Mailing", "mainGroupKey": "Abp.Mailing",
@ -5323,11 +5374,7 @@
"descriptionKey": "Abp.Mailing.AWS.AccessKey.Description", "descriptionKey": "Abp.Mailing.AWS.AccessKey.Description",
"defaultValue": "aXW8L21rP6dPO6Txj76Be2FCpWRBa25EMrSAVL76", "defaultValue": "aXW8L21rP6dPO6Txj76Be2FCpWRBa25EMrSAVL76",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Mailing", "mainGroupKey": "Abp.Mailing",
@ -5343,11 +5390,7 @@
"descriptionKey": "Abp.Mailing.AWS.AccessKeyId.Description", "descriptionKey": "Abp.Mailing.AWS.AccessKeyId.Description",
"defaultValue": "AKIATULUYBLX4IY3S2P1", "defaultValue": "AKIATULUYBLX4IY3S2P1",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Mailing", "mainGroupKey": "Abp.Mailing",
@ -5363,10 +5406,7 @@
"descriptionKey": "Abp.Account.IsSelfRegistrationEnabled.Description", "descriptionKey": "Abp.Account.IsSelfRegistrationEnabled.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Account", "mainGroupKey": "Abp.Account",
@ -5382,10 +5422,7 @@
"descriptionKey": "Abp.Account.EnableLocalLogin.Description", "descriptionKey": "Abp.Account.EnableLocalLogin.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Account", "mainGroupKey": "Abp.Account",
@ -5401,11 +5438,7 @@
"descriptionKey": "Abp.Account.TwoFactor.Enabled.Description", "descriptionKey": "Abp.Account.TwoFactor.Enabled.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Account", "mainGroupKey": "Abp.Account",
@ -5421,10 +5454,7 @@
"descriptionKey": "Abp.Account.Captcha.MaxFailedAccessAttempts.Description", "descriptionKey": "Abp.Account.Captcha.MaxFailedAccessAttempts.Description",
"defaultValue": "3", "defaultValue": "3",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Account", "mainGroupKey": "Abp.Account",
@ -5440,10 +5470,7 @@
"descriptionKey": "Abp.Account.Captcha.EndPoint.Description", "descriptionKey": "Abp.Account.Captcha.EndPoint.Description",
"defaultValue": "https://challenges.cloudflare.com/turnstile/v0/siteverify", "defaultValue": "https://challenges.cloudflare.com/turnstile/v0/siteverify",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Account", "mainGroupKey": "Abp.Account",
@ -5459,10 +5486,7 @@
"descriptionKey": "Abp.Account.Captcha.SiteKey.Description", "descriptionKey": "Abp.Account.Captcha.SiteKey.Description",
"defaultValue": "0x4AAAAAAAGadwQME-GSYuJU", "defaultValue": "0x4AAAAAAAGadwQME-GSYuJU",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Account", "mainGroupKey": "Abp.Account",
@ -5478,10 +5502,7 @@
"descriptionKey": "Abp.Account.Captcha.SecretKey.Description", "descriptionKey": "Abp.Account.Captcha.SecretKey.Description",
"defaultValue": "0x4AAAAAAAGad_f_WP47IcNBs9FTu5DhNX8", "defaultValue": "0x4AAAAAAAGad_f_WP47IcNBs9FTu5DhNX8",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Account", "mainGroupKey": "Abp.Account",
@ -5497,11 +5518,7 @@
"descriptionKey": "Abp.Identity.Profile.General.RequireVerifiedAccount.Description", "descriptionKey": "Abp.Identity.Profile.General.RequireVerifiedAccount.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5517,11 +5534,7 @@
"descriptionKey": "Abp.Identity.Profile.General.BlacklistedEmailProviders.Description", "descriptionKey": "Abp.Identity.Profile.General.BlacklistedEmailProviders.Description",
"defaultValue": "gmail.com\r\nyahoo.com\r\nhotmail.com", "defaultValue": "gmail.com\r\nyahoo.com\r\nhotmail.com",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5537,11 +5550,7 @@
"descriptionKey": "Abp.Identity.Password.ForceUsersToPeriodicallyChangePassword.Description", "descriptionKey": "Abp.Identity.Password.ForceUsersToPeriodicallyChangePassword.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5557,11 +5566,7 @@
"descriptionKey": "Abp.Identity.Password.PasswordChangePeriodDays.Description", "descriptionKey": "Abp.Identity.Password.PasswordChangePeriodDays.Description",
"defaultValue": "0", "defaultValue": "0",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5577,11 +5582,7 @@
"descriptionKey": "Abp.Identity.Password.RequiredLength.Description", "descriptionKey": "Abp.Identity.Password.RequiredLength.Description",
"defaultValue": "6", "defaultValue": "6",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5597,11 +5598,7 @@
"descriptionKey": "Abp.Identity.Password.RequiredUniqueChars.Description", "descriptionKey": "Abp.Identity.Password.RequiredUniqueChars.Description",
"defaultValue": "1", "defaultValue": "1",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5617,11 +5614,7 @@
"descriptionKey": "Abp.Identity.Password.RequireNonAlphanumeric.Description", "descriptionKey": "Abp.Identity.Password.RequireNonAlphanumeric.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5637,11 +5630,7 @@
"descriptionKey": "Abp.Identity.Password.RequireLowercase.Description", "descriptionKey": "Abp.Identity.Password.RequireLowercase.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5657,11 +5646,7 @@
"descriptionKey": "Abp.Identity.Password.RequireUppercase.Description", "descriptionKey": "Abp.Identity.Password.RequireUppercase.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5677,11 +5662,7 @@
"descriptionKey": "Abp.Identity.Password.RequireDigit.Description", "descriptionKey": "Abp.Identity.Password.RequireDigit.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5697,11 +5678,7 @@
"descriptionKey": "Abp.Identity.Lockout.AllowedForNewUsers.Description", "descriptionKey": "Abp.Identity.Lockout.AllowedForNewUsers.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5717,11 +5694,7 @@
"descriptionKey": "Abp.Identity.Lockout.LockoutDuration.Description", "descriptionKey": "Abp.Identity.Lockout.LockoutDuration.Description",
"defaultValue": "300", "defaultValue": "300",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5737,11 +5710,7 @@
"descriptionKey": "Abp.Identity.Lockout.MaxFailedAccessAttempts.Description", "descriptionKey": "Abp.Identity.Lockout.MaxFailedAccessAttempts.Description",
"defaultValue": "5", "defaultValue": "5",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["T", "G", "D"],
"T",
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5757,10 +5726,7 @@
"descriptionKey": "Abp.Identity.SignIn.RequireConfirmedEmail.Description", "descriptionKey": "Abp.Identity.SignIn.RequireConfirmedEmail.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5776,10 +5742,7 @@
"descriptionKey": "Abp.Identity.SignIn.RequireConfirmedPhoneNumber.Description", "descriptionKey": "Abp.Identity.SignIn.RequireConfirmedPhoneNumber.Description",
"defaultValue": "False", "defaultValue": "False",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5795,10 +5758,7 @@
"descriptionKey": "Abp.Identity.User.IsUserNameUpdateEnabled.Description", "descriptionKey": "Abp.Identity.User.IsUserNameUpdateEnabled.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -5814,10 +5774,7 @@
"descriptionKey": "Abp.Identity.User.IsEmailUpdateEnabled.Description", "descriptionKey": "Abp.Identity.User.IsEmailUpdateEnabled.Description",
"defaultValue": "True", "defaultValue": "True",
"isVisibleToClients": false, "isVisibleToClients": false,
"providers": [ "providers": ["G", "D"],
"G",
"D"
],
"isInherited": false, "isInherited": false,
"isEncrypted": false, "isEncrypted": false,
"mainGroupKey": "Abp.Identity", "mainGroupKey": "Abp.Identity",
@ -6182,7 +6139,7 @@
"Code": "App.Blog", "Code": "App.Blog",
"DisplayName": "App.Blog", "DisplayName": "App.Blog",
"Order": 10, "Order": 10,
"Url": "/admin/blog/management", "Url": "/admin/blog",
"Icon": "FcTemplate", "Icon": "FcTemplate",
"RequiredPermissionName": "App.Blog", "RequiredPermissionName": "App.Blog",
"IsDisabled": false "IsDisabled": false
@ -6192,7 +6149,7 @@
"Code": "App.Forum", "Code": "App.Forum",
"DisplayName": "App.Forum", "DisplayName": "App.Forum",
"Order": 11, "Order": 11,
"Url": "/admin/forum/management", "Url": "/admin/forum",
"Icon": "FcReading", "Icon": "FcReading",
"RequiredPermissionName": "App.Forum", "RequiredPermissionName": "App.Forum",
"IsDisabled": false "IsDisabled": false
@ -7601,7 +7558,7 @@
"GroupName": "App.Blog", "GroupName": "App.Blog",
"Name": "App.Blog.Publish", "Name": "App.Blog.Publish",
"ParentName": "App.Blog", "ParentName": "App.Blog",
"DisplayName": "Export", "DisplayName": "Publish",
"IsEnabled": true, "IsEnabled": true,
"MultiTenancySide": 2 "MultiTenancySide": 2
}, },
@ -20083,42 +20040,48 @@
"Name": "blog.categories.technology", "Name": "blog.categories.technology",
"Slug": "ai-ve-gelecegi", "Slug": "ai-ve-gelecegi",
"Description": "blog.posts.ai.excerpt", "Description": "blog.posts.ai.excerpt",
"DisplayOrder": 1 "DisplayOrder": 1,
"PostCount": 1
}, },
{ {
"Id": "6d0ae65d-8b91-5bbf-879d-87ee25410458", "Id": "6d0ae65d-8b91-5bbf-879d-87ee25410458",
"Name": "blog.categories.webdev", "Name": "blog.categories.webdev",
"Slug": "web-gelistirmede-son-trendler", "Slug": "web-gelistirmede-son-trendler",
"Description": "blog.posts.web.excerpt", "Description": "blog.posts.web.excerpt",
"DisplayOrder": 2 "DisplayOrder": 2,
"PostCount": 1
}, },
{ {
"Id": "e938e6e6-f355-5807-a7f7-f0d4fe368fc5", "Id": "e938e6e6-f355-5807-a7f7-f0d4fe368fc5",
"Name": "blog.categories.security", "Name": "blog.categories.security",
"Slug": "siber-guvenlik-tehditleri-ve-korunma-yollari", "Slug": "siber-guvenlik-tehditleri-ve-korunma-yollari",
"Description": "blog.posts.security.excerpt", "Description": "blog.posts.security.excerpt",
"DisplayOrder": 3 "DisplayOrder": 3,
"PostCount": 1
}, },
{ {
"Id": "ebd1b9aa-14ce-5c0b-9514-a4cbd5a7cf94", "Id": "ebd1b9aa-14ce-5c0b-9514-a4cbd5a7cf94",
"Name": "blog.categories.mobile", "Name": "blog.categories.mobile",
"Slug": "mobil-uygulama-gelistirmede-cross-platform-cozumler", "Slug": "mobil-uygulama-gelistirmede-cross-platform-cozumler",
"Description": "blog.posts.mobile.excerpt", "Description": "blog.posts.mobile.excerpt",
"DisplayOrder": 4 "DisplayOrder": 4,
"PostCount": 1
}, },
{ {
"Id": "741ef542-0591-5472-9bf2-593047eb4122", "Id": "741ef542-0591-5472-9bf2-593047eb4122",
"Name": "blog.categories.database", "Name": "blog.categories.database",
"Slug": "veritabani-yonetim-sistemleri-karsilastirmasi", "Slug": "veritabani-yonetim-sistemleri-karsilastirmasi",
"Description": "blog.posts.database.excerpt", "Description": "blog.posts.database.excerpt",
"DisplayOrder": 5 "DisplayOrder": 5,
"PostCount": 1
}, },
{ {
"Id": "dbc8578c-1a99-594a-8997-bddd0eac8571", "Id": "dbc8578c-1a99-594a-8997-bddd0eac8571",
"Name": "blog.categories.digital", "Name": "blog.categories.digital",
"Slug": "dijital-pazarlamada-veri-analizi", "Slug": "dijital-pazarlamada-veri-analizi",
"Description": "blog.posts.digital.excerpt", "Description": "blog.posts.digital.excerpt",
"DisplayOrder": 6 "DisplayOrder": 6,
"PostCount": 1
} }
], ],
"BlogPosts": [ "BlogPosts": [

View file

@ -207,6 +207,7 @@ public class BlogCategorySeedDto
public string Slug { get; set; } public string Slug { get; set; }
public string Description { get; set; } public string Description { get; set; }
public int DisplayOrder { get; set; } public int DisplayOrder { get; set; }
public int PostCount { get; set; }
} }
public class BlogPostSeedDto public class BlogPostSeedDto

View file

@ -1,57 +0,0 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Kurs.Platform.Blog
{
public class BlogComment : FullAuditedEntity<Guid>, IMultiTenant
{
public Guid? TenantId { get; set; }
public Guid PostId { get; set; }
public virtual BlogPost Post { get; set; }
public string Content { get; set; }
public Guid AuthorId { get; set; }
public Guid? ParentId { get; set; }
public virtual BlogComment Parent { get; set; }
public virtual ICollection<BlogComment> Replies { get; set; }
public virtual ICollection<BlogCommentLike> Likes { get; set; }
public int LikeCount { get; set; }
protected BlogComment()
{
Replies = new HashSet<BlogComment>();
Likes = new HashSet<BlogCommentLike>();
}
public BlogComment(
Guid id,
Guid postId,
string content,
Guid authorId,
Guid? parentId = null,
Guid? tenantId = null) : base(id)
{
PostId = postId;
Content = content;
AuthorId = authorId;
ParentId = parentId;
TenantId = tenantId;
LikeCount = 0;
Replies = new HashSet<BlogComment>();
Likes = new HashSet<BlogCommentLike>();
}
public void UpdateLikeCount(int count)
{
LikeCount = count;
}
}
}

View file

@ -1,31 +0,0 @@
using System;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Kurs.Platform.Blog
{
public class BlogCommentLike : CreationAuditedEntity<Guid>, IMultiTenant
{
public Guid? TenantId { get; set; }
public Guid CommentId { get; set; }
public virtual BlogComment Comment { get; set; }
public Guid UserId { get; set; }
protected BlogCommentLike()
{
}
public BlogCommentLike(
Guid id,
Guid commentId,
Guid userId,
Guid? tenantId = null) : base(id)
{
CommentId = commentId;
UserId = userId;
TenantId = tenantId;
}
}
}

View file

@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
@ -28,15 +27,8 @@ namespace Kurs.Platform.Blog
public bool IsPublished { get; set; } public bool IsPublished { get; set; }
public DateTime? PublishedAt { get; set; } public DateTime? PublishedAt { get; set; }
public virtual ICollection<BlogPostTag> Tags { get; set; }
public virtual ICollection<BlogComment> Comments { get; set; }
public virtual ICollection<BlogPostLike> Likes { get; set; }
protected BlogPost() protected BlogPost()
{ {
Tags = new HashSet<BlogPostTag>();
Comments = new HashSet<BlogComment>();
Likes = new HashSet<BlogPostLike>();
} }
public BlogPost( public BlogPost(
@ -65,10 +57,6 @@ namespace Kurs.Platform.Blog
LikeCount = 0; LikeCount = 0;
CommentCount = 0; CommentCount = 0;
IsPublished = false; IsPublished = false;
Tags = new HashSet<BlogPostTag>();
Comments = new HashSet<BlogComment>();
Likes = new HashSet<BlogPostLike>();
} }
public void Publish() public void Publish()
@ -82,20 +70,5 @@ namespace Kurs.Platform.Blog
IsPublished = false; IsPublished = false;
PublishedAt = null; PublishedAt = null;
} }
public void IncrementViewCount()
{
ViewCount++;
}
public void UpdateLikeCount(int count)
{
LikeCount = count;
}
public void UpdateCommentCount(int count)
{
CommentCount = count;
}
} }
} }

View file

@ -1,31 +0,0 @@
using System;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Kurs.Platform.Blog
{
public class BlogPostLike : CreationAuditedEntity<Guid>, IMultiTenant
{
public Guid? TenantId { get; set; }
public Guid PostId { get; set; }
public virtual BlogPost Post { get; set; }
public Guid UserId { get; set; }
protected BlogPostLike()
{
}
public BlogPostLike(
Guid id,
Guid postId,
Guid userId,
Guid? tenantId = null) : base(id)
{
PostId = postId;
UserId = userId;
TenantId = tenantId;
}
}
}

View file

@ -1,31 +0,0 @@
using System;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Kurs.Platform.Blog
{
public class BlogPostTag : Entity<Guid>, IMultiTenant
{
public Guid? TenantId { get; set; }
public Guid PostId { get; set; }
public virtual BlogPost Post { get; set; }
public string Tag { get; set; }
protected BlogPostTag()
{
}
public BlogPostTag(
Guid id,
Guid postId,
string tag,
Guid? tenantId = null) : base(id)
{
PostId = postId;
Tag = tag;
TenantId = tenantId;
}
}
}

View file

@ -62,10 +62,6 @@ public class PlatformDbContext :
// Blog Entities // Blog Entities
public DbSet<BlogPost> BlogPosts { get; set; } public DbSet<BlogPost> BlogPosts { get; set; }
public DbSet<BlogCategory> BlogCategories { get; set; } public DbSet<BlogCategory> BlogCategories { get; set; }
public DbSet<BlogComment> BlogComments { get; set; }
public DbSet<BlogPostTag> BlogPostTags { get; set; }
public DbSet<BlogPostLike> BlogPostLikes { get; set; }
public DbSet<BlogCommentLike> BlogCommentLikes { get; set; }
// Forum Entities // Forum Entities
public DbSet<ForumCategory> ForumCategories { get; set; } public DbSet<ForumCategory> ForumCategories { get; set; }
@ -443,66 +439,6 @@ public class PlatformDbContext :
.OnDelete(DeleteBehavior.Restrict); .OnDelete(DeleteBehavior.Restrict);
}); });
builder.Entity<BlogComment>(b =>
{
b.ToTable(PlatformConsts.DbTablePrefix + "BlogComments", PlatformConsts.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Content).IsRequired().HasMaxLength(2048);
b.HasOne(x => x.Post)
.WithMany(x => x.Comments)
.HasForeignKey(x => x.PostId)
.OnDelete(DeleteBehavior.Cascade);
b.HasOne(x => x.Parent)
.WithMany(x => x.Replies)
.HasForeignKey(x => x.ParentId)
.OnDelete(DeleteBehavior.Restrict);
});
builder.Entity<BlogPostTag>(b =>
{
b.ToTable(PlatformConsts.DbTablePrefix + "BlogPostTags", PlatformConsts.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Tag).IsRequired().HasMaxLength(64);
b.HasIndex(x => new { x.PostId, x.Tag }).IsUnique();
b.HasIndex(x => x.Tag);
b.HasOne(x => x.Post)
.WithMany(x => x.Tags)
.HasForeignKey(x => x.PostId)
.OnDelete(DeleteBehavior.Cascade);
});
builder.Entity<BlogPostLike>(b =>
{
b.ToTable(PlatformConsts.DbTablePrefix + "BlogPostLikes", PlatformConsts.DbSchema);
b.ConfigureByConvention();
b.HasIndex(x => new { x.PostId, x.UserId }).IsUnique();
b.HasOne(x => x.Post)
.WithMany(x => x.Likes)
.HasForeignKey(x => x.PostId)
.OnDelete(DeleteBehavior.Cascade);
});
builder.Entity<BlogCommentLike>(b =>
{
b.ToTable(PlatformConsts.DbTablePrefix + "BlogCommentLikes", PlatformConsts.DbSchema);
b.ConfigureByConvention();
b.HasIndex(x => new { x.CommentId, x.UserId }).IsUnique();
b.HasOne(x => x.Comment)
.WithMany(x => x.Likes)
.HasForeignKey(x => x.CommentId)
.OnDelete(DeleteBehavior.Cascade);
});
// Forum Entity Configurations // Forum Entity Configurations
builder.Entity<ForumCategory>(b => builder.Entity<ForumCategory>(b =>
{ {

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Kurs.Platform.Migrations namespace Kurs.Platform.Migrations
{ {
[DbContext(typeof(PlatformDbContext))] [DbContext(typeof(PlatformDbContext))]
[Migration("20250619205823_AddBlogForumEntities")] [Migration("20250620094517_AddBlogForumEntities")]
partial class AddBlogForumEntities partial class AddBlogForumEntities
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -725,102 +725,6 @@ namespace Kurs.Platform.Migrations
b.ToTable("PBlogCategories", (string)null); b.ToTable("PBlogCategories", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Blog.BlogComment", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("AuthorId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Content")
.IsRequired()
.HasMaxLength(2048)
.HasColumnType("nvarchar(2048)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<int>("LikeCount")
.HasColumnType("int");
b.Property<Guid?>("ParentId")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("PostId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("ParentId");
b.HasIndex("PostId");
b.ToTable("PBlogComments", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogCommentLike", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("CommentId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<Guid>("UserId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("CommentId", "UserId")
.IsUnique();
b.ToTable("PBlogCommentLikes", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogPost", b => modelBuilder.Entity("Kurs.Platform.Blog.BlogPost", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -932,64 +836,6 @@ namespace Kurs.Platform.Migrations
b.ToTable("PBlogPosts", (string)null); b.ToTable("PBlogPosts", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Blog.BlogPostLike", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid>("PostId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<Guid>("UserId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("PostId", "UserId")
.IsUnique();
b.ToTable("PBlogPostLikes", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogPostTag", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("PostId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Tag")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("Tag");
b.HasIndex("PostId", "Tag")
.IsUnique();
b.ToTable("PBlogPostTags", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.AiBot", b => modelBuilder.Entity("Kurs.Platform.Entities.AiBot", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -5003,35 +4849,6 @@ namespace Kurs.Platform.Migrations
.OnDelete(DeleteBehavior.SetNull); .OnDelete(DeleteBehavior.SetNull);
}); });
modelBuilder.Entity("Kurs.Platform.Blog.BlogComment", b =>
{
b.HasOne("Kurs.Platform.Blog.BlogComment", "Parent")
.WithMany("Replies")
.HasForeignKey("ParentId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("Kurs.Platform.Blog.BlogPost", "Post")
.WithMany("Comments")
.HasForeignKey("PostId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Parent");
b.Navigation("Post");
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogCommentLike", b =>
{
b.HasOne("Kurs.Platform.Blog.BlogComment", "Comment")
.WithMany("Likes")
.HasForeignKey("CommentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Comment");
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogPost", b => modelBuilder.Entity("Kurs.Platform.Blog.BlogPost", b =>
{ {
b.HasOne("Kurs.Platform.Blog.BlogCategory", "Category") b.HasOne("Kurs.Platform.Blog.BlogCategory", "Category")
@ -5043,28 +4860,6 @@ namespace Kurs.Platform.Migrations
b.Navigation("Category"); b.Navigation("Category");
}); });
modelBuilder.Entity("Kurs.Platform.Blog.BlogPostLike", b =>
{
b.HasOne("Kurs.Platform.Blog.BlogPost", "Post")
.WithMany("Likes")
.HasForeignKey("PostId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Post");
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogPostTag", b =>
{
b.HasOne("Kurs.Platform.Blog.BlogPost", "Post")
.WithMany("Tags")
.HasForeignKey("PostId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Post");
});
modelBuilder.Entity("Kurs.Platform.Entities.BankAccount", b => modelBuilder.Entity("Kurs.Platform.Entities.BankAccount", b =>
{ {
b.HasOne("Kurs.Platform.Entities.Bank", "Bank") b.HasOne("Kurs.Platform.Entities.Bank", "Bank")
@ -5355,22 +5150,6 @@ namespace Kurs.Platform.Migrations
b.Navigation("Posts"); b.Navigation("Posts");
}); });
modelBuilder.Entity("Kurs.Platform.Blog.BlogComment", b =>
{
b.Navigation("Likes");
b.Navigation("Replies");
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogPost", b =>
{
b.Navigation("Comments");
b.Navigation("Likes");
b.Navigation("Tags");
});
modelBuilder.Entity("Kurs.Platform.Entities.Country", b => modelBuilder.Entity("Kurs.Platform.Entities.Country", b =>
{ {
b.Navigation("States"); b.Navigation("States");

View file

@ -148,84 +148,6 @@ namespace Kurs.Platform.Migrations
onDelete: ReferentialAction.Restrict); onDelete: ReferentialAction.Restrict);
}); });
migrationBuilder.CreateTable(
name: "PBlogComments",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
PostId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Content = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: false),
AuthorId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
ParentId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LikeCount = table.Column<int>(type: "int", nullable: false),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PBlogComments", x => x.Id);
table.ForeignKey(
name: "FK_PBlogComments_PBlogComments_ParentId",
column: x => x.ParentId,
principalTable: "PBlogComments",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_PBlogComments_PBlogPosts_PostId",
column: x => x.PostId,
principalTable: "PBlogPosts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "PBlogPostLikes",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
PostId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PBlogPostLikes", x => x.Id);
table.ForeignKey(
name: "FK_PBlogPostLikes_PBlogPosts_PostId",
column: x => x.PostId,
principalTable: "PBlogPosts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "PBlogPostTags",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
PostId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Tag = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PBlogPostTags", x => x.Id);
table.ForeignKey(
name: "FK_PBlogPostTags_PBlogPosts_PostId",
column: x => x.PostId,
principalTable: "PBlogPosts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "PForumPosts", name: "PForumPosts",
columns: table => new columns: table => new
@ -298,28 +220,6 @@ namespace Kurs.Platform.Migrations
onDelete: ReferentialAction.Cascade); onDelete: ReferentialAction.Cascade);
}); });
migrationBuilder.CreateTable(
name: "PBlogCommentLikes",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
CommentId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PBlogCommentLikes", x => x.Id);
table.ForeignKey(
name: "FK_PBlogCommentLikes_PBlogComments_CommentId",
column: x => x.CommentId,
principalTable: "PBlogComments",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "PForumPostLikes", name: "PForumPostLikes",
columns: table => new columns: table => new
@ -347,28 +247,6 @@ namespace Kurs.Platform.Migrations
table: "PBlogCategories", table: "PBlogCategories",
column: "Slug"); column: "Slug");
migrationBuilder.CreateIndex(
name: "IX_PBlogCommentLikes_CommentId_UserId",
table: "PBlogCommentLikes",
columns: new[] { "CommentId", "UserId" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PBlogComments_ParentId",
table: "PBlogComments",
column: "ParentId");
migrationBuilder.CreateIndex(
name: "IX_PBlogComments_PostId",
table: "PBlogComments",
column: "PostId");
migrationBuilder.CreateIndex(
name: "IX_PBlogPostLikes_PostId_UserId",
table: "PBlogPostLikes",
columns: new[] { "PostId", "UserId" },
unique: true);
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_PBlogPosts_CategoryId", name: "IX_PBlogPosts_CategoryId",
table: "PBlogPosts", table: "PBlogPosts",
@ -389,17 +267,6 @@ namespace Kurs.Platform.Migrations
table: "PBlogPosts", table: "PBlogPosts",
column: "Slug"); column: "Slug");
migrationBuilder.CreateIndex(
name: "IX_PBlogPostTags_PostId_Tag",
table: "PBlogPostTags",
columns: new[] { "PostId", "Tag" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PBlogPostTags_Tag",
table: "PBlogPostTags",
column: "Tag");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_PForumCategories_DisplayOrder", name: "IX_PForumCategories_DisplayOrder",
table: "PForumCategories", table: "PForumCategories",
@ -453,13 +320,7 @@ namespace Kurs.Platform.Migrations
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "PBlogCommentLikes"); name: "PBlogPosts");
migrationBuilder.DropTable(
name: "PBlogPostLikes");
migrationBuilder.DropTable(
name: "PBlogPostTags");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "PForumPostLikes"); name: "PForumPostLikes");
@ -471,20 +332,14 @@ namespace Kurs.Platform.Migrations
name: "PForumTopicTags"); name: "PForumTopicTags");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "PBlogComments"); name: "PBlogCategories");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "PForumPosts"); name: "PForumPosts");
migrationBuilder.DropTable(
name: "PBlogPosts");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "PForumTopics"); name: "PForumTopics");
migrationBuilder.DropTable(
name: "PBlogCategories");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "PForumCategories"); name: "PForumCategories");
} }

View file

@ -722,102 +722,6 @@ namespace Kurs.Platform.Migrations
b.ToTable("PBlogCategories", (string)null); b.ToTable("PBlogCategories", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Blog.BlogComment", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("AuthorId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Content")
.IsRequired()
.HasMaxLength(2048)
.HasColumnType("nvarchar(2048)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<int>("LikeCount")
.HasColumnType("int");
b.Property<Guid?>("ParentId")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("PostId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("ParentId");
b.HasIndex("PostId");
b.ToTable("PBlogComments", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogCommentLike", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("CommentId")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<Guid>("UserId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("CommentId", "UserId")
.IsUnique();
b.ToTable("PBlogCommentLikes", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogPost", b => modelBuilder.Entity("Kurs.Platform.Blog.BlogPost", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -929,64 +833,6 @@ namespace Kurs.Platform.Migrations
b.ToTable("PBlogPosts", (string)null); b.ToTable("PBlogPosts", (string)null);
}); });
modelBuilder.Entity("Kurs.Platform.Blog.BlogPostLike", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<Guid>("PostId")
.HasColumnType("uniqueidentifier");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<Guid>("UserId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("PostId", "UserId")
.IsUnique();
b.ToTable("PBlogPostLikes", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogPostTag", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("PostId")
.HasColumnType("uniqueidentifier");
b.Property<string>("Tag")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("nvarchar(64)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("Tag");
b.HasIndex("PostId", "Tag")
.IsUnique();
b.ToTable("PBlogPostTags", (string)null);
});
modelBuilder.Entity("Kurs.Platform.Entities.AiBot", b => modelBuilder.Entity("Kurs.Platform.Entities.AiBot", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -5000,35 +4846,6 @@ namespace Kurs.Platform.Migrations
.OnDelete(DeleteBehavior.SetNull); .OnDelete(DeleteBehavior.SetNull);
}); });
modelBuilder.Entity("Kurs.Platform.Blog.BlogComment", b =>
{
b.HasOne("Kurs.Platform.Blog.BlogComment", "Parent")
.WithMany("Replies")
.HasForeignKey("ParentId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("Kurs.Platform.Blog.BlogPost", "Post")
.WithMany("Comments")
.HasForeignKey("PostId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Parent");
b.Navigation("Post");
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogCommentLike", b =>
{
b.HasOne("Kurs.Platform.Blog.BlogComment", "Comment")
.WithMany("Likes")
.HasForeignKey("CommentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Comment");
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogPost", b => modelBuilder.Entity("Kurs.Platform.Blog.BlogPost", b =>
{ {
b.HasOne("Kurs.Platform.Blog.BlogCategory", "Category") b.HasOne("Kurs.Platform.Blog.BlogCategory", "Category")
@ -5040,28 +4857,6 @@ namespace Kurs.Platform.Migrations
b.Navigation("Category"); b.Navigation("Category");
}); });
modelBuilder.Entity("Kurs.Platform.Blog.BlogPostLike", b =>
{
b.HasOne("Kurs.Platform.Blog.BlogPost", "Post")
.WithMany("Likes")
.HasForeignKey("PostId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Post");
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogPostTag", b =>
{
b.HasOne("Kurs.Platform.Blog.BlogPost", "Post")
.WithMany("Tags")
.HasForeignKey("PostId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Post");
});
modelBuilder.Entity("Kurs.Platform.Entities.BankAccount", b => modelBuilder.Entity("Kurs.Platform.Entities.BankAccount", b =>
{ {
b.HasOne("Kurs.Platform.Entities.Bank", "Bank") b.HasOne("Kurs.Platform.Entities.Bank", "Bank")
@ -5352,22 +5147,6 @@ namespace Kurs.Platform.Migrations
b.Navigation("Posts"); b.Navigation("Posts");
}); });
modelBuilder.Entity("Kurs.Platform.Blog.BlogComment", b =>
{
b.Navigation("Likes");
b.Navigation("Replies");
});
modelBuilder.Entity("Kurs.Platform.Blog.BlogPost", b =>
{
b.Navigation("Comments");
b.Navigation("Likes");
b.Navigation("Tags");
});
modelBuilder.Entity("Kurs.Platform.Entities.Country", b => modelBuilder.Entity("Kurs.Platform.Entities.Country", b =>
{ {
b.Navigation("States"); b.Navigation("States");

View file

@ -1,203 +0,0 @@
export interface BlogPostContent {
title: string;
excerpt: string;
content: string;
date: string;
category: string;
readTime: string;
}
export interface BlogPosts {
[key: string]: {
tr: BlogPostContent;
en: BlogPostContent;
image: string;
author: string;
};
}
export const blogContent: BlogPosts = {
'ai-ve-gelecegi': {
tr: {
title: 'blog.posts.ai.title',
excerpt: 'blog.posts.ai.excerpt',
content: `Yapay zeka (YZ), günümüzün en hızlı gelişen ve dönüştürücü teknolojilerinden biridir. Makine öğrenimi, doğal dil işleme ve bilgisayar görüşü gibi alanlardaki ilerlemeler, YZ'nin hayatımızın birçok alanında etkili olmasını sağlamıştır. Akıllı asistanlardan otonom araçlara, tıbbi teşhislerden finansal analizlere kadar geniş bir yelpazede YZ uygulamalarıyla karşılaşıyoruz.
YZ'nin geleceği parlak görünmekle birlikte, beraberinde önemli etik, sosyal ve ekonomik zorlukları da getiriyor. YZ'nin karar alma süreçlerindeki şeffaflık, algoritmik yanlılık, gücü piyasası üzerindeki potansiyel etkileri ve YZ'nin kötüye kullanımı gibi konular, küresel düzeyde dikkatle ele alınması gereken meselelerdir.
Bu blog yazısında, yapay zekanın mevcut yeteneklerini, farklı sektörlerdeki kullanım alanlarını ve gelecekteki potansiyelini detaylı bir şekilde inceleyeceğiz. Ayrıca, YZ'nin sorumlu bir şekilde geliştirilmesi ve kullanılması için atılması gereken adımları ve bu alandaki güncel tartışmaları ele alarak, yapay zekanın geleceğine dair kapsamlı bir bakış sunacağız.`,
date: 'blog.posts.ai.date',
category: 'blog.categories.technology',
readTime: "5 dk",
},
en: {
title: 'blog.posts.ai.title',
excerpt: 'blog.posts.ai.excerpt',
content: `Artificial Intelligence (AI) is one of the fastest-growing and most transformative technologies today. Advances in areas such as machine learning, natural language processing, and computer vision have enabled AI to be effective in many areas of our lives. We encounter AI applications in a wide range of fields, from smart assistants to autonomous vehicles, medical diagnostics to financial analysis.
While the future of AI looks bright, it also brings significant ethical, social, and economic challenges. Issues such as transparency in AI decision-making processes, algorithmic bias, potential impacts on the labor market, and the misuse of AI are matters that need to be carefully addressed globally.
In this blog post, we will delve into the current capabilities of artificial intelligence, its applications in various sectors, and its future potential. Furthermore, we will discuss the steps that need to be taken for the responsible development and use of AI and the current debates in this field, providing a comprehensive outlook on the future of artificial intelligence.`,
date: 'blog.posts.ai.date',
category: 'blog.categories.technology',
readTime: "5 dk",
},
image: 'https://images.pexels.com/photos/8386434/pexels-photo-8386434.jpeg?auto=compress&cs=tinysrgb&w=1920',
author: "Ahmet Yılmaz",
},
'web-gelistirmede-son-trendler': {
tr: {
title: 'blog.posts.web.title',
excerpt: 'blog.posts.web.excerpt',
content: `Web geliştirme dünyası sürekli bir değişim ve evrim içinde. Her yıl yeni teknolojiler, frameworkler ve yaklaşımlar ortaya çıkıyor, bu da web geliştiricilerinin sürekli öğrenmesini ve adapte olmasını gerektiriyor. 2024 yılı da bu açıdan farklı değil; heyecan verici yeni trendler ve gelişmeler web ekosistemini şekillendiriyor.
Bu yılın öne çıkan trendlerinden biri Serverless mimarilerin yükselişi. Geliştiricilerin sunucu yönetimiyle uğraşmadan uygulama geliştirmesine olanak tanıyan Serverless, maliyet etkinliği ve ölçeklenebilirlik gibi avantajlar sunuyor. Bir diğer önemli trend ise WebAssembly (Wasm). Web tarayıcılarında yüksek performanslı kod çalıştırmayı mümkün kılan Wasm, oyunlar, video düzenleyiciler ve CAD yazılımları gibi daha karmaşık uygulamaların web'e taşınmasını sağlıyor.
Mikro ön uçlar da büyük ölçekli web uygulamalarının yönetimini kolaylaştıran bir mimari desen olarak popülerlik kazanıyor. Bu yaklaşım, büyük ön projelerini daha küçük, bağımsız parçalara ayırarak farklı ekiplerin aynı proje üzerinde paralel çalışmasına imkan tanıyor. Bu blog yazısında, bu ve benzeri web geliştirme trendlerini daha detaylı inceleyerek, geleceğin web uygulamalarını şekillendiren teknolojilere derinlemesine bir bakış sunacağız.`,
date: 'blog.posts.web.date',
category: 'blog.categories.webdev',
readTime: "7 dk",
},
en: {
title: 'blog.posts.web.title',
excerpt: 'blog.posts.web.excerpt',
content: `The world of web development is in a constant state of change and evolution. Every year, new technologies, frameworks, and approaches emerge, requiring web developers to continuously learn and adapt. 2024 is no different in this regard; exciting new trends and developments are shaping the web ecosystem.
One of the prominent trends this year is the rise of Serverless architectures. Enabling developers to build applications without managing servers, Serverless offers advantages such as cost-effectiveness and scalability. Another significant trend is WebAssembly (Wasm). Allowing high-performance code to run in web browsers, Wasm makes it possible to bring more complex applications like games, video editors, and CAD software to the web.
Micro frontends are also gaining popularity as an architectural pattern that simplifies the management of large-scale web applications. This approach allows breaking down large frontend projects into smaller, independent pieces, enabling different teams to work on the same project in parallel. In this blog post, we will delve deeper into these and similar web development trends, providing an in-depth look at the technologies shaping the future of web applications.`,
date: 'blog.posts.web.date',
category: 'blog.categories.webdev',
readTime: "7 dk",
},
image: 'https://images.pexels.com/photos/11035471/pexels-photo-11035471.jpeg?auto=compress&cs=tinysrgb&w=1920',
author: "Mehmet Kaya",
},
'siber-guvenlik-tehditleri-ve-korunma-yollari': {
tr: {
title: 'blog.posts.security.title',
excerpt: 'blog.posts.security.excerpt',
content: `Dijitalleşmenin hızla artmasıyla birlikte, siber güvenlik tehditlerinin sayısı ve karmaşıklığı da eş zamanlı olarak artmaktadır. Artık sadece büyük şirketler değil, bireyler ve küçük işletmeler de siber saldırganların hedefi haline gelmiştir. Kişisel verilerin çalınması, kimlik avı (phishing) saldırıları, fidye yazılımları (ransomware) ve dağıtılmış hizmet engelleme (DDoS) saldırıları gibi tehditler, hem maddi hem de manevi zararlara yol açabilmektedir.
Bu tehditlere karşı korunmak, dijital dünyada güvende kalmak için hayati öneme sahiptir. Güçlü ve benzersiz şifreler kullanmak, iki faktörlü kimlik doğrulama yöntemlerini benimsemek ve yazılımlarımızı düzenli olarak güncellemek, alabileceğimiz temel önlemler arasındadır. Ayrıca, bilinmeyen kaynaklardan gelen e-postalara ve linklere karşı dikkatli olmak, halka ık Wi-Fi ağlarında hassas işlemler yapmaktan kaçınmak da önemlidir.
Bu blog yazısında, güncel siber güvenlik tehditlerini daha detaylı bir şekilde inceleyeceğiz. Her bir tehdit türünün nasıl çalıştığını, potansiyel etkilerini ve bu tehditlere karşı bireysel ve kurumsal düzeyde alınabilecek en etkili korunma yollarını ele alacağız. Siber güvenlik bilincini artırmak ve dijital varlıklarımızı korumak için pratik ipuçları sunarak, daha güvenli bir çevrimiçi deneyim için rehberlik edeceğiz.`,
date: 'blog.posts.security.date',
category: 'blog.categories.security',
readTime: "6 dk",
},
en: {
title: 'blog.posts.security.title',
excerpt: 'blog.posts.security.excerpt',
content: `With the rapid increase in digitalization, the number and complexity of cyber security threats are also increasing simultaneously. Not only large corporations but also individuals and small businesses have become targets for cyber attackers. Threats such as personal data theft, phishing attacks, ransomware, and distributed denial-of-service (DDoS) attacks can cause both financial and emotional damage.
Protecting against these threats is vital for staying safe in the digital world. Using strong and unique passwords, adopting two-factor authentication methods, and regularly updating our software are among the basic precautions we can take. Furthermore, it is important to be cautious about emails and links from unknown sources and to avoid performing sensitive transactions on public Wi-Fi networks.
In this blog post, we will examine current cyber security threats in more detail. We will discuss how each type of threat works, its potential impacts, and the most effective ways to protect against these threats at both individual and organizational levels. By providing practical tips to increase cyber security awareness and protect our digital assets, we will offer guidance for a safer online experience.`,
date: 'blog.posts.security.date',
category: 'blog.categories.security',
readTime: "6 dk",
},
image: 'https://images.pexels.com/photos/5380642/pexels-photo-5380642.jpeg?auto=compress&cs=tinysrgb&w=1920',
author: "Ayşe Demir",
},
'mobil-uygulama-gelistirmede-cross-platform-cozumler': {
tr: {
title: 'blog.posts.mobile.title',
excerpt: 'blog.posts.mobile.excerpt',
content: `Mobil uygulama geliştirme, günümüzün en dinamik ve talep gören yazılım alanlarından biridir. Akıllı telefonların yaygınlaşmasıyla birlikte, işletmeler ve bireyler mobil platformlarda yer almak için sürekli yeni uygulamalar geliştirmektedir. Geleneksel olarak, iOS ve Android gibi farklı mobil işletim sistemleri için ayrı ayrı native uygulamalar geliştirmek gerekiyordu, bu da zaman ve kaynak açısından maliyetli olabiliyordu.
Ancak son yıllarda, cross-platform (çapraz platform) mobil geliştirme frameworkleri popülerlik kazanmıştır. Bu frameworkler, geliştiricilerin tek bir kod tabanı kullanarak hem iOS hem de Android platformları için uygulama geliştirmesine olanak tanır. Bu yaklaşım, geliştirme sürecini hızlandırır, maliyetleri düşürür ve bakım kolaylığı sağlar. React Native, Flutter ve Xamarin gibi frameworkler, cross-platform geliştirme alanında öne çıkan çözümlerdir.
Bu blog yazısında, cross-platform mobil geliştirmenin avantajlarını ve dezavantajlarını detaylı bir şekilde inceleyeceğiz. Farklı frameworklerin özelliklerini, performanslarını ve hangi senaryolarda daha uygun olduklarını karşılaştıracağız. Mobil uygulama geliştirme projeniz için en doğru cross-platform çözümü seçmenize yardımcı olacak bilgiler sunarak, geliştirme sürecinizi daha verimli hale getirmeniz için rehberlik edeceğiz.`,
date: 'blog.posts.mobile.date',
category: 'blog.categories.mobile',
readTime: "4 dk",
},
en: {
title: 'blog.posts.mobile.title',
excerpt: 'blog.posts.mobile.excerpt',
content: `Mobile application development is one of the most dynamic and in-demand software fields today. With the widespread adoption of smartphones, businesses and individuals are constantly developing new applications to have a presence on mobile platforms. Traditionally, it was necessary to develop separate native applications for different mobile operating systems like iOS and Android, which could be costly in terms of time and resources.
However, in recent years, cross-platform mobile development frameworks have gained popularity. These frameworks allow developers to build applications for both iOS and Android platforms using a single codebase. This approach accelerates the development process, reduces costs, and provides ease of maintenance. Frameworks like React Native, Flutter, and Xamarin are prominent solutions in the field of cross-platform development.
In this blog post, we will delve into the advantages and disadvantages of cross-platform mobile development in detail. We will compare the features and performance of different frameworks and discuss in which scenarios they are more suitable. By providing information to help you choose the right cross-platform solution for your mobile application development project, we will guide you to make your development process more efficient.`,
date: 'blog.posts.mobile.date',
category: 'blog.categories.mobile',
readTime: "4 dk",
},
image: 'https://images.pexels.com/photos/13017583/pexels-photo-13017583.jpeg?auto=compress&cs=tinysrgb&w=1920',
author: "Can Öztürk",
},
'veritabani-yonetim-sistemleri-karsilastirmasi': {
tr: {
title: 'blog.posts.database.title',
excerpt: 'blog.posts.database.excerpt',
content: `Veritabanları, modern yazılım uygulamalarının kalbidir. Uygulamaların veriyi depolaması, yönetmesi ve erişmesi için güvenilir ve etkili bir veritabanı yönetim sistemi (VTYS) seçimi kritik öneme sahiptir. Piyasada birçok farklı türde VTYS bulunmaktadır ve her birinin kendine özgü avantajları ve dezavantajları vardır. Doğru VTYS seçimi, uygulamanın performansı, ölçeklenebilirliği, güvenliği ve geliştirme süreci üzerinde doğrudan bir etkiye sahiptir.
İlişkisel veritabanları (RDBMS), veriyi tablolar halinde düzenler ve SQL (Yapısal Sorgu Dili) kullanarak verilere erişim sağlar. MySQL, PostgreSQL, Oracle ve SQL Server gibi sistemler bu kategoriye girer. RDBMS'ler, veri tutarlılığı ve karmaşık sorgular için güçlü yetenekler sunar. Ancak, büyük ölçekli ve yapısal olmayan verilerle çalışırken performans sorunları yaşanabilir.
NoSQL (Not Only SQL) veritabanları ise daha esnek veri modelleri sunar ve genellikle büyük ölçekli, dağıtık sistemler ve hızlı veri erişimi gerektiren uygulamalar için tercih edilir. MongoDB (belge tabanlı), Cassandra (geniş sütunlu) ve Redis (anahtar-değer) gibi sistemler NoSQL kategorisine örnektir. NoSQL veritabanları, yatay ölçeklenebilirlik ve yüksek erişilebilirlik sağlama konusunda genellikle RDBMS'lerden daha iyidir. Bu blog yazısında, ilişkisel ve NoSQL veritabanı yönetim sistemlerini detaylı bir şekilde karşılaştırarak, farklı kullanım senaryolarına göre hangi VTYS'nin daha uygun olduğunu ele alacağız.`,
date: 'blog.posts.database.date',
readTime: "8 dk",
category: 'blog.categories.database',
},
en: {
title: 'blog.posts.database.title',
excerpt: 'blog.posts.database.excerpt',
content: `Databases are the heart of modern software applications. Choosing a reliable and effective database management system (DBMS) is crucial for applications to store, manage, and access data. There are many different types of DBMS available in the market, each with its own unique advantages and disadvantages. The right DBMS choice has a direct impact on the application's performance, scalability, security, and development process.
Relational database management systems (RDBMS) organize data in tables and provide access to data using SQL (Structured Query Language). Systems like MySQL, PostgreSQL, Oracle, and SQL Server fall into this category. RDBMSs offer strong capabilities for data consistency and complex queries. However, performance issues can arise when working with large-scale and unstructured data.
NoSQL (Not Only SQL) databases, on the other hand, offer more flexible data models and are generally preferred for large-scale, distributed systems and applications requiring fast data access. Systems like MongoDB (document-based), Cassandra (wide-column), and Redis (key-value) are examples of NoSQL databases. NoSQL databases are generally better than RDBMSs at providing horizontal scalability and high availability. In this blog post, we will compare relational and NoSQL database management systems in detail, discussing which DBMS is more suitable for different use cases.`,
date: 'blog.posts.database.date',
readTime: "8 dk",
category: 'blog.categories.database',
},
image: 'https://images.pexels.com/photos/325229/pexels-photo-325229.jpeg?auto=compress&cs=tinysrgb&w=1920',
author: "Zeynep Yıldız",
},
'dijital-pazarlamada-veri-analizi': {
tr: {
title: 'blog.posts.digital.title',
excerpt: 'blog.posts.digital.excerpt',
content: `Dijital pazarlama dünyası, sürekli değişen algoritmalar, yeni platformlar ve gelişen tüketici davranışlarıyla dinamik bir yapıya sahiptir. Bu karmaşık ortamda başarılı olmak için, pazarlamacıların verilere dayalı kararlar alması ve stratejilerini sürekli olarak optimize etmesi gerekmektedir. İşte tam bu noktada veri analizi devreye girer. Dijital pazarlamada veri analizi, kampanyaların performansını ölçmek, müşteri davranışlarını anlamak, hedef kitleyi daha iyi tanımak ve pazarlama yatırımlarının geri dönüşünü (ROI) maksimize etmek için kritik bir araçtır.
Veri analizi sayesinde, hangi pazarlama kanallarının en etkili olduğunu belirleyebilir, hangi içerik türlerinin daha fazla etkileşim aldığını anlayabilir ve müşteri yolculuğunun farklı aşamalarındaki performans darboğazlarını tespit edebiliriz. Google Analytics, Adobe Analytics gibi web analizi araçları, sosyal medya analiz platformları ve müşteri ilişkileri yönetimi (CRM) sistemleri, dijital pazarlamacılara değerli veriler sunar. Bu verilerin doğru bir şekilde toplanması, temizlenmesi, analiz edilmesi ve yorumlanması, daha bilinçli ve etkili pazarlama stratejileri oluşturmanın temelini oluşturur.
Bu blog yazısında, dijital pazarlamada veri analizinin neden bu kadar önemli olduğunu detaylı bir şekilde ele alacağız. Kullanılan temel metrikleri (örneğin, dönüşüm oranı, tıklama oranı, müşteri edinme maliyeti), farklı analiz yöntemlerini ve veri görselleştirme tekniklerini inceleyeceğiz. Ayrıca, veri analizinden elde edilen içgörüleri pazarlama stratejilerine nasıl entegre edebileceğinize dair pratik ipuçları sunarak, dijital pazarlama çabalarınızın etkinliğini artırmanız için rehberlik edeceğiz.`,
date: 'blog.posts.digital.date',
readTime: "6 dk",
category: 'blog.categories.digital',
},
en: {
title: 'blog.posts.digital.title',
excerpt: 'blog.posts.digital.excerpt',
content: `The world of digital marketing is dynamic, with constantly changing algorithms, new platforms, and evolving consumer behavior. To succeed in this complex environment, marketers need to make data-driven decisions and continuously optimize their strategies. This is where data analysis comes into play. In digital marketing, data analysis is a critical tool for measuring campaign performance, understanding customer behavior, getting to know the target audience better, and maximizing the return on marketing investments (ROI).
Through data analysis, we can determine which marketing channels are most effective, understand which types of content receive more engagement, and identify performance bottlenecks at different stages of the customer journey. Web analytics tools like Google Analytics and Adobe Analytics, social media analytics platforms, and customer relationship management (CRM) systems provide valuable data to digital marketers. The accurate collection, cleaning, analysis, and interpretation of this data form the basis for creating more informed and effective marketing strategies.
In this blog post, we will delve into why data analysis is so important in digital marketing. We will examine key metrics used (e.g., conversion rate, click-through rate, customer acquisition cost), different analysis methods, and data visualization techniques. Furthermore, we will provide practical tips on how to integrate insights gained from data analysis into your marketing strategies, guiding you to increase the effectiveness of your digital marketing efforts.`,
date: 'blog.posts.digital.date',
readTime: "6 dk",
category: 'blog.categories.digital',
},
image: 'https://images.pexels.com/photos/7681091/pexels-photo-7681091.jpeg?auto=compress&cs=tinysrgb&w=1920',
author: "Ali Kara",
},
};
// Blog yazılarının listesi (Blog.tsx'te kullanılacak)
export const blogPostsList = Object.keys(blogContent).map(slug => {
const post = blogContent[slug].tr; // Varsayılan olarak Türkçe içeriği kullanıyoruz, dil bağlamına göre değiştirilebilir
return {
slug,
title: post.title,
excerpt: post.excerpt,
image: blogContent[slug].image, // Görsel bilgisi blogContent'ten alındı
author: blogContent[slug].author, // Yazar bilgisi blogContent'ten alındı
date: post.date,
readTime: post.readTime,
category: post.category,
};
});

View file

@ -30,19 +30,18 @@ const Blog = () => {
const [postsData, categoriesData] = await Promise.all([ const [postsData, categoriesData] = await Promise.all([
blogService.getPosts({ blogService.getPosts({
page: currentPage, page: currentPage,
pageSize: 9, pageSize: 10,
categoryId: selectedCategory, categoryId: selectedCategory,
search: searchQuery, search: searchQuery,
}), }),
blogService.getCategories(), blogService.getCategories(),
]); ]);
setPosts(postsData.items); setPosts(postsData.items.filter(a=> a.isPublished));
setTotalPages(postsData.totalPages); setTotalPages(postsData.totalPages);
setCategories(categoriesData); setCategories(categoriesData.filter(a=> a.isActive));
} catch (error) { } catch (error) {
console.error("Blog verileri yüklenemedi:", error); console.error("Blog verileri yüklenemedi:", error);
// Fallback to static data if API fails
setPosts([]); setPosts([]);
} finally { } finally {
setLoading(false); setLoading(false);

View file

@ -35,6 +35,7 @@ export interface BlogCategory {
slug: string; slug: string;
description?: string; description?: string;
postCount: number; postCount: number;
isActive: boolean;
} }
export interface BlogComment { export interface BlogComment {
@ -75,10 +76,7 @@ export interface BlogListParams {
page?: number; page?: number;
pageSize?: number; pageSize?: number;
categoryId?: string; categoryId?: string;
tag?: string;
search?: string; search?: string;
authorId?: string;
sortBy?: "latest" | "popular" | "trending";
} }
export interface PaginatedResponse<T> { export interface PaginatedResponse<T> {
@ -150,59 +148,12 @@ class BlogService {
return response.data; return response.data;
} }
async incrementViewCount(id: string): Promise<void> {
await apiClient.post(`/api/app/blog/posts/${id}/view`);
}
async likePost(id: string): Promise<void> {
await apiClient.post(`/api/app/blog/posts/${id}/like`);
}
async unlikePost(id: string): Promise<void> {
await apiClient.delete(`/api/app/blog/posts/${id}/like`);
}
async getCategories(): Promise<BlogCategory[]> { async getCategories(): Promise<BlogCategory[]> {
const response = await apiClient.get<BlogCategory[]>( const response = await apiClient.get<BlogCategory[]>(
"/api/app/blog/categories" "/api/app/blog/categories"
); );
return response.data; return response.data;
} }
async getTags(count = 20): Promise<string[]> {
const response = await apiClient.get<string[]>(
`/api/app/blog/tags?count=${count}`
);
return response.data;
}
// Opsiyonel - Yorum API'si mevcutsa kullanılır
async getComments(postId: string): Promise<BlogComment[]> {
const response = await apiClient.get<BlogComment[]>(
`/api/app/blog/posts/${postId}/comments`
);
return response.data;
}
async createComment(data: CreateCommentRequest): Promise<BlogComment> {
const response = await apiClient.post<BlogComment>(
"/api/app/blog/comments",
data
);
return response.data;
}
async deleteComment(id: string): Promise<void> {
await apiClient.delete(`/api/app/blog/comments/${id}`);
}
async likeComment(id: string): Promise<void> {
await apiClient.post(`/api/app/blog/comments/${id}/like`);
}
async unlikeComment(id: string): Promise<void> {
await apiClient.delete(`/api/app/blog/comments/${id}/like`);
}
} }
export const blogService = new BlogService(); export const blogService = new BlogService();

View file

@ -30,14 +30,14 @@ export const useAuthStore = create<AuthState>()(
const user = authService.getUser(); const user = authService.getUser();
const tenantId = authService.getTenantId(); const tenantId = authService.getTenantId();
set({ user, isAuthenticated: true, isLoading: false, tenantId }); set({ user, isAuthenticated: true, isLoading: false, tenantId });
toast.success('Giriş başarılı!'); toast.success('Giriş başarılı!', { position:'top-center' });
} catch (error: any) { } catch (error: any) {
set({ isLoading: false }); set({ isLoading: false });
const errorMessage = error.response?.data?.error_description || const errorMessage = error.response?.data?.error_description ||
error.response?.data?.error?.message || error.response?.data?.error?.message ||
error.response?.data?.message || error.response?.data?.message ||
'Giriş başarısız!'; 'Giriş başarısız!';
toast.error(errorMessage); toast.error(errorMessage, { position:'top-center' });
throw error; throw error;
} }
}, },
@ -47,10 +47,10 @@ export const useAuthStore = create<AuthState>()(
try { try {
await authService.register(data); await authService.register(data);
set({ isLoading: false }); set({ isLoading: false });
toast.success('Kayıt başarılı! Lütfen giriş yapın.'); toast.success('Kayıt başarılı! Lütfen giriş yapın.', { position:'top-center' });
} catch (error: any) { } catch (error: any) {
set({ isLoading: false }); set({ isLoading: false });
toast.error(error.response?.data?.error?.message || 'Kayıt başarısız!'); toast.error(error.response?.data?.error?.message || 'Kayıt başarısız!', { position:'top-center' });
throw error; throw error;
} }
}, },
@ -58,7 +58,7 @@ export const useAuthStore = create<AuthState>()(
logout: () => { logout: () => {
authService.logout(); authService.logout();
set({ user: null, isAuthenticated: false, tenantId: null }); set({ user: null, isAuthenticated: false, tenantId: null });
toast.success(ıkış yapıldı'); toast.success(ıkış yapıldı', { position:'top-center' });
}, },
checkAuth: async () => { checkAuth: async () => {

View file

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

View file

@ -36,10 +36,10 @@ export const ROUTES_ENUM = {
}, },
chart: '/admin/chart/edit/:chartCode', chart: '/admin/chart/edit/:chartCode',
blog: { blog: {
management: '/admin/blog/management', management: '/admin/blog',
}, },
forum: { forum: {
management: '/admin/forum/management', management: '/admin/forum',
}, },
}, },
settings: '/settings', settings: '/settings',

View file

@ -33,6 +33,9 @@ export interface BlogCategory {
slug: string slug: string
description?: string description?: string
postCount: number postCount: number
isActive: boolean
icon: string
displayOrder: number
} }
export interface BlogComment { export interface BlogComment {
@ -54,6 +57,7 @@ export interface BlogComment {
export interface CreateUpdateBlogPostDto { export interface CreateUpdateBlogPostDto {
title: string title: string
slug: string
content: string content: string
summary: string summary: string
categoryId: string categoryId: string

View file

@ -31,19 +31,26 @@ import THead from '@/components/ui/Table/THead'
import TBody from '@/components/ui/Table/TBody' import TBody from '@/components/ui/Table/TBody'
import Td from '@/components/ui/Table/Td' import Td from '@/components/ui/Table/Td'
import { SelectBoxOption } from '@/shared/types' import { SelectBoxOption } from '@/shared/types'
import { enumToList } from '@/utils/enumUtils' import { CheckBox } from 'devextreme-react'
import { Checkbox } from '@/components/ui'
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
title: Yup.string().required('Başlık gereklidir'), title: Yup.string().required(),
summary: Yup.string().required('Özet gereklidir'), summary: Yup.string().required(),
categoryId: Yup.string().required('Kategori seçiniz'), categoryId: Yup.string().required(),
content: Yup.string().required('İçerik gereklidir'), content: Yup.string(),
tags: Yup.string(),
coverImage: Yup.string(),
isPublished: Yup.bool(),
}) })
const categoryValidationSchema = Yup.object().shape({ const categoryValidationSchema = Yup.object().shape({
name: Yup.string().required('İsim gereklidir'), name: Yup.string().required(),
slug: Yup.string().required('Slug gereklidir'), slug: Yup.string().required(),
description: Yup.string().required('Açıklama gereklidir'), description: Yup.string(),
icon: Yup.string(),
displayOrder: Yup.number(),
isActive: Yup.bool(),
}) })
const BlogManagement = () => { const BlogManagement = () => {
@ -79,6 +86,9 @@ const BlogManagement = () => {
<Notification title="Hata" type="danger"> <Notification title="Hata" type="danger">
Veriler yüklenirken hata oluştu Veriler yüklenirken hata oluştu
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} finally { } finally {
setLoading(false) setLoading(false)
@ -102,6 +112,9 @@ const BlogManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Blog yazısı silindi Blog yazısı silindi
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
loadData() loadData()
} catch (error) { } catch (error) {
@ -109,6 +122,9 @@ const BlogManagement = () => {
<Notification title="Hata" type="danger"> <Notification title="Hata" type="danger">
Silme işlemi başarısız Silme işlemi başarısız
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} }
} }
@ -117,6 +133,7 @@ const BlogManagement = () => {
try { try {
const data: CreateUpdateBlogPostDto = { const data: CreateUpdateBlogPostDto = {
title: values.title, title: values.title,
slug: values.slug,
content: values.content, content: values.content,
summary: values.summary, summary: values.summary,
categoryId: values.categoryId, categoryId: values.categoryId,
@ -131,6 +148,9 @@ const BlogManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Blog yazısı güncellendi Blog yazısı güncellendi
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} else { } else {
await blogService.createPost(data) await blogService.createPost(data)
@ -138,6 +158,9 @@ const BlogManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Blog yazısı oluşturuldu Blog yazısı oluşturuldu
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} }
@ -148,6 +171,9 @@ const BlogManagement = () => {
<Notification title="Hata" type="danger"> <Notification title="Hata" type="danger">
İşlem başarısız İşlem başarısız
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} finally { } finally {
setSubmitting(false) setSubmitting(false)
@ -162,6 +188,9 @@ const BlogManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Yayından kaldırıldı Yayından kaldırıldı
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} else { } else {
await blogService.publishPost(post.id) await blogService.publishPost(post.id)
@ -169,6 +198,9 @@ const BlogManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Yayınlandı Yayınlandı
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} }
loadData() loadData()
@ -177,6 +209,9 @@ const BlogManagement = () => {
<Notification title="Hata" type="danger"> <Notification title="Hata" type="danger">
İşlem başarısız İşlem başarısız
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} }
} }
@ -189,6 +224,7 @@ const BlogManagement = () => {
const handleEditCategory = (category: BlogCategory) => { const handleEditCategory = (category: BlogCategory) => {
setEditingCategory(category) setEditingCategory(category)
console.log(category)
setCategoryModalVisible(true) setCategoryModalVisible(true)
} }
@ -199,6 +235,9 @@ const BlogManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Kategori silindi Kategori silindi
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
loadData() loadData()
} catch (error) { } catch (error) {
@ -206,6 +245,9 @@ const BlogManagement = () => {
<Notification title="Hata" type="danger"> <Notification title="Hata" type="danger">
Silme işlemi başarısız Silme işlemi başarısız
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} }
} }
@ -227,6 +269,9 @@ const BlogManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Kategori güncellendi Kategori güncellendi
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} else { } else {
await blogService.createCategory(data) await blogService.createCategory(data)
@ -234,6 +279,9 @@ const BlogManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Kategori oluşturuldu Kategori oluşturuldu
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} }
@ -244,6 +292,9 @@ const BlogManagement = () => {
<Notification title="Hata" type="danger"> <Notification title="Hata" type="danger">
İşlem başarısız İşlem başarısız
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} finally { } finally {
setSubmitting(false) setSubmitting(false)
@ -253,6 +304,7 @@ const BlogManagement = () => {
const initialValues = editingPost const initialValues = editingPost
? { ? {
title: editingPost.title, title: editingPost.title,
slug: editingPost.slug,
summary: editingPost.summary, summary: editingPost.summary,
content: editingPost.content, content: editingPost.content,
categoryId: editingPost.category.id, categoryId: editingPost.category.id,
@ -262,6 +314,7 @@ const BlogManagement = () => {
} }
: { : {
title: '', title: '',
slug: '',
summary: '', summary: '',
content: '', content: '',
categoryId: '', categoryId: '',
@ -275,9 +328,9 @@ const BlogManagement = () => {
name: editingCategory.name, name: editingCategory.name,
slug: editingCategory.slug, slug: editingCategory.slug,
description: editingCategory.description || '', description: editingCategory.description || '',
icon: '', icon: editingCategory.icon,
displayOrder: 0, displayOrder: editingCategory.displayOrder,
isActive: true, isActive: editingCategory.isActive,
} }
: { : {
name: '', name: '',
@ -290,19 +343,6 @@ const BlogManagement = () => {
return ( return (
<> <>
<div className="flex justify-between items-center mb-4">
<h3>Blog Yönetimi</h3>
{activeTab === 'posts' ? (
<Button variant="solid" size="xs" icon={<HiPlus />} onClick={handleCreate}>
Yeni Blog Yazısı
</Button>
) : (
<Button variant="solid" size="xs" icon={<HiPlus />} onClick={handleCreateCategory}>
Yeni Kategori
</Button>
)}
</div>
<Card> <Card>
<div className="mb-4"> <div className="mb-4">
<div className="flex gap-4 border-b"> <div className="flex gap-4 border-b">
@ -322,64 +362,46 @@ const BlogManagement = () => {
</div> </div>
{activeTab === 'posts' ? ( {activeTab === 'posts' ? (
<Table> <Table compact>
<THead> <THead>
<Tr> <Tr>
<Th>Başlık</Th> <Th>Başlık</Th>
<Th>Slug</Th>
<Th>Kategori</Th> <Th>Kategori</Th>
<Th>Yazar</Th> <Th>Yazar</Th>
<Th>Durum</Th>
<Th>Gör. / Beğ. / Yorum</Th>
<Th>Yayın Tarihi</Th> <Th>Yayın Tarihi</Th>
<Th>İşlemler</Th> <Th>Durum</Th>
<Th>
{' '}
<Button variant="solid" size="xs" icon={<HiPlus />} onClick={handleCreate}>
Yeni
</Button>
</Th>
</Tr> </Tr>
</THead> </THead>
<TBody> <TBody>
{loading ? ( {loading ? (
<Tr> <Tr>
<Td colSpan={7}>Yükleniyor...</Td> <Td colSpan={7} className='text-center'>Yükleniyor...</Td>
</Tr> </Tr>
) : ( ) : (
posts.map((post) => ( posts.map((post) => (
<Tr key={post.id}> <Tr key={post.id}>
<Td> <Td className="font-medium">{post.title}</Td>
<a <Td>{post.slug}</Td>
className="text-blue-600 hover:underline cursor-pointer"
onClick={() => navigate(`/blog/${post.slug || post.id}`)}
>
{post.title}
</a>
</Td>
<Td>{post.category?.name}</Td> <Td>{post.category?.name}</Td>
<Td>{post.author?.name}</Td> <Td>{post.author?.name}</Td>
<Td>
<Tag
className={
post.isPublished
? 'bg-green-100 text-green-800'
: 'bg-orange-100 text-orange-800'
}
>
{post.isPublished ? 'Yayında' : 'Taslak'}
</Tag>
</Td>
<Td>
{post.viewCount} / {post.likeCount} / {post.commentCount}
</Td>
<Td> <Td>
{post.publishedAt {post.publishedAt
? format(new Date(post.publishedAt), 'dd MMM yyyy', { locale: tr }) ? format(new Date(post.publishedAt), 'dd MMM yyyy', { locale: tr })
: '-'} : '-'}
</Td> </Td>
<Td> <Td>
<div className="flex gap-2">
<Button
size="sm"
icon={<HiEye />}
onClick={() => navigate(`/blog/${post.slug || post.id}`)}
/>
<Button size="sm" icon={<HiPencil />} onClick={() => handleEdit(post)} />
<Switcher checked={post.isPublished} onChange={() => handlePublish(post)} /> <Switcher checked={post.isPublished} onChange={() => handlePublish(post)} />
</Td>
<Td>
<div className="flex gap-2">
<Button size="sm" icon={<HiPencil />} onClick={() => handleEdit(post)} />
<Button <Button
size="sm" size="sm"
variant="solid" variant="solid"
@ -395,20 +417,30 @@ const BlogManagement = () => {
</TBody> </TBody>
</Table> </Table>
) : ( ) : (
<Table> <Table compact>
<THead> <THead>
<Tr> <Tr>
<Th>İsim</Th> <Th>İsim</Th>
<Th>Slug</Th> <Th>Slug</Th>
<Th>ıklama</Th> <Th>ıklama</Th>
<Th>Yazı Sayısı</Th> <Th>Yazı Sayısı</Th>
<Th>İşlemler</Th> <Th>Durum</Th>
<Th>
<Button
variant="solid"
size="xs"
icon={<HiPlus />}
onClick={handleCreateCategory}
>
Yeni
</Button>
</Th>
</Tr> </Tr>
</THead> </THead>
<TBody> <TBody>
{loading ? ( {loading ? (
<Tr> <Tr>
<Td colSpan={5}>Yükleniyor...</Td> <Td colSpan={5} className='text-center'>Yükleniyor...</Td>
</Tr> </Tr>
) : ( ) : (
categories.map((category) => ( categories.map((category) => (
@ -417,6 +449,17 @@ const BlogManagement = () => {
<Td>{category.slug}</Td> <Td>{category.slug}</Td>
<Td>{category.description}</Td> <Td>{category.description}</Td>
<Td>{category.postCount}</Td> <Td>{category.postCount}</Td>
<Td>
<Tag
className={
category.isActive
? 'bg-green-100 text-green-800'
: 'bg-orange-100 text-orange-800'
}
>
{category.isActive ? 'Aktif' : 'Pasif'}
</Tag>
</Td>
<Td> <Td>
<div className="flex gap-2"> <div className="flex gap-2">
<Button <Button
@ -454,12 +497,13 @@ const BlogManagement = () => {
initialValues={initialValues} initialValues={initialValues}
validationSchema={validationSchema} validationSchema={validationSchema}
onSubmit={handleSubmit} onSubmit={handleSubmit}
enableReinitialize enableReinitialize={true}
> >
{({ values, touched, errors, isSubmitting, setFieldValue }) => ( {({ values, touched, errors, isSubmitting, setFieldValue }) => (
<Form> <Form>
<FormContainer> <FormContainer>
<FormItem <FormItem
asterisk
label="Başlık" label="Başlık"
invalid={errors.title && touched.title} invalid={errors.title && touched.title}
errorMessage={errors.title} errorMessage={errors.title}
@ -474,6 +518,16 @@ const BlogManagement = () => {
</FormItem> </FormItem>
<FormItem <FormItem
asterisk
label="Slug"
invalid={errors.title && touched.title}
errorMessage={errors.title}
>
<Field type="text" name="slug" placeholder="Slug" component={Input} />
</FormItem>
<FormItem
asterisk
label="Özet" label="Özet"
invalid={errors.summary && touched.summary} invalid={errors.summary && touched.summary}
errorMessage={errors.summary} errorMessage={errors.summary}
@ -488,6 +542,7 @@ const BlogManagement = () => {
</FormItem> </FormItem>
<FormItem <FormItem
asterisk
label="Kategori" label="Kategori"
invalid={errors.categoryId && touched.categoryId} invalid={errors.categoryId && touched.categoryId}
errorMessage={errors.categoryId} errorMessage={errors.categoryId}
@ -499,16 +554,14 @@ const BlogManagement = () => {
form={form} form={form}
options={categoryItems} options={categoryItems}
isClearable={true} isClearable={true}
value={categoryItems.filter( value={categoryItems.filter((option) => option.value === values.categoryId)}
(option) => option.value === values.categoryId,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)} onChange={(option) => form.setFieldValue(field.name, option?.value)}
/> />
)} )}
</Field> </Field>
</FormItem> </FormItem>
<FormItem label="Etiketler" extra="Virgül ile ayırarak yazınız"> <FormItem label="Etiketler - Virgül ile ayırarak yazınız">
<Field <Field
type="text" type="text"
name="tags" name="tags"
@ -528,7 +581,8 @@ const BlogManagement = () => {
<FormItem <FormItem
label="İçerik" label="İçerik"
invalid={errors.content && touched.content} asterisk
invalid={!!errors.content}
errorMessage={errors.content} errorMessage={errors.content}
> >
<ReactQuill <ReactQuill
@ -539,17 +593,12 @@ const BlogManagement = () => {
/> />
</FormItem> </FormItem>
<FormItem> <FormItem
<Field name="isPublished"> label="Durum"
{({ field, form }: any) => ( invalid={errors.isPublished && touched.isPublished}
<Switcher errorMessage={errors.isPublished}
{...field} >
onChange={(checked) => form.setFieldValue(field.name, checked)} <Field name="isPublished" component={Switcher} />
checkedContent="Yayınla"
unCheckedContent="Taslak"
/>
)}
</Field>
</FormItem> </FormItem>
<FormItem> <FormItem>
@ -587,6 +636,7 @@ const BlogManagement = () => {
<Form> <Form>
<FormContainer> <FormContainer>
<FormItem <FormItem
asterisk
label="İsim" label="İsim"
invalid={errors.name && touched.name} invalid={errors.name && touched.name}
errorMessage={errors.name} errorMessage={errors.name}
@ -601,6 +651,7 @@ const BlogManagement = () => {
</FormItem> </FormItem>
<FormItem <FormItem
asterisk
label="Slug" label="Slug"
invalid={errors.slug && touched.slug} invalid={errors.slug && touched.slug}
errorMessage={errors.slug} errorMessage={errors.slug}
@ -630,17 +681,12 @@ const BlogManagement = () => {
<Field type="number" name="displayOrder" placeholder="0" component={Input} /> <Field type="number" name="displayOrder" placeholder="0" component={Input} />
</FormItem> </FormItem>
<FormItem> <FormItem
<Field name="isActive"> label="Durum"
{({ field, form }: any) => ( invalid={errors.isActive && touched.isActive}
<Switcher errorMessage={errors.isActive}
{...field} >
onChange={(checked) => form.setFieldValue(field.name, checked)} <Field name="isActive" component={Checkbox} />
checkedContent="Aktif"
unCheckedContent="Pasif"
/>
)}
</Field>
</FormItem> </FormItem>
<FormItem> <FormItem>

View file

@ -61,6 +61,9 @@ const ForumManagement = () => {
<Notification title="Hata" type="danger"> <Notification title="Hata" type="danger">
Veriler yüklenirken hata oluştu Veriler yüklenirken hata oluştu
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} finally { } finally {
setLoading(false) setLoading(false)
@ -84,6 +87,9 @@ const ForumManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Kategori silindi Kategori silindi
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
loadData() loadData()
} catch (error) { } catch (error) {
@ -91,6 +97,9 @@ const ForumManagement = () => {
<Notification title="Hata" type="danger"> <Notification title="Hata" type="danger">
Silme işlemi başarısız Silme işlemi başarısız
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} }
} }
@ -113,6 +122,9 @@ const ForumManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Kategori güncellendi Kategori güncellendi
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} else { } else {
await forumService.createCategory(data) await forumService.createCategory(data)
@ -120,6 +132,9 @@ const ForumManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Kategori oluşturuldu Kategori oluşturuldu
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} }
@ -130,6 +145,9 @@ const ForumManagement = () => {
<Notification title="Hata" type="danger"> <Notification title="Hata" type="danger">
İşlem başarısız İşlem başarısız
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} finally { } finally {
setSubmitting(false) setSubmitting(false)
@ -144,6 +162,9 @@ const ForumManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Konu kilidi ıldı Konu kilidi ıldı
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} else { } else {
await forumService.lockTopic(topic.id) await forumService.lockTopic(topic.id)
@ -151,6 +172,9 @@ const ForumManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Konu kilitlendi Konu kilitlendi
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} }
loadData() loadData()
@ -159,6 +183,9 @@ const ForumManagement = () => {
<Notification title="Hata" type="danger"> <Notification title="Hata" type="danger">
İşlem başarısız İşlem başarısız
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} }
} }
@ -171,6 +198,9 @@ const ForumManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Sabitleme kaldırıldı Sabitleme kaldırıldı
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} else { } else {
await forumService.pinTopic(topic.id) await forumService.pinTopic(topic.id)
@ -178,6 +208,9 @@ const ForumManagement = () => {
<Notification title="Başarılı" type="success"> <Notification title="Başarılı" type="success">
Konu sabitlendi Konu sabitlendi
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} }
loadData() loadData()
@ -186,6 +219,9 @@ const ForumManagement = () => {
<Notification title="Hata" type="danger"> <Notification title="Hata" type="danger">
İşlem başarısız İşlem başarısız
</Notification>, </Notification>,
{
placement: 'top-center',
},
) )
} }
} }
@ -257,7 +293,7 @@ const ForumManagement = () => {
<TBody> <TBody>
{loading ? ( {loading ? (
<Tr> <Tr>
<Td colSpan={8}>Yükleniyor...</Td> <Td colSpan={8} className='text-center'>Yükleniyor...</Td>
</Tr> </Tr>
) : ( ) : (
categories.map((category) => ( categories.map((category) => (
@ -327,7 +363,7 @@ const ForumManagement = () => {
<TBody> <TBody>
{loading ? ( {loading ? (
<Tr> <Tr>
<Td colSpan={7}>Yükleniyor...</Td> <Td colSpan={7} className='text-center'>Yükleniyor...</Td>
</Tr> </Tr>
) : ( ) : (
topics.map((topic) => ( topics.map((topic) => (