mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 18:26:38 +00:00
ui bugfixes
This commit is contained in:
parent
c182eaefa1
commit
d4b9e19f9c
@ -85,12 +85,14 @@ export function GitHubAuthDialog({ isOpen, onClose }: GitHubAuthDialogProps) {
|
||||
>
|
||||
<Dialog.Content className="bg-white dark:bg-[#1A1A1A] rounded-lg shadow-xl max-w-sm w-full mx-4 overflow-hidden">
|
||||
<div className="p-4 space-y-3">
|
||||
<h2 className="text-lg font-semibold text-[#111111] dark:text-white">Access Private Repositories</h2>
|
||||
<Dialog.Title className="text-lg font-semibold text-[#111111] dark:text-white">
|
||||
Access Private Repositories
|
||||
</Dialog.Title>
|
||||
|
||||
<p className="text-sm text-[#666666] dark:text-[#999999]">
|
||||
<Dialog.Description className="text-sm text-[#666666] dark:text-[#999999]">
|
||||
To access private repositories, you need to connect your GitHub account by providing a personal access
|
||||
token.
|
||||
</p>
|
||||
</Dialog.Description>
|
||||
|
||||
<div className="bg-[#F9F9F9] dark:bg-[#252525] p-4 rounded-lg space-y-3">
|
||||
<h3 className="text-base font-medium text-[#111111] dark:text-white">Connect with GitHub Token</h3>
|
||||
|
@ -303,15 +303,12 @@ export function PushToGitHubDialog({ isOpen, onClose, onPush }: PushToGitHubDial
|
||||
<div className="i-ph:check-circle w-5 h-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-medium text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary-dark">
|
||||
<Dialog.Title className="text-lg font-medium text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary-dark">
|
||||
Successfully pushed to GitHub
|
||||
</h3>
|
||||
<p
|
||||
id="success-dialog-description"
|
||||
className="text-sm text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary-dark"
|
||||
>
|
||||
</Dialog.Title>
|
||||
<Dialog.Description className="text-sm text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary-dark">
|
||||
Your code is now available on GitHub
|
||||
</p>
|
||||
</Dialog.Description>
|
||||
</div>
|
||||
</div>
|
||||
<Dialog.Close asChild>
|
||||
@ -513,12 +510,9 @@ export function PushToGitHubDialog({ isOpen, onClose, onPush }: PushToGitHubDial
|
||||
<Dialog.Title className="text-lg font-medium text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary-dark">
|
||||
Push to GitHub
|
||||
</Dialog.Title>
|
||||
<p
|
||||
id="push-dialog-description"
|
||||
className="text-sm text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary-dark"
|
||||
>
|
||||
<Dialog.Description className="text-sm text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary-dark">
|
||||
Push your code to a new or existing GitHub repository
|
||||
</p>
|
||||
</Dialog.Description>
|
||||
</div>
|
||||
<Dialog.Close asChild>
|
||||
<button
|
||||
|
@ -269,7 +269,9 @@ export function PushToGitLabDialog({ isOpen, onClose, onPush }: PushToGitLabDial
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2 text-green-500">
|
||||
<div className="i-ph:check-circle w-5 h-5" />
|
||||
<h3 className="text-lg font-medium">Successfully pushed to GitLab</h3>
|
||||
<Dialog.Title className="text-lg font-medium text-green-700 dark:text-green-400">
|
||||
Successfully pushed to GitLab
|
||||
</Dialog.Title>
|
||||
</div>
|
||||
<Dialog.Close
|
||||
onClick={handleClose}
|
||||
@ -286,9 +288,9 @@ export function PushToGitLabDialog({ isOpen, onClose, onPush }: PushToGitLabDial
|
||||
</div>
|
||||
|
||||
<div className="bg-bolt-elements-background-depth-2 dark:bg-bolt-elements-background-depth-3 rounded-lg p-3 text-left">
|
||||
<p className="text-xs text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary-dark mb-2">
|
||||
<Dialog.Description className="text-xs text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary-dark mb-2">
|
||||
Project URL
|
||||
</p>
|
||||
</Dialog.Description>
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="flex-1 text-sm bg-bolt-elements-background dark:bg-bolt-elements-background-dark px-3 py-2 rounded border border-bolt-elements-borderColor dark:border-bolt-elements-borderColor-dark text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary-dark font-mono">
|
||||
{createdProjectUrl}
|
||||
@ -308,9 +310,9 @@ export function PushToGitLabDialog({ isOpen, onClose, onPush }: PushToGitLabDial
|
||||
</div>
|
||||
|
||||
<div className="bg-bolt-elements-background-depth-2 dark:bg-bolt-elements-background-depth-3 rounded-lg p-3">
|
||||
<p className="text-xs text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary-dark mb-2">
|
||||
<Dialog.Description className="text-xs text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary-dark mb-2">
|
||||
Pushed Files ({pushedFiles.length})
|
||||
</p>
|
||||
</Dialog.Description>
|
||||
<div className="max-h-[200px] overflow-y-auto custom-scrollbar">
|
||||
{pushedFiles.map((file) => (
|
||||
<div
|
||||
@ -391,10 +393,12 @@ export function PushToGitLabDialog({ isOpen, onClose, onPush }: PushToGitLabDial
|
||||
>
|
||||
<div className="i-ph:gitlab-logo w-6 h-6" />
|
||||
</motion.div>
|
||||
<h3 className="text-lg font-medium text-gray-900 dark:text-white">GitLab Connection Required</h3>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">
|
||||
GitLab Connection Required
|
||||
</Dialog.Title>
|
||||
<Dialog.Description className="text-sm text-gray-600 dark:text-gray-400">
|
||||
Please connect your GitLab account in Settings {'>'} Connections to push your code to GitLab.
|
||||
</p>
|
||||
</Dialog.Description>
|
||||
<motion.button
|
||||
className="px-4 py-2 rounded-lg bg-orange-600 text-white text-sm hover:bg-orange-700 inline-flex items-center gap-2"
|
||||
whileHover={{ scale: 1.02 }}
|
||||
@ -440,9 +444,9 @@ export function PushToGitLabDialog({ isOpen, onClose, onPush }: PushToGitLabDial
|
||||
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">
|
||||
Push to GitLab
|
||||
</Dialog.Title>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
<Dialog.Description className="text-sm text-gray-600 dark:text-gray-400">
|
||||
Push your code to a new or existing GitLab project
|
||||
</p>
|
||||
</Dialog.Description>
|
||||
</div>
|
||||
<Dialog.Close asChild>
|
||||
<button
|
||||
|
@ -4,12 +4,11 @@ import { toast } from 'react-toastify';
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import { classNames } from '~/utils/classNames';
|
||||
import { getLocalStorage } from '~/lib/persistence';
|
||||
import { motion } from 'framer-motion';
|
||||
import { formatSize } from '~/utils/formatSize';
|
||||
import { Input } from '~/components/ui/Input';
|
||||
import Cookies from 'js-cookie';
|
||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||
import { RepositoryDialogContext } from './RepositoryDialogContext';
|
||||
import { StatsDialog } from './StatsDialog';
|
||||
import { GitHubAuthDialog } from './GitHubAuthDialog';
|
||||
|
||||
interface RepositorySelectionDialogProps {
|
||||
isOpen: boolean;
|
||||
@ -23,450 +22,6 @@ interface SearchFilters {
|
||||
forks?: number;
|
||||
}
|
||||
|
||||
interface StatsDialogProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onConfirm: () => void;
|
||||
stats: RepositoryStats;
|
||||
isLargeRepo?: boolean;
|
||||
}
|
||||
|
||||
function StatsDialog({ isOpen, onClose, onConfirm, stats, isLargeRepo }: StatsDialogProps) {
|
||||
return (
|
||||
<Dialog.Root open={isOpen} onOpenChange={(open) => !open && onClose()}>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="fixed inset-0 bg-black/50 backdrop-blur-sm z-[9999]" />
|
||||
<div className="fixed inset-0 flex items-center justify-center z-[9999]">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.95 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="w-[90vw] md:w-[500px]"
|
||||
>
|
||||
<Dialog.Content className="bg-white dark:bg-[#1E1E1E] rounded-lg border border-[#E5E5E5] dark:border-[#333333] shadow-xl">
|
||||
<div className="p-6 space-y-4">
|
||||
<div>
|
||||
<h3 className="text-lg font-medium text-[#111111] dark:text-white">Repository Overview</h3>
|
||||
<div className="mt-4 space-y-2">
|
||||
<p className="text-sm text-[#666666] dark:text-[#999999]">Repository Statistics:</p>
|
||||
<div className="space-y-2 text-sm text-[#111111] dark:text-white">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="i-ph:files text-purple-500 w-4 h-4" />
|
||||
<span>Total Files: {stats.totalFiles}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="i-ph:database text-purple-500 w-4 h-4" />
|
||||
<span>Total Size: {formatSize(stats.totalSize)}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="i-ph:code text-purple-500 w-4 h-4" />
|
||||
<span>
|
||||
Languages:{' '}
|
||||
{Object.entries(stats.languages)
|
||||
.sort(([, a], [, b]) => b - a)
|
||||
.slice(0, 3)
|
||||
.map(([lang, size]) => `${lang} (${formatSize(size)})`)
|
||||
.join(', ')}
|
||||
</span>
|
||||
</div>
|
||||
{stats.hasPackageJson && (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="i-ph:package text-purple-500 w-4 h-4" />
|
||||
<span>Has package.json</span>
|
||||
</div>
|
||||
)}
|
||||
{stats.hasDependencies && (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="i-ph:tree-structure text-purple-500 w-4 h-4" />
|
||||
<span>Has dependencies</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{isLargeRepo && (
|
||||
<div className="mt-4 p-3 bg-yellow-50 dark:bg-yellow-500/10 rounded-lg text-sm flex items-start gap-2">
|
||||
<span className="i-ph:warning text-yellow-600 dark:text-yellow-500 w-4 h-4 flex-shrink-0 mt-0.5" />
|
||||
<div className="text-yellow-800 dark:text-yellow-500">
|
||||
This repository is quite large ({formatSize(stats.totalSize)}). Importing it might take a while
|
||||
and could impact performance.
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-[#E5E5E5] dark:border-[#333333] p-4 flex justify-end gap-3 bg-[#F9F9F9] dark:bg-[#252525] rounded-b-lg">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 rounded-lg bg-[#F5F5F5] dark:bg-[#333333] text-[#666666] hover:text-[#111111] dark:text-[#999999] dark:hover:text-white transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={onConfirm}
|
||||
className="px-4 py-2 rounded-lg bg-purple-500 text-white hover:bg-purple-600 transition-colors"
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</motion.div>
|
||||
</div>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
);
|
||||
}
|
||||
|
||||
function GitlabAuthDialog({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) {
|
||||
const [token, setToken] = useState('');
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [gitlabUrl, setGitlabUrl] = useState('https://gitlab.com');
|
||||
const tokenType = 'personal-access-token';
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!token.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${gitlabUrl}/api/v4/user`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const userData = (await response.json()) as {
|
||||
username: string;
|
||||
avatar_url: string;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
// Save connection data
|
||||
const connectionData = {
|
||||
token,
|
||||
tokenType,
|
||||
user: {
|
||||
login: userData.username,
|
||||
avatar_url: userData.avatar_url,
|
||||
name: userData.name || userData.username,
|
||||
},
|
||||
connected_at: new Date().toISOString(),
|
||||
gitlabUrl,
|
||||
};
|
||||
|
||||
localStorage.setItem('gitlab_connection', JSON.stringify(connectionData));
|
||||
|
||||
// Set cookies for API requests
|
||||
Cookies.set('gitlabToken', token);
|
||||
Cookies.set('gitlabUsername', userData.username);
|
||||
Cookies.set('git:gitlab.com', JSON.stringify({ username: userData.username, password: token }));
|
||||
Cookies.set('gitlabUrl', gitlabUrl);
|
||||
|
||||
toast.success(`Successfully connected as ${userData.username}`);
|
||||
onClose();
|
||||
} else {
|
||||
if (response.status === 401) {
|
||||
toast.error('Invalid GitLab token. Please check and try again.');
|
||||
} else {
|
||||
toast.error(`GitLab API error: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error connecting to GitLab:', error);
|
||||
toast.error('Failed to connect to GitLab. Please try again.');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog.Root open={isOpen} onOpenChange={(open) => !open && onClose()}>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="fixed inset-0 bg-black/50 backdrop-blur-sm z-[9999]" />
|
||||
<div className="fixed inset-0 flex items-center justify-center z-[9999]">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.95 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<Dialog.Content className="bg-white dark:bg-[#1A1A1A] rounded-lg shadow-xl max-w-sm w-full mx-4 overflow-hidden">
|
||||
<div className="p-4 space-y-3">
|
||||
<h2 className="text-lg font-semibold text-[#111111] dark:text-white">Access Private Repositories</h2>
|
||||
|
||||
<p className="text-sm text-[#666666] dark:text-[#999999]">
|
||||
To access private repositories, you need to connect your GitLab account by providing a personal access
|
||||
token.
|
||||
</p>
|
||||
|
||||
<div className="bg-[#F9F9F9] dark:bg-[#252525] p-4 rounded-lg space-y-3">
|
||||
<h3 className="text-base font-medium text-[#111111] dark:text-white">Connect with GitLab Token</h3>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-3">
|
||||
<div>
|
||||
<label className="block text-sm text-[#666666] dark:text-[#999999] mb-1">GitLab URL</label>
|
||||
<input
|
||||
type="text"
|
||||
value={gitlabUrl}
|
||||
onChange={(e) => setGitlabUrl(e.target.value)}
|
||||
placeholder="https://gitlab.com"
|
||||
className="w-full px-3 py-1.5 rounded-lg border border-[#E5E5E5] dark:border-[#333333] bg-white dark:bg-[#1A1A1A] text-[#111111] dark:text-white placeholder-[#999999] text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm text-[#666666] dark:text-[#999999] mb-1">
|
||||
GitLab Personal Access Token
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
value={token}
|
||||
onChange={(e) => setToken(e.target.value)}
|
||||
placeholder="glpat-xxxxxxxxxxxxxxxxxxxx"
|
||||
className="w-full px-3 py-1.5 rounded-lg border border-[#E5E5E5] dark:border-[#333333] bg-white dark:bg-[#1A1A1A] text-[#111111] dark:text-white placeholder-[#999999] text-sm"
|
||||
/>
|
||||
<div className="mt-1 text-xs text-[#666666] dark:text-[#999999]">
|
||||
Get your token at{' '}
|
||||
<a
|
||||
href={`${gitlabUrl}/-/user_settings/personal_access_tokens`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-purple-500 hover:underline"
|
||||
>
|
||||
GitLab Personal Access Tokens
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className="w-full py-2 bg-[#FC6D26] hover:bg-[#E24329] text-white rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm"
|
||||
>
|
||||
{isSubmitting ? 'Connecting...' : 'Connect to GitLab'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="bg-amber-50 dark:bg-amber-900/20 p-3 rounded-lg space-y-1.5">
|
||||
<h3 className="text-sm text-amber-800 dark:text-amber-300 font-medium flex items-center gap-1.5">
|
||||
<span className="i-ph:warning-circle w-4 h-4" />
|
||||
Accessing Private Repositories
|
||||
</h3>
|
||||
<p className="text-xs text-amber-700 dark:text-amber-400">
|
||||
Important things to know about accessing private repositories:
|
||||
</p>
|
||||
<ul className="list-disc pl-4 text-xs text-amber-700 dark:text-amber-400 space-y-0.5">
|
||||
<li>You must be granted access to the repository by its owner</li>
|
||||
<li>Your GitLab token must have the 'api' and 'read_repository' scopes</li>
|
||||
<li>For organization repositories, you may need additional permissions</li>
|
||||
<li>No token can give you access to repositories you don't have permission for</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-[#E5E5E5] dark:border-[#333333] p-3 flex justify-end">
|
||||
<Dialog.Close asChild>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-1.5 bg-[#F5F5F5] hover:bg-[#E5E5E5] dark:bg-[#252525] dark:hover:bg-[#333333] rounded-lg text-[#111111] dark:text-white transition-colors text-sm"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</motion.div>
|
||||
</div>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
);
|
||||
}
|
||||
|
||||
function GitHubAuthDialog({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) {
|
||||
const [token, setToken] = useState('');
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [tokenType, setTokenType] = useState<'classic' | 'fine-grained'>('classic');
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!token.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
const response = await fetch('https://api.github.com/user', {
|
||||
headers: {
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const userData = (await response.json()) as GitHubUserResponse;
|
||||
|
||||
// Save connection data
|
||||
const connectionData = {
|
||||
token,
|
||||
tokenType,
|
||||
user: {
|
||||
login: userData.login,
|
||||
avatar_url: userData.avatar_url,
|
||||
name: userData.name || userData.login,
|
||||
},
|
||||
connected_at: new Date().toISOString(),
|
||||
};
|
||||
|
||||
localStorage.setItem('github_connection', JSON.stringify(connectionData));
|
||||
|
||||
// Set cookies for API requests
|
||||
Cookies.set('githubToken', token);
|
||||
Cookies.set('githubUsername', userData.login);
|
||||
Cookies.set('git:github.com', JSON.stringify({ username: token, password: 'x-oauth-basic' }));
|
||||
|
||||
toast.success(`Successfully connected as ${userData.login}`);
|
||||
onClose();
|
||||
} else {
|
||||
if (response.status === 401) {
|
||||
toast.error('Invalid GitHub token. Please check and try again.');
|
||||
} else {
|
||||
toast.error(`GitHub API error: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error connecting to GitHub:', error);
|
||||
toast.error('Failed to connect to GitHub. Please try again.');
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog.Root open={isOpen} onOpenChange={(open) => !open && onClose()}>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="fixed inset-0 bg-black/50 backdrop-blur-sm z-[9999]" />
|
||||
<div className="fixed inset-0 flex items-center justify-center z-[9999]">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.95 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<Dialog.Content className="bg-white dark:bg-[#1A1A1A] rounded-lg shadow-xl max-w-sm w-full mx-4 overflow-hidden">
|
||||
<div className="p-4 space-y-3">
|
||||
<h2 className="text-lg font-semibold text-[#111111] dark:text-white">Access Private Repositories</h2>
|
||||
|
||||
<p className="text-sm text-[#666666] dark:text-[#999999]">
|
||||
To access private repositories, you need to connect your GitHub account by providing a personal access
|
||||
token.
|
||||
</p>
|
||||
|
||||
<div className="bg-[#F9F9F9] dark:bg-[#252525] p-4 rounded-lg space-y-3">
|
||||
<h3 className="text-base font-medium text-[#111111] dark:text-white">Connect with GitHub Token</h3>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-3">
|
||||
<div>
|
||||
<label className="block text-sm text-[#666666] dark:text-[#999999] mb-1">
|
||||
GitHub Personal Access Token
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
value={token}
|
||||
onChange={(e) => setToken(e.target.value)}
|
||||
placeholder="ghp_xxxxxxxxxxxxxxxxxxxx"
|
||||
className="w-full px-3 py-1.5 rounded-lg border border-[#E5E5E5] dark:border-[#333333] bg-white dark:bg-[#1A1A1A] text-[#111111] dark:text-white placeholder-[#999999] text-sm"
|
||||
/>
|
||||
<div className="mt-1 text-xs text-[#666666] dark:text-[#999999]">
|
||||
Get your token at{' '}
|
||||
<a
|
||||
href="https://github.com/settings/tokens"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-purple-500 hover:underline"
|
||||
>
|
||||
github.com/settings/tokens
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<label className="block text-sm text-[#666666] dark:text-[#999999]">Token Type</label>
|
||||
<div className="flex gap-4">
|
||||
<label className="flex items-center gap-2">
|
||||
<input
|
||||
type="radio"
|
||||
checked={tokenType === 'classic'}
|
||||
onChange={() => setTokenType('classic')}
|
||||
className="w-3.5 h-3.5 accent-purple-500"
|
||||
/>
|
||||
<span className="text-sm text-[#111111] dark:text-white">Classic</span>
|
||||
</label>
|
||||
<label className="flex items-center gap-2">
|
||||
<input
|
||||
type="radio"
|
||||
checked={tokenType === 'fine-grained'}
|
||||
onChange={() => setTokenType('fine-grained')}
|
||||
className="w-3.5 h-3.5 accent-purple-500"
|
||||
/>
|
||||
<span className="text-sm text-[#111111] dark:text-white">Fine-grained</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className="w-full py-2 bg-purple-500 hover:bg-purple-600 text-white rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm"
|
||||
>
|
||||
{isSubmitting ? 'Connecting...' : 'Connect to GitHub'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="bg-amber-50 dark:bg-amber-900/20 p-3 rounded-lg space-y-1.5">
|
||||
<h3 className="text-sm text-amber-800 dark:text-amber-300 font-medium flex items-center gap-1.5">
|
||||
<span className="i-ph:warning-circle w-4 h-4" />
|
||||
Accessing Private Repositories
|
||||
</h3>
|
||||
<p className="text-xs text-amber-700 dark:text-amber-400">
|
||||
Important things to know about accessing private repositories:
|
||||
</p>
|
||||
<ul className="list-disc pl-4 text-xs text-amber-700 dark:text-amber-400 space-y-0.5">
|
||||
<li>You must be granted access to the repository by its owner</li>
|
||||
<li>Your GitHub token must have the 'repo' scope</li>
|
||||
<li>For organization repositories, you may need additional permissions</li>
|
||||
<li>No token can give you access to repositories you don't have permission for</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-[#E5E5E5] dark:border-[#333333] p-3 flex justify-end">
|
||||
<Dialog.Close asChild>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-1.5 bg-[#F5F5F5] hover:bg-[#E5E5E5] dark:bg-[#252525] dark:hover:bg-[#333333] rounded-lg text-[#111111] dark:text-white transition-colors text-sm"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</motion.div>
|
||||
</div>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
);
|
||||
}
|
||||
|
||||
export function RepositorySelectionDialog({ isOpen, onClose, onSelect }: RepositorySelectionDialogProps) {
|
||||
const [selectedRepository, setSelectedRepository] = useState<GitHubRepoInfo | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@ -483,18 +38,6 @@ export function RepositorySelectionDialog({ isOpen, onClose, onSelect }: Reposit
|
||||
const [currentStats, setCurrentStats] = useState<RepositoryStats | null>(null);
|
||||
const [pendingGitUrl, setPendingGitUrl] = useState<string>('');
|
||||
const [showAuthDialog, setShowAuthDialog] = useState(false);
|
||||
const [showAuthGitlabDialog, setShowAuthGitlabDialog] = useState(false);
|
||||
|
||||
// Handle GitHub auth dialog close and refresh repositories
|
||||
const handleAuthDialogClose = (category: 'github' | 'gitlab') => {
|
||||
setShowAuthDialog(false);
|
||||
setShowAuthGitlabDialog(false);
|
||||
|
||||
// If we're on the my-repos tab, refresh the repository list
|
||||
if (activeTab === 'my-repos') {
|
||||
fetchUserRepos(category);
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize GitLab connection and fetch projects
|
||||
useEffect(() => {
|
||||
@ -522,7 +65,7 @@ export function RepositorySelectionDialog({ isOpen, onClose, onSelect }: Reposit
|
||||
const userData = data as {
|
||||
username: string;
|
||||
avatar_url: string;
|
||||
name: string;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
// Save connection to local storage
|
||||
@ -675,7 +218,6 @@ export function RepositorySelectionDialog({ isOpen, onClose, onSelect }: Reposit
|
||||
if (!gitlabConnection?.token) {
|
||||
toast.error('GitLab token not found. Please connect your GitLab account first.');
|
||||
|
||||
setShowAuthGitlabDialog(true);
|
||||
setIsLoading(false);
|
||||
|
||||
return;
|
||||
@ -699,7 +241,6 @@ export function RepositorySelectionDialog({ isOpen, onClose, onSelect }: Reposit
|
||||
console.error('GitLab API error:', response.status, errorText);
|
||||
toast.error(`Failed to fetch GitLab projects: ${response.status} ${response.statusText}`);
|
||||
|
||||
setShowAuthGitlabDialog(true);
|
||||
setIsLoading(false);
|
||||
|
||||
return;
|
||||
@ -1115,7 +656,7 @@ export function RepositorySelectionDialog({ isOpen, onClose, onSelect }: Reposit
|
||||
errorMessage.includes('404') ||
|
||||
errorMessage.includes('access permissions')
|
||||
) {
|
||||
setShowAuthDialog(true);
|
||||
setIsLoading(true);
|
||||
}
|
||||
|
||||
toast.error(errorMessage);
|
||||
@ -1171,16 +712,6 @@ export function RepositorySelectionDialog({ isOpen, onClose, onSelect }: Reposit
|
||||
) {
|
||||
// Directly show the auth dialog instead of just showing a toast
|
||||
setShowAuthDialog(true);
|
||||
|
||||
toast.error(
|
||||
<div className="space-y-2">
|
||||
<p>{errorMessage}</p>
|
||||
<button onClick={() => setShowAuthDialog(true)} className="underline font-medium block text-purple-500">
|
||||
Learn how to access private repositories
|
||||
</button>
|
||||
</div>,
|
||||
{ autoClose: 10000 }, // Keep the toast visible longer
|
||||
);
|
||||
} else {
|
||||
toast.error(errorMessage);
|
||||
}
|
||||
@ -1215,6 +746,14 @@ export function RepositorySelectionDialog({ isOpen, onClose, onSelect }: Reposit
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleAuthDialogClose = (category: 'github' | 'gitlab') => {
|
||||
setShowAuthDialog(false);
|
||||
|
||||
if (activeTab === 'my-repos') {
|
||||
fetchUserRepos(category);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<RepositoryDialogContext.Provider value={{ setShowAuthDialog }}>
|
||||
<Dialog.Root
|
||||
@ -1251,54 +790,6 @@ export function RepositorySelectionDialog({ isOpen, onClose, onSelect }: Reposit
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
|
||||
<div className="p-4 border-b border-[#E5E5E5] dark:border-[#333333] flex items-center justify-between">
|
||||
<div className="ml-auto">
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger className="text-base font-semibold flex items-center gap-2 px-4 py-2 bg-bolt-elements-background-depth-2 dark:bg-bolt-elements-background-depth-3 rounded-lg shadow-sm text-bolt-elements-item-contentDefault enabled:hover:text-bolt-elements-item-contentActive enabled:hover:bg-bolt-elements-item-backgroundActive disabled:cursor-not-allowed">
|
||||
<div className="i-ph:box-arrow-up" />
|
||||
Need to access private repositories (GitHub or GitLab)?
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Content
|
||||
className={classNames(
|
||||
'min-w-[240px] z-[250]',
|
||||
'bg-white dark:bg-[#141414]',
|
||||
'rounded-lg shadow-lg',
|
||||
'border border-gray-200/50 dark:border-gray-800/50',
|
||||
'animate-in fade-in-0 zoom-in-95',
|
||||
'py-1',
|
||||
)}
|
||||
sideOffset={5}
|
||||
align="end"
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className={classNames(
|
||||
'cursor-pointer flex items-center w-full px-4 py-2 text-sm text-bolt-elements-textPrimary' +
|
||||
' hover:bg-bolt-elements-item-backgroundActive gap-2 rounded-md group relative',
|
||||
)}
|
||||
onClick={() => setShowAuthDialog(true)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="i-ph:key" />
|
||||
GitHub Account
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className={classNames(
|
||||
'cursor-pointer flex items-center w-full px-4 py-2 text-sm text-bolt-elements-textPrimary hover:bg-bolt-elements-item-backgroundActive gap-2 rounded-md group relative',
|
||||
)}
|
||||
onClick={() => setShowAuthGitlabDialog(true)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="i-ph:gitlab-logo" />
|
||||
GitLab Account
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4">
|
||||
<div className="flex gap-2 mb-4">
|
||||
<TabButton active={activeTab === 'my-repos'} onClick={() => setActiveTab('my-repos')}>
|
||||
@ -1445,9 +936,6 @@ export function RepositorySelectionDialog({ isOpen, onClose, onSelect }: Reposit
|
||||
|
||||
{/* GitHub Auth Dialog */}
|
||||
<GitHubAuthDialog isOpen={showAuthDialog} onClose={() => handleAuthDialogClose('github')} />
|
||||
|
||||
<GitlabAuthDialog isOpen={showAuthGitlabDialog} onClose={() => handleAuthDialogClose('gitlab')} />
|
||||
|
||||
{/* Repository Stats Dialog */}
|
||||
{currentStats && (
|
||||
<StatsDialog
|
||||
|
@ -33,9 +33,11 @@ export function StatsDialog({ isOpen, onClose, onConfirm, stats, isLargeRepo }:
|
||||
<span className="i-ph:git-branch w-5 h-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-medium text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary-dark">
|
||||
Repository Overview
|
||||
</h3>
|
||||
<Dialog.Title asChild>
|
||||
<h3 className="text-lg font-medium text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary-dark">
|
||||
Repository Overview
|
||||
</h3>
|
||||
</Dialog.Title>
|
||||
<p className="text-sm text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary-dark">
|
||||
Review repository details before importing
|
||||
</p>
|
||||
|
Loading…
Reference in New Issue
Block a user