feat: implement responsive tab plugin and new hover card v3 component, and add admin controllers for price and recruiting tables.
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
<style>
|
||||
/* Bento Layout 02 (Featured 1 + 2x2 Grid) */
|
||||
.news-section-v3 {
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
padding: 24px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
gap: 24px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.news-section-v3 .badge-news {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
background-color: #f1f5f9;
|
||||
color: #475569;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
border-radius: 4px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.news-section-v3 .post-date {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
.news-section-v3 .featured-post {
|
||||
grid-column: span 6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
.news-section-v3 .featured-img-box {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.news-section-v3 .featured-img-box img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.news-section-v3 .featured-post:hover .featured-img-box img {
|
||||
transform: scale(1.03);
|
||||
}
|
||||
.news-section-v3 .meta-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.news-section-v3 .featured-title {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #0284c7;
|
||||
line-height: 1.3;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.news-section-v3 .featured-title:hover {
|
||||
color: #0369a1;
|
||||
}
|
||||
.news-section-v3 .featured-excerpt {
|
||||
font-size: 14px;
|
||||
color: #475569;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.news-section-v3 .side-news-grid {
|
||||
grid-column: span 6;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
.news-section-v3 .small-post {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
.news-section-v3 .small-img-box {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 10;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.news-section-v3 .small-img-box img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.news-section-v3 .small-post:hover .small-img-box img {
|
||||
transform: scale(1.04);
|
||||
}
|
||||
.news-section-v3 .small-title {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
}
|
||||
.news-section-v3 .small-post:hover .small-title {
|
||||
color: #0284c7;
|
||||
}
|
||||
@media (max-width: 992px) {
|
||||
.news-section-v3 .featured-post,
|
||||
.news-section-v3 .side-news-grid {
|
||||
grid-column: span 12;
|
||||
}
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.news-section-v3 .side-news-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.news-section-v3 .featured-title {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="news-section-v3" th:if="${posts != null}">
|
||||
<th:block th:with="filteredPosts=${posts}">
|
||||
<article class="featured-post">
|
||||
<div class="featured-img-box" th:if="${(block.data['showImage'] == null or block.data['showImage'] == true) and firstPost.featuredImage != null and !#strings.isEmpty(firstPost.featuredImage)}">
|
||||
<img th:src="${firstPost.featuredImage}" th:alt="${firstPost.title}" />
|
||||
</div>
|
||||
<div class="meta-row">
|
||||
<span class="badge-news" th:if="${firstPost.category != null}" th:text="${firstPost.category.name}">Tin tức</span>
|
||||
<span class="post-date" th:if="${(block.data['showDate'] == null or block.data['showDate'] == true) and firstPost.createdDate != null}" th:text="${#temporals.format(firstPost.createdDate, 'dd/MM/yyyy')}">06/07/2026</span>
|
||||
</div>
|
||||
<a th:href="@{'/posts/' + ${firstPost.slug}}" class="featured-title" th:utext="${firstPost.title}">Featured Post Title</a>
|
||||
<p class="featured-excerpt" th:if="${(block.data['showExcerpt'] == null or block.data['showExcerpt'] == true) and firstPost.excerpt != null and !#strings.isEmpty(firstPost.excerpt)}" th:utext="${firstPost.excerpt}">Featured Excerpt...</p>
|
||||
</article>
|
||||
</th:block>
|
||||
|
||||
<!-- CỘT PHẢI: Lưới 4 bài viết nhỏ (2x2) -->
|
||||
<div class="side-news-grid">
|
||||
<th:block th:each="p, iterStat : ${posts}" th:if="${iterStat.index > 0 and iterStat.index <= 4}">
|
||||
<a th:href="@{'/posts/' + ${p.slug}}" class="small-post">
|
||||
<div class="small-img-box" th:if="${(block.data['showImage'] == null or block.data['showImage'] == true) and p.featuredImage != null and !#strings.isEmpty(p.featuredImage)}">
|
||||
<img th:src="${p.featuredImage}" th:alt="${p.title}" />
|
||||
</div>
|
||||
<div class="meta-row">
|
||||
<span class="badge-news" th:if="${p.category != null}" th:text="${p.category.name}">Category</span>
|
||||
</div>
|
||||
<h3 class="small-title" th:utext="${p.title}">Small Post Title</h3>
|
||||
<span class="post-date" th:if="${(block.data['showDate'] == null or block.data['showDate'] == true) and p.createdDate != null}" th:text="${#temporals.format(p.createdDate, 'dd/MM/yyyy')}">04/08/2026</span>
|
||||
</a>
|
||||
</th:block>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user