버그 수정, 모임장 설정 수정
This commit is contained in:
+14
-1
@@ -15,7 +15,7 @@
|
||||
<div class="club-header">
|
||||
<div class="club-title">
|
||||
<h3>{{ selectedClub.name }}</h3>
|
||||
<router-link v-if="userRole === 'admin' || userRole === '모임장' || userRole === '운영진'"
|
||||
<router-link v-if="userRole === 'admin' || userClubRole === '모임장'"
|
||||
:to="{ name: 'ClubSettings' }"
|
||||
class="settings-icon"
|
||||
title="클럽 설정">
|
||||
@@ -340,6 +340,19 @@ const loadMenuItems = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 사이드바 새로고침 메소드
|
||||
const refreshSidebar = async () => {
|
||||
console.log('사이드바 새로고침 시작...');
|
||||
await fetchClubs();
|
||||
await fetchClubInfo();
|
||||
loadMenuItems();
|
||||
initializeExpandedGroups(); // 메뉴 그룹 초기화
|
||||
console.log('사이드바 새로고침 완료');
|
||||
};
|
||||
|
||||
// 사이드바 새로고침 메소드를 전역으로 노출
|
||||
provide('refreshSidebar', refreshSidebar);
|
||||
|
||||
// 사용자 정보 로드
|
||||
const loadUserData = async () => {
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
@@ -47,6 +47,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 클럽 추가 버튼 -->
|
||||
<div class="club-add-wrapper">
|
||||
<Button icon="pi pi-plus-circle" class="club-add-button p-button-outlined" @click="goToClubCreate"
|
||||
title="새 클럽 만들기" />
|
||||
</div>
|
||||
|
||||
<!-- 사용자 메뉴 -->
|
||||
<div class="user-menu-wrapper">
|
||||
<Button @click="toggleUserMenu" class="user-menu-button p-button-outlined">
|
||||
@@ -301,6 +308,11 @@ const toggleNotifications = (event) => {
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
// 클럽 생성 페이지로 이동
|
||||
const goToClubCreate = () => {
|
||||
router.push({ name: 'UserClubCreate' });
|
||||
};
|
||||
|
||||
// 문서 클릭 시 알림 패널 닫기
|
||||
const handleDocumentClick = (event) => {
|
||||
const notificationWrapper = document.querySelector('.notification-wrapper');
|
||||
@@ -348,13 +360,15 @@ onBeforeUnmount(() => {
|
||||
|
||||
.user-menu-wrapper,
|
||||
.admin-menu-wrapper,
|
||||
.notification-wrapper {
|
||||
.notification-wrapper,
|
||||
.club-add-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.user-menu-button,
|
||||
.admin-menu-button,
|
||||
.notification-button {
|
||||
.notification-button,
|
||||
.club-add-button {
|
||||
background-color: transparent !important;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3) !important;
|
||||
color: white !important;
|
||||
@@ -362,7 +376,8 @@ onBeforeUnmount(() => {
|
||||
|
||||
.user-menu-button:hover,
|
||||
.admin-menu-button:hover,
|
||||
.notification-button:hover {
|
||||
.notification-button:hover,
|
||||
.club-add-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ const clubService = {
|
||||
...memberData
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 클럽 회원 삭제
|
||||
* @param {Number} clubId - 클럽 ID
|
||||
@@ -95,6 +95,8 @@ const clubService = {
|
||||
deleteClubMember(clubId, memberId) {
|
||||
return apiClient.post('/api/club/members/delete', { clubId, memberId });
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 클럽 회원 통계 조회
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { ref, computed, onMounted, inject } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import clubService from '@/services/clubService';
|
||||
@@ -44,6 +44,7 @@ import axios from 'axios'
|
||||
const toast = useToast();
|
||||
const authStore = useAuthStore();
|
||||
const router = useRouter();
|
||||
const refreshSidebar = inject('refreshSidebar'); // App.vue에서 제공하는 사이드바 새로고침 메소드 주입
|
||||
|
||||
const clubName = ref('');
|
||||
const clubDescription = ref('');
|
||||
@@ -106,6 +107,16 @@ const handleCreateClub = async () => {
|
||||
// 성공 메시지
|
||||
toast.add({ severity: 'success', summary: '성공', detail: '클럽이 성공적으로 생성되었습니다.', life: 3000 });
|
||||
|
||||
// 사이드바 새로고침
|
||||
if (refreshSidebar) {
|
||||
try {
|
||||
await refreshSidebar();
|
||||
console.log('클럽 생성 후 사이드바 새로고침 완료');
|
||||
} catch (refreshError) {
|
||||
console.error('사이드바 새로고침 오류:', refreshError);
|
||||
}
|
||||
}
|
||||
|
||||
// 클럽 페이지로 이동
|
||||
router.push('/club');
|
||||
} catch (error) {
|
||||
|
||||
@@ -27,6 +27,15 @@
|
||||
<input type="number" id="femaleHandicap" v-model="clubInfo.femaleHandicap" class="form-control" min="0" max="100" />
|
||||
<small class="form-text text-muted">여성 회원을 멤버(게스트)로 등록할 때 기본 핸디캡 값입니다.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="clubOwner">모임장 설정</label>
|
||||
<select id="clubOwner" v-model="selectedOwnerId" class="form-control">
|
||||
<option v-for="member in members" :key="member.id" :value="member.userId">
|
||||
{{ member.name }} ({{ member.memberType }}) [ID: {{ member.userId }}]
|
||||
</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">모임장 권한을 이전할 회원을 선택합니다. 모임장은 클럽의 모든 관리 권한을 가집니다.</small>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button @click="saveClubInfo" class="btn btn-primary">저장</button>
|
||||
</div>
|
||||
@@ -111,7 +120,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { ref, onMounted, computed, inject } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { useClubStore } from '@/stores/clubStore';
|
||||
@@ -121,6 +130,7 @@ const clubStore = useClubStore();
|
||||
|
||||
const router = useRouter();
|
||||
const toast = useToast();
|
||||
const refreshSidebar = inject('refreshSidebar'); // 사이드바 새로고침 메소드 주입
|
||||
|
||||
// 클럽 정보
|
||||
const clubInfo = ref({
|
||||
@@ -128,15 +138,21 @@ const clubInfo = ref({
|
||||
name: '',
|
||||
description: '',
|
||||
location: '',
|
||||
femaleHandicap: 15 // 기본값 설정
|
||||
femaleHandicap: 15, // 기본값 설정
|
||||
ownerId: null // 모임장 ID
|
||||
});
|
||||
|
||||
// 구독 정보
|
||||
const subscriptionInfo = ref(null);
|
||||
const clubId = localStorage.getItem('clubId');
|
||||
|
||||
// 기능 목록
|
||||
// 기능 정보
|
||||
const features = ref([]);
|
||||
|
||||
// 회원 목록
|
||||
const members = ref([]);
|
||||
const selectedOwnerId = ref(null); // 선택된 모임장 ID
|
||||
|
||||
// 구독 상태 텍스트 계산
|
||||
const subscriptionStatusText = computed(() => {
|
||||
if (!subscriptionInfo.value) return '없음';
|
||||
@@ -179,9 +195,8 @@ const formatDate = (dateString) => {
|
||||
// 클럽 정보 로드
|
||||
const loadClubInfo = async () => {
|
||||
try {
|
||||
const clubId = localStorage.getItem('clubId');
|
||||
if (!clubId) {
|
||||
toast.add({ severity: 'error', summary: '오류', detail: '클럽 정보를 찾을 수 없습니다.', life: 3000 });
|
||||
router.push('/club');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -192,17 +207,19 @@ const loadClubInfo = async () => {
|
||||
description: clubData.description || '',
|
||||
location: clubData.location || '',
|
||||
memberCount: clubData.memberCount || 0,
|
||||
femaleHandicap: clubData.femaleHandicap || 15
|
||||
femaleHandicap: clubData.femaleHandicap || 15,
|
||||
ownerId: clubData.ownerId // 모임장 ID 추가
|
||||
};
|
||||
|
||||
// 구독 정보 로드
|
||||
await loadSubscriptionInfo(clubId);
|
||||
|
||||
// 기능 정보 로드
|
||||
// 선택된 모임장 ID 설정
|
||||
selectedOwnerId.value = clubData.ownerId;
|
||||
|
||||
// 구독 정보 및 기능 정보 로드
|
||||
await loadSubscriptionInfo(clubId);
|
||||
await loadFeatures(clubId);
|
||||
} catch (error) {
|
||||
console.error('클럽 정보 로드 오류:', error);
|
||||
toast.add({ severity: 'error', summary: '오류', detail: '클럽 정보를 불러오는 중 오류가 발생했습니다.', life: 3000 });
|
||||
toast.add({ severity: 'error', summary: '오류', detail: '클럽 정보를 가져오는 중 오류가 발생했습니다.', life: 3000 });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -283,34 +300,86 @@ const getFeatureIcon = (featureType) => {
|
||||
// 클럽 정보 저장
|
||||
const saveClubInfo = async () => {
|
||||
try {
|
||||
const clubId = clubInfo.value.id;
|
||||
if (!clubId) {
|
||||
toast.add({ severity: 'error', summary: '오류', detail: '클럽 정보를 찾을 수 없습니다.', life: 3000 });
|
||||
router.push('/club');
|
||||
return;
|
||||
}
|
||||
|
||||
// clubService를 사용하여 클럽 정보 업데이트
|
||||
await clubService.updateClub(clubId, {
|
||||
|
||||
// 모임장 변경 확인
|
||||
const isOwnerChanged = selectedOwnerId.value !== clubInfo.value.ownerId;
|
||||
let updateData = {
|
||||
name: clubInfo.value.name,
|
||||
description: clubInfo.value.description,
|
||||
location: clubInfo.value.location,
|
||||
femaleHandicap: clubInfo.value.femaleHandicap
|
||||
});
|
||||
};
|
||||
|
||||
// 모임장이 변경되었다면 확인 메시지 표시
|
||||
if (isOwnerChanged) {
|
||||
const confirmChange = confirm('모임장을 변경하시겠습니까? 모임장 권한이 새로운 회원에게 이전됩니다.');
|
||||
if (!confirmChange) {
|
||||
// 선택을 원래 모임장으로 되돌림
|
||||
selectedOwnerId.value = clubInfo.value.ownerId;
|
||||
return;
|
||||
}
|
||||
|
||||
// 모임장 ID 추가
|
||||
updateData.ownerId = selectedOwnerId.value;
|
||||
}
|
||||
|
||||
// clubService를 사용하여 클럽 정보 업데이트
|
||||
await clubService.updateClub(clubId, updateData);
|
||||
|
||||
// 성공 메시지 표시
|
||||
toast.add({ severity: 'success', summary: '성공', detail: '클럽 정보가 저장되었습니다.', life: 3000 });
|
||||
let successMessage = '클럽 정보가 저장되었습니다.';
|
||||
if (isOwnerChanged) {
|
||||
successMessage = '클럽 정보가 저장되었으며, 모임장이 변경되었습니다.';
|
||||
}
|
||||
|
||||
// 클럽 정보 다시 불러오기
|
||||
toast.add({ severity: 'success', summary: '성공', detail: successMessage, life: 3000 });
|
||||
|
||||
// 클럽 정보와 회원 목록 다시 불러오기
|
||||
await loadClubInfo();
|
||||
await loadMembers();
|
||||
|
||||
// 사이드바 새로고침
|
||||
if (refreshSidebar) {
|
||||
try {
|
||||
await refreshSidebar();
|
||||
} catch (refreshError) {
|
||||
console.error('사이드바 새로고침 오류:', refreshError);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('클럽 정보 저장 오류:', error);
|
||||
toast.add({ severity: 'error', summary: '오류', detail: '클럽 정보를 저장하는 중 오류가 발생했습니다.', life: 3000 });
|
||||
}
|
||||
};
|
||||
|
||||
// 회원 목록 가져오기
|
||||
const loadMembers = async () => {
|
||||
try {
|
||||
const response = await clubService.getClubMembers(clubId);
|
||||
|
||||
if (response.data) {
|
||||
const data = response.data;
|
||||
members.value = data;
|
||||
|
||||
// 현재 모임장 정보 설정
|
||||
if (clubInfo.value && clubInfo.value.ownerId) {
|
||||
selectedOwnerId.value = clubInfo.value.ownerId;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('회원 목록 가져오기 오류:', error);
|
||||
toast.add({ severity: 'error', summary: '오류', detail: '회원 목록을 가져오는 중 오류가 발생했습니다.', life: 3000 });
|
||||
}
|
||||
};
|
||||
|
||||
// 컴포넌트 마운트 시 데이터 로드
|
||||
onMounted(() => {
|
||||
loadClubInfo();
|
||||
onMounted(async () => {
|
||||
await loadClubInfo();
|
||||
await loadMembers();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
</div>
|
||||
<div class="field col-6 mb-3">
|
||||
<label for="memberType" class="mb-1">회원 유형</label>
|
||||
<Select id="memberType" v-model="member.memberType" :options="memberTypes" optionLabel="name" optionValue="code" placeholder="회원 유형 선택" class="w-full" />
|
||||
<Select id="memberType" v-model="member.memberType" :options="memberTypeOptions" optionLabel="name" optionValue="code" placeholder="회원 유형 선택" class="w-full" />
|
||||
</div>
|
||||
<div class="field col-6 mb-3">
|
||||
<label for="phone" class="mb-1">전화번호</label>
|
||||
@@ -194,12 +194,12 @@ import { useToast } from 'primevue/usetoast';
|
||||
const clubStore = useClubStore();
|
||||
const toast = useToast();
|
||||
|
||||
// 회원 유형 목록
|
||||
const memberTypes = ref([
|
||||
// 회원 유형 옵션
|
||||
const memberTypeOptions = ref([
|
||||
{ name: '정회원', code: '정회원' },
|
||||
{ name: '준회원', code: '준회원' },
|
||||
{ name: '운영진', code: '운영진' },
|
||||
{ name: '모임장', code: '모임장' }
|
||||
{ name: '운영진', code: '운영진' }
|
||||
// 모임장 옵션은 제거 - 클럽 설정에서만 변경 가능하도록 제한
|
||||
]);
|
||||
|
||||
// 성별 옵션
|
||||
|
||||
@@ -1097,6 +1097,10 @@ const processPayment = async () => {
|
||||
// 토스페이먼츠 SDK 초기화
|
||||
const tossPayments = await loadTossPaymentsSDK();
|
||||
|
||||
// 결제 성공/실패 핸들러 등록 (전역 함수로 등록)
|
||||
window.handlePaymentSuccess = handlePaymentSuccess;
|
||||
window.handlePaymentFail = handlePaymentFail;
|
||||
|
||||
// 결제 창 열기 - 결제 방법을 미리 지정하지 않고 토스페이먼츠 결제 창에서 선택하도록 함
|
||||
await tossPayments.requestPayment({
|
||||
...response.data.paymentData,
|
||||
@@ -1118,6 +1122,21 @@ const processPayment = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 선택 항목 초기화
|
||||
const resetSelections = () => {
|
||||
selectedPlan.value = null;
|
||||
selectedFeatures.value = [];
|
||||
selectedPlanDuration.value = 1;
|
||||
showPlans.value = false;
|
||||
showFeatures.value = false;
|
||||
isSelectingPlan.value = false;
|
||||
isSelectingFeatures.value = false;
|
||||
changeOption.value = 'immediate';
|
||||
paymentAgreement.value = false;
|
||||
showPaymentSection.value = false;
|
||||
showAdditionalFeatures.value = false;
|
||||
};
|
||||
|
||||
// 토스페이먼츠 SDK 로드
|
||||
const loadTossPaymentsSDK = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -1153,6 +1172,8 @@ const loadTossPaymentsSDK = () => {
|
||||
// 결제 성공 처리
|
||||
const handlePaymentSuccess = async (orderId) => {
|
||||
try {
|
||||
console.log('결제 성공:', orderId);
|
||||
|
||||
// 데이터 다시 로드
|
||||
await Promise.all([
|
||||
loadCurrentSubscription(),
|
||||
@@ -1162,23 +1183,40 @@ const handlePaymentSuccess = async (orderId) => {
|
||||
]);
|
||||
|
||||
// 상태 초기화
|
||||
selectedPlan.value = null;
|
||||
selectedFeatures.value = [];
|
||||
paymentAgreement.value = false;
|
||||
showPaymentSection.value = false;
|
||||
showPlans.value = false;
|
||||
showAdditionalFeatures.value = false;
|
||||
changeOption.value = isUpgrade.value ? 'immediate' : 'nextBilling';
|
||||
resetSelections();
|
||||
|
||||
alert('결제가 성공적으로 완료되었습니다.');
|
||||
// 성공 메시지 표시 (토스트 메시지 추가)
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: '결제 성공',
|
||||
detail: '구독 플랜 결제가 성공적으로 완료되었습니다.',
|
||||
life: 5000
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('결제 성공 후 처리 오류:', error);
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: '오류',
|
||||
detail: '구독 정보를 새로고침하는 중 오류가 발생했습니다.',
|
||||
life: 5000
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 결제 실패 처리
|
||||
const handlePaymentFail = (message) => {
|
||||
alert(`결제 실패: ${message || '사용자가 결제를 취소했습니다.'}`);
|
||||
console.log('결제 실패:', message);
|
||||
|
||||
// 실패 메시지 표시 (토스트 메시지 추가)
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: '결제 실패',
|
||||
detail: message || '결제 처리 중 오류가 발생했습니다.',
|
||||
life: 5000
|
||||
});
|
||||
|
||||
// 결제 실패 후 초기화
|
||||
resetSelections();
|
||||
};
|
||||
|
||||
// 플랜 구독 (기존 기능 - 레거시)
|
||||
|
||||
Reference in New Issue
Block a user