93 lines
5.7 KiB
TypeScript
93 lines
5.7 KiB
TypeScript
|
|
|
||
|
|
import React, { useState } from 'react';
|
||
|
|
import { Card } from '../../ui/card';
|
||
|
|
import { Button } from '../../ui/button';
|
||
|
|
import { CheckCircle, ExternalLink, RefreshCw, AlertCircle } from 'lucide-react';
|
||
|
|
import { useToast } from '../../../context/ToastContext';
|
||
|
|
|
||
|
|
interface Integration {
|
||
|
|
id: string;
|
||
|
|
name: string;
|
||
|
|
description: string;
|
||
|
|
icon: string; // URL or placeholder
|
||
|
|
connected: boolean;
|
||
|
|
status: 'active' | 'error' | 'syncing' | 'disconnected';
|
||
|
|
lastSync?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
const MOCK_INTEGRATIONS: Integration[] = [
|
||
|
|
{ id: '1', name: 'Spotify', description: 'Display your top tracks and sync playlists.', icon: 'https://upload.wikimedia.org/wikipedia/commons/1/19/Spotify_logo_without_text.svg', connected: true, status: 'active', lastSync: '10 mins ago' },
|
||
|
|
{ id: '2', name: 'SoundCloud', description: 'Import tracks directly from your SC account.', icon: 'https://a-v2.sndcdn.com/assets/images/sc-icons/ios-a62dfc8f.png', connected: false, status: 'disconnected' },
|
||
|
|
{ id: '3', name: 'Discord', description: 'Show your production status in rich presence.', icon: 'https://assets-global.website-files.com/6257adef93867e56f84d3092/636e0a6a49cf127bf92de1e2_icon_clyde_blurple_RGB.png', connected: true, status: 'active', lastSync: 'Live' },
|
||
|
|
{ id: '4', name: 'Stripe', description: 'Process payments for your marketplace sales.', icon: 'https://upload.wikimedia.org/wikipedia/commons/b/ba/Stripe_Logo%2C_revised_2016.svg', connected: true, status: 'error', lastSync: 'Failed 2h ago' },
|
||
|
|
{ id: '5', name: 'Dropbox', description: 'Auto-backup your projects to the cloud.', icon: 'https://aem.dropbox.com/cms/content/dam/dropbox/www/en-us/branding/app-icons/dropbox-app-icon-blue.svg', connected: false, status: 'disconnected' },
|
||
|
|
];
|
||
|
|
|
||
|
|
export const IntegrationsView: React.FC = () => {
|
||
|
|
const { addToast } = useToast();
|
||
|
|
const [integrations, setIntegrations] = useState<Integration[]>(MOCK_INTEGRATIONS);
|
||
|
|
|
||
|
|
const toggleConnection = (id: string) => {
|
||
|
|
setIntegrations(prev => prev.map(int => {
|
||
|
|
if (int.id === id) {
|
||
|
|
const newState = !int.connected;
|
||
|
|
addToast(newState ? `Connected to ${int.name}` : `Disconnected from ${int.name}`, newState ? 'success' : 'info');
|
||
|
|
return { ...int, connected: newState, status: newState ? 'active' : 'disconnected' };
|
||
|
|
}
|
||
|
|
return int;
|
||
|
|
}));
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="space-y-6 animate-fadeIn pb-20">
|
||
|
|
<div>
|
||
|
|
<h2 className="text-2xl font-bold text-white mb-2">CONNECTED APPS</h2>
|
||
|
|
<p className="text-gray-400 font-mono text-sm">Supercharge your workflow with external tools.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
|
|
{integrations.map(integration => (
|
||
|
|
<Card key={integration.id} variant="default" className={`flex flex-col h-full ${integration.connected ? 'border-kodo-cyan/30' : 'border-kodo-steel/50'}`}>
|
||
|
|
<div className="flex items-start justify-between mb-4">
|
||
|
|
<div className="flex items-center gap-4">
|
||
|
|
<div className="w-12 h-12 bg-white rounded-xl p-2 flex items-center justify-center">
|
||
|
|
<img src={integration.icon} alt={integration.name} className="w-full h-full object-contain" />
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<h3 className="font-bold text-white text-lg">{integration.name}</h3>
|
||
|
|
<div className="flex items-center gap-2 text-xs">
|
||
|
|
{integration.status === 'active' && <span className="text-kodo-lime flex items-center gap-1"><CheckCircle className="w-3 h-3" /> Active</span>}
|
||
|
|
{integration.status === 'error' && <span className="text-kodo-red flex items-center gap-1"><AlertCircle className="w-3 h-3" /> Error</span>}
|
||
|
|
{integration.status === 'disconnected' && <span className="text-gray-500">Not Connected</span>}
|
||
|
|
{integration.lastSync && <span className="text-gray-500">• {integration.lastSync}</span>}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
{integration.connected && (
|
||
|
|
<Button variant="ghost" size="icon" title="Sync Now" onClick={() => addToast("Syncing...")}>
|
||
|
|
<RefreshCw className="w-4 h-4 text-gray-400 hover:text-white" />
|
||
|
|
</Button>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<p className="text-gray-400 text-sm mb-6 flex-1">{integration.description}</p>
|
||
|
|
|
||
|
|
<div className="flex gap-3 mt-auto">
|
||
|
|
{integration.connected ? (
|
||
|
|
<>
|
||
|
|
<Button variant="ghost" className="flex-1 border border-kodo-steel text-gray-300" onClick={() => addToast("Settings opened")}>Settings</Button>
|
||
|
|
<Button variant="ghost" className="flex-1 text-kodo-red hover:bg-kodo-red/10 border border-kodo-red/30" onClick={() => toggleConnection(integration.id)}>Disconnect</Button>
|
||
|
|
</>
|
||
|
|
) : (
|
||
|
|
<Button variant="primary" className="w-full" onClick={() => toggleConnection(integration.id)}>
|
||
|
|
Connect {integration.name} <ExternalLink className="w-3 h-3 ml-2" />
|
||
|
|
</Button>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</Card>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|