...
This commit is contained in:
@@ -30,9 +30,11 @@
|
||||
<div class="tab-link active" onclick="openTab(event, 'myInfo')">내 정보</div>
|
||||
<div class="tab-link" onclick="openTab(event, 'myPosts')">내가 쓴 글</div>
|
||||
<div class="tab-link" onclick="openTab(event, 'myComments')">내가 쓴 댓글</div>
|
||||
<div class="tab-link" onclick="openTab(event, 'myRanks')">내 게임 랭킹</div>
|
||||
<th:block sec:authorize="hasRole('ADMIN')">
|
||||
<div class="tab-link" onclick="openTab(event, 'userManagement')">회원 관리</div>
|
||||
<div class="tab-link" onclick="openTab(event, 'contentManagement')">콘텐츠 관리</div>
|
||||
<div class="tab-link" onclick="openTab(event, 'postManagement')">게시물 관리</div>
|
||||
<div class="tab-link" onclick="openTab(event, 'bannerManagement')">배너 관리</div>
|
||||
</th:block>
|
||||
</div>
|
||||
|
||||
@@ -77,6 +79,32 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="myRanks" class="tab-content">
|
||||
<div class="box">
|
||||
<ul class="post-list">
|
||||
<li th:if="${#lists.isEmpty(myRanks)}">플레이한 게임 기록이 없습니다.</li>
|
||||
<li th:each="rank : ${myRanks}">
|
||||
<div>
|
||||
<span th:switch="${rank.gameType.name()}">
|
||||
<strong th:case="'GAME_2048'">2048</strong>
|
||||
<strong th:case="'SUDOKU'">스도쿠</strong>
|
||||
<strong th:case="'SPIDER'">스파이더 카드</strong>
|
||||
<strong th:case="'NONOGRAM'">노노그램</strong>
|
||||
<strong th:case="*">기타 게임</strong>
|
||||
</span>
|
||||
<span style="margin-left: 1em;" th:switch="${rank.gameType.name()}">
|
||||
<span th:case="'SUDOKU'" th:text="|기록: ${rank.primaryScore / 60}분 ${rank.primaryScore % 60}초|"></span>
|
||||
<span th:case="'NONOGRAM'" th:text="|기록: ${rank.primaryScore / 60}분 ${rank.primaryScore % 60}초|"></span>
|
||||
<span th:case="'SPIDER'" th:text="|기록: ${rank.primaryScore} moves|"></span>
|
||||
<span th:case="*" th:text="|점수: ${#numbers.formatInteger(rank.primaryScore, 3, 'COMMA')}|"></span>
|
||||
</span>
|
||||
</div>
|
||||
<span th:text="${#temporals.format(rank.timestamp, 'yyyy-MM-dd HH:mm')}"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="userManagement" class="tab-content" sec:authorize="hasRole('ADMIN')">
|
||||
<div class="box">
|
||||
<h4>권한 요청</h4>
|
||||
@@ -105,7 +133,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="contentManagement" class="tab-content" sec:authorize="hasRole('ADMIN')">
|
||||
<div id="postManagement" class="tab-content" sec:authorize="hasRole('ADMIN')">
|
||||
<div class="box">
|
||||
<h4>최신 글 관리</h4>
|
||||
<ul class="post-list">
|
||||
@@ -120,6 +148,29 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="bannerManagement" class="tab-content" sec:authorize="hasRole('ADMIN')">
|
||||
<div class="box">
|
||||
<h4>배너 이미지 관리</h4>
|
||||
<ul class="post-list">
|
||||
<li th:each="image : ${allImages}" th:id="'image-row-' + ${image.id}">
|
||||
<div style="display: flex; align-items: center; gap: 1em;">
|
||||
<img th:src="@{'/api/images/' + ${image.fileName}}" alt="Image Thumbnail" style="width: 100px; height: 60px; object-fit: cover; border-radius: 4px;"/>
|
||||
<div>
|
||||
<strong th:text="${image.fileName}"></strong><br>
|
||||
<span th:if="${image.isBannerCandidate}" style="color: #2a9d8f; font-weight: bold;">(배너로 사용 중)</span>
|
||||
<span th:unless="${image.isBannerCandidate}" style="color: #888;">(배너로 사용 안 함)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button th:if="${!image.isBannerCandidate}" class="button small primary" th:onclick="handleBannerPermission('[[${image.id}]]', 'approve')">배너로 승인</button>
|
||||
<button th:if="${image.isBannerCandidate}" class="button small alt" th:onclick="handleBannerPermission('[[${image.id}]]', 'revoke')">승인 해제</button>
|
||||
</div>
|
||||
</li>
|
||||
<li th:if="${#lists.isEmpty(allImages)}">업로드된 이미지가 없습니다.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -211,7 +262,8 @@
|
||||
* @param {'block' | 'unblock'} action - 수행할 작업
|
||||
*/
|
||||
function handleContent(postId, action) {
|
||||
const url = `/blog/post/${postId}/${action}`;
|
||||
const cleanpostId = postId.replace(/^"|"$/g, '');
|
||||
const url = `/blog/post/${cleanpostId}/${action}`;
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { [csrfHeader]: csrfToken }
|
||||
@@ -227,6 +279,42 @@
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
|
||||
/**
|
||||
* (관리자) 이미지의 배너 사용 권한을 승인하거나 해제하는 함수
|
||||
* @param {string} imageId - 대상 이미지의 ID
|
||||
* @param {'approve' | 'revoke'} action - 수행할 작업
|
||||
*/
|
||||
function handleBannerPermission(imageId, action) {
|
||||
const cleanImageId = imageId.replace(/^"|"$/g, '');
|
||||
|
||||
// [수정] 깨끗하게 정리된 cleanImageId를 URL에 사용합니다.
|
||||
const url = `/api/images/${cleanImageId}/${action === 'approve' ? 'approve-banner' : 'revoke-banner'}`;
|
||||
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { [csrfHeader]: csrfToken }
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Server returned an error.');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data && data.id) {
|
||||
alert(`이미지 상태를 성공적으로 변경했습니다.`);
|
||||
location.reload(); // 페이지를 새로고침하여 상태(버튼, 텍스트)를 즉시 반영
|
||||
} else {
|
||||
alert('작업에 실패했습니다.');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('작업 중 오류가 발생했습니다.');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</th:block>
|
||||
</html>
|
||||
Reference in New Issue
Block a user