FAQ 디자인 적용

This commit is contained in:
현성필
2025-12-04 15:30:21 +09:00
parent 4ad34c9dfc
commit dacae095c5
2 changed files with 115 additions and 171 deletions
@@ -1,158 +1,106 @@
// -----------------------------------------------------------------------------
// Accordion Component - FAQ and Collapsible Sections
// Figma Design: node 984-2146
// -----------------------------------------------------------------------------
// FAQ Accordion
// FAQ Accordion - Figma Design
.faq-accordion {
background: $white;
background: #F6F9FB;
border-radius: $border-radius-lg;
box-shadow: $shadow-sm;
overflow: hidden;
margin-bottom: $spacing-2xl;
.faq-item {
border-bottom: 1px solid $border-gray;
border-bottom: 1px solid #DADADA;
transition: $transition-base;
&:last-child {
border-bottom: none;
}
// Active state - when accordion is open
&.active {
background: rgba($primary-blue, 0.02);
.faq-question {
color: $primary-blue;
.faq-icon {
transform: rotate(180deg);
color: $primary-blue;
}
}
.faq-answer {
display: block;
animation: slideDown 0.3s ease;
}
}
}
// Question row - Figma: 18px bold, #212529
.faq-question {
display: flex;
align-items: center;
justify-content: space-between;
padding: $spacing-lg $spacing-xl;
font-size: $font-size-base;
font-weight: $font-weight-medium;
color: $text-dark;
padding: $spacing-lg 20px;
font-size: 18px;
font-weight: $font-weight-bold;
color: #212529;
cursor: pointer;
transition: $transition-base;
position: relative;
gap: $spacing-md;
gap: 95px;
min-height: 54px;
@media (max-width: $breakpoint-sm) {
@media (max-width: $breakpoint-md) {
padding: $spacing-md $spacing-lg;
font-size: $font-size-sm;
}
&::before {
content: 'Q';
position: absolute;
left: $spacing-xl;
top: 50%;
transform: translateY(-50%);
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: $light-bg;
color: $primary-blue;
border-radius: $border-radius-circle;
font-size: $font-size-sm;
font-weight: $font-weight-bold;
transition: $transition-base;
@media (max-width: $breakpoint-sm) {
width: 28px;
height: 28px;
font-size: $font-size-xs;
}
font-size: 16px;
gap: $spacing-lg;
}
&:hover {
background: rgba($primary-blue, 0.03);
color: $primary-blue;
background: rgba($primary-blue, 0.02);
}
.faq-question-text {
flex: 1;
padding-left: 48px;
line-height: $line-height-normal;
@media (max-width: $breakpoint-sm) {
padding-left: 40px;
}
}
// Chevron icon
.faq-icon {
flex-shrink: 0;
font-size: 20px;
color: $text-gray;
transition: $transition-base;
display: flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
color: #212529;
transition: transform 0.3s ease;
@media (max-width: $breakpoint-sm) {
font-size: 18px;
svg {
width: 14px;
height: 8px;
}
}
}
// Answer section - white background
.faq-answer {
display: none;
padding: 0 $spacing-xl $spacing-lg;
margin-left: 48px;
margin-right: $spacing-xl;
position: relative;
background: #F6F9FB;
padding: $spacing-lg 20px $spacing-xl;
border-top: none;
@media (max-width: $breakpoint-sm) {
padding: 0 $spacing-lg $spacing-md;
margin-left: 40px;
margin-right: $spacing-lg;
}
&::before {
content: 'A';
position: absolute;
left: -48px;
top: 8px;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: $primary-blue;
color: $white;
border-radius: $border-radius-circle;
font-size: $font-size-sm;
font-weight: $font-weight-bold;
@media (max-width: $breakpoint-sm) {
left: -40px;
width: 28px;
height: 28px;
font-size: $font-size-xs;
}
}
&.show {
display: block;
animation: slideDown 0.3s ease;
@media (max-width: $breakpoint-md) {
padding: $spacing-md $spacing-lg $spacing-lg;
}
.faq-answer-content {
padding: $spacing-md 0;
font-size: $font-size-sm;
color: $text-gray;
font-size: 18px;
font-weight: $font-weight-regular;
color: #515151;
background-color: #ffffff;
padding: $spacing-lg;
line-height: $line-height-loose;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-xs;
padding: $spacing-sm 0;
@media (max-width: $breakpoint-md) {
font-size: 14px;
}
// Content formatting
@@ -209,7 +157,7 @@
}
}
// FAQ Page specific styles
// FAQ Page specific styles (legacy - for backward compatibility)
.faq-page {
min-height: calc(100vh - 140px);
background: $gray-bg;
@@ -1,90 +1,90 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_title_layout}">
<body>
<section layout:fragment="title">
<div class="page-title-banner">
<img th:src="@{/img/img_title_bg.png}" class="title-image" alt="타이틀 배경">
<h1>FAQ</h1>
</div>
</section>
<th:block layout:fragment="contentFragment">
<!-- FAQ Page -->
<div class="faq-page">
<div class="faq-container">
<section class="user-management-container">
<!-- Page Header -->
<div class="faq-header">
<h1 class="page-title">FAQ</h1>
<p class="page-description">자주 묻는 질문과 답변을 확인하세요.</p>
</div>
<!-- Search and Filter Controls -->
<form name="faqForm" th:action="@{/faq_list}" method="post" th:object="${search}" onsubmit="return false;">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="hidden" name="page" value="1">
<!-- Search and Filter Controls -->
<form name="articleForm" th:action="@{/faq_list}" method="post" th:object="${search}" onSubmit="fn_search_article(); return false;">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input name="page" type="hidden" value="">
<div class="table-controls">
<h2 class="total-count">
<strong th:text="${page.totalElements}">0</strong>
</h2>
<div class="table-controls">
<div class="total-count">
<strong th:text="${page.totalElements}">0</strong>
</div>
<div class="search-box">
<input type="text"
th:field="*{searchWrd}"
placeholder="질문 또는 답변 내용으로 검색">
<button type="button" class="btn btn-primary" onclick="fn_search_article()">
<i class="fas fa-search"></i>
<span>검색</span>
</button>
</div>
<div class="search-field">
<input type="text"
th:field="*{searchWrd}"
placeholder="질문 또는 답변 내용으로 검색"
onkeypress="if(event.keyCode === 13) { fn_search_faq(); return false; }">
<button type="button" class="search-field-btn" onclick="fn_search_faq()">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.5 17.5L13.875 13.875M15.8333 9.16667C15.8333 12.8486 12.8486 15.8333 9.16667 15.8333C5.48477 15.8333 2.5 12.8486 2.5 9.16667C2.5 5.48477 5.48477 2.5 9.16667 2.5C12.8486 2.5 15.8333 5.48477 15.8333 9.16667Z" stroke="currentColor" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span class="blind">검색</span>
</button>
</div>
</form>
</div>
</form>
<!-- FAQ Accordion -->
<div class="faq-accordion" th:if="${!#lists.isEmpty(page.content)}">
<div class="faq-item" th:each="faq : ${page.content}">
<div class="faq-question">
<div class="faq-question-text" th:text="${faq.faqQuestion}">
질문 내용이 여기에 표시됩니다.
</div>
<i class="faq-icon fas fa-chevron-down"></i>
</div>
<div class="faq-answer">
<div class="faq-answer-content" th:utext="${faq.faqAnswer}">
답변 내용이 여기에 표시됩니다.
</div>
<!-- FAQ Accordion -->
<div class="faq-accordion" th:if="${!#lists.isEmpty(page.content)}">
<div class="faq-item" th:each="faq : ${page.content}">
<div class="faq-question">
<span class="faq-question-text" th:text="${faq.faqQuestion}">질문 내용이 여기에 표시됩니다.</span>
<span class="faq-icon">
<svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1L7 7L13 1" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
</div>
<div class="faq-answer">
<div class="faq-answer-content" th:utext="${faq.faqAnswer}">
답변 내용이 여기에 표시됩니다.
</div>
</div>
</div>
<!-- Empty State -->
<div class="table-empty" th:if="${#lists.isEmpty(page.content)}">
<div class="empty-icon">
<img th:src="@{/img/img_nodata.png}" alt="데이터 없음">
</div>
<p class="empty-text">조회된 검색 결과가 없습니다.</p>
</div>
<!-- Pagination -->
<div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}">
</div>
</div>
</div>
<!-- Empty State -->
<div class="user-empty-state" th:if="${#lists.isEmpty(page.content)}">
<div class="empty-icon">
<img th:src="@{/img/img_nodata.png}" alt="데이터 없음">
</div>
<h3>조회된 FAQ가 없습니다</h3>
<p>검색 조건을 변경하여 다시 시도해주세요.</p>
</div>
<!-- Pagination -->
<div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}">
</div>
</section>
</th:block>
</body>
<th:block layout:fragment="contentScript">
<script>
function fn_select_page(pageNo) {
document.articleForm.page.value = pageNo;
document.articleForm.action = '[[@{/faq_list}]]';
document.articleForm.submit();
document.faqForm.page.value = pageNo;
document.faqForm.submit();
}
function fn_search_article() {
document.articleForm.page.value = 1;
document.articleForm.submit();
function fn_search_faq() {
document.faqForm.page.value = 1;
document.faqForm.submit();
}
function decodeHTMLEntities(text) {
@@ -105,23 +105,19 @@
e.preventDefault();
const faqItem = this.closest('.faq-item');
const faqAnswer = faqItem.querySelector('.faq-answer');
const isActive = faqItem.classList.contains('active');
// Close all other items (optional - for single open behavior)
// document.querySelectorAll('.faq-item.active').forEach(function(item) {
// item.classList.remove('active');
// });
// Toggle active state
faqItem.classList.toggle('active');
// Toggle answer visibility with animation
if (isActive) {
faqAnswer.classList.remove('show');
} else {
faqAnswer.classList.add('show');
}
});
});
});
</script>
</th:block>
</html>