mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-01-29 04:47:33 +00:00
add i18n feat && Add partial translation
This commit is contained in:
@@ -3,8 +3,10 @@ import { X, FolderPlus, GitBranch, Key, ChevronRight, ChevronLeft, Check, Loader
|
||||
import { Button } from './ui/button';
|
||||
import { Input } from './ui/input';
|
||||
import { api } from '../utils/api';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
const { t } = useTranslation();
|
||||
// Wizard state
|
||||
const [step, setStep] = useState(1); // 1: Choose type, 2: Configure, 3: Confirm
|
||||
const [workspaceType, setWorkspaceType] = useState(null); // 'existing' or 'new'
|
||||
@@ -88,13 +90,13 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
|
||||
if (step === 1) {
|
||||
if (!workspaceType) {
|
||||
setError('Please select whether you have an existing workspace or want to create a new one');
|
||||
setError(t('projectWizard.errors.selectType'));
|
||||
return;
|
||||
}
|
||||
setStep(2);
|
||||
} else if (step === 2) {
|
||||
if (!workspacePath.trim()) {
|
||||
setError('Please provide a workspace path');
|
||||
setError(t('projectWizard.errors.providePath'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -133,7 +135,7 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to create workspace');
|
||||
throw new Error(data.error || t('projectWizard.errors.failedToCreate'));
|
||||
}
|
||||
|
||||
// Success!
|
||||
@@ -144,7 +146,7 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error('Error creating workspace:', error);
|
||||
setError(error.message || 'Failed to create workspace');
|
||||
setError(error.message || t('projectWizard.errors.failedToCreate'));
|
||||
} finally {
|
||||
setIsCreating(false);
|
||||
}
|
||||
@@ -165,7 +167,7 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
<FolderPlus className="w-4 h-4 text-blue-600 dark:text-blue-400" />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
Create New Project
|
||||
{t('projectWizard.title')}
|
||||
</h3>
|
||||
</div>
|
||||
<button
|
||||
@@ -195,7 +197,7 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
{s < step ? <Check className="w-4 h-4" /> : s}
|
||||
</div>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300 hidden sm:inline">
|
||||
{s === 1 ? 'Type' : s === 2 ? 'Configure' : 'Confirm'}
|
||||
{s === 1 ? t('projectWizard.steps.type') : s === 2 ? t('projectWizard.steps.configure') : t('projectWizard.steps.confirm')}
|
||||
</span>
|
||||
</div>
|
||||
{s < 3 && (
|
||||
@@ -227,7 +229,7 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
|
||||
Do you already have a workspace, or would you like to create a new one?
|
||||
{t('projectWizard.step1.question')}
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Existing Workspace */}
|
||||
@@ -245,10 +247,10 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h5 className="font-semibold text-gray-900 dark:text-white mb-1">
|
||||
Existing Workspace
|
||||
{t('projectWizard.step1.existing.title')}
|
||||
</h5>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
I already have a workspace on my server and just need to add it to the project list
|
||||
{t('projectWizard.step1.existing.description')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -269,10 +271,10 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h5 className="font-semibold text-gray-900 dark:text-white mb-1">
|
||||
New Workspace
|
||||
{t('projectWizard.step1.new.title')}
|
||||
</h5>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
Create a new workspace, optionally clone from a GitHub repository
|
||||
{t('projectWizard.step1.new.description')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -288,14 +290,14 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
{/* Workspace Path */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{workspaceType === 'existing' ? 'Workspace Path' : 'Where should the workspace be created?'}
|
||||
{workspaceType === 'existing' ? t('projectWizard.step2.existingPath') : t('projectWizard.step2.newPath')}
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
type="text"
|
||||
value={workspacePath}
|
||||
onChange={(e) => setWorkspacePath(e.target.value)}
|
||||
placeholder={workspaceType === 'existing' ? '/path/to/existing/workspace' : '/path/to/new/workspace'}
|
||||
placeholder={workspaceType === 'existing' ? t('projectWizard.step2.existingPlaceholder') : t('projectWizard.step2.newPlaceholder')}
|
||||
className="w-full"
|
||||
/>
|
||||
{showPathDropdown && pathSuggestions.length > 0 && (
|
||||
@@ -315,8 +317,8 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
{workspaceType === 'existing'
|
||||
? 'Full path to your existing workspace directory'
|
||||
: 'Full path where the new workspace will be created'}
|
||||
? t('projectWizard.step2.existingHelp')
|
||||
: t('projectWizard.step2.newHelp')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -325,17 +327,17 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
<>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
GitHub URL (Optional)
|
||||
{t('projectWizard.step2.githubUrl')}
|
||||
</label>
|
||||
<Input
|
||||
type="text"
|
||||
value={githubUrl}
|
||||
onChange={(e) => setGithubUrl(e.target.value)}
|
||||
placeholder="https://github.com/username/repository"
|
||||
placeholder={t('projectWizard.step2.githubPlaceholder')}
|
||||
className="w-full"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
Leave empty to create an empty workspace, or provide a GitHub URL to clone
|
||||
{t('projectWizard.step2.githubHelp')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -346,10 +348,10 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
<Key className="w-5 h-5 text-gray-600 dark:text-gray-400 flex-shrink-0 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<h5 className="font-medium text-gray-900 dark:text-white mb-1">
|
||||
GitHub Authentication (Optional)
|
||||
{t('projectWizard.step2.githubAuth')}
|
||||
</h5>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
Only required for private repositories. Public repos can be cloned without authentication.
|
||||
{t('projectWizard.step2.githubAuthHelp')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -357,7 +359,7 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
{loadingTokens ? (
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500">
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
Loading stored tokens...
|
||||
{t('projectWizard.step2.loadingTokens')}
|
||||
</div>
|
||||
) : availableTokens.length > 0 ? (
|
||||
<>
|
||||
@@ -371,7 +373,7 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
|
||||
}`}
|
||||
>
|
||||
Stored Token
|
||||
{t('projectWizard.step2.storedToken')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setTokenMode('new')}
|
||||
@@ -381,7 +383,7 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
|
||||
}`}
|
||||
>
|
||||
New Token
|
||||
{t('projectWizard.step2.newToken')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
@@ -395,21 +397,21 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
|
||||
}`}
|
||||
>
|
||||
None (Public)
|
||||
{t('projectWizard.step2.nonePublic')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{tokenMode === 'stored' ? (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Select Token
|
||||
{t('projectWizard.step2.selectToken')}
|
||||
</label>
|
||||
<select
|
||||
value={selectedGithubToken}
|
||||
onChange={(e) => setSelectedGithubToken(e.target.value)}
|
||||
className="w-full px-3 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg text-sm"
|
||||
>
|
||||
<option value="">-- Select a token --</option>
|
||||
<option value="">{t('projectWizard.step2.selectTokenPlaceholder')}</option>
|
||||
{availableTokens.map((token) => (
|
||||
<option key={token.id} value={token.id}>
|
||||
{token.credential_name}
|
||||
@@ -420,17 +422,17 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
) : tokenMode === 'new' ? (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
GitHub Token
|
||||
{t('projectWizard.step2.newToken')}
|
||||
</label>
|
||||
<Input
|
||||
type="password"
|
||||
value={newGithubToken}
|
||||
onChange={(e) => setNewGithubToken(e.target.value)}
|
||||
placeholder="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
placeholder={t('projectWizard.step2.tokenPlaceholder')}
|
||||
className="w-full"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
This token will be used only for this operation
|
||||
{t('projectWizard.step2.tokenHelp')}
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
@@ -439,23 +441,23 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
<div className="space-y-4">
|
||||
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-3 border border-blue-200 dark:border-blue-800">
|
||||
<p className="text-sm text-blue-800 dark:text-blue-200">
|
||||
💡 <strong>Public repositories</strong> don't require authentication. You can skip providing a token if cloning a public repo.
|
||||
{t('projectWizard.step2.publicRepoInfo')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
GitHub Token (Optional for Public Repos)
|
||||
{t('projectWizard.step2.optionalTokenPublic')}
|
||||
</label>
|
||||
<Input
|
||||
type="password"
|
||||
value={newGithubToken}
|
||||
onChange={(e) => setNewGithubToken(e.target.value)}
|
||||
placeholder="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (leave empty for public repos)"
|
||||
placeholder={t('projectWizard.step2.tokenPublicPlaceholder')}
|
||||
className="w-full"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
No stored tokens available. You can add tokens in Settings → API Keys for easier reuse.
|
||||
{t('projectWizard.step2.noTokensHelp')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -472,17 +474,17 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
<div className="space-y-4">
|
||||
<div className="bg-gray-50 dark:bg-gray-900/50 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
|
||||
<h4 className="text-sm font-semibold text-gray-900 dark:text-white mb-3">
|
||||
Review Your Configuration
|
||||
{t('projectWizard.step3.reviewConfig')}
|
||||
</h4>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-gray-600 dark:text-gray-400">Workspace Type:</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">{t('projectWizard.step3.workspaceType')}</span>
|
||||
<span className="font-medium text-gray-900 dark:text-white">
|
||||
{workspaceType === 'existing' ? 'Existing Workspace' : 'New Workspace'}
|
||||
{workspaceType === 'existing' ? t('projectWizard.step3.existingWorkspace') : t('projectWizard.step3.newWorkspace')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-gray-600 dark:text-gray-400">Path:</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">{t('projectWizard.step3.path')}</span>
|
||||
<span className="font-mono text-xs text-gray-900 dark:text-white break-all">
|
||||
{workspacePath}
|
||||
</span>
|
||||
@@ -490,19 +492,19 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
{workspaceType === 'new' && githubUrl && (
|
||||
<>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-gray-600 dark:text-gray-400">Clone From:</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">{t('projectWizard.step3.cloneFrom')}</span>
|
||||
<span className="font-mono text-xs text-gray-900 dark:text-white break-all">
|
||||
{githubUrl}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-gray-600 dark:text-gray-400">Authentication:</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">{t('projectWizard.step3.authentication')}</span>
|
||||
<span className="text-xs text-gray-900 dark:text-white">
|
||||
{tokenMode === 'stored' && selectedGithubToken
|
||||
? `Using stored token: ${availableTokens.find(t => t.id.toString() === selectedGithubToken)?.credential_name || 'Unknown'}`
|
||||
? `${t('projectWizard.step3.usingStoredToken')} ${availableTokens.find(t => t.id.toString() === selectedGithubToken)?.credential_name || 'Unknown'}`
|
||||
: tokenMode === 'new' && newGithubToken
|
||||
? 'Using provided token'
|
||||
: 'No authentication'}
|
||||
? t('projectWizard.step3.usingProvidedToken')
|
||||
: t('projectWizard.step3.noAuthentication')}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
@@ -513,10 +515,10 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-4 border border-blue-200 dark:border-blue-800">
|
||||
<p className="text-sm text-blue-800 dark:text-blue-200">
|
||||
{workspaceType === 'existing'
|
||||
? 'The workspace will be added to your project list and will be available for Claude/Cursor sessions.'
|
||||
? t('projectWizard.step3.existingInfo')
|
||||
: githubUrl
|
||||
? 'A new workspace will be created and the repository will be cloned from GitHub.'
|
||||
: 'An empty workspace directory will be created at the specified path.'}
|
||||
? t('projectWizard.step3.newWithClone')
|
||||
: t('projectWizard.step3.newEmpty')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -531,11 +533,11 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
disabled={isCreating}
|
||||
>
|
||||
{step === 1 ? (
|
||||
'Cancel'
|
||||
t('projectWizard.buttons.cancel')
|
||||
) : (
|
||||
<>
|
||||
<ChevronLeft className="w-4 h-4 mr-1" />
|
||||
Back
|
||||
{t('projectWizard.buttons.back')}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
@@ -547,16 +549,16 @@ const ProjectCreationWizard = ({ onClose, onProjectCreated }) => {
|
||||
{isCreating ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||||
Creating...
|
||||
{t('projectWizard.buttons.creating')}
|
||||
</>
|
||||
) : step === 3 ? (
|
||||
<>
|
||||
<Check className="w-4 h-4 mr-1" />
|
||||
Create Project
|
||||
{t('projectWizard.buttons.createProject')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Next
|
||||
{t('projectWizard.buttons.next')}
|
||||
<ChevronRight className="w-4 h-4 ml-1" />
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user