first commit

This commit is contained in:
2026-06-16 17:12:08 +09:00
commit e1bc5a1dfc
257 changed files with 49823 additions and 0 deletions
+223
View File
@@ -0,0 +1,223 @@
<script setup lang="ts">
// 저장소 헤더 카드 — 상세/멤버/활동/설정 화면이 공유
import { computed } from 'vue'
import { RouterLink } from 'vue-router'
import type { Repo, User } from '@/mock/relay.mock'
interface Props {
repo: Repo
/** 메타 우측 텍스트(예: "2시간 전 업데이트", "멤버 5명") */
metaText: string
/** 아바타 스택 override (기본: repo.members) */
members?: User[]
/** +N 표기 (기본: repo.moreCount) */
moreCount?: number
}
const props = defineProps<Props>()
const avatars = computed(() => props.members ?? props.repo.members)
const extra = computed(() => props.moreCount ?? props.repo.moreCount)
</script>
<template>
<div class="repo-head">
<div class="repo-ico">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20" />
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" />
</svg>
</div>
<div class="rh-main">
<div class="rh-title">
<span class="rh-name">{{ repo.name }}</span>
<span
class="vis"
:class="repo.visibility"
>
<svg
v-if="repo.visibility === 'private'"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2.5"
>
<rect
x="3"
y="11"
width="18"
height="11"
rx="2"
/><path d="M7 11V7a5 5 0 0 1 10 0v4" />
</svg>
<svg
v-else
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2.2"
>
<circle
cx="12"
cy="12"
r="10"
/><path d="M2 12h20M12 2a15 15 0 0 1 0 20M12 2a15 15 0 0 0 0 20" />
</svg>
{{ repo.visibility === 'private' ? '비공개' : '공개' }}
</span>
</div>
<div class="rh-slug">
{{ repo.owner }} / {{ repo.id }}
</div>
<div class="rh-desc">
{{ repo.desc }}
</div>
<div class="rh-meta">
<span class="mi">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line
x1="6"
y1="3"
x2="6"
y2="15"
/><circle
cx="18"
cy="6"
r="3"
/><circle
cx="6"
cy="18"
r="3"
/><path d="M18 9a9 9 0 0 1-9 9" />
</svg>
{{ repo.branch }}
</span>
<span class="mi avstack">
<span
v-for="m in avatars"
:key="m.id"
class="avatar"
:class="m.color"
>{{ m.initial }}</span>
<span
v-if="extra > 0"
class="avatar av-more"
>+{{ extra }}</span>
</span>
<span class="mi">{{ metaText }}</span>
</div>
<!-- 진행률 스트립 추가 영역 -->
<slot name="extra" />
</div>
<div class="rh-actions">
<slot name="actions">
<RouterLink
class="btn primary"
:to="`/repos/${repo.id}/tasks/new`"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2.2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M5 12h14M12 5v14" />
</svg>
업무
</RouterLink>
</slot>
</div>
</div>
</template>
<style scoped>
.repo-head {
display: flex;
align-items: flex-start;
gap: 1rem;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 0.625rem;
padding: 1.25rem 1.375rem;
box-shadow: var(--shadow-sm);
}
.repo-ico {
width: 2.875rem;
height: 2.875rem;
border-radius: 0.625rem;
background: var(--accent-weak);
color: var(--accent);
display: grid;
place-items: center;
flex-shrink: 0;
}
.repo-ico svg {
width: 1.5rem;
height: 1.5rem;
}
.rh-main {
flex: 1;
min-width: 0;
}
.rh-title {
display: flex;
align-items: center;
gap: 0.625rem;
}
.rh-name {
font-size: 1.125rem;
font-weight: 700;
letter-spacing: -0.01875rem;
white-space: nowrap;
}
.rh-slug {
font-size: 0.781rem;
color: var(--text-3);
margin-top: 0.3125rem;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.rh-desc {
font-size: 0.844rem;
color: var(--text-2);
margin-top: 0.375rem;
}
.rh-meta {
display: flex;
align-items: center;
gap: 0.9375rem;
margin-top: 0.75rem;
font-size: 0.75rem;
color: var(--text-3);
}
.rh-meta .mi {
display: flex;
align-items: center;
gap: 0.3125rem;
white-space: nowrap;
}
.rh-meta .mi svg {
width: 0.8125rem;
height: 0.8125rem;
}
.rh-actions {
display: flex;
align-items: center;
gap: 0.5rem;
flex-shrink: 0;
}
</style>
+88
View File
@@ -0,0 +1,88 @@
<script setup lang="ts">
// 저장소 서브탭 — 업무/멤버/활동/설정
import { RouterLink } from 'vue-router'
interface Props {
repoId: string
active: 'tasks' | 'members' | 'activity' | 'settings'
taskCount: number
memberCount: number
}
defineProps<Props>()
</script>
<template>
<nav class="tabs">
<RouterLink
:to="`/repos/${repoId}`"
:class="{ active: active === 'tasks' }"
>
<span class="tb-label">업무</span> <span class="tb-count">{{ taskCount }}</span>
</RouterLink>
<RouterLink
:to="`/repos/${repoId}/members`"
:class="{ active: active === 'members' }"
>
<span class="tb-label">멤버</span> <span class="tb-count">{{ memberCount }}</span>
</RouterLink>
<RouterLink
:to="`/repos/${repoId}/activity`"
:class="{ active: active === 'activity' }"
>
<span class="tb-label">활동</span>
</RouterLink>
<RouterLink
:to="`/repos/${repoId}/settings`"
:class="{ active: active === 'settings' }"
>
<span class="tb-label">설정</span>
</RouterLink>
</nav>
</template>
<style scoped>
.tabs {
display: flex;
gap: 0.125rem;
border-bottom: 1px solid var(--border);
margin: 1.25rem 0 1.125rem;
}
.tabs a {
padding: 0.625rem 0.875rem;
font-size: 0.844rem;
font-weight: 600;
color: var(--text-2);
text-decoration: none;
border-bottom: 2px solid transparent;
margin-bottom: -1px;
display: flex;
align-items: center;
gap: 0.375rem;
white-space: nowrap;
}
.tabs a:hover {
color: var(--text);
}
.tabs a.active {
color: var(--accent);
border-bottom-color: var(--accent);
}
.tabs a .tb-count {
font-size: 0.6875rem;
font-weight: 600;
color: var(--text-2);
background: #f1f2f4;
border-radius: 20px;
padding: 0 0.4375rem;
height: 1.0625rem;
min-width: 1.0625rem;
display: inline-flex;
align-items: center;
justify-content: center;
line-height: 1;
}
.tabs a.active .tb-count {
background: var(--accent-weak);
color: var(--accent);
}
</style>