커뮤니티 - 스타일 변경

This commit is contained in:
현성필
2025-11-05 17:13:09 +09:00
parent edb40d4a0f
commit 5168347f11
16 changed files with 4583 additions and 439 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
@@ -0,0 +1,351 @@
// -----------------------------------------------------------------------------
// Accordion Component - FAQ and Collapsible Sections
// -----------------------------------------------------------------------------
// FAQ Accordion
.faq-accordion {
background: $white;
border-radius: $border-radius-lg;
box-shadow: $shadow-sm;
overflow: hidden;
.faq-item {
border-bottom: 1px solid $border-gray;
transition: $transition-base;
&:last-child {
border-bottom: none;
}
&.active {
background: rgba($primary-blue, 0.02);
.faq-question {
color: $primary-blue;
&::before {
background: $gradient-primary;
}
.faq-icon {
transform: rotate(180deg);
color: $primary-blue;
}
}
}
}
.faq-question {
display: flex;
align-items: center;
justify-content: space-between;
padding: $spacing-lg $spacing-xl;
font-size: $font-size-base;
font-weight: $font-weight-medium;
color: $text-dark;
cursor: pointer;
transition: $transition-base;
position: relative;
gap: $spacing-md;
@media (max-width: $breakpoint-sm) {
padding: $spacing-md $spacing-lg;
font-size: $font-size-sm;
}
&::before {
content: 'Q';
position: absolute;
left: $spacing-xl;
top: 50%;
transform: translateY(-50%);
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: $light-bg;
color: $primary-blue;
border-radius: $border-radius-circle;
font-size: $font-size-sm;
font-weight: $font-weight-bold;
transition: $transition-base;
@media (max-width: $breakpoint-sm) {
width: 28px;
height: 28px;
font-size: $font-size-xs;
}
}
&:hover {
background: rgba($primary-blue, 0.03);
color: $primary-blue;
}
.faq-question-text {
flex: 1;
padding-left: 48px;
line-height: $line-height-normal;
@media (max-width: $breakpoint-sm) {
padding-left: 40px;
}
}
.faq-icon {
flex-shrink: 0;
font-size: 20px;
color: $text-gray;
transition: $transition-base;
@media (max-width: $breakpoint-sm) {
font-size: 18px;
}
}
}
.faq-answer {
display: none;
padding: 0 $spacing-xl $spacing-lg;
margin-left: 48px;
margin-right: $spacing-xl;
position: relative;
@media (max-width: $breakpoint-sm) {
padding: 0 $spacing-lg $spacing-md;
margin-left: 40px;
margin-right: $spacing-lg;
}
&::before {
content: 'A';
position: absolute;
left: -48px;
top: 8px;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: $gradient-primary;
color: $white;
border-radius: $border-radius-circle;
font-size: $font-size-sm;
font-weight: $font-weight-bold;
@media (max-width: $breakpoint-sm) {
left: -40px;
width: 28px;
height: 28px;
font-size: $font-size-xs;
}
}
&.show {
display: block;
animation: slideDown 0.3s ease;
}
.faq-answer-content {
padding: $spacing-md 0;
font-size: $font-size-sm;
color: $text-gray;
line-height: $line-height-loose;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-xs;
padding: $spacing-sm 0;
}
// Content formatting
p {
margin-bottom: $spacing-md;
&:last-child {
margin-bottom: 0;
}
}
ul, ol {
margin: $spacing-sm 0;
padding-left: $spacing-lg;
}
li {
margin-bottom: $spacing-xs;
}
a {
color: $primary-blue;
text-decoration: underline;
&:hover {
color: $secondary-blue;
}
}
code {
background: $gray-bg;
padding: 2px 6px;
border-radius: $border-radius-sm;
font-family: $font-family-mono;
font-size: 0.9em;
color: $primary-blue;
}
pre {
background: $gray-bg;
border: 1px solid $border-gray;
border-radius: $border-radius-md;
padding: $spacing-md;
overflow-x: auto;
margin: $spacing-md 0;
code {
background: transparent;
padding: 0;
color: $text-dark;
}
}
}
}
}
// FAQ Page specific styles
.faq-page {
min-height: calc(100vh - 140px);
background: $gray-bg;
padding: $spacing-3xl $spacing-lg;
@media (max-width: $breakpoint-sm) {
padding: $spacing-2xl $spacing-md;
}
}
.faq-container {
max-width: $container-max-width;
margin: 0 auto;
}
.faq-header {
margin-bottom: $spacing-3xl;
@media (max-width: $breakpoint-sm) {
margin-bottom: $spacing-2xl;
}
.page-title {
font-size: $font-size-3xl;
font-weight: $font-weight-bold;
color: $text-dark;
margin-bottom: $spacing-md;
background: $gradient-primary;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-2xl;
}
}
.page-description {
font-size: $font-size-base;
color: $text-gray;
line-height: $line-height-normal;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-sm;
}
}
}
// Slide down animation
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
// Generic accordion (for reuse)
.accordion {
background: $white;
border-radius: $border-radius-lg;
box-shadow: $shadow-sm;
overflow: hidden;
.accordion-item {
border-bottom: 1px solid $border-gray;
&:last-child {
border-bottom: none;
}
&.active {
.accordion-header {
background: rgba($primary-blue, 0.03);
color: $primary-blue;
.accordion-icon {
transform: rotate(180deg);
}
}
}
}
.accordion-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: $spacing-lg $spacing-xl;
font-size: $font-size-base;
font-weight: $font-weight-medium;
color: $text-dark;
cursor: pointer;
transition: $transition-base;
@media (max-width: $breakpoint-sm) {
padding: $spacing-md $spacing-lg;
font-size: $font-size-sm;
}
&:hover {
background: rgba($primary-blue, 0.02);
}
.accordion-title {
flex: 1;
}
.accordion-icon {
flex-shrink: 0;
font-size: 20px;
color: $text-gray;
transition: $transition-base;
}
}
.accordion-content {
display: none;
padding: $spacing-lg $spacing-xl;
font-size: $font-size-sm;
color: $text-gray;
line-height: $line-height-normal;
border-top: 1px solid $border-gray;
@media (max-width: $breakpoint-sm) {
padding: $spacing-md $spacing-lg;
}
&.show {
display: block;
animation: slideDown 0.3s ease;
}
}
}
@@ -0,0 +1,361 @@
// -----------------------------------------------------------------------------
// Table Component Styles - Reusable table designs
// -----------------------------------------------------------------------------
// Base table styles
.data-table {
width: 100%;
background: $white;
border-radius: $border-radius-lg;
overflow: hidden;
box-shadow: $shadow-sm;
// Table wrapper for responsive scrolling
&-wrapper {
overflow-x: auto;
margin-bottom: $spacing-xl;
@media (max-width: $breakpoint-sm) {
border-radius: $border-radius-lg;
box-shadow: $shadow-sm;
}
}
table {
width: 100%;
border-collapse: collapse;
thead {
background: $light-bg;
tr {
border-bottom: 2px solid $border-gray;
}
th {
padding: $spacing-md $spacing-lg;
text-align: left;
font-size: $font-size-sm;
font-weight: $font-weight-semibold;
color: $text-dark;
white-space: nowrap;
@media (max-width: $breakpoint-sm) {
padding: $spacing-sm $spacing-md;
font-size: $font-size-xs;
}
}
}
tbody {
tr {
border-bottom: 1px solid $border-gray;
transition: $transition-base;
&:last-child {
border-bottom: none;
}
&:hover {
background-color: rgba($primary-blue, 0.02);
}
}
td {
padding: $spacing-md $spacing-lg;
font-size: $font-size-sm;
color: $text-gray;
@media (max-width: $breakpoint-sm) {
padding: $spacing-sm $spacing-md;
font-size: $font-size-xs;
}
a {
color: $text-dark;
text-decoration: none;
transition: $transition-base;
&:hover {
color: $primary-blue;
}
}
}
}
}
}
// Notice board table (list view with columns)
.notice-table {
width: 100%;
background: $white;
border-radius: $border-radius-lg;
overflow: hidden;
box-shadow: $shadow-sm;
&-wrapper {
overflow-x: auto;
@media (max-width: $breakpoint-sm) {
border-radius: 0;
box-shadow: none;
}
}
// Table header
.table-header {
display: grid;
grid-template-columns: 80px 1fr 120px;
gap: $spacing-md;
padding: $spacing-md $spacing-lg;
background: $light-bg;
border-bottom: 2px solid $border-gray;
font-size: $font-size-sm;
font-weight: $font-weight-semibold;
color: $text-dark;
@media (max-width: $breakpoint-md) {
grid-template-columns: 60px 1fr 100px;
padding: $spacing-sm $spacing-md;
font-size: $font-size-xs;
}
@media (max-width: $breakpoint-sm) {
display: none; // Hide header on mobile
}
.col-num {
text-align: center;
}
.col-title {
text-align: left;
}
.col-date {
text-align: center;
}
}
// Table rows
.table-row {
display: grid;
grid-template-columns: 80px 1fr 120px;
gap: $spacing-md;
padding: $spacing-lg;
border-bottom: 1px solid $border-gray;
transition: $transition-base;
cursor: pointer;
@media (max-width: $breakpoint-md) {
grid-template-columns: 60px 1fr 100px;
padding: $spacing-md;
}
@media (max-width: $breakpoint-sm) {
grid-template-columns: 1fr;
gap: $spacing-sm;
padding: $spacing-md;
}
&:last-child {
border-bottom: none;
}
&:hover {
background-color: rgba($primary-blue, 0.02);
}
.col-num {
display: flex;
align-items: center;
justify-content: center;
font-size: $font-size-sm;
color: $text-gray;
font-weight: $font-weight-medium;
@media (max-width: $breakpoint-sm) {
display: none;
}
}
.col-title {
display: flex;
align-items: center;
font-size: $font-size-base;
color: $text-dark;
font-weight: $font-weight-medium;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-sm;
}
a {
color: inherit;
text-decoration: none;
display: flex;
align-items: center;
gap: $spacing-sm;
transition: $transition-base;
&:hover {
color: $primary-blue;
}
}
.file-icon {
display: inline-flex;
align-items: center;
margin-left: $spacing-sm;
opacity: 0.6;
img {
width: 16px;
height: 16px;
}
}
}
.col-date {
display: flex;
align-items: center;
justify-content: center;
font-size: $font-size-sm;
color: $text-light;
@media (max-width: $breakpoint-sm) {
justify-content: flex-start;
font-size: $font-size-xs;
padding-top: $spacing-xs;
}
}
}
// Mobile label for date
@media (max-width: $breakpoint-sm) {
.table-row .col-date::before {
content: '등록일: ';
color: $text-gray;
margin-right: $spacing-xs;
}
}
}
// Empty state for tables
.table-empty {
padding: $spacing-5xl $spacing-lg;
text-align: center;
background: $white;
border-radius: $border-radius-lg;
.empty-icon {
margin-bottom: $spacing-lg;
img {
max-width: 200px;
opacity: 0.7;
@media (max-width: $breakpoint-sm) {
max-width: 150px;
}
}
}
.empty-text {
font-size: $font-size-base;
color: $text-gray;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-sm;
}
}
}
// Search and filter section for tables
.table-controls {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: $spacing-xl;
gap: $spacing-lg;
@media (max-width: $breakpoint-sm) {
flex-direction: column;
align-items: stretch;
gap: $spacing-md;
}
.total-count {
font-size: $font-size-sm;
color: $text-gray;
strong {
color: $primary-blue;
font-weight: $font-weight-semibold;
}
@media (max-width: $breakpoint-sm) {
order: 2;
text-align: center;
}
}
.search-box {
display: flex;
gap: $spacing-sm;
@media (max-width: $breakpoint-sm) {
order: 1;
}
input {
flex: 1;
min-width: 250px;
padding: $spacing-sm $spacing-md;
border: 1px solid $border-gray;
border-radius: $border-radius-md;
font-size: $font-size-sm;
transition: $transition-base;
@media (max-width: $breakpoint-sm) {
min-width: auto;
}
&:focus {
outline: none;
border-color: $primary-blue;
box-shadow: 0 0 0 3px rgba($primary-blue, 0.1);
}
&::placeholder {
color: $text-light;
}
}
button {
padding: $spacing-sm $spacing-lg;
background: $gradient-primary;
color: $white;
border: none;
border-radius: $border-radius-md;
font-size: $font-size-sm;
font-weight: $font-weight-medium;
cursor: pointer;
transition: $transition-base;
white-space: nowrap;
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-md;
}
&:active {
transform: translateY(0);
}
i {
margin-right: $spacing-xs;
}
}
}
}
+4
View File
@@ -30,6 +30,8 @@
@import 'components/cta'; @import 'components/cta';
@import 'components/password-popup'; @import 'components/password-popup';
@import 'components/header-auth'; @import 'components/header-auth';
@import 'components/tables';
@import 'components/accordion';
// 5. Page-specific styles // 5. Page-specific styles
@import 'pages/index'; @import 'pages/index';
@@ -42,6 +44,8 @@
@import 'pages/mypage'; @import 'pages/mypage';
@import 'pages/apikey-register'; @import 'pages/apikey-register';
@import 'pages/apikey-detail'; @import 'pages/apikey-detail';
@import 'pages/notice';
@import 'pages/inquiry';
// 6. Themes // 6. Themes
@import 'themes/dark'; @import 'themes/dark';
@@ -0,0 +1,961 @@
// -----------------------------------------------------------------------------
// Inquiry (Q&A) Pages - List, Detail, and Form Views
// -----------------------------------------------------------------------------
// Inquiry list page container
.inquiry-page {
min-height: calc(100vh - 140px);
background: $gray-bg;
padding: $spacing-3xl $spacing-lg;
@media (max-width: $breakpoint-sm) {
padding: $spacing-2xl $spacing-md;
}
}
.inquiry-container {
max-width: $container-max-width;
margin: 0 auto;
}
// Page header
.inquiry-header {
margin-bottom: $spacing-3xl;
@media (max-width: $breakpoint-sm) {
margin-bottom: $spacing-2xl;
}
.page-title {
font-size: $font-size-3xl;
font-weight: $font-weight-bold;
color: $text-dark;
margin-bottom: $spacing-md;
background: $gradient-primary;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-2xl;
}
}
.page-description {
font-size: $font-size-base;
color: $text-gray;
line-height: $line-height-normal;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-sm;
}
}
}
// Inquiry table wrapper
.inquiry-list-wrapper {
background: $white;
border-radius: $border-radius-lg;
box-shadow: $shadow-md;
overflow: hidden;
margin-bottom: $spacing-xl;
}
// Inquiry table (extends notice-table structure)
.inquiry-table {
width: 100%;
background: $white;
border-radius: $border-radius-lg;
overflow: hidden;
// Table header
.table-header {
display: grid;
grid-template-columns: 80px 1fr 100px 100px 120px;
gap: $spacing-md;
padding: $spacing-md $spacing-lg;
background: $light-bg;
border-bottom: 2px solid $border-gray;
font-size: $font-size-sm;
font-weight: $font-weight-semibold;
color: $text-dark;
@media (max-width: $breakpoint-md) {
grid-template-columns: 60px 1fr 80px 100px;
padding: $spacing-sm $spacing-md;
font-size: $font-size-xs;
.col-name {
display: none;
}
}
@media (max-width: $breakpoint-sm) {
display: none;
}
.col-num {
text-align: center;
}
.col-title {
text-align: left;
}
.col-name {
text-align: center;
}
.col-status {
text-align: center;
}
.col-date {
text-align: center;
}
}
// Table rows
.table-row {
display: grid;
grid-template-columns: 80px 1fr 100px 100px 120px;
gap: $spacing-md;
padding: $spacing-lg;
border-bottom: 1px solid $border-gray;
transition: $transition-base;
cursor: pointer;
@media (max-width: $breakpoint-md) {
grid-template-columns: 60px 1fr 80px 100px;
padding: $spacing-md;
.col-name {
display: none;
}
}
@media (max-width: $breakpoint-sm) {
grid-template-columns: 1fr;
gap: $spacing-sm;
padding: $spacing-md;
}
&:last-child {
border-bottom: none;
}
&:hover {
background-color: rgba($primary-blue, 0.02);
}
.col-num {
display: flex;
align-items: center;
justify-content: center;
font-size: $font-size-sm;
color: $text-gray;
font-weight: $font-weight-medium;
@media (max-width: $breakpoint-sm) {
display: none;
}
}
.col-title {
display: flex;
align-items: center;
font-size: $font-size-base;
color: $text-dark;
font-weight: $font-weight-medium;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-sm;
}
a {
color: inherit;
text-decoration: none;
display: flex;
align-items: center;
gap: $spacing-sm;
transition: $transition-base;
&:hover {
color: $primary-blue;
}
}
.file-icon {
display: inline-flex;
align-items: center;
margin-left: $spacing-sm;
opacity: 0.6;
img {
width: 16px;
height: 16px;
}
}
}
.col-name {
display: flex;
align-items: center;
justify-content: center;
font-size: $font-size-sm;
color: $text-gray;
@media (max-width: $breakpoint-md) {
display: none;
}
}
.col-status {
display: flex;
align-items: center;
justify-content: center;
@media (max-width: $breakpoint-sm) {
justify-content: flex-start;
}
.status-badge {
display: inline-flex;
align-items: center;
justify-content: center;
padding: $spacing-xs $spacing-md;
border-radius: $border-radius-full;
font-size: $font-size-xs;
font-weight: $font-weight-semibold;
white-space: nowrap;
&.status-pending {
background: rgba($accent-orange, 0.1);
color: $accent-orange;
}
&.status-completed {
background: rgba($accent-green, 0.1);
color: $accent-green;
}
}
}
.col-date {
display: flex;
align-items: center;
justify-content: center;
font-size: $font-size-sm;
color: $text-light;
@media (max-width: $breakpoint-sm) {
justify-content: flex-start;
font-size: $font-size-xs;
padding-top: $spacing-xs;
}
}
}
// Mobile label for date
@media (max-width: $breakpoint-sm) {
.table-row .col-date::before {
content: '등록일: ';
color: $text-gray;
margin-right: $spacing-xs;
}
}
}
// ===========================
// Shared Button Styles (used across all inquiry pages)
// ===========================
// Base action button styles - NOT nested, applies globally
.action-btn-new,
.action-btn-primary,
.action-btn-secondary,
.action-btn-edit,
.action-btn-delete {
padding: $spacing-md $spacing-2xl;
border-radius: $border-radius-md;
font-size: $font-size-base;
font-weight: $font-weight-medium;
text-decoration: none;
transition: $transition-base;
display: inline-flex;
align-items: center;
justify-content: center;
gap: $spacing-sm;
cursor: pointer;
border: none;
@media (max-width: $breakpoint-sm) {
padding: $spacing-md $spacing-lg;
font-size: $font-size-sm;
}
i {
font-size: 16px;
}
}
// New inquiry button & primary action (create, submit)
.action-btn-new,
.action-btn-primary {
background: $gradient-primary;
color: $white;
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-md;
}
&:active {
transform: translateY(0);
}
}
// Secondary action button (cancel, back, list)
.action-btn-secondary {
background: $white;
color: $text-dark;
border: 1px solid $border-gray;
&:hover {
background: $gray-bg;
border-color: $primary-blue;
color: $primary-blue;
}
&:active {
transform: scale(0.98);
}
}
// Edit button
.action-btn-edit {
background: $primary-blue;
color: $white;
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-md;
background: $secondary-blue;
}
&:active {
transform: translateY(0);
}
}
// Delete button
.action-btn-delete {
background: $accent-orange;
color: $white;
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-md;
background: darken($accent-orange, 5%);
}
&:active {
transform: translateY(0);
}
}
// ===========================
// Button Container Layouts
// ===========================
// Inquiry actions (list page + detail page)
.inquiry-actions {
display: flex;
justify-content: center;
align-items: center;
gap: $spacing-md;
margin-top: $spacing-xl;
@media (max-width: $breakpoint-sm) {
flex-direction: column;
align-items: stretch;
}
// Layout for detail page split actions
.actions-left,
.actions-right {
display: flex;
align-items: center;
gap: $spacing-md;
@media (max-width: $breakpoint-sm) {
width: 100%;
justify-content: center;
}
}
@media (max-width: $breakpoint-sm) {
flex-direction: column;
align-items: stretch;
gap: $spacing-md;
.actions-left,
.actions-right {
flex-direction: column;
}
}
}
// ===========================
// Inquiry Detail Page
// ===========================
.inquiry-detail-page {
min-height: calc(100vh - 140px);
background: $gray-bg;
padding: $spacing-3xl $spacing-lg;
@media (max-width: $breakpoint-sm) {
padding: $spacing-2xl $spacing-md;
}
}
.inquiry-detail-container {
max-width: 960px;
margin: 0 auto;
}
// 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;
background: linear-gradient(180deg, $light-bg 0%, $white 100%);
@media (max-width: $breakpoint-sm) {
padding: $spacing-2xl $spacing-lg $spacing-md;
}
.status-badge-header {
display: inline-flex;
align-items: center;
justify-content: center;
padding: $spacing-xs $spacing-md;
border-radius: $border-radius-full;
font-size: $font-size-xs;
font-weight: $font-weight-semibold;
margin-bottom: $spacing-md;
&.status-pending {
background: rgba($accent-orange, 0.1);
color: $accent-orange;
}
&.status-completed {
background: rgba($accent-green, 0.1);
color: $accent-green;
}
}
.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 {
padding: $spacing-lg $spacing-2xl;
background: $gray-bg;
border-bottom: 1px solid $border-gray;
@media (max-width: $breakpoint-sm) {
padding: $spacing-md $spacing-lg;
}
.attachment-list {
display: flex;
flex-direction: column;
gap: $spacing-sm;
.attachment-item {
display: flex;
align-items: center;
gap: $spacing-sm;
a {
color: $text-dark;
text-decoration: none;
font-size: $font-size-sm;
transition: $transition-base;
display: flex;
align-items: center;
gap: $spacing-xs;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-xs;
}
&:hover {
color: $primary-blue;
i {
transform: translateY(-2px);
}
}
i {
color: $primary-blue;
transition: $transition-base;
}
}
}
}
}
// Content section
.detail-content {
padding: $spacing-3xl $spacing-2xl;
min-height: 200px;
@media (max-width: $breakpoint-sm) {
padding: $spacing-2xl $spacing-lg;
}
.content-body {
font-size: $font-size-base;
color: $text-dark;
line-height: $line-height-loose;
white-space: pre-line;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-sm;
}
}
}
}
// Response section (answer from admin)
.inquiry-response-card {
background: $white;
border-radius: $border-radius-lg;
box-shadow: $shadow-md;
overflow: hidden;
margin-bottom: $spacing-xl;
border-left: 4px solid $accent-green;
.response-header {
padding: $spacing-xl $spacing-2xl;
background: rgba($accent-green, 0.03);
border-bottom: 1px solid $border-gray;
@media (max-width: $breakpoint-sm) {
padding: $spacing-lg;
}
.response-title {
display: flex;
align-items: center;
gap: $spacing-sm;
font-size: $font-size-lg;
font-weight: $font-weight-semibold;
color: $text-dark;
margin-bottom: $spacing-sm;
i {
color: $accent-green;
}
}
.response-meta {
font-size: $font-size-sm;
color: $text-gray;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-xs;
}
}
}
.response-content {
padding: $spacing-2xl;
font-size: $font-size-base;
color: $text-dark;
line-height: $line-height-loose;
white-space: pre-line;
@media (max-width: $breakpoint-sm) {
padding: $spacing-lg;
font-size: $font-size-sm;
}
}
}
// Inquiry actions (detail page buttons)
.inquiry-detail-actions {
display: flex;
justify-content: center;
gap: $spacing-md;
margin-bottom: $spacing-3xl;
flex-wrap: wrap;
@media (max-width: $breakpoint-sm) {
flex-direction: column;
align-items: stretch;
}
.action-btn {
padding: $spacing-md $spacing-2xl;
border-radius: $border-radius-md;
font-size: $font-size-base;
font-weight: $font-weight-medium;
text-decoration: none;
transition: $transition-base;
display: inline-flex;
align-items: center;
justify-content: center;
gap: $spacing-sm;
cursor: pointer;
border: none;
@media (max-width: $breakpoint-sm) {
padding: $spacing-md $spacing-lg;
font-size: $font-size-sm;
}
&.btn-list {
background: $white;
color: $text-dark;
border: 1px solid $border-gray;
&:hover {
background: $gray-bg;
border-color: $primary-blue;
}
}
&.btn-edit {
background: $gradient-primary;
color: $white;
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-md;
}
}
&.btn-delete {
background: $accent-orange;
color: $white;
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-md;
}
}
i {
font-size: 16px;
}
}
}
// ===========================
// Inquiry Form Page
// ===========================
.inquiry-form-page {
min-height: calc(100vh - 140px);
background: $gray-bg;
padding: $spacing-3xl $spacing-lg;
@media (max-width: $breakpoint-sm) {
padding: $spacing-2xl $spacing-md;
}
}
.inquiry-form-container {
max-width: 960px;
margin: 0 auto;
}
.inquiry-form-card {
background: $white;
border-radius: $border-radius-lg;
box-shadow: $shadow-md;
padding: $spacing-3xl;
margin-bottom: $spacing-xl;
@media (max-width: $breakpoint-sm) {
padding: $spacing-2xl $spacing-lg;
}
.form-group {
margin-bottom: $spacing-2xl;
&:last-child {
margin-bottom: 0;
}
.form-label {
display: block;
font-size: $font-size-base;
font-weight: $font-weight-semibold;
color: $text-dark;
margin-bottom: $spacing-md;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-sm;
}
&.required::after {
content: '*';
color: $accent-orange;
margin-left: $spacing-xs;
}
}
.form-input {
width: 100%;
padding: $spacing-md;
border: 1px solid $border-gray;
border-radius: $border-radius-md;
font-size: $font-size-base;
transition: $transition-base;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-sm;
padding: $spacing-sm $spacing-md;
}
&:focus {
outline: none;
border-color: $primary-blue;
box-shadow: 0 0 0 3px rgba($primary-blue, 0.1);
}
&::placeholder {
color: $text-light;
}
}
.form-textarea {
width: 100%;
min-height: 200px;
padding: $spacing-md;
border: 1px solid $border-gray;
border-radius: $border-radius-md;
font-size: $font-size-base;
font-family: $font-family-primary;
resize: vertical;
transition: $transition-base;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-sm;
padding: $spacing-sm $spacing-md;
min-height: 150px;
}
&:focus {
outline: none;
border-color: $primary-blue;
box-shadow: 0 0 0 3px rgba($primary-blue, 0.1);
}
&::placeholder {
color: $text-light;
}
}
// File upload field wrapper
.file-upload-wrapper {
display: flex;
gap: $spacing-md;
align-items: center;
width: 100%;
@media (max-width: $breakpoint-sm) {
flex-direction: column;
align-items: stretch;
}
// File name display input
.file-name-display {
flex: 1;
padding: $spacing-md;
background: $gray-bg;
border: 1px solid $border-gray;
border-radius: $border-radius-md;
font-size: $font-size-sm;
color: $text-gray;
cursor: default;
transition: $transition-base;
&.has-file {
color: $text-dark;
background: rgba($primary-blue, 0.05);
border-color: $primary-blue;
}
&:focus {
outline: none;
border-color: $primary-blue;
box-shadow: 0 0 0 3px rgba($primary-blue, 0.1);
}
}
// File upload button (label)
.file-upload-btn {
padding: $spacing-md $spacing-lg;
background: $gradient-primary;
color: $white;
border-radius: $border-radius-md;
font-size: $font-size-sm;
font-weight: $font-weight-medium;
cursor: pointer;
transition: $transition-base;
white-space: nowrap;
display: inline-flex;
align-items: center;
gap: $spacing-xs;
border: none;
@media (max-width: $breakpoint-sm) {
justify-content: center;
}
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-md;
}
&:active {
transform: translateY(0);
}
i {
font-size: 14px;
}
}
// File remove button
.file-remove-btn {
padding: $spacing-md;
background: $accent-orange;
color: $white;
border-radius: $border-radius-md;
border: none;
cursor: pointer;
transition: $transition-base;
display: inline-flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
@media (max-width: $breakpoint-sm) {
width: 100%;
height: auto;
padding: $spacing-md;
}
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-md;
background: darken($accent-orange, 5%);
}
&:active {
transform: translateY(0);
}
i {
font-size: 14px;
}
}
// Hidden file input
input[type="file"] {
display: none;
}
}
// File help text
.form-help-text {
margin-top: $spacing-md;
font-size: $font-size-xs;
color: $text-light;
line-height: $line-height-normal;
p {
margin: $spacing-xs 0;
display: flex;
align-items: flex-start;
gap: $spacing-xs;
i {
color: $primary-blue;
margin-top: 2px;
flex-shrink: 0;
}
}
}
}
}
// Inquiry form actions
.inquiry-form-actions {
display: flex;
justify-content: center;
gap: $spacing-md;
margin-top: $spacing-xl;
@media (max-width: $breakpoint-sm) {
flex-direction: column-reverse;
align-items: stretch;
}
// Uses the same action button styles from .inquiry-actions above
// Supports: action-btn-primary, action-btn-secondary
}
@@ -0,0 +1,455 @@
// -----------------------------------------------------------------------------
// Notice Pages - List and Detail Views
// -----------------------------------------------------------------------------
// Notice list page container
.notice-page {
min-height: calc(100vh - 140px);
background: $gray-bg;
padding: $spacing-3xl $spacing-lg;
@media (max-width: $breakpoint-sm) {
padding: $spacing-2xl $spacing-md;
}
}
.notice-container {
max-width: $container-max-width;
margin: 0 auto;
}
// Page header
.notice-header {
margin-bottom: $spacing-3xl;
@media (max-width: $breakpoint-sm) {
margin-bottom: $spacing-2xl;
}
.page-title {
font-size: $font-size-3xl;
font-weight: $font-weight-bold;
color: $text-dark;
margin-bottom: $spacing-md;
background: $gradient-primary;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-2xl;
}
}
.page-description {
font-size: $font-size-base;
color: $text-gray;
line-height: $line-height-normal;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-sm;
}
}
}
// Notice list wrapper
.notice-list-wrapper {
background: $white;
border-radius: $border-radius-lg;
box-shadow: $shadow-md;
overflow: hidden;
margin-bottom: $spacing-xl;
}
// Notice detail page
.notice-detail-page {
min-height: calc(100vh - 140px);
background: $gray-bg;
padding: $spacing-3xl $spacing-lg;
@media (max-width: $breakpoint-sm) {
padding: $spacing-2xl $spacing-md;
}
}
.notice-detail-container {
max-width: 960px;
margin: 0 auto;
}
// Notice detail card
.notice-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;
background: linear-gradient(180deg, $light-bg 0%, $white 100%);
@media (max-width: $breakpoint-sm) {
padding: $spacing-2xl $spacing-lg $spacing-md;
}
.notice-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;
}
}
.notice-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 {
padding: $spacing-lg $spacing-2xl;
background: $gray-bg;
border-bottom: 1px solid $border-gray;
@media (max-width: $breakpoint-sm) {
padding: $spacing-md $spacing-lg;
}
.attachment-list {
display: flex;
flex-direction: column;
gap: $spacing-sm;
.attachment-item {
display: flex;
align-items: center;
gap: $spacing-sm;
a {
color: $text-dark;
text-decoration: none;
font-size: $font-size-sm;
transition: $transition-base;
display: flex;
align-items: center;
gap: $spacing-xs;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-xs;
}
&:hover {
color: $primary-blue;
i {
transform: translateY(-2px);
}
}
i {
color: $primary-blue;
transition: $transition-base;
}
}
}
}
}
// Content section
.detail-content {
padding: $spacing-3xl $spacing-2xl;
min-height: 300px;
@media (max-width: $breakpoint-sm) {
padding: $spacing-2xl $spacing-lg;
}
.content-body {
font-size: $font-size-base;
color: $text-dark;
line-height: $line-height-loose;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-sm;
}
// Content formatting
p {
margin-bottom: $spacing-md;
&:last-child {
margin-bottom: 0;
}
}
h1, h2, h3, h4, h5, h6 {
margin-top: $spacing-xl;
margin-bottom: $spacing-md;
font-weight: $font-weight-semibold;
color: $text-dark;
}
img {
max-width: 100%;
height: auto;
border-radius: $border-radius-md;
margin: $spacing-lg 0;
}
a {
color: $primary-blue;
text-decoration: underline;
&:hover {
color: $secondary-blue;
}
}
ul, ol {
margin: $spacing-md 0;
padding-left: $spacing-xl;
}
li {
margin-bottom: $spacing-sm;
}
blockquote {
border-left: 4px solid $primary-blue;
padding-left: $spacing-lg;
margin: $spacing-lg 0;
color: $text-gray;
font-style: italic;
}
code {
background: $gray-bg;
padding: 2px 6px;
border-radius: $border-radius-sm;
font-family: $font-family-mono;
font-size: 0.9em;
color: $primary-blue;
}
pre {
background: $gray-bg;
border: 1px solid $border-gray;
border-radius: $border-radius-md;
padding: $spacing-md;
overflow-x: auto;
margin: $spacing-lg 0;
code {
background: transparent;
padding: 0;
color: $text-dark;
}
}
table {
width: 100%;
border-collapse: collapse;
margin: $spacing-lg 0;
border: 1px solid $border-gray;
border-radius: $border-radius-md;
overflow: hidden;
th {
background: $light-bg;
padding: $spacing-sm $spacing-md;
text-align: left;
font-weight: $font-weight-semibold;
border-bottom: 2px solid $border-gray;
}
td {
padding: $spacing-sm $spacing-md;
border-bottom: 1px solid $border-gray;
}
tr:last-child td {
border-bottom: none;
}
}
}
}
}
// Action buttons
.notice-actions {
display: flex;
justify-content: center;
gap: $spacing-md;
margin-bottom: $spacing-3xl;
@media (max-width: $breakpoint-sm) {
flex-direction: column;
align-items: stretch;
}
.action-btn {
padding: $spacing-md $spacing-2xl;
border-radius: $border-radius-md;
font-size: $font-size-base;
font-weight: $font-weight-medium;
text-decoration: none;
transition: $transition-base;
display: inline-flex;
align-items: center;
justify-content: center;
gap: $spacing-sm;
cursor: pointer;
@media (max-width: $breakpoint-sm) {
padding: $spacing-md $spacing-lg;
font-size: $font-size-sm;
}
&.btn-list {
background: $white;
color: $text-dark;
border: 1px solid $border-gray;
&:hover {
background: $gray-bg;
border-color: $primary-blue;
}
}
&.btn-edit {
background: $gradient-primary;
color: $white;
border: none;
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-md;
}
}
&.btn-delete {
background: $accent-orange;
color: $white;
border: none;
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-md;
}
}
i {
font-size: 16px;
}
}
}
// Navigation (prev/next)
.notice-navigation {
background: $white;
border-radius: $border-radius-lg;
box-shadow: $shadow-sm;
overflow: hidden;
.nav-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: $spacing-lg $spacing-2xl;
border-bottom: 1px solid $border-gray;
transition: $transition-base;
cursor: pointer;
@media (max-width: $breakpoint-sm) {
flex-direction: column;
align-items: flex-start;
padding: $spacing-md $spacing-lg;
gap: $spacing-sm;
}
&:last-child {
border-bottom: none;
}
&:hover {
background: $gray-bg;
.nav-title {
color: $primary-blue;
}
}
.nav-label {
display: flex;
align-items: center;
gap: $spacing-sm;
font-size: $font-size-sm;
font-weight: $font-weight-medium;
color: $text-gray;
min-width: 80px;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-xs;
}
i {
color: $primary-blue;
}
}
.nav-title {
flex: 1;
font-size: $font-size-base;
color: $text-dark;
transition: $transition-base;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-sm;
}
}
.nav-date {
font-size: $font-size-sm;
color: $text-light;
min-width: 100px;
text-align: right;
@media (max-width: $breakpoint-sm) {
font-size: $font-size-xs;
text-align: left;
}
}
}
}
@@ -1,121 +1,125 @@
<!doctype html> <!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" <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/kbank_base_layout}"> xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
<body> <body>
<section class="content" layout:fragment="contentFragment"> <th:block layout:fragment="contentFragment">
<div class="content_wrap"> <!-- FAQ Page -->
<div class="sub_title3"> <div class="faq-page">
<h2 class="title">FAQ</h2> <div class="faq-container">
</div>
<div class="inner i_cs h_inner8"> <!-- Page Header -->
<!-- 검색영역 --> <div class="faq-header">
<h1 class="page-title">FAQ</h1>
<p class="page-description">자주 묻는 질문과 답변을 확인하세요.</p>
</div>
<!-- Search and Filter Controls -->
<form name="articleForm" th:action="@{/faq_list}" method="post" th:object="${search}" onSubmit="fn_search_article(); return false;"> <form name="articleForm" th:action="@{/faq_list}" method="post" th:object="${search}" onSubmit="fn_search_article(); return false;">
<div class="board_search"> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<div class="total-post-wrap"> <input name="page" type="hidden" value="">
<span class="total-post pc-only">
<strong th:text="${page.totalElements}"></strong> <div class="table-controls">
</span> <div class="total-count">
<strong th:text="${page.totalElements}">0</strong>
</div> </div>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input name="page" type="hidden" value=""> <div class="search-box">
<div class="search_txt "> <input type="text"
<input type="text" class="common_input_type_1 h_inp2" th:field="*{searchWrd}" placeholder="제목/내용 검색"> th:field="*{searchWrd}"
</div> placeholder="질문 또는 답변 내용으로 검색">
<div class="btn_wrap search_btn"> <button type="button" onclick="fn_search_article()">
<button type="button" class="btn_search" onclick="fn_search_article()">검색</button> <i class="fas fa-search"></i>
<i class="i_search"></i> <span>검색</span>
</button>
</div> </div>
</div> </div>
</form> </form>
<div class="total-post-wrap">
<span class="total-post m-only">
<strong th:text="${page.totalElements}"></strong>
</span>
</div>
<!-- // 검색영역 -->
<div class="board-list-wrap notice"> <!-- FAQ Accordion -->
<div class="box-brandInfo gapType-1 faq"> <div class="faq-accordion" th:if="${!#lists.isEmpty(page.content)}">
<ul> <div class="faq-item" th:each="faq : ${page.content}">
<li th:each="faq : ${page.content}"> <div class="faq-question">
<div class="bd_txt"> <div class="faq-question-text" th:text="${faq.faqQuestion}">
<a href="#none" th:text="${faq.faqQuestion}"></a> 질문 내용이 여기에 표시됩니다.
</div> </div>
<div class="box-ct"> <i class="faq-icon fas fa-chevron-down"></i>
<div id="faqAnswer" class="list_txt" th:utext="${faq.faqAnswer}"></div> </div>
</div> <div class="faq-answer">
</li> <div class="faq-answer-content" th:utext="${faq.faqAnswer}">
<li class="no-result-wrap" th:if="${#lists.isEmpty(page.content)}"> 답변 내용이 여기에 표시됩니다.
<div class="no_data"> </div>
<div class="no_result_text m-only"> </div>
<img th:src="@{/img/img_nodata.png}" alt="">
<p>조회된 검색 결과가 없습니다.</p>
</div>
<div class="no_result_text pc-only">
<img th:src="@{/img/img_nodata2.png}" alt="">
<p>조회된 검색 결과가 없습니다.</p>
</div>
</div>
</li>
</ul>
</div> </div>
</div> </div>
<!-- pagination --> <!-- Empty State -->
<div class="table-empty" th:if="${#lists.isEmpty(page.content)}">
<div class="empty-icon">
<img th:src="@{/img/img_nodata.png}" alt="데이터 없음">
</div>
<p class="empty-text">조회된 검색 결과가 없습니다.</p>
</div>
<!-- Pagination -->
<div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}"> <div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}">
</div> </div>
<!-- // pagination -->
</div> </div>
</div> </div>
</section> </th:block>
</body> </body>
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
<script> <script>
function fn_init() {
document.inquiryForm.searchWrd.focus();
}
function fn_select_page(pageNo) { function fn_select_page(pageNo) {
document.articleForm.page.value = pageNo; document.articleForm.page.value = pageNo;
document.articleForm.action = '[[@{/faq_list}]]'; document.articleForm.action = '[[@{/faq_list}]]';
document.articleForm.submit(); document.articleForm.submit();
} }
/*********************************************************
* 조회 처리 함수
******************************************************** */
function fn_search_article() { function fn_search_article() {
document.articleForm.page.value = 1; document.articleForm.page.value = 1;
document.articleForm.submit(); document.articleForm.submit();
} }
function decodeHTMLEntities(text) { function decodeHTMLEntities(text) {
var textArea = document.createElement('textarea'); var textArea = document.createElement('textarea');
textArea.innerHTML = text; textArea.innerHTML = text;
return textArea.value; return textArea.value;
} }
$(function() { document.addEventListener('DOMContentLoaded', function() {
fn_init(); // Decode HTML entities in FAQ answers
document.querySelectorAll('.faq-answer-content').forEach(function(element) {
element.innerHTML = decodeHTMLEntities(element.innerHTML);
});
// FAQ 아코디언 기능 추가 // FAQ Accordion functionality
$('.box-brandInfo.faq .bd_txt a').click(function(e) { document.querySelectorAll('.faq-question').forEach(function(question) {
e.preventDefault(); question.addEventListener('click', function(e) {
$(this).closest('li').toggleClass('sel'); e.preventDefault();
$(this).closest('li').find('.box-ct').slideToggle();
const faqItem = this.closest('.faq-item');
const faqAnswer = faqItem.querySelector('.faq-answer');
const isActive = faqItem.classList.contains('active');
// Toggle active state
faqItem.classList.toggle('active');
// Toggle answer visibility with animation
if (isActive) {
faqAnswer.classList.remove('show');
} else {
faqAnswer.classList.add('show');
}
});
}); });
}); });
$('[id="faqAnswer"]').each(function() {
$(this).html(decodeHTMLEntities($(this).html()));
});
</script> </script>
</th:block> </th:block>
@@ -1,72 +1,110 @@
<!doctype html> <!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" <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/kbank_base_layout}"> xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
<body> <body>
<section layout:fragment="contentFragment" class="content"> <th:block layout:fragment="contentFragment">
<div class="content_wrap"> <!-- Inquiry Detail Page -->
<div class="sub_title3"> <div class="inquiry-page">
<h2 class="title">Q&amp;A</h2> <div class="inquiry-container">
</div>
<div class="inner i_cs h_inner8 h_inner10">
<div class="board-view"> <!-- Page Header -->
<div class="board-view-head qna"> <div class="inquiry-header">
<div class="complete_box"> <h1 class="page-title">Q&A</h1>
<span th:class="${inquiry.inquiryStatus == 'RESPONDED' ? 'complete' : 'question'}" th:text="${inquiry.inquiryStatus =='RESPONDED'?'답변완료': '문의중'}"></span> <p class="page-description">문의 내용을 확인하세요.</p>
</div> </div>
<div class="detail_title">
<strong class="title" th:text="${inquiry.inquirySubject}"></strong> <!-- Inquiry Detail Card -->
</div> <div class="inquiry-detail-card">
<div class="write-info"> <div class="detail-header">
<p class="date-info"> <div class="status-wrapper">
<span class="date_stxt">등록일 : <em>[[${#temporals.format(inquiry.createdDate, 'yyyy-MM-dd HH:mm')}]]</em></span> <span class="status-badge-header"
<span>작성자 : [[${inquiry.maskedInquirerName}]]</span> th:classappend="${inquiry.inquiryStatus == 'RESPONDED' ? 'status-completed' : 'status-pending'}"
</p> th:text="${inquiry.inquiryStatus == 'RESPONDED' ? '답변완료' : '문의중'}">
</div> 문의중
</div>
<div class="board_file" th:if="${!#strings.isEmpty(inquiry.attachFile)}">
<p class="file_group" th:with="fileInfo=${@fileService.findById(inquiry.attachFile)}">
<span th:each="fileDetail, status : ${fileInfo.getFileDetails()}">
<a th:href="'javascript:fn_downloadFile(\'' + ${fileDetail.fileId} + '\',\''+ ${fileDetail.fileSn} +'\')'">[[${fileDetail.originalFileName}]].[[${fileDetail.fileExtension}]]</a>
</span> </span>
</p> </div>
<h2 class="inquiry-title" th:text="${inquiry.inquirySubject}">질문 제목</h2>
<div class="inquiry-meta">
<div class="meta-item">
<i class="fas fa-calendar-alt"></i>
<span>등록일</span>
<strong th:text="${#temporals.format(inquiry.createdDate, 'yyyy-MM-dd HH:mm')}">2025.01.01 10:00</strong>
</div>
<div class="meta-item">
<i class="fas fa-user"></i>
<span>작성자</span>
<strong th:text="${inquiry.maskedInquirerName}">홍**</strong>
</div>
</div>
</div> </div>
<div class="board-view-body" style="white-space: pre-line;">
<!-- Attachment Section -->
<div class="detail-attachment" th:if="${!#strings.isEmpty(inquiry.attachFile)}">
<div class="attachment-icon">
<i class="fas fa-paperclip"></i>
</div>
<div class="attachment-list" th:with="fileInfo=${@fileService.findById(inquiry.attachFile)}">
<a class="attachment-item"
th:each="fileDetail : ${fileInfo.getFileDetails()}"
th:href="'javascript:fn_downloadFile(\\'' + ${fileDetail.fileId} + '\\',\\''+ ${fileDetail.fileSn} +'\\')'">
<i class="fas fa-file"></i>
<span>[[${fileDetail.originalFileName}]].[[${fileDetail.fileExtension}]]</span>
</a>
</div>
</div>
<!-- Inquiry Content -->
<div class="detail-content" style="white-space: pre-line;">
<th:block th:utext="${inquiry.inquiryDetail}"></th:block> <th:block th:utext="${inquiry.inquiryDetail}"></th:block>
</div> </div>
</div> </div>
<div class="comment_wrap" th:if="${inquiry.inquiryStatus == 'RESPONDED'}"> <!-- Admin Response Card -->
<div class="comment_wrap_head"> <div class="inquiry-response-card" th:if="${inquiry.inquiryStatus == 'RESPONDED'}">
<strong class="title"> <div class="response-header">
[[${inquiry.responderName}]] <em>(등록일 : [[${#temporals.format(inquiry.responseDate, 'yyyy-MM-dd HH:mm')}]])</em> <div class="response-title">
</strong> <i class="fas fa-comment-dots"></i>
<div class="m-only"> <span>관리자 답변</span>
<th:block class="responseDetail" th:utext="${inquiry.responseDetail}"></th:block>
</div> </div>
<div class="pc-only"> <div class="response-meta">
<th:block class="responseDetail pc-only" th:utext="${inquiry.responseDetail}"></th:block> <span th:text="${inquiry.responderName}">관리자</span>
<span class="meta-separator"></span>
<span th:text="${#temporals.format(inquiry.responseDate, 'yyyy-MM-dd HH:mm')}">2025.01.02 14:30</span>
</div> </div>
</div> </div>
<div class="response-content">
<th:block class="responseDetail" th:utext="${inquiry.responseDetail}"></th:block>
</div>
</div> </div>
<div class="qna_btn_group">
<th:block th:if="${inquiry.inquiryStatus != 'RESPONDED' && inquiry.inquirer.id == #authentication.principal.id}">
<form id="deleteForm" th:action="@{/inquiry/{id}/delete(id=${inquiry.id})}" method="post" style="display: inline;"></form>
<a th:href="@{/inquiry/edit(id=${inquiry.id})}" class="common_btn_type_1 btn_edit">수정</a>
<a href="#" class="common_btn_type_1 btn_delete" onclick="confirmDelete(); return false;">삭제</a>
</th:block>
</div>
<div class="btn_board btn_top" >
<a href="/inquiry" class="common_btn_type_1">목록</a>
</div>
</div>
<!-- Action Buttons -->
<div class="inquiry-actions">
<div class="actions-left">
<a th:href="@{/inquiry}" class="action-btn-secondary">
<i class="fas fa-list"></i>
<span>목록</span>
</a>
</div>
<div class="actions-right" th:if="${inquiry.inquiryStatus != 'RESPONDED' && inquiry.inquirer.id == #authentication.principal.id}">
<form id="deleteForm" th:action="@{/inquiry/{id}/delete(id=${inquiry.id})}" method="post" style="display: inline;"></form>
<a th:href="@{/inquiry/edit(id=${inquiry.id})}" class="action-btn-edit">
<i class="fas fa-edit"></i>
<span>수정</span>
</a>
<a href="#" class="action-btn-delete" onclick="confirmDelete(); return false;">
<i class="fas fa-trash-alt"></i>
<span>삭제</span>
</a>
</div>
</div>
</div>
</div> </div>
</section> </th:block>
</body> </body>
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
<script th:inline="javascript"> <script th:inline="javascript">
@@ -1,156 +1,189 @@
<!doctype html> <!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" <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/kbank_base_layout}"> xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
<body> <body>
<section layout:fragment="contentFragment" th:with="isNew=${inquiry.id == null}" class="content"> <th:block layout:fragment="contentFragment" th:with="isNew=${inquiry.id == null}">
<div class="content_wrap"> <!-- Inquiry Form Page -->
<div class="sub_title3"> <div class="inquiry-page">
<h2 class="title" th:text="${isNew ? 'Q&amp;A 질문하기' : 'Q&amp;A 질문 수정'}"></h2> <div class="inquiry-container">
</div>
<div class="inner i_cs h_inner5">
<form name="inquiryForm" id="inquiryForm" th:action="${isNew}? @{/inquiry} : @{/inquiry/edit}"
th:object="${inquiry}" enctype="multipart/form-data" method="post">
<input type="hidden" th:field="*{id}" th:if="${!isNew}">
<div class="form_type">
<div class="info1 form_top">
<p class="title w_tit">
<span class="dot">제목</span>
</p>
<div class="info_line info_add">
<div class="info_box1 w_inp4">
<input type="text" class="common_input_type_1" placeholder="제목" id="inquirySubject"
th:field="*{inquirySubject}" required>
</div>
</div>
</div>
<div class="info1"> <!-- Page Header -->
<p class="title w_tit"> <div class="inquiry-header">
<span class="dot">내용</span> <h1 class="page-title" th:text="${isNew ? 'Q&A 질문하기' : 'Q&A 질문 수정'}">Q&A 질문하기</h1>
</p> <p class="page-description">궁금하신 사항을 작성해 주세요.</p>
<div class="info_line info_add"> </div>
<div class="info_box1 qna_text w_inp4">
<textarea name="text" id="inquiryDetail" rows="5" cols="30" class="common_textareaType_1 f_inp"
th:field="*{inquiryDetail}" placeholder="내용"></textarea>
<!-- <span>158/1,000 byte</span> -->
</div>
</div>
</div>
<div class="info1 pt28"> <!-- Inquiry Form Card -->
<p class="title tit_add w_tit"> 첨부파일 </p> <div class="inquiry-form-card">
<div class="info_line info_add"> <form name="inquiryForm" id="inquiryForm" th:action="${isNew}? @{/inquiry} : @{/inquiry/edit}"
<div class="info_box1 info_add2"> th:object="${inquiry}" enctype="multipart/form-data" method="post">
<input type="hidden" th:field="*{id}" th:if="${!isNew}">
<div class="filebox"> <!-- Subject Field -->
<input type="text" class="upload-name before" <div class="form-group">
th:class="${!#strings.isEmpty(inquiry.attachFile)} ? 'upload-name i_trash after' : 'upload-name before'" <label for="inquirySubject" class="form-label required">
th:value="${!#strings.isEmpty(inquiry.attachFile)} ? ${@fileService.findById(inquiry.attachFile).fileDetails[0].originalFileName + '.' + @fileService.findById(inquiry.attachFile).fileDetails[0].fileExtension}" <span>제목</span>
readonly="readonly"> </label>
<input type="hidden" th:field="*{attachFile}"> <div class="form-input-wrapper">
</div> <input type="text"
<div class="btn_check"> id="inquirySubject"
<div class="file_btn"> th:field="*{inquirySubject}"
<label for="file">파일선택</label> class="form-input"
<input type="file" id="file" name="files" placeholder="제목을 입력하세요"
accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.hwp,.gif,.jpg,.png"> required>
</div> </div>
</div> </div>
</div>
<div class="list-dot"> <!-- Content Field -->
<p>첨부파일은 10MB 이내로 등록해 주세요.</p> <div class="form-group">
<p>문서파일과 이미지 파일만 등록 가능합니다. (pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg, <label for="inquiryDetail" class="form-label required">
png)</p> <span>내용</span>
</div> </label>
</div> <div class="form-input-wrapper">
</div> <textarea id="inquiryDetail"
th:field="*{inquiryDetail}"
class="form-textarea"
rows="10"
placeholder="내용을 입력하세요"
required></textarea>
</div>
</div>
<!-- File Upload Field -->
<div class="form-group">
<label for="file" class="form-label">
<span>첨부파일</span>
</label>
<div class="form-input-wrapper">
<div class="file-upload-wrapper">
<input type="text"
class="form-input file-name-display"
th:class="${!#strings.isEmpty(inquiry.attachFile)} ? 'form-input file-name-display has-file' : 'form-input file-name-display'"
th:value="${!#strings.isEmpty(inquiry.attachFile)} ? ${@fileService.findById(inquiry.attachFile).fileDetails[0].originalFileName + '.' + @fileService.findById(inquiry.attachFile).fileDetails[0].fileExtension} : ''"
placeholder="파일을 선택하세요"
readonly>
<input type="hidden" th:field="*{attachFile}">
<label for="file" class="file-upload-btn">
<i class="fas fa-paperclip"></i>
<span>파일 선택</span>
</label>
<input type="file"
id="file"
name="files"
accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.hwp,.gif,.jpg,.png"
style="display: none;">
<button type="button" class="file-remove-btn" style="display: none;">
<i class="fas fa-times"></i>
</button>
</div>
<div class="form-help-text">
<p><i class="fas fa-info-circle"></i> 첨부파일은 10MB 이내로 등록해 주세요.</p>
<p><i class="fas fa-info-circle"></i> 문서파일과 이미지 파일만 등록 가능합니다. (pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg, png)</p>
</div>
</div>
</div>
<!-- Form Actions -->
<div class="inquiry-form-actions">
<a th:href="@{/inquiry}" class="action-btn-secondary">
<i class="fas fa-times"></i>
<span>취소</span>
</a>
<button type="button" class="action-btn-primary btn-submit">
<i class="fas fa-check"></i>
<span>확인</span>
</button>
</div>
</form>
</div>
<div class="btn_application btn_top3">
<a th:href="@{/inquiry}" class="common_btn_type_1 gray"><span>취소</span></a>
<div class="btn_gap"></div>
<a href="#none" class="common_btn_type_1 btn_register"><span>확인</span></a>
</div>
</div>
</form>
</div>
</div> </div>
</section> </div>
</th:block>
</body> </body>
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
<script th:if="${error}" th:inline="javascript"> <script th:if="${error}" th:inline="javascript">
$(document).ready(function () { $(document).ready(function () {
const errorMsg = [[${error}]]; const errorMsg = [[${error}]];
const formattedMsg = errorMsg.replace(/\n/g,'<br>'); const formattedMsg = errorMsg.replace(/\n/g,'<br>');
$('#customAlertMessage').html(formattedMsg); $('#customAlertMessage').html(formattedMsg);
$('#customAlert').css('display','flex'); $('#customAlert').css('display','flex');
}); });
</script> </script>
<script th:inline="javascript"> <script th:inline="javascript">
$(function () { $(function () {
let fnRegister = document.querySelector('.btn_register'); let btnSubmit = document.querySelector('.btn-submit');
let fileInput = document.getElementById('file');
let fileNameDisplay = document.querySelector('.file-name-display');
let fileRemoveBtn = document.querySelector('.file-remove-btn');
fnRegister.addEventListener('click', function (event) { // Form submission handler
btnSubmit.addEventListener('click', function (event) {
event.preventDefault();
let form = document.getElementById('inquiryForm'); let form = document.getElementById('inquiryForm');
let inquirySubject = document.getElementById('inquirySubject').value.trim(); let inquirySubject = document.getElementById('inquirySubject').value.trim();
let inquiryDetail = document.getElementById('inquiryDetail').value; let inquiryDetail = document.getElementById('inquiryDetail').value;
// validation 체크 // Validation
if (!inquirySubject) { if (!inquirySubject) {
customPopups.showAlert('제목을 작성해 주세요.'); customPopups.showAlert('제목을 작성해 주세요.');
document.getElementById('inquirySubject').focus(); document.getElementById('inquirySubject').focus();
return; return;
} }
if (!inquiryDetail.trim()) { if (!inquiryDetail.trim()) {
customPopups.showAlert('내용을 작성해 주세요.'); customPopups.showAlert('내용을 작성해 주세요.');
document.getElementById('inquiryDetail').focus(); document.getElementById('inquiryDetail').focus();
return false; return false;
} }
form.submit(); form.submit();
}); });
document.getElementById('inquiryForm').inquirySubject.focus(); // File input change handler
const fileInput = document.getElementById('file'); fileInput.addEventListener('change', function () {
const fileNameInput = document.querySelector('.upload-name'); const file = fileInput.files[0];
fileInput.addEventListener('change', function () { if (file) {
const file = fileInput.files[0]; const allowedExtensions = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.hwp', '.gif', '.jpg', '.jpeg', '.png'];
const fileExt = '.' + file.name.split('.').pop().toLowerCase();
if (file) { if (!allowedExtensions.includes(fileExt)) {
const allowedExtensions = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.hwp', '.gif', '.jpg', '.jpeg', '.png']; customPopups.showAlert("허용된 파일 형식이 아닙니다.\n문서파일(pdf, doc, docx, xls, xlsx, ppt, pptx, hwp)과 이미지 파일(gif, jpg, png)만 등록 가능합니다.");
const fileExt = '.' + file.name.split('.').pop().toLowerCase(); fileInput.value = '';
fileNameDisplay.value = '';
fileNameDisplay.classList.remove('has-file');
fileRemoveBtn.style.display = 'none';
return;
}
if (!allowedExtensions.includes(fileExt)) { fileNameDisplay.value = file.name;
customPopups.showAlert("허용된 파일 형식이 아닙니다.\n문서파일(pdf, doc, docx, xls, xlsx, ppt, pptx, hwp)과 이미지 파일(gif, jpg, png)만 등록 가능합니다."); fileNameDisplay.classList.add('has-file');
fileInput.value = ''; fileRemoveBtn.style.display = 'flex';
fileNameInput.value = ''; }
fileNameInput.classList.remove('i_trash', 'after'); });
fileNameInput.classList.add('before');
return;
}
fileNameInput.value = file.name; // Set the file name // File remove handler
fileNameInput.classList.remove('before'); // Remove the 'before' class fileRemoveBtn.addEventListener('click', function () {
fileNameInput.classList.add('i_trash', 'after'); // Add 'i_trash' and 'after' classes fileInput.value = '';
} fileNameDisplay.value = '';
}); fileNameDisplay.classList.remove('has-file');
fileRemoveBtn.style.display = 'none';
});
// Handle file removal when upload-name is clicked // Show remove button if file already exists
fileNameInput.addEventListener('click', function () { if (fileNameDisplay.value) {
if (fileNameInput.classList.contains('i_trash')) { fileRemoveBtn.style.display = 'flex';
fileInput.value = ''; // Clear the file input }
fileNameInput.value = ''; // Reset the file name input
fileNameInput.classList.remove('i_trash', 'after'); // Remove the 'i_trash' and 'after' classes // Focus on subject field
fileNameInput.classList.add('before'); // Add the 'before' class back document.getElementById('inquirySubject').focus();
} });
}); </script>
});
</script>
</th:block> </th:block>
</html> </html>
@@ -1,101 +1,106 @@
<!doctype html> <!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" <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/kbank_base_layout}"> xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
<body> <body>
<section class="content" layout:fragment="contentFragment"> <th:block layout:fragment="contentFragment">
<div class="content_wrap"> <!-- Inquiry List Page -->
<div class="sub_title3"> <div class="inquiry-page">
<h2 class="title">Q&amp;A</h2> <div class="inquiry-container">
</div>
<div class="inner i_cs h_inner8">
<!-- 검색영역 --> <!-- Page Header -->
<div class="inquiry-header">
<h1 class="page-title">Q&A</h1>
<p class="page-description">궁금하신 사항을 문의하고 답변을 확인하세요.</p>
</div>
<!-- Search and Filter Controls -->
<form name="inquiryForm" th:action="@{/inquiry}" method="get" onsubmit="return false;"> <form name="inquiryForm" th:action="@{/inquiry}" method="get" onsubmit="return false;">
<div class="board_search"> <input type="hidden" name="page" value="1">
<div class="total-post-wrap"> <input name="searchCnd" type="hidden" value="0">
<span class="total-post pc-only">
<strong th:text="${page.totalElements}"></strong> <div class="table-controls">
</span> <div class="total-count">
<strong th:text="${page.totalElements}">0</strong>
</div> </div>
<input type="hidden" name="page" value="1">
<input name="searchCnd" type="hidden" value="0"> <div class="search-box">
<div class="search_txt "> <input type="text"
<input type="text" class="common_input_type_1 h_inp2" name="searchWrd" name="searchWrd"
th:value="${search.searchWrd}" placeholder="제목/내용 검색"> th:value="${search.searchWrd}"
</div> placeholder="제목 또는 내용으로 검색">
<div class="btn_wrap search_btn"> <button type="button" onclick="fn_search_inquiry()">
<button type="button" class="btn_search" onclick="fn_search_inquiry()">검색</button> <i class="fas fa-search"></i>
<i class="i_search"></i> <span>검색</span>
</button>
</div> </div>
</div> </div>
</form> </form>
<div class="total-post-wrap">
<span class="total-post m-only">
<strong th:text="${page.totalElements}"></strong>
</span>
</div>
<!-- // 검색영역 -->
<div class="board-list-wrap notice"> <!-- Inquiry Table -->
<ul class="board-list qna_list"> <div class="inquiry-list-wrapper" th:if="${!inquiries.isEmpty()}">
<li class="th-head"> <div class="inquiry-table">
<div> <!-- Table Header -->
<span class="col-num">No</span> <div class="table-header">
<span class="col-title">제목</span> <div class="col-num">NO</div>
<span class="col-name">작성자</span> <div class="col-title">제목</div>
<span class="col-comp">처리상태</span> <div class="col-name">작성자</div>
<span class="col-date">등록일</span> <div class="col-status">처리상태</div>
</div> <div class="col-date">등록일</div>
</li> </div>
<li th:each="inquiry, status : ${inquiries}">
<div class="td-body">
<span class="col-num"
th:text="${page.totalElements - (page.number * page.size) - status.index}"></span>
<span class="col-title">
<em class="qna-type">
<a th:href="@{/inquiry/detail(id=${inquiry.id})}">[[${inquiry.inquirySubject}]]<span
th:if="${#strings.isEmpty(inquiry.attachFile) == false}" class="icon_file">
<img th:src="@{/img/icon/icon_file.png}" alt=""></span></a>
</em>
</span>
<span class="col-name" th:text="${inquiry.maskedInquirerName}"></span>
<span class="col-comp">
<span th:class="${inquiry.inquiryStatus == 'RESPONDED' ? 'complete' : 'question'}"
th:text="${inquiry.inquiryStatus =='RESPONDED'?'답변완료': '문의중'}"></span>
</span>
<span class="col-date"
th:text="${#temporals.format(inquiry.createdDate, 'yyyy.MM.dd')}"></span>
</div>
</li>
<li class="no-result-wrap" th:if="${inquiries.isEmpty()}">
<div class="no_data">
<div class="no_result_text m-only">
<img th:src="@{/img/img_nodata.png}" alt="">
<p>조회된 검색 결과가 없습니다.</p>
</div>
<div class="no_result_text pc-only">
<img th:src="@{/img/img_nodata2.png}" alt="">
<p>조회된 검색 결과가 없습니다.</p>
</div>
</div>
</li>
</ul>
</div>
<div class="pg_btn"> <!-- Table Rows -->
<!-- pagination --> <div class="table-row"
<div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}"> th:each="inquiry, status : ${inquiries}"
</div> th:onclick="'location.href=\'' + @{/inquiry/detail(id=${inquiry.id})} + '\''">
<!-- // pagination --> <div class="col-num"
<div class="btn_board btn_top"> th:text="${page.totalElements - (page.number * page.size) - status.index}">1</div>
<a th:href="@{/inquiry/new}" class="common_btn_type_1 btn_qna">질문하기</a> <div class="col-title">
<a th:href="@{/inquiry/detail(id=${inquiry.id})}">
<span th:text="${inquiry.inquirySubject}">질문 제목</span>
<span class="file-icon" th:if="${!#strings.isEmpty(inquiry.attachFile)}">
<img th:src="@{/img/icon/icon_file.png}" alt="첨부파일">
</span>
</a>
</div>
<div class="col-name" th:text="${inquiry.maskedInquirerName}">홍**</div>
<div class="col-status">
<span class="status-badge"
th:classappend="${inquiry.inquiryStatus == 'RESPONDED' ? 'status-completed' : 'status-pending'}"
th:text="${inquiry.inquiryStatus == 'RESPONDED' ? '답변완료' : '문의중'}">
문의중
</span>
</div>
<div class="col-date"
th:text="${#temporals.format(inquiry.createdDate, 'yyyy.MM.dd')}">2025.01.01</div>
</div>
</div> </div>
</div> </div>
<!-- Empty State -->
<div class="table-empty" th:if="${inquiries.isEmpty()}">
<div class="empty-icon">
<img th:src="@{/img/img_nodata.png}" alt="데이터 없음">
</div>
<p class="empty-text">조회된 검색 결과가 없습니다.</p>
</div>
<!-- Pagination -->
<div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}">
</div>
<!-- New Inquiry Button -->
<div class="inquiry-actions">
<a th:href="@{/inquiry/new}" class="action-btn-new">
<i class="fas fa-pen"></i>
<span>질문하기</span>
</a>
</div>
</div> </div>
</div> </div>
</section> </th:block>
</body> </body>
@@ -1,42 +1,64 @@
<!doctype html> <!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" <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/kbank_base_layout}"> xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
<body> <body>
<section class="content" layout:fragment="contentFragment"> <th:block layout:fragment="contentFragment">
<div class="content_wrap"> <!-- Notice Detail Page -->
<div class="sub_title3"> <div class="notice-detail-page">
<h2 class="title">공지사항</h2> <div class="notice-detail-container">
</div>
<div class="inner i_cs h_inner8"> <!-- Page Header -->
<div class="board-view"> <div class="notice-header">
<div class="board-view-head"> <h1 class="page-title">공지사항</h1>
<strong class="title" th:text="${portalNotice.noticeSubject}"></strong> </div>
<div class="write-info">
<p class="date-info"> <!-- Notice Detail Card -->
<span>등록일 <em th:text="${#temporals.format(portalNotice.createdDate, 'yyyy.MM.dd')}"></em></span> <div class="notice-detail-card">
</p> <!-- Header Section -->
<div class="detail-header">
<h2 class="notice-title" th:text="${portalNotice.noticeSubject}">공지사항 제목</h2>
<div class="notice-meta">
<div class="meta-item">
<i class="fas fa-calendar-alt"></i>
<span>등록일</span>
<strong th:text="${#temporals.format(portalNotice.createdDate, 'yyyy.MM.dd')}">2025.01.01</strong>
</div>
</div> </div>
</div> </div>
<div class="board_file" th:if="${!#strings.isEmpty(portalNotice.fileId)}">
<p class="file_group" th:with="fileInfo=${@fileService.findById(portalNotice.fileId)}"> <!-- Attachments Section -->
<span th:each="fileDetail, status : ${fileInfo.getFileDetails()}"> <div class="detail-attachments" th:if="${!#strings.isEmpty(portalNotice.fileId)}">
<a th:href="'javascript:fn_downloadFile(\'' + ${fileDetail.fileId} + '\',\''+ ${fileDetail.fileSn} +'\')'">첨부파일 : [[${fileDetail.originalFileName}]].[[${fileDetail.fileExtension}]]</a> <div class="attachment-list" th:with="fileInfo=${@fileService.findById(portalNotice.fileId)}">
</span> <div class="attachment-item" th:each="fileDetail, status : ${fileInfo.getFileDetails()}">
</p> <a th:href="'javascript:fn_downloadFile(\'' + ${fileDetail.fileId} + '\',\''+ ${fileDetail.fileSn} +'\')'">
<i class="fas fa-paperclip"></i>
<span>[[${fileDetail.originalFileName}]].[[${fileDetail.fileExtension}]]</span>
</a>
</div>
</div>
</div> </div>
<div class="board-view-body">
<div id="noticeDetail" th:utext="${portalNotice.noticeDetail}"></div> <!-- Content Section -->
<div class="detail-content">
<div id="noticeDetail" class="content-body" th:utext="${portalNotice.noticeDetail}">
공지사항 내용이 여기에 표시됩니다.
</div>
</div> </div>
</div> </div>
<div class="btn_board btn_top"> <!-- Action Buttons -->
<a href="#" class="common_btn_type_1" th:onclick="|window.location.href='@{/portalnotice}';|">목록</a> <div class="notice-actions">
<a href="#" class="action-btn btn-list" th:onclick="|window.location.href='@{/portalnotice}';|">
<i class="fas fa-list"></i>
<span>목록으로</span>
</a>
</div> </div>
</div> </div>
</div> </div>
</section> </th:block>
</body> </body>
@@ -1,90 +1,86 @@
<!doctype html> <!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" <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/kbank_base_layout}"> xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
<body> <body>
<section class="content" layout:fragment="contentFragment"> <th:block layout:fragment="contentFragment">
<div class="content_wrap"> <!-- Notice List Page -->
<div class="sub_title3"> <div class="notice-page">
<h2 class="title">공지사항</h2> <div class="notice-container">
</div>
<div class="inner i_cs4 h_inner8 h_inner10">
<!-- 검색영역 --> <!-- Page Header -->
<div class="notice-header">
<h1 class="page-title">공지사항</h1>
<p class="page-description">광주은행 API Portal의 새로운 소식과 공지사항을 확인하세요.</p>
</div>
<!-- Search and Filter Controls -->
<form name="noticeForm" th:action="@{/portalnotice}" method="get" onsubmit="return false;"> <form name="noticeForm" th:action="@{/portalnotice}" method="get" onsubmit="return false;">
<div class="board_search"> <input type="hidden" name="page" value="1">
<div class="total-post-wrap">
<span class="total-post pc-only"> <div class="table-controls">
<strong th:text="${page.totalElements}"></strong> <div class="total-count">
</span> <strong th:text="${page.totalElements}">0</strong>
</div> </div>
<input type="hidden" name="page" value="1"> <div class="search-box">
<div class="search_txt"> <input type="text"
<input type="text" class="common_input_type_1 h_inp2" name="searchSubjectOrDetail" th:value="${search.searchSubjectOrDetail}" placeholder="제목/내용 검색"> name="searchSubjectOrDetail"
</div> th:value="${search.searchSubjectOrDetail}"
<div class="btn_wrap search_btn"> placeholder="제목 또는 내용으로 검색">
<button type="button" class="btn_search" onclick="fn_search_notice()">검색</button> <button type="button" onclick="fn_search_notice()">
<i class="i_search"></i> <i class="fas fa-search"></i>
<span>검색</span>
</button>
</div> </div>
</div> </div>
</form> </form>
<div class="total-post-wrap">
<span class="total-post m-only">
<strong th:text="${page.totalElements}"></strong>
</span>
</div>
<!-- // 검색영역 -->
<div class="board-list-wrap notice"> <!-- Notice Table -->
<ul class="board-list notice-type"> <div class="notice-list-wrapper" th:if="${!portalNotices.isEmpty()}">
<li class="th-head"> <div class="notice-table">
<div> <!-- Table Header -->
<span class="col-num">NO</span> <div class="table-header">
<span class="col-title">제목</span> <div class="col-num">NO</div>
<span class="col-date">등록일</span> <div class="col-title">제목</div>
<div class="col-date">등록일</div>
</div>
<!-- Table Rows -->
<div class="table-row"
th:each="notice, status : ${portalNotices}"
th:onclick="'location.href=\'' + @{/portalnotice/detail(id=${notice.id})} + '\''">
<div class="col-num"
th:text="${page.totalElements - (page.number * page.size) - status.index}">1</div>
<div class="col-title">
<a th:href="@{/portalnotice/detail(id=${notice.id})}">
<span th:text="${notice.noticeSubject}">공지사항 제목</span>
<span class="file-icon" th:if="${notice.fileId != null and !notice.fileId.isEmpty()}">
<img th:src="@{/img/icon/icon_file.png}" alt="첨부파일">
</span>
</a>
</div> </div>
</li> <div class="col-date"
<li th:each="notice, status : ${portalNotices}"> th:text="${#temporals.format(notice.createdDate, 'yyyy.MM.dd')}">2025.01.01</div>
<div> </div>
<span class="col-num" th:text="${page.totalElements - (page.number * page.size) - status.index}"></span> </div>
<span class="col-title icon_file">
<em>
<a th:href="@{/portalnotice/detail(id=${notice.id})}">
<span th:text="${notice.noticeSubject}"></span>
<span class="icon_file" th:if="${notice.fileId != null and !notice.fileId.isEmpty()}">
<img th:src="@{/img/icon/icon_file.png}" alt="첨부파일">
</span>
</a>
</em>
</span>
<span class="col-date" th:text="${#temporals.format(notice.createdDate, 'yyyy.MM.dd')}"></span>
</div>
</li>
<li class="no-result-wrap" th:if="${portalNotices.isEmpty()}">
<div class="no_data">
<div class="no_result_text">
<div class="no_result_text m-only">
<img th:src="@{/img/img_nodata.png}" alt="">
<p>조회된 데이터가 없습니다.</p>
</div>
<div class="no_result_text pc-only">
<img th:src="@{/img/img_nodata2.png}" alt="">
<p>조회된 데이터가 없습니다.</p>
</div>
</div>
</div>
</li>
</ul>
</div> </div>
<!-- pagination --> <!-- Empty State -->
<div class="table-empty" th:if="${portalNotices.isEmpty()}">
<div class="empty-icon">
<img th:src="@{/img/img_nodata.png}" alt="데이터 없음">
</div>
<p class="empty-text">조회된 데이터가 없습니다.</p>
</div>
<!-- Pagination -->
<div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}"> <div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}">
</div> </div>
<!-- // pagination -->
</div> </div>
</div> </div>
</section> </th:block>
</body> </body>
@@ -59,9 +59,6 @@
<span class="user-message">즐거운 하루 되세요!</span> <span class="user-message">즐거운 하루 되세요!</span>
</div> </div>
<ul class="profile-menu-list"> <ul class="profile-menu-list">
<li sec:authorize="hasRole('ROLE_DASHBOARD')">
<a th:href="@{/dashboard}"><i class="fas fa-chart-line"></i>대시보드</a>
</li>
<li sec:authorize="hasRole('ROLE_USER_MANAGER')"> <li sec:authorize="hasRole('ROLE_USER_MANAGER')">
<a th:href="@{/users}"><i class="fas fa-users"></i>이용자 관리</a> <a th:href="@{/users}"><i class="fas fa-users"></i>이용자 관리</a>
</li> </li>
@@ -150,11 +147,6 @@
<li sec:authorize="isAuthenticated()"> <li sec:authorize="isAuthenticated()">
<a href="#community" class="drawer-link">MY</a> <a href="#community" class="drawer-link">MY</a>
</li> </li>
<li sec:authorize="hasRole('ROLE_DASHBOARD')">
<a th:href="@{/dashboard}" class="drawer-link">
<i class="fas fa-chart-line"></i> 대시보드
</a>
</li>
<li sec:authorize="hasRole('ROLE_USER_MANAGER')"> <li sec:authorize="hasRole('ROLE_USER_MANAGER')">
<a th:href="@{/users}" class="drawer-link"> <a th:href="@{/users}" class="drawer-link">
<i class="fas fa-users"></i> 이용자 관리 <i class="fas fa-users"></i> 이용자 관리