fix: FAQ , Q&A , 피드백 반응형 퍼블리싱

This commit is contained in:
hong
2026-07-24 17:41:39 +09:00
parent 3eedc522e2
commit d5f0f3aab4
20 changed files with 1333 additions and 4329 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

@@ -8,20 +8,39 @@
// -----------------------------------------------------------------------------
// FAQ Accordion - Figma Design
.faq-container {
.table-controls {
@media (max-width: $breakpoint-sm) {
display: contents;
padding: 0;
}
}
}
.faq-accordion {
display: flex;
flex-direction: column;
gap: 6px;
margin-bottom: $spacing-2xl;
@media (max-width: $breakpoint-sm) {
margin-top: 24px;
}
.faq-item {
position: relative;
border-radius: 10px;
background: #fff;
transition: $transition-base;
// Active state - when accordion is open
&.active {
&:hover {
.faq-question {
background: #4685ef;
border-color: #e8dddd;
@@ -33,14 +52,8 @@
.faq-icon {
color: #fff;
transform: rotate(180deg);
}
}
.faq-answer {
display: block;
animation: slideDown 0.3s ease;
}
}
}
@@ -1,3 +1,5 @@
@use '../abstracts/variables' as *;
// -----------------------------------------------------------------------------
// DJBank Custom — Q&A 댓글 영역 (DJPGPT0002)
// 설계서: DJPCSQ0300P 참조
@@ -160,7 +162,8 @@
}
.djb-comment-form {
display: block;
display: flex;
flex-direction: column;
width: 100%;
}
@@ -202,6 +205,17 @@ textarea.djb-comment-input {
justify-content: space-between;
width: 100%;
margin-top: 12px;
gap: 12px;
// Let the checkbox area and counter align naturally
.djb-comment-private-toggle {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 14px;
color: #374151;
cursor: pointer;
}
}
.djb-comment-char-counter {
@@ -223,6 +237,13 @@ button.djb-comment-submit {
font-weight: 700;
cursor: pointer;
transition: all 0.2s ease;
flex-shrink: 0;
align-self: flex-end; // Float to the right under flex-direction: column
margin-top: 12px;
@media (max-width: $breakpoint-sm) {
width: 100%; // Full width button on mobile
}
&:hover {
background-color: #f8faff;
@@ -406,6 +406,126 @@
}
}
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Inquiry List Specific Overrides (Djb Q&A List Design)
// -----------------------------------------------------------------------------
.inquiry-list-container {
.table-controls {
@media (max-width: $breakpoint-sm) {
flex-direction: column !important;
align-items: stretch !important;
gap: 24px !important;
.search-field {
order: 1 !important;
}
.total-count {
display: flex !important;
align-items: center;
width: 100%;
order: 2 !important;
text-align: left;
font-size: 16px;
color: #000;
padding-bottom: 6px;
border-bottom: 1.5px solid #212529;
margin-bottom: 0;
.inquiry-visibility-notice {
margin-left: auto !important;
}
}
}
}
}
.inquiry-list-container {
.board-table {
.board-table-row {
.row-cell {
&--number {
width: 80px;
}
&--title {
flex: 1;
min-width: 200px;
max-width: 500px;
@media (max-width: $breakpoint-sm) {
max-width: none;
}
.notice-title-link {
display: inline-flex;
align-items: center;
max-width: 100%;
.file-icon {
display: inline-flex;
align-items: center;
flex-shrink: 0;
margin-right: 6px;
color: #888;
}
.inquiry-private-label {
color: #8c959f;
font-style: italic;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 320px;
}
.inquiry-subject-text {
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 240px;
vertical-align: middle;
}
.inquiry-comment-count {
color: #0049b4;
font-weight: 600;
margin-left: 6px;
flex-shrink: 0;
font-size: 14px;
}
}
}
&--writer {
width: 120px;
.inquiry-writer-org {
font-size: 12px;
color: #666;
margin-left: 4px;
}
}
&--status {
width: 120px;
}
&--views {
width: 80px;
}
&--date {
width: 120px;
}
}
}
}
}
// -----------------------------------------------------------------------------
// List Table - Modern design with colored header (Figma: 984-2173)
// Used in: User management, API key list, etc.
@@ -828,6 +828,13 @@
gap: 10px;
margin-top: 40px;
@media (max-width: $breakpoint-sm) {
flex-direction: column; // Stack vertically on mobile
align-items: stretch;
width: 100%;
gap: 12px;
}
.btn-cancel {
width: 100px;
height: 48px;
@@ -844,6 +851,11 @@
&:hover {
background-color: #f8f9fa;
}
@media (max-width: $breakpoint-sm) {
width: 100%;
order: 2; // Position Cancel at the bottom
}
}
.btn-submit {
@@ -860,6 +872,12 @@
&:hover {
background-color: darken(#2a69de, 5%);
}
@media (max-width: $breakpoint-sm) {
width: 100%;
order: 1; // Position Submit at the top
max-width: 100%;
}
}
}
}
}
@@ -105,7 +105,7 @@
gap: 15px;
@media (max-width: $breakpoint-md) {
flex-direction: column;
// flex-direction: column;
align-items: flex-start;
gap: 8px;
}
@@ -159,6 +159,18 @@
border: 1px solid #b3d4ff;
}
&--test {
background: #e6fffa;
color: #0d9488;
border: 1px solid #99f6e4;
}
&--normal {
background: #f3f4f6;
color: #374151;
border: 1px solid #e5e7eb;
}
&.lg {
height: 28px;
@@ -330,12 +342,15 @@
border-radius: 8px;
font-size: 13px;
color: #555555;
white-space: nowrap;
font-weight: 500;
word-break: break-all;
max-width: 100%;
box-sizing: border-box;
strong {
color: #0a4ea3;
font-weight: 700;
flex-shrink: 0;
}
}
@@ -14,61 +14,144 @@
@include respond-to('sm') {
margin-bottom: $spacing-3xl;
}
}
.page-header-content {
max-width: 800px;
margin: 0 auto;
// Recent applications accordion block styling
.recent-apps {
margin-bottom: 70px;
.recent-apps-title {
font-size: 18px;
font-weight: 700;
color: #0f172a;
margin-bottom: 4px;
}
.page-title {
font-size: $font-size-3xl;
font-weight: $font-weight-bold;
color: $text-dark;
margin-bottom: $spacing-md;
.recent-apps-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 12px;
}
.recent-apps-item {
border: 1px solid #e2e8f0;
border-radius: 8px;
overflow: hidden;
background: #ffffff;
transition: border-color 0.2s ease;
&.active {
border-color: #2a69de;
box-shadow: 0 4px 12px rgba(42, 105, 222, 0.08);
.recent-apps-arrow {
transform: rotate(180deg);
}
}
}
.recent-apps-head {
display: flex;
align-items: center;
justify-content: center;
gap: $spacing-md;
width: 100%;
padding: 8px 20px;
background: none;
border: none;
text-align: left;
cursor: pointer;
gap: 16px;
&:focus {
outline: none;
}
}
.recent-apps-date {
font-size: 14px;
color: #64748b;
flex-shrink: 0;
width: 90px;
}
.recent-apps-subject {
font-size: 15px;
font-weight: 600;
color: #1e293b;
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.recent-apps-arrow {
color: #64748b;
transition: transform 0.2s ease;
flex-shrink: 0;
}
.recent-apps-body {
display: none;
padding: 20px;
background: #f8fafc;
border-top: 1px solid #f1f5f9;
font-size: 14px;
line-height: 1.6;
color: #334155;
word-break: break-word;
}
}
.page-title {
font-size: $font-size-3xl;
font-weight: $font-weight-bold;
color: $text-dark;
margin-bottom: $spacing-md;
display: flex;
align-items: center;
justify-content: center;
gap: $spacing-md;
@include respond-to('sm') {
font-size: $font-size-2xl;
flex-direction: column;
gap: $spacing-sm;
}
i {
color: $primary-blue;
font-size: $font-size-3xl;
@include respond-to('sm') {
font-size: $font-size-2xl;
flex-direction: column;
gap: $spacing-sm;
}
i {
color: $primary-blue;
font-size: $font-size-3xl;
@include respond-to('sm') {
font-size: $font-size-2xl;
}
}
}
}
.page-description {
font-size: $font-size-lg;
color: $text-gray;
line-height: $line-height-normal;
.page-description {
font-size: $font-size-lg;
color: $text-gray;
line-height: $line-height-normal;
@include respond-to('sm') {
font-size: $font-size-base;
}
@include respond-to('sm') {
font-size: $font-size-base;
}
.highlight {
color: $primary-blue;
font-weight: $font-weight-semibold;
position: relative;
.highlight {
color: $primary-blue;
font-weight: $font-weight-semibold;
position: relative;
&::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
right: 0;
height: 2px;
border-radius: 2px;
}
&::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
right: 0;
height: 2px;
border-radius: 2px;
}
}
}
@@ -194,27 +277,56 @@
// .form-actions moved to components/_forms.scss
// File Upload Wrapper Override for Partnership Page
.partnership-card .file-upload-wrapper {
.file-name-display {
&.has-file {
color: $text-dark;
background: rgba($primary-blue, 0.05);
border-color: $primary-blue;
font-weight: $font-weight-medium;
}
}
@media (max-width: $breakpoint-sm) {
.file-upload-btn {
order: 1;
}
.partnership-card,
.service-content {
.file-upload-wrapper {
.file-name-display {
order: 2;
&.has-file {
color: $text-dark;
background: rgba($primary-blue, 0.05);
border-color: $primary-blue;
font-weight: $font-weight-medium;
}
}
.file-remove-btn {
order: 3;
.file-upload-btn {
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-weight: 700;
height: 55px;
width: 120px;
border-radius: 10px;
font-size: 14px;
background-color: #ffffff;
color: #4685ef;
border: 1px solid #2a69de;
transition: all 0.2s ease;
&:hover {
background-color: #f8faff;
}
&:active {
transform: translateY(1px);
}
}
@media (max-width: $breakpoint-sm) {
.file-upload-btn {
order: 1;
width: 100%; // Make full-width on mobile
}
.file-name-display {
order: 2;
}
.file-remove-btn {
order: 3;
}
}
}
}
@@ -251,9 +363,10 @@
}
.file-upload-wrapper {
.file-upload-btn,
.file-remove-btn {
display: none;
}
}
}
}
@@ -139,8 +139,14 @@ $service-card-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgb
flex-direction: column;
gap: 40px;
min-width: 0; // Prevent flex item from expanding horizontally beyond parent
&.n-gap {
gap: 0;
}
}
.service-sidebar {
width: 213px;
flex-shrink: 0;
@@ -1111,7 +1117,7 @@ $o2leg-err-fg: #a23b3b;
// 연속 배치된 grid 사이 간격을 내부 gap(24px) 동일하게 유지
// (언어별 코드 블럭이 grid 나뉘어 있어 간격이 달라 보이는 문제 방지)
+ .oauth2-2legged__step-grid {
+.oauth2-2legged__step-grid {
margin-top: 24px;
}
}
@@ -1,38 +1,36 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="commentSection(inquiry, commentWritable)" class="djb-comment-section"
th:attr="data-inquiry-id=${inquiry.id},data-closed=${inquiry.inquiryStatus == 'CLOSED' ? 'true' : 'false'}">
<div th:fragment="commentSection(inquiry, commentWritable)" class="djb-comment-section"
th:attr="data-inquiry-id=${inquiry.id},data-closed=${inquiry.inquiryStatus == 'CLOSED' ? 'true' : 'false'}">
<div class="djb-comment-list-box">
<ul class="djb-comment-list" id="djbCommentList">
<li class="djb-comment-empty" id="djbCommentEmpty">아직 등록된 댓글이 없습니다.</li>
</ul>
</div>
<div class="djb-comment-list-box">
<ul class="djb-comment-list" id="djbCommentList">
<li class="djb-comment-empty" id="djbCommentEmpty">아직 등록된 댓글이 없습니다.</li>
</ul>
</div>
<div class="djb-comment-form-wrapper" th:if="${commentWritable}">
<form id="djbCommentForm" class="djb-comment-form" onsubmit="return false;">
<textarea id="djbCommentInput"
class="djb-comment-input"
rows="4"
maxlength="2000"
placeholder="내용"
required></textarea>
<div class="djb-comment-form-actions">
<label class="djb-comment-private-toggle">
<input type="checkbox" id="djbCommentPrivate"> 비공개
</label>
<span class="djb-comment-char-counter" id="djbCommentCharCounter">0 / 2000</span>
<div class="djb-comment-form-wrapper" th:if="${commentWritable}">
<form id="djbCommentForm" class="djb-comment-form" onsubmit="return false;">
<textarea id="djbCommentInput" class="djb-comment-input" rows="4" maxlength="2000" placeholder="내용"
required></textarea>
<div class="djb-comment-form-actions">
<label class="djb-comment-private-toggle">
<input type="checkbox" id="djbCommentPrivate"> 비공개
</label>
<span class="djb-comment-char-counter" id="djbCommentCharCounter">0 / 2000</span>
</div>
<button type="submit" class="djb-comment-submit">댓글달기</button>
</div>
</form>
</div>
</form>
</div>
<div class="djb-comment-form-wrapper djb-comment-readonly" th:unless="${commentWritable}">
<p class="djb-comment-readonly-msg">종료된 문의에는 댓글을 작성할 수 없습니다.</p>
<div class="djb-comment-form-wrapper djb-comment-readonly" th:unless="${commentWritable}">
<p class="djb-comment-readonly-msg">종료된 문의에는 댓글을 작성할 수 없습니다.</p>
</div>
</div>
</div>
</body>
</html>
</html>
@@ -1,56 +1,91 @@
<!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/djbank_title_layout}">
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_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>
<section layout:fragment="contentFragment">
<div class="notice-detail-container">
<!-- FAQ Header: Question and Date -->
<div class="notice-detail-header">
<h2 class="notice-detail-title">
<span th:text="${faq.faqQuestion}">FAQ 질문</span>
</h2>
<span class="notice-detail-date" th:text="${#temporals.format(faq.createdDate, 'yyyy.MM.dd')}">2025.11.01</span>
<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>
<!-- Attachment Section -->
<div class="notice-detail-attachment" th:if="${!#strings.isEmpty(faq.fileId)}">
<div class="attachment-list" th:with="fileInfo=${@fileService.findById(faq.fileId)}">
<div class="attachment-item" th:each="fileDetail, status : ${fileInfo.getFileDetails()}">
<a th:href="'javascript:fn_downloadFile(\'' + ${fileDetail.fileId} + '\',\''+ ${fileDetail.fileSn} +'\')'" class="notice-attachment-link">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 15.75C18 17.4833 17.3917 18.9583 16.175 20.175C14.9583 21.3917 13.4833 22 11.75 22C10.0167 22 8.54167 21.3917 7.325 20.175C6.10833 18.9583 5.5 17.4833 5.5 15.75V6.5C5.5 5.25 5.9375 4.1875 6.8125 3.3125C7.6875 2.4375 8.75 2 10 2C11.25 2 12.3125 2.4375 13.1875 3.3125C14.0625 4.1875 14.5 5.25 14.5 6.5V15.25C14.5 16.0167 14.2333 16.6667 13.7 17.2C13.1667 17.7333 12.5167 18 11.75 18C10.9833 18 10.3333 17.7333 9.8 17.2C9.26667 16.6667 9 16.0167 9 15.25V6H11V15.25C11 15.4667 11.0708 15.6458 11.2125 15.7875C11.3542 15.9292 11.5333 16 11.75 16C11.9667 16 12.1458 15.9292 12.2875 15.7875C12.4292 15.6458 12.5 15.4667 12.5 15.25V6.5C12.4833 5.8 12.2375 5.20833 11.7625 4.725C11.2875 4.24167 10.7 4 10 4C9.3 4 8.70833 4.24167 8.225 4.725C7.74167 5.20833 7.5 5.8 7.5 6.5V15.75C7.48333 16.9333 7.89167 17.9375 8.725 18.7625C9.55833 19.5875 10.5667 20 11.75 20C12.9167 20 13.9083 19.5875 14.725 18.7625C15.5417 17.9375 15.9667 16.9333 16 15.75V6H18V15.75Z" fill="currentColor"/>
</svg>
<span class="attachment-label">첨부파일</span>
<span class="attachment-filename">[[${fileDetail.originalFileName}]].[[${fileDetail.fileExtension}]]</span>
</a>
<th:block layout:fragment="contentFragment">
<div class="signup-guide-v2">
<!-- Hero Section -->
<section class="service-hero">
<div class="service-hero__inner">
<div class="service-hero__icon-wrapper">
<img th:src="@{/img/keyimage/faq_img.svg}" alt="FAQ 아이콘"
style="width: 100%; height: 100%; object-fit: contain;" />
</div>
<div class="service-hero__content">
<div class="service-hero__badge">
<span class="service-hero__badge-dot"></span>
<span class="service-hero__badge-text">FAQ 게시판</span>
</div>
<h1 class="service-hero__title">FAQ</h1>
<p class="service-hero__desc">서비스 이용 중 궁금한 사항에 대한 답변을 빠르게 확인해 보세요<br>카테고리별 분류와 검색을 통해 원하는 정보를 쉽게 찾아보실 수
있습니다.</p>
</div>
</div>
</section>
<!-- Main Content -->
<div class="container service-main">
<!-- Sidebar -->
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('faq')}"></th:block>
<!-- Content -->
<section class="service-content n-gap">
<!-- FAQ Header: Question and Date -->
<div class="notice-detail-header">
<p class="notice-detail-date" th:text="${#temporals.format(faq.createdDate, 'yyyy.MM.dd')}">2025.11.01</p>
<div class="notice-detail-title-wrapper">
<span class="faq-question-q"
style="font-family: 'OneShinhan', sans-serif; font-size: 28px; font-weight: 700; color: #4685ef; margin-right: 15px; line-height: 1;">Q</span>
<h2 class="notice-detail-title" th:text="${faq.faqQuestion}">FAQ 질문</h2>
</div>
</div>
<!-- Attachment Section -->
<div class="notice-detail-attachment" th:if="${!#strings.isEmpty(faq.fileId)}">
<div class="attachment-list" th:with="fileInfo=${@fileService.findById(faq.fileId)}"
th:if="${fileInfo != null}">
<div class="attachment-item" th:each="fileDetail, status : ${fileInfo.getFileDetails()}">
<a th:href="'javascript:fn_downloadFile(\'' + ${fileDetail.fileId} + '\',\''+ ${fileDetail.fileSn} +'\')'"
class="notice-attachment-link">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M18 15.75C18 17.4833 17.3917 18.9583 16.175 20.175C14.9583 21.3917 13.4833 22 11.75 22C10.0167 22 8.54167 21.3917 7.325 20.175C6.10833 18.9583 5.5 17.4833 5.5 15.75V6.5C5.5 5.25 5.9375 4.1875 6.8125 3.3125C7.6875 2.4375 8.75 2 10 2C11.25 2 12.3125 2.4375 13.1875 3.3125C14.0625 4.1875 14.5 5.25 14.5 6.5V15.25C14.5 16.0167 14.2333 16.6667 13.7 17.2C13.1667 17.7333 12.5167 18 11.75 18C10.9833 18 10.3333 17.7333 9.8 17.2C9.26667 16.6667 9 16.0167 9 15.25V6H11V15.25C11 15.4667 11.0708 15.6458 11.2125 15.7875C11.3542 15.9292 11.5333 16 11.75 16C11.9667 16 12.1458 15.9292 12.2875 15.7875C12.4292 15.6458 12.5 15.4667 12.5 15.25V6.5C12.4833 5.8 12.2375 5.20833 11.7625 4.725C11.2875 4.24167 10.7 4 10 4C9.3 4 8.70833 4.24167 8.225 4.725C7.74167 5.20833 7.5 5.8 7.5 6.5V15.75C7.48333 16.9333 7.89167 17.9375 8.725 18.7625C9.55833 19.5875 10.5667 20 11.75 20C12.9167 20 13.9083 19.5875 14.725 18.7625C15.5417 17.9375 15.9667 16.9333 16 15.75V6H18V15.75Z"
fill="currentColor" />
</svg>
<span class="attachment-label">첨부파일</span>
<span
class="attachment-filename">[[${fileDetail.originalFileName}]].[[${fileDetail.fileExtension}]]</span>
</a>
</div>
</div>
</div>
<!-- Content Section -->
<div class="notice-detail-content">
<div id="faqDetail" class="notice-content-body editor-content" th:utext="${faq.faqAnswer}">
답변 내용이 여기에 표시됩니다.
</div>
</div>
<!-- Action Buttons -->
<div class="notice-detail-actions">
<button type="button" class="btn-action-primary btn-notice-list"
th:onclick="|location.href='@{/faq_list}'|">목록</button>
</div>
</section>
</div>
</div>
<!-- Content Section -->
<div class="notice-detail-content">
<div id="faqDetail" class="notice-content-body editor-content" th:utext="${faq.faqAnswer}">
답변 내용이 여기에 표시됩니다.
</div>
</div>
<!-- Action Buttons -->
<div class="notice-detail-actions">
<button type="button" class="btn-notice-list" th:onclick="|location.href='@{/faq_list}'|">목록</button>
</div>
</div>
</section>
</th:block>
</body>
<th:block layout:fragment="contentScript">
@@ -65,7 +100,7 @@
return textArea.value;
}
document.addEventListener('DOMContentLoaded', function() {
document.addEventListener('DOMContentLoaded', function () {
var element = document.getElementById('faqDetail');
if (element) {
element.innerHTML = decodeHTMLEntities(element.innerHTML);
@@ -74,4 +109,4 @@
</script>
</th:block>
</html>
</html>
@@ -3,6 +3,13 @@
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_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">
<div class="signup-guide-v2">
<!-- Hero Section -->
@@ -26,85 +33,77 @@
</section>
<!-- Main Content -->
<div class="service-main">
<div class="container service-main">
<!-- Sidebar -->
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('faq')}"></th:block>
<!-- Content -->
<section class="service-content">
<div class="notice-list-container"
style="padding: 0; min-height: auto; margin: 0; background: transparent; box-shadow: none;">
<section class="service-content n-gap faq-container">
<!-- 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="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">
<div class="table-controls">
<h2 class="total-count">
<strong th:text="${page.totalElements}">0</strong>
</h2>
<div class="table-controls">
<h2 class="total-count">
<strong th:text="${page.totalElements}">0</strong>
</h2>
<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>
</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">
<span class="faq-question-q"
style="font-family: 'OneShinhan', sans-serif; font-size: 20px; font-weight: 700; color: #555; margin-right: 15px;">Q</span>
<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 editor-content" th:utext="${faq.faqAnswer}">
답변 내용이 여기에 표시됩니다.
</div>
</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>
</div>
</form>
<!-- Empty State -->
<div class="user-empty-state" th:if="${#lists.isEmpty(page.content)}">
<div class="empty-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 120 120" fill="none"
role="img" aria-label="데이터 없음">
<circle cx="60" cy="60" r="50" fill="#F3F4F6" />
<rect x="30" y="35" width="60" height="50" rx="4" fill="#E5E7EB" stroke="#D1D5DB" stroke-width="2" />
<line x1="40" y1="50" x2="80" y2="50" stroke="#9CA3AF" stroke-width="2" stroke-linecap="round" />
<line x1="40" y1="60" x2="72" y2="60" stroke="#9CA3AF" stroke-width="2" stroke-linecap="round" />
<line x1="40" y1="70" x2="65" y2="70" stroke="#9CA3AF" stroke-width="2" stroke-linecap="round" />
<circle cx="85" cy="80" r="14" fill="#F9FAFB" stroke="#D1D5DB" stroke-width="2" />
<line x1="95" y1="90" x2="102" y2="97" stroke="#D1D5DB" stroke-width="3" stroke-linecap="round" />
<line x1="81" y1="80" x2="89" y2="80" stroke="#9CA3AF" stroke-width="2" stroke-linecap="round" />
</svg>
<!-- FAQ Accordion (Click to Redirect) -->
<div class="faq-accordion" th:if="${!#lists.isEmpty(page.content)}">
<div class="faq-item faq-item--link" th:each="faq : ${page.content}"
th:onclick="|location.href='@{/faq_view(id=${faq.id})}'|" style="cursor: pointer;">
<div class="faq-question">
<span class="faq-question-q"
style="font-family: 'OneShinhan', sans-serif; font-size: 20px; font-weight: 700; color: #555; margin-right: 15px;">Q</span>
<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" transform="rotate(-90 7 4)" />
</svg>
</span>
</div>
<h3>조회된 FAQ가 없습니다</h3>
<p>검색 조건을 변경하여 다시 시도해주세요.</p>
</div>
</div>
<!-- Pagination -->
<div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}">
<!-- Empty State -->
<div class="user-empty-state" th:if="${#lists.isEmpty(page.content)}">
<div class="empty-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 120 120" fill="none"
role="img" aria-label="데이터 없음">
<circle cx="60" cy="60" r="50" fill="#F3F4F6" />
<rect x="30" y="35" width="60" height="50" rx="4" fill="#E5E7EB" stroke="#D1D5DB" stroke-width="2" />
<line x1="40" y1="50" x2="80" y2="50" stroke="#9CA3AF" stroke-width="2" stroke-linecap="round" />
<line x1="40" y1="60" x2="72" y2="60" stroke="#9CA3AF" stroke-width="2" stroke-linecap="round" />
<line x1="40" y1="70" x2="65" y2="70" stroke="#9CA3AF" stroke-width="2" stroke-linecap="round" />
<circle cx="85" cy="80" r="14" fill="#F9FAFB" stroke="#D1D5DB" stroke-width="2" />
<line x1="95" y1="90" x2="102" y2="97" stroke="#D1D5DB" stroke-width="3" stroke-linecap="round" />
<line x1="81" y1="80" x2="89" y2="80" stroke="#9CA3AF" stroke-width="2" stroke-linecap="round" />
</svg>
</div>
<h3>조회된 FAQ가 없습니다</h3>
<p>검색 조건을 변경하여 다시 시도해주세요.</p>
</div>
<!-- Pagination -->
<div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}">
</div>
</section>
</div>
@@ -124,37 +123,6 @@
document.faqForm.page.value = 1;
document.faqForm.submit();
}
function decodeHTMLEntities(text) {
var textArea = document.createElement('textarea');
textArea.innerHTML = text;
return textArea.value;
}
document.addEventListener('DOMContentLoaded', function () {
// Decode HTML entities in FAQ answers
document.querySelectorAll('.faq-answer-content').forEach(function (element) {
element.innerHTML = decodeHTMLEntities(element.innerHTML);
});
// FAQ Accordion functionality
document.querySelectorAll('.faq-question').forEach(function (question) {
question.addEventListener('click', function (e) {
e.preventDefault();
const faqItem = this.closest('.faq-item');
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');
});
});
});
</script>
</th:block>
@@ -16,7 +16,7 @@
<section class="service-hero">
<div class="service-hero__inner">
<div class="service-hero__icon-wrapper">
<img th:src="@{/img/keyimage/q&a_img.svg}" alt="Q&A 아이콘"
<img th:src="@{/img/keyimage/q&a_img.png}" alt="Q&A 아이콘"
style="width: 100%; height: 100%; object-fit: contain;" />
</div>
<div class="service-hero__content">
@@ -32,7 +32,7 @@
</section>
<!-- Main Content -->
<div class="service-main">
<div class="container service-main">
<!-- Sidebar -->
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('qna')}"></th:block>
@@ -73,7 +73,8 @@
문의 내용이 여기에 표시됩니다.
</div>
<!-- Attachment: 강제 다운로드 링크만 제공 (이미지 인라인 노출 안 함) -->
<div class="inquiry-detail-attach" th:if="${inquiry.attachFile != null and !inquiry.attachFile.isEmpty()}">
<div class="inquiry-detail-attach"
th:if="${inquiry.attachFile != null and !inquiry.attachFile.isEmpty()}">
<a th:href="@{/file/download(fileId=${inquiry.attachFile}, fileSn=1)}" class="inquiry-attach-link"
download>
첨부 이미지 다운로드
@@ -127,7 +128,7 @@
<th:block layout:fragment="contentScript">
<script th:inline="javascript">
$(document).ready(function () {
var successMsg = [[${success}]];
var successMsg = [[${ success }]];
if (successMsg) {
customPopups.showAlert(successMsg);
}
@@ -1,123 +1,115 @@
<!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/djbank_title_layout}">
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_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>Q&A</h1>
</div>
</section>
<section layout:fragment="title">
<div class="page-title-banner">
<img th:src="@{/img/img_title_bg.png}" class="title-image" alt="타이틀 배경">
<h1>Q&A</h1>
</div>
</section>
<th:block layout:fragment="contentFragment" th:with="isNew=${inquiry.id == null}">
<div class="signup-guide-v2">
<!-- Hero Section -->
<section class="service-hero">
<div class="service-hero__inner">
<div class="service-hero__icon-wrapper">
<img th:src="@{/img/keyimage/q&a_img.svg}" alt="Q&A 아이콘"
style="width: 100%; height: 100%; object-fit: contain;" />
</div>
<div class="service-hero__content">
<div class="service-hero__badge">
<span class="service-hero__badge-dot"></span>
<span class="service-hero__badge-text">Q&A 게시판</span>
<th:block layout:fragment="contentFragment" th:with="isNew=${inquiry.id == null}">
<div class="signup-guide-v2">
<!-- Hero Section -->
<section class="service-hero">
<div class="service-hero__inner">
<div class="service-hero__icon-wrapper">
<img th:src="@{/img/keyimage/q&a_img.png}" alt="Q&A 아이콘"
style="width: 100%; height: 100%; object-fit: contain;" />
</div>
<h1 class="service-hero__title">Q&A</h1>
<p class="service-hero__desc">DJBank 오픈 API 이용 중 발생한 의문점이나 불편 사항을 보내주세요<br>접수해주신 문의 사항은 담당자 확인 후 빠르게 안내해 드리겠습니다.</p>
</div>
</div>
</section>
<!-- Main Content -->
<div class="service-main">
<!-- Sidebar -->
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('qna')}"></th:block>
<!-- Content -->
<section class="service-content">
<div class="djb-board-write-container">
<!-- Title Bar -->
<div class="djb-board-write-header">
<h2 class="title" th:text="${isNew ? '질문하기' : '질문 수정'}">질문하기</h2>
<p class="subtitle">궁금하신 사항을 작성해 주세요.</p>
<div class="service-hero__content">
<div class="service-hero__badge">
<span class="service-hero__badge-dot"></span>
<span class="service-hero__badge-text">Q&A 게시판</span>
</div>
<h1 class="service-hero__title">Q&A</h1>
<p class="service-hero__desc">DJBank 오픈 API 이용 중 발생한 의문점이나 불편 사항을 보내주세요<br>접수해주신 문의 사항은 담당자 확인 후 빠르게 안내해
드리겠습니다.</p>
</div>
<!-- Form Content -->
<form name="inquiryForm" id="inquiryForm" th:action="${isNew}? @{/inquiry} : @{/inquiry/edit}"
th:object="${inquiry}" method="post" enctype="multipart/form-data" class="djb-board-form">
<input type="hidden" th:field="*{id}" th:if="${!isNew}">
<!-- Subject Field -->
<div class="form-group">
<label class="djb-label" for="inquirySubject">제목 <span class="required">*</span></label>
<input type="text"
id="inquirySubject"
th:field="*{inquirySubject}"
class="djb-input"
placeholder="제목을 입력하세요"
required
maxlength="200">
</div>
<!-- Content Field -->
<div class="form-group">
<label class="djb-label" for="inquiryDetail">내용 <span class="required">*</span></label>
<textarea id="inquiryDetail"
th:field="*{inquiryDetail}"
class="djb-textarea"
placeholder="내용을 입력하세요"
required
maxlength="2000"></textarea>
<div class="djb-char-counter-wrapper">
<span class="char-counter">0 / 2000</span>
</div>
</div>
<!-- Visibility Field -->
<div class="form-group">
<label class="djb-label" for="inquiryVisibility">공개범위</label>
<select id="inquiryVisibility" th:field="*{visibility}" class="djb-input">
<option th:if="${allowAll}" value="ALL">전체공개</option>
<option th:if="${allowOrg}" value="ORG">법인공개</option>
<option value="PRIVATE">비공개</option>
</select>
<small class="form-help-text">비공개 글은 본인과 소속 법인 관리자만 열람할 수 있습니다.</small>
</div>
<!-- Image Attach Field -->
<div class="form-group">
<label class="djb-label" for="inquiryImage">이미지 첨부</label>
<input type="file" id="inquiryImage" name="image" class="djb-input"
accept="image/png,image/jpeg,image/gif">
<small class="form-help-text">jpg, jpeg, png, gif 이미지 1개만 첨부할 수 있습니다.</small>
<small th:if="${!isNew and inquiry.attachFile != null and !inquiry.attachFile.isEmpty()}"
class="form-help-text">현재 첨부된 이미지가 있습니다. 새 파일을 선택하면 교체됩니다.</small>
</div>
</form>
<!-- Form Actions -->
<div class="form-actions">
<button type="button" class="btn-cancel" th:onclick="|location.href='@{/inquiry}'|">취소</button>
<button type="button" class="btn-submit btn-submit-form">
<span th:text="${isNew ? '등록' : '수정'}">등록</span>
</button>
</div>
</div>
</section>
<!-- Main Content -->
<div class="service-main">
<!-- Sidebar -->
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('qna')}"></th:block>
<!-- Content -->
<section class="service-content">
<div class="djb-board-write-container">
<!-- Title Bar -->
<div class="djb-board-write-header">
<h2 class="title" th:text="${isNew ? '질문하기' : '질문 수정'}">질문하기</h2>
<p class="subtitle">궁금하신 사항을 작성해 주세요.</p>
</div>
<!-- Form Content -->
<form name="inquiryForm" id="inquiryForm" th:action="${isNew}? @{/inquiry} : @{/inquiry/edit}"
th:object="${inquiry}" method="post" enctype="multipart/form-data" class="djb-board-form">
<input type="hidden" th:field="*{id}" th:if="${!isNew}">
<!-- Subject Field -->
<div class="form-group">
<label class="djb-label" for="inquirySubject">제목 <span class="required">*</span></label>
<input type="text" id="inquirySubject" th:field="*{inquirySubject}" class="djb-input"
placeholder="제목을 입력하세요" required maxlength="200">
</div>
<!-- Content Field -->
<div class="form-group">
<label class="djb-label" for="inquiryDetail">내용 <span class="required">*</span></label>
<textarea id="inquiryDetail" th:field="*{inquiryDetail}" class="djb-textarea" placeholder="내용을 입력하세요"
required maxlength="2000"></textarea>
<div class="djb-char-counter-wrapper">
<span class="char-counter">0 / 2000</span>
</div>
</div>
<!-- Visibility Field -->
<div class="form-group">
<label class="djb-label" for="inquiryVisibility">공개범위</label>
<select id="inquiryVisibility" th:field="*{visibility}" class="djb-input">
<option th:if="${allowAll}" value="ALL">전체공개</option>
<option th:if="${allowOrg}" value="ORG">법인공개</option>
<option value="PRIVATE">비공개</option>
</select>
<small class="form-help-text">비공개 글은 본인과 소속 법인 관리자만 열람할 수 있습니다.</small>
</div>
<!-- Image Attach Field -->
<div class="form-group">
<label class="djb-label" for="inquiryImage">이미지 첨부</label>
<input type="file" id="inquiryImage" name="image" class="djb-input"
accept="image/png,image/jpeg,image/gif">
<small class="form-help-text">jpg, jpeg, png, gif 이미지 1개만 첨부할 수 있습니다.</small>
<small th:if="${!isNew and inquiry.attachFile != null and !inquiry.attachFile.isEmpty()}"
class="form-help-text">현재 첨부된 이미지가 있습니다. 새 파일을 선택하면 교체됩니다.</small>
</div>
</form>
<!-- Form Actions -->
<div class="form-actions">
<button type="button" class="btn-cancel" th:onclick="|location.href='@{/inquiry}'|">취소</button>
<button type="button" class="btn-submit btn-submit-form">
<span th:text="${isNew ? '등록' : '수정'}">등록</span>
</button>
</div>
</div>
</section>
</div>
</div>
</div>
</th:block>
</th:block>
</body>
<th:block layout:fragment="contentScript">
<script th:if="${error}" th:inline="javascript">
$(document).ready(function () {
const errorMsg = [[${error}]];
const errorMsg = [[${ error }]];
customPopups.showAlert(errorMsg);
});
</script>
@@ -133,7 +125,7 @@
const maxLength = inquiryDetailTextarea.getAttribute('maxlength');
charCounter.textContent = currentLength + ' / ' + maxLength;
inquiryDetailTextarea.addEventListener('input', function() {
inquiryDetailTextarea.addEventListener('input', function () {
const currentLength = this.value.length;
const maxLength = this.getAttribute('maxlength');
charCounter.textContent = currentLength + ' / ' + maxLength;
@@ -182,4 +174,5 @@
});
</script>
</th:block>
</html>
</html>
@@ -3,20 +3,26 @@
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_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>Q&A</h1>
</div>
</section>
<th:block layout:fragment="contentFragment">
<div class="signup-guide-v2">
<!-- Hero Section -->
<section class="service-hero">
<div class="service-hero__inner">
<div class="service-hero__icon-wrapper">
<!-- Used notice icon as a placeholder, can be updated if inquiry icon exists -->
<img th:src="@{/img/keyimage/notice_img.svg}" alt="Q&A 아이콘"
<img th:src="@{/img/keyimage/q&a_img.png}" alt="Q&A 아이콘"
style="width: 100%; height: 100%; object-fit: contain;" />
</div>
<div class="service-hero__content">
<div class="service-hero__badge">
<span class="service-hero__badge-dot"></span>
<span class="service-hero__badge-text">공지사항 목록</span>
<span class="service-hero__badge-text">Q&A 게시판</span>
</div>
<h1 class="service-hero__title">Q&A</h1>
<p class="service-hero__desc">DJBank 오픈 API 이용 중 발생한 의문점이나 불편 사항을 보내주세요<br>접수해주신 문의 사항은 담당자 확인 후 빠르게 안내해
@@ -26,12 +32,12 @@
</section>
<!-- Main Content -->
<div class="service-main">
<div class="container service-main">
<!-- Sidebar -->
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('qna')}"></th:block>
<!-- Content -->
<section class="service-content">
<section class="service-content n-gap">
<section class="user-management-container inquiry-list-container"
style="padding: 0; min-height: auto; margin: 0; background: transparent; box-shadow: none;">
@@ -43,8 +49,10 @@
<div class="table-controls">
<h2 class="total-count">
<strong th:text="${page.totalElements}">0</strong>
<span class="inquiry-visibility-notice" th:if="${visibilityCeiling != null}">
공개범위 설정: <span th:text="${visibilityCeiling.label()}">법인공개</span>
<span class="inquiry-visibility-notice" th:if="${visibilityCeiling != null}"
style="font-size: 13px; color: #666; font-weight: normal; margin-left: 10px;">
공개범위 설정: <span th:text="${visibilityCeiling.label()}"
style="font-weight: 600; color: #0049b4;">법인공개</span>
</span>
</h2>
@@ -69,7 +77,7 @@
<div class="board-table-header">
<div class="header-cell" style="width: 80px;">NO</div>
<div class="header-cell" style="flex: 1; min-width: 200px;">제목</div>
<div class="header-cell" style="width: 100px;">작성자</div>
<div class="header-cell" style="width: 120px;">작성자</div>
<div class="header-cell" style="width: 120px;">처리상태</div>
<div class="header-cell" style="width: 80px;">조회수</div>
<div class="header-cell" style="width: 120px;">등록일</div>
@@ -81,41 +89,50 @@
th:classappend="${inquiry.privatePlaceholder} ? ' board-table-row--private'"
th:onclick="${inquiry.privatePlaceholder} ? null : ('location.href=\'' + @{/inquiry/detail(id=${inquiry.id})} + '\'')"
th:style="${inquiry.privatePlaceholder} ? 'cursor: default;' : 'cursor: pointer;'">
<div class="row-cell row-cell--number" style="width: 80px;" data-label="NO"
<div class="row-cell row-cell--number" data-label="NO"
th:text="${page.totalElements - (page.number * page.size) - status.index}">1</div>
<div class="row-cell row-cell--title" style="flex: 1; min-width: 200px;" data-label="제목">
<div class="row-cell row-cell--title" data-label="제목">
<a th:href="${inquiry.privatePlaceholder} ? null : @{/inquiry/detail(id=${inquiry.id})}"
class="notice-title-link"
th:classappend="${inquiry.privatePlaceholder} ? ' notice-title-link--disabled'">
<span class="notice-number"
th:text="|[${page.totalElements - (page.number * page.size) - status.index}]|">[1]</span>
<svg th:if="${inquiry.visibility == 'PRIVATE'}" class="inquiry-lock-icon" width="14" height="14"
viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="비공개">
<rect x="4" y="10" width="16" height="11" rx="2" fill="currentColor" />
<path d="M7 10V7a5 5 0 0 1 10 0v3" stroke="currentColor" stroke-width="2" fill="none" />
</svg>
<!-- Private/Lock Icon -->
<span class="file-icon" th:if="${inquiry.visibility == 'PRIVATE'}">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"
aria-label="비공개">
<rect x="4" y="10" width="16" height="11" rx="2" fill="currentColor" />
<path d="M7 10V7a5 5 0 0 1 10 0v3" stroke="currentColor" stroke-width="2" fill="none" />
</svg>
</span>
<span th:if="${inquiry.privatePlaceholder}" class="inquiry-private-label">비공개 게시물</span>
<span th:unless="${inquiry.privatePlaceholder}" th:text="${inquiry.inquirySubject}">질문 제목</span>
<span th:unless="${inquiry.privatePlaceholder}" th:text="${inquiry.inquirySubject}"
class="inquiry-subject-text">질문 제목</span>
<!-- Comment Count -->
<span class="inquiry-comment-count"
th:if="${commentCounts != null and commentCounts.get(inquiry.id) != null and commentCounts.get(inquiry.id) > 0}"
th:text="|[${commentCounts.get(inquiry.id)}]|">[3]</span>
</a>
</div>
<div class="row-cell row-cell--writer" style="width: 100px;" data-label="작성자">
<div class="row-cell row-cell--writer" data-label="작성자">
<span class="inquiry-writer-name" th:text="${inquiry.maskedInquirerName}">홍**</span>
<span class="inquiry-writer-org"
th:if="${inquiry.inquirerOrgName != null and !inquiry.inquirerOrgName.isEmpty()}"
th:text="|(${inquiry.inquirerOrgName})|">(법인명)</span>
</div>
<div class="row-cell" style="width: 120px;" data-label="처리상태">
<span class="inquiry-status-badge"
th:classappend="${T(com.eactive.apim.portal.djb.community.qna.constant.DjbInquiryStatus).badgeClass(inquiry.inquiryStatus)}"
<div class="row-cell row-cell--status" data-label="처리상태">
<!-- completed for RESPONDED, closed for CLOSED, pending for PENDING, maintenance for REVIEWING -->
<span class="notice-type-badge lg"
th:classappend="${inquiry.inquiryStatus == 'RESPONDED' ? 'notice-type-badge--completed' : (inquiry.inquiryStatus == 'CLOSED' ? 'notice-type-badge--closed' : (inquiry.inquiryStatus == 'REVIEWING' ? 'notice-type-badge--maintenance' : 'notice-type-badge--pending'))}"
th:text="${T(com.eactive.apim.portal.djb.community.qna.constant.DjbInquiryStatus).displayName(inquiry.inquiryStatus)}">
답변대기
</span>
</div>
<div class="row-cell" style="width: 80px;" data-label="조회수" th:text="${inquiry.viewCount}">0</div>
<div class="row-cell" style="width: 120px;" data-label="등록일"
<div class="row-cell row-cell--views" data-label="조회수" th:text="${inquiry.viewCount}">0</div>
<div class="row-cell row-cell--date" data-label="등록일"
th:text="${#temporals.format(inquiry.createdDate, 'yyyy.MM.dd')}">2025.01.01</div>
</div>
</div>
@@ -133,8 +150,8 @@
<!-- New Inquiry Button -->
<div style="display: flex; justify-content: center; margin-top: 40px;">
<a th:href="@{/inquiry/new}" class="btn btn-primary"
style="width: 247px; height: 60px; font-size: 20px; font-weight: 700; border-radius: 10px; display: flex; align-items: center; justify-content: center; background: #2a69de; color: #fff; text-decoration: none;">
<a th:href="@{/inquiry/new}" class="btn-action-primary btn-notice-list"
style="display: inline-flex; align-items: center; justify-content: center; text-decoration: none;">
문의하기
</a>
</div>
@@ -89,7 +89,11 @@
th:if="${notice.noticeType == '3'}">장애</span>
<span class="notice-type-badge notice-type-badge--maintenance"
th:if="${notice.noticeType == '2'}">점검</span>
<span th:text="${notice.noticeSubject}">공지사항 제목</span>
<span class="notice-type-badge notice-type-badge--test"
th:if="${notice.noticeType == '4'}">테스트</span>
<span class="notice-type-badge notice-type-badge--normal"
th:if="${notice.noticeType == '1'}">공지</span>
<span th:text="${#strings.replace(#strings.replace(#strings.replace(#strings.replace(notice.noticeSubject, '[장애]', ''), '[점검]', ''), '[테스트]', ''), '[공지]', '')}">공지사항 제목</span>
<span class="file-icon" th:if="${notice.fileId != null and !notice.fileId.isEmpty()}">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
@@ -31,39 +31,53 @@
</section>
<!-- Main Content -->
<div class="service-main">
<div class="container service-main">
<!-- Sidebar -->
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('feedback')}"></th:block>
<!-- Content -->
<section class="service-content">
<div class="djb-board-write-container">
<!-- Title Bar -->
<div class="djb-board-write-header">
<h2 class="title">피드백 / 개선 요청</h2>
<p class="subtitle">DJ Bank은 온라인 비즈니스 혁신을 위한 피드백/개선요청을 환영합니다.</p>
<!-- 상단: 최근 작성한 글 목록 -->
<div class="recent-apps">
<div class="recent-apps-header-group">
<p class="recent-apps-title" style="font-size: 24px; font-weight: 700; color: #000; margin-bottom: 8px;">
피드백 / 개선 요청</p>
<p class="recent-apps-subtitle" style="font-size: 14px; color: #7f8a95; margin-bottom: 24px;">최근 최대 3개의 나의
피드백/ 개선 요청을 보여줍니다.</p>
</div>
<!-- 내가 작성한 최근 글(최대 3건) 아코디언 -->
<div class="recent-apps" th:if="${recentApplications != null and !recentApplications.isEmpty()}">
<p class="recent-apps-title">내가 작성한 최근 글</p>
<ul class="recent-apps-list">
<li class="recent-apps-item" th:each="item : ${recentApplications}">
<button type="button" class="recent-apps-head">
<span class="recent-apps-subject" th:text="${item.bizSubject}">제목</span>
<span class="recent-apps-date"
th:text="${item.createdDate != null ? #temporals.format(item.createdDate, 'yyyy.MM.dd') : ''}">2026.07.13</span>
<svg class="recent-apps-arrow" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
<div class="recent-apps-body">
<p class="recent-apps-detail" th:text="${item.bizDetail}">내용</p>
</div>
</li>
</ul>
<ul class="recent-apps-list" th:if="${recentApplications != null and !recentApplications.isEmpty()}">
<li class="recent-apps-item" th:each="item : ${recentApplications}">
<button type="button" class="recent-apps-head">
<span class="recent-apps-date"
th:text="${item.createdDate != null ? #temporals.format(item.createdDate, 'yyyy.MM.dd') : ''}">2026.07.13</span>
<span class="recent-apps-subject" th:text="${item.bizSubject}">제목</span>
<svg class="recent-apps-arrow" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
<div class="recent-apps-body">
<p class="recent-apps-detail" th:text="${item.bizDetail}">내용</p>
</div>
</li>
</ul>
<!-- Empty State for Recent Applications -->
<div class="recent-apps-empty" th:unless="${recentApplications != null and !recentApplications.isEmpty()}"
style="padding: 35px; text-align: center; background: #f8fafc; border: 1px dashed #e2e8f0; border-radius: 8px; font-size: 14px; color: #94a3b8;">
작성하신 피드백 / 개선 요청 내역이 없습니다.
</div>
</div>
<!-- 하단: 신규 작성 폼 카드 -->
<div class="djb-board-write-container">
<!-- Title Bar -->
<div class="djb-board-write-header" style="margin-bottom: 30px;">
<h2 class="title" style="font-size: 24px; font-weight: 700; color: #000; margin-bottom: 8px;">피드백 / 개선 요청
</h2>
<p class="subtitle" style="font-size: 14px; color: #7f8a95; margin: 0;">DJ Bank은 온라인 비즈니스 혁신을 위한 피드백/개선요청을
환영합니다.</p>
</div>
<!-- Form Content -->
@@ -87,6 +101,27 @@
</div>
</div>
<!-- 파일 첨부 Field -->
<div class="form-group">
<label class="djb-label" for="file">첨부파일</label>
<div class="file-upload-wrapper" id="fileInputDisplay">
<span class="file-name-display" id="fileDisplayText">선택된 파일이 없습니다</span>
<button type="button" class="file-remove-btn" id="btnRemoveFile" style="display: none;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
<label for="file" class="file-upload-btn">
파일 선택
</label>
<input type="file" id="file" name="file" style="display: none;" />
</div>
<small class="form-help-text" style="margin-top: 8px; display: block; color: #8c959f;">
문서파일(pdf, doc, docx, xls, xlsx, ppt, pptx, hwp)과 이미지 파일(gif, jpg, png)만 등록 가능합니다.
</small>
</div>
</form>
<!-- Form Actions -->
@@ -208,6 +243,19 @@
});
}
// Recent applications accordion toggle handler
$('.recent-apps-head').on('click', function () {
const item = $(this).closest('.recent-apps-item');
const body = item.find('.recent-apps-body');
// Toggle active class and slide
item.toggleClass('active');
body.slideToggle(200);
// Optional: Close other open accordion items
item.siblings('.recent-apps-item').removeClass('active').find('.recent-apps-body').slideUp(200);
});
// Focus on subject field
document.getElementById('bizSubject').focus();
});