← back to Dear Bubbe Nextjs
app/marketing/page.tsx
951 lines
'use client'
import { useState, useEffect, useRef } from 'react'
import { useSession, signIn } from 'next-auth/react'
interface Stats {
totalConversations: number
blogPostsCreated: number
socialPostsGenerated: number
emailCampaignsDrafted: number
lastActivity: string
uptime: number
tasksCompleted: number
errorsToday: number
qnaReadiness: number
responseTime: number
}
interface HealthStatus {
status: 'healthy' | 'warning' | 'critical'
services: {
name: string
status: 'online' | 'offline' | 'error'
details: string
}[]
systemInfo: {
uptime: number
memoryUsage: any
timestamp: string
}
suggestions: string[]
}
interface Product {
id: string
title: string
sku: string
description: string
image?: string
price?: string
variant_title?: string
}
interface BlogPost {
id: string
title: string
createdAt: string
publishedAt: string
tags: string
summary: string
author: string
}
interface ChatMessage {
role: 'user' | 'assistant'
content: string
timestamp?: string
}
interface Toast {
id: string
message: string
type: 'success' | 'error' | 'info'
}
export default function MarketingDashboard() {
const { data: session } = useSession()
const [stats, setStats] = useState<Stats | null>(null)
const [health, setHealth] = useState<HealthStatus | null>(null)
const [products, setProducts] = useState<Product[]>([])
const [blogs, setBlogs] = useState<BlogPost[]>([])
const [selectedProduct, setSelectedProduct] = useState<Product | null>(null)
const [isLoading, setIsLoading] = useState(false)
const [isGenerating, setIsGenerating] = useState(false)
const [activeTab, setActiveTab] = useState<'dashboard' | 'chat' | 'blog' | 'create' | 'social' | 'email' | 'sms' | 'health'>('dashboard')
const [messages, setMessages] = useState<ChatMessage[]>([
{
role: 'assistant',
content: "Hi! I'm your Marketing Agent. I can help you:\n\n• Create blog posts from new SKUs\n• View and manage Shopify blog posts\n• Generate social media content\n• Draft email campaigns\n• Write compelling product descriptions\n\nWhat would you like to create today?"
}
])
const [inputMessage, setInputMessage] = useState('')
const [toasts, setToasts] = useState<Toast[]>([])
const [sidebarOpen, setSidebarOpen] = useState(false)
const [socialOutput, setSocialOutput] = useState('')
const [showEmailModal, setShowEmailModal] = useState(false)
const [showSlackModal, setShowSlackModal] = useState(false)
const [showSMSModal, setShowSMSModal] = useState(false)
// Modal form states
const [emailForm, setEmailForm] = useState({
to: '',
subject: '',
body: ''
})
const [slackForm, setSlackForm] = useState({
channel: '#general',
message: ''
})
const [smsForm, setSmsForm] = useState({
to: '',
message: ''
})
const messagesRef = useRef<HTMLDivElement>(null)
// Load initial data
useEffect(() => {
if (session) {
loadStats()
loadHealth()
const interval = setInterval(loadStats, 30000) // Update every 30 seconds
return () => clearInterval(interval)
}
}, [session])
// Auto-scroll chat messages
useEffect(() => {
if (messagesRef.current) {
messagesRef.current.scrollTop = messagesRef.current.scrollHeight
}
}, [messages])
const showToast = (message: string, type: 'success' | 'error' | 'info' = 'info') => {
const id = Date.now().toString()
const toast = { id, message, type }
setToasts(prev => [...prev, toast])
setTimeout(() => {
setToasts(prev => prev.filter(t => t.id !== id))
}, 5000)
}
const loadStats = async () => {
try {
const response = await fetch('/api/stats')
if (response.ok) {
const data = await response.json()
setStats(data)
}
} catch (error) {
console.error('Error loading stats:', error)
}
}
const loadHealth = async () => {
try {
const response = await fetch('/api/health-check')
if (response.ok) {
const data = await response.json()
setHealth(data)
}
} catch (error) {
console.error('Error loading health status:', error)
}
}
const loadProducts = async () => {
setIsLoading(true)
try {
const response = await fetch('/api/shopify/products')
if (response.ok) {
const data = await response.json()
setProducts(data.products || [])
showToast(`Loaded ${data.products?.length || 0} products`, 'success')
} else {
throw new Error('Failed to load products')
}
} catch (error) {
console.error('Error loading products:', error)
showToast('Failed to load products', 'error')
}
setIsLoading(false)
}
const loadBlogs = async () => {
setIsLoading(true)
try {
const response = await fetch('/api/shopify/blogs')
if (response.ok) {
const data = await response.json()
setBlogs(data.blogs || [])
showToast(`Loaded ${data.blogs?.length || 0} blog posts`, 'success')
} else {
throw new Error('Failed to load blogs')
}
} catch (error) {
console.error('Error loading blogs:', error)
showToast('Failed to load blog posts', 'error')
}
setIsLoading(false)
}
const runHealthCheck = async () => {
setIsLoading(true)
try {
await loadHealth()
showToast('Health check completed', 'success')
} catch (error) {
showToast('Health check failed', 'error')
}
setIsLoading(false)
}
const runAutoFix = async () => {
setIsLoading(true)
try {
const response = await fetch('/api/health-check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'fix' })
})
if (response.ok) {
const result = await response.json()
showToast(`Auto-fix completed: ${result.fixes?.join(', ') || 'System optimized'}`, 'success')
await loadHealth()
} else {
throw new Error('Auto-fix failed')
}
} catch (error) {
console.error('Error running auto-fix:', error)
showToast('Auto-fix failed', 'error')
}
setIsLoading(false)
}
const generateBlog = async () => {
if (!selectedProduct) {
showToast('Please select a product first', 'error')
return
}
setIsGenerating(true)
try {
const response = await fetch('/api/generate/blog', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: selectedProduct.title,
sku: selectedProduct.sku,
description: selectedProduct.description
})
})
if (response.ok) {
const result = await response.json()
showToast(`Blog post generated: "${result.blog?.title || 'New post'}"`, 'success')
await loadStats()
// Add to chat as well
addMessage('assistant', `✅ Blog post generated successfully!\n\nTitle: "${result.blog?.title}"\nFor SKU: ${selectedProduct.sku}\n\nThe post has been created and is ready for review.`)
} else {
throw new Error('Blog generation failed')
}
} catch (error) {
console.error('Error generating blog:', error)
showToast('Failed to generate blog post', 'error')
}
setIsGenerating(false)
}
const generateSocial = async (platform: string) => {
setIsGenerating(true)
setSocialOutput('')
try {
const response = await fetch('/api/generate/social', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
platform,
topic: selectedProduct ? `New product: ${selectedProduct.title}` : 'New product launch'
})
})
if (response.ok) {
const result = await response.json()
setSocialOutput(result.content || 'Content generated successfully!')
showToast(`${platform} post generated successfully`, 'success')
await loadStats()
} else {
throw new Error('Social generation failed')
}
} catch (error) {
console.error('Error generating social content:', error)
showToast('Failed to generate social content', 'error')
}
setIsGenerating(false)
}
const sendEmail = async () => {
if (!emailForm.to || !emailForm.subject || !emailForm.body) {
showToast('Please fill in all email fields', 'error')
return
}
setIsLoading(true)
try {
const response = await fetch('/api/email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(emailForm)
})
if (response.ok) {
showToast('Email sent successfully!', 'success')
setShowEmailModal(false)
setEmailForm({ to: '', subject: '', body: '' })
await loadStats()
} else {
throw new Error('Email sending failed')
}
} catch (error) {
console.error('Error sending email:', error)
showToast('Failed to send email', 'error')
}
setIsLoading(false)
}
const sendSMS = async () => {
if (!smsForm.to || !smsForm.message) {
showToast('Please fill in all SMS fields', 'error')
return
}
setIsLoading(true)
try {
const response = await fetch('/api/sms', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(smsForm)
})
if (response.ok) {
showToast('SMS sent successfully!', 'success')
setShowSMSModal(false)
setSmsForm({ to: '', message: '' })
await loadStats()
} else {
throw new Error('SMS sending failed')
}
} catch (error) {
console.error('Error sending SMS:', error)
showToast('Failed to send SMS', 'error')
}
setIsLoading(false)
}
const addMessage = (role: 'user' | 'assistant', content: string) => {
setMessages(prev => [...prev, { role, content, timestamp: new Date().toISOString() }])
}
const sendMessage = async () => {
if (!inputMessage.trim()) return
const message = inputMessage.trim()
addMessage('user', message)
setInputMessage('')
setIsLoading(true)
try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message })
})
if (response.ok) {
const data = await response.json()
addMessage('assistant', data.response || 'I understand. How can I help you create marketing content today?')
await loadStats()
} else {
throw new Error('Chat failed')
}
} catch (error) {
console.error('Chat error:', error)
addMessage('assistant', 'Sorry, I encountered an error. Please try again.')
showToast('Failed to send message', 'error')
}
setIsLoading(false)
}
const sendSuggestion = (text: string) => {
setInputMessage(text)
setTimeout(() => sendMessage(), 100)
}
if (!session) {
return (
<div className="min-h-screen bg-gradient-to-br from-[#f093fb] to-[#f5576c] flex items-center justify-center p-5">
<div className="text-center bg-white p-8 rounded-[15px] shadow-lg max-w-md">
<div className="text-6xl mb-4">📢</div>
<h1 className="text-3xl font-bold text-gray-800 mb-4">Marketing Dashboard</h1>
<p className="text-gray-600 mb-8">Please sign in to access the marketing tools</p>
<button
onClick={() => signIn()}
className="bg-gradient-to-r from-[#f093fb] to-[#f5576c] text-white px-8 py-3 rounded-lg hover:shadow-lg transition-all transform hover:-translate-y-1 font-semibold"
>
Sign In
</button>
</div>
</div>
)
}
return (
<div className="min-h-screen bg-gradient-to-br from-[#f093fb] to-[#f5576c] relative">
{/* Mobile sidebar toggle */}
<button
onClick={() => setSidebarOpen(!sidebarOpen)}
className="lg:hidden fixed top-5 left-5 z-50 bg-white rounded-full w-12 h-12 flex items-center justify-center shadow-lg hover:shadow-xl transition-all"
>
☰
</button>
{/* Sidebar overlay for mobile */}
{sidebarOpen && (
<div
className="lg:hidden fixed inset-0 bg-black bg-opacity-50 z-40"
onClick={() => setSidebarOpen(false)}
/>
)}
<div className="flex">
{/* Sidebar */}
<div className={`fixed lg:static inset-y-0 left-0 z-40 w-80 bg-white shadow-2xl overflow-y-auto transition-transform duration-300 ${
sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'
}`}>
<div className="p-5 space-y-5">
{/* Header */}
<div className="bg-gradient-to-r from-[#f093fb] to-[#f5576c] p-6 rounded-[15px] text-white">
<div className="flex items-center gap-3 mb-3">
<div className="text-2xl">📢</div>
<h1 className="text-xl font-bold">Marketing Agent</h1>
</div>
<div className="text-sm opacity-90">Content Creation & Campaigns</div>
<div className="text-xs mt-3 opacity-75">
Welcome, {session.user?.name || session.user?.email}
</div>
</div>
{/* Stats Cards */}
<div className="space-y-4">
<div className="bg-white p-5 rounded-[15px] shadow-md border">
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">Blog Posts Created</h3>
<div className="text-2xl font-bold text-[#d8194e]">{stats?.blogPostsCreated || 0}</div>
</div>
<div className="bg-white p-5 rounded-[15px] shadow-md border">
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">Social Posts</h3>
<div className="text-2xl font-bold text-[#d8194e]">{stats?.socialPostsGenerated || 0}</div>
</div>
<div className="bg-white p-5 rounded-[15px] shadow-md border">
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">Email Campaigns</h3>
<div className="text-2xl font-bold text-[#d8194e]">{stats?.emailCampaignsDrafted || 0}</div>
</div>
<div className="bg-white p-5 rounded-[15px] shadow-md border">
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">Conversations</h3>
<div className="text-2xl font-bold text-[#d8194e]">{stats?.totalConversations || 0}</div>
</div>
</div>
{/* System Tools */}
<div className="bg-white p-5 rounded-[15px] shadow-md">
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-4">System Tools</h3>
<div className="space-y-3">
<button
onClick={runHealthCheck}
disabled={isLoading}
className="w-full flex items-center gap-3 p-3 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-lg hover:shadow-lg transition-all disabled:opacity-50"
>
<span>🏥</span> Health Check
</button>
<button
onClick={runAutoFix}
disabled={isLoading}
className="w-full flex items-center gap-3 p-3 bg-gradient-to-r from-[#f093fb] to-[#f5576c] text-white rounded-lg hover:shadow-lg transition-all disabled:opacity-50"
>
<span>🔧</span> Auto-Fix
</button>
</div>
</div>
{/* Communication Tools */}
<div className="bg-white p-5 rounded-[15px] shadow-md">
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-4">📧 Communication</h3>
<div className="space-y-3">
<button
onClick={() => setShowEmailModal(true)}
className="w-full flex items-center gap-3 p-3 bg-gradient-to-r from-green-500 to-green-600 text-white rounded-lg hover:shadow-lg transition-all"
>
<span>📧</span> Send Email
</button>
<button
onClick={() => setShowSlackModal(true)}
className="w-full flex items-center gap-3 p-3 bg-gradient-to-r from-purple-500 to-purple-600 text-white rounded-lg hover:shadow-lg transition-all"
>
<span>💬</span> Post to Slack
</button>
<button
onClick={() => setShowSMSModal(true)}
className="w-full flex items-center gap-3 p-3 bg-gradient-to-r from-orange-500 to-orange-600 text-white rounded-lg hover:shadow-lg transition-all"
>
<span>📱</span> Send SMS
</button>
</div>
</div>
</div>
</div>
{/* Main Content */}
<div className="flex-1 lg:ml-0 p-5 lg:pt-5 pt-20">
<div className="bg-white rounded-[15px] shadow-lg overflow-hidden">
{/* Tab Navigation */}
<div className="flex overflow-x-auto bg-gray-50 scrollbar-thin scrollbar-thumb-gray-300">
{[
{ id: 'dashboard', label: '💬 Chat Assistant', icon: '💬' },
{ id: 'blog', label: '📝 Blog Posts', icon: '📝' },
{ id: 'create', label: '✨ Create from SKU', icon: '✨' },
{ id: 'social', label: '📱 Social Media', icon: '📱' },
{ id: 'health', label: '🏥 Health Monitor', icon: '🏥' }
].map(tab => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id as any)}
className={`flex-shrink-0 min-w-[140px] py-4 px-4 text-center font-semibold transition-all ${
activeTab === tab.id
? 'bg-white text-[#d8194e] border-b-3 border-[#d8194e]'
: 'text-gray-600 hover:bg-gray-100'
}`}
>
{tab.label}
</button>
))}
</div>
{/* Tab Content */}
<div className="p-6">
{/* Chat Tab */}
{activeTab === 'dashboard' && (
<div>
<h2 className="text-xl font-bold text-[#f5576c] mb-4">Chat with Marketing Agent</h2>
<div className="flex flex-wrap gap-2 mb-4">
{[
'📝 Recent blogs',
'✨ Create blog',
'📸 Instagram post',
'📧 Email campaign'
].map(suggestion => (
<button
key={suggestion}
onClick={() => sendSuggestion(suggestion)}
className="px-4 py-2 bg-gray-100 rounded-full text-sm hover:bg-[#f5576c] hover:text-white transition-all"
>
{suggestion}
</button>
))}
</div>
<div className="h-96 flex flex-col">
<div ref={messagesRef} className="flex-1 overflow-y-auto p-4 bg-gray-50 rounded-lg mb-4 space-y-3">
{messages.map((msg, idx) => (
<div key={idx} className={`flex gap-3 ${msg.role === 'user' ? 'flex-row-reverse' : ''}`}>
<div className="w-10 h-10 rounded-full flex items-center justify-center text-lg flex-shrink-0">
{msg.role === 'user' ? '👤' : '🤖'}
</div>
<div className={`max-w-[70%] p-3 rounded-xl ${
msg.role === 'user'
? 'bg-gradient-to-r from-[#f093fb] to-[#f5576c] text-white rounded-br-sm'
: 'bg-white shadow-sm rounded-bl-sm'
}`}>
<div className="whitespace-pre-wrap">{msg.content}</div>
</div>
</div>
))}
</div>
<div className="flex gap-2">
<input
type="text"
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
placeholder="Ask me to create content, view blogs, or generate posts..."
className="flex-1 p-3 border-2 border-gray-200 rounded-full focus:border-[#f5576c] outline-none"
/>
<button
onClick={sendMessage}
disabled={isLoading || !inputMessage.trim()}
className="px-6 py-3 bg-gradient-to-r from-[#f093fb] to-[#f5576c] text-white rounded-full hover:shadow-lg transition-all disabled:opacity-50"
>
{isLoading ? <div className="animate-spin w-5 h-5 border-2 border-white border-t-transparent rounded-full" /> : 'Send'}
</button>
</div>
</div>
</div>
)}
{/* Blog Posts Tab */}
{activeTab === 'blog' && (
<div>
<h2 className="text-xl font-bold text-[#f5576c] mb-4">Recent Blog Posts from Shopify</h2>
<button
onClick={loadBlogs}
disabled={isLoading}
className="mb-6 px-6 py-3 bg-gradient-to-r from-[#f093fb] to-[#f5576c] text-white rounded-lg hover:shadow-lg transition-all disabled:opacity-50"
>
🔄 {isLoading ? 'Loading...' : 'Refresh Blog List'}
</button>
<div className="space-y-3 max-h-96 overflow-y-auto">
{blogs.length > 0 ? (
blogs.map(blog => (
<div key={blog.id} className="p-4 border rounded-lg hover:bg-gray-50 transition-colors">
<div className="font-semibold text-gray-900 mb-1">{blog.title}</div>
<div className="text-sm text-gray-500">
Created: {new Date(blog.createdAt).toLocaleDateString()} |
{blog.publishedAt ? ' Published' : ' Draft'}
{blog.tags && ` | Tags: ${blog.tags}`}
</div>
{blog.summary && <div className="text-sm text-gray-600 mt-2">{blog.summary}</div>}
</div>
))
) : (
<div className="text-gray-500 text-center py-8">
Click "Refresh Blog List" to load recent posts from Shopify...
</div>
)}
</div>
</div>
)}
{/* Create from SKU Tab */}
{activeTab === 'create' && (
<div>
<h2 className="text-xl font-bold text-[#f5576c] mb-4">Create Blog Post from Product</h2>
<button
onClick={loadProducts}
disabled={isLoading}
className="mb-6 px-6 py-3 bg-gradient-to-r from-[#f093fb] to-[#f5576c] text-white rounded-lg hover:shadow-lg transition-all disabled:opacity-50"
>
🔄 {isLoading ? 'Loading...' : 'Load Recent Products'}
</button>
<p className="text-gray-600 mb-4">Select a product below to generate a blog post:</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 max-h-96 overflow-y-auto">
{products.length > 0 ? (
products.map(product => (
<div
key={product.id}
onClick={() => setSelectedProduct(product)}
className={`p-4 border-2 rounded-lg cursor-pointer transition-all hover:shadow-md ${
selectedProduct?.id === product.id
? 'border-[#f5576c] bg-pink-50'
: 'border-gray-200 hover:border-gray-300'
}`}
>
{product.image && (
<img
src={product.image}
alt={product.title}
className="w-full h-32 object-cover rounded-lg mb-3"
/>
)}
<div className="font-semibold text-sm mb-1">{product.title}</div>
<div className="text-xs text-gray-500 mb-1">SKU: {product.sku}</div>
{product.price && <div className="text-xs text-green-600 font-semibold">{product.price}</div>}
</div>
))
) : (
<div className="col-span-full text-gray-500 text-center py-8">
Click "Load Recent Products" to see available SKUs...
</div>
)}
</div>
{selectedProduct && (
<div className="mt-6">
<div className="p-4 bg-gray-50 rounded-lg mb-4">
<div className="font-semibold">Selected Product:</div>
<div className="text-sm text-gray-600">{selectedProduct.title} (SKU: {selectedProduct.sku})</div>
</div>
<button
onClick={generateBlog}
disabled={isGenerating}
className="px-6 py-3 bg-gradient-to-r from-[#f093fb] to-[#f5576c] text-white rounded-lg hover:shadow-lg transition-all disabled:opacity-50"
>
{isGenerating ? 'Generating...' : '✨ Generate Blog Post'}
</button>
</div>
)}
</div>
)}
{/* Social Media Tab */}
{activeTab === 'social' && (
<div>
<h2 className="text-xl font-bold text-[#f5576c] mb-4">Social Media Content Generator</h2>
<p className="text-gray-600 mb-6">Generate social media posts for Instagram, Facebook, and more!</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
{[
{ platform: 'instagram', label: '📸 Instagram Post', color: 'from-pink-500 to-purple-600' },
{ platform: 'facebook', label: '👍 Facebook Post', color: 'from-blue-500 to-blue-600' },
{ platform: 'pinterest', label: '📌 Pinterest Description', color: 'from-red-500 to-red-600' },
{ platform: 'twitter', label: '🐦 Twitter Post', color: 'from-blue-400 to-blue-500' },
{ platform: 'linkedin', label: '💼 LinkedIn Post', color: 'from-blue-600 to-blue-700' },
{ platform: 'tiktok', label: '🎵 TikTok Caption', color: 'from-black to-gray-800' }
].map(({ platform, label, color }) => (
<button
key={platform}
onClick={() => generateSocial(platform)}
disabled={isGenerating}
className={`p-4 bg-gradient-to-r ${color} text-white rounded-lg hover:shadow-lg transition-all disabled:opacity-50`}
>
{label}
</button>
))}
</div>
{socialOutput && (
<div className="p-5 bg-gray-50 rounded-lg">
<h3 className="text-lg font-semibold text-[#f5576c] mb-3">Generated Content:</h3>
<pre className="whitespace-pre-wrap text-sm text-gray-700 font-sans">{socialOutput}</pre>
</div>
)}
</div>
)}
{/* Health Tab */}
{activeTab === 'health' && (
<div>
<div className="flex items-center justify-between mb-6">
<h2 className="text-xl font-bold text-[#f5576c]">System Health Monitor</h2>
<div className="text-sm text-gray-500">
Last updated: {health?.systemInfo?.timestamp ? new Date(health.systemInfo.timestamp).toLocaleTimeString() : 'Never'}
</div>
</div>
{health ? (
<div className="space-y-6">
<div className={`text-lg font-semibold p-4 rounded-lg ${
health.status === 'healthy' ? 'text-green-700 bg-green-50' :
health.status === 'warning' ? 'text-yellow-700 bg-yellow-50' :
'text-red-700 bg-red-50'
}`}>
Overall Status: {health.status.charAt(0).toUpperCase() + health.status.slice(1)}
</div>
<div className="space-y-3">
<h3 className="font-semibold text-gray-800">Services Status:</h3>
{health.services.map((service, index) => (
<div key={index} className="flex items-center justify-between p-4 bg-gray-50 rounded-lg">
<div className="font-medium">{service.name}</div>
<div className="flex items-center gap-3">
<span className={`px-3 py-1 rounded-full text-sm font-medium ${
service.status === 'online' ? 'bg-green-100 text-green-800' :
service.status === 'offline' ? 'bg-yellow-100 text-yellow-800' :
'bg-red-100 text-red-800'
}`}>
{service.status}
</span>
<span className="text-gray-500 text-sm">{service.details}</span>
</div>
</div>
))}
</div>
{health.suggestions.length > 0 && (
<div className="p-4 bg-blue-50 rounded-lg">
<h4 className="font-medium text-blue-800 mb-3">Suggestions:</h4>
<ul className="list-disc list-inside text-blue-700 space-y-1">
{health.suggestions.map((suggestion, index) => (
<li key={index}>{suggestion}</li>
))}
</ul>
</div>
)}
{health.systemInfo && (
<div className="p-4 bg-gray-50 rounded-lg">
<h4 className="font-medium text-gray-800 mb-3">System Information:</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
<div>
<span className="text-gray-600">Uptime:</span>
<span className="ml-2 font-medium">{Math.floor(health.systemInfo.uptime / 3600)}h {Math.floor((health.systemInfo.uptime % 3600) / 60)}m</span>
</div>
<div>
<span className="text-gray-600">Memory Usage:</span>
<span className="ml-2 font-medium">
{health.systemInfo.memoryUsage?.used ?
`${Math.round(health.systemInfo.memoryUsage.used / 1024 / 1024)}MB` :
'N/A'
}
</span>
</div>
</div>
</div>
)}
</div>
) : (
<div className="text-center py-8 text-gray-500">
Click "Health Check" in the sidebar to run system diagnostics...
</div>
)}
</div>
)}
</div>
</div>
</div>
</div>
{/* Toast Notifications */}
<div className="fixed top-5 right-5 z-50 space-y-2">
{toasts.map(toast => (
<div
key={toast.id}
className={`flex items-center gap-3 p-4 bg-white rounded-lg shadow-lg border-l-4 min-w-72 transform transition-all duration-300 ${
toast.type === 'success' ? 'border-green-500' :
toast.type === 'error' ? 'border-red-500' : 'border-blue-500'
}`}
>
<div className="text-lg">
{toast.type === 'success' ? '✓' : toast.type === 'error' ? '✕' : 'ℹ'}
</div>
<div className="flex-1 text-sm">{toast.message}</div>
<button
onClick={() => setToasts(prev => prev.filter(t => t.id !== toast.id))}
className="text-gray-400 hover:text-gray-600"
>
×
</button>
</div>
))}
</div>
{/* Email Modal */}
{showEmailModal && (
<div className="fixed inset-0 z-50 bg-black bg-opacity-50 flex items-center justify-center p-4">
<div className="bg-white rounded-[15px] w-full max-w-md">
<div className="bg-gradient-to-r from-[#f093fb] to-[#f5576c] text-white p-5 rounded-t-[15px] flex items-center justify-between">
<h3 className="text-lg font-semibold">📧 Send Email</h3>
<button
onClick={() => setShowEmailModal(false)}
className="text-white hover:bg-white hover:bg-opacity-20 rounded-full w-8 h-8 flex items-center justify-center"
>
×
</button>
</div>
<div className="p-5 space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">To:</label>
<input
type="email"
value={emailForm.to}
onChange={(e) => setEmailForm(prev => ({ ...prev, to: e.target.value }))}
className="w-full p-3 border rounded-lg focus:border-[#f5576c] outline-none"
placeholder="recipient@example.com"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Subject:</label>
<input
type="text"
value={emailForm.subject}
onChange={(e) => setEmailForm(prev => ({ ...prev, subject: e.target.value }))}
className="w-full p-3 border rounded-lg focus:border-[#f5576c] outline-none"
placeholder="Email subject"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Message:</label>
<textarea
value={emailForm.body}
onChange={(e) => setEmailForm(prev => ({ ...prev, body: e.target.value }))}
className="w-full p-3 border rounded-lg focus:border-[#f5576c] outline-none h-32 resize-none"
placeholder="Type your message here..."
/>
</div>
</div>
<div className="p-5 bg-gray-50 rounded-b-[15px] flex gap-3 justify-end">
<button
onClick={() => setShowEmailModal(false)}
className="px-4 py-2 text-gray-600 hover:bg-gray-200 rounded-lg transition-colors"
>
Cancel
</button>
<button
onClick={sendEmail}
disabled={isLoading}
className="px-6 py-2 bg-gradient-to-r from-[#f093fb] to-[#f5576c] text-white rounded-lg hover:shadow-lg transition-all disabled:opacity-50"
>
{isLoading ? 'Sending...' : 'Send Email'}
</button>
</div>
</div>
</div>
)}
{/* SMS Modal */}
{showSMSModal && (
<div className="fixed inset-0 z-50 bg-black bg-opacity-50 flex items-center justify-center p-4">
<div className="bg-white rounded-[15px] w-full max-w-md">
<div className="bg-gradient-to-r from-[#f093fb] to-[#f5576c] text-white p-5 rounded-t-[15px] flex items-center justify-between">
<h3 className="text-lg font-semibold">📱 Send SMS</h3>
<button
onClick={() => setShowSMSModal(false)}
className="text-white hover:bg-white hover:bg-opacity-20 rounded-full w-8 h-8 flex items-center justify-center"
>
×
</button>
</div>
<div className="p-5 space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">To:</label>
<input
type="tel"
value={smsForm.to}
onChange={(e) => setSmsForm(prev => ({ ...prev, to: e.target.value }))}
className="w-full p-3 border rounded-lg focus:border-[#f5576c] outline-none"
placeholder="+1234567890"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Message:</label>
<textarea
value={smsForm.message}
onChange={(e) => setSmsForm(prev => ({ ...prev, message: e.target.value }))}
className="w-full p-3 border rounded-lg focus:border-[#f5576c] outline-none h-32 resize-none"
placeholder="Type your SMS message here..."
maxLength={160}
/>
<div className="text-xs text-gray-500 mt-1">{smsForm.message.length}/160 characters</div>
</div>
</div>
<div className="p-5 bg-gray-50 rounded-b-[15px] flex gap-3 justify-end">
<button
onClick={() => setShowSMSModal(false)}
className="px-4 py-2 text-gray-600 hover:bg-gray-200 rounded-lg transition-colors"
>
Cancel
</button>
<button
onClick={sendSMS}
disabled={isLoading}
className="px-6 py-2 bg-gradient-to-r from-[#f093fb] to-[#f5576c] text-white rounded-lg hover:shadow-lg transition-all disabled:opacity-50"
>
{isLoading ? 'Sending...' : 'Send SMS'}
</button>
</div>
</div>
</div>
)}
{/* Loading Overlay */}
{isLoading && (
<div className="fixed inset-0 z-40 bg-black bg-opacity-30 flex items-center justify-center">
<div className="bg-white p-6 rounded-[15px] text-center shadow-xl">
<div className="animate-spin w-8 h-8 border-3 border-[#f5576c] border-t-transparent rounded-full mx-auto mb-3" />
<div className="text-gray-700 font-medium">Processing...</div>
</div>
</div>
)}
</div>
)
}