import { useEffect, useState } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { GitHubConfigForm } from './GitHubConfigForm'; import { GiteaConfigForm } from './GiteaConfigForm'; import { ScheduleConfigForm } from './ScheduleConfigForm'; import type { ConfigApiResponse, GiteaConfig, GitHubConfig, SaveConfigApiRequest, SaveConfigApiResponse, ScheduleConfig, } from '@/types/config'; import { Button } from '../ui/button'; import { useAuth } from '@/hooks/useAuth'; import { apiRequest } from '@/lib/utils'; import { Copy, CopyCheck, RefreshCw } from 'lucide-react'; import { toast } from 'sonner'; import { Skeleton } from '@/components/ui/skeleton'; type ConfigState = { githubConfig: GitHubConfig; giteaConfig: GiteaConfig; scheduleConfig: ScheduleConfig; }; export function ConfigTabs() { const [config, setConfig] = useState({ githubConfig: { username: '', token: '', skipForks: false, privateRepositories: false, mirrorIssues: false, mirrorStarred: false, preserveOrgStructure: false, skipStarredIssues: false, }, giteaConfig: { url: '', username: '', token: '', organization: 'github-mirrors', visibility: 'public', starredReposOrg: 'github', }, scheduleConfig: { enabled: false, interval: 3600, }, }); const { user, refreshUser } = useAuth(); const [isLoading, setIsLoading] = useState(true); const [dockerCode, setDockerCode] = useState(''); const [isCopied, setIsCopied] = useState(false); const [isSyncing, setIsSyncing] = useState(false); const [isConfigSaved, setIsConfigSaved] = useState(false); const isConfigFormValid = (): boolean => { const { githubConfig, giteaConfig } = config; const isGitHubValid = !!( githubConfig.username.trim() && githubConfig.token.trim() ); const isGiteaValid = !!( giteaConfig.url.trim() && giteaConfig.username.trim() && giteaConfig.token.trim() ); return isGitHubValid && isGiteaValid; }; useEffect(() => { const updateLastAndNextRun = () => { const lastRun = config.scheduleConfig.lastRun ? new Date(config.scheduleConfig.lastRun) : new Date(); const intervalInSeconds = config.scheduleConfig.interval; const nextRun = new Date( lastRun.getTime() + intervalInSeconds * 1000, ); setConfig(prev => ({ ...prev, scheduleConfig: { ...prev.scheduleConfig, lastRun, nextRun, }, })); }; updateLastAndNextRun(); }, [config.scheduleConfig.interval]); const handleImportGitHubData = async () => { if (!user?.id) return; setIsSyncing(true); try { const result = await apiRequest<{ success: boolean; message?: string }>( `/sync?userId=${user.id}`, { method: 'POST' }, ); result.success ? toast.success( 'GitHub data imported successfully! Head to the Dashboard to start mirroring repositories.', ) : toast.error( `Failed to import GitHub data: ${ result.message || 'Unknown error' }`, ); } catch (error) { toast.error( `Error importing GitHub data: ${ error instanceof Error ? error.message : String(error) }`, ); } finally { setIsSyncing(false); } }; const handleSaveConfig = async () => { if (!user?.id) return; const reqPayload: SaveConfigApiRequest = { userId: user.id, githubConfig: config.githubConfig, giteaConfig: config.giteaConfig, scheduleConfig: config.scheduleConfig, }; try { const response = await fetch('/api/config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(reqPayload), }); const result: SaveConfigApiResponse = await response.json(); if (result.success) { await refreshUser(); setIsConfigSaved(true); toast.success( 'Configuration saved successfully! Now import your GitHub data to begin.', ); } else { toast.error( `Failed to save configuration: ${result.message || 'Unknown error'}`, ); } } catch (error) { toast.error( `An error occurred while saving the configuration: ${ error instanceof Error ? error.message : String(error) }`, ); } }; useEffect(() => { if (!user) return; const fetchConfig = async () => { setIsLoading(true); try { const response = await apiRequest( `/config?userId=${user.id}`, { method: 'GET' }, ); if (response && !response.error) { setConfig({ githubConfig: response.githubConfig || config.githubConfig, giteaConfig: response.giteaConfig || config.giteaConfig, scheduleConfig: response.scheduleConfig || config.scheduleConfig, }); if (response.id) setIsConfigSaved(true); } } catch (error) { console.warn( 'Could not fetch configuration, using defaults:', error, ); } setIsLoading(false); }; fetchConfig(); }, [user]); useEffect(() => { const generateDockerCode = () => ` services: gitea-mirror: image: arunavo4/gitea-mirror:latest restart: unless-stopped container_name: gitea-mirror environment: - GITHUB_USERNAME=${config.githubConfig.username} - GITEA_URL=${config.giteaConfig.url} - GITEA_TOKEN=${config.giteaConfig.token} - GITHUB_TOKEN=${config.githubConfig.token} - SKIP_FORKS=${config.githubConfig.skipForks} - PRIVATE_REPOSITORIES=${config.githubConfig.privateRepositories} - MIRROR_ISSUES=${config.githubConfig.mirrorIssues} - MIRROR_STARRED=${config.githubConfig.mirrorStarred} - PRESERVE_ORG_STRUCTURE=${config.githubConfig.preserveOrgStructure} - SKIP_STARRED_ISSUES=${config.githubConfig.skipStarredIssues} - GITEA_ORGANIZATION=${config.giteaConfig.organization} - GITEA_ORG_VISIBILITY=${config.giteaConfig.visibility} - DELAY=${config.scheduleConfig.interval}`; setDockerCode(generateDockerCode()); }, [config]); const handleCopyToClipboard = (text: string) => { navigator.clipboard.writeText(text).then( () => { setIsCopied(true); toast.success('Docker configuration copied to clipboard!'); setTimeout(() => setIsCopied(false), 2000); }, () => toast.error('Could not copy text to clipboard.'), ); }; function ConfigCardSkeleton() { return (
); } function DockerConfigSkeleton() { return ( ); } return isLoading ? (
) : (
Configuration Settings Configure your GitHub and Gitea connections, and set up automatic mirroring.
setConfig(prev => ({ ...prev, githubConfig: typeof update === 'function' ? update(prev.githubConfig) : update, })) } /> setConfig(prev => ({ ...prev, giteaConfig: typeof update === 'function' ? update(prev.giteaConfig) : update, })) } />
setConfig(prev => ({ ...prev, scheduleConfig: typeof update === 'function' ? update(prev.scheduleConfig) : update, })) } />
Docker Configuration Equivalent Docker configuration for your current settings.
            {dockerCode}
          
); }