feat: 반려동물 정보 표시 기능 추가

API:
- /api/admin/pos-live에 pets 배열 추가
- 적립된 회원의 반려동물 정보 조회 (이름, 종류, 품종, 사진)

테이블 (바깥):
- 적립 열에 반려동물 아이콘 표시 (🐕🐈)

상세 패널:
- 반려동물 카드 섹션 추가
- 사진 + 이름 + 품종 표시
- 노란색 그라데이션 카드 스타일
This commit is contained in:
thug0bin 2026-03-02 16:11:59 +09:00
parent 695c1f707f
commit a7b3d5b7e0
3 changed files with 136 additions and 4 deletions

View File

@ -4933,9 +4933,9 @@ def api_admin_pos_live():
qr_record = sqlite_cursor.fetchone()
qr_issued = bool(qr_record)
# SQLite에서 적립 사용자 조회
# SQLite에서 적립 사용자 조회 (user_id 포함)
sqlite_cursor.execute("""
SELECT u.nickname, u.phone, ct.claimable_points
SELECT u.id as user_id, u.nickname, u.phone, ct.claimable_points
FROM claim_tokens ct
LEFT JOIN users u ON ct.claimed_by_user_id = u.id
WHERE ct.transaction_id = ? AND ct.claimed_at IS NOT NULL
@ -4943,7 +4943,9 @@ def api_admin_pos_live():
claimed_user = sqlite_cursor.fetchone()
# 적립 사용자 정보 분리
claimed_user_id = None
if claimed_user and claimed_user['nickname'] and claimed_user['phone']:
claimed_user_id = claimed_user['user_id']
claimed_name = claimed_user['nickname']
claimed_phone = claimed_user['phone']
claimed_points = claimed_user['claimable_points']
@ -4952,6 +4954,23 @@ def api_admin_pos_live():
claimed_phone = ""
claimed_points = 0
# 반려동물 정보 조회
pets_list = []
if claimed_user_id:
sqlite_cursor.execute("""
SELECT name, species, breed, photo_url
FROM pets
WHERE user_id = ? AND is_active = 1
""", (claimed_user_id,))
pets_rows = sqlite_cursor.fetchall()
for pet in pets_rows:
pets_list.append({
'name': pet['name'],
'species': pet['species'], # dog, cat
'breed': pet['breed'] or '',
'photo_url': pet['photo_url'] or ''
})
# 결제수단 판별
card_amt = float(card_total) if card_total else 0.0
cash_amt = float(cash_total) if cash_total else 0.0
@ -4989,7 +5008,8 @@ def api_admin_pos_live():
'claimed_name': claimed_name,
'claimed_phone': claimed_phone,
'claimed_points': claimed_points,
'qr_issued': qr_issued
'qr_issued': qr_issued,
'pets': pets_list # 반려동물 정보
})
return jsonify({

23
backend/check_pets.py Normal file
View File

@ -0,0 +1,23 @@
import sqlite3
conn = sqlite3.connect('db/mileage.db')
c = conn.cursor()
# 테이블 구조
c.execute("SELECT sql FROM sqlite_master WHERE name='pets'")
print("=== PETS TABLE SCHEMA ===")
print(c.fetchone())
# 샘플 데이터
c.execute("SELECT * FROM pets LIMIT 5")
print("\n=== SAMPLE DATA ===")
for row in c.fetchall():
print(row)
# 컬럼명
c.execute("PRAGMA table_info(pets)")
print("\n=== COLUMNS ===")
for col in c.fetchall():
print(col)
conn.close()

View File

@ -637,6 +637,60 @@
margin-top: 2px;
}
/* ── 반려동물 카드 ── */
.detail-pets-section {
margin: 16px 0;
}
.pets-grid {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.pet-card {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
background: linear-gradient(135deg, #fef3c7, #fde68a);
border-radius: 12px;
flex: 1;
min-width: 140px;
max-width: 200px;
}
.pet-photo {
width: 44px;
height: 44px;
border-radius: 50%;
object-fit: cover;
border: 2px solid #fff;
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
}
.pet-photo-placeholder {
width: 44px;
height: 44px;
border-radius: 50%;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
}
.pet-info {
flex: 1;
min-width: 0;
}
.pet-name {
font-weight: 700;
font-size: 14px;
color: #92400e;
}
.pet-breed {
font-size: 11px;
color: #a16207;
margin-top: 2px;
}
/* ── 반응형 ── */
@media (max-width: 768px) {
.control-section {
@ -852,8 +906,15 @@
let claimedHtml = '-';
if (sale.claimed_name) {
// 반려동물 아이콘 생성
let petIcons = '';
if (sale.pets && sale.pets.length > 0) {
petIcons = sale.pets.map(p =>
p.species === 'dog' ? '🐕' : p.species === 'cat' ? '🐈' : '🐾'
).join('');
}
claimedHtml = `
<div class="claimed-info">${sale.claimed_name}</div>
<div class="claimed-info">${sale.claimed_name} ${petIcons}</div>
<div class="claimed-phone">${formatPhone(sale.claimed_phone)}</div>
`;
}
@ -986,6 +1047,33 @@
</div>`
: '';
// 반려동물 섹션
let petsSection = '';
if (sale.pets && sale.pets.length > 0) {
const petsHtml = sale.pets.map(pet => {
const speciesIcon = pet.species === 'dog' ? '🐕' : pet.species === 'cat' ? '🐈' : '🐾';
const speciesName = pet.species === 'dog' ? '강아지' : pet.species === 'cat' ? '고양이' : '반려동물';
const photoHtml = pet.photo_url
? `<img src="${pet.photo_url}" class="pet-photo" alt="${pet.name}">`
: `<div class="pet-photo-placeholder">${speciesIcon}</div>`;
return `
<div class="pet-card">
${photoHtml}
<div class="pet-info">
<div class="pet-name">${speciesIcon} ${pet.name}</div>
<div class="pet-breed">${pet.breed || speciesName}</div>
</div>
</div>
`;
}).join('');
petsSection = `
<div class="detail-pets-section">
<div class="detail-items-title">🐾 반려동물</div>
<div class="pets-grid">${petsHtml}</div>
</div>
`;
}
// 기본 정보 표시
document.getElementById('detailContent').innerHTML = `
<!-- 액션 버튼 -->
@ -1029,6 +1117,7 @@
</div>
</div>
${claimedInfo}
${petsSection}
<div class="detail-items-title">📦 품목 목록</div>
<div id="itemsList">
<div class="loading"><div class="spinner"></div>품목 로딩 중...</div>