Blog sistemindeki güncellemeler
This commit is contained in:
parent
2baff7538f
commit
2a1f06b2f4
15 changed files with 1072 additions and 382 deletions
|
|
@ -46,6 +46,7 @@ namespace Kurs.Platform.Blog
|
|||
public string Slug { get; set; }
|
||||
public string Content { get; set; }
|
||||
public string Summary { get; set; }
|
||||
public string ReadTime { get; set; }
|
||||
public string CoverImage { get; set; }
|
||||
public Guid CategoryId { get; set; }
|
||||
public List<string> Tags { get; set; }
|
||||
|
|
@ -62,6 +63,7 @@ namespace Kurs.Platform.Blog
|
|||
public string Title { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Summary { get; set; }
|
||||
public string ReadTime { get; set; }
|
||||
public string CoverImage { get; set; }
|
||||
|
||||
public BlogCategoryDto Category { get; set; }
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ namespace Kurs.Platform.Blog
|
|||
dto.Author = new AuthorDto
|
||||
{
|
||||
Id = post.AuthorId,
|
||||
Name = post.CreatorId.HasValue ? "User" : "Unknown" // You should get actual user name
|
||||
Name = post.CreatorId.HasValue ? "User" : "Unknown"
|
||||
};
|
||||
|
||||
postDtos.Add(dto);
|
||||
|
|
@ -164,6 +164,8 @@ namespace Kurs.Platform.Blog
|
|||
input.Slug,
|
||||
input.Content,
|
||||
input.Summary,
|
||||
input.ReadTime,
|
||||
input.CoverImage,
|
||||
input.CategoryId,
|
||||
_currentUser.Id.Value,
|
||||
CurrentTenant.Id
|
||||
|
|
@ -202,7 +204,7 @@ namespace Kurs.Platform.Blog
|
|||
var post = await _postRepository.GetAsync(id);
|
||||
|
||||
// Check if user is author or has permission
|
||||
if (post.AuthorId != _currentUser.Id && !await AuthorizationService.IsGrantedAsync("Blog.Posts.Update"))
|
||||
if (post.AuthorId != _currentUser.Id && !await AuthorizationService.IsGrantedAsync("App.Blog.Update"))
|
||||
{
|
||||
throw new Volo.Abp.Authorization.AbpAuthorizationException();
|
||||
}
|
||||
|
|
@ -249,7 +251,7 @@ namespace Kurs.Platform.Blog
|
|||
var post = await _postRepository.GetAsync(id);
|
||||
|
||||
// Check if user is author or has permission
|
||||
if (post.AuthorId != _currentUser.Id && !await AuthorizationService.IsGrantedAsync("Blog.Posts.Delete"))
|
||||
if (post.AuthorId != _currentUser.Id && !await AuthorizationService.IsGrantedAsync("App.Blog.Delete"))
|
||||
{
|
||||
throw new Volo.Abp.Authorization.AbpAuthorizationException();
|
||||
}
|
||||
|
|
@ -267,7 +269,7 @@ namespace Kurs.Platform.Blog
|
|||
var post = await _postRepository.GetAsync(id);
|
||||
|
||||
// Check if user is author or has permission
|
||||
if (post.AuthorId != _currentUser.Id && !await AuthorizationService.IsGrantedAsync("Blog.Posts.Publish"))
|
||||
if (post.AuthorId != _currentUser.Id && !await AuthorizationService.IsGrantedAsync("App.Blog.Publish"))
|
||||
{
|
||||
throw new Volo.Abp.Authorization.AbpAuthorizationException();
|
||||
}
|
||||
|
|
@ -283,7 +285,7 @@ namespace Kurs.Platform.Blog
|
|||
var post = await _postRepository.GetAsync(id);
|
||||
|
||||
// Check if user is author or has permission
|
||||
if (post.AuthorId != _currentUser.Id && !await AuthorizationService.IsGrantedAsync("Blog.Posts.Publish"))
|
||||
if (post.AuthorId != _currentUser.Id && !await AuthorizationService.IsGrantedAsync("App.Blog.Publish"))
|
||||
{
|
||||
throw new Volo.Abp.Authorization.AbpAuthorizationException();
|
||||
}
|
||||
|
|
@ -357,7 +359,7 @@ namespace Kurs.Platform.Blog
|
|||
return ObjectMapper.Map<BlogCategory, BlogCategoryDto>(category);
|
||||
}
|
||||
|
||||
[Authorize("Blog.Categories.Create")]
|
||||
[Authorize("App.Blog.Create")]
|
||||
public async Task<BlogCategoryDto> CreateCategoryAsync(CreateUpdateBlogCategoryDto input)
|
||||
{
|
||||
var category = new BlogCategory(
|
||||
|
|
@ -377,7 +379,7 @@ namespace Kurs.Platform.Blog
|
|||
return ObjectMapper.Map<BlogCategory, BlogCategoryDto>(category);
|
||||
}
|
||||
|
||||
[Authorize("Blog.Categories.Update")]
|
||||
[Authorize("App.Blog.Update")]
|
||||
public async Task<BlogCategoryDto> UpdateCategoryAsync(Guid id, CreateUpdateBlogCategoryDto input)
|
||||
{
|
||||
var category = await _categoryRepository.GetAsync(id);
|
||||
|
|
@ -394,7 +396,7 @@ namespace Kurs.Platform.Blog
|
|||
return ObjectMapper.Map<BlogCategory, BlogCategoryDto>(category);
|
||||
}
|
||||
|
||||
[Authorize("Blog.Categories.Delete")]
|
||||
[Authorize("App.Blog.Delete")]
|
||||
public async Task DeleteCategoryAsync(Guid id)
|
||||
{
|
||||
// Check if category has posts
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Text.Json;
|
|||
using System.Threading.Tasks;
|
||||
using Kurs.Languages.Entities;
|
||||
using Kurs.Notifications.Entities;
|
||||
using Kurs.Platform.Blog;
|
||||
using Kurs.Platform.Charts.Dto;
|
||||
using Kurs.Platform.Entities;
|
||||
using Kurs.Platform.Enums;
|
||||
|
|
@ -48,6 +49,8 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
private readonly IRepository<SkillLevel, Guid> _skillLevelRepository;
|
||||
private readonly IRepository<ContactTag, Guid> _contactTagRepository;
|
||||
private readonly IRepository<ContactTitle, Guid> _contactTitleRepository;
|
||||
private readonly IRepository<BlogCategory, Guid> _blogCategoryRepository;
|
||||
private readonly IRepository<BlogPost, Guid> _blogPostsRepository;
|
||||
|
||||
public PlatformDataSeeder(
|
||||
IRepository<Language, Guid> languages,
|
||||
|
|
@ -73,7 +76,9 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
IRepository<Skill, Guid> skillRepository,
|
||||
IRepository<SkillLevel, Guid> skillLevelRepository,
|
||||
IRepository<ContactTag, Guid> contactTagRepository,
|
||||
IRepository<ContactTitle, Guid> contactTitleRepository
|
||||
IRepository<ContactTitle, Guid> contactTitleRepository,
|
||||
IRepository<BlogCategory, Guid> blogCategoryRepository,
|
||||
IRepository<BlogPost, Guid> blogPostsRepository
|
||||
)
|
||||
{
|
||||
_languages = languages;
|
||||
|
|
@ -100,6 +105,8 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
_skillLevelRepository = skillLevelRepository;
|
||||
_contactTagRepository = contactTagRepository;
|
||||
_contactTitleRepository = contactTitleRepository;
|
||||
_blogCategoryRepository = blogCategoryRepository;
|
||||
_blogPostsRepository = blogPostsRepository;
|
||||
}
|
||||
|
||||
private static IConfigurationRoot BuildConfiguration()
|
||||
|
|
@ -543,5 +550,46 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in items.BlogCategories)
|
||||
{
|
||||
var exists = await _blogCategoryRepository.AnyAsync(x => x.Name == item.Name);
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
var newCategory = new BlogCategory(
|
||||
item.Id,
|
||||
item.Name,
|
||||
item.Slug,
|
||||
item.Description
|
||||
)
|
||||
{
|
||||
DisplayOrder = item.DisplayOrder,
|
||||
PostCount = 1
|
||||
};
|
||||
|
||||
await _blogCategoryRepository.InsertAsync(newCategory);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in items.BlogPosts)
|
||||
{
|
||||
var exists = await _blogPostsRepository.AnyAsync(x => x.Title == item.Title);
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
await _blogPostsRepository.InsertAsync(new BlogPost(
|
||||
item.Id,
|
||||
item.Title,
|
||||
item.Slug,
|
||||
item.Content,
|
||||
item.Summary,
|
||||
item.ReadTime,
|
||||
item.CoverImage,
|
||||
item.CategoryId,
|
||||
item.AuthorId
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Kurs.Languages.Entities;
|
||||
using Kurs.Platform.Charts.Dto;
|
||||
using Kurs.Platform.Entities;
|
||||
|
|
@ -32,6 +33,8 @@ public class SeederDto
|
|||
public List<SkillLevelSeedDto> SkillLevels { get; set; }
|
||||
public List<ContactTagSeedDto> ContactTags { get; set; }
|
||||
public List<ContactTitleSeedDto> ContactTitles { get; set; }
|
||||
public List<BlogCategorySeedDto> BlogCategories { get; set; }
|
||||
public List<BlogPostSeedDto> BlogPosts { get; set; }
|
||||
}
|
||||
|
||||
public class ChartsSeedDto
|
||||
|
|
@ -194,5 +197,27 @@ public class ContactTagSeedDto
|
|||
public class ContactTitleSeedDto
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Abbreviation { get; set; }
|
||||
public string Abbreviation { get; set; }
|
||||
}
|
||||
|
||||
public class BlogCategorySeedDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Description { get; set; }
|
||||
public int DisplayOrder { get; set; }
|
||||
}
|
||||
|
||||
public class BlogPostSeedDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Content { get; set; }
|
||||
public string ReadTime { get; set; }
|
||||
public string Summary { get; set; }
|
||||
public string CoverImage { get; set; }
|
||||
public Guid CategoryId { get; set; }
|
||||
public Guid AuthorId { get; set; }
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ namespace Kurs.Platform.Blog
|
|||
public string Content { get; set; }
|
||||
public string Summary { get; set; }
|
||||
public string CoverImage { get; set; }
|
||||
public string ReadTime { get; set; }
|
||||
|
||||
public Guid CategoryId { get; set; }
|
||||
public virtual BlogCategory Category { get; set; }
|
||||
|
|
@ -44,6 +45,8 @@ namespace Kurs.Platform.Blog
|
|||
string slug,
|
||||
string content,
|
||||
string summary,
|
||||
string readTime,
|
||||
string coverImage,
|
||||
Guid categoryId,
|
||||
Guid authorId,
|
||||
Guid? tenantId = null) : base(id)
|
||||
|
|
@ -52,6 +55,8 @@ namespace Kurs.Platform.Blog
|
|||
Slug = slug;
|
||||
Content = content;
|
||||
Summary = summary;
|
||||
ReadTime = readTime;
|
||||
CoverImage = coverImage;
|
||||
CategoryId = categoryId;
|
||||
AuthorId = authorId;
|
||||
TenantId = tenantId;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
|||
namespace Kurs.Platform.Migrations
|
||||
{
|
||||
[DbContext(typeof(PlatformDbContext))]
|
||||
[Migration("20250619131606_AddBlogForumEntities")]
|
||||
[Migration("20250619205823_AddBlogForumEntities")]
|
||||
partial class AddBlogForumEntities
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
|
@ -894,6 +894,9 @@ namespace Kurs.Platform.Migrations
|
|||
b.Property<DateTime?>("PublishedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("ReadTime")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
|
|
@ -79,6 +79,7 @@ namespace Kurs.Platform.Migrations
|
|||
Content = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Summary = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
||||
CoverImage = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
||||
ReadTime = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
CategoryId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
AuthorId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
ViewCount = table.Column<int>(type: "int", nullable: false),
|
||||
|
|
@ -891,6 +891,9 @@ namespace Kurs.Platform.Migrations
|
|||
b.Property<DateTime?>("PublishedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("ReadTime")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ const Blog = () => {
|
|||
: "bg-gray-200 text-gray-700 hover:bg-gray-300"
|
||||
}`}
|
||||
>
|
||||
{category.name} ({category.postCount})
|
||||
{ t(category.name)} ({category.postCount})
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -158,20 +158,20 @@ const Blog = () => {
|
|||
<div className="aspect-w-16 aspect-h-9 relative">
|
||||
<img
|
||||
src={
|
||||
post.coverImage || "https://via.placeholder.com/400x225"
|
||||
post.coverImage
|
||||
}
|
||||
alt={post.title}
|
||||
className="object-cover w-full h-48"
|
||||
/>
|
||||
<div className="absolute top-4 right-4 bg-blue-600 text-white px-3 py-1 rounded-full text-sm">
|
||||
{post.category.name}
|
||||
{t(post.category.name)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-6 flex-1 flex flex-col">
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-3 hover:text-blue-600 transition-colors">
|
||||
{post.title}
|
||||
{t(post.title)}
|
||||
</h2>
|
||||
<p className="text-gray-600 mb-4 flex-1">{post.summary}</p>
|
||||
<p className="text-gray-600 mb-4 flex-1">{t(post.summary)}</p>
|
||||
|
||||
{/* Tags */}
|
||||
{post.tags.length > 0 && (
|
||||
|
|
@ -203,11 +203,7 @@ const Blog = () => {
|
|||
</div>
|
||||
<div className="flex items-center">
|
||||
<Clock size={16} className="mr-1" />
|
||||
{typeof post.content === "string" &&
|
||||
post.content.length > 0
|
||||
? Math.ceil(post.content.length / 1000)
|
||||
: "-"}{" "}
|
||||
dk
|
||||
{post.readTime}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,66 +1,110 @@
|
|||
import React from 'react';
|
||||
import { Link, useParams } from 'react-router-dom'; // Link ve useParams'ı import et
|
||||
import { useLanguage } from '../context/LanguageContext'; // useLanguage hook'unu import et
|
||||
import { blogContent, BlogPostContent } from '../locales/blogContent'; // blogContent ve BlogPostContent interface'ini import et
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import { useLanguage } from "../context/LanguageContext";
|
||||
import { BlogPost, blogService } from "../services/api/blog.service";
|
||||
import { format } from "date-fns";
|
||||
import { tr } from "date-fns/locale";
|
||||
|
||||
interface PostData {
|
||||
image?: string;
|
||||
author?: {
|
||||
id: string;
|
||||
name: string;
|
||||
avatar?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const BlogDetail: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const { t, language } = useLanguage(); // useLanguage hook'unu kullan ve dil bilgisini al
|
||||
const { t } = useLanguage();
|
||||
const [blogPost, setBlogPost] = useState<BlogPost | null>(null);
|
||||
const [postData, setPostData] = useState<PostData | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Basit slug oluşturma fonksiyonu (Blog.tsx'teki ile aynı olmalı)
|
||||
const createSlug = (title: string) => {
|
||||
return title
|
||||
.toLowerCase()
|
||||
.replace(/ /g, '-')
|
||||
.replace(/[^\w-]+/g, '');
|
||||
};
|
||||
useEffect(() => {
|
||||
const fetchBlogPost = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
if (id) {
|
||||
const response = await blogService.getPostBySlug(id);
|
||||
setBlogPost(response);
|
||||
setPostData({
|
||||
image: response.coverImage,
|
||||
author: response.author,
|
||||
});
|
||||
} else {
|
||||
setError("Blog post ID is missing.");
|
||||
}
|
||||
} catch (error: any) {
|
||||
setError(error.message || "Failed to fetch blog post.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// URL'deki slug'a göre blog yazısını bul
|
||||
// blogContent objesinden slug'a karşılık gelen blog yazısını ve mevcut dile göre içeriğini al
|
||||
const postData = blogContent[id || '']; // id undefined olabilir, boş string ile kontrol et
|
||||
const blogPost = postData ? postData[language as 'tr' | 'en'] : undefined; // Mevcut dile göre içeriği al
|
||||
fetchBlogPost();
|
||||
}, [id]);
|
||||
|
||||
if (!blogPost) {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<h1 className="text-2xl font-bold text-gray-900">{t('blog.notFound')}</h1> {/* Çeviri kullan */}
|
||||
<h1 className="text-2xl font-bold text-gray-900">Loading...</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<h1 className="text-2xl font-bold text-gray-900">Error: {error}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!blogPost || !postData) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
{t("blog.notFound")}
|
||||
</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 pt-32 pb-16"> {/* py-16 yerine pt-32 pb-16 kullanıldı */}
|
||||
<div className="min-h-screen bg-gray-50 pt-32 pb-16">
|
||||
<div className="container mx-auto px-4">
|
||||
<Link to="/blog" className="text-blue-600 hover:underline mb-4 inline-block">
|
||||
← {t('blog.backToBlog')} {/* Geri dönüş butonu */}
|
||||
<Link
|
||||
to="/blog"
|
||||
className="text-blue-600 hover:underline mb-4 inline-block"
|
||||
>
|
||||
← {t("blog.backToBlog")}
|
||||
</Link>
|
||||
{/* Blog yazısı görseli */}
|
||||
{postData.image && (
|
||||
<img
|
||||
src={postData.image} // Görsel bilgisi blogContent'ten alınıyor
|
||||
alt={t(blogPost.title)} // Alt metni çevir
|
||||
src={postData.image}
|
||||
alt={t(blogPost.title)}
|
||||
className="w-full h-96 object-cover rounded-lg mb-8"
|
||||
/>
|
||||
)}
|
||||
<h1 className="text-4xl font-bold text-gray-900 mb-6">{t(blogPost.title)}</h1> {/* Çeviri kullan */}
|
||||
{/* Yazar, tarih, okuma süresi gibi bilgiler eklenebilir */}
|
||||
<h1 className="text-4xl font-bold text-gray-900 mb-6">
|
||||
{t(blogPost.title)}
|
||||
</h1>
|
||||
<div className="flex items-center text-sm text-gray-500 space-x-4 mb-8">
|
||||
<div className="flex items-center">
|
||||
{/* <User size={16} className="mr-1" /> */} {/* İkonlar eklenebilir */}
|
||||
<span>{postData.author}</span> {/* Yazar bilgisi blogContent'ten alınıyor */}
|
||||
<span>{postData.author?.name}</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
{/* <Calendar size={16} className="mr-1" /> */}
|
||||
{t(blogPost.date)} {/* Çeviri kullan */}
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
{/* <Clock size={16} className="mr-1" /> */}
|
||||
{blogPost.readTime}
|
||||
{blogPost.publishedAt &&
|
||||
format(new Date(blogPost.publishedAt), "dd MMM yyyy", {
|
||||
locale: tr,
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="prose max-w-none text-gray-800"> {/* Tailwind Typography eklentisi kuruluysa kullanılabilir */}
|
||||
<p>{t(blogPost.content)}</p> {/* Tam içeriği çevirerek göster */}
|
||||
{/* Daha uzun içerik burada paragraflar halinde yer alabilir */}
|
||||
<div className="prose max-w-none text-gray-800">
|
||||
<p>{blogPost.content}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { apiClient } from './config';
|
||||
import { apiClient } from "./config";
|
||||
|
||||
export interface BlogPost {
|
||||
id: string;
|
||||
|
|
@ -6,6 +6,7 @@ export interface BlogPost {
|
|||
slug: string;
|
||||
content?: string;
|
||||
summary: string;
|
||||
readTime: string;
|
||||
coverImage?: string;
|
||||
author: {
|
||||
id: string;
|
||||
|
|
@ -22,6 +23,7 @@ export interface BlogPost {
|
|||
likeCount: number;
|
||||
commentCount: number;
|
||||
isPublished: boolean;
|
||||
isLiked?: boolean;
|
||||
publishedAt?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
|
|
@ -54,6 +56,7 @@ export interface BlogComment {
|
|||
|
||||
export interface CreateBlogPostRequest {
|
||||
title: string;
|
||||
slug: string;
|
||||
content: string;
|
||||
summary: string;
|
||||
categoryId: string;
|
||||
|
|
@ -75,7 +78,7 @@ export interface BlogListParams {
|
|||
tag?: string;
|
||||
search?: string;
|
||||
authorId?: string;
|
||||
sortBy?: 'latest' | 'popular' | 'trending';
|
||||
sortBy?: "latest" | "popular" | "trending";
|
||||
}
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
|
|
@ -87,23 +90,45 @@ export interface PaginatedResponse<T> {
|
|||
}
|
||||
|
||||
class BlogService {
|
||||
async getPosts(params: BlogListParams = {}): Promise<PaginatedResponse<BlogPost>> {
|
||||
const response = await apiClient.get<PaginatedResponse<BlogPost>>('/api/app/blog/posts', { params });
|
||||
async getPosts(
|
||||
params: BlogListParams = {}
|
||||
): Promise<PaginatedResponse<BlogPost>> {
|
||||
const response = await apiClient.get<PaginatedResponse<BlogPost>>(
|
||||
"/api/app/blog/posts",
|
||||
{ params }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getPost(idOrSlug: string): Promise<BlogPost> {
|
||||
const response = await apiClient.get<BlogPost>(`/api/app/blog/posts/${idOrSlug}`);
|
||||
async getPostById(id: string): Promise<BlogPost> {
|
||||
const response = await apiClient.get<BlogPost>(`/api/app/blog/posts/${id}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getPostBySlug(slug: string): Promise<BlogPost> {
|
||||
const response = await apiClient.get<BlogPost>(
|
||||
`/api/app/blog/post-by-slug`,
|
||||
{ params: { slug } }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async createPost(data: CreateBlogPostRequest): Promise<BlogPost> {
|
||||
const response = await apiClient.post<BlogPost>('/api/app/blog/posts', data);
|
||||
const response = await apiClient.post<BlogPost>(
|
||||
"/api/app/blog/posts",
|
||||
data
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async updatePost(id: string, data: Partial<CreateBlogPostRequest>): Promise<BlogPost> {
|
||||
const response = await apiClient.put<BlogPost>(`/api/app/blog/posts/${id}`, data);
|
||||
async updatePost(
|
||||
id: string,
|
||||
data: Partial<CreateBlogPostRequest>
|
||||
): Promise<BlogPost> {
|
||||
const response = await apiClient.put<BlogPost>(
|
||||
`/api/app/blog/posts/${id}`,
|
||||
data
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
|
|
@ -111,18 +136,59 @@ class BlogService {
|
|||
await apiClient.delete(`/api/app/blog/posts/${id}`);
|
||||
}
|
||||
|
||||
async getCategories(): Promise<BlogCategory[]> {
|
||||
const response = await apiClient.get<BlogCategory[]>('/api/app/blog/categories');
|
||||
async publishPost(id: string): Promise<BlogPost> {
|
||||
const response = await apiClient.post<BlogPost>(
|
||||
`/api/app/blog/posts/${id}/publish`
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async unpublishPost(id: string): Promise<BlogPost> {
|
||||
const response = await apiClient.post<BlogPost>(
|
||||
`/api/app/blog/posts/${id}/unpublish`
|
||||
);
|
||||
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[]> {
|
||||
const response = await apiClient.get<BlogCategory[]>(
|
||||
"/api/app/blog/categories"
|
||||
);
|
||||
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`);
|
||||
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);
|
||||
const response = await apiClient.post<BlogComment>(
|
||||
"/api/app/blog/comments",
|
||||
data
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
|
|
@ -130,25 +196,12 @@ class BlogService {
|
|||
await apiClient.delete(`/api/app/blog/comments/${id}`);
|
||||
}
|
||||
|
||||
async likePost(postId: string): Promise<void> {
|
||||
await apiClient.post(`/api/app/blog/posts/${postId}/like`);
|
||||
async likeComment(id: string): Promise<void> {
|
||||
await apiClient.post(`/api/app/blog/comments/${id}/like`);
|
||||
}
|
||||
|
||||
async unlikePost(postId: string): Promise<void> {
|
||||
await apiClient.delete(`/api/app/blog/posts/${postId}/like`);
|
||||
}
|
||||
|
||||
async likeComment(commentId: string): Promise<void> {
|
||||
await apiClient.post(`/api/app/blog/comments/${commentId}/like`);
|
||||
}
|
||||
|
||||
async unlikeComment(commentId: string): Promise<void> {
|
||||
await apiClient.delete(`/api/app/blog/comments/${commentId}/like`);
|
||||
}
|
||||
|
||||
async getTags(): Promise<string[]> {
|
||||
const response = await apiClient.get<string[]>('/api/app/blog/tags');
|
||||
return response.data;
|
||||
async unlikeComment(id: string): Promise<void> {
|
||||
await apiClient.delete(`/api/app/blog/comments/${id}/like`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict';
|
|||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||
}, {
|
||||
"url": "index.html",
|
||||
"revision": "0.n85sh48g8go"
|
||||
"revision": "0.6c62okhp6do"
|
||||
}], {});
|
||||
workbox.cleanupOutdatedCaches();
|
||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||
|
|
|
|||
|
|
@ -1,55 +1,55 @@
|
|||
import apiService from "@/services/api.service";
|
||||
import apiService from '@/services/api.service'
|
||||
|
||||
export interface BlogPost {
|
||||
id: string;
|
||||
title: string;
|
||||
slug: string;
|
||||
content?: string;
|
||||
summary: string;
|
||||
coverImage?: string;
|
||||
id: string
|
||||
title: string
|
||||
slug: string
|
||||
content?: string
|
||||
summary: string
|
||||
coverImage?: string
|
||||
author: {
|
||||
id: string;
|
||||
name: string;
|
||||
avatar?: string;
|
||||
};
|
||||
id: string
|
||||
name: string
|
||||
avatar?: string
|
||||
}
|
||||
category: {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
};
|
||||
tags: string[];
|
||||
viewCount: number;
|
||||
likeCount: number;
|
||||
commentCount: number;
|
||||
isPublished: boolean;
|
||||
publishedAt?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
id: string
|
||||
name: string
|
||||
slug: string
|
||||
}
|
||||
tags: string[]
|
||||
viewCount: number
|
||||
likeCount: number
|
||||
commentCount: number
|
||||
isPublished: boolean
|
||||
publishedAt?: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface BlogCategory {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string;
|
||||
postCount: number;
|
||||
id: string
|
||||
name: string
|
||||
slug: string
|
||||
description?: string
|
||||
postCount: number
|
||||
}
|
||||
|
||||
export interface BlogComment {
|
||||
id: string;
|
||||
postId: string;
|
||||
content: string;
|
||||
id: string
|
||||
postId: string
|
||||
content: string
|
||||
author: {
|
||||
id: string;
|
||||
name: string;
|
||||
avatar?: string;
|
||||
};
|
||||
parentId?: string;
|
||||
replies?: BlogComment[];
|
||||
likeCount: number;
|
||||
isLiked?: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
id: string
|
||||
name: string
|
||||
avatar?: string
|
||||
}
|
||||
parentId?: string
|
||||
replies?: BlogComment[]
|
||||
likeCount: number
|
||||
isLiked?: boolean
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface CreateUpdateBlogPostDto {
|
||||
|
|
@ -72,27 +72,27 @@ export interface CreateUpdateBlogCategoryDto {
|
|||
}
|
||||
|
||||
export interface CreateCommentDto {
|
||||
postId: string;
|
||||
content: string;
|
||||
parentId?: string;
|
||||
postId: string
|
||||
content: string
|
||||
parentId?: string
|
||||
}
|
||||
|
||||
export interface BlogListParams {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
categoryId?: string;
|
||||
tag?: string;
|
||||
search?: string;
|
||||
authorId?: string;
|
||||
sortBy?: 'latest' | 'popular' | 'trending';
|
||||
page?: number
|
||||
pageSize?: number
|
||||
categoryId?: string
|
||||
tag?: string
|
||||
search?: string
|
||||
authorId?: string
|
||||
sortBy?: 'latest' | 'popular' | 'trending'
|
||||
}
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
items: T[];
|
||||
totalCount: number;
|
||||
pageNumber: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
items: T[]
|
||||
totalCount: number
|
||||
pageNumber: number
|
||||
pageSize: number
|
||||
totalPages: number
|
||||
}
|
||||
|
||||
class BlogService {
|
||||
|
|
@ -100,161 +100,169 @@ class BlogService {
|
|||
const response = await apiService.fetchData<PaginatedResponse<BlogPost>>({
|
||||
url: '/api/app/blog/posts',
|
||||
method: 'GET',
|
||||
params
|
||||
});
|
||||
params,
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
async getPostBySlug(slug: string): Promise<BlogPost> {
|
||||
const response = await apiService.fetchData<BlogPost>({
|
||||
url : `/api/app/blog/posts/post-by-slug/${slug}`,
|
||||
method: 'GET',
|
||||
})
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getPost(idOrSlug: string): Promise<BlogPost> {
|
||||
const response = await apiService.fetchData<BlogPost>({
|
||||
url: `/api/app/blog/posts/${idOrSlug}`,
|
||||
method: 'GET'
|
||||
});
|
||||
return response.data;
|
||||
method: 'GET',
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
async createPost(data: CreateUpdateBlogPostDto): Promise<BlogPost> {
|
||||
const response = await apiService.fetchData<BlogPost>({
|
||||
url: '/api/app/blog/posts',
|
||||
url: '/api/app/blog/post',
|
||||
method: 'POST',
|
||||
data: data as any
|
||||
});
|
||||
return response.data;
|
||||
data: data as any,
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
async updatePost(id: string, data: CreateUpdateBlogPostDto): Promise<BlogPost> {
|
||||
const response = await apiService.fetchData<BlogPost>({
|
||||
url: `/api/app/blog/posts/${id}`,
|
||||
url: `/api/app/blog/${id}/post`,
|
||||
method: 'PUT',
|
||||
data: data as any
|
||||
});
|
||||
return response.data;
|
||||
data: data as any,
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
async deletePost(id: string): Promise<void> {
|
||||
await apiService.fetchData({
|
||||
url: `/api/app/blog/posts/${id}`,
|
||||
method: 'DELETE'
|
||||
});
|
||||
url: `/api/app/blog/${id}/post`,
|
||||
method: 'DELETE',
|
||||
})
|
||||
}
|
||||
|
||||
async publishPost(id: string): Promise<BlogPost> {
|
||||
const response = await apiService.fetchData<BlogPost>({
|
||||
url: `/api/app/blog/posts/${id}/publish`,
|
||||
method: 'POST'
|
||||
});
|
||||
return response.data;
|
||||
url: `/api/app/blog/${id}/publish-post`,
|
||||
method: 'POST',
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
async unpublishPost(id: string): Promise<BlogPost> {
|
||||
const response = await apiService.fetchData<BlogPost>({
|
||||
url: `/api/app/blog/posts/${id}/unpublish`,
|
||||
method: 'POST'
|
||||
});
|
||||
return response.data;
|
||||
url: `/api/app/blog/${id}/unpublish-post`,
|
||||
method: 'POST',
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
async getCategories(): Promise<BlogCategory[]> {
|
||||
const response = await apiService.fetchData<BlogCategory[]>({
|
||||
url: '/api/app/blog/categories',
|
||||
method: 'GET'
|
||||
});
|
||||
return response.data;
|
||||
method: 'GET',
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
async getComments(postId: string): Promise<BlogComment[]> {
|
||||
const response = await apiService.fetchData<BlogComment[]>({
|
||||
url: `/api/app/blog/posts/${postId}/comments`,
|
||||
method: 'GET'
|
||||
});
|
||||
return response.data;
|
||||
method: 'GET',
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
async createComment(data: CreateCommentDto): Promise<BlogComment> {
|
||||
const response = await apiService.fetchData<BlogComment>({
|
||||
url: '/api/app/blog/comments',
|
||||
method: 'POST',
|
||||
data: data as any
|
||||
});
|
||||
return response.data;
|
||||
data: data as any,
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
async deleteComment(id: string): Promise<void> {
|
||||
await apiService.fetchData({
|
||||
url: `/api/app/blog/comments/${id}`,
|
||||
method: 'DELETE'
|
||||
});
|
||||
method: 'DELETE',
|
||||
})
|
||||
}
|
||||
|
||||
async likePost(postId: string): Promise<void> {
|
||||
await apiService.fetchData({
|
||||
url: `/api/app/blog/posts/${postId}/like`,
|
||||
method: 'POST'
|
||||
});
|
||||
url: `/api/app/blog/${postId}/like-post`,
|
||||
method: 'POST',
|
||||
})
|
||||
}
|
||||
|
||||
async unlikePost(postId: string): Promise<void> {
|
||||
await apiService.fetchData({
|
||||
url: `/api/app/blog/posts/${postId}/like`,
|
||||
method: 'DELETE'
|
||||
});
|
||||
url: `/api/app/blog/${postId}/unlike-post`,
|
||||
method: 'DELETE',
|
||||
})
|
||||
}
|
||||
|
||||
async likeComment(commentId: string): Promise<void> {
|
||||
await apiService.fetchData({
|
||||
url: `/api/app/blog/comments/${commentId}/like`,
|
||||
method: 'POST'
|
||||
});
|
||||
method: 'POST',
|
||||
})
|
||||
}
|
||||
|
||||
async unlikeComment(commentId: string): Promise<void> {
|
||||
await apiService.fetchData({
|
||||
url: `/api/app/blog/comments/${commentId}/like`,
|
||||
method: 'DELETE'
|
||||
});
|
||||
method: 'DELETE',
|
||||
})
|
||||
}
|
||||
|
||||
async getTags(): Promise<string[]> {
|
||||
const response = await apiService.fetchData<string[]>({
|
||||
url: '/api/app/blog/tags',
|
||||
method: 'GET'
|
||||
});
|
||||
return response.data;
|
||||
method: 'GET',
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
// Category methods
|
||||
async getCategory(id: string): Promise<BlogCategory> {
|
||||
const response = await apiService.fetchData<BlogCategory>({
|
||||
url: `/api/app/blog/categories/${id}`,
|
||||
method: 'GET'
|
||||
});
|
||||
return response.data;
|
||||
url: `/api/app/blog/${id}/category`,
|
||||
method: 'GET',
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
async createCategory(data: CreateUpdateBlogCategoryDto): Promise<BlogCategory> {
|
||||
const response = await apiService.fetchData<BlogCategory>({
|
||||
url: '/api/app/blog/categories',
|
||||
url: '/api/app/blog/category',
|
||||
method: 'POST',
|
||||
data: data as any
|
||||
});
|
||||
return response.data;
|
||||
data: data as any,
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
async updateCategory(id: string, data: CreateUpdateBlogCategoryDto): Promise<BlogCategory> {
|
||||
const response = await apiService.fetchData<BlogCategory>({
|
||||
url: `/api/app/blog/categories/${id}`,
|
||||
url: `/api/app/blog/${id}/category`,
|
||||
method: 'PUT',
|
||||
data: data as any
|
||||
});
|
||||
return response.data;
|
||||
data: data as any,
|
||||
})
|
||||
return response.data
|
||||
}
|
||||
|
||||
async deleteCategory(id: string): Promise<void> {
|
||||
await apiService.fetchData({
|
||||
url: `/api/app/blog/categories/${id}`,
|
||||
method: 'DELETE'
|
||||
});
|
||||
url: `/api/app/blog/${id}/category`,
|
||||
method: 'DELETE',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const blogService = new BlogService();
|
||||
export const blogService = new BlogService()
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import {
|
|||
} from '@/services/blog.service'
|
||||
import { format } from 'date-fns'
|
||||
import { tr } from 'date-fns/locale'
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import { Field, FieldProps, Form, Formik } from 'formik'
|
||||
import * as Yup from 'yup'
|
||||
import toast from '@/components/ui/toast'
|
||||
import Notification from '@/components/ui/Notification'
|
||||
|
|
@ -30,6 +30,8 @@ import Th from '@/components/ui/Table/Th'
|
|||
import THead from '@/components/ui/Table/THead'
|
||||
import TBody from '@/components/ui/Table/TBody'
|
||||
import Td from '@/components/ui/Table/Td'
|
||||
import { SelectBoxOption } from '@/shared/types'
|
||||
import { enumToList } from '@/utils/enumUtils'
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
title: Yup.string().required('Başlık gereklidir'),
|
||||
|
|
@ -54,6 +56,10 @@ const BlogManagement = () => {
|
|||
const [categoryModalVisible, setCategoryModalVisible] = useState(false)
|
||||
const [editingPost, setEditingPost] = useState<BlogPost | null>(null)
|
||||
const [editingCategory, setEditingCategory] = useState<BlogCategory | null>(null)
|
||||
const categoryItems = categories?.map((cat) => ({
|
||||
value: cat.id,
|
||||
label: cat.name,
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
loadData()
|
||||
|
|
@ -212,7 +218,7 @@ const BlogManagement = () => {
|
|||
description: values.description,
|
||||
icon: values.icon,
|
||||
displayOrder: values.displayOrder,
|
||||
isActive: values.isActive
|
||||
isActive: values.isActive,
|
||||
}
|
||||
|
||||
if (editingCategory) {
|
||||
|
|
@ -271,7 +277,7 @@ const BlogManagement = () => {
|
|||
description: editingCategory.description || '',
|
||||
icon: '',
|
||||
displayOrder: 0,
|
||||
isActive: true
|
||||
isActive: true,
|
||||
}
|
||||
: {
|
||||
name: '',
|
||||
|
|
@ -279,7 +285,7 @@ const BlogManagement = () => {
|
|||
description: '',
|
||||
icon: '',
|
||||
displayOrder: 0,
|
||||
isActive: true
|
||||
isActive: true,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -287,11 +293,11 @@ const BlogManagement = () => {
|
|||
<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}>
|
||||
<Button variant="solid" size="xs" icon={<HiPlus />} onClick={handleCreate}>
|
||||
Yeni Blog Yazısı
|
||||
</Button>
|
||||
) : (
|
||||
<Button variant="solid" size='xs' icon={<HiPlus />} onClick={handleCreateCategory}>
|
||||
<Button variant="solid" size="xs" icon={<HiPlus />} onClick={handleCreateCategory}>
|
||||
Yeni Kategori
|
||||
</Button>
|
||||
)}
|
||||
|
|
@ -304,13 +310,13 @@ const BlogManagement = () => {
|
|||
className={`pb-2 px-1 ${activeTab === 'posts' ? 'border-b-2 border-blue-600 text-blue-600' : 'text-gray-600'}`}
|
||||
onClick={() => setActiveTab('posts')}
|
||||
>
|
||||
Blog Yazıları
|
||||
<b>Blog Yazıları</b>
|
||||
</button>
|
||||
<button
|
||||
className={`pb-2 px-1 ${activeTab === 'categories' ? 'border-b-2 border-blue-600 text-blue-600' : 'text-gray-600'}`}
|
||||
onClick={() => setActiveTab('categories')}
|
||||
>
|
||||
Kategoriler
|
||||
<b>Kategoriler</b>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -413,7 +419,11 @@ const BlogManagement = () => {
|
|||
<Td>{category.postCount}</Td>
|
||||
<Td>
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm" icon={<HiPencil />} onClick={() => handleEditCategory(category)} />
|
||||
<Button
|
||||
size="sm"
|
||||
icon={<HiPencil />}
|
||||
onClick={() => handleEditCategory(category)}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="solid"
|
||||
|
|
@ -454,7 +464,13 @@ const BlogManagement = () => {
|
|||
invalid={errors.title && touched.title}
|
||||
errorMessage={errors.title}
|
||||
>
|
||||
<Field type="text" name="title" placeholder="Blog başlığı" component={Input} />
|
||||
<Field
|
||||
type="text"
|
||||
name="title"
|
||||
placeholder="Blog başlığı"
|
||||
component={Input}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
|
|
@ -477,15 +493,16 @@ const BlogManagement = () => {
|
|||
errorMessage={errors.categoryId}
|
||||
>
|
||||
<Field name="categoryId">
|
||||
{({ field, form }: any) => (
|
||||
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
||||
<Select
|
||||
field={field}
|
||||
form={form}
|
||||
options={categories.map((cat) => ({
|
||||
value: cat.id,
|
||||
label: cat.name,
|
||||
}))}
|
||||
placeholder="Kategori seçiniz"
|
||||
options={categoryItems}
|
||||
isClearable={true}
|
||||
value={categoryItems.filter(
|
||||
(option) => option.value === values.categoryId,
|
||||
)}
|
||||
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
|
@ -575,6 +592,7 @@ const BlogManagement = () => {
|
|||
errorMessage={errors.name}
|
||||
>
|
||||
<Field
|
||||
autoFocus={true}
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder="Kategori ismi"
|
||||
|
|
@ -587,12 +605,7 @@ const BlogManagement = () => {
|
|||
invalid={errors.slug && touched.slug}
|
||||
errorMessage={errors.slug}
|
||||
>
|
||||
<Field
|
||||
type="text"
|
||||
name="slug"
|
||||
placeholder="kategori-slug"
|
||||
component={Input}
|
||||
/>
|
||||
<Field type="text" name="slug" placeholder="kategori-slug" component={Input} />
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
|
|
@ -609,26 +622,12 @@ const BlogManagement = () => {
|
|||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
label="İkon (Emoji)"
|
||||
>
|
||||
<Field
|
||||
type="text"
|
||||
name="icon"
|
||||
placeholder="📚"
|
||||
component={Input}
|
||||
/>
|
||||
<FormItem label="İkon (Emoji)">
|
||||
<Field type="text" name="icon" placeholder="📚" component={Input} />
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
label="Sıralama"
|
||||
>
|
||||
<Field
|
||||
type="number"
|
||||
name="displayOrder"
|
||||
placeholder="0"
|
||||
component={Input}
|
||||
/>
|
||||
<FormItem label="Sıralama">
|
||||
<Field type="number" name="displayOrder" placeholder="0" component={Input} />
|
||||
</FormItem>
|
||||
|
||||
<FormItem>
|
||||
|
|
@ -646,17 +645,10 @@ const BlogManagement = () => {
|
|||
|
||||
<FormItem>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="solid"
|
||||
type="submit"
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<Button variant="solid" type="submit" loading={isSubmitting}>
|
||||
{editingCategory ? 'Güncelle' : 'Oluştur'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="plain"
|
||||
onClick={() => setCategoryModalVisible(false)}
|
||||
>
|
||||
<Button variant="plain" onClick={() => setCategoryModalVisible(false)}>
|
||||
İptal
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue