Files
kandev/apps/web/app/settings/plugins/page.tsx
T
2026-01-12 20:56:03 +00:00

145 lines
4.4 KiB
TypeScript

'use client';
import { useState } from 'react';
import {
IconBrandAws,
IconBrandDocker,
IconCloud,
IconServer,
IconKey,
IconShield,
} from '@tabler/icons-react';
import { Button } from '@kandev/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@kandev/ui/card';
import { Separator } from '@kandev/ui/separator';
import { Badge } from '@kandev/ui/badge';
type Plugin = {
id: string;
name: string;
description: string;
icon: React.ReactNode;
installed: boolean;
category: 'infrastructure' | 'security';
};
export default function PluginsPage() {
const [plugins, setPlugins] = useState<Plugin[]>([
{
id: 'aws',
name: 'AWS Cloud',
description: 'Deploy and manage resources on Amazon Web Services',
icon: <IconBrandAws className="h-5 w-5" />,
installed: false,
category: 'infrastructure',
},
{
id: 'hetzner',
name: 'Hetzner',
description: 'Deploy applications to Hetzner Cloud infrastructure',
icon: <IconServer className="h-5 w-5" />,
installed: false,
category: 'infrastructure',
},
{
id: 'flyio',
name: 'Fly.io',
description: 'Deploy apps globally with Fly.io edge infrastructure',
icon: <IconCloud className="h-5 w-5" />,
installed: false,
category: 'infrastructure',
},
{
id: 'cloudflare',
name: 'Cloudflare',
description: 'Use Cloudflare Workers and edge compute platform',
icon: <IconShield className="h-5 w-5" />,
installed: false,
category: 'infrastructure',
},
{
id: 'docker',
name: 'Docker',
description: 'Container runtime for local and remote environments',
icon: <IconBrandDocker className="h-5 w-5" />,
installed: true,
category: 'infrastructure',
},
{
id: 'sso',
name: 'Single Sign-On',
description: 'Enterprise SSO authentication provider',
icon: <IconKey className="h-5 w-5" />,
installed: false,
category: 'security',
},
]);
const handleToggleInstall = (id: string) => {
setPlugins(plugins.map(plugin =>
plugin.id === id ? { ...plugin, installed: !plugin.installed } : plugin
));
};
const groupedPlugins = {
'Infrastructure': plugins.filter(p => p.category === 'infrastructure'),
'Security': plugins.filter(p => p.category === 'security'),
};
return (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold">Plugins</h2>
<p className="text-sm text-muted-foreground mt-1">
Extend functionality with infrastructure and security plugins
</p>
</div>
<Separator />
<div className="space-y-8">
{Object.entries(groupedPlugins).map(([category, items]) => (
<div key={category} className="space-y-4">
<h3 className="text-lg font-semibold">{category}</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{items.map((plugin) => (
<Card key={plugin.id}>
<CardHeader>
<div className="flex items-start justify-between">
<div className="flex items-center gap-3">
<div className="p-2 bg-muted rounded-md">
{plugin.icon}
</div>
<div>
<CardTitle className="text-base">{plugin.name}</CardTitle>
{plugin.installed && (
<Badge variant="secondary" className="mt-1">
Installed
</Badge>
)}
</div>
</div>
</div>
<CardDescription className="mt-2">
{plugin.description}
</CardDescription>
</CardHeader>
<CardContent>
<Button
variant={plugin.installed ? 'outline' : 'default'}
className="w-full"
onClick={() => handleToggleInstall(plugin.id)}
>
{plugin.installed ? 'Uninstall' : 'Install'}
</Button>
</CardContent>
</Card>
))}
</div>
</div>
))}
</div>
</div>
);
}