From eb512016691fad8062c256139276dbd7813c4a8a Mon Sep 17 00:00:00 2001 From: sb Date: Sat, 11 Jul 2026 22:14:42 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B0=95=EC=95=84=EC=A7=80=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=A4=91=EC=84=B1=ED=99=94=20=ED=95=84=EC=88=98=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20+=20AI=20=EC=B6=94=EC=B2=9C=20=EC=A4=91?= =?UTF-8?q?=EC=84=B1=ED=99=94=20=EC=A1=B0=EA=B1=B4=20=EC=98=B5=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 등록 폼: 중성화 여부(완료/안 함) 필수 토글, 미선택 시 등록 불가 - AI 추천: 중성화 찾기 옵션(상관없음/완료/안 함) → 조건 강아지 중에서만 추천 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/api/spots.ts | 7 +++++-- src/pages/HomePage.vue | 24 +++++++++++++++++++++++- src/pages/ProfilePage.vue | 35 ++++++++++++++++++++++++++++++++--- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/api/spots.ts b/src/api/spots.ts index 0892905..56f9316 100644 --- a/src/api/spots.ts +++ b/src/api/spots.ts @@ -40,8 +40,11 @@ export const spotsApi = { list: () => http.get('/api/spots'), mates: (spotId: number, userId?: number) => http.get(`/api/spots/${spotId}/mates${userId ? `?userId=${userId}` : ''}`), - aiMates: (spotId: number, dogId: number) => - http.get(`/api/spots/${spotId}/ai-mates?dogId=${dogId}`), + aiMates: (spotId: number, dogId: number, neutered?: boolean) => + http.get( + `/api/spots/${spotId}/ai-mates?dogId=${dogId}` + + (neutered != null ? `&neutered=${neutered}` : ''), + ), frequentBreeds: (spotId: number) => http.get<{ breeds: string[] }>(`/api/spots/${spotId}/frequent-breeds`), reviews: (spotId: number) => http.get(`/api/spots/${spotId}/reviews`), diff --git a/src/pages/HomePage.vue b/src/pages/HomePage.vue index 3a70139..3bb1f54 100644 --- a/src/pages/HomePage.vue +++ b/src/pages/HomePage.vue @@ -40,6 +40,26 @@ · AI 분석으로 실제와 다를 수 있어요 + +
+ 중성화 + +
친구 찾는 중… @@ -153,6 +173,7 @@ const loadError = ref(false) const aiMates = ref([]) const aiLoading = ref(false) const aiTried = ref(false) +const aiNeutered = ref<'any' | 'yes' | 'no'>('any') // AI 찾기 옵션: 중성화 조건 onMounted(load) @@ -218,8 +239,9 @@ async function loadAiMates() { if (!currentSpot.value || dogs.currentDogId == null) return aiLoading.value = true aiTried.value = true + const neutered = aiNeutered.value === 'yes' ? true : aiNeutered.value === 'no' ? false : undefined try { - aiMates.value = await spotsApi.aiMates(currentSpot.value.id, dogs.currentDogId) + aiMates.value = await spotsApi.aiMates(currentSpot.value.id, dogs.currentDogId, neutered) } catch { aiMates.value = [] } finally { diff --git a/src/pages/ProfilePage.vue b/src/pages/ProfilePage.vue index 3056ab4..8b501a5 100644 --- a/src/pages/ProfilePage.vue +++ b/src/pages/ProfilePage.vue @@ -102,6 +102,22 @@ emit-value map-options /> +
+
중성화 여부 *
+ +
@@ -110,7 +126,7 @@ unelevated label="등록" :loading="creating" - :disable="!form.name" + :disable="!form.name || form.neutered === null" @click="onCreate" /> @@ -141,10 +157,16 @@ const sizeOptions = [ { label: '중형견', value: 'MEDIUM' }, { label: '대형견', value: 'LARGE' }, ] -const form = reactive<{ name: string; breed: string; size: string | null }>({ +const form = reactive<{ + name: string + breed: string + size: string | null + neutered: boolean | null +}>({ name: '', breed: '', size: null, + neutered: null, }) onMounted(async () => { @@ -208,13 +230,20 @@ function openCreate() { form.name = '' form.breed = '' form.size = null + form.neutered = null createOpen.value = true } async function onCreate() { + if (form.neutered === null) return creating.value = true try { - await dogs.createDog({ name: form.name, breed: form.breed || null, size: form.size }) + await dogs.createDog({ + name: form.name, + breed: form.breed || null, + size: form.size, + neutered: form.neutered, + }) createOpen.value = false $q.notify({ color: 'primary', message: `${form.name} 등록 완료!`, icon: 'pets', position: 'top' }) } catch (e) {