- pets 테이블 추가 (이름, 종류, 품종, 사진 등) - 반려동물 CRUD API (/api/pets) - 확장 마이페이지 (/mypage) - 카카오 로그인 기반 - 기존 마이페이지에 퀵 메뉴 추가 (반려동물/쿠폰/구매내역/내정보) - 카카오 로그인 시 세션에 user_id 저장 - 동물약 APC 매핑 가이드 문서 추가
891 lines
28 KiB
HTML
891 lines
28 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ko">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
<meta name="theme-color" content="#6366f1">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||
<meta name="apple-mobile-web-app-title" content="청춘약국">
|
||
<link rel="manifest" href="/static/manifest.json">
|
||
<link rel="apple-touch-icon" href="/static/icons/icon-192.png">
|
||
<title>마이페이지 - 청춘약국</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
|
||
background: #f5f7fa;
|
||
min-height: 100vh;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
.app-container {
|
||
background: #ffffff;
|
||
min-height: 100vh;
|
||
max-width: 420px;
|
||
margin: 0 auto;
|
||
box-shadow: 0 0 20px rgba(0,0,0,0.05);
|
||
}
|
||
|
||
/* 헤더 */
|
||
.header {
|
||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||
padding: 20px 24px 100px;
|
||
position: relative;
|
||
}
|
||
|
||
.header-top {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.header-title {
|
||
color: white;
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.btn-logout {
|
||
color: rgba(255,255,255,0.8);
|
||
font-size: 14px;
|
||
text-decoration: none;
|
||
padding: 8px 12px;
|
||
border-radius: 8px;
|
||
transition: background 0.2s;
|
||
}
|
||
|
||
.btn-logout:hover {
|
||
background: rgba(255,255,255,0.1);
|
||
}
|
||
|
||
/* 프로필 카드 */
|
||
.profile-card {
|
||
background: white;
|
||
border-radius: 20px;
|
||
margin: -80px 16px 16px;
|
||
padding: 24px;
|
||
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
||
position: relative;
|
||
z-index: 10;
|
||
}
|
||
|
||
.profile-info {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.profile-avatar {
|
||
width: 64px;
|
||
height: 64px;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, #6366f1, #8b5cf6);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 28px;
|
||
color: white;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.profile-avatar img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.profile-details h2 {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
color: #1f2937;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.profile-details p {
|
||
color: #6b7280;
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 통계 그리드 */
|
||
.stats-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 12px;
|
||
padding-top: 20px;
|
||
border-top: 1px solid #f3f4f6;
|
||
}
|
||
|
||
.stat-item {
|
||
text-align: center;
|
||
}
|
||
|
||
.stat-icon {
|
||
width: 44px;
|
||
height: 44px;
|
||
border-radius: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin: 0 auto 8px;
|
||
font-size: 20px;
|
||
}
|
||
|
||
.stat-icon.purple { background: #ede9fe; }
|
||
.stat-icon.blue { background: #dbeafe; }
|
||
.stat-icon.pink { background: #fce7f3; }
|
||
|
||
.stat-value {
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: #1f2937;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 12px;
|
||
color: #9ca3af;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
/* 섹션 */
|
||
.section {
|
||
background: white;
|
||
margin: 16px;
|
||
border-radius: 16px;
|
||
padding: 20px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
||
}
|
||
|
||
.section-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: #1f2937;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.section-action {
|
||
color: #6366f1;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
text-decoration: none;
|
||
}
|
||
|
||
/* 반려동물 카드 */
|
||
.pet-card {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
padding: 16px;
|
||
background: #f9fafb;
|
||
border-radius: 12px;
|
||
margin-bottom: 12px;
|
||
cursor: pointer;
|
||
transition: background 0.2s;
|
||
}
|
||
|
||
.pet-card:hover {
|
||
background: #f3f4f6;
|
||
}
|
||
|
||
.pet-photo {
|
||
width: 56px;
|
||
height: 56px;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, #fbbf24, #f59e0b);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 24px;
|
||
overflow: hidden;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.pet-photo img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.pet-info {
|
||
flex: 1;
|
||
}
|
||
|
||
.pet-name {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #1f2937;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.pet-details {
|
||
font-size: 13px;
|
||
color: #6b7280;
|
||
}
|
||
|
||
.pet-arrow {
|
||
color: #d1d5db;
|
||
font-size: 18px;
|
||
}
|
||
|
||
/* 반려동물 추가 버튼 */
|
||
.add-pet-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
width: 100%;
|
||
padding: 16px;
|
||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 12px;
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: transform 0.2s, box-shadow 0.2s;
|
||
}
|
||
|
||
.add-pet-btn:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
|
||
}
|
||
|
||
/* 메뉴 리스트 */
|
||
.menu-list {
|
||
list-style: none;
|
||
}
|
||
|
||
.menu-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 16px 0;
|
||
border-bottom: 1px solid #f3f4f6;
|
||
cursor: pointer;
|
||
transition: background 0.2s;
|
||
}
|
||
|
||
.menu-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.menu-item:hover {
|
||
background: #f9fafb;
|
||
margin: 0 -20px;
|
||
padding: 16px 20px;
|
||
}
|
||
|
||
.menu-left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.menu-icon {
|
||
font-size: 20px;
|
||
}
|
||
|
||
.menu-text {
|
||
font-size: 15px;
|
||
color: #374151;
|
||
}
|
||
|
||
.menu-badge {
|
||
background: #fef3c7;
|
||
color: #92400e;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
padding: 4px 8px;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.menu-arrow {
|
||
color: #d1d5db;
|
||
}
|
||
|
||
/* 모달 */
|
||
.modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0,0,0,0.5);
|
||
display: none;
|
||
align-items: flex-end;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.modal-overlay.active {
|
||
display: flex;
|
||
}
|
||
|
||
.modal-content {
|
||
background: white;
|
||
border-radius: 24px 24px 0 0;
|
||
width: 100%;
|
||
max-width: 420px;
|
||
max-height: 90vh;
|
||
overflow-y: auto;
|
||
padding: 24px;
|
||
animation: slideUp 0.3s ease;
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from { transform: translateY(100%); }
|
||
to { transform: translateY(0); }
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.modal-title {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
color: #1f2937;
|
||
}
|
||
|
||
.modal-close {
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 50%;
|
||
background: #f3f4f6;
|
||
border: none;
|
||
font-size: 18px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* 폼 스타일 */
|
||
.form-group {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.form-label {
|
||
display: block;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #374151;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.form-input {
|
||
width: 100%;
|
||
padding: 14px 16px;
|
||
border: 2px solid #e5e7eb;
|
||
border-radius: 12px;
|
||
font-size: 15px;
|
||
transition: border-color 0.2s;
|
||
}
|
||
|
||
.form-input:focus {
|
||
outline: none;
|
||
border-color: #6366f1;
|
||
}
|
||
|
||
/* 종류 선택 */
|
||
.species-options {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.species-option {
|
||
flex: 1;
|
||
padding: 20px;
|
||
border: 2px solid #e5e7eb;
|
||
border-radius: 16px;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.species-option:hover {
|
||
border-color: #c7d2fe;
|
||
}
|
||
|
||
.species-option.selected {
|
||
border-color: #6366f1;
|
||
background: #eef2ff;
|
||
}
|
||
|
||
.species-option .icon {
|
||
font-size: 40px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.species-option .label {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #374151;
|
||
}
|
||
|
||
/* 사진 업로드 */
|
||
.photo-upload {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.photo-preview {
|
||
width: 120px;
|
||
height: 120px;
|
||
border-radius: 50%;
|
||
background: #f3f4f6;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 48px;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
transition: background 0.2s;
|
||
}
|
||
|
||
.photo-preview:hover {
|
||
background: #e5e7eb;
|
||
}
|
||
|
||
.photo-preview img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.photo-hint {
|
||
font-size: 13px;
|
||
color: #9ca3af;
|
||
}
|
||
|
||
/* 제출 버튼 */
|
||
.submit-btn {
|
||
width: 100%;
|
||
padding: 16px;
|
||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 12px;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
margin-top: 24px;
|
||
transition: transform 0.2s, box-shadow 0.2s;
|
||
}
|
||
|
||
.submit-btn:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
|
||
}
|
||
|
||
.submit-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
transform: none;
|
||
box-shadow: none;
|
||
}
|
||
|
||
/* 빈 상태 */
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 32px 16px;
|
||
color: #9ca3af;
|
||
}
|
||
|
||
.empty-state .icon {
|
||
font-size: 48px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.empty-state p {
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 로딩 */
|
||
.loading {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 40px;
|
||
}
|
||
|
||
.spinner {
|
||
width: 32px;
|
||
height: 32px;
|
||
border: 3px solid #e5e7eb;
|
||
border-top-color: #6366f1;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
|
||
@keyframes spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="app-container">
|
||
<!-- 헤더 -->
|
||
<div class="header">
|
||
<div class="header-top">
|
||
<h1 class="header-title">마이페이지</h1>
|
||
<a href="/logout" class="btn-logout">로그아웃</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 프로필 카드 -->
|
||
<div class="profile-card">
|
||
<div class="profile-info">
|
||
<div class="profile-avatar">
|
||
{% if user.profile_image_url %}
|
||
<img src="{{ user.profile_image_url }}" alt="프로필">
|
||
{% else %}
|
||
😊
|
||
{% endif %}
|
||
</div>
|
||
<div class="profile-details">
|
||
<h2>{{ user.nickname or '회원' }}님</h2>
|
||
<p>{{ user.phone or '전화번호 미등록' }}</p>
|
||
</div>
|
||
</div>
|
||
<div class="stats-grid">
|
||
<div class="stat-item">
|
||
<div class="stat-icon purple">🎁</div>
|
||
<div class="stat-value">{{ '{:,}'.format(user.mileage_balance or 0) }}</div>
|
||
<div class="stat-label">포인트</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-icon blue">📦</div>
|
||
<div class="stat-value">{{ purchase_count or 0 }}</div>
|
||
<div class="stat-label">구매</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-icon pink">🐾</div>
|
||
<div class="stat-value" id="pet-count">{{ pets|length }}</div>
|
||
<div class="stat-label">반려동물</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 반려동물 섹션 -->
|
||
<div class="section">
|
||
<div class="section-header">
|
||
<h3 class="section-title">🐾 내 반려동물</h3>
|
||
</div>
|
||
|
||
<div id="pet-list">
|
||
{% if pets %}
|
||
{% for pet in pets %}
|
||
<div class="pet-card" onclick="editPet({{ pet.id }})">
|
||
<div class="pet-photo">
|
||
{% if pet.photo_url %}
|
||
<img src="{{ pet.photo_url }}" alt="{{ pet.name }}">
|
||
{% else %}
|
||
{{ '🐕' if pet.species == 'dog' else ('🐈' if pet.species == 'cat' else '🐾') }}
|
||
{% endif %}
|
||
</div>
|
||
<div class="pet-info">
|
||
<div class="pet-name">{{ pet.name }}</div>
|
||
<div class="pet-details">
|
||
{{ pet.species_label }}
|
||
{% if pet.breed %}· {{ pet.breed }}{% endif %}
|
||
{% if pet.gender %}· {{ '♂' if pet.gender == 'male' else ('♀' if pet.gender == 'female' else '') }}{% endif %}
|
||
</div>
|
||
</div>
|
||
<span class="pet-arrow">›</span>
|
||
</div>
|
||
{% endfor %}
|
||
{% else %}
|
||
<div class="empty-state">
|
||
<div class="icon">🐾</div>
|
||
<p>등록된 반려동물이 없습니다</p>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<button class="add-pet-btn" onclick="openAddPetModal()">
|
||
<span>+</span> 반려동물 추가하기
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 메뉴 섹션 -->
|
||
<div class="section">
|
||
<ul class="menu-list">
|
||
<li class="menu-item" onclick="location.href='/my-page?phone={{ user.phone }}'">
|
||
<div class="menu-left">
|
||
<span class="menu-icon">📋</span>
|
||
<span class="menu-text">적립 내역</span>
|
||
</div>
|
||
<span class="menu-arrow">›</span>
|
||
</li>
|
||
<li class="menu-item">
|
||
<div class="menu-left">
|
||
<span class="menu-icon">📦</span>
|
||
<span class="menu-text">구매 내역</span>
|
||
<span class="menu-badge">준비중</span>
|
||
</div>
|
||
<span class="menu-arrow">›</span>
|
||
</li>
|
||
<li class="menu-item">
|
||
<div class="menu-left">
|
||
<span class="menu-icon">🎟️</span>
|
||
<span class="menu-text">쿠폰함</span>
|
||
<span class="menu-badge">준비중</span>
|
||
</div>
|
||
<span class="menu-arrow">›</span>
|
||
</li>
|
||
<li class="menu-item">
|
||
<div class="menu-left">
|
||
<span class="menu-icon">⚙️</span>
|
||
<span class="menu-text">내 정보 수정</span>
|
||
</div>
|
||
<span class="menu-arrow">›</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 반려동물 추가/수정 모달 -->
|
||
<div class="modal-overlay" id="petModal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h2 class="modal-title" id="modalTitle">반려동물 등록</h2>
|
||
<button class="modal-close" onclick="closeModal()">✕</button>
|
||
</div>
|
||
|
||
<form id="petForm" onsubmit="submitPet(event)">
|
||
<input type="hidden" id="petId" value="">
|
||
|
||
<!-- 종류 선택 -->
|
||
<div class="form-group">
|
||
<label class="form-label">종류 *</label>
|
||
<div class="species-options">
|
||
<div class="species-option" data-species="dog" onclick="selectSpecies('dog')">
|
||
<div class="icon">🐕</div>
|
||
<div class="label">강아지</div>
|
||
</div>
|
||
<div class="species-option" data-species="cat" onclick="selectSpecies('cat')">
|
||
<div class="icon">🐈</div>
|
||
<div class="label">고양이</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 이름 -->
|
||
<div class="form-group">
|
||
<label class="form-label">이름 *</label>
|
||
<input type="text" class="form-input" id="petName" placeholder="예: 뽀삐" required>
|
||
</div>
|
||
|
||
<!-- 품종 -->
|
||
<div class="form-group">
|
||
<label class="form-label">품종</label>
|
||
<select class="form-input" id="petBreed">
|
||
<option value="">선택해주세요</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- 성별 -->
|
||
<div class="form-group">
|
||
<label class="form-label">성별</label>
|
||
<select class="form-input" id="petGender">
|
||
<option value="">선택해주세요</option>
|
||
<option value="male">남아 ♂</option>
|
||
<option value="female">여아 ♀</option>
|
||
<option value="unknown">모름</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- 사진 -->
|
||
<div class="form-group">
|
||
<label class="form-label">사진</label>
|
||
<div class="photo-upload">
|
||
<div class="photo-preview" id="photoPreview" onclick="document.getElementById('photoInput').click()">
|
||
📷
|
||
</div>
|
||
<input type="file" id="photoInput" accept="image/*" style="display:none" onchange="previewPhoto(event)">
|
||
<span class="photo-hint">탭하여 사진 추가</span>
|
||
</div>
|
||
</div>
|
||
|
||
<button type="submit" class="submit-btn" id="submitBtn">등록하기</button>
|
||
<button type="button" class="submit-btn" style="background:#ef4444; margin-top:12px; display:none;" id="deleteBtn" onclick="deletePet()">삭제하기</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
let selectedSpecies = '';
|
||
let currentPetId = null;
|
||
|
||
const DOG_BREEDS = ['말티즈', '푸들', '포메라니안', '치와와', '시츄', '요크셔테리어', '비숑프리제', '골든리트리버', '래브라도리트리버', '진돗개', '시바견', '웰시코기', '닥스훈트', '비글', '보더콜리', '프렌치불독', '불독', '슈나우저', '사모예드', '허스키', '믹스견', '기타'];
|
||
const CAT_BREEDS = ['코리안숏헤어', '페르시안', '러시안블루', '샴', '먼치킨', '랙돌', '브리티쉬숏헤어', '아메리칸숏헤어', '스코티쉬폴드', '노르웨이숲', '메인쿤', '뱅갈', '아비시니안', '터키쉬앙고라', '믹스묘', '기타'];
|
||
|
||
function selectSpecies(species) {
|
||
selectedSpecies = species;
|
||
document.querySelectorAll('.species-option').forEach(el => {
|
||
el.classList.toggle('selected', el.dataset.species === species);
|
||
});
|
||
|
||
// 품종 옵션 업데이트
|
||
const breedSelect = document.getElementById('petBreed');
|
||
const breeds = species === 'dog' ? DOG_BREEDS : CAT_BREEDS;
|
||
breedSelect.innerHTML = '<option value="">선택해주세요</option>' +
|
||
breeds.map(b => `<option value="${b}">${b}</option>`).join('');
|
||
}
|
||
|
||
function openAddPetModal() {
|
||
currentPetId = null;
|
||
document.getElementById('modalTitle').textContent = '반려동물 등록';
|
||
document.getElementById('petId').value = '';
|
||
document.getElementById('petForm').reset();
|
||
document.getElementById('photoPreview').innerHTML = '📷';
|
||
document.getElementById('submitBtn').textContent = '등록하기';
|
||
document.getElementById('deleteBtn').style.display = 'none';
|
||
selectedSpecies = '';
|
||
document.querySelectorAll('.species-option').forEach(el => el.classList.remove('selected'));
|
||
document.getElementById('petModal').classList.add('active');
|
||
}
|
||
|
||
function editPet(petId) {
|
||
// TODO: API에서 pet 정보 가져와서 폼에 채우기
|
||
currentPetId = petId;
|
||
document.getElementById('modalTitle').textContent = '반려동물 수정';
|
||
document.getElementById('submitBtn').textContent = '수정하기';
|
||
document.getElementById('deleteBtn').style.display = 'block';
|
||
document.getElementById('petModal').classList.add('active');
|
||
}
|
||
|
||
function closeModal() {
|
||
document.getElementById('petModal').classList.remove('active');
|
||
}
|
||
|
||
function previewPhoto(event) {
|
||
const file = event.target.files[0];
|
||
if (file) {
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
document.getElementById('photoPreview').innerHTML =
|
||
`<img src="${e.target.result}" alt="미리보기">`;
|
||
};
|
||
reader.readAsDataURL(file);
|
||
}
|
||
}
|
||
|
||
async function submitPet(event) {
|
||
event.preventDefault();
|
||
|
||
if (!selectedSpecies) {
|
||
alert('종류를 선택해주세요.');
|
||
return;
|
||
}
|
||
|
||
const name = document.getElementById('petName').value.trim();
|
||
if (!name) {
|
||
alert('이름을 입력해주세요.');
|
||
return;
|
||
}
|
||
|
||
const btn = document.getElementById('submitBtn');
|
||
btn.disabled = true;
|
||
btn.textContent = '처리중...';
|
||
|
||
try {
|
||
const data = {
|
||
name: name,
|
||
species: selectedSpecies,
|
||
breed: document.getElementById('petBreed').value,
|
||
gender: document.getElementById('petGender').value
|
||
};
|
||
|
||
const url = currentPetId ? `/api/pets/${currentPetId}` : '/api/pets';
|
||
const method = currentPetId ? 'PUT' : 'POST';
|
||
|
||
const response = await fetch(url, {
|
||
method: method,
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(data)
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
// 사진 업로드
|
||
const photoInput = document.getElementById('photoInput');
|
||
if (photoInput.files.length > 0) {
|
||
const petId = result.pet_id || currentPetId;
|
||
const formData = new FormData();
|
||
formData.append('photo', photoInput.files[0]);
|
||
|
||
await fetch(`/api/pets/${petId}/photo`, {
|
||
method: 'POST',
|
||
body: formData
|
||
});
|
||
}
|
||
|
||
alert(result.message || '저장되었습니다!');
|
||
location.reload();
|
||
} else {
|
||
alert(result.error || '오류가 발생했습니다.');
|
||
}
|
||
} catch (error) {
|
||
console.error(error);
|
||
alert('서버 오류가 발생했습니다.');
|
||
} finally {
|
||
btn.disabled = false;
|
||
btn.textContent = currentPetId ? '수정하기' : '등록하기';
|
||
}
|
||
}
|
||
|
||
async function deletePet() {
|
||
if (!currentPetId) return;
|
||
if (!confirm('정말 삭제하시겠습니까?')) return;
|
||
|
||
try {
|
||
const response = await fetch(`/api/pets/${currentPetId}`, {
|
||
method: 'DELETE'
|
||
});
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
alert('삭제되었습니다.');
|
||
location.reload();
|
||
} else {
|
||
alert(result.error || '삭제 실패');
|
||
}
|
||
} catch (error) {
|
||
alert('서버 오류가 발생했습니다.');
|
||
}
|
||
}
|
||
|
||
// 모달 외부 클릭 시 닫기
|
||
document.getElementById('petModal').addEventListener('click', function(e) {
|
||
if (e.target === this) closeModal();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |