import React, { useState, useCallback, useEffect } from 'react'; import { Button, Space, Card, Typography, Tag, Empty, message, Spin, Descriptions, List, Badge, theme } from 'antd'; import { CloudDownloadOutlined, SyncOutlined, BranchesOutlined, CheckCircleOutlined, WarningOutlined, FileAddOutlined, EditOutlined, DeleteOutlined, QuestionCircleOutlined, } from '@ant-design/icons'; import { useApp } from '@nocobase/client-v2'; import { useGitManager } from '../context/GitManagerContext'; import { useT } from '../locale'; const { Text } = Typography; const { useToken } = theme; const FILE_STATUS_MAP: Record = { not_added: { color: 'green', icon: , label: 'New' }, modified: { color: 'blue', icon: , label: 'Modified' }, deleted: { color: 'red', icon: , label: 'Deleted' }, renamed: { color: 'orange', icon: , label: 'Renamed' }, conflicted: { color: 'red', icon: , label: 'Conflict' }, created: { color: 'green', icon: , label: 'Created' }, }; export const GitOperations: React.FC = () => { const t = useT(); const { token } = useToken(); const api = useApp().apiClient; const { selectedRepo, refreshRepos } = useGitManager(); const [actionLoading, setActionLoading] = useState(null); const [statusData, setStatusData] = useState(null); const [lastResult, setLastResult] = useState<{ action: string; success: boolean; message: string } | null>(null); const loadStatus = useCallback(async () => { if (!selectedRepo || selectedRepo.status !== 'connected') { setStatusData(null); return; } setActionLoading('status'); try { const { data } = await api.request({ url: 'gitManager:status', method: 'post', params: { repositoryId: selectedRepo.id }, }); const responseData = data?.data?.data || data?.data; setStatusData(responseData); } catch (error) { console.warn('Failed to load git status:', error); setStatusData(null); } finally { setActionLoading(null); } }, [api, selectedRepo]); useEffect(() => { loadStatus(); }, [loadStatus]); const execAction = useCallback( async (action: string) => { if (!selectedRepo) return; setActionLoading(action); setLastResult(null); try { const { data } = await api.request({ url: `gitManager:${action}`, method: 'post', params: { repositoryId: selectedRepo.id }, }); setLastResult({ action, success: true, message: t('{{action}} completed successfully', { action }) }); message.success(t('{{action}} completed', { action })); const responseData = data?.data?.data || data?.data; if (action === 'status') setStatusData(responseData); if (action === 'fetch' || action === 'pull') await loadStatus(); await refreshRepos(); } catch (err: any) { const msg = err?.response?.data?.errors?.[0]?.message || t('{{action}} failed', { action }); setLastResult({ action, success: false, message: msg }); message.error(msg); } finally { setActionLoading(null); } }, [api, selectedRepo, refreshRepos, loadStatus], ); if (!selectedRepo) { return ; } if (selectedRepo.status !== 'connected') { return ; } const allChangedFiles = statusData ? [ ...(statusData.not_added?.map((f: string) => ({ file: f, status: 'not_added' })) || []), ...(statusData.modified?.map((f: string) => ({ file: f, status: 'modified' })) || []), ...(statusData.deleted?.map((f: string) => ({ file: f, status: 'deleted' })) || []), ...(statusData.renamed?.map((f: any) => ({ file: typeof f === 'string' ? f : f.to, status: 'renamed' })) || []), ...(statusData.conflicted?.map((f: string) => ({ file: f, status: 'conflicted' })) || []), ...(statusData.created?.map((f: string) => ({ file: f, status: 'created' })) || []), ] : []; return (
{lastResult && ( {lastResult.success ? ( ) : ( )} {lastResult.action} {lastResult.message} )} {statusData && ( } color="blue"> {statusData.current} {statusData.tracking || 'N/A'} {statusData.ahead > 0 && ( )} {statusData.behind > 0 && ( )} {statusData.ahead === 0 && statusData.behind === 0 && ( }> Up to date )} {allChangedFiles.length > 0 ? ( <> {allChangedFiles.length} changed file(s) { const info = FILE_STATUS_MAP[item.status] || { color: 'default', icon: , label: item.status, }; return ( {info.icon} {info.label} {item.file} ); }} /> ) : ( Working directory clean )} )}
); };