This commit is contained in:
2025-09-19 16:32:24 +09:00
parent 5e0db4ff03
commit 4b652c4df5
25 changed files with 1166 additions and 730 deletions
@@ -2,9 +2,35 @@
<html
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}">
<head>
<title>Bookmarks</title>
<style>
.scrollable-content {
max-height: 500px; /* 콘텐츠 영역의 최대 높이를 지정 */
max-width: 500px;
overflow-y: auto; /* 세로 내용이 넘칠 경우 스크롤바 자동 생성 */
-webkit-overflow-scrolling: touch; /* 모바일에서 부드러운 스크롤 효과 */
}
</style>
<link rel="stylesheet" href="https://unpkg.com/swiper/swiper-bundle.min.css" />
<script src="https://unpkg.com/swiper/swiper-bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const swiper = new Swiper('.bookmark-swiper', {
loop: false,
pagination: {
el: '.swiper-pagination',
clickable: true,
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
});
});
</script>
</head>
<th:block layout:fragment="content">
<section class="wrapper style2">
@@ -12,71 +38,103 @@
<header class="major">
<h2>Bookmarks</h2>
<p>다른 사용자들이 저장한 유용한 페이지들을 둘러보세요.</p>
<div class="filter-controls" style="margin-bottom: 2em; text-align: center;">
<div style="margin-bottom: 1em;">
<strong>카테고리:</strong>
<a th:href="@{/bookmarks}" th:classappend="${currentCategory == null && currentTag == null} ? 'button small' : 'button alt small'">전체</a>
<a th:each="cat : ${allCategories}"
th:href="@{/bookmarks(category=${cat})}"
th:text="${cat}"
th:classappend="${currentCategory == cat} ? 'button small' : 'button alt small'"></a>
</div>
<div>
<strong>태그:</strong>
<a th:each="tg : ${allTags}"
th:href="@{/bookmarks(tag=${tg})}"
th:text="'#' + ${tg}"
th:classappend="${currentTag == tg} ? 'button small' : 'button alt small'"></a>
</div>
</div>
</header>
</div>
</section>
<section class="wrapper style1">
<div class="container">
<div class="row">
<div class="col-12" th:if="${bookmarksPage.empty}">
<p style="text-align: center;">아직 저장된 페이지가 없습니다.</p>
</div>
<div class="col-4 col-6-medium col-12-small" th:each="bookmark : ${bookmarksPage.content}">
<section class="box feature">
<a href="javascript:void(0);"
th:data-url="${bookmark.url}"
th:data-title="${bookmark.title}"
onclick="showBookmarkOptions(this)" class="image featured">
<img th:src="${bookmark.thumbnailUrl ?: '/images/pic01.jpg'}" alt="Thumbnail" />
</a>
<div class="inner">
<header>
<h3 th:text="${bookmark.title}">북마크 제목</h3>
<p th:if="${bookmark.userComment}" th:text="${bookmark.userComment}" style="font-style: italic; color: #007bff;"></p>
</header>
<p th:text="${#strings.abbreviate(bookmark.description, 100)}"></p>
<div class="swiper bookmark-swiper">
<div class="swiper-wrapper">
<div class="swiper-slide" th:each="bookmark : ${bookmarksPage.content}">
<section class="box feature" style="margin: 0; height: 100%; display: flex; flex-direction: column;">
<div class="bookmark-controls" style="margin-top: 1em; padding-top: 1em; border-top: 1px solid #eee;">
<div class="vote-controls" th:data-bookmark-id="${bookmark.id}" style="text-align: center; margin-bottom: 1em;">
<button class="button small alt" th:onclick="handleBookmarkVote(this, 'like')">
👍 (<span class="like-count" th:text="${bookmark.voteCount}">0</span>)
</button>
<button class="button small alt" th:onclick="handleBookmarkVote(this, 'unlike')" style="margin-left: 0.5em;">
👎 (<span class="unlike-count" th:text="${bookmark.unlikeCount}">0</span>)
</button>
<div th:switch="${bookmark.bookmarkType}">
<div th:case="'IMAGE'" class="image-flick-container scrollable-content">
<img th:each="imageUrl : ${bookmark.contentUrls}" th:src="${apiBaseUrl + imageUrl}" alt="Bookmark Image" />
</div>
<a href="javascript:void(0);" th:onclick="toggleCommentSection('[[${bookmark.id}]]')" class="button small fit">댓글 보기</a>
<div th:id="'comment-section-' + ${bookmark.id}" class="comment-section" style="display: none; margin-top: 1em;">
<div th:id="'comments-list-' + ${bookmark.id}" class="comments-list"></div>
<textarea th:id="'comment-input-' + ${bookmark.id}" placeholder="댓글을 입력하세요..." style="margin-top: 1em;"></textarea>
<button class="button small" th:onclick="submitBookmarkComment('[[${bookmark.id}]]')">등록</button>
<div th:case="'VIDEO'" class="video-container" th:if="${!#lists.isEmpty(bookmark.contentUrls)}">
<video controls style="width: 100%;">
<source th:src="${apiBaseUrl + bookmark.contentUrls[0]}" type="video/mp4">
</video>
</div>
<a th:case="'URL'"
href="javascript:void(0);"
th:data-url="${bookmark.url}"
th:data-title="${bookmark.title}"
onclick="showBookmarkOptions(this)" class="image featured scrollable-content">
<img th:each="imageUrl : ${bookmark.contentUrls}" th:src="${apiBaseUrl + imageUrl}" alt="Bookmark Image" />
</a>
</div>
<footer style="font-size: 0.8em; color: #888; text-align: right; margin-top: 1em;">
by <span th:text="${bookmark.userId}"></span>
</footer>
</div>
</section>
<div class="inner" style="flex-grow: 1; display: flex; flex-direction: column; justify-content: space-between;">
<div>
<header>
<h3 th:text="${bookmark.title}">북마크 제목</h3>
<p th:if="${bookmark.userComment}" th:text="${bookmark.userComment}"></p>
</header>
<p th:text="${#strings.abbreviate(bookmark.description, 100)}"></p>
</div>
<div>
<div class="vote-controls" style="margin-top: 1em; text-align: center;" th:data-bookmark-id="${bookmark.id}">
<button class="button small" onclick="handleBookmarkVote(this, 'like')">
👍 Like (<span class="like-count" th:text="${bookmark.voteCount}">0</span>)
</button>
<button class="button small" onclick="handleBookmarkVote(this, 'unlike')">
👎 Unlike (<span class="unlike-count" th:text="${bookmark.unlikeCount}">0</span>)
</button>
<button class="button alt small" th:onclick="toggleCommentSection([[${bookmark.id}]])">
💬 Comments
</button>
</div>
<section class="comment-section" th:id="|comment-section-${bookmark.id}|" style="display: none; margin-top: 1em; text-align: left;">
<th:block sec:authorize="isAuthenticated()">
<div class="comment-form-container">
<textarea th:id="|comment-input-${bookmark.id}|" placeholder="댓글을 입력하세요..." style="width: 100%;"></textarea>
<button th:onclick="submitBookmarkComment([[${bookmark.id}]])" class="button small" style="margin-top: 0.5em;">등록</button>
</div>
</th:block>
<div sec:authorize="isAnonymous()" style="padding: 1em; text-align: center; border: 1px dashed #ccc; margin-bottom: 1em;">
<p style="margin:0;">댓글을 작성하려면 <a th:href="@{/home.bs(action='login')}">로그인</a>이 필요합니다.</p>
</div>
<div th:id="|comments-list-${bookmark.id}|" style="margin-top: 1em;">
</div>
</section>
</div>
</div>
</section>
</div>
</div>
<div class="swiper-pagination"></div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
<nav th:if="${bookmarksPage.totalPages > 1}" style="text-align: center; margin-top: 2.5em;">
<ul class="pagination">
<li th:classappend="${bookmarksPage.first} ? 'disabled'">
<a th:href="@{/bookmarks(page=${bookmarksPage.number - 1})}" class="button alt small">Prev</a>
</li>
<li th:each="pageNum : ${#numbers.sequence(0, bookmarksPage.totalPages - 1)}">
<a th:href="@{/bookmarks(page=${pageNum})}"
th:text="${pageNum + 1}"
th:class="${pageNum == bookmarksPage.number} ? 'button small' : 'button alt small'"></a>
</li>
<li th:classappend="${bookmarksPage.last} ? 'disabled'">
<a th:href="@{/bookmarks(page=${bookmarksPage.number + 1})}" class="button alt small">Next</a>
</li>
</ul>
</nav>
<div th:if="${bookmarksPage.empty}">
<p style="text-align: center;">아직 저장된 페이지가 없습니다.</p>
</div>
</div>
</section>
</th:block>