builder / components /providers-models /github-connections.tsx
Leon4gr45's picture
Upload folder using huggingface_hub
eeb9404 verified
Raw
History Blame Contribute Delete
4.89 kB
'use client';
import React, { useCallback, useState, useEffect } from 'react';
import {
Plus,
MoreVertical,
Unplug,
Loader2,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { toast } from 'sonner';
import { configManager } from '@/lib/config/storage';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Drawer } from './drawer';
import { GitHubAuthPanel } from '@/components/settings/github-auth-panel';
import { track } from '@/lib/telemetry';
export function GitHubConnectionsSection() {
const [, bump] = useState(0);
const refresh = useCallback(() => bump((v) => v + 1), []);
const [drawerOpen, setDrawerOpen] = useState(false);
const [drawerMode, setDrawerMode] = useState<'connect' | 'edit' | null>(null);
const [isLoading, setIsLoading] = useState(false);
const auth = configManager.getGitHubAuth();
const isConnected = !!auth;
useEffect(() => {
const handleAuthUpdate = (e: Event) => {
const detail = (e as CustomEvent).detail;
if (detail?.provider === 'github') {
refresh();
}
};
window.addEventListener('apiKeyUpdated', handleAuthUpdate);
return () => window.removeEventListener('apiKeyUpdated', handleAuthUpdate);
}, [refresh]);
const handleDisconnect = () => {
setIsLoading(true);
try {
configManager.clearGitHubAuth();
toast.success('Disconnected from GitHub');
track('connection_removed', { provider: 'github' });
refresh();
} catch {
toast.error('Failed to disconnect');
} finally {
setIsLoading(false);
}
};
return (
<div className="flex flex-col gap-4">
<div className="flex items-start justify-between gap-3">
<div>
<h3 className="text-sm font-semibold text-foreground">GitHub</h3>
<p className="text-xs text-muted-foreground mt-1">
Connected GitHub account for project export.
</p>
</div>
{!isConnected && (
<Button variant="default" size="sm" className="gap-1.5 shrink-0" onClick={() => { setDrawerMode('connect'); setDrawerOpen(true); }}>
<Plus className="h-3.5 w-3.5" />
Connect GitHub
</Button>
)}
</div>
{!isConnected ? (
<p className="text-sm text-muted-foreground py-1 pl-1">None yet.</p>
) : (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-3">
<div className="flex items-center gap-3 bg-card border border-border rounded-md px-4 py-3">
<div className="w-[36px] h-[36px] rounded-md bg-secondary border border-border flex items-center justify-center flex-shrink-0 text-xs font-semibold text-muted-foreground">
GH
</div>
<div className="flex-1 min-w-0">
<div className="font-semibold text-sm">GitHub</div>
<div className="text-xs text-muted-foreground font-mono mt-0.5 truncate">
{auth.username || 'Connected'}
</div>
</div>
<div className="flex items-center gap-1.5 flex-shrink-0">
<div className="h-1.5 w-1.5 rounded-full bg-green-500 shadow-[0_0_6px_rgba(34,197,94,0.5)]" />
<span className="text-xs font-semibold text-green-500 mr-1">Connected</span>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="icon" variant="ghost" className="size-7" title="Connection options" disabled={isLoading}>
{isLoading ? <Loader2 className="h-4 w-4 animate-spin" /> : <MoreVertical className="h-4 w-4" />}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onSelect={() => { setDrawerMode('edit'); setDrawerOpen(true); }}>
Edit Connection
</DropdownMenuItem>
<DropdownMenuItem onSelect={handleDisconnect} className="text-destructive focus:text-destructive">
<Unplug className="h-4 w-4" />
Disconnect
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
</div>
)}
<Drawer
open={drawerOpen}
mode={drawerMode === 'connect' ? 'connect-config' : 'edit-config'}
label={drawerMode === 'connect' ? 'Connect GitHub' : 'GitHub Connection'}
title="GitHub"
scope={isConnected ? `Authenticated as ${auth?.username}` : 'Connect your account'}
onClose={() => setDrawerOpen(false)}
>
<div className="px-[18px] py-4">
<GitHubAuthPanel onAuthChange={() => { refresh(); setDrawerOpen(false); }} />
</div>
</Drawer>
</div>
);
}