diff --git a/app/components/@settings/tabs/connections/ConnectionsTab.tsx b/app/components/@settings/tabs/connections/ConnectionsTab.tsx index 663e5465..4e582eea 100644 --- a/app/components/@settings/tabs/connections/ConnectionsTab.tsx +++ b/app/components/@settings/tabs/connections/ConnectionsTab.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect } from 'react'; import { logStore } from '~/lib/stores/logs'; -import { classNames } from '~/utils/classNames'; import { motion } from 'framer-motion'; import { toast } from 'react-toastify'; import { GithubConnection } from './GithubConnection'; @@ -74,8 +73,6 @@ export default function ConnectionsTab() { tokenType: 'classic', }); const [isLoading, setIsLoading] = useState(true); - const [isConnecting, setIsConnecting] = useState(false); - const [isFetchingStats, setIsFetchingStats] = useState(false); // Load saved connection on mount useEffect(() => { @@ -101,8 +98,6 @@ export default function ConnectionsTab() { const fetchGitHubStats = async (token: string) => { try { - setIsFetchingStats(true); - // Fetch repositories - only owned by the authenticated user const reposResponse = await fetch( 'https://api.github.com/user/repos?sort=updated&per_page=10&affiliation=owner,organization_member,collaborator', @@ -184,59 +179,9 @@ export default function ConnectionsTab() { logStore.logError('Failed to fetch GitHub stats', { error }); toast.error('Failed to fetch GitHub statistics'); } finally { - setIsFetchingStats(false); } }; - const fetchGithubUser = async (token: string) => { - try { - setIsConnecting(true); - - const response = await fetch('https://api.github.com/user', { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - - if (!response.ok) { - throw new Error('Invalid token or unauthorized'); - } - - const data = (await response.json()) as GitHubUserResponse; - const newConnection: GitHubConnection = { - user: data, - token, - tokenType: connection.tokenType, - }; - - // Save connection - localStorage.setItem('github_connection', JSON.stringify(newConnection)); - setConnection(newConnection); - - // Fetch additional stats - await fetchGitHubStats(token); - - toast.success('Successfully connected to GitHub'); - } catch (error) { - logStore.logError('Failed to authenticate with GitHub', { error }); - toast.error('Failed to connect to GitHub'); - setConnection({ user: null, token: '', tokenType: 'classic' }); - } finally { - setIsConnecting(false); - } - }; - - const handleConnect = async (event: React.FormEvent) => { - event.preventDefault(); - await fetchGithubUser(connection.token); - }; - - const handleDisconnect = () => { - localStorage.removeItem('github_connection'); - setConnection({ user: null, token: '', tokenType: 'classic' }); - toast.success('Disconnected from GitHub'); - }; - if (isLoading) { return ; } @@ -259,9 +204,9 @@ export default function ConnectionsTab() {
{/* GitHub Connection */} - + {/* Netlify Connection */} - +
); diff --git a/app/components/@settings/tabs/connections/GithubConnection.tsx b/app/components/@settings/tabs/connections/GithubConnection.tsx index 8c1e9b8a..c9ee632b 100644 --- a/app/components/@settings/tabs/connections/GithubConnection.tsx +++ b/app/components/@settings/tabs/connections/GithubConnection.tsx @@ -553,4 +553,3 @@ export function GithubConnection() { ); } - \ No newline at end of file diff --git a/app/components/@settings/tabs/connections/NetlifyConnection.tsx b/app/components/@settings/tabs/connections/NetlifyConnection.tsx index 53c085d0..bdd08285 100644 --- a/app/components/@settings/tabs/connections/NetlifyConnection.tsx +++ b/app/components/@settings/tabs/connections/NetlifyConnection.tsx @@ -28,7 +28,7 @@ export function NetlifyConnection() { const sitesResponse = await fetch('https://api.netlify.com/api/v1/sites', { headers: { - 'Authorization': `Bearer ${token}`, + Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, }); @@ -37,8 +37,8 @@ export function NetlifyConnection() { throw new Error(`Failed to fetch sites: ${sitesResponse.status}`); } - const sites = await sitesResponse.json() as NetlifySite[]; - + const sites = (await sitesResponse.json()) as NetlifySite[]; + const currentState = netlifyConnection.get(); updateNetlifyConnection({ ...currentState, @@ -63,7 +63,7 @@ export function NetlifyConnection() { try { const response = await fetch('https://api.netlify.com/api/v1/user', { headers: { - 'Authorization': `Bearer ${connection.token}`, + Authorization: `Bearer ${connection.token}`, 'Content-Type': 'application/json', }, }); @@ -72,12 +72,12 @@ export function NetlifyConnection() { throw new Error('Invalid token or unauthorized'); } - const userData = await response.json() as NetlifyUser; + const userData = (await response.json()) as NetlifyUser; updateNetlifyConnection({ user: userData, token: connection.token, }); - + await fetchNetlifyStats(connection.token); toast.success('Successfully connected to Netlify'); } catch (error) { @@ -105,7 +105,13 @@ export function NetlifyConnection() {
- +

Netlify Connection

@@ -113,9 +119,7 @@ export function NetlifyConnection() { {!connection.user ? (
- +
- {connection.user.full_name}

{connection.user.full_name}

@@ -231,7 +235,12 @@ export function NetlifyConnection() { {site.name}
- + {site.url} {site.published_deploy && ( @@ -270,4 +279,4 @@ export function NetlifyConnection() {
); -} \ No newline at end of file +} diff --git a/app/components/header/HeaderActionButtons.client.tsx b/app/components/header/HeaderActionButtons.client.tsx index 4179c248..8539460a 100644 --- a/app/components/header/HeaderActionButtons.client.tsx +++ b/app/components/header/HeaderActionButtons.client.tsx @@ -16,7 +16,7 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) { const showWorkbench = useStore(workbenchStore.showWorkbench); const { showChat } = useStore(chatStore); const connection = useStore(netlifyConnection); - const [activePreviewIndex, setActivePreviewIndex] = useState(0); + const [activePreviewIndex] = useState(0); const previews = useStore(workbenchStore.previews); const activePreview = previews[activePreviewIndex]; const [isDeploying, setIsDeploying] = useState(false); @@ -31,26 +31,27 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) { try { setIsDeploying(true); + const artifact = workbenchStore.firstArtifact; - + if (!artifact) { throw new Error('No active project found'); } const actionId = 'build-' + Date.now(); const actionData: ActionCallbackData = { - messageId: "netlify build", + messageId: 'netlify build', artifactId: artifact.id, actionId, action: { type: 'build' as const, content: 'npm run build', - } + }, }; // Add the action first artifact.runner.addAction(actionData); - + // Then run it await artifact.runner.runAction(actionData); @@ -60,17 +61,21 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) { // Get the build files const container = await webcontainer; + // Remove /home/project from buildPath if it exists const buildPath = artifact.runner.buildOutput.path.replace('/home/project', ''); + // Get all files recursively async function getAllFiles(dirPath: string): Promise> { const files: Record = {}; const entries = await container.fs.readdir(dirPath, { withFileTypes: true }); - + for (const entry of entries) { const fullPath = path.join(dirPath, entry.name); + if (entry.isFile()) { const content = await container.fs.readFile(fullPath, 'utf-8'); + // Remove /dist prefix from the path const deployPath = fullPath.replace(buildPath, ''); files[deployPath] = content; @@ -79,7 +84,7 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) { Object.assign(files, subFiles); } } - + return files; } @@ -96,12 +101,12 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) { siteId: existingSiteId || undefined, files: fileContents, token: connection.token, - chatId: artifact.id + chatId: artifact.id, }), }); - const data = await response.json() as any; - + const data = (await response.json()) as any; + if (!response.ok || !data.deploy || !data.site) { console.error('Invalid deploy response:', data); throw new Error(data.error || 'Invalid deployment response'); @@ -114,35 +119,38 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) { while (attempts < maxAttempts) { try { - const statusResponse = await fetch(`https://api.netlify.com/api/v1/sites/${data.site.id}/deploys/${data.deploy.id}`, { - headers: { - 'Authorization': `Bearer ${connection.token}`, + const statusResponse = await fetch( + `https://api.netlify.com/api/v1/sites/${data.site.id}/deploys/${data.deploy.id}`, + { + headers: { + Authorization: `Bearer ${connection.token}`, + }, }, - }); - - deploymentStatus = await statusResponse.json() as any; - + ); + + deploymentStatus = (await statusResponse.json()) as any; + if (deploymentStatus.state === 'ready' || deploymentStatus.state === 'uploaded') { break; } - + if (deploymentStatus.state === 'error') { throw new Error('Deployment failed: ' + (deploymentStatus.error_message || 'Unknown error')); } - + attempts++; - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 1000)); } catch (error) { console.error('Status check error:', error); attempts++; - await new Promise(resolve => setTimeout(resolve, 2000)); + await new Promise((resolve) => setTimeout(resolve, 2000)); } } if (attempts >= maxAttempts) { throw new Error('Deployment timed out'); } - + // Store the site ID if it's a new site if (data.site) { localStorage.setItem(`netlify-site-${artifact.id}`, data.site.id); @@ -151,15 +159,15 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) { toast.success(
Deployed successfully!{' '} - View site -
+
, ); } catch (error) { console.error('Deploy error:', error); @@ -172,11 +180,11 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) { return (
- @@ -222,15 +230,17 @@ interface ButtonProps { function Button({ active = false, disabled = false, children, onClick, className }: ButtonProps) { return (