import React from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { toast } from 'sonner'; import { useGitIdentitiesStore, type GitIdentityProfile, type GitIdentityAuthType } from '@/stores/useGitIdentitiesStore'; import { RiUser3Line, RiSaveLine, RiDeleteBinLine, RiGitBranchLine, RiBriefcaseLine, RiHomeLine, RiGraduationCapLine, RiCodeLine, RiInformationLine, RiKeyLine, RiLock2Line } from '@remixicon/react'; import { cn } from '@/lib/utils'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay'; const PROFILE_COLORS = [ { key: 'keyword', label: 'Green', cssVar: 'var(--syntax-keyword)' }, { key: 'error', label: 'Red', cssVar: 'var(--status-error)' }, { key: 'string', label: 'Cyan', cssVar: 'var(--syntax-string)' }, { key: 'function', label: 'Orange', cssVar: 'var(--syntax-function)' }, { key: 'type', label: 'Yellow', cssVar: 'var(--syntax-type)' }, ]; const PROFILE_ICONS = [ { key: 'branch', Icon: RiGitBranchLine, label: 'Branch' }, { key: 'briefcase', Icon: RiBriefcaseLine, label: 'Work' }, { key: 'house', Icon: RiHomeLine, label: 'Personal' }, { key: 'graduation', Icon: RiGraduationCapLine, label: 'School' }, { key: 'code', Icon: RiCodeLine, label: 'Code' }, ]; export const GitIdentitiesPage: React.FC = () => { const { selectedProfileId, getProfileById, createProfile, updateProfile, deleteProfile, } = useGitIdentitiesStore(); // Parse import: prefix for credential import flow const importData = React.useMemo(() => { if (selectedProfileId?.startsWith('import:')) { const [, host, username] = selectedProfileId.split(':'); return { host, username }; } return null; }, [selectedProfileId]); const selectedProfile = React.useMemo(() => selectedProfileId && selectedProfileId !== 'new' && !importData ? getProfileById(selectedProfileId) : null, [selectedProfileId, getProfileById, importData] ); const isNewProfile = selectedProfileId === 'new' || importData !== null; const isGlobalProfile = selectedProfileId === 'global'; const [name, setName] = React.useState(''); const [userName, setUserName] = React.useState(''); const [userEmail, setUserEmail] = React.useState(''); const [authType, setAuthType] = React.useState('ssh'); const [sshKey, setSshKey] = React.useState(''); const [host, setHost] = React.useState(''); const [color, setColor] = React.useState('keyword'); const [icon, setIcon] = React.useState('branch'); const [isSaving, setIsSaving] = React.useState(false); React.useEffect(() => { if (importData) { const parts = importData.host.split('/'); const displayName = parts.length >= 3 ? parts[parts.length - 1] : importData.host; setName(displayName); setUserName(importData.username); setUserEmail(''); setAuthType('token'); setSshKey(''); setHost(importData.host); setColor('string'); setIcon('code'); } else if (isNewProfile) { setName(''); setUserName(''); setUserEmail(''); setAuthType('ssh'); setSshKey(''); setHost(''); setColor('keyword'); setIcon('branch'); } else if (selectedProfile) { setName(selectedProfile.name); setUserName(selectedProfile.userName); setUserEmail(selectedProfile.userEmail); setAuthType(selectedProfile.authType || 'ssh'); setSshKey(selectedProfile.sshKey || ''); setHost(selectedProfile.host || ''); setColor(selectedProfile.color || 'keyword'); setIcon(selectedProfile.icon || 'branch'); } }, [selectedProfile, isNewProfile, selectedProfileId, importData]); const handleSave = async () => { if (!userName.trim() || !userEmail.trim()) { toast.error('User name and email are required'); return; } if (authType === 'token' && !host.trim()) { toast.error('Host is required for token-based authentication'); return; } setIsSaving(true); try { const profileData: Omit & { id?: string } = { name: name.trim() || userName.trim(), userName: userName.trim(), userEmail: userEmail.trim(), authType, sshKey: authType === 'ssh' ? (sshKey.trim() || null) : null, host: authType === 'token' ? (host.trim() || null) : null, color, icon, }; let success: boolean; if (isNewProfile) { success = await createProfile(profileData); } else if (selectedProfileId) { success = await updateProfile(selectedProfileId, profileData); } else { return; } if (success) { toast.success(isNewProfile ? 'Profile created successfully' : 'Profile updated successfully'); } else { toast.error(isNewProfile ? 'Failed to create profile' : 'Failed to update profile'); } } catch (error) { console.error('Error saving profile:', error); toast.error('An error occurred while saving'); } finally { setIsSaving(false); } }; const handleDelete = async () => { if (!selectedProfileId || isNewProfile) return; if (!confirm('Are you sure you want to delete this profile?')) { return; } try { const success = await deleteProfile(selectedProfileId); if (success) { toast.success('Profile deleted successfully'); } else { toast.error('Failed to delete profile'); } } catch (error) { console.error('Error deleting profile:', error); toast.error('An error occurred while deleting'); } }; const currentColorValue = React.useMemo(() => { const colorConfig = PROFILE_COLORS.find(c => c.key === color); return colorConfig?.cssVar || 'var(--syntax-keyword)'; }, [color]); if (!selectedProfileId) { return (

Select a profile from the sidebar

or create a new one

); } return (
{/* Header */}

{importData ? 'Import Credential' : isNewProfile ? 'New Git Profile' : isGlobalProfile ? 'Global Identity' : name || 'Edit Profile'}

{importData ? `Import token credential for ${importData.host} - please fill in your email address` : isNewProfile ? 'Create a new Git identity profile for your repositories' : isGlobalProfile ? 'System-wide Git identity from global configuration (read-only)' : 'Configure Git identity settings for this profile'}

{} {!isGlobalProfile && (

Profile Information

Basic profile settings and display name

setName(e.target.value)} placeholder="Work Profile, Personal, etc." />

Friendly name to identify this profile (optional, defaults to user name)

{PROFILE_COLORS.map((c) => (
{PROFILE_ICONS.map((i) => { const IconComponent = i.Icon; return ( ); })}
)} {}

Git Configuration

Git user settings that will be applied to repositories

setUserName(e.target.value)} placeholder="John Doe" required={!isGlobalProfile} readOnly={isGlobalProfile} disabled={isGlobalProfile} />

Git user.name configuration value

setUserEmail(e.target.value)} placeholder="john@example.com" required={!isGlobalProfile} readOnly={isGlobalProfile} disabled={isGlobalProfile} />

Git user.email configuration value

{/* Auth Type Selector */} {!isGlobalProfile && (
)} {/* SSH Key Path - only for SSH auth type */} {authType === 'ssh' && (
setSshKey(e.target.value)} placeholder="/Users/username/.ssh/id_rsa" readOnly={isGlobalProfile} disabled={isGlobalProfile} />

Path to SSH private key for authentication (optional)

)} {/* Host - only for Token auth type */} {authType === 'token' && !isGlobalProfile && (
setHost(e.target.value)} placeholder="github.com" required />

Git host for token authentication (from ~/.git-credentials)

)} {} {!isGlobalProfile && (
{!isNewProfile && ( )}
)}
); };