115 lines
3.6 KiB
TypeScript
115 lines
3.6 KiB
TypeScript
'use client';
|
|
|
|
import { useAction, useQuery } from 'convex/react';
|
|
import { makeFunctionReference } from 'convex/server';
|
|
import { Github, RefreshCw } from 'lucide-react';
|
|
import { toast } from 'sonner';
|
|
|
|
import { api } from '@spoon/backend/convex/_generated/api.js';
|
|
import { Button, Card, CardContent, CardHeader, CardTitle } from '@spoon/ui';
|
|
|
|
const listReposRef = makeFunctionReference<
|
|
'action',
|
|
Record<string, never>,
|
|
{
|
|
id: number;
|
|
name: string;
|
|
fullName: string;
|
|
owner: string;
|
|
private: boolean;
|
|
fork: boolean;
|
|
url: string;
|
|
defaultBranch: string;
|
|
description?: string;
|
|
}[]
|
|
>('githubNode:listInstallationRepositories');
|
|
|
|
export const GithubIntegrationPanel = () => {
|
|
const connection = useQuery(api.github.getConnection, {});
|
|
const installUrl = useQuery(api.github.getInstallUrl, {});
|
|
const listRepos = useAction(listReposRef);
|
|
|
|
const refresh = async () => {
|
|
try {
|
|
const repos = await listRepos({});
|
|
toast.success(`GitHub can access ${repos.length} repositories.`);
|
|
} catch (error) {
|
|
console.error(error);
|
|
toast.error('Could not list GitHub repositories.');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Card className='shadow-none'>
|
|
<CardHeader>
|
|
<CardTitle className='flex items-center gap-2 text-base'>
|
|
<Github className='size-4' />
|
|
GitHub App
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className='space-y-4'>
|
|
{connection && connection.status !== 'active' ? (
|
|
<div
|
|
className={
|
|
connection.status === 'revoked'
|
|
? 'rounded-md border border-red-500/40 bg-red-500/10 p-3 text-sm text-red-600'
|
|
: 'rounded-md border border-amber-500/40 bg-amber-500/10 p-3 text-sm text-amber-600'
|
|
}
|
|
>
|
|
<p className='font-medium'>
|
|
{connection.status === 'revoked'
|
|
? 'GitHub App installation removed'
|
|
: 'GitHub access needs re-authorization'}
|
|
</p>
|
|
<p className='mt-1'>
|
|
{connection.status === 'revoked'
|
|
? 'Reinstall the app to resume syncing.'
|
|
: 'Reconnect the app to resume syncing.'}
|
|
</p>
|
|
{installUrl ? (
|
|
<a
|
|
className='mt-2 inline-block underline'
|
|
href={installUrl}
|
|
target='_blank'
|
|
rel='noreferrer'
|
|
>
|
|
Reconnect GitHub App
|
|
</a>
|
|
) : null}
|
|
</div>
|
|
) : null}
|
|
{connection ? (
|
|
<div className='grid gap-2 text-sm'>
|
|
<div>
|
|
<p className='text-muted-foreground'>Connected account</p>
|
|
<p className='font-medium'>{connection.displayName}</p>
|
|
</div>
|
|
<div>
|
|
<p className='text-muted-foreground'>Installation ID</p>
|
|
<p className='font-mono text-xs'>{connection.installationId}</p>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<p className='text-muted-foreground text-sm'>
|
|
Install the GitHub App to let Spoon create forks and refresh
|
|
repository state.
|
|
</p>
|
|
)}
|
|
<div className='flex flex-wrap gap-2'>
|
|
{installUrl ? (
|
|
<Button asChild>
|
|
<a href={installUrl} target='_blank' rel='noreferrer'>
|
|
Configure GitHub App
|
|
</a>
|
|
</Button>
|
|
) : null}
|
|
<Button variant='outline' onClick={refresh} disabled={!connection}>
|
|
<RefreshCw className='size-4' />
|
|
Check repository access
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
};
|