Faq , q&a 상세 , 피드백 , 마이페이지 이용자 관리 퍼블리싱

This commit is contained in:
hong
2026-07-09 09:47:16 +09:00
parent cc4c1a6b53
commit 4f577c8c14
25 changed files with 2577 additions and 1651 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
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 133 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 159 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 239 KiB

@@ -79,6 +79,9 @@
li.innerHTML = ''
+ '<div class="djb-comment-meta">'
+ ' <div class="djb-comment-meta-left">'
+ ' <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" style="margin-right: 2px; opacity: 0.5;">'
+ ' <path d="M5 3v5a2 2 0 0 0 2 2h5 M9 7l3 3-3 3" stroke="#000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>'
+ ' </svg>'
+ ' <span class="djb-comment-writer">' + escapeHtml(c.writerName || '(알 수 없음)') + '</span>'
+ (c.adminYn === 'Y' ? ' <span class="djb-comment-admin-badge">관리자</span>' : '')
+ ' </div>'
@@ -9,28 +9,30 @@
// FAQ Accordion - Figma Design
.faq-accordion {
background: #F6F9FB;
border-radius: $border-radius-lg;
overflow: hidden;
display: flex;
flex-direction: column;
gap: 6px;
margin-bottom: $spacing-2xl;
@include respond-to('sm') {
background: #FFFFFF;
border-radius: 0;
}
.faq-item {
border-bottom: 1px solid #DADADA;
position: relative;
border-radius: 10px;
background: #fff;
transition: $transition-base;
&:last-child {
border-bottom: none;
}
// Active state - when accordion is open
&.active {
.faq-question {
background: #4685ef;
border-color: #e8dddd;
.faq-question-q,
.faq-question-text {
color: #fff !important;
}
.faq-icon {
color: #fff;
transform: rotate(180deg);
}
}
@@ -42,33 +44,48 @@
}
}
// Question row - Figma: 18px bold, #212529
// Question row
.faq-question {
display: flex;
align-items: center;
justify-content: space-between;
padding: $spacing-md $spacing-lg;
font-size: 18px;
font-weight: $font-weight-bold;
color: #212529;
min-height: 47px;
height: auto;
padding: 8px 35px 8px 21px;
background: #fff;
border: 1px solid #dedede;
border-radius: 10px;
cursor: pointer;
transition: $transition-base;
gap: 95px;
min-height: 54px;
@media (max-width: $breakpoint-md) {
padding: $spacing-md $spacing-lg;
font-size: 16px;
gap: $spacing-lg;
padding: 12px 16px;
min-height: 44px;
}
&:hover {
background: rgba($primary-blue, 0.02);
.faq-question-q {
font-size: 20px;
font-weight: 700;
color: #555;
margin-right: 15px;
line-height: 1;
@media (max-width: $breakpoint-md) {
font-size: 16px;
margin-right: 10px;
}
}
.faq-question-text {
flex: 1;
line-height: $line-height-normal;
font-size: 14px;
color: #000;
line-height: normal;
word-break: keep-all;
@media (max-width: $breakpoint-md) {
font-size: 13px;
line-height: 1.4;
}
}
// Chevron icon
@@ -79,7 +96,7 @@
justify-content: center;
width: 22px;
height: 22px;
color: #212529;
color: #000;
transition: transform 0.3s ease;
svg {
@@ -89,49 +106,41 @@
}
}
// Answer section - white background
// Answer section
.faq-answer {
display: none;
background: #F6F9FB;
padding: $spacing-lg 20px $spacing-xl;
border-top: none;
@include respond-to('sm') {
background: #FFFFFF;
}
padding: 19px 21px 25px 21px;
@media (max-width: $breakpoint-md) {
padding: $spacing-md $spacing-lg $spacing-lg;
padding: 16px;
}
.faq-answer-content {
font-size: 18px;
font-weight: $font-weight-regular;
color: #515151;
background-color: #ffffff;
padding: $spacing-lg;
line-height: $line-height-loose;
font-size: 14px;
color: #000;
line-height: 1.6;
@media (max-width: $breakpoint-md) {
font-size: 14px;
font-size: 13px;
}
// Content formatting
p {
margin-bottom: $spacing-md;
margin-bottom: 8px;
&:last-child {
margin-bottom: 0;
}
}
ul, ol {
margin: $spacing-sm 0;
padding-left: $spacing-lg;
ul,
ol {
margin: 8px 0;
padding-left: 20px;
}
li {
margin-bottom: $spacing-xs;
margin-bottom: 4px;
}
a {
@@ -225,6 +234,7 @@
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
@@ -41,9 +41,7 @@
// ── 댓글 목록 박스 (설계서 ① 영역) ─────────────────────────
.djb-comment-list-box {
padding: 24px 28px;
background-color: #FFFFFF;
border: 1px solid #E5E7EB;
border-radius: 8px;
min-height: 120px;
margin-bottom: 28px;
@@ -64,16 +62,16 @@
// ── 댓글 한 건 (설계서 작성자 + 날짜 + Contents) ───────────
.djb-comment-item {
padding: 18px 0;
border-bottom: 1px dashed #E5E7EB;
padding: 40px 0;
border-bottom: 1px solid #DFDFDF;
&:first-child {
padding-top: 4px;
padding-top: 20px;
}
&:last-child {
border-bottom: none;
padding-bottom: 4px;
padding-bottom: 20px;
}
&--admin .djb-comment-writer {
@@ -94,21 +92,21 @@
}
.djb-comment-writer {
font-size: 18px;
font-weight: 700;
color: #111827;
font-size: 14px;
font-weight: 500;
color: #000;
letter-spacing: -0.2px;
}
.djb-comment-admin-badge {
display: inline-flex;
align-items: center;
padding: 2px 8px;
background-color: #DBEAFE;
color: #1D4ED8;
padding: 3px 8px;
background-color: #ecf1ff;
color: #2a69de;
border-radius: 10px;
font-size: 11px;
font-weight: 600;
font-size: 9px;
font-weight: 700;
}
.djb-comment-meta-right {
@@ -123,29 +121,32 @@
}
.djb-comment-delete-btn {
background: none;
border: 1px solid #E5E7EB;
border-radius: 4px;
padding: 3px 10px;
font-size: 12px;
color: #6B7280;
background: #fbe7e9;
border: 1px solid #f1c2d6;
border-radius: 5px;
padding: 3px 6px;
font-size: 10px;
font-weight: 500;
color: #bb1026;
cursor: pointer;
transition: all 0.15s ease;
display: flex;
align-items: center;
justify-content: center;
height: 20px;
&:hover {
background-color: #FEE2E2;
border-color: #FCA5A5;
color: #B91C1C;
background-color: #f1c2d6; // Changed from darken(#fbe7e9, 5%) to fix warning
}
}
.djb-comment-body {
padding: 12px 16px;
background-color: #F3F4F6;
border-radius: 6px;
padding: 24px;
background-color: #f4f5f9;
border-radius: 10px;
font-size: 14px;
line-height: 1.6;
color: #1F2937;
line-height: 20px;
color: #000;
white-space: pre-wrap;
word-break: break-word;
}
@@ -167,31 +168,31 @@ textarea.djb-comment-input {
display: block;
width: 100%;
box-sizing: border-box;
padding: 16px 18px;
border: 1px solid #D1D5DB;
border-radius: 8px;
background-color: #F9FAFB;
padding: 14px 18px;
border: 1px solid #dfdfdf;
border-radius: 10px;
background-color: #ffffff;
font-size: 14px;
line-height: 1.6;
color: #1F2937;
color: #000;
resize: vertical;
min-height: 120px;
min-height: 103px;
font-family: inherit;
outline: none;
transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
transition: border-color 0.15s ease, box-shadow 0.15s ease;
&::placeholder {
color: #9CA3AF;
color: #7f8a95;
}
&:hover {
border-color: #9CA3AF;
border-color: #2a69de;
}
&:focus {
border-color: #1D4ED8;
border-color: #2a69de;
background-color: #FFFFFF;
box-shadow: 0 0 0 3px rgba(29, 78, 216, 0.15);
box-shadow: 0 0 0 3px rgba(42, 105, 222, 0.1);
}
}
@@ -209,28 +210,22 @@ textarea.djb-comment-input {
}
button.djb-comment-submit {
display: inline-block;
min-width: 140px;
padding: 12px 28px;
background-color: #1D4ED8;
color: #FFFFFF;
border: 1px solid #1D4ED8;
border-radius: 8px;
font-size: 15px;
display: flex;
align-items: center;
justify-content: center;
width: 149px;
height: 39px;
background-color: #ffffff;
color: #4685ef;
border: 1px solid #2a69de;
border-radius: 10px;
font-size: 14px;
font-weight: 700;
letter-spacing: -0.2px;
cursor: pointer;
appearance: none;
-webkit-appearance: none;
line-height: 1.2;
text-align: center;
box-shadow: 0 2px 6px rgba(29, 78, 216, 0.18);
transition: background-color 0.15s ease, box-shadow 0.15s ease, transform 0.05s ease;
transition: all 0.2s ease;
&:hover {
background-color: #1E40AF;
border-color: #1E40AF;
box-shadow: 0 3px 10px rgba(29, 78, 216, 0.28);
background-color: #f8faff;
}
&:active {
@@ -238,9 +233,9 @@ button.djb-comment-submit {
}
&:disabled {
background-color: #94A3B8;
border-color: #94A3B8;
box-shadow: none;
background-color: #f4f5f9;
border-color: #dfdfdf;
color: #7f8a95;
cursor: not-allowed;
}
}
@@ -13,7 +13,7 @@
label {
display: block;
font-size: $font-size-sm;
font-weight: $font-weight-medium;
font-weight: $font-weight-semibold;
color: $text-dark;
margin-bottom: $spacing-sm;
@@ -453,6 +453,7 @@ select.form-control {
.form-field-wrapper {
flex: 1;
@include respond-to('sm') {
width: 100%;
}
@@ -757,6 +758,7 @@ select.form-control {
}
.btn {
// Figma 모바일 (1291-3170): 144px × 40px, border-radius 8px, 14px bold
@include respond-to('sm') {
width: 144px !important;
@@ -960,3 +962,71 @@ select.form-control {
display: flex;
align-items: center;
}
// -----------------------------------------------------------------------------
// DJB Common Form Elements (Global Reusable Styles)
// -----------------------------------------------------------------------------
.djb-label {
display: block;
font-size: 14px;
font-weight: 700;
color: #000;
margin-bottom: 12px;
.required {
color: #e53e3e;
margin-left: 4px;
}
}
.djb-input {
width: 100%;
padding: 14px 18px;
border: 1px solid #DFDFDF;
border-radius: 8px;
font-size: 14px;
color: #000;
transition: border-color 0.2s;
background-color: #fcfcfc;
&::placeholder {
color: #94A3B8;
}
&:focus {
outline: none;
border-color: #2a69de;
}
}
.djb-textarea {
width: 100%;
padding: 14px 18px;
border: 1px solid #DFDFDF;
border-radius: 8px;
font-size: 14px;
color: #000;
resize: none;
transition: border-color 0.2s;
min-height: 250px;
&::placeholder {
color: #94A3B8;
}
&:focus {
outline: none;
border-color: #2a69de;
}
}
.djb-char-counter-wrapper {
text-align: right;
margin-top: 8px;
.char-counter {
font-size: 13px;
color: #7f8a95;
}
}
@@ -587,17 +587,17 @@
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 $spacing-sm;
height: 30px;
padding: 0 $spacing-md;
height: 28px;
white-space: nowrap;
border-radius: $border-radius-sm;
font-size: 13px;
border-radius: 5px;
font-size: 12px;
font-weight: $font-weight-regular;
font-family: 'Spoqa Han Sans Neo', sans-serif;
color: #000000;
border: none;
border: 1px solid transparent;
cursor: pointer;
transition: $transition-base;
white-space: nowrap;
&:hover {
transform: translateY(-1px);
@@ -610,35 +610,42 @@
// Button variants
&--default {
background-color: #DADADA;
background-color: #f5f5f4;
border-color: #bdc7cf;
color: #64748b;
&:hover {
background-color: darken(#DADADA, 5%);
background-color: darken(#f5f5f4, 5%);
}
}
&--primary {
background-color: #A4D6EA;
background-color: #ecf0fa;
border-color: #4685ef;
color: #2a69de;
&:hover {
background-color: darken(#A4D6EA, 5%);
background-color: darken(#ecf0fa, 5%);
}
}
&--secondary {
background-color: #CDD4F0;
background-color: #f5f5f4;
border-color: #bdc7cf;
color: #64748b;
&:hover {
background-color: darken(#CDD4F0, 5%);
background-color: darken(#f5f5f4, 5%);
}
}
&--danger {
background-color: #F8D7DA;
color: #B02A37;
background-color: #fbe7e9;
border-color: #f1c2d6;
color: #bb1026;
&:hover {
background-color: darken(#F8D7DA, 5%);
background-color: darken(#fbe7e9, 5%);
}
}
}
@@ -799,9 +799,9 @@
padding: 0 60px;
box-shadow:
0 12px 35px rgba(82, 127, 255, .12),
inset 0 1px 1px rgba(255,255,255,.95),
inset 0 -2px 8px rgba(255,255,255,.15),
inset 0 0 40px rgba(255,255,255,.28);
inset 0 1px 1px rgba(255, 255, 255, .95),
inset 0 -2px 8px rgba(255, 255, 255, .15),
inset 0 0 40px rgba(255, 255, 255, .28);
gap: 40px;
@media (max-width: 1200px) {
@@ -1006,8 +1006,8 @@
z-index: 1;
}
.header-title-box .section-header{
text-align:left;
.header-title-box .section-header {
text-align: left;
}
@@ -1038,7 +1038,7 @@
color: var(--text-dark);
margin-bottom: 12px;
letter-spacing: -0.5px;
text-align :left;
text-align: left;
@include respond-to('md') {
font-size: 36px;
@@ -1173,11 +1173,12 @@
background: linear-gradient(135deg, #50caff 0%, #2088ff 100%);
transform: translateY(-10px);
box-shadow: var(--shadow-hover);
color:white;
color: white;
.card-title ,.card-description {
color:white;
.card-title,
.card-description {
color: white;
}
}
@@ -1210,7 +1211,7 @@
font-weight: 700;
color: var(--text-dark);
margin-bottom: 12px;
text-align:left;
text-align: left;
@include respond-to('sm') {
// Figma 모바일: 15px Bold, 색상 #000000
@@ -1227,7 +1228,7 @@
font-size: 14px;
color: var(--text-gray);
line-height: 1.5;
text-align:left;
text-align: left;
@include respond-to('sm') {
// Figma 모바일: 12px Medium, 색상 #515151
@@ -1386,7 +1387,7 @@
}
.btn-secondary{
.btn-secondary {
background-color: rgba(255, 255, 255, 0.08);
color: rgb(255, 255, 255);
border-width: 1px;
@@ -1401,8 +1402,8 @@
box-shadow: rgba(0, 138, 226, 0.3) 0px 4px 15px;
}
.btn-secondary{
&:hover{
.btn-secondary {
&:hover {
background-color: rgba(255, 255, 255, 0.15);
border-color: #FFFFFF;
transform: translateY(-2px);
@@ -1410,8 +1411,8 @@
}
.btn-primary{
&:hover{
.btn-primary {
&:hover {
background-color: #00A3FF;
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 138, 226, 0.45);
@@ -1483,7 +1484,7 @@
justify-content: center;
.info-vector-art{
.info-vector-art {
width: 100%;
max-width: 400px;
}
@@ -2029,7 +2030,10 @@
color: #FFFFFF;
border: none;
h3, p, .card-icon, .card-arrow {
h3,
p,
.card-icon,
.card-arrow {
color: #FFFFFF;
}
@@ -2643,6 +2647,7 @@
font-size: 30px;
color: #000000;
line-height: normal;
font-weight: 600;
margin: 0;
font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
@@ -26,6 +26,7 @@
.inquiry-header {
margin-bottom: $spacing-3xl;
@media (max-width: $breakpoint-sm) {
margin-bottom: $spacing-2xl;
}
@@ -261,13 +262,68 @@
justify-content: center;
align-items: center;
gap: $spacing-md;
margin-top: $spacing-xl;
margin-top: 40px;
@media (max-width: $breakpoint-sm) {
flex-direction: column;
align-items: stretch;
}
// List Button - Figma Design
.btn-inquiry-new {
display: flex;
align-items: center;
justify-content: center;
width: 247px;
height: 60px;
background: #2a69de;
color: #fff;
border: none;
font-size: 20px;
font-weight: 700;
border-radius: 10px;
cursor: pointer;
text-decoration: none;
transition: $transition-base;
@media (max-width: $breakpoint-md) {
width: 100%;
height: 50px;
font-size: 16px;
}
&:hover {
background: #1d52b5; // Changed from darken(#2a69de, 10%)
transform: translateY(-2px);
}
&--outline {
background: #fff;
color: #2a69de;
border: 1px solid #2a69de;
width: 120px;
height: 50px;
font-size: 16px;
&:hover {
background: #f8faff;
}
}
&--danger {
background: #fff;
color: #bb1026;
border: 1px solid #bb1026;
width: 120px;
height: 50px;
font-size: 16px;
&:hover {
background: #fdf5f6;
}
}
}
// Layout for detail page split actions
.actions-left,
.actions-right {
@@ -307,73 +363,117 @@
}
.inquiry-detail-container {
min-height: 600px;
margin: 0 auto;
padding: 0;
background: transparent;
box-shadow: none;
min-height: auto;
margin: 0;
// Header styles
.notice-detail-header {
border-bottom: 1px solid #000;
padding-bottom: 25px;
.inquiry-header-meta {
display: flex;
gap: 6px;
font-size: 16px;
color: #767e89;
margin-bottom: 17px;
}
.notice-detail-title-wrapper {
display: flex;
align-items: flex-end;
gap: 15px;
@media (max-width: $breakpoint-md) {
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
}
.notice-detail-title {
margin: 0;
font-size: 28px;
font-weight: 500;
@media (max-width: $breakpoint-md) {
font-size: 22px;
}
}
}
// Detail body
.notice-detail-body {
padding-top: 20px;
border-bottom: 1px solid #dfdfdf;
font-size: 14px;
line-height: 20px;
color: #000;
img {
max-width: 100%;
height: auto;
}
}
// Admin response
.inquiry-response-section {
padding-top: 40px;
padding-bottom: 40px;
border-bottom: 1px solid #dfdfdf;
.inquiry-response-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0px;
border: none;
.response-admin {
display: flex;
gap: 9px;
align-items: center;
.admin-label {
font-size: 14px;
font-weight: 500;
color: #000;
}
.admin-badge {
background: #ecf1ff;
color: #2a69de;
font-size: 9px;
font-weight: 700;
padding: 3px 8px;
border-radius: 10px;
}
}
.response-meta {
color: #767e89;
font-size: 14px;
}
}
.inquiry-response-body {
background: #f4f5f9;
border-radius: 10px;
padding: 24px;
font-size: 14px;
line-height: 20px;
color: #000;
}
}
}
// Inquiry detail card
.inquiry-detail-card {
background: $white;
border-radius: $border-radius-lg;
box-shadow: $shadow-md;
overflow: hidden;
margin-bottom: $spacing-xl;
// Header section
.detail-header {
padding: $spacing-3xl $spacing-2xl $spacing-xl;
border-bottom: 2px solid $border-gray;
@media (max-width: $breakpoint-sm) {
padding: $spacing-2xl $spacing-lg $spacing-md;
}
// Status badge header styles now in components/_badges.scss
.inquiry-title {
font-size: $font-size-2xl;
font-weight: $font-weight-bold;
color: $text-dark;
line-height: $line-height-tight;
margin-bottom: $spacing-lg;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-xl;
}
}
.inquiry-meta {
display: flex;
align-items: center;
gap: $spacing-lg;
flex-wrap: wrap;
font-size: $font-size-sm;
color: $text-gray;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-xs;
gap: $spacing-md;
}
.meta-item {
display: flex;
align-items: center;
gap: $spacing-xs;
i {
color: $primary-blue;
}
strong {
color: $text-dark;
font-weight: $font-weight-medium;
}
}
}
}
// Attachment section
.detail-attachments {
// Attachment section
.detail-attachments {
padding: $spacing-lg $spacing-2xl;
background: $gray-bg;
border-bottom: 1px solid $border-gray;
@@ -420,10 +520,10 @@
}
}
}
}
}
// Content section
.detail-content {
// Content section
.detail-content {
padding: $spacing-3xl $spacing-2xl;
min-height: 200px;
@@ -441,9 +541,9 @@
font-size: $font-size-sm;
}
}
}
}
// Response section (answer from admin)
.inquiry-response-card {
background: $white;
@@ -692,3 +792,74 @@
// Uses the same action button styles from .inquiry-actions above
// Supports: action-btn-primary, action-btn-secondary
}
// -----------------------------------------------------------------------------
// New DJB Board Write Form (피드백/개선요청, Q&A 등록 등)
// -----------------------------------------------------------------------------
.djb-board-write-container {
padding: 0;
.djb-board-write-header {
margin-bottom: 30px;
padding-bottom: 20px;
.title {
font-size: 24px;
font-weight: 700;
color: #000;
margin-bottom: 8px;
}
.subtitle {
font-size: 14px;
color: #7f8a95;
}
}
.djb-board-form {
.form-group {
margin-bottom: 30px;
}
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 40px;
.btn-cancel {
width: 100px;
height: 48px;
background-color: #fff;
border: 1px solid #DFDFDF;
border-radius: 8px;
color: #515961;
font-size: 15px;
font-weight: 700;
cursor: pointer;
transition: background-color 0.2s;
display: block;
&:hover {
background-color: #f8f9fa;
}
}
.btn-submit {
width: 100px;
height: 48px;
background-color: #2a69de;
border: 1px solid #2a69de;
border-radius: 8px;
color: #fff;
font-size: 15px;
font-weight: 700;
cursor: pointer;
&:hover {
background-color: darken(#2a69de, 5%);
}
}
}
}
@@ -71,6 +71,7 @@
margin: 0 auto;
padding: $spacing-3xl 0;
min-height: 600px;
@media (max-width: $breakpoint-md) {
padding: $spacing-2xl $spacing-md;
}
@@ -78,10 +79,11 @@
// Notice Detail Header - Title and Date in one row
.notice-detail-header {
margin-bottom: 26px;
padding-bottom: 24px;
border-bottom: 1px solid #212529;
@media (max-width: $breakpoint-md) {
margin-bottom: 16px;
padding-bottom: 16px;
}
}
@@ -114,10 +116,12 @@
align-items: center;
justify-content: center;
height: 20px;
padding: 0 8px;
width: 50px;
margin-right: 6px;
border-radius: 5px;
font-size: 10px;
font-weight: 500;
box-sizing: border-box;
&--incident {
border: 1px solid #f1c2d6;
@@ -126,8 +130,33 @@
}
&--maintenance {
background: #fff4d6;
color: #9a6700;
background: #fdf4d4;
color: #ef9546;
border: 1px solid #ffcea1;
}
&--completed {
background: #e5f0ff;
color: #0a4ea3;
border: 1px solid #b3d4ff;
}
&--closed {
background: #f5f5f5;
color: #888888;
border: 1px solid #dedede;
}
&--pending {
background: #fdf4d4;
color: #ef9546;
border: 1px solid #ffcea1;
}
&--fix {
background: #e5f0ff;
color: #0a4ea3;
border: 1px solid #b3d4ff;
}
}
@@ -144,6 +173,7 @@
// Notice Attachment Section
.notice-detail-attachment {
margin-top: 20px;
margin-bottom: 20px;
padding: 15px 20px;
background: #f8fbfe;
@@ -197,13 +227,13 @@
// Incident Info Table
.notice-detail-incident-info {
margin-bottom: 10px;
margin-bottom: 20px;
.detail-table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
border-top: 1px solid #e3e8f0;
border-top: none;
border-bottom: 1px solid #e3e8f0;
tr {
@@ -274,7 +304,8 @@
}
}
ul, ol {
ul,
ol {
margin: $spacing-md 0;
padding-left: $spacing-xl;
}
@@ -321,6 +352,7 @@
}
&:active {
transform: scale(0.98);
}
}
@@ -461,7 +493,12 @@
}
}
h1, h2, h3, h4, h5, h6 {
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: $spacing-xl;
margin-bottom: $spacing-md;
font-weight: $font-weight-semibold;
@@ -484,7 +521,8 @@
}
}
ul, ol {
ul,
ol {
margin: $spacing-md 0;
padding-left: $spacing-xl;
}
@@ -759,9 +797,8 @@
}
.inquiry-content-body {
background: #F6F9FB;
border-radius: 20px;
padding: $spacing-2xl;
padding: 10px 20px;
min-height: 200px;
font-size: $font-size-base;
color: $text-dark;
@@ -139,6 +139,7 @@ $service-card-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgb
display: flex;
flex-direction: column;
gap: 40px;
min-width: 0; // Prevent flex item from expanding horizontally beyond parent
}
.service-sidebar {
@@ -163,6 +164,44 @@ $service-card-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgb
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.service-sidebar__profile {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
.avatar {
width: 53px;
height: 52px;
background-color: #9ac8ee;
border-radius: 11px;
display: flex;
align-items: center;
justify-content: center;
svg {
width: 36px;
height: 36px;
fill: #ffffff;
}
}
.user-name {
font-size: 16px;
font-weight: 500;
color: #000000;
text-align: center;
margin: 0;
}
}
.service-sidebar__divider {
height: 1px;
background-color: #e3e8f0;
margin: 4px 0;
width: 100%;
}
&__item {
font-size: 16px;
font-weight: 500;
@@ -13,6 +13,7 @@
margin: 0 auto;
padding: $spacing-3xl 0;
margin-bottom: $spacing-2xl;
min-height: 750px; // Ensure the footer doesn't float when content is short
@media (max-width: $breakpoint-sm) {
padding: $spacing-lg $spacing-md;
@@ -67,8 +68,25 @@
// Extends .list-table from components/_tables.scss
// -----------------------------------------------------------------------------
.user-management-container {
.list-table {
.board-table {
margin-bottom: $spacing-2xl;
overflow-x: auto;
// Allow table rows to expand if buttons wrap
.board-table-row {
height: auto;
min-height: 50px;
.row-cell.row-actions {
white-space: normal;
}
}
.actions-desktop {
flex-wrap: wrap;
justify-content: center;
gap: 6px;
}
// Link styles in table cells
.row-cell a {
@@ -82,6 +100,39 @@
}
}
// Grid layout for user list table to avoid inline styles
.board-table--user-list {
.board-table-header,
.board-table-row {
display: grid;
grid-template-columns: 24px 112px 1fr 100px 100px 287px;
padding: 0 29px; // Match standard board-table padding
gap: 10px;
@media (max-width: $breakpoint-sm) {
display: flex;
padding: 15px;
min-width: auto;
}
}
.header-cell,
.row-cell {
padding: 0;
width: 100%;
box-sizing: border-box;
&:first-child {
padding-left: 0;
}
&:last-child {
padding-right: 0;
}
}
}
.table-controls {
@extend .common-title-bar;
@@ -92,7 +143,14 @@
.search-box {
.btn {
height: 44px;
height: 48px;
background-color: #2a69de;
border: 1px solid #2a69de;
border-radius: 9px;
color: #fff;
font-size: 15px;
font-weight: 700;
cursor: pointer;
}
}
}
@@ -101,15 +159,22 @@
// Current user badge (used in actions column)
.user-current-badge {
display: inline-flex;
height: 30px;
align-items: center;
gap: 6px;
padding: 0 $spacing-sm;
background-color: rgba($accent-purple, 0.1);
color: $accent-purple;
border-radius: $border-radius-sm;
font-size: 13px;
font-weight: $font-weight-medium;
justify-content: center;
padding: 0 16px;
height: 28px;
background-color: #ecf0fa;
color: #2a69de;
border: 1px solid #4685ef;
border-radius: 5px;
font-size: 12px;
font-weight: 400;
font-family: 'Spoqa Han Sans Neo', sans-serif;
gap: 4px;
i {
display: none; // Hide the icon to match Figma exactly
}
// 모바일에서 숨김
@media (max-width: $breakpoint-sm) {
@@ -339,9 +404,12 @@
}
@keyframes pulse {
0%, 100% {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
@@ -415,6 +483,7 @@
// 사용자 관리 모바일 디자인
// -----------------------------------------------------------------------------
@media (max-width: $breakpoint-sm) {
// Show mobile actions, hide desktop actions
.actions-desktop {
display: none !important;
@@ -659,7 +728,8 @@
width: 20px;
height: 20px;
i, svg {
i,
svg {
font-size: 14px;
width: 14px;
height: 14px;
@@ -693,6 +763,7 @@
.list-table {
.list-table-header {
.header-cell {
// NO 컬럼 - 모바일에서 숨김
&:nth-child(1) {
display: none;
@@ -729,6 +800,7 @@
.list-table-row {
.row-cell {
// NO 컬럼 - 모바일에서 숨김
&:nth-child(1),
&.row-cell--number {
@@ -754,7 +826,7 @@
display: none !important;
}
> span:not(.notice-number):not(.file-icon) {
>span:not(.notice-number):not(.file-icon) {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -840,6 +912,7 @@
.list-table {
.list-table-header {
.header-cell {
// NO 컬럼 - 모바일에서 숨김
&:nth-child(1) {
display: none;
@@ -862,6 +935,7 @@
.list-table-row {
.row-cell {
// NO 컬럼 - 모바일에서 숨김
&:nth-child(1),
&.row-cell--number {
@@ -886,7 +960,7 @@
display: none !important;
}
> span:not(.notice-number):not(.file-icon) {
>span:not(.notice-number):not(.file-icon) {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -1054,7 +1128,7 @@
background: #e0efff;
}
& + .dropdown-item {
&+.dropdown-item {
border-top: 1px solid #eee;
}
@@ -3,14 +3,15 @@
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_title_layout}">
<body>
<th:block layout:fragment="contentFragment">
<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">
<!-- TODO: Update icon to faq icon -->
<img th:src="@{/img/keyimage/notice_img.svg}" alt="FAQ 아이콘" style="width: 100%; height: 100%; object-fit: contain;" />
<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">
@@ -18,7 +19,8 @@
<span class="service-hero__badge-text">FAQ 게시판</span>
</div>
<h1 class="service-hero__title">FAQ</h1>
<p class="service-hero__desc">서비스 이용 중 궁금한 사항에 대한 답변을 빠르게 확인해 보세요<br>카테고리별 분류와 검색을 통해 원하는 정보를 쉽게 찾아보실 수 있습니다.</p>
<p class="service-hero__desc">서비스 이용 중 궁금한 사항에 대한 답변을 빠르게 확인해 보세요<br>카테고리별 분류와 검색을 통해 원하는 정보를 쉽게 찾아보실 수
있습니다.</p>
</div>
</div>
</section>
@@ -30,7 +32,8 @@
<!-- Content -->
<section class="service-content">
<div class="notice-list-container" style="padding: 0; min-height: auto; margin: 0; background: transparent; box-shadow: none;">
<div class="notice-list-container"
style="padding: 0; min-height: auto; margin: 0; background: transparent; box-shadow: none;">
<!-- Search and Filter Controls -->
<form name="faqForm" th:action="@{/faq_list}" method="post" th:object="${search}" onsubmit="return false;">
@@ -43,13 +46,13 @@
</h2>
<div class="search-field">
<input type="text"
th:field="*{searchWrd}"
placeholder="질문 또는 답변 내용으로 검색"
<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"/>
<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>
@@ -61,11 +64,13 @@
<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-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"/>
<path d="M1 1L7 7L13 1" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</span>
</div>
@@ -80,15 +85,16 @@
<!-- 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 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>
@@ -103,7 +109,7 @@
</section>
</div>
</div>
</th:block>
</th:block>
</body>
@@ -125,15 +131,15 @@
return textArea.value;
}
document.addEventListener('DOMContentLoaded', function() {
document.addEventListener('DOMContentLoaded', function () {
// Decode HTML entities in FAQ answers
document.querySelectorAll('.faq-answer-content').forEach(function(element) {
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) {
document.querySelectorAll('.faq-question').forEach(function (question) {
question.addEventListener('click', function (e) {
e.preventDefault();
const faqItem = this.closest('.faq-item');
@@ -3,76 +3,116 @@
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_title_layout}">
<body>
<section layout:fragment="title">
<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>
<section layout:fragment="contentFragment">
<section 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/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>
</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="inquiry-detail-container">
<!-- Inquiry Header: Status Badge and Title -->
<div class="inquiry-detail-header">
<div class="inquiry-header-top">
<span class="inquiry-status-badge"
th:classappend="${inquiry.inquiryStatus == 'RESPONDED' ? 'inquiry-status-badge--completed' : (inquiry.inquiryStatus == 'CLOSED' ? 'inquiry-status-badge--closed' : 'inquiry-status-badge--pending')}"
th:text="${inquiry.inquiryStatus == 'RESPONDED' ? '답변완료' : (inquiry.inquiryStatus == 'CLOSED' ? '종료' : '답변대기')}">
답변대기
</span>
<div class="inquiry-header-right">
<span class="inquiry-detail-writer"
th:text="${inquiry.inquirerName != null ? inquiry.inquirerName : (inquiry.inquirer != null ? inquiry.inquirer.userName : '')}">작성자</span>
<span class="inquiry-detail-divider">|</span>
<span class="inquiry-detail-date" th:text="${#temporals.format(inquiry.createdDate, 'yyyy.MM.dd')}">2025.11.01</span>
<!-- Detail Header -->
<div class="notice-detail-header">
<div class="inquiry-header-meta">
<span th:text="${#temporals.format(inquiry.createdDate, 'yyyy.MM.dd')}">2026.06.24</span>
<span>|</span>
<span
th:text="${inquiry.inquirerName != null ? inquiry.inquirerName : (inquiry.inquirer != null ? inquiry.inquirer.userName : '')}">EACTIVE</span>
</div>
<div class="notice-detail-title-wrapper">
<span class="notice-type-badge notice-type-badge--completed"
th:if="${inquiry.inquiryStatus == 'RESPONDED'}">답변완료</span>
<span class="notice-type-badge notice-type-badge--closed"
th:if="${inquiry.inquiryStatus == 'CLOSED'}">종료</span>
<span class="notice-type-badge notice-type-badge--pending"
th:if="${inquiry.inquiryStatus != 'RESPONDED' and inquiry.inquiryStatus != 'CLOSED'}">답변대기</span>
<h2 class="notice-detail-title" th:text="${inquiry.inquirySubject}">질문 제목</h2>
</div>
<h2 class="inquiry-detail-title" th:text="${inquiry.inquirySubject}">질문 제목</h2>
</div>
<!-- Inquiry Content Section -->
<div class="inquiry-detail-content">
<!-- Detail Body -->
<div class="notice-detail-body">
<div id="inquiryDetail" class="inquiry-content-body" th:utext="${inquiry.inquiryDetail}">
문의 내용이 여기에 표시됩니다.
</div>
</div>
<!-- Admin Response Section -->
<div class="inquiry-response-section" th:if="${inquiry.inquiryStatus == 'RESPONDED' or inquiry.inquiryStatus == 'CLOSED'}">
<div class="inquiry-response-section"
th:if="${inquiry.inquiryStatus == 'RESPONDED' or inquiry.inquiryStatus == 'CLOSED'}">
<div class="inquiry-response-header">
<span class="response-label">관리자 답변</span>
<span class="response-date" th:text="${#temporals.format(inquiry.responseDate, 'yyyy.MM.dd')}">2025.11.02</span>
<div class="response-admin">
<span class="admin-label">ADMIN</span>
<span class="admin-badge">관리자</span>
</div>
<div class="inquiry-response-body" id="responseDetail" th:utext="${inquiry.responseDetail}">
<div class="response-meta">
<span th:text="${#temporals.format(inquiry.responseDate, 'yyyy.MM.dd')}">2026-06-24</span>
</div>
</div>
<div id="responseDetail" class="inquiry-response-body" th:utext="${inquiry.responseDetail}">
답변 내용이 여기에 표시됩니다.
</div>
</div>
<!-- Comment Section (DJBank Custom DJPGPT0002) -->
<th:block th:replace="~{apps/community/djb/fragments-inquiry-comments :: commentSection(${inquiry}, ${commentWritable})}"></th:block>
<th:block
th:replace="~{apps/community/djb/fragments-inquiry-comments :: commentSection(${inquiry}, ${commentWritable})}">
</th:block>
<!-- Action Buttons -->
<div class="inquiry-detail-actions">
<div class="actions-left">
<button type="button" class="btn-inquiry-list" th:onclick="|location.href='@{/inquiry}'|">목록</button>
</div>
<div class="actions-right" th:if="${inquiry.inquiryStatus == 'PENDING' && inquiry.inquirer.id == #authentication.principal.id}">
<form id="deleteForm" th:action="@{/inquiry/{id}/delete(id=${inquiry.id})}" method="post" style="display: none;"></form>
<button type="button" class="btn-inquiry-edit" th:onclick="|location.href='@{/inquiry/edit(id=${inquiry.id})}'|">수정</button>
<button type="button" class="btn-inquiry-delete" onclick="confirmDelete()">삭제</button>
<div class="inquiry-actions">
<button type="button" class="btn-inquiry-new" th:onclick="|location.href='@{/inquiry}'|">목록</button>
<th:block
th:if="${inquiry.inquiryStatus == 'PENDING' && inquiry.inquirer.id == #authentication.principal.id}">
<form id="deleteForm" th:action="@{/inquiry/{id}/delete(id=${inquiry.id})}" method="post"
style="display: none;"></form>
<button type="button" class="btn-inquiry-new btn-inquiry-new--outline"
th:onclick="|location.href='@{/inquiry/edit(id=${inquiry.id})}'|">수정</button>
<button type="button" class="btn-inquiry-new btn-inquiry-new--danger"
onclick="confirmDelete()">삭제</button>
</th:block>
</div>
</div>
</section>
</div>
</section>
</div>
</section>
</body>
<th:block layout:fragment="contentScript">
<script th:inline="javascript">
$(document).ready(function() {
var successMsg = [[${success}]];
$(document).ready(function () {
var successMsg = [[${ success }]];
if (successMsg) {
customPopups.showAlert(successMsg);
}
@@ -87,14 +127,14 @@
}
function confirmDelete() {
customPopups.showConfirm('문의글을 삭제하시겠습니까?', function(result) {
customPopups.showConfirm('문의글을 삭제하시겠습니까?', function (result) {
if (result) {
document.getElementById('deleteForm').submit();
}
});
}
document.addEventListener('DOMContentLoaded', function() {
document.addEventListener('DOMContentLoaded', function () {
var inquiryElement = document.getElementById('inquiryDetail');
if (inquiryElement) {
inquiryElement.innerHTML = decodeHTMLEntities(inquiryElement.innerHTML);
@@ -11,69 +11,85 @@
</section>
<th:block layout:fragment="contentFragment" th:with="isNew=${inquiry.id == null}">
<section class="inquiry-form-container">
<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>
</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="register-title-bar">
<h2 class="register-title" th:text="${isNew ? '질문하기' : '질문 수정'}">질문하기</h2>
<span class="register-subtitle">궁금하신 사항을 작성해 주세요.</span>
<div class="djb-board-write-header">
<h2 class="title" th:text="${isNew ? '질문하기' : '질문 수정'}">질문하기</h2>
<p class="subtitle">궁금하신 사항을 작성해 주세요.</p>
</div>
<!-- Form Content -->
<div class="register-form-container">
<form name="inquiryForm" id="inquiryForm" th:action="${isNew}? @{/inquiry} : @{/inquiry/edit}"
th:object="${inquiry}" method="post" class="register-form">
th:object="${inquiry}" method="post" class="djb-board-form">
<input type="hidden" th:field="*{id}" th:if="${!isNew}">
<!-- Subject Field -->
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">제목</span>
<span class="required-badge">필수</span>
</div>
<div class="form-field-wrapper">
<div class="form-group">
<label class="djb-label" for="inquirySubject">제목 <span class="required">*</span></label>
<input type="text"
id="inquirySubject"
th:field="*{inquirySubject}"
class="form-input"
class="djb-input"
placeholder="제목을 입력하세요"
required
maxlength="200">
</div>
</div>
<!-- Content Field -->
<div class="form-row">
<div class="form-label-wrapper">
<span class="form-label-text">내용</span>
<span class="required-badge">필수</span>
</div>
<div class="form-field-wrapper">
<div class="form-group">
<label class="djb-label" for="inquiryDetail">내용 <span class="required">*</span></label>
<textarea id="inquiryDetail"
th:field="*{inquiryDetail}"
class="form-textarea"
rows="10"
class="djb-textarea"
placeholder="내용을 입력하세요"
required
maxlength="2000"></textarea>
<small class="char-counter-wrapper">
<div class="djb-char-counter-wrapper">
<span class="char-counter">0 / 2000</span>
</small>
</div>
</div>
</form>
</div>
<!-- Form Actions -->
<div class="form-actions">
<button type="button" class="btn btn-submit btn-secondary" th:onclick="|location.href='@{/inquiry}'|">취소</button>
<button type="button" class="btn btn-submit btn-primary btn-submit-form">
<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>
</th:block>
</body>
@@ -85,12 +85,9 @@
<a th:href="@{/portalnotice/detail(id=${notice.id})}" class="notice-title-link">
<span class="notice-number"
th:text="|[${page.totalElements - (page.number * page.size) - status.index}]|">[1]</span>
<span class="notice-fix-badge" th:if="${notice.fixYn == 'Y'}"
style="display:inline-block;padding:2px 8px;margin-right:6px;border-radius:10px;font-size:12px;background:#e5f0ff;color:#0a4ea3;font-weight:600;">고정</span>
<span class="notice-type-badge notice-type-badge--incident" th:if="${notice.noticeType == '3'}"
style="display:inline-block;padding:2px 8px;margin-right:6px;border-radius:10px;font-size:12px;background:#fde7e9;color:#c0152a;">장애</span>
<span class="notice-type-badge notice-type-badge--maintenance" th:if="${notice.noticeType == '2'}"
style="display:inline-block;padding:2px 8px;margin-right:6px;border-radius:10px;font-size:12px;background:#fff4d6;color:#9a6700;">점검</span>
<span class="notice-type-badge notice-type-badge--fix" th:if="${notice.fixYn == 'Y'}">고정</span>
<span class="notice-type-badge notice-type-badge--incident" 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="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">
@@ -3,130 +3,94 @@
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/djbank_title_layout}">
<body>
<section layout:fragment="title">
<section layout:fragment="title">
<div class="page-title-banner">
<img th:src="@{/img/img_title_bg.png}" class="title-image" alt="타이틀 배경">
<h1>피드백/개선요청</h1>
</div>
</section>
</section>
<th:block layout:fragment="contentFragment">
<section class="inquiry-form-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/feedback_img.svg}" alt="피드백 아이콘"
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>
</div>
<h1 class="service-hero__title">피드백/개선요청</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('feedback')}"></th:block>
<!-- Content -->
<section class="service-content">
<div class="djb-board-write-container">
<!-- Title Bar -->
<div class="register-title-bar">
<h2 class="register-title">피드백/개선요청</h2>
<span class="register-subtitle">DJBank은 온라인 비즈니스 혁신을 위한 피드백/개선요청을 환영합니다.</span>
<div class="djb-board-write-header">
<h2 class="title">피드백 / 개선 요청</h2>
<p class="subtitle">DJ Bank은 온라인 비즈니스 혁신을 위한 피드백/개선요청을 환영합니다.</p>
</div>
<!-- Form Content -->
<div class="register-form-container">
<form name="partnershipForm" id="partnershipForm" th:action="@{/partnership}" th:object="${partnership}"
enctype="multipart/form-data" method="post" class="register-form">
enctype="multipart/form-data" method="post" class="djb-board-form">
<!-- 제목 Field -->
<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">제목</span>
<span class="required-badge">필수</span>
</div>
<div class="form-field-wrapper">
<input type="text"
id="bizSubject"
th:field="*{bizSubject}"
class="form-input"
placeholder="제안하고자 하는 제목을 입력해 주세요"
required
maxlength="200">
</div>
<div class="form-group">
<label class="djb-label" for="bizSubject">제목 <span class="required">*</span></label>
<input type="text" id="bizSubject" th:field="*{bizSubject}" class="djb-input"
placeholder="제안하고자 하는 제목을 입력해주세요" required maxlength="200">
</div>
<!-- 사업 내용 Field -->
<div class="form-row">
<div class="form-label-wrapper">
<span class="form-label-text">내용</span>
<span class="required-badge">필수</span>
</div>
<div class="form-field-wrapper">
<textarea id="bizDetail"
th:field="*{bizDetail}"
class="form-textarea"
rows="10"
placeholder="제안하고자 하시는 내용을 상세히 입력해 주세요"
required
maxlength="4000"></textarea>
<small class="char-counter-wrapper">
<!-- 내용 Field -->
<div class="form-group">
<label class="djb-label" for="bizDetail">내용 <span class="required">*</span></label>
<textarea id="bizDetail" th:field="*{bizDetail}" class="djb-textarea"
placeholder="제안하고자 하는 내용을 상세히 입력해주세요" required maxlength="4000"></textarea>
<div class="djb-char-counter-wrapper">
<span class="char-counter">0 / 4000</span>
</small>
</div>
</div>
<!-- File Upload Field -->
<!--/* 260520 - DJB 에서는 첨부 파일 허용안함. */-->
<!--<div class="form-row">
<div class="form-label-wrapper label-offset">
<span class="form-label-text">첨부파일</span>
</div>
<div class="form-field-wrapper">
<div class="file-upload-inline">
<div class="file-input-display" id="fileInputDisplay">
<span class="file-display-text" id="fileDisplayText">선택된 파일이 없습니다</span>
<button type="button" class="btn-remove-file-inline" id="btnRemoveFile" title="파일 삭제" style="display: none;">
<svg width="16" height="16" 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>
</div>
<button type="button" class="btn-file-attach" onclick="document.getElementById('file').click()">
<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>
파일첨부
</button>
<input type="file"
id="file"
name="files"
th:accept="${isInternalUser ? '*' : '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.hwp,.gif,.jpg,.jpeg,.png'}"
style="display: none;">
</div>
<div class="form-help-text">
<p><i class="fas fa-check-circle"></i>
<span th:text="'첨부파일은 ' + ${@portalProperties.file.maxSize} + ' 이내로 등록해 주세요.'">첨부파일은 8MB 이내로 등록해 주세요.</span>
</p>
<p>
<i class="fas fa-check-circle"></i>
문서파일과 이미지 파일만 등록 가능합니다. (pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg, png)
</p>
</div>
</div>
</div>-->
</form>
</div>
<!-- Form Actions -->
<div class="form-actions">
<button type="button" class="btn btn-submit btn-secondary" th:onclick="|location.href='@{/}'|">취소</button>
<button type="button" class="btn btn-submit btn-primary btn-submit-form">
<span>작성 완료</span>
</button>
<button type="button" class="btn-cancel" th:onclick="|location.href='@{/}'|">취소</button>
<button type="button" class="btn-submit btn-submit-form">작성완료</button>
</div>
</div>
</section>
</th:block>
</div>
</div>
</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>
<script th:if="${success}" th:inline="javascript">
$(document).ready(function () {
customPopups.showAlert([[${success}]]);
customPopups.showAlert([[${ success }]]);
});
</script>
<script th:inline="javascript">
@@ -146,7 +110,7 @@
charCounter.textContent = currentLength + ' / ' + maxLength;
// Character counter for textarea
bizDetailTextarea.addEventListener('input', function() {
bizDetailTextarea.addEventListener('input', function () {
const currentLength = this.value.length;
const maxLength = this.getAttribute('maxlength');
charCounter.textContent = currentLength + ' / ' + maxLength;
@@ -183,6 +147,7 @@
form.submit();
});
if (fileInput) {
fileInput.addEventListener('change', function () {
const file = fileInput.files[0];
@@ -198,26 +163,34 @@
}
/*[/]*/
if (fileDisplayText && fileInputDisplay && btnRemoveFile) {
fileDisplayText.textContent = file.name;
fileDisplayText.classList.add('has-file');
fileInputDisplay.classList.add('has-file');
btnRemoveFile.style.display = 'flex';
}
}
});
}
// File remove handler
if (btnRemoveFile) {
btnRemoveFile.addEventListener('click', function (e) {
e.stopPropagation();
fileInput.value = '';
if (fileInput) fileInput.value = '';
if (fileDisplayText) {
fileDisplayText.textContent = '선택된 파일이 없습니다';
fileDisplayText.classList.remove('has-file');
fileInputDisplay.classList.remove('has-file');
btnRemoveFile.style.display = 'none';
}
if (fileInputDisplay) fileInputDisplay.classList.remove('has-file');
this.style.display = 'none';
});
}
// Focus on subject field
document.getElementById('bizSubject').focus();
});
</script>
</th:block>
</html>
File diff suppressed because one or more lines are too long
@@ -3,19 +3,22 @@
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">
<h1>이용자 관리</h1>
</div>
</section>
<th:block layout:fragment="contentFragment">
<section class="user-management-container">
<div class="service-main" style="padding-top: 40px;">
<!-- Sidebar -->
<th:block th:replace="~{fragment/djbank/service_sidebar :: sidebar('users')}"></th:block>
<!-- Main Content -->
<section class="service-content">
<section class="user-management-container" style="padding-top: 0;">
<!-- App List Title Section -->
<div class="table-controls">
<h2 class="total-count">
<strong th:text="${(users != null ? users.size() : 0) + (pendingUsers != null ? pendingUsers.size() : 0)}">0</strong>
<strong th:text="${(users != null ? users.size() : 0) + (pendingUsers != null ? pendingUsers.size() : 0)}">0</strong>
</h2>
<div class="search-box">
<button type="button" class="btn btn-primary" id="addUserBtn">
@@ -26,37 +29,37 @@
</div>
<!-- User Table -->
<div class="list-table" th:if="${(users != null and !users.isEmpty()) or (pendingUsers != null and !pendingUsers.isEmpty())}">
<div class="board-table board-table--user-list" th:if="${(users != null and !users.isEmpty()) or (pendingUsers != null and !pendingUsers.isEmpty())}">
<!-- Table Header -->
<div class="list-table-header">
<div class="header-cell" style="width: 90px;">NO.</div>
<div class="header-cell" style="width: 90px;">성명</div>
<div class="header-cell" style="flex: 1; min-width: 180px;">이메일 / 휴대폰</div>
<div class="header-cell" style="width: 120px;">권한</div>
<div class="header-cell" style="width: 120px;">계정상태</div>
<div class="header-cell" style="width: 320px;">설정</div>
<div class="board-table-header">
<div class="header-cell">NO.</div>
<div class="header-cell">성명</div>
<div class="header-cell">이메일 / 휴대폰</div>
<div class="header-cell">권한</div>
<div class="header-cell">계정상태</div>
<div class="header-cell">설정</div>
</div>
<!-- Table Body -->
<div class="list-table-body">
<div class="board-table-body">
<!-- Active/Inactive Users -->
<div class="list-table-row" th:each="user, status : ${users}">
<div class="row-cell" style="width: 90px;" data-label="NO" th:text="${status.index + 1}">1</div>
<div class="row-cell" style="width: 90px;" data-label="성명">
<div class="board-table-row" th:each="user, status : ${users}">
<div class="row-cell" data-label="NO" th:text="${status.index + 1}">1</div>
<div class="row-cell" data-label="성명">
<a th:href="@{/users/detail(id=${user.id})}" th:text="${user.maskedUserName}">
사용자 이름
</a>
</div>
<div class="row-cell" style="flex: 1; min-width: 180px;" data-label="이메일" th:text="${user.maskedEmailAddr}">
<div class="row-cell" data-label="이메일" th:text="${user.maskedEmailAddr}">
user@example.com
</div>
<div class="row-cell" style="width: 120px;" data-label="권한">
<div class="row-cell" data-label="권한">
<span th:if="${user.roleCode != null}" th:text="#{${user.roleCode}}">권한</span>
</div>
<div class="row-cell" style="width: 120px;" data-label="계정상태" th:text="${user.userStatus.getDescription()}">
<div class="row-cell" data-label="계정상태" th:text="${user.userStatus.getDescription()}">
활성
</div>
<div class="row-cell row-actions" style="width: 320px;">
<div class="row-cell row-actions">
<!-- Current User -->
<span class="user-current-badge" th:if="${user.id == currentUserId}">
<i class="fas fa-user-check"></i> 현재 사용자
@@ -146,15 +149,15 @@
</div>
<!-- Pending Invitation Users -->
<div class="list-table-row" th:each="user : ${pendingUsers}">
<div class="row-cell" style="width: 90px;" data-label="NO">-</div>
<div class="row-cell" style="width: 90px;" data-label="성명">-</div>
<div class="row-cell" style="flex: 1; min-width: 180px;" data-label="휴대폰" th:text="${user.maskedMobileNumber}">
<div class="board-table-row" th:each="user : ${pendingUsers}">
<div class="row-cell" data-label="NO">-</div>
<div class="row-cell" data-label="성명">-</div>
<div class="row-cell" data-label="휴대폰" th:text="${user.maskedMobileNumber}">
010-****-****
</div>
<div class="row-cell" style="width: 120px;" data-label="권한">이용자</div>
<div class="row-cell" style="width: 120px;" data-label="계정상태">초대중</div>
<div class="row-cell row-actions" style="width: 320px;">
<div class="row-cell" data-label="권한">이용자</div>
<div class="row-cell" data-label="계정상태">초대중</div>
<div class="row-cell row-actions">
<!-- Desktop -->
<div class="actions-desktop">
<button type="button" class="list-table-btn list-table-btn--secondary cancel_invitation"
@@ -199,6 +202,8 @@
th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}"></div>
</section>
</section>
</div>
</th:block>
<section layout:fragment="pagePopups" class="content pop">
@@ -32,6 +32,42 @@
th:classappend="${activeMenu == 'feedback'} ? 'service-nav__item--active' : ''"
class="service-nav__item">피드백/개선요청</a>
</th:block>
<!-- 마이페이지 그룹 (My Page) -->
<th:block th:if="${activeMenu == 'users' or activeMenu == 'apiKey' or activeMenu == 'statistics' or activeMenu == 'profile' or activeMenu == 'password'}">
<!-- Profile Section -->
<div class="service-sidebar__profile">
<div class="avatar">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
</svg>
</div>
<p class="user-name"><span th:text="${#authentication.principal.userName}">사용자</span></p>
</div>
<div class="service-sidebar__divider"></div>
<a th:href="@{/users}"
th:classappend="${activeMenu == 'users'} ? 'service-nav__item--active' : ''"
class="service-nav__item"
sec:authorize="hasRole('ROLE_CORP_MANAGER')">이용자 관리</a>
<a th:href="@{/myapikey}"
th:classappend="${activeMenu == 'apiKey'} ? 'service-nav__item--active' : ''"
class="service-nav__item">인증 키 관리</a>
<a th:href="@{/statistics/daily}"
th:classappend="${activeMenu == 'statistics'} ? 'service-nav__item--active' : ''"
class="service-nav__item">이용통계</a>
<a th:href="@{/mypage/update_user}"
th:classappend="${activeMenu == 'profile'} ? 'service-nav__item--active' : ''"
class="service-nav__item">내정보 관리</a>
<a th:href="@{/mypage/password}"
th:classappend="${activeMenu == 'password'} ? 'service-nav__item--active' : ''"
class="service-nav__item">비밀번호 변경</a>
</th:block>
</nav>
</aside>
</body>