generated from kevisual/vite-react-template
- Implement CreateRepoDialog for creating new repositories with form validation. - Implement EditRepoDialog for editing existing repository details. - Implement SyncRepoDialog for syncing repositories with Gitea, including repository creation if necessary. - Implement WorkspaceDetailDialog for managing workspace links and actions. - Enhance the repo store with new state management for repository actions, including creating, editing, and syncing repositories. - Add build configuration utilities for repository synchronization. - Create a new page for repository management, integrating all dialogs and functionalities. - Add login route for authentication.
110 lines
3.6 KiB
TypeScript
110 lines
3.6 KiB
TypeScript
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Input } from '@/components/ui/input'
|
|
import { Label } from '@/components/ui/label'
|
|
import { useRepoStore } from '../store'
|
|
import { useState, useEffect } from 'react'
|
|
import { get, set } from 'idb-keyval'
|
|
import { gitea } from '@/agents/app';
|
|
import { toast } from 'sonner'
|
|
|
|
const SYNC_REPO_STORAGE_KEY = 'sync-repo-mapping'
|
|
|
|
export function SyncRepoDialog() {
|
|
const { syncDialogOpen, setSyncDialogOpen, selectedSyncRepo, buildSync } = useRepoStore()
|
|
const [toRepo, setToRepo] = useState('')
|
|
|
|
useEffect(() => {
|
|
const loadSavedMapping = async () => {
|
|
if (syncDialogOpen && selectedSyncRepo) {
|
|
const currentPath = selectedSyncRepo.path || ''
|
|
// 从 idb-keyval 获取存储的映射
|
|
const mapping = await get<Record<string, string>>(SYNC_REPO_STORAGE_KEY)
|
|
// 如果有存储的值,使用存储的值,否则使用当前仓库路径
|
|
setToRepo(mapping?.[currentPath] || currentPath)
|
|
}
|
|
}
|
|
loadSavedMapping()
|
|
}, [syncDialogOpen, selectedSyncRepo])
|
|
|
|
const handleSync = async () => {
|
|
if (!selectedSyncRepo || !toRepo.trim()) {
|
|
return
|
|
}
|
|
|
|
// 保存映射到 idb-keyval
|
|
const currentPath = selectedSyncRepo.path || ''
|
|
const mapping = await get<Record<string, string>>(SYNC_REPO_STORAGE_KEY) || {}
|
|
mapping[currentPath] = toRepo
|
|
await set(SYNC_REPO_STORAGE_KEY, mapping)
|
|
|
|
await buildSync(selectedSyncRepo, { toRepo })
|
|
setSyncDialogOpen(false)
|
|
}
|
|
const onCreateRepo = async () => {
|
|
if (!toRepo.trim()) {
|
|
return
|
|
}
|
|
try {
|
|
const res = await gitea.repo.createRepo({ name: toRepo })
|
|
if (res.code !== 200 && res.code !== 409) {
|
|
// 409 表示仓库已存在,可以继续同步
|
|
throw new Error(`${res.message}`)
|
|
}
|
|
if (res.code === 200) {
|
|
toast.success('仓库创建成功,正在同步...')
|
|
} else {
|
|
toast.warning('仓库已存在,正在同步...')
|
|
}
|
|
handleSync()
|
|
} catch (error) {
|
|
console.error('创建仓库失败:', error)
|
|
}
|
|
}
|
|
return (
|
|
<Dialog open={syncDialogOpen} onOpenChange={setSyncDialogOpen}>
|
|
<DialogContent className="sm:max-w-125">
|
|
<DialogHeader>
|
|
<DialogTitle>同步仓库到 Gitea</DialogTitle>
|
|
<DialogDescription>
|
|
将仓库 <span className="font-semibold text-neutral-900">{selectedSyncRepo?.path}</span> 同步到目标仓库
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-4 py-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="toRepo">目标仓库路径</Label>
|
|
<Input
|
|
id="toRepo"
|
|
placeholder="例如: kevisual/my-repo"
|
|
value={toRepo}
|
|
onChange={(e) => setToRepo(e.target.value)}
|
|
/>
|
|
<p className="text-xs text-neutral-500">
|
|
格式: owner/repo-name
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex justify-end gap-2">
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => setSyncDialogOpen(false)}
|
|
>
|
|
取消
|
|
</Button>
|
|
<Button onClick={onCreateRepo} disabled={!toRepo.trim()}>
|
|
先创建仓库再同步
|
|
</Button>
|
|
<Button
|
|
onClick={handleSync}
|
|
disabled={!toRepo.trim()}
|
|
>
|
|
开始同步
|
|
</Button>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|