PostGen / frontend /src /pages /Dashboard.jsx
Seth
update
f80e9b3
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { createPageUrl } from '@/utils';
import { motion } from 'framer-motion';
import {
Calendar,
FileText,
Image,
TrendingUp,
Clock,
Zap,
ArrowRight,
Plus,
Sparkles,
BarChart3,
FolderOpen,
Layers
} from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Progress } from '@/components/ui/progress';
export default function Dashboard() {
const stats = [
{ label: 'Scheduled Posts', value: '24', icon: Calendar, color: 'from-blue-500 to-indigo-600', change: '+12%' },
{ label: 'Repository Assets', value: '156', icon: FolderOpen, color: 'from-emerald-500 to-teal-600', change: '+8%' },
{ label: 'Posts This Month', value: '18', icon: TrendingUp, color: 'from-violet-500 to-purple-600', change: '+24%' },
{ label: 'Engagement Rate', value: '4.8%', icon: BarChart3, color: 'from-amber-500 to-orange-600', change: '+2.1%' },
];
const upcomingPosts = [
{ id: 1, title: 'OCR Document Automation Benefits', type: 'Carousel', date: 'Today, 2:00 PM', product: 'OCR', status: 'ready' },
{ id: 2, title: 'P2P Workflow Efficiency Guide', type: 'Cover Image + Content', date: 'Tomorrow, 10:00 AM', product: 'P2P', status: 'pending' },
{ id: 3, title: 'O2C Webinar Announcement', type: 'Webinar Invite', date: 'Dec 28, 3:00 PM', product: 'O2C', status: 'draft' },
];
const productStats = [
{ name: 'Document Parsing (OCR)', posts: 8, assets: 45, progress: 75 },
{ name: 'Purchase To Pay (P2P)', posts: 6, assets: 52, progress: 60 },
{ name: 'Order to Cash (O2C)', posts: 10, assets: 59, progress: 85 },
];
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: { staggerChildren: 0.1 }
}
};
const item = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0 }
};
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-blue-50/30">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Header */}
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
className="mb-8"
>
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<div>
<h1 className="text-3xl font-bold text-slate-900 tracking-tight">
Welcome back
</h1>
<p className="text-slate-500 mt-1">
Here's what's happening with your LinkedIn content
</p>
</div>
<div className="flex gap-3">
<Link to={createPageUrl('Scheduler')}>
<Button variant="outline" className="gap-2 border-slate-200 hover:bg-slate-50">
<Calendar className="w-4 h-4" />
Schedule
</Button>
</Link>
<Link to={createPageUrl('PostEditor')}>
<Button className="gap-2 bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 shadow-lg shadow-blue-500/25">
<Plus className="w-4 h-4" />
Create Post
</Button>
</Link>
</div>
</div>
</motion.div>
{/* Stats Grid */}
<motion.div
variants={container}
initial="hidden"
animate="show"
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-8"
>
{stats.map((stat, index) => (
<motion.div key={index} variants={item}>
<Card className="border-0 shadow-lg shadow-slate-200/50 hover:shadow-xl transition-all duration-300 overflow-hidden group">
<CardContent className="p-6 relative">
<div className={`absolute top-0 right-0 w-32 h-32 bg-gradient-to-br ${stat.color} opacity-5 rounded-full -translate-y-1/2 translate-x-1/2 group-hover:scale-150 transition-transform duration-500`} />
<div className="flex items-start justify-between relative">
<div>
<p className="text-sm font-medium text-slate-500">{stat.label}</p>
<p className="text-3xl font-bold text-slate-900 mt-2">{stat.value}</p>
<Badge variant="secondary" className="mt-2 bg-emerald-50 text-emerald-700 border-0">
{stat.change}
</Badge>
</div>
<div className={`p-3 rounded-xl bg-gradient-to-br ${stat.color} shadow-lg`}>
<stat.icon className="w-5 h-5 text-white" />
</div>
</div>
</CardContent>
</Card>
</motion.div>
))}
</motion.div>
<div className="grid lg:grid-cols-3 gap-6">
{/* Upcoming Posts */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.3 }}
className="lg:col-span-2"
>
<Card className="border-0 shadow-lg shadow-slate-200/50">
<CardHeader className="pb-4">
<div className="flex items-center justify-between">
<CardTitle className="text-lg font-semibold flex items-center gap-2">
<Clock className="w-5 h-5 text-blue-600" />
Upcoming Posts
</CardTitle>
<Link to={createPageUrl('Scheduler')}>
<Button variant="ghost" size="sm" className="text-blue-600 hover:text-blue-700 gap-1">
View all <ArrowRight className="w-4 h-4" />
</Button>
</Link>
</div>
</CardHeader>
<CardContent className="space-y-3">
{upcomingPosts.map((post, index) => (
<motion.div
key={post.id}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4 + index * 0.1 }}
className="group p-4 rounded-xl bg-slate-50/50 hover:bg-white hover:shadow-md border border-transparent hover:border-slate-100 transition-all duration-300 cursor-pointer"
>
<div className="flex items-center gap-4">
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${
post.product === 'OCR' ? 'bg-blue-100 text-blue-600' :
post.product === 'P2P' ? 'bg-emerald-100 text-emerald-600' :
'bg-violet-100 text-violet-600'
}`}>
{post.type === 'Carousel' ? <Layers className="w-5 h-5" /> :
post.type === 'Webinar Invite' ? <Sparkles className="w-5 h-5" /> :
<Image className="w-5 h-5" />}
</div>
<div className="flex-1 min-w-0">
<h3 className="font-medium text-slate-900 truncate">{post.title}</h3>
<div className="flex items-center gap-2 mt-1">
<span className="text-sm text-slate-500">{post.date}</span>
<span className="text-slate-300"></span>
<Badge variant="outline" className="text-xs border-slate-200">
{post.type}
</Badge>
</div>
</div>
<Badge className={`capitalize ${
post.status === 'ready' ? 'bg-emerald-100 text-emerald-700 border-0' :
post.status === 'pending' ? 'bg-amber-100 text-amber-700 border-0' :
'bg-slate-100 text-slate-600 border-0'
}`}>
{post.status}
</Badge>
</div>
</motion.div>
))}
</CardContent>
</Card>
</motion.div>
{/* Product Distribution */}
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.4 }}
>
<Card className="border-0 shadow-lg shadow-slate-200/50 h-full">
<CardHeader className="pb-4">
<CardTitle className="text-lg font-semibold flex items-center gap-2">
<Zap className="w-5 h-5 text-amber-500" />
Content by Product
</CardTitle>
</CardHeader>
<CardContent className="space-y-5">
{productStats.map((product, index) => (
<div key={index} className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-slate-700">{product.name}</span>
<span className="text-xs text-slate-500">{product.posts} posts</span>
</div>
<Progress value={product.progress} className="h-2" />
<div className="flex items-center justify-between text-xs text-slate-500">
<span>{product.assets} assets</span>
<span>{product.progress}% coverage</span>
</div>
</div>
))}
</CardContent>
</Card>
</motion.div>
</div>
{/* Quick Actions */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.5 }}
className="mt-8"
>
<h2 className="text-lg font-semibold text-slate-900 mb-4">Quick Actions</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{[
{ label: 'Upload Assets', icon: FolderOpen, color: 'blue', href: 'Repository' },
{ label: 'Schedule Campaign', icon: Calendar, color: 'violet', href: 'Scheduler' },
{ label: 'Connect Canva', icon: Sparkles, color: 'pink', href: 'Integrations' },
{ label: 'View Analytics', icon: BarChart3, color: 'emerald', href: 'Dashboard' },
].map((action, index) => (
<Link key={index} to={createPageUrl(action.href)}>
<Card className="border-0 shadow-md hover:shadow-lg transition-all duration-300 cursor-pointer group overflow-hidden">
<CardContent className="p-5 flex items-center gap-4">
<div className={`p-3 rounded-xl bg-${action.color}-100 group-hover:scale-110 transition-transform`}>
<action.icon className={`w-5 h-5 text-${action.color}-600`} />
</div>
<span className="font-medium text-slate-700 group-hover:text-slate-900">{action.label}</span>
<ArrowRight className="w-4 h-4 text-slate-400 ml-auto group-hover:translate-x-1 transition-transform" />
</CardContent>
</Card>
</Link>
))}
</div>
</motion.div>
</div>
</div>
);
}