PostGen / frontend /src /pages /Integrations.jsx
Seth
update
f80e9b3
import React, { useState } from 'react';
import { motion } from 'framer-motion';
import {
Link2,
Check,
X,
ExternalLink,
Settings,
RefreshCw,
Shield,
Key,
AlertCircle,
CheckCircle,
Linkedin,
Palette,
Zap,
ArrowRight,
Info,
Save
} from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
import { Separator } from '@/components/ui/separator';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
DialogDescription,
} from '@/components/ui/dialog';
import {
Alert,
AlertDescription,
AlertTitle,
} from '@/components/ui/alert';
const integrations = [
{
id: 'linkedin',
name: 'LinkedIn',
description: 'Connect your LinkedIn account to schedule and publish posts directly',
icon: Linkedin,
color: 'blue',
bgColor: 'bg-[#0A66C2]',
connected: true,
account: 'alex.business@company.com',
lastSync: '2 mins ago',
features: [
'Schedule posts up to 60 days in advance',
'Publish carousel, image, and text posts',
'Track post performance analytics',
'Manage multiple company pages'
]
},
{
id: 'canva',
name: 'Canva',
description: 'Import designs directly from your Canva workspace for post visuals',
icon: Palette,
color: 'purple',
bgColor: 'bg-gradient-to-br from-[#00C4CC] to-[#7B2FF7]',
connected: false,
account: null,
lastSync: null,
features: [
'Browse and import Canva designs',
'Access brand kit assets',
'Import templates for carousels',
'Sync design updates automatically'
]
},
];
export default function Integrations() {
const [linkedInDialogOpen, setLinkedInDialogOpen] = useState(false);
const [canvaDialogOpen, setCanvaDialogOpen] = useState(false);
const [autoPost, setAutoPost] = useState(true);
const [notifications, setNotifications] = useState(true);
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-blue-50/30">
<div className="max-w-5xl 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">
Integrations
</h1>
<p className="text-slate-500 mt-1">
Connect your tools to streamline your content workflow
</p>
</div>
</div>
</motion.div>
{/* Integration Status Banner */}
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="mb-8"
>
<Alert className="border-blue-200 bg-blue-50">
<Info className="h-4 w-4 text-blue-600" />
<AlertTitle className="text-blue-800">Quick Setup</AlertTitle>
<AlertDescription className="text-blue-700">
Connect both LinkedIn and Canva to unlock the full potential of automated content scheduling. Your designs will flow seamlessly to your LinkedIn audience.
</AlertDescription>
</Alert>
</motion.div>
{/* Integration Cards */}
<div className="space-y-6">
{integrations.map((integration, index) => (
<motion.div
key={integration.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
>
<Card className="border-0 shadow-lg shadow-slate-200/50 overflow-hidden">
<CardContent className="p-0">
<div className="flex flex-col lg:flex-row">
{/* Left Section - Integration Info */}
<div className="flex-1 p-6">
<div className="flex items-start gap-4">
<div className={`w-14 h-14 rounded-xl ${integration.bgColor} flex items-center justify-center shadow-lg`}>
<integration.icon className="w-7 h-7 text-white" />
</div>
<div className="flex-1">
<div className="flex items-center gap-3">
<h3 className="text-xl font-semibold text-slate-900">
{integration.name}
</h3>
{integration.connected ? (
<Badge className="bg-emerald-100 text-emerald-700 border-0 gap-1">
<CheckCircle className="w-3 h-3" />
Connected
</Badge>
) : (
<Badge variant="outline" className="text-slate-500 border-slate-300">
Not Connected
</Badge>
)}
</div>
<p className="text-slate-500 mt-1">
{integration.description}
</p>
{integration.connected && (
<div className="flex items-center gap-4 mt-3">
<div className="flex items-center gap-2 text-sm">
<Key className="w-4 h-4 text-slate-400" />
<span className="text-slate-600">{integration.account}</span>
</div>
<div className="flex items-center gap-2 text-sm">
<RefreshCw className="w-4 h-4 text-slate-400" />
<span className="text-slate-500">Synced {integration.lastSync}</span>
</div>
</div>
)}
</div>
</div>
{/* Features */}
<div className="mt-6">
<p className="text-sm font-medium text-slate-700 mb-3">Features</p>
<div className="grid sm:grid-cols-2 gap-2">
{integration.features.map((feature, idx) => (
<div key={idx} className="flex items-center gap-2 text-sm text-slate-600">
<Check className="w-4 h-4 text-emerald-500 shrink-0" />
<span>{feature}</span>
</div>
))}
</div>
</div>
</div>
{/* Right Section - Actions */}
<div className="lg:w-64 p-6 bg-slate-50 border-t lg:border-t-0 lg:border-l border-slate-100 flex flex-col justify-center">
{integration.connected ? (
<div className="space-y-4">
<Dialog>
<DialogTrigger asChild>
<Button variant="outline" className="w-full gap-2">
<Settings className="w-4 h-4" />
Settings
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-lg ${integration.bgColor} flex items-center justify-center`}>
<integration.icon className="w-5 h-5 text-white" />
</div>
{integration.name} Settings
</DialogTitle>
<DialogDescription>
Configure your {integration.name} integration preferences
</DialogDescription>
</DialogHeader>
<div className="space-y-6 pt-4">
<div className="flex items-center justify-between">
<div>
<Label className="text-sm font-medium">Auto-post scheduled content</Label>
<p className="text-xs text-slate-500 mt-0.5">
Automatically publish posts at scheduled times
</p>
</div>
<Switch checked={autoPost} onCheckedChange={setAutoPost} />
</div>
<Separator />
<div className="flex items-center justify-between">
<div>
<Label className="text-sm font-medium">Post notifications</Label>
<p className="text-xs text-slate-500 mt-0.5">
Get notified when posts are published
</p>
</div>
<Switch checked={notifications} onCheckedChange={setNotifications} />
</div>
<Separator />
<div>
<Label className="text-sm font-medium">Connected Account</Label>
<div className="flex items-center gap-3 mt-2 p-3 rounded-lg bg-slate-50">
<div className={`w-8 h-8 rounded-lg ${integration.bgColor} flex items-center justify-center`}>
<integration.icon className="w-4 h-4 text-white" />
</div>
<div className="flex-1">
<p className="text-sm font-medium text-slate-700">{integration.account}</p>
<p className="text-xs text-slate-500">Connected</p>
</div>
<Button variant="ghost" size="sm" className="text-red-600 hover:text-red-700 hover:bg-red-50">
Disconnect
</Button>
</div>
</div>
</div>
</DialogContent>
</Dialog>
<Button variant="ghost" className="w-full gap-2 text-red-600 hover:text-red-700 hover:bg-red-50">
<X className="w-4 h-4" />
Disconnect
</Button>
</div>
) : (
<div className="space-y-4">
<Dialog>
<DialogTrigger asChild>
<Button className={`w-full gap-2 ${
integration.id === 'linkedin'
? 'bg-[#0A66C2] hover:bg-[#004182]'
: 'bg-gradient-to-r from-[#00C4CC] to-[#7B2FF7] hover:opacity-90'
}`}>
<Link2 className="w-4 h-4" />
Connect {integration.name}
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-lg ${integration.bgColor} flex items-center justify-center`}>
<integration.icon className="w-5 h-5 text-white" />
</div>
Connect to {integration.name}
</DialogTitle>
<DialogDescription>
Authorize access to your {integration.name} account
</DialogDescription>
</DialogHeader>
<div className="space-y-6 pt-4">
<div className="p-4 rounded-xl bg-slate-50 border border-slate-100">
<div className="flex items-start gap-3">
<Shield className="w-5 h-5 text-blue-600 mt-0.5" />
<div>
<p className="text-sm font-medium text-slate-700">Secure Connection</p>
<p className="text-xs text-slate-500 mt-1">
Your credentials are encrypted and never stored. We only request necessary permissions.
</p>
</div>
</div>
</div>
<div>
<Label className="text-sm font-medium">Permissions Requested</Label>
<div className="mt-2 space-y-2">
{integration.id === 'linkedin' ? (
<>
<div className="flex items-center gap-2 text-sm text-slate-600">
<Check className="w-4 h-4 text-emerald-500" />
Read and write posts on your behalf
</div>
<div className="flex items-center gap-2 text-sm text-slate-600">
<Check className="w-4 h-4 text-emerald-500" />
Schedule posts for future publishing
</div>
<div className="flex items-center gap-2 text-sm text-slate-600">
<Check className="w-4 h-4 text-emerald-500" />
Access post analytics and insights
</div>
</>
) : (
<>
<div className="flex items-center gap-2 text-sm text-slate-600">
<Check className="w-4 h-4 text-emerald-500" />
View your Canva designs
</div>
<div className="flex items-center gap-2 text-sm text-slate-600">
<Check className="w-4 h-4 text-emerald-500" />
Download designs as images
</div>
<div className="flex items-center gap-2 text-sm text-slate-600">
<Check className="w-4 h-4 text-emerald-500" />
Access brand kit assets
</div>
</>
)}
</div>
</div>
<Button className={`w-full gap-2 ${
integration.id === 'linkedin'
? 'bg-[#0A66C2] hover:bg-[#004182]'
: 'bg-gradient-to-r from-[#00C4CC] to-[#7B2FF7] hover:opacity-90'
}`}>
Continue with {integration.name}
<ExternalLink className="w-4 h-4" />
</Button>
</div>
</DialogContent>
</Dialog>
<p className="text-xs text-center text-slate-500">
You'll be redirected to {integration.name} to authorize
</p>
</div>
)}
</div>
</div>
</CardContent>
</Card>
</motion.div>
))}
</div>
{/* API Configuration */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 }}
className="mt-8"
>
<Card className="border-0 shadow-lg shadow-slate-200/50">
<CardHeader>
<CardTitle className="text-lg font-semibold flex items-center gap-2">
<Key className="w-5 h-5 text-amber-500" />
API Configuration
</CardTitle>
<CardDescription>
For advanced users who want to configure custom API settings
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid md:grid-cols-2 gap-4">
<div>
<Label className="text-sm text-slate-600">LinkedIn Client ID</Label>
<Input
className="mt-1.5"
placeholder="Enter your LinkedIn Client ID"
type="password"
defaultValue="••••••••••••••••"
/>
</div>
<div>
<Label className="text-sm text-slate-600">LinkedIn Client Secret</Label>
<Input
className="mt-1.5"
placeholder="Enter your LinkedIn Client Secret"
type="password"
defaultValue="••••••••••••••••"
/>
</div>
<div>
<Label className="text-sm text-slate-600">Canva API Key</Label>
<Input
className="mt-1.5"
placeholder="Enter your Canva API Key"
type="password"
/>
</div>
<div>
<Label className="text-sm text-slate-600">Webhook URL</Label>
<Input
className="mt-1.5"
placeholder="https://your-webhook-url.com"
/>
</div>
</div>
<div className="flex justify-end">
<Button variant="outline" className="gap-2">
<Save className="w-4 h-4" />
Save Configuration
</Button>
</div>
</CardContent>
</Card>
</motion.div>
</div>
</div>
);
}