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,10 +14,94 @@
@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;
}
.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;
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 {
@@ -71,7 +155,6 @@
}
}
}
}
// Partnership Form Container
.partnership-form-container {
@@ -194,7 +277,9 @@
// .form-actions moved to components/_forms.scss
// File Upload Wrapper Override for Partnership Page
.partnership-card .file-upload-wrapper {
.partnership-card,
.service-content {
.file-upload-wrapper {
.file-name-display {
&.has-file {
color: $text-dark;
@@ -204,9 +289,35 @@
}
}
.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 {
@@ -218,6 +329,7 @@
}
}
}
}
// Focus states for better accessibility
@@ -251,6 +363,7 @@
}
.file-upload-wrapper {
.file-upload-btn,
.file-remove-btn {
display: none;
@@ -139,7 +139,13 @@ $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;
@@ -1,5 +1,6 @@
<!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"
@@ -13,19 +14,15 @@
<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="내용"
<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>
<button type="submit" class="djb-comment-submit">댓글달기</button>
</div>
<button type="submit" class="djb-comment-submit">댓글달기</button>
</form>
</div>
@@ -35,4 +32,5 @@
</div>
</body>
</html>
@@ -10,27 +10,60 @@
</div>
</section>
<section layout:fragment="contentFragment">
<div class="notice-detail-container">
<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">
<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>
<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)}">
<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="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"/>
<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>
<span
class="attachment-filename">[[${fileDetail.originalFileName}]].[[${fileDetail.fileExtension}]]</span>
</a>
</div>
</div>
@@ -45,12 +78,14 @@
<!-- Action Buttons -->
<div class="notice-detail-actions">
<button type="button" class="btn-notice-list" th:onclick="|location.href='@{/faq_list}'|">목록</button>
<button type="button" class="btn-action-primary btn-notice-list"
th:onclick="|location.href='@{/faq_list}'|">목록</button>
</div>
</div>
</section>
</div>
</div>
</th:block>
</body>
<th:block layout:fragment="contentScript">
@@ -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,14 +33,12 @@
</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;">
@@ -60,9 +65,10 @@
</div>
</form>
<!-- FAQ Accordion -->
<!-- FAQ Accordion (Click to Redirect) -->
<div class="faq-accordion" th:if="${!#lists.isEmpty(page.content)}">
<div class="faq-item" th:each="faq : ${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>
@@ -70,15 +76,10 @@
<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" />
stroke-linejoin="round" transform="rotate(-90 7 4)" />
</svg>
</span>
</div>
<div class="faq-answer">
<div class="faq-answer-content editor-content" th:utext="${faq.faqAnswer}">
답변 내용이 여기에 표시됩니다.
</div>
</div>
</div>
</div>
@@ -104,8 +105,6 @@
<!-- Pagination -->
<div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}">
</div>
</div>
</section>
</div>
</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>
첨부 이미지 다운로드
@@ -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">
@@ -25,7 +25,8 @@
<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>
<p class="service-hero__desc">DJBank 오픈 API 이용 중 발생한 의문점이나 불편 사항을 보내주세요<br>접수해주신 문의 사항은 담당자 확인 후 빠르게 안내해
드리겠습니다.</p>
</div>
</div>
</section>
@@ -53,24 +54,15 @@
<!-- 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">
<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>
<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>
@@ -182,4 +174,5 @@
});
</script>
</th:block>
</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="비공개">
<!-- 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,29 +31,27 @@
</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">
<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-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>
<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>
@@ -64,6 +62,22 @@
</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();
});