forum Management istatistikleri dinamik hale getirildi.
This commit is contained in:
parent
2e44509ffb
commit
7d8b56701c
1 changed files with 86 additions and 43 deletions
|
|
@ -1,22 +1,28 @@
|
||||||
import React from 'react';
|
import { Folder, MessageSquare, FileText, TrendingUp } from 'lucide-react'
|
||||||
import { Folder, MessageSquare, FileText, TrendingUp } from 'lucide-react';
|
import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum'
|
||||||
import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum';
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization';
|
import { formatDistanceToNow } from 'date-fns'
|
||||||
|
|
||||||
interface AdminStatsProps {
|
interface AdminStatsProps {
|
||||||
categories: ForumCategory[];
|
categories: ForumCategory[]
|
||||||
topics: ForumTopic[];
|
topics: ForumTopic[]
|
||||||
posts: ForumPost[];
|
posts: ForumPost[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Activity {
|
||||||
|
message: string
|
||||||
|
color: string
|
||||||
|
date: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AdminStats({ categories, topics, posts }: AdminStatsProps) {
|
export function AdminStats({ categories, topics, posts }: AdminStatsProps) {
|
||||||
const { translate } = useLocalization();
|
const { translate } = useLocalization()
|
||||||
const totalCategories = categories.length;
|
const totalCategories = categories.length
|
||||||
const activeCategories = categories.filter(c => c.isActive).length;
|
const activeCategories = categories.filter((c) => c.isActive).length
|
||||||
const totalTopics = topics.length;
|
const totalTopics = topics.length
|
||||||
const solvedTopics = topics.filter(t => t.isSolved).length;
|
const solvedTopics = topics.filter((t) => t.isSolved).length
|
||||||
const totalPosts = posts.length;
|
const totalPosts = posts.length
|
||||||
const acceptedAnswers = posts.filter(p => p.isAcceptedAnswer).length;
|
const acceptedAnswers = posts.filter((p) => p.isAcceptedAnswer).length
|
||||||
|
|
||||||
const stats = [
|
const stats = [
|
||||||
{
|
{
|
||||||
|
|
@ -47,20 +53,65 @@ export function AdminStats({ categories, topics, posts }: AdminStatsProps) {
|
||||||
icon: TrendingUp,
|
icon: TrendingUp,
|
||||||
color: 'bg-purple-500',
|
color: 'bg-purple-500',
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
|
|
||||||
|
const recentActivities: Activity[] = []
|
||||||
|
|
||||||
|
// Topics -> "New topic created in {categoryName}"
|
||||||
|
topics.forEach((topic) => {
|
||||||
|
const category = categories.find((c) => c.id === topic.categoryId)
|
||||||
|
if (topic.creationTime) {
|
||||||
|
recentActivities.push({
|
||||||
|
message: `New topic created in ${category?.name ?? 'Unknown Category'}`,
|
||||||
|
color: 'bg-blue-500',
|
||||||
|
date: new Date(topic.creationTime),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Posts -> "Post marked as accepted answer"
|
||||||
|
posts.forEach((post) => {
|
||||||
|
if (post.isAcceptedAnswer && post.creationTime) {
|
||||||
|
recentActivities.push({
|
||||||
|
message: 'Post marked as accepted answer',
|
||||||
|
color: 'bg-emerald-500',
|
||||||
|
date: new Date(post.creationTime),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Categories -> "New category created: {name}"
|
||||||
|
categories.forEach((category) => {
|
||||||
|
if (category.creationTime) {
|
||||||
|
recentActivities.push({
|
||||||
|
message: `New category created: ${category.name}`,
|
||||||
|
color: 'bg-orange-500',
|
||||||
|
date: new Date(category.creationTime),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Tarihe göre sırala, en güncel ilk 3 aktiviteyi al
|
||||||
|
const latestActivities = recentActivities
|
||||||
|
.sort((a, b) => b.date.getTime() - a.date.getTime())
|
||||||
|
.slice(0, 3)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">{translate('::App.Forum.Dashboard.Statistics')}</h2>
|
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||||
|
{translate('::App.Forum.Dashboard.Statistics')}
|
||||||
|
</h2>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
{stats.map((stat, index) => {
|
{stats.map((stat, index) => {
|
||||||
const Icon = stat.icon;
|
const Icon = stat.icon
|
||||||
return (
|
return (
|
||||||
<div key={index} className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
<div key={index} className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<div className={`w-12 h-12 ${stat.color} rounded-lg flex items-center justify-center`}>
|
<div
|
||||||
|
className={`w-12 h-12 ${stat.color} rounded-lg flex items-center justify-center`}
|
||||||
|
>
|
||||||
<Icon className="w-6 h-6 text-white" />
|
<Icon className="w-6 h-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -70,38 +121,30 @@ export function AdminStats({ categories, topics, posts }: AdminStatsProps) {
|
||||||
<p className="text-xs text-gray-500">{stat.subtitle}</p>
|
<p className="text-xs text-gray-500">{stat.subtitle}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Recent Activity */}
|
{/* Recent Activity */}
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">{translate('::App.Forum.Dashboard.RecentActivity')}</h3>
|
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||||
|
{translate('::App.Forum.Dashboard.RecentActivity')}
|
||||||
|
</h3>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex items-start space-x-3 p-3 bg-gray-50 rounded-lg">
|
{latestActivities.map((activity, index) => (
|
||||||
<div className="w-2 h-2 bg-blue-500 rounded-full mt-2"></div>
|
<div key={index} className="flex items-start space-x-3 p-3 bg-gray-50 rounded-lg">
|
||||||
<div>
|
<div className={`w-2 h-2 ${activity.color} rounded-full mt-2`} />
|
||||||
<p className="text-sm text-gray-900">{translate('::App.Forum.Dashboard.GeneralDiscussion')}</p>
|
<div>
|
||||||
<p className="text-xs text-gray-500">2 hours ago</p>
|
<p className="text-sm text-gray-900">{activity.message}</p>
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
{formatDistanceToNow(activity.date, { addSuffix: true })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))}
|
||||||
<div className="flex items-start space-x-3 p-3 bg-gray-50 rounded-lg">
|
|
||||||
<div className="w-2 h-2 bg-emerald-500 rounded-full mt-2"></div>
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-gray-900">{translate('::App.Forum.Dashboard.Postmarked')}</p>
|
|
||||||
<p className="text-xs text-gray-500">4 hours ago</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start space-x-3 p-3 bg-gray-50 rounded-lg">
|
|
||||||
<div className="w-2 h-2 bg-orange-500 rounded-full mt-2"></div>
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-gray-900">{translate('::App.Forum.Dashboard.FeatureRequests')}</p>
|
|
||||||
<p className="text-xs text-gray-500">1 day ago</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue