mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-05-07 13:44:40 +00:00
add: various improvements to connections
- improved organisation of connections (collapsibles) - improved deploy button - improved unique chat deployments
This commit is contained in:
parent
96a0b2a066
commit
19137c934b
@ -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>
|
||||||
|
@ -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">
|
||||||
|
@ -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">
|
||||||
|
@ -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>
|
||||||
)}
|
)}
|
||||||
|
@ -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');
|
||||||
|
Loading…
Reference in New Issue
Block a user