Spaces:
Sleeping
Sleeping
| 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> | |
| ); | |
| } | |