| import React, { useState } from 'react'; |
| import { motion } from 'framer-motion'; |
| import { |
| Save, |
| Send, |
| Eye, |
| Calendar, |
| Clock, |
| Image, |
| Layers, |
| FileText, |
| Video, |
| Sparkles, |
| ChevronDown, |
| Plus, |
| X, |
| GripVertical, |
| RefreshCw, |
| ThumbsUp, |
| MessageCircle, |
| Share2, |
| MoreHorizontal, |
| Globe, |
| Link2, |
| Hash, |
| AtSign, |
| Bold, |
| Italic, |
| List, |
| Smile, |
| ImagePlus, |
| Trash2, |
| ArrowLeft, |
| Linkedin |
| } from 'lucide-react'; |
| import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; |
| import { Button } from '@/components/ui/button'; |
| import { Input } from '@/components/ui/input'; |
| import { Textarea } from '@/components/ui/textarea'; |
| import { Badge } from '@/components/ui/badge'; |
| import { Label } from '@/components/ui/label'; |
| import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; |
| import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; |
| import { Separator } from '@/components/ui/separator'; |
| import { |
| Select, |
| SelectContent, |
| SelectItem, |
| SelectTrigger, |
| SelectValue, |
| } from '@/components/ui/select'; |
| import { |
| Popover, |
| PopoverContent, |
| PopoverTrigger, |
| } from '@/components/ui/popover'; |
| import { Calendar as CalendarComponent } from '@/components/ui/calendar'; |
| import { format } from 'date-fns'; |
| import { Link } from 'react-router-dom'; |
| import { createPageUrl } from '@/utils'; |
|
|
| const postTypes = [ |
| { id: 'carousel', name: 'Carousel', icon: Layers }, |
| { id: 'cover_content', name: 'Cover Image + Content', icon: Image }, |
| { id: 'content_only', name: 'Content Only', icon: FileText }, |
| { id: 'webinar', name: 'Webinar Invite', icon: Video }, |
| ]; |
|
|
| const products = [ |
| { id: 'ocr', name: 'Document Parsing (OCR)', shortName: 'OCR' }, |
| { id: 'p2p', name: 'Purchase To Pay', shortName: 'P2P' }, |
| { id: 'o2c', name: 'Order to Cash', shortName: 'O2C' }, |
| ]; |
|
|
| export default function PostEditor() { |
| const [postType, setPostType] = useState('carousel'); |
| const [product, setProduct] = useState('ocr'); |
| const [content, setContent] = useState(`🚀 Transform Your Document Processing with AI-Powered OCR |
| |
| Are you still manually processing invoices and documents? |
| |
| Here's how our Intelligent Document Parsing solution can revolutionize your workflow: |
| |
| ✅ 99.5% accuracy in data extraction |
| ✅ Process 1000+ documents per hour |
| ✅ Seamless integration with existing systems |
| ✅ Reduce manual errors by 95% |
| |
| The future of document automation is here. Ready to transform your business? |
| |
| #DocumentAutomation #OCR #AITechnology #DigitalTransformation #BusinessEfficiency`); |
| const [scheduledDate, setScheduledDate] = useState(new Date()); |
| const [scheduledTime, setScheduledTime] = useState('10:00'); |
| const [carouselSlides, setCarouselSlides] = useState([ |
| { id: 1, title: 'Slide 1', hasImage: true }, |
| { id: 2, title: 'Slide 2', hasImage: true }, |
| { id: 3, title: 'Slide 3', hasImage: true }, |
| ]); |
| const [selectedAssets, setSelectedAssets] = useState([ |
| { id: 1, name: 'OCR_Demo.png', type: 'image' }, |
| { id: 2, name: 'Workflow_Diagram.png', type: 'image' }, |
| ]); |
|
|
| const addSlide = () => { |
| setCarouselSlides([...carouselSlides, { id: Date.now(), title: `Slide ${carouselSlides.length + 1}`, hasImage: false }]); |
| }; |
|
|
| const removeSlide = (id) => { |
| setCarouselSlides(carouselSlides.filter(slide => slide.id !== id)); |
| }; |
|
|
| 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 className="flex items-center gap-4"> |
| <Link to={createPageUrl('Scheduler')}> |
| <Button variant="ghost" size="icon" className="h-10 w-10"> |
| <ArrowLeft className="w-5 h-5" /> |
| </Button> |
| </Link> |
| <div> |
| <h1 className="text-2xl font-bold text-slate-900 tracking-tight"> |
| Create Post |
| </h1> |
| <p className="text-slate-500 text-sm mt-0.5"> |
| Compose and schedule your LinkedIn content |
| </p> |
| </div> |
| </div> |
| <div className="flex gap-3"> |
| <Button variant="outline" className="gap-2 border-slate-200"> |
| <Save className="w-4 h-4" /> |
| Save Draft |
| </Button> |
| <Button variant="outline" className="gap-2 border-slate-200"> |
| <Eye className="w-4 h-4" /> |
| Preview |
| </Button> |
| <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"> |
| <Send className="w-4 h-4" /> |
| Schedule Post |
| </Button> |
| </div> |
| </div> |
| </motion.div> |
| |
| <div className="grid lg:grid-cols-5 gap-6"> |
| {/* Editor Panel */} |
| <motion.div |
| initial={{ opacity: 0, x: -20 }} |
| animate={{ opacity: 1, x: 0 }} |
| className="lg:col-span-3 space-y-6" |
| > |
| {/* Post Type Selection */} |
| <Card className="border-0 shadow-lg shadow-slate-200/50"> |
| <CardHeader className="pb-4"> |
| <CardTitle className="text-base font-semibold">Post Type</CardTitle> |
| </CardHeader> |
| <CardContent> |
| <div className="grid grid-cols-2 md:grid-cols-4 gap-3"> |
| {postTypes.map(type => ( |
| <button |
| key={type.id} |
| onClick={() => setPostType(type.id)} |
| className={`p-4 rounded-xl border-2 transition-all ${ |
| postType === type.id |
| ? 'border-blue-500 bg-blue-50' |
| : 'border-slate-200 hover:border-slate-300 bg-white' |
| }`} |
| > |
| <type.icon className={`w-6 h-6 mx-auto mb-2 ${ |
| postType === type.id ? 'text-blue-600' : 'text-slate-400' |
| }`} /> |
| <p className={`text-sm font-medium ${ |
| postType === type.id ? 'text-blue-700' : 'text-slate-600' |
| }`}> |
| {type.name} |
| </p> |
| </button> |
| ))} |
| </div> |
| </CardContent> |
| </Card> |
| |
| {/* Content Editor */} |
| <Card className="border-0 shadow-lg shadow-slate-200/50"> |
| <CardHeader className="pb-4"> |
| <div className="flex items-center justify-between"> |
| <CardTitle className="text-base font-semibold">Content</CardTitle> |
| <Button variant="outline" size="sm" className="gap-2 text-violet-600 border-violet-200 hover:bg-violet-50"> |
| <Sparkles className="w-4 h-4" /> |
| AI Generate |
| </Button> |
| </div> |
| </CardHeader> |
| <CardContent className="space-y-4"> |
| {/* Formatting Toolbar */} |
| <div className="flex items-center gap-1 p-2 bg-slate-50 rounded-lg"> |
| <Button variant="ghost" size="icon" className="h-8 w-8"> |
| <Bold className="w-4 h-4" /> |
| </Button> |
| <Button variant="ghost" size="icon" className="h-8 w-8"> |
| <Italic className="w-4 h-4" /> |
| </Button> |
| <Button variant="ghost" size="icon" className="h-8 w-8"> |
| <List className="w-4 h-4" /> |
| </Button> |
| <Separator orientation="vertical" className="h-6 mx-1" /> |
| <Button variant="ghost" size="icon" className="h-8 w-8"> |
| <Hash className="w-4 h-4" /> |
| </Button> |
| <Button variant="ghost" size="icon" className="h-8 w-8"> |
| <AtSign className="w-4 h-4" /> |
| </Button> |
| <Button variant="ghost" size="icon" className="h-8 w-8"> |
| <Link2 className="w-4 h-4" /> |
| </Button> |
| <Button variant="ghost" size="icon" className="h-8 w-8"> |
| <Smile className="w-4 h-4" /> |
| </Button> |
| </div> |
| |
| <Textarea |
| value={content} |
| onChange={(e) => setContent(e.target.value)} |
| className="min-h-[250px] text-sm leading-relaxed border-slate-200 focus:border-blue-300" |
| placeholder="Write your post content here..." |
| /> |
| |
| <div className="flex items-center justify-between text-sm text-slate-500"> |
| <span>{content.length} / 3000 characters</span> |
| <Button variant="ghost" size="sm" className="gap-1 text-blue-600"> |
| <RefreshCw className="w-3 h-3" /> |
| Regenerate |
| </Button> |
| </div> |
| </CardContent> |
| </Card> |
| |
| {/* Carousel Slides (if carousel type) */} |
| {postType === 'carousel' && ( |
| <Card className="border-0 shadow-lg shadow-slate-200/50"> |
| <CardHeader className="pb-4"> |
| <div className="flex items-center justify-between"> |
| <CardTitle className="text-base font-semibold">Carousel Slides</CardTitle> |
| <Button onClick={addSlide} variant="outline" size="sm" className="gap-1"> |
| <Plus className="w-4 h-4" /> |
| Add Slide |
| </Button> |
| </div> |
| </CardHeader> |
| <CardContent> |
| <div className="space-y-3"> |
| {carouselSlides.map((slide, index) => ( |
| <motion.div |
| key={slide.id} |
| initial={{ opacity: 0, y: 10 }} |
| animate={{ opacity: 1, y: 0 }} |
| className="flex items-center gap-3 p-3 rounded-xl border border-slate-200 bg-white hover:border-slate-300 transition-colors" |
| > |
| <GripVertical className="w-4 h-4 text-slate-400 cursor-grab" /> |
| <div className="w-16 h-16 rounded-lg bg-gradient-to-br from-slate-100 to-slate-50 flex items-center justify-center border border-slate-200"> |
| {slide.hasImage ? ( |
| <Image className="w-6 h-6 text-slate-400" /> |
| ) : ( |
| <ImagePlus className="w-6 h-6 text-slate-300" /> |
| )} |
| </div> |
| <div className="flex-1"> |
| <Input |
| value={slide.title} |
| className="h-8 text-sm border-0 bg-transparent p-0 font-medium" |
| placeholder="Slide title" |
| /> |
| <p className="text-xs text-slate-500 mt-0.5"> |
| {slide.hasImage ? 'Image attached' : 'No image'} |
| </p> |
| </div> |
| <Button variant="ghost" size="sm" className="gap-1 text-blue-600"> |
| <ImagePlus className="w-4 h-4" /> |
| {slide.hasImage ? 'Change' : 'Add'} Image |
| </Button> |
| <Button |
| variant="ghost" |
| size="icon" |
| onClick={() => removeSlide(slide.id)} |
| className="h-8 w-8 text-slate-400 hover:text-red-500" |
| > |
| <Trash2 className="w-4 h-4" /> |
| </Button> |
| </motion.div> |
| ))} |
| </div> |
| </CardContent> |
| </Card> |
| )} |
| |
| {/* Media Attachments */} |
| <Card className="border-0 shadow-lg shadow-slate-200/50"> |
| <CardHeader className="pb-4"> |
| <div className="flex items-center justify-between"> |
| <CardTitle className="text-base font-semibold">Media from Repository</CardTitle> |
| <Link to={createPageUrl('Repository')}> |
| <Button variant="outline" size="sm" className="gap-1"> |
| <Plus className="w-4 h-4" /> |
| Browse Assets |
| </Button> |
| </Link> |
| </div> |
| </CardHeader> |
| <CardContent> |
| <div className="flex flex-wrap gap-3"> |
| {selectedAssets.map(asset => ( |
| <div |
| key={asset.id} |
| className="flex items-center gap-2 px-3 py-2 rounded-lg bg-slate-50 border border-slate-200" |
| > |
| <Image className="w-4 h-4 text-blue-500" /> |
| <span className="text-sm text-slate-700">{asset.name}</span> |
| <Button variant="ghost" size="icon" className="h-5 w-5 text-slate-400 hover:text-red-500"> |
| <X className="w-3 h-3" /> |
| </Button> |
| </div> |
| ))} |
| <button className="flex items-center gap-2 px-3 py-2 rounded-lg border-2 border-dashed border-slate-200 text-slate-500 hover:border-blue-300 hover:text-blue-600 transition-colors"> |
| <Plus className="w-4 h-4" /> |
| <span className="text-sm">Add from Canva</span> |
| </button> |
| </div> |
| </CardContent> |
| </Card> |
| </motion.div> |
| |
| {/* Preview & Settings Panel */} |
| <motion.div |
| initial={{ opacity: 0, x: 20 }} |
| animate={{ opacity: 1, x: 0 }} |
| className="lg:col-span-2 space-y-6" |
| > |
| {/* Schedule Settings */} |
| <Card className="border-0 shadow-lg shadow-slate-200/50"> |
| <CardHeader className="pb-4"> |
| <CardTitle className="text-base font-semibold flex items-center gap-2"> |
| <Calendar className="w-4 h-4 text-blue-600" /> |
| Schedule |
| </CardTitle> |
| </CardHeader> |
| <CardContent className="space-y-4"> |
| <div> |
| <Label className="text-sm text-slate-600">Product Category</Label> |
| <Select value={product} onValueChange={setProduct}> |
| <SelectTrigger className="mt-1.5"> |
| <SelectValue /> |
| </SelectTrigger> |
| <SelectContent> |
| {products.map(p => ( |
| <SelectItem key={p.id} value={p.id}>{p.name}</SelectItem> |
| ))} |
| </SelectContent> |
| </Select> |
| </div> |
| |
| <div className="grid grid-cols-2 gap-3"> |
| <div> |
| <Label className="text-sm text-slate-600">Date</Label> |
| <Popover> |
| <PopoverTrigger asChild> |
| <Button variant="outline" className="w-full justify-start mt-1.5 text-left font-normal"> |
| <Calendar className="w-4 h-4 mr-2" /> |
| {format(scheduledDate, 'MMM d, yyyy')} |
| </Button> |
| </PopoverTrigger> |
| <PopoverContent className="w-auto p-0" align="start"> |
| <CalendarComponent |
| mode="single" |
| selected={scheduledDate} |
| onSelect={setScheduledDate} |
| /> |
| </PopoverContent> |
| </Popover> |
| </div> |
| <div> |
| <Label className="text-sm text-slate-600">Time</Label> |
| <Select value={scheduledTime} onValueChange={setScheduledTime}> |
| <SelectTrigger className="mt-1.5"> |
| <Clock className="w-4 h-4 mr-2" /> |
| <SelectValue /> |
| </SelectTrigger> |
| <SelectContent> |
| {['09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00'].map(time => ( |
| <SelectItem key={time} value={time}>{time}</SelectItem> |
| ))} |
| </SelectContent> |
| </Select> |
| </div> |
| </div> |
| </CardContent> |
| </Card> |
| |
| {/* LinkedIn Preview */} |
| <Card className="border-0 shadow-lg shadow-slate-200/50 overflow-hidden"> |
| <CardHeader className="pb-3 bg-gradient-to-r from-[#0A66C2] to-[#004182]"> |
| <CardTitle className="text-base font-semibold text-white flex items-center gap-2"> |
| <Linkedin className="w-4 h-4" /> |
| LinkedIn Preview |
| </CardTitle> |
| </CardHeader> |
| <CardContent className="p-0"> |
| <div className="p-4 bg-white"> |
| {/* Post Header */} |
| <div className="flex items-start gap-3"> |
| <Avatar className="h-12 w-12"> |
| <AvatarImage src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop" /> |
| <AvatarFallback>AB</AvatarFallback> |
| </Avatar> |
| <div className="flex-1"> |
| <p className="font-semibold text-sm text-slate-900">Alex Business</p> |
| <p className="text-xs text-slate-500">Marketing Director at TechCorp</p> |
| <p className="text-xs text-slate-400 flex items-center gap-1 mt-0.5"> |
| {format(scheduledDate, 'MMM d')} • <Globe className="w-3 h-3" /> |
| </p> |
| </div> |
| <Button variant="ghost" size="icon" className="h-8 w-8"> |
| <MoreHorizontal className="w-4 h-4" /> |
| </Button> |
| </div> |
| |
| {/* Post Content */} |
| <div className="mt-3 text-sm text-slate-800 whitespace-pre-line leading-relaxed"> |
| {content.length > 300 ? content.slice(0, 300) + '...' : content} |
| {content.length > 300 && ( |
| <button className="text-blue-600 hover:underline ml-1">see more</button> |
| )} |
| </div> |
| |
| {/* Post Image Preview */} |
| {postType !== 'content_only' && ( |
| <div className="mt-3 -mx-4"> |
| <div className="aspect-video bg-gradient-to-br from-blue-100 to-indigo-100 flex items-center justify-center"> |
| {postType === 'carousel' ? ( |
| <div className="text-center"> |
| <Layers className="w-12 h-12 text-blue-400 mx-auto mb-2" /> |
| <p className="text-sm text-blue-600">{carouselSlides.length} slides</p> |
| </div> |
| ) : ( |
| <Image className="w-12 h-12 text-blue-400" /> |
| )} |
| </div> |
| </div> |
| )} |
| |
| {/* Engagement Stats */} |
| <div className="flex items-center justify-between mt-3 pt-3 border-t border-slate-100"> |
| <div className="flex items-center gap-1 text-xs text-slate-500"> |
| <div className="flex -space-x-1"> |
| <div className="w-4 h-4 rounded-full bg-blue-500 flex items-center justify-center"> |
| <ThumbsUp className="w-2 h-2 text-white" /> |
| </div> |
| <div className="w-4 h-4 rounded-full bg-red-500 flex items-center justify-center"> |
| <span className="text-[8px]">❤️</span> |
| </div> |
| </div> |
| <span>Preview mode</span> |
| </div> |
| <span className="text-xs text-slate-500">0 comments</span> |
| </div> |
| |
| {/* Action Buttons */} |
| <div className="flex items-center justify-between mt-3 pt-3 border-t border-slate-100"> |
| {[ |
| { icon: ThumbsUp, label: 'Like' }, |
| { icon: MessageCircle, label: 'Comment' }, |
| { icon: Share2, label: 'Share' }, |
| { icon: Send, label: 'Send' }, |
| ].map((action, idx) => ( |
| <button |
| key={idx} |
| className="flex items-center gap-1.5 px-3 py-2 text-slate-600 hover:bg-slate-50 rounded-lg transition-colors" |
| > |
| <action.icon className="w-4 h-4" /> |
| <span className="text-xs font-medium">{action.label}</span> |
| </button> |
| ))} |
| </div> |
| </div> |
| </CardContent> |
| </Card> |
| |
| {/* Action Buttons */} |
| <div className="space-y-3"> |
| <Button className="w-full gap-2 h-12 bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 shadow-lg shadow-blue-500/25"> |
| <Send className="w-5 h-5" /> |
| Schedule for {format(scheduledDate, 'MMM d')} at {scheduledTime} |
| </Button> |
| <Button variant="outline" className="w-full gap-2 h-12 border-slate-200"> |
| <Eye className="w-5 h-5" /> |
| Post Immediately |
| </Button> |
| </div> |
| </motion.div> |
| </div> |
| </div> |
| </div> |
| ); |
| } |
|
|
|
|