릴리스: v0.1.6 MariaDB 개발 환경 및 저장소 설정 정리

This commit is contained in:
2026-03-19 14:48:03 +09:00
commit 0c30ae5cb3
52 changed files with 9346 additions and 0 deletions

201
frontend/src/App.vue Normal file
View File

@@ -0,0 +1,201 @@
<script setup>
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useAuthStore } from './stores/auth'
import { toApiUrl } from './lib/runtime'
const route = useRoute()
const router = useRouter()
const auth = useAuthStore()
const isAdmin = computed(() => !!auth.user?.isAdmin)
const avatarUrl = computed(() => {
if (!auth.user?.avatarSrc) return ''
return toApiUrl(auth.user.avatarSrc)
})
const menuOpen = ref(false)
onMounted(async () => {
await auth.refresh()
document.addEventListener('click', onDocumentClick)
})
onUnmounted(() => {
document.removeEventListener('click', onDocumentClick)
})
watch(
() => route.fullPath,
() => {
menuOpen.value = false
}
)
function toggleMenu() {
menuOpen.value = !menuOpen.value
}
function onDocumentClick(event) {
if (!event.target.closest('.user')) {
menuOpen.value = false
}
}
function goProfile() {
menuOpen.value = false
router.push('/profile')
}
async function logout() {
menuOpen.value = false
await auth.logout()
router.push('/')
}
</script>
<template>
<div class="app-shell">
<header class="app-header">
<div class="brand" @click="$router.push('/')">
<span class="brand__title">Tier Maker</span>
<span class="brand__sub">Vue</span>
</div>
<nav class="nav">
<RouterLink to="/" class="nav__link">게임</RouterLink>
<RouterLink to="/me" class="nav__link"> 티어표</RouterLink>
<RouterLink v-if="isAdmin" to="/admin" class="nav__link">관리자</RouterLink>
<RouterLink v-if="!auth.user" to="/login" class="nav__link">로그인</RouterLink>
<div v-else class="user">
<button class="avatarBtn" @click.stop="toggleMenu" :title="auth.user.email">
<img v-if="avatarUrl" :src="avatarUrl" class="avatarImg" alt="avatar" />
<div v-else class="avatarFallback">{{ (auth.user.email || 'U')[0].toUpperCase() }}</div>
</button>
<div v-if="menuOpen" class="menu">
<button class="menuItem" @click="goProfile">프로필</button>
<button class="menuItem" @click="logout">로그아웃</button>
</div>
</div>
</nav>
</header>
<main class="app-main">
<RouterView />
</main>
</div>
</template>
<style scoped>
.app-shell {
min-height: 100vh;
background: radial-gradient(1200px 800px at 20% 10%, rgba(110, 231, 183, 0.18), transparent 55%),
radial-gradient(1000px 700px at 80% 20%, rgba(96, 165, 250, 0.18), transparent 55%),
#0b1220;
color: rgba(255, 255, 255, 0.92);
}
.app-header {
position: sticky;
top: 0;
z-index: 10;
display: flex;
gap: 16px;
align-items: center;
justify-content: space-between;
padding: 14px 18px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(11, 18, 32, 0.72);
backdrop-filter: blur(10px);
}
.brand {
display: flex;
gap: 10px;
align-items: baseline;
cursor: pointer;
user-select: none;
}
.brand__title {
font-weight: 800;
letter-spacing: -0.02em;
}
.brand__sub {
font-size: 12px;
padding: 2px 8px;
border: 1px solid rgba(255, 255, 255, 0.14);
border-radius: 999px;
opacity: 0.9;
}
.nav {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
justify-content: flex-end;
}
.nav__link {
text-decoration: none;
color: rgba(255, 255, 255, 0.86);
padding: 8px 10px;
border-radius: 10px;
border: 1px solid transparent;
}
.nav__link.router-link-active {
border-color: rgba(255, 255, 255, 0.18);
background: rgba(255, 255, 255, 0.06);
}
.app-main {
padding: 20px 18px 60px;
max-width: 1100px;
margin: 0 auto;
}
.user {
position: relative;
}
.avatarBtn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.14);
background: rgba(255, 255, 255, 0.06);
cursor: pointer;
padding: 0;
}
.avatarImg {
width: 100%;
height: 100%;
border-radius: 999px;
object-fit: cover;
}
.avatarFallback {
font-weight: 900;
opacity: 0.9;
}
.menu {
position: absolute;
right: 0;
top: calc(100% + 8px);
width: 160px;
border-radius: 14px;
border: 1px solid rgba(255, 255, 255, 0.14);
background: rgba(11, 18, 32, 0.92);
backdrop-filter: blur(10px);
padding: 6px;
display: grid;
gap: 6px;
}
.menuItem {
text-align: left;
padding: 10px 10px;
border-radius: 12px;
border: 1px solid transparent;
background: rgba(255, 255, 255, 0.06);
color: rgba(255, 255, 255, 0.92);
cursor: pointer;
font-weight: 800;
}
.menuItem:hover {
background: rgba(255, 255, 255, 0.09);
}
</style>