468 lines
17 KiB
TypeScript
468 lines
17 KiB
TypeScript
import React, { useState } from 'react';
|
|
import {
|
|
Plus,
|
|
Edit,
|
|
Eye,
|
|
Trash2,
|
|
Clock,
|
|
CheckCircle,
|
|
XCircle,
|
|
AlertCircle,
|
|
Upload,
|
|
Image,
|
|
Video
|
|
} from 'lucide-react';
|
|
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
|
|
import { Button } from './ui/button';
|
|
import { Input } from './ui/input';
|
|
import { Textarea } from './ui/textarea';
|
|
import { Badge } from './ui/badge';
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogTrigger
|
|
} from './ui/dialog';
|
|
import { Label } from './ui/label';
|
|
import { useAppContext } from './AppShell';
|
|
|
|
// Mock blog submissions data
|
|
const mockBlogData = [
|
|
{
|
|
id: '1',
|
|
title: 'The Future of Remote Leadership: Lessons from the Pandemic',
|
|
excerpt: 'Exploring how the COVID-19 pandemic transformed leadership approaches and what lessons we can carry forward...',
|
|
content: 'Full article content would be here...',
|
|
status: 'published',
|
|
submittedAt: '2024-08-15T10:00:00Z',
|
|
publishedAt: '2024-08-18T14:00:00Z',
|
|
views: 1247,
|
|
tags: ['Remote Leadership', 'Digital Transformation', 'Crisis Management']
|
|
},
|
|
{
|
|
id: '2',
|
|
title: 'Building Resilient Teams in Uncertain Times',
|
|
excerpt: 'Strategies for developing team resilience and maintaining high performance during periods of uncertainty...',
|
|
content: 'Full article content would be here...',
|
|
status: 'under-review',
|
|
submittedAt: '2024-09-01T15:30:00Z',
|
|
tags: ['Team Building', 'Resilience', 'Leadership']
|
|
},
|
|
{
|
|
id: '3',
|
|
title: 'Emotional Intelligence in Modern Leadership',
|
|
excerpt: 'Why EQ matters more than ever in today\'s complex business environment and how to develop it...',
|
|
content: 'Full article content would be here...',
|
|
status: 'rejected',
|
|
submittedAt: '2024-08-20T09:15:00Z',
|
|
rejectedAt: '2024-08-22T11:00:00Z',
|
|
rejectionReason: 'Content needs more original research and citations. Please add specific examples and data to support your claims.',
|
|
tags: ['Emotional Intelligence', 'Leadership Development']
|
|
},
|
|
{
|
|
id: '4',
|
|
title: 'Sustainable Leadership Practices',
|
|
excerpt: 'Draft exploring how leaders can integrate sustainability into their decision-making processes...',
|
|
content: 'Full article content would be here...',
|
|
status: 'draft',
|
|
updatedAt: '2024-09-02T16:45:00Z',
|
|
tags: ['Sustainability', 'Strategic Leadership']
|
|
}
|
|
];
|
|
|
|
export function Blog() {
|
|
const { user } = useAppContext();
|
|
const [activeTab, setActiveTab] = useState('my-posts');
|
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
const [editingPost, setEditingPost] = useState<any>(null);
|
|
const [formData, setFormData] = useState({
|
|
title: '',
|
|
excerpt: '',
|
|
content: '',
|
|
tags: ''
|
|
});
|
|
|
|
const getStatusIcon = (status: string) => {
|
|
switch (status) {
|
|
case 'published': return CheckCircle;
|
|
case 'under-review': return Clock;
|
|
case 'rejected': return XCircle;
|
|
case 'draft': return Edit;
|
|
default: return AlertCircle;
|
|
}
|
|
};
|
|
|
|
const getStatusColor = (status: string) => {
|
|
switch (status) {
|
|
case 'published': return 'bg-green-100 text-green-800';
|
|
case 'under-review': return 'bg-blue-100 text-blue-800';
|
|
case 'rejected': return 'bg-red-100 text-red-800';
|
|
case 'draft': return 'bg-gray-100 text-gray-800';
|
|
default: return 'bg-gray-100 text-gray-800';
|
|
}
|
|
};
|
|
|
|
const formatDate = (dateStr: string) => {
|
|
return new Date(dateStr).toLocaleDateString('en-IN', {
|
|
day: '2-digit',
|
|
month: 'short',
|
|
year: 'numeric',
|
|
timeZone: 'Asia/Kolkata'
|
|
});
|
|
};
|
|
|
|
const handleSubmit = () => {
|
|
// TODO: Submit blog post
|
|
console.log('Submitting:', formData);
|
|
setIsDialogOpen(false);
|
|
setFormData({ title: '', excerpt: '', content: '', tags: '' });
|
|
setEditingPost(null);
|
|
};
|
|
|
|
const BlogPostCard = ({ post }: { post: any }) => {
|
|
const StatusIcon = getStatusIcon(post.status);
|
|
|
|
return (
|
|
<Card>
|
|
<CardContent className="p-4 space-y-3">
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex-1 space-y-2">
|
|
<div className="flex items-center gap-2">
|
|
<StatusIcon className="h-4 w-4" />
|
|
<Badge className={`capitalize ${getStatusColor(post.status)}`}>
|
|
{post.status.replace('-', ' ')}
|
|
</Badge>
|
|
</div>
|
|
|
|
<h3 className="font-medium">{post.title}</h3>
|
|
<p className="text-sm text-muted-foreground line-clamp-2">
|
|
{post.excerpt}
|
|
</p>
|
|
|
|
<div className="flex flex-wrap gap-1">
|
|
{post.tags.map((tag: string) => (
|
|
<Badge key={tag} variant="outline" className="text-xs">
|
|
{tag}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
|
|
<div className="flex items-center gap-4 text-xs text-muted-foreground">
|
|
<span>
|
|
Submitted {formatDate(post.submittedAt)}
|
|
</span>
|
|
{post.publishedAt && (
|
|
<span>
|
|
Published {formatDate(post.publishedAt)}
|
|
</span>
|
|
)}
|
|
{post.views && (
|
|
<span>
|
|
{post.views} views
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
{post.rejectionReason && (
|
|
<div className="p-3 bg-red-50 border border-red-200 rounded-lg">
|
|
<p className="text-sm font-medium text-red-800 mb-1">Rejection Reason:</p>
|
|
<p className="text-sm text-red-700">{post.rejectionReason}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex gap-2 ml-4">
|
|
<Button
|
|
size="icon"
|
|
variant="ghost"
|
|
onClick={() => {
|
|
setEditingPost(post);
|
|
setFormData({
|
|
title: post.title,
|
|
excerpt: post.excerpt,
|
|
content: post.content,
|
|
tags: post.tags.join(', ')
|
|
});
|
|
setIsDialogOpen(true);
|
|
}}
|
|
className="h-8 w-8"
|
|
>
|
|
<Edit className="h-3 w-3" />
|
|
</Button>
|
|
|
|
{post.status === 'published' && (
|
|
<Button
|
|
size="icon"
|
|
variant="ghost"
|
|
className="h-8 w-8"
|
|
>
|
|
<Eye className="h-3 w-3" />
|
|
</Button>
|
|
)}
|
|
|
|
<Button
|
|
size="icon"
|
|
variant="ghost"
|
|
className="h-8 w-8 text-destructive hover:text-destructive"
|
|
>
|
|
<Trash2 className="h-3 w-3" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="p-6 space-y-6">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-3xl font-medium">Leadership Blog</h1>
|
|
<p className="text-muted-foreground">
|
|
Share your leadership insights and experiences
|
|
</p>
|
|
</div>
|
|
|
|
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
|
<DialogTrigger asChild>
|
|
<Button onClick={() => {
|
|
setEditingPost(null);
|
|
setFormData({ title: '', excerpt: '', content: '', tags: '' });
|
|
}}>
|
|
<Plus className="h-4 w-4 mr-2" />
|
|
Write Article
|
|
</Button>
|
|
</DialogTrigger>
|
|
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
|
|
<DialogHeader>
|
|
<DialogTitle>
|
|
{editingPost ? 'Edit Article' : 'Write New Article'}
|
|
</DialogTitle>
|
|
</DialogHeader>
|
|
<div className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="title">Title</Label>
|
|
<Input
|
|
id="title"
|
|
placeholder="Enter article title..."
|
|
value={formData.title}
|
|
onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="excerpt">Excerpt</Label>
|
|
<Textarea
|
|
id="excerpt"
|
|
placeholder="Brief summary of your article..."
|
|
className="h-20"
|
|
value={formData.excerpt}
|
|
onChange={(e) => setFormData(prev => ({ ...prev, excerpt: e.target.value }))}
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="content">Content</Label>
|
|
<div className="border rounded-lg">
|
|
<div className="flex items-center gap-2 p-2 border-b bg-muted">
|
|
<Button size="sm" variant="ghost">
|
|
<Image className="h-4 w-4" />
|
|
</Button>
|
|
<Button size="sm" variant="ghost">
|
|
<Video className="h-4 w-4" />
|
|
</Button>
|
|
<Button size="sm" variant="ghost">
|
|
<Upload className="h-4 w-4" />
|
|
</Button>
|
|
<span className="text-xs text-muted-foreground ml-auto">
|
|
Markdown supported
|
|
</span>
|
|
</div>
|
|
<Textarea
|
|
id="content"
|
|
placeholder="Write your article content here. You can use markdown formatting and embed images/videos..."
|
|
className="min-h-[300px] border-0 resize-none focus-visible:ring-0"
|
|
value={formData.content}
|
|
onChange={(e) => setFormData(prev => ({ ...prev, content: e.target.value }))}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="tags">Tags</Label>
|
|
<Input
|
|
id="tags"
|
|
placeholder="Leadership, Strategy, Team Building (comma separated)"
|
|
value={formData.tags}
|
|
onChange={(e) => setFormData(prev => ({ ...prev, tags: e.target.value }))}
|
|
/>
|
|
</div>
|
|
|
|
<div className="p-4 bg-blue-50 border border-blue-200 rounded-lg">
|
|
<h4 className="font-medium text-blue-800 mb-2">Submission Guidelines</h4>
|
|
<ul className="text-sm text-blue-700 space-y-1">
|
|
<li>• Articles are reviewed by our editorial team before publication</li>
|
|
<li>• Focus on original insights and practical leadership advice</li>
|
|
<li>• Include specific examples and actionable takeaways</li>
|
|
<li>• Maintain professional tone and cite sources when needed</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div className="flex justify-end gap-2">
|
|
<Button variant="outline" onClick={() => setIsDialogOpen(false)}>
|
|
Cancel
|
|
</Button>
|
|
<Button variant="outline">
|
|
Save Draft
|
|
</Button>
|
|
<Button onClick={handleSubmit}>
|
|
{editingPost ? 'Update Article' : 'Submit for Review'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</div>
|
|
|
|
{/* Stats Cards */}
|
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
<Card>
|
|
<CardContent className="p-4">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 rounded-lg bg-green-500 flex items-center justify-center text-white">
|
|
<CheckCircle className="h-5 w-5" />
|
|
</div>
|
|
<div>
|
|
<p className="text-2xl font-bold">{mockBlogData.filter(p => p.status === 'published').length}</p>
|
|
<p className="text-sm text-muted-foreground">Published</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardContent className="p-4">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 rounded-lg bg-blue-500 flex items-center justify-center text-white">
|
|
<Clock className="h-5 w-5" />
|
|
</div>
|
|
<div>
|
|
<p className="text-2xl font-bold">{mockBlogData.filter(p => p.status === 'under-review').length}</p>
|
|
<p className="text-sm text-muted-foreground">Under Review</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardContent className="p-4">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 rounded-lg bg-gray-500 flex items-center justify-center text-white">
|
|
<Edit className="h-5 w-5" />
|
|
</div>
|
|
<div>
|
|
<p className="text-2xl font-bold">{mockBlogData.filter(p => p.status === 'draft').length}</p>
|
|
<p className="text-sm text-muted-foreground">Drafts</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardContent className="p-4">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 rounded-lg bg-[#F8C301] flex items-center justify-center text-[#04045B]">
|
|
<Eye className="h-5 w-5" />
|
|
</div>
|
|
<div>
|
|
<p className="text-2xl font-bold">
|
|
{mockBlogData.reduce((sum, post) => sum + (post.views || 0), 0)}
|
|
</p>
|
|
<p className="text-sm text-muted-foreground">Total Views</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Tabs */}
|
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-4">
|
|
<TabsList>
|
|
<TabsTrigger value="my-posts">My Submissions</TabsTrigger>
|
|
<TabsTrigger value="guidelines">Guidelines</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<TabsContent value="my-posts" className="space-y-4">
|
|
{mockBlogData.length === 0 ? (
|
|
<Card>
|
|
<CardContent className="p-8 text-center">
|
|
<Edit className="h-12 w-12 mx-auto text-muted-foreground mb-4" />
|
|
<h3 className="font-medium mb-2">No articles yet</h3>
|
|
<p className="text-sm text-muted-foreground mb-4">
|
|
Start sharing your leadership insights with the community
|
|
</p>
|
|
<Button>
|
|
<Plus className="h-4 w-4 mr-2" />
|
|
Write Your First Article
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
) : (
|
|
<div className="space-y-4">
|
|
{mockBlogData.map((post) => (
|
|
<BlogPostCard key={post.id} post={post} />
|
|
))}
|
|
</div>
|
|
)}
|
|
</TabsContent>
|
|
|
|
<TabsContent value="guidelines">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Article Submission Guidelines</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-4">
|
|
<div>
|
|
<h3 className="font-medium mb-2">Content Requirements</h3>
|
|
<ul className="text-sm space-y-1 text-muted-foreground">
|
|
<li>• Minimum 800 words, maximum 3000 words</li>
|
|
<li>• Original content with practical leadership insights</li>
|
|
<li>• Include specific examples and actionable advice</li>
|
|
<li>• Cite sources and provide references where applicable</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="font-medium mb-2">Review Process</h3>
|
|
<ul className="text-sm space-y-1 text-muted-foreground">
|
|
<li>• Articles are reviewed within 5-7 business days</li>
|
|
<li>• Editorial team may suggest revisions</li>
|
|
<li>• Approved articles are published on the KLC website</li>
|
|
<li>• Authors are notified of publication via email</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="font-medium mb-2">Topics We're Looking For</h3>
|
|
<ul className="text-sm space-y-1 text-muted-foreground">
|
|
<li>• Strategic leadership and vision</li>
|
|
<li>• Team building and management</li>
|
|
<li>• Change management and innovation</li>
|
|
<li>• Digital transformation leadership</li>
|
|
<li>• Emotional intelligence and soft skills</li>
|
|
<li>• Industry-specific leadership challenges</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
</Tabs>
|
|
</div>
|
|
);
|
|
} |