feat: 프로젝트 저장소 연동 UI + 화면 문구 정리 (Phase 2b)

- useRepo 복구(프로젝트 종속 /projects/:projectId/repos): list/meta/checkName/create/remove
- types/repo ApiRepo 를 새 RepoResponse 와 정합(projectId 추가, members/counts 제거)
- ProjectTabs 에 '저장소' 탭(repoCount) 추가, 4개 페이지 repo-count 전달
- ProjectReposPage 신설(/projects/:id/repos): 연동 저장소 목록 + 연동(생성) 폼
  (slug 가용성 디바운스 체크·템플릿 옵션) + Gitea 열기 + 연동 해제(admin)
- 화면 잔여 '저장소' 문구를 '프로젝트'로 정리(crumb/설정/멤버 모달/주석/not-found)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-20 19:52:23 +09:00
parent e028449312
commit 8a2e34d617
10 changed files with 831 additions and 38 deletions
+58
View File
@@ -0,0 +1,58 @@
import { useApi } from './useApi'
import type {
ApiRepo,
CreateRepoPayload,
RepoCreateMeta,
} from '@/types/repo'
import type { Paginated } from '@/types/pagination'
// 저장소 도메인 API Composable — 프로젝트에 연동된 Gitea 저장소.
// 모든 경로는 /projects/:projectId/repos 하위. 인터셉터가 success/data 를 언래핑.
export function useRepo() {
const api = useApi()
async function list(
projectId: string,
page = 1,
size = 20,
): Promise<Paginated<ApiRepo>> {
return (await api.get(`/projects/${projectId}/repos`, {
params: { page, size },
})) as unknown as Paginated<ApiRepo>
}
// 생성 폼 메타(소유자/템플릿)
async function getCreateMeta(projectId: string): Promise<RepoCreateMeta> {
return (await api.get(
`/projects/${projectId}/repos/meta`,
)) as unknown as RepoCreateMeta
}
// slug 사용 가능 여부 — 전역 유일 검사
async function checkName(
projectId: string,
slug: string,
): Promise<boolean> {
const res = (await api.get(`/projects/${projectId}/repos/name-available`, {
params: { slug },
})) as unknown as { available: boolean }
return res.available
}
async function create(
projectId: string,
payload: CreateRepoPayload,
): Promise<ApiRepo> {
return (await api.post(
`/projects/${projectId}/repos`,
payload,
)) as unknown as ApiRepo
}
// 연동 해제(삭제)
async function remove(projectId: string, repoId: string): Promise<void> {
await api.delete(`/projects/${projectId}/repos/${repoId}`)
}
return { list, getCreateMeta, checkName, create, remove }
}