커뮤니티 - 스타일 변경

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/password-popup';
@import 'components/header-auth';
@import 'components/tables';
@import 'components/accordion';
// 5. Page-specific styles
@import 'pages/index';
@@ -42,6 +44,8 @@
@import 'pages/mypage';
@import 'pages/apikey-register';
@import 'pages/apikey-detail';
@import 'pages/notice';
@import 'pages/inquiry';
// 6. Themes
@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>
<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>
<section class="content" layout:fragment="contentFragment">
<div class="content_wrap">
<div class="sub_title3">
<h2 class="title">FAQ</h2>
</div>
<div class="inner i_cs h_inner8">
<!-- 검색영역 -->
<th:block layout:fragment="contentFragment">
<!-- FAQ Page -->
<div class="faq-page">
<div class="faq-container">
<!-- Page Header -->
<div class="faq-header">
<h1 class="page-title">FAQ</h1>
<p class="page-description">자주 묻는 질문과 답변을 확인하세요.</p>
</div>
<!-- Search and Filter Controls -->
<form name="articleForm" th:action="@{/faq_list}" method="post" th:object="${search}" onSubmit="fn_search_article(); return false;">
<div class="board_search">
<div class="total-post-wrap">
<span class="total-post pc-only">
<strong th:text="${page.totalElements}"></strong>
</span>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input name="page" type="hidden" value="">
<div class="table-controls">
<div class="total-count">
<strong th:text="${page.totalElements}">0</strong>
</div>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input name="page" type="hidden" value="">
<div class="search_txt ">
<input type="text" class="common_input_type_1 h_inp2" th:field="*{searchWrd}" placeholder="제목/내용 검색">
</div>
<div class="btn_wrap search_btn">
<button type="button" class="btn_search" onclick="fn_search_article()">검색</button>
<i class="i_search"></i>
<div class="search-box">
<input type="text"
th:field="*{searchWrd}"
placeholder="질문 또는 답변 내용으로 검색">
<button type="button" onclick="fn_search_article()">
<i class="fas fa-search"></i>
<span>검색</span>
</button>
</div>
</div>
</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">
<div class="box-brandInfo gapType-1 faq">
<ul>
<li th:each="faq : ${page.content}">
<div class="bd_txt">
<a href="#none" th:text="${faq.faqQuestion}"></a>
</div>
<div class="box-ct">
<div id="faqAnswer" class="list_txt" th:utext="${faq.faqAnswer}"></div>
</div>
</li>
<li class="no-result-wrap" th:if="${#lists.isEmpty(page.content)}">
<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>
<!-- FAQ Accordion -->
<div class="faq-accordion" th:if="${!#lists.isEmpty(page.content)}">
<div class="faq-item" th:each="faq : ${page.content}">
<div class="faq-question">
<div class="faq-question-text" th:text="${faq.faqQuestion}">
질문 내용이 여기에 표시됩니다.
</div>
<i class="faq-icon fas fa-chevron-down"></i>
</div>
<div class="faq-answer">
<div class="faq-answer-content" th:utext="${faq.faqAnswer}">
답변 내용이 여기에 표시됩니다.
</div>
</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>
<!-- // pagination -->
</div>
</div>
</section>
</th:block>
</body>
<th:block layout:fragment="contentScript">
<script>
function fn_init() {
document.inquiryForm.searchWrd.focus();
}
function fn_select_page(pageNo) {
document.articleForm.page.value = pageNo;
document.articleForm.action = '[[@{/faq_list}]]';
document.articleForm.submit();
}
/*********************************************************
* 조회 처리 함수
******************************************************** */
function fn_search_article() {
document.articleForm.page.value = 1;
document.articleForm.submit();
}
function decodeHTMLEntities(text) {
var textArea = document.createElement('textarea');
textArea.innerHTML = text;
return textArea.value;
var textArea = document.createElement('textarea');
textArea.innerHTML = text;
return textArea.value;
}
$(function() {
fn_init();
document.addEventListener('DOMContentLoaded', function() {
// Decode HTML entities in FAQ answers
document.querySelectorAll('.faq-answer-content').forEach(function(element) {
element.innerHTML = decodeHTMLEntities(element.innerHTML);
});
// FAQ 아코디언 기능 추가
$('.box-brandInfo.faq .bd_txt a').click(function(e) {
e.preventDefault();
$(this).closest('li').toggleClass('sel');
$(this).closest('li').find('.box-ct').slideToggle();
// FAQ Accordion functionality
document.querySelectorAll('.faq-question').forEach(function(question) {
question.addEventListener('click', function(e) {
e.preventDefault();
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>
</th:block>
@@ -1,72 +1,110 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kbank_base_layout}">
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
<body>
<section layout:fragment="contentFragment" class="content">
<th:block layout:fragment="contentFragment">
<div class="content_wrap">
<div class="sub_title3">
<h2 class="title">Q&amp;A</h2>
</div>
<div class="inner i_cs h_inner8 h_inner10">
<!-- Inquiry Detail Page -->
<div class="inquiry-page">
<div class="inquiry-container">
<div class="board-view">
<div class="board-view-head qna">
<div class="complete_box">
<span th:class="${inquiry.inquiryStatus == 'RESPONDED' ? 'complete' : 'question'}" th:text="${inquiry.inquiryStatus =='RESPONDED'?'답변완료': '문의중'}"></span>
</div>
<div class="detail_title">
<strong class="title" th:text="${inquiry.inquirySubject}"></strong>
</div>
<div class="write-info">
<p class="date-info">
<span class="date_stxt">등록일 : <em>[[${#temporals.format(inquiry.createdDate, 'yyyy-MM-dd HH:mm')}]]</em></span>
<span>작성자 : [[${inquiry.maskedInquirerName}]]</span>
</p>
</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>
<!-- Page Header -->
<div class="inquiry-header">
<h1 class="page-title">Q&A</h1>
<p class="page-description">문의 내용을 확인하세요.</p>
</div>
<!-- Inquiry Detail Card -->
<div class="inquiry-detail-card">
<div class="detail-header">
<div class="status-wrapper">
<span class="status-badge-header"
th:classappend="${inquiry.inquiryStatus == 'RESPONDED' ? 'status-completed' : 'status-pending'}"
th:text="${inquiry.inquiryStatus == 'RESPONDED' ? '답변완료' : '문의중'}">
문의중
</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 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>
</div>
</div>
<div class="comment_wrap" th:if="${inquiry.inquiryStatus == 'RESPONDED'}">
<div class="comment_wrap_head">
<strong class="title">
[[${inquiry.responderName}]] <em>(등록일 : [[${#temporals.format(inquiry.responseDate, 'yyyy-MM-dd HH:mm')}]])</em>
</strong>
<div class="m-only">
<th:block class="responseDetail" th:utext="${inquiry.responseDetail}"></th:block>
<!-- Admin Response Card -->
<div class="inquiry-response-card" th:if="${inquiry.inquiryStatus == 'RESPONDED'}">
<div class="response-header">
<div class="response-title">
<i class="fas fa-comment-dots"></i>
<span>관리자 답변</span>
</div>
<div class="pc-only">
<th:block class="responseDetail pc-only" th:utext="${inquiry.responseDetail}"></th:block>
<div class="response-meta">
<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 class="response-content">
<th:block class="responseDetail" th:utext="${inquiry.responseDetail}"></th:block>
</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>
</section>
</th:block>
</body>
<th:block layout:fragment="contentScript">
<script th:inline="javascript">
@@ -1,156 +1,189 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kbank_base_layout}">
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
<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">
<div class="sub_title3">
<h2 class="title" th:text="${isNew ? 'Q&amp;A 질문하기' : 'Q&amp;A 질문 수정'}"></h2>
</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>
<!-- Inquiry Form Page -->
<div class="inquiry-page">
<div class="inquiry-container">
<div class="info1">
<p class="title w_tit">
<span class="dot">내용</span>
</p>
<div class="info_line info_add">
<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>
<!-- Page Header -->
<div class="inquiry-header">
<h1 class="page-title" th:text="${isNew ? 'Q&A 질문하기' : 'Q&A 질문 수정'}">Q&A 질문하기</h1>
<p class="page-description">궁금하신 사항을 작성해 주세요.</p>
</div>
<div class="info1 pt28">
<p class="title tit_add w_tit"> 첨부파일 </p>
<div class="info_line info_add">
<div class="info_box1 info_add2">
<!-- Inquiry Form Card -->
<div class="inquiry-form-card">
<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="filebox">
<input type="text" class="upload-name before"
th:class="${!#strings.isEmpty(inquiry.attachFile)} ? 'upload-name i_trash after' : 'upload-name before'"
th:value="${!#strings.isEmpty(inquiry.attachFile)} ? ${@fileService.findById(inquiry.attachFile).fileDetails[0].originalFileName + '.' + @fileService.findById(inquiry.attachFile).fileDetails[0].fileExtension}"
readonly="readonly">
<input type="hidden" th:field="*{attachFile}">
</div>
<div class="btn_check">
<div class="file_btn">
<label for="file">파일선택</label>
<input type="file" id="file" name="files"
accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.hwp,.gif,.jpg,.png">
</div>
</div>
</div>
<!-- Subject Field -->
<div class="form-group">
<label for="inquirySubject" class="form-label required">
<span>제목</span>
</label>
<div class="form-input-wrapper">
<input type="text"
id="inquirySubject"
th:field="*{inquirySubject}"
class="form-input"
placeholder="제목을 입력하세요"
required>
</div>
</div>
<div class="list-dot">
<p>첨부파일은 10MB 이내로 등록해 주세요.</p>
<p>문서파일과 이미지 파일만 등록 가능합니다. (pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg,
png)</p>
</div>
</div>
</div>
<!-- Content Field -->
<div class="form-group">
<label for="inquiryDetail" class="form-label required">
<span>내용</span>
</label>
<div class="form-input-wrapper">
<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>
</section>
</div>
</th:block>
</body>
<th:block layout:fragment="contentScript">
<script th:if="${error}" th:inline="javascript">
$(document).ready(function () {
const errorMsg = [[${error}]];
const formattedMsg = errorMsg.replace(/\n/g,'<br>');
$('#customAlertMessage').html(formattedMsg);
$('#customAlert').css('display','flex');
});
</script>
<script th:inline="javascript">
$(function () {
let fnRegister = document.querySelector('.btn_register');
<script th:if="${error}" th:inline="javascript">
$(document).ready(function () {
const errorMsg = [[${error}]];
const formattedMsg = errorMsg.replace(/\n/g,'<br>');
$('#customAlertMessage').html(formattedMsg);
$('#customAlert').css('display','flex');
});
</script>
<script th:inline="javascript">
$(function () {
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 inquirySubject = document.getElementById('inquirySubject').value.trim();
let inquiryDetail = document.getElementById('inquiryDetail').value;
let form = document.getElementById('inquiryForm');
let inquirySubject = document.getElementById('inquirySubject').value.trim();
let inquiryDetail = document.getElementById('inquiryDetail').value;
// validation 체크
if (!inquirySubject) {
customPopups.showAlert('제목을 작성해 주세요.');
document.getElementById('inquirySubject').focus();
return;
}
if (!inquiryDetail.trim()) {
customPopups.showAlert('내용을 작성해 주세요.');
document.getElementById('inquiryDetail').focus();
return false;
}
// Validation
if (!inquirySubject) {
customPopups.showAlert('제목을 작성해 주세요.');
document.getElementById('inquirySubject').focus();
return;
}
if (!inquiryDetail.trim()) {
customPopups.showAlert('내용을 작성해 주세요.');
document.getElementById('inquiryDetail').focus();
return false;
}
form.submit();
});
form.submit();
});
document.getElementById('inquiryForm').inquirySubject.focus();
const fileInput = document.getElementById('file');
const fileNameInput = document.querySelector('.upload-name');
// File input change handler
fileInput.addEventListener('change', function () {
const file = fileInput.files[0];
fileInput.addEventListener('change', function () {
const file = fileInput.files[0];
if (file) {
const allowedExtensions = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.hwp', '.gif', '.jpg', '.jpeg', '.png'];
const fileExt = '.' + file.name.split('.').pop().toLowerCase();
if (file) {
const allowedExtensions = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.hwp', '.gif', '.jpg', '.jpeg', '.png'];
const fileExt = '.' + file.name.split('.').pop().toLowerCase();
if (!allowedExtensions.includes(fileExt)) {
customPopups.showAlert("허용된 파일 형식이 아닙니다.\n문서파일(pdf, doc, docx, xls, xlsx, ppt, pptx, hwp)과 이미지 파일(gif, jpg, png)만 등록 가능합니다.");
fileInput.value = '';
fileNameDisplay.value = '';
fileNameDisplay.classList.remove('has-file');
fileRemoveBtn.style.display = 'none';
return;
}
if (!allowedExtensions.includes(fileExt)) {
customPopups.showAlert("허용된 파일 형식이 아닙니다.\n문서파일(pdf, doc, docx, xls, xlsx, ppt, pptx, hwp)과 이미지 파일(gif, jpg, png)만 등록 가능합니다.");
fileInput.value = '';
fileNameInput.value = '';
fileNameInput.classList.remove('i_trash', 'after');
fileNameInput.classList.add('before');
return;
}
fileNameDisplay.value = file.name;
fileNameDisplay.classList.add('has-file');
fileRemoveBtn.style.display = 'flex';
}
});
fileNameInput.value = file.name; // Set the file name
fileNameInput.classList.remove('before'); // Remove the 'before' class
fileNameInput.classList.add('i_trash', 'after'); // Add 'i_trash' and 'after' classes
}
});
// File remove handler
fileRemoveBtn.addEventListener('click', function () {
fileInput.value = '';
fileNameDisplay.value = '';
fileNameDisplay.classList.remove('has-file');
fileRemoveBtn.style.display = 'none';
});
// Handle file removal when upload-name is clicked
fileNameInput.addEventListener('click', function () {
if (fileNameInput.classList.contains('i_trash')) {
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
fileNameInput.classList.add('before'); // Add the 'before' class back
}
});
});
</script>
// Show remove button if file already exists
if (fileNameDisplay.value) {
fileRemoveBtn.style.display = 'flex';
}
// Focus on subject field
document.getElementById('inquirySubject').focus();
});
</script>
</th:block>
</html>
@@ -1,101 +1,106 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kbank_base_layout}">
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
<body>
<section class="content" layout:fragment="contentFragment">
<div class="content_wrap">
<div class="sub_title3">
<h2 class="title">Q&amp;A</h2>
</div>
<div class="inner i_cs h_inner8">
<th:block layout:fragment="contentFragment">
<!-- Inquiry List Page -->
<div class="inquiry-page">
<div class="inquiry-container">
<!-- 검색영역 -->
<!-- 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;">
<div class="board_search">
<div class="total-post-wrap">
<span class="total-post pc-only">
<strong th:text="${page.totalElements}"></strong>
</span>
<input type="hidden" name="page" value="1">
<input name="searchCnd" type="hidden" value="0">
<div class="table-controls">
<div class="total-count">
<strong th:text="${page.totalElements}">0</strong>
</div>
<input type="hidden" name="page" value="1">
<input name="searchCnd" type="hidden" value="0">
<div class="search_txt ">
<input type="text" class="common_input_type_1 h_inp2" name="searchWrd"
th:value="${search.searchWrd}" placeholder="제목/내용 검색">
</div>
<div class="btn_wrap search_btn">
<button type="button" class="btn_search" onclick="fn_search_inquiry()">검색</button>
<i class="i_search"></i>
<div class="search-box">
<input type="text"
name="searchWrd"
th:value="${search.searchWrd}"
placeholder="제목 또는 내용으로 검색">
<button type="button" onclick="fn_search_inquiry()">
<i class="fas fa-search"></i>
<span>검색</span>
</button>
</div>
</div>
</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">
<ul class="board-list qna_list">
<li class="th-head">
<div>
<span class="col-num">No</span>
<span class="col-title">제목</span>
<span class="col-name">작성자</span>
<span class="col-comp">처리상태</span>
<span class="col-date">등록일</span>
</div>
</li>
<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>
<!-- Inquiry Table -->
<div class="inquiry-list-wrapper" th:if="${!inquiries.isEmpty()}">
<div class="inquiry-table">
<!-- Table Header -->
<div class="table-header">
<div class="col-num">NO</div>
<div class="col-title">제목</div>
<div class="col-name">작성자</div>
<div class="col-status">처리상태</div>
<div class="col-date">등록일</div>
</div>
<div class="pg_btn">
<!-- pagination -->
<div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}">
</div>
<!-- // pagination -->
<div class="btn_board btn_top">
<a th:href="@{/inquiry/new}" class="common_btn_type_1 btn_qna">질문하기</a>
<!-- Table Rows -->
<div class="table-row"
th:each="inquiry, status : ${inquiries}"
th:onclick="'location.href=\'' + @{/inquiry/detail(id=${inquiry.id})} + '\''">
<div class="col-num"
th:text="${page.totalElements - (page.number * page.size) - status.index}">1</div>
<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>
<!-- 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>
</section>
</th:block>
</body>
@@ -1,42 +1,64 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kbank_base_layout}">
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
<body>
<section class="content" layout:fragment="contentFragment">
<div class="content_wrap">
<div class="sub_title3">
<h2 class="title">공지사항</h2>
</div>
<div class="inner i_cs h_inner8">
<div class="board-view">
<div class="board-view-head">
<strong class="title" th:text="${portalNotice.noticeSubject}"></strong>
<div class="write-info">
<p class="date-info">
<span>등록일 <em th:text="${#temporals.format(portalNotice.createdDate, 'yyyy.MM.dd')}"></em></span>
</p>
<th:block layout:fragment="contentFragment">
<!-- Notice Detail Page -->
<div class="notice-detail-page">
<div class="notice-detail-container">
<!-- Page Header -->
<div class="notice-header">
<h1 class="page-title">공지사항</h1>
</div>
<!-- Notice Detail Card -->
<div class="notice-detail-card">
<!-- 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 class="board_file" th:if="${!#strings.isEmpty(portalNotice.fileId)}">
<p class="file_group" th:with="fileInfo=${@fileService.findById(portalNotice.fileId)}">
<span th:each="fileDetail, status : ${fileInfo.getFileDetails()}">
<a th:href="'javascript:fn_downloadFile(\'' + ${fileDetail.fileId} + '\',\''+ ${fileDetail.fileSn} +'\')'">첨부파일 : [[${fileDetail.originalFileName}]].[[${fileDetail.fileExtension}]]</a>
</span>
</p>
<!-- Attachments Section -->
<div class="detail-attachments" th:if="${!#strings.isEmpty(portalNotice.fileId)}">
<div class="attachment-list" th:with="fileInfo=${@fileService.findById(portalNotice.fileId)}">
<div class="attachment-item" th:each="fileDetail, status : ${fileInfo.getFileDetails()}">
<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 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 class="btn_board btn_top">
<a href="#" class="common_btn_type_1" th:onclick="|window.location.href='@{/portalnotice}';|">목록</a>
<!-- Action Buttons -->
<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>
</section>
</th:block>
</body>
@@ -1,90 +1,86 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kbank_base_layout}">
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}">
<body>
<section class="content" layout:fragment="contentFragment">
<div class="content_wrap">
<div class="sub_title3">
<h2 class="title">공지사항</h2>
</div>
<div class="inner i_cs4 h_inner8 h_inner10">
<th:block layout:fragment="contentFragment">
<!-- Notice List Page -->
<div class="notice-page">
<div class="notice-container">
<!-- 검색영역 -->
<!-- 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;">
<div class="board_search">
<div class="total-post-wrap">
<span class="total-post pc-only">
<strong th:text="${page.totalElements}"></strong>
</span>
<input type="hidden" name="page" value="1">
<div class="table-controls">
<div class="total-count">
<strong th:text="${page.totalElements}">0</strong>
</div>
<input type="hidden" name="page" value="1">
<div class="search_txt">
<input type="text" class="common_input_type_1 h_inp2" name="searchSubjectOrDetail" th:value="${search.searchSubjectOrDetail}" placeholder="제목/내용 검색">
</div>
<div class="btn_wrap search_btn">
<button type="button" class="btn_search" onclick="fn_search_notice()">검색</button>
<i class="i_search"></i>
<div class="search-box">
<input type="text"
name="searchSubjectOrDetail"
th:value="${search.searchSubjectOrDetail}"
placeholder="제목 또는 내용으로 검색">
<button type="button" onclick="fn_search_notice()">
<i class="fas fa-search"></i>
<span>검색</span>
</button>
</div>
</div>
</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">
<ul class="board-list notice-type">
<li class="th-head">
<div>
<span class="col-num">NO</span>
<span class="col-title">제목</span>
<span class="col-date">등록일</span>
<!-- Notice Table -->
<div class="notice-list-wrapper" th:if="${!portalNotices.isEmpty()}">
<div class="notice-table">
<!-- Table Header -->
<div class="table-header">
<div class="col-num">NO</div>
<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>
</li>
<li th:each="notice, status : ${portalNotices}">
<div>
<span class="col-num" th:text="${page.totalElements - (page.number * page.size) - status.index}"></span>
<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 class="col-date"
th:text="${#temporals.format(notice.createdDate, 'yyyy.MM.dd')}">2025.01.01</div>
</div>
</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>
<!-- // pagination -->
</div>
</div>
</section>
</th:block>
</body>
@@ -59,9 +59,6 @@
<span class="user-message">즐거운 하루 되세요!</span>
</div>
<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')">
<a th:href="@{/users}"><i class="fas fa-users"></i>이용자 관리</a>
</li>
@@ -150,11 +147,6 @@
<li sec:authorize="isAuthenticated()">
<a href="#community" class="drawer-link">MY</a>
</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')">
<a th:href="@{/users}" class="drawer-link">
<i class="fas fa-users"></i> 이용자 관리