feat: add repository management dialogs and store functionality

- 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.
This commit is contained in:
2026-02-25 01:02:55 +08:00
parent f4643464ba
commit 7ec6428643
32 changed files with 3303 additions and 71 deletions

View File

@@ -0,0 +1,140 @@
import { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import { TagsInput } from '@/components/tags-input'
import { useRepoStore } from '../store'
interface EditRepoDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
repo: {
id: string
path: string
description: string
site: string
topics: string
license: string
} | null
}
interface FormData {
description: string
site: string
topics: string
license: string
}
export function EditRepoDialog({ open, onOpenChange, repo }: EditRepoDialogProps) {
const { updateRepoInfo, getList } = useRepoStore()
const { register, handleSubmit, reset, setValue } = useForm<FormData>()
const [tags, setTags] = useState<string[]>([])
useEffect(() => {
if (repo) {
const topicsArray = repo.topics ? repo.topics.split(',').map(t => t.trim()).filter(Boolean) : []
setTags(topicsArray)
reset({
description: repo.description || '',
site: repo.site || '',
topics: repo.topics || '',
license: repo.license || ''
})
}
}, [repo, reset])
const onSubmit = async (data: FormData) => {
if (!repo) return
await updateRepoInfo({
path: repo.path,
description: data.description?.trim() || '',
site: data.site?.trim() || '',
topics: tags.join(','),
license: data.license?.trim() || '',
})
await getList(true)
onOpenChange(false)
}
if (!repo) return null
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-2xl!">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription>{repo.path}</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
<div className="space-y-2">
<Label htmlFor="description"></Label>
<Textarea
id="description"
{...register('description')}
placeholder="输入仓库描述"
className="w-full min-h-[100px]"
rows={4}
/>
</div>
<div className="space-y-2">
<Label htmlFor="site"></Label>
<Input
id="site"
{...register('site')}
placeholder="https://example.com"
type="url"
className="w-full"
/>
</div>
<div className="space-y-2">
<Label htmlFor="topics"></Label>
<TagsInput
value={tags}
onChange={setTags}
placeholder="输入标签后按 Enter 或逗号添加"
/>
<p className="text-xs text-gray-500"> Enter × </p>
</div>
<div className="space-y-2">
<Label htmlFor="license"></Label>
<Input
id="license"
{...register('license')}
placeholder="MIT, Apache-2.0, GPL-3.0 等"
className="w-full"
/>
</div>
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => onOpenChange(false)}
>
</Button>
<Button type="submit">
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
)
}