Files
dognation_PT/src/layouts/MainLayout.vue
T
sb 3bebe77cab
CI / build (push) Failing after 15m8s
chore: UI 문구 '우리 개' → '우리 강아지'
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 19:00:34 +09:00

165 lines
5.4 KiB
Vue

<template>
<q-layout view="hHh lpR fFf">
<q-header class="bg-primary text-white safe-top">
<q-toolbar>
<q-toolbar-title class="text-weight-bold">
<q-icon name="pets" class="q-mr-sm" />산책갈개
</q-toolbar-title>
<q-chip dense color="white" text-color="primary" icon="bolt">
{{ tierLabel }}
</q-chip>
<!-- 받은 매칭 알림 -->
<q-btn flat round dense icon="notifications" class="q-mr-xs">
<q-badge v-if="matching.received.length" color="red" floating>
{{ matching.received.length }}
</q-badge>
<q-menu anchor="bottom right" self="top right">
<q-list style="min-width: 260px">
<q-item-label header>받은 매칭 신청</q-item-label>
<q-item v-if="!matching.received.length">
<q-item-section class="text-grey-6">받은 신청이 없어요.</q-item-section>
</q-item>
<q-item v-for="r in matching.received" :key="r.id">
<q-item-section avatar>
<q-avatar color="secondary" text-color="accent" icon="pets" />
</q-item-section>
<q-item-section>
<q-item-label class="text-weight-medium">{{ r.requesterDogName }}</q-item-label>
<q-item-label caption>{{ r.requesterDogBreed || '견종 미상' }} · 매칭 신청</q-item-label>
</q-item-section>
<q-item-section side>
<div class="row q-gutter-xs">
<q-btn dense unelevated color="primary" label="수락" @click="onAccept(r.id)" />
<q-btn dense flat color="grey-7" label="거절" @click="onReject(r.id)" />
</div>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
<q-btn flat round dense icon="account_circle">
<q-menu>
<q-list style="min-width: 160px">
<q-item>
<q-item-section>
<q-item-label class="text-weight-medium">{{ auth.member?.nickname }}</q-item-label>
<q-item-label caption>{{ auth.member?.email }}</q-item-label>
</q-item-section>
</q-item>
<q-separator />
<q-item clickable v-close-popup @click="onLogout">
<q-item-section avatar><q-icon name="logout" /></q-item-section>
<q-item-section>로그아웃</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</q-toolbar>
</q-header>
<q-page-container>
<router-view />
</q-page-container>
<q-footer class="bg-white text-grey-8 safe-bottom">
<!-- FREE 티어 하단 배너 광고 (탭바 ). 유료 티어는 숨김 콘텐츠 공간 회수 -->
<AdBanner />
<q-tabs
v-model="tab"
active-color="primary"
indicator-color="primary"
class="text-grey-6"
>
<q-route-tab name="home" :to="{ name: 'home' }" icon="map" label="산책 스팟" />
<q-route-tab name="profile" :to="{ name: 'profile' }" icon="pets" label="우리 강아지" />
</q-tabs>
</q-footer>
<!-- 매칭 수락 1:1 채팅 -->
<MatchChatDialog v-if="matchChatId != null" v-model="matchChatOpen" :match-id="matchChatId" />
</q-layout>
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useQuasar } from 'quasar'
import AdBanner from '@/components/AdBanner.vue'
import MatchChatDialog from '@/components/MatchChatDialog.vue'
import { useSubscriptionStore } from '@/stores/subscription'
import { useAuthStore } from '@/stores/auth'
import { useDogsStore } from '@/stores/dogs'
import { useMatchingStore } from '@/stores/matching'
import { ApiError } from '@/api/http'
const tab = ref('home')
const subscription = useSubscriptionStore()
const auth = useAuthStore()
const dogs = useDogsStore()
const matching = useMatchingStore()
const router = useRouter()
const $q = useQuasar()
const matchChatOpen = ref(false)
const matchChatId = ref<number | null>(null)
onMounted(async () => {
await dogs.loadMyDogs()
if (auth.member && dogs.currentDogId != null) {
matching.connect(auth.member.id, dogs.currentDogId)
}
})
onBeforeUnmount(() => matching.disconnect())
// 수락(내가/상대가) → 1:1 채팅 열기
watch(
() => matching.openChatMatchId,
(id) => {
if (id != null) {
matchChatId.value = id
matchChatOpen.value = true
matching.openChatMatchId = null
}
},
)
async function onAccept(id: number) {
try {
await matching.accept(id)
} catch (e) {
notifyError(e)
}
}
async function onReject(id: number) {
try {
await matching.reject(id)
} catch (e) {
notifyError(e)
}
}
function notifyError(e: unknown) {
const message = e instanceof ApiError ? e.message : '처리 중 오류가 발생했습니다.'
$q.notify({ color: 'negative', message, icon: 'error', position: 'top' })
}
const tierLabel = computed(() => {
switch (subscription.tier) {
case 'PREMIUM':
return 'PREMIUM'
case 'AD_FREE':
return '광고제거'
default:
return 'FREE'
}
})
async function onLogout() {
matching.disconnect()
await auth.logout()
dogs.reset()
router.replace({ name: 'login' })
}
</script>