Blog Türkçe İngilizce Content

This commit is contained in:
Sedat Öztürk 2025-06-22 15:44:11 +03:00
parent a663cc0079
commit d0d255f41e
18 changed files with 112 additions and 70 deletions

View file

@ -8,7 +8,8 @@ namespace Kurs.Platform.Blog
{ {
public string Title { get; set; } public string Title { get; set; }
public string Slug { get; set; } public string Slug { get; set; }
public string Content { get; set; } public string ContentTr { get; set; }
public string ContentEn { get; set; }
public string Summary { get; set; } public string Summary { get; set; }
public string CoverImage { get; set; } public string CoverImage { get; set; }
@ -44,7 +45,8 @@ namespace Kurs.Platform.Blog
{ {
public string Title { get; set; } public string Title { get; set; }
public string Slug { get; set; } public string Slug { get; set; }
public string Content { get; set; } public string ContentTr { get; set; }
public string ContentEn { get; set; }
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; }
@ -65,7 +67,8 @@ 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 string ContentTr { get; set; }
public string ContentEn { get; set; }
public BlogCategoryDto Category { get; set; } public BlogCategoryDto Category { get; set; }
public AuthorDto Author { get; set; } public AuthorDto Author { get; set; }

View file

@ -126,7 +126,8 @@ namespace Kurs.Platform.Blog
GuidGenerator.Create(), GuidGenerator.Create(),
input.Title, input.Title,
input.Slug, input.Slug,
input.Content, input.ContentTr,
input.ContentEn,
input.Summary, input.Summary,
input.ReadTime, input.ReadTime,
input.CoverImage, input.CoverImage,
@ -160,7 +161,8 @@ namespace Kurs.Platform.Blog
post.Slug = input.Slug; post.Slug = input.Slug;
post.Summary = input.Summary; post.Summary = input.Summary;
post.CoverImage = input.CoverImage; post.CoverImage = input.CoverImage;
post.Content = input.Content; post.ContentTr = input.ContentTr;
post.ContentEn = input.ContentEn;
if (input.IsPublished) post.Publish(); else post.Unpublish(); if (input.IsPublished) post.Publish(); else post.Unpublish();

View file

@ -582,7 +582,8 @@ public class PlatformDataSeeder : IDataSeedContributor, ITransientDependency
item.Id, item.Id,
item.Title, item.Title,
item.Slug, item.Slug,
item.Content, item.ContentTr,
item.ContentEn,
item.Summary, item.Summary,
item.ReadTime, item.ReadTime,
item.CoverImage, item.CoverImage,

File diff suppressed because one or more lines are too long

View file

@ -215,7 +215,8 @@ public class BlogPostSeedDto
public Guid Id { get; set; } public Guid Id { get; set; }
public string Title { get; set; } public string Title { get; set; }
public string Slug { get; set; } public string Slug { get; set; }
public string Content { get; set; } public string ContentTr { get; set; }
public string ContentEn { get; set; }
public string ReadTime { get; set; } public string ReadTime { get; set; }
public string Summary { get; set; } public string Summary { get; set; }
public string CoverImage { get; set; } public string CoverImage { get; set; }

View file

@ -10,7 +10,8 @@ namespace Kurs.Platform.Blog
public string Title { get; set; } public string Title { get; set; }
public string Slug { get; set; } public string Slug { get; set; }
public string Content { get; set; } public string ContentTr { get; set; }
public string ContentEn { get; set; }
public string Summary { get; set; } public string Summary { get; set; }
public string CoverImage { get; set; } public string CoverImage { get; set; }
public string ReadTime { get; set; } public string ReadTime { get; set; }
@ -35,7 +36,8 @@ namespace Kurs.Platform.Blog
Guid id, Guid id,
string title, string title,
string slug, string slug,
string content, string contentTr,
string contentEn,
string summary, string summary,
string readTime, string readTime,
string coverImage, string coverImage,
@ -45,7 +47,8 @@ namespace Kurs.Platform.Blog
{ {
Title = title; Title = title;
Slug = slug; Slug = slug;
Content = content; ContentTr = contentTr;
ContentEn = contentEn;
Summary = summary; Summary = summary;
ReadTime = readTime; ReadTime = readTime;
CoverImage = coverImage; CoverImage = coverImage;

View file

@ -415,7 +415,8 @@ public class PlatformDbContext :
b.Property(x => x.Title).IsRequired().HasMaxLength(256); b.Property(x => x.Title).IsRequired().HasMaxLength(256);
b.Property(x => x.Slug).IsRequired().HasMaxLength(256); b.Property(x => x.Slug).IsRequired().HasMaxLength(256);
b.Property(x => x.Summary).IsRequired().HasMaxLength(512); b.Property(x => x.Summary).IsRequired().HasMaxLength(512);
b.Property(x => x.Content).IsRequired(); b.Property(x => x.ContentTr).IsRequired();
b.Property(x => x.ContentEn).IsRequired();
b.Property(x => x.CoverImage).HasMaxLength(512); b.Property(x => x.CoverImage).HasMaxLength(512);
b.HasIndex(x => x.Slug); b.HasIndex(x => x.Slug);

View file

@ -13,8 +13,8 @@ using Volo.Abp.EntityFrameworkCore;
namespace Kurs.Platform.Migrations namespace Kurs.Platform.Migrations
{ {
[DbContext(typeof(PlatformDbContext))] [DbContext(typeof(PlatformDbContext))]
[Migration("20250622112214_AddBlogEntities")] [Migration("20250622123512_AddBlog")]
partial class AddBlogEntities partial class AddBlog
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -746,7 +746,11 @@ namespace Kurs.Platform.Migrations
.HasColumnType("nvarchar(40)") .HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp"); .HasColumnName("ConcurrencyStamp");
b.Property<string>("Content") b.Property<string>("ContentEn")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ContentTr")
.IsRequired() .IsRequired()
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");

View file

@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Kurs.Platform.Migrations namespace Kurs.Platform.Migrations
{ {
/// <inheritdoc /> /// <inheritdoc />
public partial class AddBlogEntities : Migration public partial class AddBlog : Migration
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
@ -45,7 +45,8 @@ namespace Kurs.Platform.Migrations
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true), TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false), Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Slug = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false), Slug = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Content = table.Column<string>(type: "nvarchar(max)", nullable: false), ContentTr = table.Column<string>(type: "nvarchar(max)", nullable: false),
ContentEn = table.Column<string>(type: "nvarchar(max)", nullable: false),
Summary = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false), Summary = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
CoverImage = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true), CoverImage = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
ReadTime = table.Column<string>(type: "nvarchar(max)", nullable: true), ReadTime = table.Column<string>(type: "nvarchar(max)", nullable: true),

View file

@ -743,7 +743,11 @@ namespace Kurs.Platform.Migrations
.HasColumnType("nvarchar(40)") .HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp"); .HasColumnName("ConcurrencyStamp");
b.Property<string>("Content") b.Property<string>("ContentEn")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ContentTr")
.IsRequired() .IsRequired()
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");

View file

@ -30,12 +30,12 @@ function App() {
<Layout> <Layout>
<Routes> <Routes>
<Route path="/" element={<Home />} /> <Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/products" element={<Products />} /> <Route path="/products" element={<Products />} />
<Route path="/services" element={<Services />} /> <Route path="/services" element={<Services />} />
<Route path="/about" element={<About />} />
<Route path="/blog" element={<Blog />} /> <Route path="/blog" element={<Blog />} />
<Route path="/contact" element={<Contact />} />
<Route path="/blog/:id" element={<BlogDetail />} /> <Route path="/blog/:id" element={<BlogDetail />} />
<Route path="/contact" element={<Contact />} />
<Route path="*" element={<NotFound />} /> <Route path="*" element={<NotFound />} />
</Routes> </Routes>
</Layout> </Layout>

File diff suppressed because one or more lines are too long

View file

@ -16,7 +16,7 @@ interface PostData {
const BlogDetail: React.FC = () => { const BlogDetail: React.FC = () => {
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const { t } = useLanguage(); const { language, setLanguage, t } = useLanguage();
const [blogPost, setBlogPost] = useState<BlogPost | null>(null); const [blogPost, setBlogPost] = useState<BlogPost | null>(null);
const [postData, setPostData] = useState<PostData | null>(null); const [postData, setPostData] = useState<PostData | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@ -103,7 +103,7 @@ const BlogDetail: React.FC = () => {
})} })}
</div> </div>
</div> </div>
<div className="prose max-w-none text-gray-800" dangerouslySetInnerHTML={{ __html: t(blogPost.content!!) || "" }} /> <div className="prose max-w-none text-gray-800" dangerouslySetInnerHTML={{ __html: language == "tr" ? t(blogPost.contentTr!!) : t(blogPost.contentEn!!) }} />
</div> </div>
</div> </div>
); );

View file

@ -4,7 +4,8 @@ export interface BlogPost {
id: string; id: string;
title: string; title: string;
slug: string; slug: string;
content?: string; contentTr?: string;
contentEn?: string;
summary: string; summary: string;
readTime: string; readTime: string;
coverImage?: string; coverImage?: string;
@ -58,7 +59,8 @@ export interface BlogComment {
export interface CreateBlogPostRequest { export interface CreateBlogPostRequest {
title: string; title: string;
slug: string; slug: string;
content: string; contentTr: string;
contentEn: string;
summary: string; summary: string;
categoryId: string; categoryId: string;
tags: string[]; tags: string[];

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.6hosm5icco8" "revision": "0.pd7p0avqcno"
}], {}); }], {});
workbox.cleanupOutdatedCaches(); workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {

View file

@ -63,12 +63,6 @@ const adminRoutes: Routes = [
component: lazy(() => import('@/views/blog/BlogManagement')), component: lazy(() => import('@/views/blog/BlogManagement')),
authority: [], authority: [],
}, },
{
key: ROUTES_ENUM.admin.forum.management,
path: ROUTES_ENUM.admin.forum.management,
component: lazy(() => import('@/views/forum/AdminView')),
authority: [],
},
] ]
export { adminRoutes } export { adminRoutes }

View file

@ -4,7 +4,8 @@ export interface BlogPost {
id: string id: string
title: string title: string
slug: string slug: string
content?: string contentTr?: string
contentEn?: string
summary: string summary: string
coverImage?: string coverImage?: string
author: { author: {
@ -58,7 +59,8 @@ export interface BlogComment {
export interface CreateUpdateBlogPostDto { export interface CreateUpdateBlogPostDto {
title: string title: string
slug: string slug: string
content: string contentTr: string
contentEn: string
summary: string summary: string
categoryId: string categoryId: string
tags: string[] tags: string[]

View file

@ -31,11 +31,14 @@ 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 { Checkbox } from '@/components/ui' import { Checkbox, Tabs } from '@/components/ui'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { ConfirmDialog } from '@/components/shared' import { ConfirmDialog } from '@/components/shared'
import { useStoreState } from '@/store/store' import { useStoreState } from '@/store/store'
import TabList from '@/components/ui/Tabs/TabList'
import TabNav from '@/components/ui/Tabs/TabNav'
import TabContent from '@/components/ui/Tabs/TabContent'
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
title: Yup.string().required(), title: Yup.string().required(),
@ -84,8 +87,8 @@ const BlogManagement = () => {
blogService.getPosts({ pageSize: 100 }), blogService.getPosts({ pageSize: 100 }),
blogService.getCategories(), blogService.getCategories(),
]) ])
setCategories(categoriesData.filter(a=>a.name.startsWith("blog"))) setCategories(categoriesData.filter((a) => a.name.startsWith('blog')))
setPosts(postsData.items.filter(a=> a.title.startsWith("blog"))) setPosts(postsData.items.filter((a) => a.title.startsWith('blog')))
} catch (error) { } catch (error) {
toast.push( toast.push(
<Notification title="Hata" type="danger"> <Notification title="Hata" type="danger">
@ -139,7 +142,8 @@ const BlogManagement = () => {
const data: CreateUpdateBlogPostDto = { const data: CreateUpdateBlogPostDto = {
title: values.title, title: values.title,
slug: values.slug, slug: values.slug,
content: values.content, contentTr: values.contentTr,
contentEn: values.contentEn,
summary: values.summary, summary: values.summary,
categoryId: values.categoryId, categoryId: values.categoryId,
tags: values.tags ? values.tags.split(',').map((t: string) => t.trim()) : [], tags: values.tags ? values.tags.split(',').map((t: string) => t.trim()) : [],
@ -311,7 +315,8 @@ const BlogManagement = () => {
title: editingPost.title, title: editingPost.title,
slug: editingPost.slug, slug: editingPost.slug,
summary: editingPost.summary, summary: editingPost.summary,
content: editingPost.content, contentTr: editingPost.contentTr,
contentEn: editingPost.contentEn,
categoryId: editingPost.category.id, categoryId: editingPost.category.id,
tags: editingPost.tags.join(', '), tags: editingPost.tags.join(', '),
coverImage: editingPost.coverImage || '', coverImage: editingPost.coverImage || '',
@ -389,7 +394,7 @@ const BlogManagement = () => {
</div> </div>
{activeTab === 'posts' ? ( {activeTab === 'posts' ? (
<Table compact > <Table compact>
<THead> <THead>
<Tr> <Tr>
<Th>{translate('::blog.posts.post.title')}</Th> <Th>{translate('::blog.posts.post.title')}</Th>
@ -641,19 +646,44 @@ const BlogManagement = () => {
<Field type="text" name="coverImage" component={Input} /> <Field type="text" name="coverImage" component={Input} />
</FormItem> </FormItem>
<Tabs defaultValue="tr" variant="pill">
<TabList className="flex-wrap border-b mb-4 bg-slate-50 rounded-t">
<TabNav value="tr">Türkçe</TabNav>
<TabNav value="en">English</TabNav>
</TabList>
<TabContent value="tr">
<FormItem <FormItem
label={translate('::blog.posts.post.content')} label={translate('::blog.posts.post.content')}
asterisk asterisk
invalid={!!errors.content} invalid={!!errors.contentTr}
errorMessage={errors.content} errorMessage={errors.contentTr}
> >
<ReactQuill <ReactQuill
theme="snow" theme="snow"
value={values.content} value={values.contentTr}
onChange={(val: string) => setFieldValue('content', val)} onChange={(val: string) => setFieldValue('contentTr', val)}
style={{ height: '300px', marginBottom: '50px' }} style={{ height: '300px', marginBottom: '50px' }}
/> />
</FormItem> </FormItem>
</TabContent>
<TabContent value="en">
<FormItem
label={translate('::blog.posts.post.content')}
asterisk
invalid={!!errors.contentEn}
errorMessage={errors.contentEn}
>
<ReactQuill
theme="snow"
value={values.contentEn}
onChange={(val: string) => setFieldValue('contentEn', val)}
style={{ height: '300px', marginBottom: '50px' }}
/>
</FormItem>
</TabContent>
</Tabs>
<FormItem <FormItem
label={translate('::blog.posts.post.status')} label={translate('::blog.posts.post.status')}