2026
@@ -224,28 +233,25 @@
if (!grid) return;
const cards = Array.from(grid.querySelectorAll('.news-card'));
- const itemsPerPage = parseInt(grid.getAttribute('data-items-per-page')) || 9;
+ const itemsPerPage = Math.min(parseInt(grid.getAttribute('data-items-per-page')) || 12, 12);
let currentPage = 1;
let activeFilter = 'ALL';
- // Render Tabs from posts.js multi-choice selection or extracted cards
if (tabsContainer) {
const rawConfigured = tabsContainer.getAttribute('data-configured-tabs') || '';
let tabNames = rawConfigured.split(',').map(function (s) { return s.trim(); }).filter(Boolean);
- // Fallback to extracting from cards if no specific multi-choice selection was made
if (tabNames.length === 0) {
const foundSet = new Set();
cards.forEach(function (card) {
const cat = card.getAttribute('data-category');
- if (cat) foundSet.add(cat.trim());
+ if (cat && cat.trim()) foundSet.add(cat.trim());
const tags = (card.getAttribute('data-tags') || '').split(',');
tags.forEach(function (t) { if (t.trim()) foundSet.add(t.trim()); });
});
tabNames = Array.from(foundSet);
}
- // Build tab button elements
tabNames.forEach(function (name) {
const btn = document.createElement('button');
btn.type = 'button';
@@ -255,7 +261,6 @@
tabsContainer.appendChild(btn);
});
- // Single active tab switching
tabsContainer.addEventListener('click', function (e) {
const btn = e.target.closest('.tab-btn');
if (!btn) return;
@@ -274,29 +279,40 @@
});
}
- // Render card visibility based on active tab & calculate pagination
function render() {
let visibleCards = cards.filter(function (card) {
if (activeFilter === 'ALL') return true;
- const cardCat = (card.getAttribute('data-category') || '').trim();
- const cardTags = (card.getAttribute('data-tags') || '').split(',').map(function (s) { return s.trim(); });
- return cardCat === activeFilter || cardTags.includes(activeFilter);
+ const targetFilter = activeFilter.trim().toLowerCase();
+ const cardCat = (card.getAttribute('data-category') || '').trim().toLowerCase();
+ const cardTags = (card.getAttribute('data-tags') || '').split(',').map(function (s) { return s.trim().toLowerCase(); });
+ return cardCat === targetFilter || cardTags.includes(targetFilter);
});
const totalPages = Math.ceil(visibleCards.length / itemsPerPage) || 1;
if (currentPage > totalPages) currentPage = totalPages;
- // Hide all
cards.forEach(function (c) { c.style.display = 'none'; });
- // Show current page slice
+ let noPostsMsg = grid.querySelector('.no-posts-found');
+ if (visibleCards.length === 0) {
+ if (!noPostsMsg) {
+ noPostsMsg = document.createElement('div');
+ noPostsMsg.className = 'no-posts-found';
+ noPostsMsg.style.cssText = 'grid-column: 1 / -1; text-align: center; color: #64748b; padding: 40px 20px; font-size: 15px; background: #f8fafc; border-radius: 8px; border: 1px dashed #cbd5e1; margin: 20px 0;';
+ noPostsMsg.textContent = 'Chưa có bài viết nào thuộc danh mục/thẻ này.';
+ grid.appendChild(noPostsMsg);
+ }
+ noPostsMsg.style.display = 'block';
+ } else {
+ if (noPostsMsg) noPostsMsg.style.display = 'none';
+ }
+
const startIdx = (currentPage - 1) * itemsPerPage;
const endIdx = startIdx + itemsPerPage;
visibleCards.slice(startIdx, endIdx).forEach(function (c) {
c.style.display = 'flex';
});
- // Render Pagination
if (pagination) {
pagination.innerHTML = '';
if (totalPages <= 1) return;
diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/config/SecurityConfiguration.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/config/SecurityConfiguration.java
index a338ab6e..bc5bf950 100644
--- a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/config/SecurityConfiguration.java
+++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/config/SecurityConfiguration.java
@@ -62,7 +62,7 @@ public class SecurityConfiguration {
authz
.requestMatchers(HttpMethod.GET, "/", "/about", "/flex-finish", "/tin-tuc", "/tin-tuc/**", "/lien-he",
"/manage/login", "/css/**", "/images/**", "/js/**", "/vendor/**", "/fonts/**", "/login-assets/**", "/UMass*/**", "/Undergraduate*/**",
- "/favicon.ico", "/favicons/**", "/flex-finish/**", "/contact-us/**", "/uploads/**", "/upload/**", "/api/manage/snippets/**", "/api/categories", "/api/tags", "/page/**", "/pages/**", "/news/article/**", "/post/**", "/error",
+ "/favicon.ico", "/favicons/**", "/flex-finish/**", "/contact-us/**", "/uploads/**", "/upload/**", "/api/manage/snippets/**", "/api/categories", "/api/tags", "/page/**", "/pages/**", "/news/article/**", "/post/**", "/posts", "/posts/**", "/error",
"/about-us", "/specialty", "/doctor", "/bac-si/**", "/service", "/health-library", "/news-and-events", "/patient-support", "/medical-expert", "/umcers", "/bidding", "/contact-us", "/theme-assets/**", "/dao-tao/**", "/chuyen-khoa", "/chuyen-khoa/**",
"/Đào tạo tại UMC_files/**", "/dat-lich", "/dat-lich/**", "/api/public/doctor-schedule/**")
.permitAll()
diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/Post.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/Post.java
index 06e5bd33..daa0cfbb 100644
--- a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/Post.java
+++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/Post.java
@@ -2,7 +2,9 @@ package com.sisvietnamvn.web.domain;
import java.io.Serial;
import java.util.HashSet;
+import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -101,6 +103,20 @@ public class Post extends AbstractAuditingEntity
{
)
private Set tags = new HashSet<>();
+ public String getCategoryName() {
+ return category != null ? category.getName() : "";
+ }
+
+ public String getTagNamesString() {
+ if (tags == null || tags.isEmpty()) {
+ return "";
+ }
+ return tags.stream()
+ .map(Tag::getName)
+ .filter(Objects::nonNull)
+ .collect(Collectors.joining(","));
+ }
+
// --- Getters and Setters ---
@Override
diff --git a/sisvietnamvn_main/src/main/resources/static/js/manage/editor-plugins/posts.js b/sisvietnamvn_main/src/main/resources/static/js/manage/editor-plugins/posts.js
index 0f71dad5..2874d4da 100644
--- a/sisvietnamvn_main/src/main/resources/static/js/manage/editor-plugins/posts.js
+++ b/sisvietnamvn_main/src/main/resources/static/js/manage/editor-plugins/posts.js
@@ -29,7 +29,7 @@ class SISPostsTool {
statusFilter: data && data.statusFilter ? data.statusFilter : 'PUBLISHED',
displayStyle: data && data.displayStyle ? data.displayStyle : 'grid', // 'grid', 'list', 'cards', 'compact'
cols: parseInt(data && data.cols) || 3,
- limit: parseInt(data && data.limit) || 6,
+ limit: parseInt(data && data.limit) || 12,
orderBy: data && data.orderBy ? data.orderBy : 'createdDate_desc',
locationFilter: data && data.locationFilter ? data.locationFilter : '',
postIds: data && data.postIds ? data.postIds : '', // Optional manually comma-separated IDs
@@ -37,7 +37,7 @@ class SISPostsTool {
showExcerpt: data && data.showExcerpt !== undefined ? !!data.showExcerpt : true,
showDate: data && data.showDate !== undefined ? !!data.showDate : true,
showLocation: data && data.showLocation !== undefined ? !!data.showLocation : true,
- itemsPerPage: parseInt(data && data.itemsPerPage) || parseInt(data && data.limit) || 9,
+ itemsPerPage: parseInt(data && data.itemsPerPage) || parseInt(data && data.limit) || 12,
enablePagination: data && data.enablePagination !== undefined ? !!data.enablePagination : true,
enableTabs: data && data.enableTabs !== undefined ? !!data.enableTabs : true,
globalId: data && data.globalId ? data.globalId : '',
@@ -114,7 +114,7 @@ class SISPostsTool {
// 3. Limit (Number of posts)
const limitDiv = document.createElement('div');
limitDiv.className = 'col-md-2 mb-2';
- limitDiv.innerHTML = '';
+ limitDiv.innerHTML = '';
const limitInput = document.createElement('input');
limitInput.type = 'number';
limitInput.className = 'form-control form-control-sm';
@@ -122,7 +122,11 @@ class SISPostsTool {
limitInput.min = 1;
limitInput.max = 50;
if (this.readOnly) limitInput.disabled = true;
- limitInput.addEventListener('input', (e) => this.data.limit = parseInt(e.target.value) || 6);
+ limitInput.addEventListener('input', (e) => {
+ const val = parseInt(e.target.value) || 12;
+ this.data.limit = val;
+ this.data.itemsPerPage = val;
+ });
limitDiv.appendChild(limitInput);
formRow1.appendChild(limitDiv);
diff --git a/sisvietnamvn_main/src/main/resources/templates/page.html b/sisvietnamvn_main/src/main/resources/templates/page.html
index ea242ae4..09f47f60 100644
--- a/sisvietnamvn_main/src/main/resources/templates/page.html
+++ b/sisvietnamvn_main/src/main/resources/templates/page.html
@@ -621,12 +621,21 @@
-