feat: 프론트엔드 프로젝트 우선 구조 재구성 (Phase 2-a)

- 데이터 계층 신설: types/project·useProject·project.store (프로젝트 CRUD·페이지네이션)
- useTask/useMember/useActivity 경로를 /projects/:projectId/... 로 전환
- task·my-task·activity 타입/스토어 repoId/repoName → projectId/projectName
- 알림 store 라우팅·문구 프로젝트화, 활동 타입 repo.linked/unlinked·project.* 반영
- 라우팅 /projects/... 전면 교체, / → /projects 리다이렉트
- 페이지 rename Repo*→Project* (List/Create/Detail/Settings/Members/Activity)
- ProjectCreatePage: Gitea 저장소 폼 → 단순 프로젝트 생성 폼으로 재작성
- RepoHeader→ProjectHeader, RepoTabs→ProjectTabs, AppShell 네비 프로젝트화
- 죽은 파일 삭제: repo.store·useRepo·GiteaRepoBar

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-20 19:37:51 +09:00
parent 6ba148314c
commit 7cd6676ef9
35 changed files with 886 additions and 1709 deletions
+32 -28
View File
@@ -5,7 +5,7 @@ import { useAuthStore } from '@/stores/auth.store'
const routes: RouteRecordRaw[] = [
{
path: '/',
redirect: '/repos',
redirect: '/projects',
},
{
path: '/login',
@@ -20,63 +20,63 @@ const routes: RouteRecordRaw[] = [
meta: { requiresAuth: false },
},
{
path: '/repos',
name: 'repo-list',
component: () => import('@/pages/relay/RepoListPage.vue'),
path: '/projects',
name: 'project-list',
component: () => import('@/pages/relay/ProjectListPage.vue'),
meta: { requiresAuth: true },
},
{
// 정적 경로 '/repos/new' 는 동적 '/repos/:repoId' 보다 먼저 선언해 우선 매칭되게 한다
path: '/repos/new',
name: 'repo-create',
component: () => import('@/pages/relay/RepoCreatePage.vue'),
// 정적 '/projects/new' 는 동적 '/projects/:projectId' 보다 먼저 선언
path: '/projects/new',
name: 'project-create',
component: () => import('@/pages/relay/ProjectCreatePage.vue'),
meta: { requiresAuth: true },
},
{
path: '/repos/:repoId',
name: 'repo-detail',
component: () => import('@/pages/relay/RepoDetailPage.vue'),
path: '/projects/:projectId',
name: 'project-detail',
component: () => import('@/pages/relay/ProjectDetailPage.vue'),
meta: { requiresAuth: true },
},
{
path: '/repos/:repoId/members',
name: 'repo-members',
component: () => import('@/pages/relay/RepoMembersPage.vue'),
path: '/projects/:projectId/members',
name: 'project-members',
component: () => import('@/pages/relay/ProjectMembersPage.vue'),
meta: { requiresAuth: true },
},
{
path: '/repos/:repoId/activity',
name: 'repo-activity',
component: () => import('@/pages/relay/RepoActivityPage.vue'),
path: '/projects/:projectId/activity',
name: 'project-activity',
component: () => import('@/pages/relay/ProjectActivityPage.vue'),
meta: { requiresAuth: true },
},
{
path: '/repos/:repoId/settings',
name: 'repo-settings',
component: () => import('@/pages/relay/RepoSettingsPage.vue'),
path: '/projects/:projectId/settings',
name: 'project-settings',
component: () => import('@/pages/relay/ProjectSettingsPage.vue'),
meta: { requiresAuth: true },
},
{
path: '/repos/:repoId/tasks/new',
path: '/projects/:projectId/tasks/new',
name: 'task-create',
component: () => import('@/pages/relay/TaskCreatePage.vue'),
meta: { requiresAuth: true },
},
{
// 업무 수정 — 생성 폼(TaskCreatePage)을 편집 모드로 재사용
path: '/repos/:repoId/tasks/:taskId/edit',
path: '/projects/:projectId/tasks/:taskId/edit',
name: 'task-edit',
component: () => import('@/pages/relay/TaskCreatePage.vue'),
meta: { requiresAuth: true },
},
{
path: '/repos/:repoId/tasks/:taskId',
path: '/projects/:projectId/tasks/:taskId',
name: 'task-detail',
component: () => import('@/pages/relay/TaskDetailPage.vue'),
meta: { requiresAuth: true },
},
{
// '내 업무' — 담당/지시 저장소 횡단 집계
// '내 업무' — 담당/지시 프로젝트 횡단 집계
path: '/tasks',
name: 'my-tasks',
component: () => import('@/pages/relay/MyTasksPage.vue'),
@@ -103,14 +103,18 @@ router.beforeEach(async (to) => {
await authStore.bootstrap()
}
// 보호 라우트인데 미로그인 → 로그인 페이지로 (로그인 후에는 항상 저장소 목록으로 진입)
// 보호 라우트인데 미로그인 → 로그인 페이지로 (로그인 후에는 항상 프로젝트 목록으로 진입)
if (to.meta.requiresAuth && !authStore.isLoggedIn) {
return { name: 'login' }
}
// 이미 로그인 상태에서 로그인/회원가입 진입 → 저장소 목록으로
if (!to.meta.requiresAuth && authStore.isLoggedIn && (to.name === 'login' || to.name === 'signup')) {
return { path: '/repos' }
// 이미 로그인 상태에서 로그인/회원가입 진입 → 프로젝트 목록으로
if (
!to.meta.requiresAuth &&
authStore.isLoggedIn &&
(to.name === 'login' || to.name === 'signup')
) {
return { path: '/projects' }
}
return true