Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions src/main/kotlin/codel/admin/presentation/AdminController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,9 @@ class AdminController(
"PENDING" to adminService.countMembersByStatus("PENDING"),
"DONE" to adminService.countMembersByStatus("DONE"),
"REJECT" to adminService.countMembersByStatus("REJECT"),
"PHONE_VERIFIED" to adminService.countMembersByStatus("PHONE_VERIFIED")
"PHONE_VERIFIED" to adminService.countMembersByStatus("PHONE_VERIFIED"),
"PERSONALITY_COMPLETED" to adminService.countMembersByStatus("PERSONALITY_COMPLETED"),
"WITHDRAWN" to adminService.countMembersByStatus("WITHDRAWN")
)

model.addAttribute("members", members)
Expand Down Expand Up @@ -390,14 +392,19 @@ class AdminController(
fun createQuestion(
@RequestParam content: String,
@RequestParam category: String,
@RequestParam questionGroup: String,
@RequestParam(required = false) questionGroup: String?,
@RequestParam(required = false) description: String?,
@RequestParam(defaultValue = "true") isActive: Boolean,
redirectAttributes: RedirectAttributes
): String {
try {
val questionCategory = QuestionCategory.valueOf(category)
val group = QuestionGroup.valueOf(questionGroup)
// 회원가입 전용 카테고리(채팅 미사용)는 자동으로 RANDOM 그룹 지정
val group = if (questionCategory.usedInSignup && !questionCategory.usedInChat) {
QuestionGroup.RANDOM
} else {
QuestionGroup.valueOf(questionGroup ?: "RANDOM")
}
adminService.createQuestionV2(content, questionCategory, group, description, isActive)
redirectAttributes.addFlashAttribute("success", "질문이 성공적으로 등록되었습니다.")
} catch (e: Exception) {
Expand All @@ -423,14 +430,19 @@ class AdminController(
@PathVariable questionId: Long,
@RequestParam content: String,
@RequestParam category: String,
@RequestParam questionGroup: String,
@RequestParam(required = false) questionGroup: String?,
@RequestParam(required = false) description: String?,
@RequestParam(defaultValue = "false") isActive: Boolean,
redirectAttributes: RedirectAttributes
): String {
try {
val questionCategory = QuestionCategory.valueOf(category)
val group = QuestionGroup.valueOf(questionGroup)
// 회원가입 전용 카테고리(채팅 미사용)는 자동으로 RANDOM 그룹 지정
val group = if (questionCategory.usedInSignup && !questionCategory.usedInChat) {
QuestionGroup.RANDOM
} else {
QuestionGroup.valueOf(questionGroup ?: "RANDOM")
}
adminService.updateQuestionV2(questionId, content, questionCategory, group, description, isActive)
redirectAttributes.addFlashAttribute("success", "질문이 성공적으로 수정되었습니다.")
} catch (e: Exception) {
Expand Down
183 changes: 173 additions & 10 deletions src/main/resources/templates/questionEditForm.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,28 @@
border-radius: 1rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07);
}

.info-box {
background: #E3F2FD;
border-left: 3px solid #4A90E2;
padding: 1rem;
border-radius: 4px;
margin-bottom: 1.5rem;
}

.info-box-title {
font-size: 0.9rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: #4A90E2;
}

.info-box ul {
margin-left: 1.5rem;
font-size: 0.85rem;
color: #7F8C8D;
margin-bottom: 0;
}
</style>
</head>
<body>
Expand All @@ -49,6 +71,15 @@ <h1 class="fw-bold">질문 수정</h1>

<div class="card form-card">
<div class="card-body">
<div class="info-box">
<div class="info-box-title">📌 그룹 설정 가이드</div>
<ul>
<li><strong>A그룹:</strong> 가벼운/진입용 질문 (먼저 노출, 대화 시작에 적합)</li>
<li><strong>B그룹:</strong> 깊이있는/무게감 질문 (A그룹 소진 후 노출)</li>
<li><strong>랜덤:</strong> 텐션업 코드 등 그룹 구분 없는 질문</li>
</ul>
</div>

<form th:action="@{/v1/admin/questions/{id}(id=${question.id})}" method="post">
<!-- 필터 파라미터 유지 -->
<input type="hidden" name="keyword" th:value="${filterKeyword}">
Expand All @@ -57,6 +88,10 @@ <h1 class="fw-bold">질문 수정</h1>
<input type="hidden" name="page" th:value="${filterPage}">
<input type="hidden" name="size" th:value="${filterSize}">

<!-- 기존 카테고리 값 저장 (JavaScript에서 사용) -->
<input type="hidden" id="currentCategory" th:value="${question.category.name}">
<input type="hidden" id="currentGroup" th:value="${question.questionGroup.name}">

<div class="mb-4">
<label for="content" class="form-label fw-bold">질문 내용 <span class="text-danger">*</span></label>
<textarea class="form-control" id="content" name="content" rows="3"
Expand All @@ -65,32 +100,36 @@ <h1 class="fw-bold">질문 수정</h1>
<div class="form-text">최대 500자까지 입력 가능합니다.</div>
</div>

<div class="mb-4">
<label for="purpose" class="form-label fw-bold">용도 <span class="text-danger">*</span></label>
<select class="form-select" id="purpose" required>
<option value="">용도를 선택하세요</option>
<option value="chatroom">채팅방 질문 추천</option>
<option value="profile">회원가입·프로필 수정</option>
</select>
</div>

<div class="mb-4">
<label for="category" class="form-label fw-bold">카테고리 <span class="text-danger">*</span></label>
<select class="form-select" id="category" name="category" required>
<option value="">카테고리를 선택하세요</option>
<option th:each="cat : ${categories}"
th:value="${cat.name}"
th:text="${cat.displayName + ' - ' + cat.description}"
th:selected="${cat.name == question.category.name}"></option>
<option value="">먼저 용도를 선택하세요</option>
</select>
</div>

<div class="mb-4">
<div class="mb-4" id="groupContainer">
<label for="questionGroup" class="form-label fw-bold">질문 그룹 <span class="text-danger">*</span></label>
<select class="form-select" id="questionGroup" name="questionGroup" required>
<option value="">그룹을 선택하세요</option>
<option th:each="group : ${questionGroups}"
th:value="${group.name}"
th:text="${group.displayName}"
th:selected="${group.name == question.questionGroup.name}"></option>
th:text="${group.displayName}"></option>
</select>
<div class="form-text">A그룹은 우선 추천, B그룹은 A그룹 소진 후 추천됩니다. 텐션업 카테고리는 랜덤을 선택하세요.</div>
</div>

<div class="mb-4">
<label for="description" class="form-label fw-bold">질문 설명</label>
<textarea class="form-control" id="description" name="description" rows="2"
<textarea class="form-control" id="description" name="description" rows="2"
placeholder="질문에 대한 부가 설명을 입력하세요 (선택사항)" maxlength="1000"
th:text="${question.description}"></textarea>
<div class="form-text">최대 1000자까지 입력 가능합니다.</div>
Expand Down Expand Up @@ -124,5 +163,129 @@ <h1 class="fw-bold">질문 수정</h1>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 용도별 카테고리 옵션
const categoryOptions = {
chatroom: [
{ value: 'VALUES', label: '가치관 코드' },
{ value: 'TENSION_UP', label: '텐션업 코드' },
{ value: 'IF', label: '만약에 코드' },
{ value: 'SECRET', label: '비밀 코드 (19+)' }
],
profile: [
{ value: 'VALUES', label: '가치관' },
{ value: 'FAVORITE', label: 'favorite' },
{ value: 'DATE', label: '데이트' },
{ value: 'MEMORY', label: '추억' },
{ value: 'WANT_TALK', label: '이런대화해보고싶어' }
]
};

// 카테고리별 용도 매핑
const categoryToPurpose = {
'VALUES': 'both', // 양쪽 모두 가능
'TENSION_UP': 'chatroom',
'IF': 'chatroom',
'SECRET': 'chatroom',
'FAVORITE': 'profile',
'DATE': 'profile',
'MEMORY': 'profile',
'WANT_TALK': 'profile'
};

// 회원가입 전용 카테고리 (그룹 자동 RANDOM)
const signupOnlyCategories = ['FAVORITE', 'DATE', 'MEMORY', 'WANT_TALK'];

const purposeSelect = document.getElementById('purpose');
const categorySelect = document.getElementById('category');
const groupSelect = document.getElementById('questionGroup');
const groupContainer = document.getElementById('groupContainer');

// 기존 값
const currentCategory = document.getElementById('currentCategory').value;
const currentGroup = document.getElementById('currentGroup').value;

// 용도 변경 시 카테고리 옵션 업데이트
function updateCategoryOptions(selectedCategory = null) {
const purpose = purposeSelect.value;

if (!purpose) {
categorySelect.innerHTML = '<option value="">먼저 용도를 선택하세요</option>';
return;
}

const options = categoryOptions[purpose];
let html = '<option value="">카테고리를 선택하세요</option>';
options.forEach(opt => {
const selected = selectedCategory === opt.value ? ' selected' : '';
html += `<option value="${opt.value}"${selected}>${opt.label}</option>`;
});
categorySelect.innerHTML = html;

updateGroupSelector();
}

// 그룹 선택기 업데이트
function updateGroupSelector() {
const purpose = purposeSelect.value;
const selectedCategory = categorySelect.value;

if (purpose === 'profile' || signupOnlyCategories.includes(selectedCategory)) {
// 회원가입·프로필 수정 용도: 그룹을 RANDOM으로 고정
groupSelect.value = 'RANDOM';
groupSelect.disabled = true;
groupSelect.removeAttribute('required');
groupContainer.querySelector('.form-text').textContent =
'회원가입·프로필 수정 용도의 질문은 그룹이 자동으로 \'랜덤\'으로 지정됩니다.';
} else {
// 채팅방 용도: 그룹 선택 가능
groupSelect.disabled = false;
groupSelect.setAttribute('required', 'required');
groupContainer.querySelector('.form-text').textContent =
'A그룹은 우선 추천, B그룹은 A그룹 소진 후 추천됩니다. 텐션업 카테고리는 랜덤을 선택하세요.';
}
}

// 페이지 로드 시 초기화
document.addEventListener('DOMContentLoaded', function() {
// 기존 카테고리로 용도 결정
const purpose = categoryToPurpose[currentCategory];

if (purpose === 'both') {
// VALUES는 채팅방 용도로 기본 설정 (채팅방에서 사용되는 경우가 더 흔함)
// 그룹이 RANDOM이면 profile, 아니면 chatroom으로 추정
if (currentGroup === 'RANDOM') {
purposeSelect.value = 'profile';
} else {
purposeSelect.value = 'chatroom';
}
} else if (purpose === 'chatroom') {
purposeSelect.value = 'chatroom';
} else {
purposeSelect.value = 'profile';
}

// 카테고리 옵션 업데이트 및 기존 값 선택
updateCategoryOptions(currentCategory);

// 기존 그룹 값 설정
if (!groupSelect.disabled) {
groupSelect.value = currentGroup;
}
});

// 이벤트 리스너
purposeSelect.addEventListener('change', function() {
updateCategoryOptions();
});
categorySelect.addEventListener('change', updateGroupSelector);

// 폼 제출 시 disabled 필드도 전송되도록 처리
document.querySelector('form').addEventListener('submit', function() {
if (groupSelect.disabled) {
groupSelect.disabled = false;
}
});
</script>
</body>
</html>
</html>
Loading