feat: 관리자 대시보드 사용자 모달에 반려동물 탭 추가

- /admin/user/<id> API에 pets 데이터 추가
- 사용자 상세 모달에 🐾 반려동물 탭 추가
- 반려동물 사진, 이름, 종류, 품종, 성별, 등록일 표시
This commit is contained in:
thug0bin 2026-03-02 14:51:46 +09:00
parent 1cebb02ec6
commit 52a4f69abc
4 changed files with 91 additions and 2 deletions

View File

@ -1621,7 +1621,38 @@ def admin_user_detail(user_id):
except Exception as interest_error:
logging.warning(f"관심상품 조회 실패 (user {user_id}): {interest_error}")
# 8. 응답 생성
# 8. 반려동물 조회
pets = []
try:
cursor.execute("""
SELECT id, name, species, breed, gender, birth_date, age_months,
weight, photo_url, notes, created_at
FROM pets
WHERE user_id = ? AND is_active = TRUE
ORDER BY created_at DESC
""", (user_id,))
for row in cursor.fetchall():
species_label = '강아지 🐕' if row['species'] == 'dog' else ('고양이 🐈' if row['species'] == 'cat' else '기타')
gender_label = '♂ 남아' if row['gender'] == 'male' else ('♀ 여아' if row['gender'] == 'female' else '')
pets.append({
'id': row['id'],
'name': row['name'],
'species': row['species'],
'species_label': species_label,
'breed': row['breed'],
'gender': row['gender'],
'gender_label': gender_label,
'birth_date': row['birth_date'],
'age_months': row['age_months'],
'weight': float(row['weight']) if row['weight'] else None,
'photo_url': row['photo_url'],
'notes': row['notes'],
'created_at': utc_to_kst_str(row['created_at'])
})
except Exception as pet_error:
logging.warning(f"반려동물 조회 실패 (user {user_id}): {pet_error}")
# 9. 응답 생성
return jsonify({
'success': True,
'user': {
@ -1647,7 +1678,8 @@ def admin_user_detail(user_id):
'purchases': purchases,
'prescriptions': prescriptions,
'pos_customer': pos_customer,
'interests': interests
'interests': interests,
'pets': pets
})
except Exception as e:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 MiB

View File

@ -913,6 +913,9 @@
<button onclick="switchTab('interests')" id="tab-interests" class="tab-btn" style="padding: 12px 20px; border: none; background: none; font-size: 15px; font-weight: 600; cursor: pointer; border-bottom: 3px solid transparent; color: #868e96;">
💝 관심 (${data.interests ? data.interests.length : 0})
</button>
<button onclick="switchTab('pets')" id="tab-pets" class="tab-btn" style="padding: 12px 20px; border: none; background: none; font-size: 15px; font-weight: 600; cursor: pointer; border-bottom: 3px solid transparent; color: #868e96;">
🐾 반려동물 (${data.pets ? data.pets.length : 0})
</button>
</div>
<!-- 정렬 버튼 (구매 이력용) -->
@ -1070,6 +1073,53 @@
html += '<p style="text-align: center; padding: 40px; color: #868e96;">💝 관심 상품이 없습니다<br><small>마일리지 적립 시 AI 추천에서 "관심있어요"를 누르면 여기에 표시됩니다</small></p>';
}
html += `
</div>
<!-- 반려동물 탭 -->
<div id="tab-content-pets" class="tab-content" style="display: none;">
`;
// 반려동물 렌더링
const pets = data.pets || [];
if (pets.length > 0) {
html += '<div style="display: grid; gap: 16px;">';
pets.forEach(pet => {
const photoHtml = pet.photo_url
? `<img src="${pet.photo_url}" alt="${pet.name}" style="width: 80px; height: 80px; border-radius: 50%; object-fit: cover;">`
: `<div style="width: 80px; height: 80px; border-radius: 50%; background: linear-gradient(135deg, #fbbf24, #f59e0b); display: flex; align-items: center; justify-content: center; font-size: 36px;">${pet.species === 'dog' ? '🐕' : (pet.species === 'cat' ? '🐈' : '🐾')}</div>`;
html += `
<div style="border: 1px solid #e9ecef; border-radius: 16px; padding: 20px; display: flex; gap: 20px; align-items: center; border-left: 4px solid #f59e0b;">
<div style="flex-shrink: 0;">
${photoHtml}
</div>
<div style="flex: 1;">
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
<span style="font-size: 20px; font-weight: 700; color: #212529;">${pet.name}</span>
<span style="background: ${pet.species === 'dog' ? '#dbeafe' : '#fce7f3'}; color: ${pet.species === 'dog' ? '#1e40af' : '#9d174d'}; font-size: 12px; font-weight: 600; padding: 4px 10px; border-radius: 20px;">
${pet.species_label}
</span>
</div>
<div style="display: flex; flex-wrap: wrap; gap: 12px; font-size: 14px; color: #6b7280;">
${pet.breed ? `<span>🏷️ ${pet.breed}</span>` : ''}
${pet.gender_label ? `<span>${pet.gender_label}</span>` : ''}
${pet.weight ? `<span>⚖️ ${pet.weight}kg</span>` : ''}
${pet.age_months ? `<span>🎂 ${pet.age_months}개월</span>` : ''}
</div>
${pet.notes ? `<div style="margin-top: 8px; font-size: 13px; color: #9ca3af; background: #f9fafb; padding: 8px 12px; border-radius: 8px;">📝 ${pet.notes}</div>` : ''}
<div style="margin-top: 10px; font-size: 12px; color: #d1d5db;">
등록일: ${pet.created_at}
</div>
</div>
</div>
`;
});
html += '</div>';
} else {
html += '<p style="text-align: center; padding: 40px; color: #868e96;">🐾 등록된 반려동물이 없습니다<br><small>고객이 마이페이지에서 반려동물을 등록하면 여기에 표시됩니다</small></p>';
}
html += `
</div>
`;
@ -1727,6 +1777,13 @@
// KIMS 약물 상호작용 체크
// ═══════════════════════════════════════════════════
function escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
async function checkDrugInteraction(drugCodes, preSerial) {
// drugCodes가 문자열로 넘어올 수 있음
if (typeof drugCodes === 'string') {