final polish phase 1

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-27 21:27:37 +00:00
parent 627938aa95
commit 5f9a4b8dca
25 changed files with 3286 additions and 1397 deletions

View File

@@ -0,0 +1,137 @@
/**
* RecentActivityWidget - Shows last 5 significant operations
* Displays AI task completions, publishing events, etc.
*/
import { Link } from 'react-router-dom';
import {
GroupIcon,
BoltIcon,
FileTextIcon,
FileIcon,
PaperPlaneIcon,
ListIcon,
AlertIcon,
CheckCircleIcon,
} from '../../icons';
export interface ActivityItem {
id: string;
type: 'clustering' | 'ideas' | 'content' | 'images' | 'published' | 'keywords' | 'error' | 'sync';
title: string;
description: string;
timestamp: Date;
href?: string;
success?: boolean;
}
interface RecentActivityWidgetProps {
activities: ActivityItem[];
loading?: boolean;
}
const activityConfig = {
clustering: { icon: GroupIcon, color: 'text-purple-600 dark:text-purple-400', bgColor: 'bg-purple-100 dark:bg-purple-900/40' },
ideas: { icon: BoltIcon, color: 'text-orange-600 dark:text-orange-400', bgColor: 'bg-orange-100 dark:bg-orange-900/40' },
content: { icon: FileTextIcon, color: 'text-green-600 dark:text-green-400', bgColor: 'bg-green-100 dark:bg-green-900/40' },
images: { icon: FileIcon, color: 'text-pink-600 dark:text-pink-400', bgColor: 'bg-pink-100 dark:bg-pink-900/40' },
published: { icon: PaperPlaneIcon, color: 'text-emerald-600 dark:text-emerald-400', bgColor: 'bg-emerald-100 dark:bg-emerald-900/40' },
keywords: { icon: ListIcon, color: 'text-blue-600 dark:text-blue-400', bgColor: 'bg-blue-100 dark:bg-blue-900/40' },
error: { icon: AlertIcon, color: 'text-red-600 dark:text-red-400', bgColor: 'bg-red-100 dark:bg-red-900/40' },
sync: { icon: CheckCircleIcon, color: 'text-teal-600 dark:text-teal-400', bgColor: 'bg-teal-100 dark:bg-teal-900/40' },
};
function formatRelativeTime(date: Date): string {
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffMins = Math.floor(diffMs / 60000);
const diffHours = Math.floor(diffMs / 3600000);
const diffDays = Math.floor(diffMs / 86400000);
if (diffMins < 1) return 'Just now';
if (diffMins < 60) return `${diffMins}m ago`;
if (diffHours < 24) return `${diffHours}h ago`;
if (diffDays === 1) return 'Yesterday';
if (diffDays < 7) return `${diffDays}d ago`;
return date.toLocaleDateString();
}
export default function RecentActivityWidget({ activities, loading }: RecentActivityWidgetProps) {
return (
<div className="bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-800 p-5">
{/* Header */}
<h3 className="text-base font-semibold text-gray-800 dark:text-gray-200 uppercase tracking-wide mb-4">
Recent Activity
</h3>
{/* Activity List */}
<div className="space-y-3">
{loading ? (
// Loading skeleton
Array.from({ length: 5 }).map((_, i) => (
<div key={i} className="flex items-start gap-3 animate-pulse">
<div className="w-9 h-9 rounded-lg bg-gray-100 dark:bg-gray-800"></div>
<div className="flex-1">
<div className="h-4 w-3/4 bg-gray-100 dark:bg-gray-800 rounded mb-2"></div>
<div className="h-3 w-1/4 bg-gray-100 dark:bg-gray-800 rounded"></div>
</div>
</div>
))
) : activities.length === 0 ? (
<div className="text-center py-8">
<p className="text-base text-gray-600 dark:text-gray-400">No recent activity</p>
<p className="text-sm text-gray-500 dark:text-gray-500 mt-1">
AI operations will appear here
</p>
</div>
) : (
activities.slice(0, 5).map((activity) => {
const config = activityConfig[activity.type];
const Icon = config.icon;
const content = (
<div className="flex items-start gap-3">
<div className={`w-9 h-9 rounded-lg ${config.bgColor} flex items-center justify-center flex-shrink-0`}>
<Icon className={`w-5 h-5 ${config.color}`} />
</div>
<div className="flex-1 min-w-0">
<p className="text-base text-gray-800 dark:text-gray-200 line-clamp-1">
{activity.title}
</p>
<p className="text-sm text-gray-600 dark:text-gray-400">
{formatRelativeTime(activity.timestamp)}
</p>
</div>
</div>
);
return activity.href ? (
<Link
key={activity.id}
to={activity.href}
className="block hover:bg-gray-50 dark:hover:bg-gray-800/50 rounded-lg p-1 -m-1 transition-colors"
>
{content}
</Link>
) : (
<div key={activity.id} className="p-1 -m-1">
{content}
</div>
);
})
)}
</div>
{/* View All Link */}
{activities.length > 0 && (
<Link
to="/account/activity"
className="block mt-3 pt-3 border-t border-gray-100 dark:border-gray-800 text-xs font-medium text-brand-600 hover:text-brand-700 dark:text-brand-400 dark:hover:text-brand-300 text-center"
>
View All Activity
</Link>
)}
</div>
);
}