add: various improvements to connections

- improved organisation of connections (collapsibles)
- improved deploy button
- improved unique chat deployments
This commit is contained in:
KevIsDev 2025-02-25 00:41:44 +00:00
parent 96a0b2a066
commit 19137c934b
5 changed files with 95 additions and 154 deletions

View File

@ -1,73 +1,8 @@
import React, { useState, useEffect } from 'react';
import { logStore } from '~/lib/stores/logs';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { toast } from 'react-toastify';
import { GithubConnection } from './GithubConnection'; import { GithubConnection } from './GithubConnection';
import { NetlifyConnection } from './NetlifyConnection'; import { NetlifyConnection } from './NetlifyConnection';
interface GitHubUserResponse {
login: string;
avatar_url: string;
html_url: string;
name: string;
bio: string;
public_repos: number;
followers: number;
following: number;
created_at: string;
public_gists: number;
}
interface GitHubRepoInfo {
name: string;
full_name: string;
html_url: string;
description: string;
stargazers_count: number;
forks_count: number;
default_branch: string;
updated_at: string;
languages_url: string;
}
interface GitHubOrganization {
login: string;
avatar_url: string;
html_url: string;
}
interface GitHubEvent {
id: string;
type: string;
repo: {
name: string;
};
created_at: string;
}
interface GitHubLanguageStats {
[language: string]: number;
}
interface GitHubStats {
repos: GitHubRepoInfo[];
totalStars: number;
totalForks: number;
organizations: GitHubOrganization[];
recentActivity: GitHubEvent[];
languages: GitHubLanguageStats;
totalGists: number;
}
interface GitHubConnection {
user: GitHubUserResponse | null;
token: string;
tokenType: 'classic' | 'fine-grained';
stats?: GitHubStats;
}
export default function ConnectionsTab() { export default function ConnectionsTab() {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
{/* Header */} {/* Header */}
@ -85,9 +20,7 @@ export default function ConnectionsTab() {
</p> </p>
<div className="grid grid-cols-1 gap-4"> <div className="grid grid-cols-1 gap-4">
{/* GitHub Connection */}
<GithubConnection /> <GithubConnection />
{/* Netlify Connection */}
<NetlifyConnection /> <NetlifyConnection />
</div> </div>
</div> </div>

View File

@ -73,7 +73,6 @@ export function GithubConnection() {
}); });
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [isConnecting, setIsConnecting] = useState(false); const [isConnecting, setIsConnecting] = useState(false);
const [isFetchingStats, setIsFetchingStats] = useState(false);
const [expandedSections, setExpandedSections] = useState({ const [expandedSections, setExpandedSections] = useState({
organizations: false, organizations: false,
languages: false, languages: false,
@ -82,16 +81,14 @@ export function GithubConnection() {
}); });
const toggleSection = (section: keyof typeof expandedSections) => { const toggleSection = (section: keyof typeof expandedSections) => {
setExpandedSections(prev => ({ setExpandedSections((prev) => ({
...prev, ...prev,
[section]: !prev[section] [section]: !prev[section],
})); }));
}; };
const fetchGitHubStats = async (token: string) => { const fetchGitHubStats = async (token: string) => {
try { try {
setIsFetchingStats(true);
const reposResponse = await fetch( const reposResponse = await fetch(
'https://api.github.com/user/repos?sort=updated&per_page=10&affiliation=owner,organization_member,collaborator', 'https://api.github.com/user/repos?sort=updated&per_page=10&affiliation=owner,organization_member,collaborator',
{ {
@ -168,7 +165,6 @@ export function GithubConnection() {
logStore.logError('Failed to fetch GitHub stats', { error }); logStore.logError('Failed to fetch GitHub stats', { error });
toast.error('Failed to fetch GitHub statistics'); toast.error('Failed to fetch GitHub statistics');
} finally { } finally {
setIsFetchingStats(false);
} }
}; };
@ -188,6 +184,7 @@ export function GithubConnection() {
fetchGitHubStats(parsed.token); fetchGitHubStats(parsed.token);
} }
} }
setIsLoading(false); setIsLoading(false);
}, []); }, []);
@ -407,10 +404,12 @@ export function GithubConnection() {
> >
<div className="i-ph:buildings w-4 h-4" /> <div className="i-ph:buildings w-4 h-4" />
Organizations ({connection.stats.organizations.length}) Organizations ({connection.stats.organizations.length})
<div className={classNames( <div
"i-ph:caret-down w-4 h-4 ml-auto transition-transform", className={classNames(
expandedSections.organizations ? "rotate-180" : "" 'i-ph:caret-down w-4 h-4 ml-auto transition-transform',
)} /> expandedSections.organizations ? 'rotate-180' : '',
)}
/>
</button> </button>
{expandedSections.organizations && ( {expandedSections.organizations && (
<div className="flex flex-wrap gap-3 pb-4"> <div className="flex flex-wrap gap-3 pb-4">
@ -439,10 +438,12 @@ export function GithubConnection() {
> >
<div className="i-ph:code w-4 h-4" /> <div className="i-ph:code w-4 h-4" />
Top Languages ({Object.keys(connection.stats.languages).length}) Top Languages ({Object.keys(connection.stats.languages).length})
<div className={classNames( <div
"i-ph:caret-down w-4 h-4 ml-auto transition-transform", className={classNames(
expandedSections.languages ? "rotate-180" : "" 'i-ph:caret-down w-4 h-4 ml-auto transition-transform',
)} /> expandedSections.languages ? 'rotate-180' : '',
)}
/>
</button> </button>
{expandedSections.languages && ( {expandedSections.languages && (
<div className="flex flex-wrap gap-2 pb-4"> <div className="flex flex-wrap gap-2 pb-4">
@ -469,10 +470,12 @@ export function GithubConnection() {
> >
<div className="i-ph:activity w-4 h-4" /> <div className="i-ph:activity w-4 h-4" />
Recent Activity ({connection.stats.recentActivity.length}) Recent Activity ({connection.stats.recentActivity.length})
<div className={classNames( <div
"i-ph:caret-down w-4 h-4 ml-auto transition-transform", className={classNames(
expandedSections.recentActivity ? "rotate-180" : "" 'i-ph:caret-down w-4 h-4 ml-auto transition-transform',
)} /> expandedSections.recentActivity ? 'rotate-180' : '',
)}
/>
</button> </button>
{expandedSections.recentActivity && ( {expandedSections.recentActivity && (
<div className="space-y-3 pb-4"> <div className="space-y-3 pb-4">
@ -509,10 +512,12 @@ export function GithubConnection() {
> >
<div className="i-ph:clock-counter-clockwise w-4 h-4" /> <div className="i-ph:clock-counter-clockwise w-4 h-4" />
Recent Repositories ({connection.stats.repos.length}) Recent Repositories ({connection.stats.repos.length})
<div className={classNames( <div
"i-ph:caret-down w-4 h-4 ml-auto transition-transform", className={classNames(
expandedSections.repositories ? "rotate-180" : "" 'i-ph:caret-down w-4 h-4 ml-auto transition-transform',
)} /> expandedSections.repositories ? 'rotate-180' : '',
)}
/>
</button> </button>
{expandedSections.repositories && ( {expandedSections.repositories && (
<div className="space-y-3"> <div className="space-y-3">

View File

@ -220,10 +220,12 @@ export function NetlifyConnection() {
> >
<div className="i-ph:buildings w-4 h-4" /> <div className="i-ph:buildings w-4 h-4" />
Your Sites ({connection.stats?.totalSites || 0}) Your Sites ({connection.stats?.totalSites || 0})
<div className={classNames( <div
"i-ph:caret-down w-4 h-4 ml-auto transition-transform", className={classNames(
isSitesExpanded ? "rotate-180" : "" 'i-ph:caret-down w-4 h-4 ml-auto transition-transform',
)} /> isSitesExpanded ? 'rotate-180' : '',
)}
/>
</button> </button>
{isSitesExpanded && connection.stats?.sites?.length ? ( {isSitesExpanded && connection.stats?.sites?.length ? (
<div className="grid gap-3"> <div className="grid gap-3">

View File

@ -33,6 +33,7 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) {
} }
} }
document.addEventListener('mousedown', handleClickOutside); document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside);
}, []); }, []);
@ -109,6 +110,7 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) {
} }
const fileContents = await getAllFiles(buildPath); const fileContents = await getAllFiles(buildPath);
// Use chatId instead of artifact.id // Use chatId instead of artifact.id
const existingSiteId = localStorage.getItem(`netlify-site-${currentChatId}`); const existingSiteId = localStorage.getItem(`netlify-site-${currentChatId}`);
@ -209,10 +211,9 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) {
className="px-4 hover:bg-bolt-elements-item-backgroundActive flex items-center gap-2" className="px-4 hover:bg-bolt-elements-item-backgroundActive flex items-center gap-2"
> >
{isDeploying ? 'Deploying...' : 'Deploy'} {isDeploying ? 'Deploying...' : 'Deploy'}
<div className={classNames( <div
"i-ph:caret-down w-4 h-4 transition-transform", className={classNames('i-ph:caret-down w-4 h-4 transition-transform', isDropdownOpen ? 'rotate-180' : '')}
isDropdownOpen ? "rotate-180" : "" />
)} />
</Button> </Button>
</div> </div>
@ -234,39 +235,39 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) {
crossOrigin="anonymous" crossOrigin="anonymous"
src="https://cdn.simpleicons.org/netlify" src="https://cdn.simpleicons.org/netlify"
/> />
<span className='mx-auto'>Deploy to Netlify</span> <span className="mx-auto">Deploy to Netlify</span>
</Button> </Button>
<Button <Button
active={false} active={false}
disabled disabled
className="flex items-center w-full rounded-md px-4 py-2 text-sm text-bolt-elements-textTertiary gap-2" className="flex items-center w-full rounded-md px-4 py-2 text-sm text-bolt-elements-textTertiary gap-2"
> >
<span className='sr-only'>Coming Soon</span> <span className="sr-only">Coming Soon</span>
<img <img
className="w-5 h-5 bg-black p-1 rounded" className="w-5 h-5 bg-black p-1 rounded"
height="24" height="24"
width="24" width="24"
crossOrigin="anonymous" crossOrigin="anonymous"
src="https://cdn.simpleicons.org/vercel/white" src="https://cdn.simpleicons.org/vercel/white"
alt='vercel' alt="vercel"
/> />
<span className='mx-auto'>Deploy to Vercel (Coming Soon)</span> <span className="mx-auto">Deploy to Vercel (Coming Soon)</span>
</Button> </Button>
<Button <Button
active={false} active={false}
disabled disabled
className="flex items-center w-full rounded-md px-4 py-2 text-sm text-bolt-elements-textTertiary gap-2" className="flex items-center w-full rounded-md px-4 py-2 text-sm text-bolt-elements-textTertiary gap-2"
> >
<span className='sr-only'>Coming Soon</span> <span className="sr-only">Coming Soon</span>
<img <img
className="w-5 h-5" className="w-5 h-5"
height="24" height="24"
width="24" width="24"
crossOrigin="anonymous" crossOrigin="anonymous"
src="https://cdn.simpleicons.org/cloudflare" src="https://cdn.simpleicons.org/cloudflare"
alt='vercel' alt="vercel"
/> />
<span className='mx-auto'>Deploy to Cloudflare (Coming Soon)</span> <span className="mx-auto">Deploy to Cloudflare (Coming Soon)</span>
</Button> </Button>
</div> </div>
)} )}

View File

@ -5,7 +5,7 @@ import type { ChatHistoryItem } from './useChatHistory';
export interface IChatMetadata { export interface IChatMetadata {
gitUrl: string; gitUrl: string;
gitBranch?: string; gitBranch?: string;
netlifySiteId?: string; // Add this field netlifySiteId?: string;
} }
const logger = createScopedLogger('ChatHistory'); const logger = createScopedLogger('ChatHistory');