feat: implement automated Swiper slider seeding and integrate slider component into training details page
This commit is contained in:
@@ -1212,3 +1212,37 @@ figure.table table tr:hover {
|
||||
}
|
||||
}
|
||||
|
||||
.education {
|
||||
.flex {
|
||||
display: flex !important;
|
||||
}
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
.rounded-full {
|
||||
justify-content: center;
|
||||
background-color: var(--color-white);
|
||||
border-radius: 32px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
align-items: center;
|
||||
}
|
||||
.training-sidebar {
|
||||
.gap-3 {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.text-white {
|
||||
margin-bottom: 4px;
|
||||
|
||||
}
|
||||
.btn {
|
||||
padding: 10px 32px;
|
||||
background-color: var(--color-background) !important;
|
||||
border-radius: 0.5rem;
|
||||
text-align: center;
|
||||
a {
|
||||
text-decoration: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="vi" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{themes/__${activeTheme}__/layout(forceFullWidth=true, bodyClass='education path-frontpage')}">
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="vi"
|
||||
xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{themes/__${activeTheme}__/layout(forceFullWidth=true, bodyClass='education path-frontpage')}"
|
||||
>
|
||||
<head>
|
||||
<title th:text="${post.title}">Chi tiết đào tạo</title>
|
||||
<style>
|
||||
.overview-wrapper-shadow {
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
|
||||
box-shadow:
|
||||
0 1px 3px 0 rgba(0, 0, 0, 0.1),
|
||||
0 1px 2px -1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
@@ -138,7 +145,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<section class="bg-gray-50 training-section" style="padding-top: 1.5rem; padding-bottom: 1.5rem;">
|
||||
<section class="bg-gray-50 training-section" style="padding-top: 1.5rem; padding-bottom: 1.5rem">
|
||||
<div class="container mx-auto px-4 training-container">
|
||||
<div class="training-layout relative">
|
||||
<div class="overview-wrapper-shadow training-main">
|
||||
@@ -146,12 +153,12 @@
|
||||
<h1 class="display-7 text-primary-600 xl:mb-4 md:mb-3 mb-2" th:text="${post.title}">Tiêu đề Khóa học</h1>
|
||||
<!-- Ảnh đại diện -->
|
||||
<div class="img-container" th:if="${post.featuredImage != null and !#strings.isEmpty(post.featuredImage)}">
|
||||
<img th:src="${post.featuredImage}" th:alt="${post.title}" loading="lazy" decoding="async">
|
||||
<img th:src="${post.featuredImage}" th:alt="${post.title}" loading="lazy" decoding="async" />
|
||||
</div>
|
||||
<div class="prose prose-blog prose-table prose-content max-w-none" id="postContentParsed">
|
||||
<!-- Nội dung chi tiết sẽ được render tại đây -->
|
||||
</div>
|
||||
<div id="postContentRaw" style="display:none;" th:text="${post.content}"></div>
|
||||
<div id="postContentRaw" style="display: none" th:text="${post.content}"></div>
|
||||
</div>
|
||||
<!-- Sidebar info -->
|
||||
<div id="info" class="training-sidebar">
|
||||
@@ -167,136 +174,189 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Swiper CSS (Must be inside layout fragment to not be stripped by Thymeleaf) -->
|
||||
<link rel="stylesheet" th:href="@{/css/swiper-bundle.min.css}" />
|
||||
<style>
|
||||
.swiper-button-lock {
|
||||
display: none !important;
|
||||
}
|
||||
.swiper-button-disabled {
|
||||
opacity: 0.5;
|
||||
cursor: auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
[class*='btn-navigation-']:hover:not([disabled]):not(.swiper-button-disabled):not(.swiper-button-lock) {
|
||||
background-color: var(--color-primary-600);
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Related Courses (Chương trình khác) -->
|
||||
<section class="related-courses-section" style="background-color: #f0f9ff; padding: 3rem 0; margin-top: 2rem;">
|
||||
<section class="py-6 xl:py-12 md:py-8 bg-white">
|
||||
<div class="container mx-auto px-4">
|
||||
<h2 class="text-center mb-8 font-bold" style="font-size: 1.5rem; color: var(--color-primary-600);">Chương trình khác</h2>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem;">
|
||||
<!-- Card 1 -->
|
||||
<div style="background-color: white; border-radius: 0.5rem; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1); border: 1px solid #e5e7eb;">
|
||||
<div style="aspect-ratio: 16/9; background-color: #e2e8f0;"></div>
|
||||
<div style="padding: 1rem;">
|
||||
<h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; line-height: 1.4;">Chương trình thực hành 12 tháng Bác sĩ y khoa</h3>
|
||||
<p style="color: #6b7280; font-size: 0.875rem; margin-bottom: 1rem;">12/09/2026</p>
|
||||
<a href="#" style="display: block; width: 100%; text-align: center; background-color: var(--color-primary-600, #a82024); color: white; padding: 0.5rem; border-radius: 0.25rem; font-weight: 600; text-decoration: none;">42.400.000 VNĐ</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card 2 -->
|
||||
<div style="background-color: white; border-radius: 0.5rem; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1); border: 1px solid #e5e7eb;">
|
||||
<div style="aspect-ratio: 16/9; background-color: #e2e8f0;"></div>
|
||||
<div style="padding: 1rem;">
|
||||
<h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; line-height: 1.4;">Lớp Nghiệp vụ hộ lý trợ giúp chăm sóc</h3>
|
||||
<p style="color: #6b7280; font-size: 0.875rem; margin-bottom: 1rem;">01/04/2026</p>
|
||||
<a href="#" style="display: block; width: 100%; text-align: center; background-color: var(--color-primary-600, #a82024); color: white; padding: 0.5rem; border-radius: 0.25rem; font-weight: 600; text-decoration: none;">23.000.000 VNĐ</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card 3 -->
|
||||
<div style="background-color: white; border-radius: 0.5rem; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1); border: 1px solid #e5e7eb;">
|
||||
<div style="aspect-ratio: 16/9; background-color: #e2e8f0;"></div>
|
||||
<div style="padding: 1rem;">
|
||||
<h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; line-height: 1.4;">Thực hành cấp cứu và điều trị đột quỵ</h3>
|
||||
<p style="color: #6b7280; font-size: 0.875rem; margin-bottom: 1rem;">29/12/2025</p>
|
||||
<a href="#" style="display: block; width: 100%; text-align: center; background-color: var(--color-primary-600, #a82024); color: white; padding: 0.5rem; border-radius: 0.25rem; font-weight: 600; text-decoration: none;">32.500.000 VNĐ</a>
|
||||
<h2 class="display-7 text-primary-600 text-center xl:mb-8 md:mb-6 mb-4">Chương trình khác</h2>
|
||||
<div class="relative md:flex md:items-center">
|
||||
<button
|
||||
class="btn-navigation flex-shrink-0 lg:mr-4 mr-2 !relative z-10 md:size-[42px] size-[32px] items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group btn-navigation-chuong-trinh-khac-prev md:!flex hidden"
|
||||
>
|
||||
<svg
|
||||
class="lucide lucide-chevron-up size-4 -rotate-90"
|
||||
fill="none"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="m18 15-6-6-6 6"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<div
|
||||
class="swiper swiper-chuong-trinh-khac [&>.swiper-pagination]:!static [&>.swiper-pagination]:mt-1 lg:[&>.swiper-pagination]:!hidden w-full"
|
||||
>
|
||||
<div class="swiper-wrapper">
|
||||
<th:block th:utext="${hookManager.applyFilters('swiper_slider_items', '', 'chuong-trinh-khac')}"></th:block>
|
||||
</div>
|
||||
<div class="swiper-pagination"></div>
|
||||
</div>
|
||||
<button
|
||||
class="btn-navigation flex-shrink-0 lg:ml-4 ml-2 !relative z-10 md:size-[42px] size-[32px] items-center justify-center rounded-full bg-white shadow-sm border border-gray-100 hover:bg-primary-600 hover:text-white transition-colors cursor-pointer group btn-navigation-chuong-trinh-khac-next md:!flex hidden"
|
||||
>
|
||||
<svg
|
||||
class="lucide lucide-chevron-down size-4 -rotate-90"
|
||||
fill="none"
|
||||
height="24"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="m6 9 6 6 6-6"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- Content rendering and dynamic tabs parsing -->
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var rawData = document.getElementById("postContentRaw").textContent;
|
||||
var parsedContainer = document.getElementById("postContentParsed");
|
||||
|
||||
// Content is now HTML
|
||||
parsedContainer.innerHTML = rawData;
|
||||
|
||||
// Parse dynamic tabs from HTML (migrated from Editor.js)
|
||||
var tabStarts = Array.from(parsedContainer.querySelectorAll('h3.dynamic-tab-start'));
|
||||
if (tabStarts.length > 0) {
|
||||
var tabsGroup = [];
|
||||
var wrapper = document.createElement('div');
|
||||
wrapper.className = 'custom-tabs-wrapper training-course-tabs-wrapper';
|
||||
|
||||
var navContainer = document.createElement('div');
|
||||
navContainer.className = 'custom-tabs-nav-container';
|
||||
var tabsContainer = document.createElement('div');
|
||||
tabsContainer.className = 'tabs-container';
|
||||
navContainer.appendChild(tabsContainer);
|
||||
wrapper.appendChild(navContainer);
|
||||
|
||||
var firstTabStart = tabStarts[0];
|
||||
var parentNode = firstTabStart.parentNode;
|
||||
|
||||
// Collect all elements into tabs
|
||||
tabStarts.forEach(function(startEl, index) {
|
||||
var tabTitle = startEl.textContent;
|
||||
var btn = document.createElement('button');
|
||||
btn.className = 'dao-tao-tab-btn custom-tab-btn' + (index === 0 ? ' active' : '');
|
||||
btn.setAttribute('data-tab-idx', index);
|
||||
btn.textContent = tabTitle;
|
||||
tabsContainer.appendChild(btn);
|
||||
|
||||
var panel = document.createElement('div');
|
||||
panel.className = 'dao-tao-tab-panel custom-tab-panel';
|
||||
panel.setAttribute('data-tab-idx', index);
|
||||
panel.style.display = (index === 0) ? 'block' : 'none';
|
||||
|
||||
var currentEl = startEl.nextElementSibling;
|
||||
while (currentEl && !currentEl.classList.contains('dynamic-tab-start') && !currentEl.classList.contains('dynamic-tab-end')) {
|
||||
var nextEl = currentEl.nextElementSibling;
|
||||
panel.appendChild(currentEl);
|
||||
currentEl = nextEl;
|
||||
}
|
||||
|
||||
// Remove the dynamic-tab-end if present
|
||||
if (currentEl && currentEl.classList.contains('dynamic-tab-end')) {
|
||||
currentEl.parentNode.removeChild(currentEl);
|
||||
}
|
||||
|
||||
wrapper.appendChild(panel);
|
||||
startEl.parentNode.removeChild(startEl);
|
||||
});
|
||||
|
||||
parentNode.appendChild(wrapper);
|
||||
|
||||
// Tab click listener
|
||||
wrapper.addEventListener('click', function(e) {
|
||||
if (e.target.classList.contains('custom-tab-btn')) {
|
||||
var btn = e.target;
|
||||
var idx = btn.getAttribute('data-tab-idx');
|
||||
|
||||
wrapper.querySelectorAll('.custom-tab-btn').forEach(function(b) {
|
||||
b.classList.remove('active');
|
||||
});
|
||||
btn.classList.add('active');
|
||||
|
||||
wrapper.querySelectorAll('.custom-tab-panel').forEach(function(p) {
|
||||
p.style.display = 'none';
|
||||
});
|
||||
var activePanel = wrapper.querySelector('.custom-tab-panel[data-tab-idx="' + idx + '"]');
|
||||
if (activePanel) activePanel.style.display = 'block';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
<!-- Add Swiper JS -->
|
||||
<script th:src="@{/js/swiper-bundle.min.js}"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const el = document.querySelector('.swiper-chuong-trinh-khac');
|
||||
if (el && typeof Swiper !== 'undefined' && !el.classList.contains('swiper-initialized')) {
|
||||
new Swiper('.swiper-chuong-trinh-khac', {
|
||||
slidesPerView: 'auto',
|
||||
spaceBetween: 16,
|
||||
navigation: {
|
||||
nextEl: '.btn-navigation-chuong-trinh-khac-next',
|
||||
prevEl: '.btn-navigation-chuong-trinh-khac-prev',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<!-- Content rendering and dynamic tabs parsing -->
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var rawData = document.getElementById('postContentRaw').textContent;
|
||||
var parsedContainer = document.getElementById('postContentParsed');
|
||||
|
||||
// Content is now HTML
|
||||
parsedContainer.innerHTML = rawData;
|
||||
|
||||
// Parse dynamic tabs from HTML (migrated from Editor.js)
|
||||
var tabStarts = Array.from(parsedContainer.querySelectorAll('h3.dynamic-tab-start'));
|
||||
if (tabStarts.length > 0) {
|
||||
var tabsGroup = [];
|
||||
var wrapper = document.createElement('div');
|
||||
wrapper.className = 'custom-tabs-wrapper training-course-tabs-wrapper';
|
||||
|
||||
var navContainer = document.createElement('div');
|
||||
navContainer.className = 'custom-tabs-nav-container';
|
||||
var tabsContainer = document.createElement('div');
|
||||
tabsContainer.className = 'tabs-container';
|
||||
navContainer.appendChild(tabsContainer);
|
||||
wrapper.appendChild(navContainer);
|
||||
|
||||
var firstTabStart = tabStarts[0];
|
||||
var parentNode = firstTabStart.parentNode;
|
||||
|
||||
// Collect all elements into tabs
|
||||
tabStarts.forEach(function (startEl, index) {
|
||||
var tabTitle = startEl.textContent;
|
||||
var btn = document.createElement('button');
|
||||
btn.className = 'dao-tao-tab-btn custom-tab-btn' + (index === 0 ? ' active' : '');
|
||||
btn.setAttribute('data-tab-idx', index);
|
||||
btn.textContent = tabTitle;
|
||||
tabsContainer.appendChild(btn);
|
||||
|
||||
var panel = document.createElement('div');
|
||||
panel.className = 'dao-tao-tab-panel custom-tab-panel';
|
||||
panel.setAttribute('data-tab-idx', index);
|
||||
panel.style.display = index === 0 ? 'block' : 'none';
|
||||
|
||||
var currentEl = startEl.nextElementSibling;
|
||||
while (currentEl && !currentEl.classList.contains('dynamic-tab-start') && !currentEl.classList.contains('dynamic-tab-end')) {
|
||||
var nextEl = currentEl.nextElementSibling;
|
||||
panel.appendChild(currentEl);
|
||||
currentEl = nextEl;
|
||||
}
|
||||
|
||||
// Remove the dynamic-tab-end if present
|
||||
if (currentEl && currentEl.classList.contains('dynamic-tab-end')) {
|
||||
currentEl.parentNode.removeChild(currentEl);
|
||||
}
|
||||
|
||||
wrapper.appendChild(panel);
|
||||
startEl.parentNode.removeChild(startEl);
|
||||
});
|
||||
|
||||
parentNode.appendChild(wrapper);
|
||||
|
||||
// Tab click listener
|
||||
wrapper.addEventListener('click', function (e) {
|
||||
if (e.target.classList.contains('custom-tab-btn')) {
|
||||
var btn = e.target;
|
||||
var idx = btn.getAttribute('data-tab-idx');
|
||||
|
||||
wrapper.querySelectorAll('.custom-tab-btn').forEach(function (b) {
|
||||
b.classList.remove('active');
|
||||
});
|
||||
btn.classList.add('active');
|
||||
|
||||
wrapper.querySelectorAll('.custom-tab-panel').forEach(function (p) {
|
||||
p.style.display = 'none';
|
||||
});
|
||||
var activePanel = wrapper.querySelector('.custom-tab-panel[data-tab-idx="' + idx + '"]');
|
||||
if (activePanel) activePanel.style.display = 'block';
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
function switchTrainingTab(btn, tabId) {
|
||||
var container = btn.closest('.training-course-tabs-wrapper');
|
||||
if(!container) return;
|
||||
|
||||
if (!container) return;
|
||||
|
||||
var btns = container.querySelectorAll('.dao-tao-tab-btn');
|
||||
for(var i = 0; i < btns.length; i++) btns[i].classList.remove('active');
|
||||
for (var i = 0; i < btns.length; i++) btns[i].classList.remove('active');
|
||||
btn.classList.add('active');
|
||||
|
||||
|
||||
var panels = container.querySelectorAll('.dao-tao-tab-panel');
|
||||
for(var j = 0; j < panels.length; j++) panels[j].classList.remove('active');
|
||||
for (var j = 0; j < panels.length; j++) panels[j].classList.remove('active');
|
||||
var panel = container.querySelector('.dao-tao-tab-panel[data-tab-content="' + tabId + '"]');
|
||||
if(panel) panel.classList.add('active');
|
||||
if (panel) panel.classList.add('active');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,45 +1,98 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<body>
|
||||
<!-- Widget hiển thị thông tin động của khóa học (Ngày khai giảng, địa điểm, hình thức...) -->
|
||||
<div th:fragment="course-info" class="bg-primary-600 rounded-lg p-4 flex flex-col justify-center mb-4">
|
||||
<div class="xl:space-y-8 md:space-y-6 space-y-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<body>
|
||||
<!-- Widget hiển thị thông tin động của khóa học (Ngày khai giảng, địa điểm, hình thức...) -->
|
||||
<div th:fragment="course-info" class="bg-primary-600 rounded-lg p-4 flex flex-col justify-center mb-4" style="height: 100%">
|
||||
<div class="flex flex-col gap-3 xl:space-y-8 md:space-y-6 space-y-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-8 h-8 bg-white rounded-full flex items-center justify-center shrink-0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-calendar size-3.5 text-primary-600"><path d="M8 2v4"></path><path d="M16 2v4"></path><rect width="18" height="18" x="3" y="4" rx="2"></rect><path d="M3 10h18"></path></svg>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-calendar size-3.5 text-primary-600"
|
||||
>
|
||||
<path d="M8 2v4"></path>
|
||||
<path d="M16 2v4"></path>
|
||||
<rect width="18" height="18" x="3" y="4" rx="2"></rect>
|
||||
<path d="M3 10h18"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-white/80 text-sm">Ngày khai giảng</p>
|
||||
<p class="text-white font-semibold" th:text="${post.eventTime != null ? #temporals.format(post.eventTime, 'dd/MM/yyyy') : 'Đang cập nhật'}">Đang cập nhật</p>
|
||||
<p class="text-white text-sm">Ngày khai giảng</p>
|
||||
<p
|
||||
class="text-white font-semibold"
|
||||
th:text="${post.eventTime != null ? #temporals.format(post.eventTime, 'dd/MM/yyyy') : 'Đang cập nhật'}"
|
||||
>
|
||||
Đang cập nhật
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-8 h-8 bg-white rounded-full flex items-center justify-center shrink-0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin size-3.5 text-primary-600"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-map-pin size-3.5 text-primary-600"
|
||||
>
|
||||
<path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path>
|
||||
<circle cx="12" cy="10" r="3"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-white/80 text-sm">Địa điểm</p>
|
||||
<p class="text-white font-semibold" th:text="${post.location != null ? post.location : 'SIS Cần Thơ'}">SIS Cần Thơ</p>
|
||||
<p class="text-white text-sm">Địa điểm</p>
|
||||
<p class="text-white font-semibold" th:text="${post.location != null ? post.location : 'SIS Cần Thơ'}">SIS Cần Thơ</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-3">
|
||||
</div>
|
||||
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-8 h-8 bg-white rounded-full flex items-center justify-center shrink-0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-users size-3.5 text-primary-600"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M22 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-users size-3.5 text-primary-600"
|
||||
>
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="9" cy="7" r="4"></circle>
|
||||
<path d="M22 21v-2a4 4 0 0 0-3-3.87"></path>
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-white/80 text-sm">Hình thức</p>
|
||||
<p class="text-white font-semibold">Trực tiếp / Tập trung</p>
|
||||
<p class="text-white text-sm">Hình thức</p>
|
||||
<p class="text-white font-semibold">Trực tiếp / Tập trung</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-4 mt-4 border-t border-white/20">
|
||||
<a href="#" class="block w-full py-3 px-4 bg-white text-primary-600 text-center font-bold rounded-lg hover:bg-gray-100 transition-colors">
|
||||
Đăng ký ngay
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="pt-4 mt-4 border-t border-white/20">
|
||||
<div
|
||||
class="btn btn-primary bg-white block w-full py-3 px-4 bg-white text-primary-600 text-center font-bold rounded-lg hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<a href="#"> Đăng ký ngay </a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+6
-1
@@ -37,7 +37,11 @@
|
||||
<label>Slug (Dùng cho Shortcode)</label>
|
||||
<input type="text" class="form-control" onchange="updateGroup('slug', this.value)" id="groupSlug" placeholder="Ví dụ: chung-chi" required />
|
||||
</div>
|
||||
<div class="col-md-12 tab-field mt-3">
|
||||
<div class="col-md-6 tab-field mt-3">
|
||||
<label>Ghi chú (Hiển thị ở danh sách)</label>
|
||||
<input type="text" class="form-control" onchange="updateGroup('note', this.value)" id="groupNote" placeholder="Ví dụ: Dùng cho trang chủ" />
|
||||
</div>
|
||||
<div class="col-md-6 tab-field mt-3">
|
||||
<label>Mẫu Giao Diện (Layout Type)</label>
|
||||
<select class="form-control" onchange="updateGroup('layoutType', this.value); toggleCustomTemplates(this.value)" id="groupLayoutType">
|
||||
<option value="">Mặc định (Dựa theo Slug, hỗ trợ tương thích ngược)</option>
|
||||
@@ -85,6 +89,7 @@
|
||||
function renderGroup() {
|
||||
document.getElementById('groupName').value = group.name || '';
|
||||
document.getElementById('groupSlug').value = group.slug || '';
|
||||
document.getElementById('groupNote').value = group.note || '';
|
||||
document.getElementById('groupLayoutType').value = group.layoutType || '';
|
||||
document.getElementById('groupOuterTemplate').value = group.outerTemplate || '';
|
||||
document.getElementById('groupItemTemplate').value = group.itemTemplate || '';
|
||||
|
||||
+3
-1
@@ -26,6 +26,7 @@
|
||||
<tr>
|
||||
<th>Tên Nhóm</th>
|
||||
<th>Slug (Shortcode)</th>
|
||||
<th>Ghi chú</th>
|
||||
<th>Mẫu Giao Diện</th>
|
||||
<th>Số lượng Slide</th>
|
||||
<th>Hành động</th>
|
||||
@@ -35,6 +36,7 @@
|
||||
<tr th:each="group : ${groups}">
|
||||
<td th:text="${group.name}"></td>
|
||||
<td><code th:text="${group.slug}"></code></td>
|
||||
<td><em th:text="${group.note ?: ''}"></em></td>
|
||||
<td th:text="${group.layoutType == 'gallery' ? 'Thư viện ảnh' : (group.layoutType == 'card' ? 'Dạng thẻ' : (group.layoutType == 'large-image' ? 'Hình ảnh lớn' : (group.layoutType == 'custom' ? 'Tùy chỉnh' : 'Mặc định')))}"></td>
|
||||
<td th:text="${group.items != null ? group.items.size() : 0}"></td>
|
||||
<td>
|
||||
@@ -46,7 +48,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr th:if="${groups.empty}">
|
||||
<td colspan="5" class="text-center">Chưa có nhóm slider nào. Hãy tạo mới!</td>
|
||||
<td colspan="6" class="text-center">Chưa có nhóm slider nào. Hãy tạo mới!</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Reference in New Issue
Block a user