veza/apps/web/src/components/settings/integrations/IntegrationsView.tsx

196 lines
6.4 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 '../../../components/feedback/ToastProvider';
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-kodo-content-dim 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-kodo-content-dim">Not Connected</span>
)}
{integration.lastSync && (
<span className="text-kodo-content-dim">
{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-kodo-content-dim hover:text-white" />
</Button>
)}
</div>
<p className="text-kodo-content-dim text-sm mb-6 flex-1">
{integration.description}
</p>
<div className="flex gap-4 mt-auto">
{integration.connected ? (
<>
<Button
variant="ghost"
className="flex-1 border border-kodo-steel text-kodo-text-main"
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>
);
};