{% include '@Application/inc/central_header.html.twig' %}
<style>
:root {
--n-cream: #F7F5F0;
--n-dark: #1A1D2E;
--n-amber: #C07D2A;
--n-sage: #3D6B52;
--n-slate: #3D4F72;
--n-muted: #6B7280;
--n-border: rgba(26,29,46,.09);
}
/* ── Hero ── */
.n-page-hero {
background: var(--n-cream);
padding: 72px 0 48px;
text-align: center;
}
.n-page-hero .n-eyebrow {
display: inline-block;
font-size: .75rem;
font-weight: 700;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--n-amber);
margin-bottom: 14px;
}
.n-page-hero h1 {
font-family: 'Montserrat', sans-serif;
font-size: clamp(1.9rem, 4vw, 2.6rem);
font-weight: 700;
color: var(--n-dark);
margin: 0 0 20px;
}
/* topic filter */
.n-topic-bar {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
max-width: 900px;
margin: 0 auto 0;
}
.n-topic-btn {
padding: 7px 18px;
border: 1.5px solid var(--n-border);
border-radius: 50px;
background: #fff;
font-family: 'DM Sans', sans-serif;
font-size: .82rem;
font-weight: 500;
color: var(--n-muted);
cursor: pointer;
transition: all .2s;
}
.n-topic-btn.active,
.n-topic-btn:hover {
background: var(--n-dark);
color: #fff;
border-color: var(--n-dark);
}
.n-search-bar {
display: flex;
max-width: 380px;
margin: 0 auto 0;
border: 1.5px solid var(--n-border);
border-radius: 50px;
overflow: hidden;
background: #fff;
}
.n-search-bar input {
flex: 1;
padding: 10px 18px;
border: none;
font-family: 'DM Sans', sans-serif;
font-size: .88rem;
color: var(--n-dark);
background: transparent;
outline: none;
}
.n-search-bar button {
padding: 0 18px;
background: var(--n-amber);
border: none;
color: #fff;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
font-size: .82rem;
font-weight: 600;
font-family: 'DM Sans', sans-serif;
transition: background .2s;
}
.n-search-bar button:hover { background: #a86d24; }
/* filter row */
.n-filter-row {
max-width: 1100px;
margin: 0 auto;
padding: 24px 24px 0;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
}
/* ── Blog Body ── */
.n-blog-body {
background: #fff;
padding: 56px 0 96px;
}
.n-blog-container {
max-width: 1100px;
margin: 0 auto;
padding: 0 24px;
}
/* Featured + sidebar top row */
.n-blog-top {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 40px;
margin-bottom: 56px;
}
@media(max-width: 768px) {
.n-blog-top { grid-template-columns: 1fr; gap: 28px; }
.n-blog-sidebar { display: none; }
}
/* Featured card */
.n-featured-card {
border-radius: 16px;
overflow: hidden;
border: 1.5px solid var(--n-border);
background: var(--n-cream);
display: flex;
flex-direction: column;
transition: box-shadow .25s;
}
.n-featured-card:hover { box-shadow: 0 8px 32px rgba(26,29,46,.1); }
.n-featured-card img {
width: 100%;
height: 240px;
object-fit: cover;
display: block;
}
.n-featured-body { padding: 24px; flex: 1; display: flex; flex-direction: column; }
.n-featured-body h5 {
font-family: 'Montserrat', sans-serif;
font-size: 1.1rem;
font-weight: 700;
color: var(--n-dark);
margin: 0 0 10px;
line-height: 1.3;
}
.n-featured-body p {
font-size: .9rem;
color: var(--n-muted);
line-height: 1.65;
margin: 0 0 18px;
flex: 1;
}
.n-featured-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
.n-know-more {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 18px;
background: var(--n-dark);
color: #fff;
border-radius: 6px;
font-size: .83rem;
font-weight: 600;
text-decoration: none;
transition: background .2s;
}
.n-know-more:hover { background: var(--n-amber); color: #fff; text-decoration: none; }
.n-card-date { font-size: .78rem; color: var(--n-muted); }
/* Sidebar */
.n-blog-sidebar h4 {
font-family: 'Montserrat', sans-serif;
font-size: .95rem;
font-weight: 700;
color: var(--n-dark);
margin: 0 0 18px;
}
.n-recent-list { list-style: none; margin: 0; padding: 0; }
.n-recent-list li { padding: 14px 0; border-bottom: 1px solid var(--n-border); }
.n-recent-list li:last-child { border-bottom: none; }
.n-recent-list a {
font-family: 'DM Sans', sans-serif;
font-size: .9rem;
font-weight: 600;
color: var(--n-dark);
text-decoration: none;
line-height: 1.35;
display: block;
margin-bottom: 4px;
transition: color .2s;
}
.n-recent-list a:hover { color: var(--n-amber); }
.n-recent-meta { font-size: .75rem; color: var(--n-muted); }
.n-recent-meta .n-date { margin-left: 6px; }
/* Blog grid */
.n-blog-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 24px;
}
.n-blog-card {
border-radius: 14px;
overflow: hidden;
border: 1.5px solid var(--n-border);
background: var(--n-cream);
display: flex;
flex-direction: column;
transition: box-shadow .25s, transform .2s;
}
.n-blog-card:hover { box-shadow: 0 6px 24px rgba(26,29,46,.09); transform: translateY(-2px); }
.n-blog-card img {
width: 100%;
height: 180px;
object-fit: cover;
display: block;
}
.n-blog-card-body { padding: 18px; flex: 1; display: flex; flex-direction: column; }
.n-blog-card-body h5 {
font-family: 'Montserrat', sans-serif;
font-size: .95rem;
font-weight: 700;
color: var(--n-dark);
margin: 0 0 8px;
line-height: 1.3;
}
.n-blog-card-body p {
font-size: .85rem;
color: var(--n-muted);
line-height: 1.6;
margin: 0 0 12px;
flex: 1;
}
.n-blog-card-meta { font-size: .75rem; color: var(--n-muted); margin-bottom: 12px; }
.n-blog-card-meta .n-date { margin-left: 6px; }
.n-blog-card-link {
font-size: .82rem;
font-weight: 600;
color: var(--n-amber);
text-decoration: none;
transition: color .2s;
}
.n-blog-card-link:hover { color: var(--n-dark); }
/* Pagination */
.n-pagination {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 48px;
padding-top: 28px;
border-top: 1.5px solid var(--n-border);
flex-wrap: wrap;
gap: 12px;
}
.n-pag-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 18px;
border: 1.5px solid var(--n-border);
border-radius: 8px;
background: #fff;
font-family: 'DM Sans', sans-serif;
font-size: .88rem;
font-weight: 600;
color: var(--n-dark);
text-decoration: none;
transition: all .2s;
}
.n-pag-btn:hover:not(.disabled) { background: var(--n-dark); color: #fff; border-color: var(--n-dark); text-decoration: none; }
.n-pag-btn.disabled { opacity: .4; pointer-events: none; }
.n-page-nums { display: flex; gap: 4px; align-items: center; }
.n-page-num {
min-width: 34px;
height: 34px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
font-size: .85rem;
font-weight: 500;
text-decoration: none;
color: var(--n-muted);
border: 1.5px solid transparent;
transition: all .2s;
}
.n-page-num:hover { border-color: var(--n-border); color: var(--n-dark); text-decoration: none; }
.n-page-num.active { background: var(--n-dark); color: #fff; font-weight: 700; }
.n-page-num.dots { cursor: default; color: var(--n-muted); }
</style>
<!-- Hero + Filters -->
<section class="n-page-hero">
<div class="n-wrap">
<span class="n-eyebrow">Insights & Ideas</span>
<h1>Latest Blogs</h1>
</div>
<div class="n-filter-row" style="max-width:900px; margin:0 auto; padding:20px 24px 0;">
<div class="n-topic-bar all-tabs">
<button class="n-topic-btn topic-btn active" data-topic-id="all">All Topics</button>
{% for topic in topics %}
<button class="n-topic-btn topic-btn" data-topic-id="{{ topic.id }}">{{ topic.topicName }}</button>
{% else %}
{% endfor %}
</div>
<div class="n-search-bar">
<input type="text" id="blogSearchInput" placeholder="Search blogs…">
<button id="blogSearchBtn" type="button"><i class="fas fa-search"></i> Search</button>
</div>
</div>
</section>
<!-- Blog Content -->
<section class="n-blog-body">
<div class="n-blog-container">
<!-- Featured + Sidebar -->
<div class="n-blog-top">
<div id="blogSection">
{% if featuredBlog %}
<div class="n-featured-card">
<img src="{{ featuredBlog.mainImage
? absolute_url(path('dashboard')) ~ '/' ~ featuredBlog.mainImage ~ '?version=' ~ constant('ApplicationBundle\\Constants\\GeneralConstant::ENTITY_APP_VERSION')
: absolute_url(path('dashboard')) ~ 'honeybee_web_assets/images/Blogs/pic1.png?version=' ~ constant('ApplicationBundle\\Constants\\GeneralConstant::ENTITY_APP_VERSION') }}"
alt="{{ featuredBlog.title }}">
<div class="n-featured-body">
<h5>{{ featuredBlog.title }}</h5>
<p class="card-text" data-full-text="{{ featuredBlog.content|striptags }}"></p>
<div class="n-featured-footer">
<a href="{{ path('honeybee_single_blog') }}?id={{ featuredBlog.id }}" class="n-know-more">
<i class="fa fa-arrow-right"></i> Read More
</a>
<span class="n-card-date">{{ featuredBlog.createdAt ? featuredBlog.createdAt|date('d/m/Y') : 'N/A' }}</span>
</div>
</div>
</div>
{% else %}
<div class="n-featured-card">
<div class="n-featured-body">
<h5>No Featured Blog Available</h5>
<p>Check back soon for featured content.</p>
</div>
</div>
{% endif %}
</div>
<div class="n-blog-sidebar recent-blog-sidebar">
<h4>Recent Posts</h4>
<ul class="n-recent-list recent-blogs">
{% set recentCount = 0 %}
{% for blog in blogs %}
{% if recentCount < 4 %}
<li>
<a href="{{ path('honeybee_single_blog') }}?id={{ blog.id }}">{{ blog.title }}</a>
<p class="n-recent-meta">
By {{ blog.authorName ?? 'Unknown' }}
<span class="n-date">{{ blog.createdAt ? blog.createdAt|date('F d, Y') : 'N/A' }}</span>
</p>
</li>
{% set recentCount = recentCount + 1 %}
{% endif %}
{% else %}
<li><p>No recent blogs found.</p></li>
{% endfor %}
</ul>
</div>
</div>
<!-- Blog Grid -->
<div class="n-blog-grid" id="additionalBlogs">
{% for blog in blogs %}
<div class="n-blog-card filterable-blog-item" data-topic="{{ blog.topicId }}">
<img src="{{ blog.mainImage
? absolute_url(path('dashboard')) ~ '/' ~ blog.mainImage
: absolute_url(path('dashboard')) ~ 'honeybee_web_assets/images/Blogs/pic4.png?version=' ~ constant('ApplicationBundle\\Constants\\GeneralConstant::ENTITY_APP_VERSION') }}"
alt="{{ blog.title }}">
<div class="n-blog-card-body">
<h5 class="searchable-title">{{ blog.title }}</h5>
<p>{{ blog.content|striptags|slice(0, 150) }}…</p>
<p class="n-blog-card-meta">
By {{ blog.authorName ?? 'Unknown' }}
<span class="n-date">{{ blog.createdAt ? blog.createdAt|date('d/m/Y') : 'N/A' }}</span>
</p>
<a href="{{ path('honeybee_single_blog') }}?id={{ blog.id }}" class="n-blog-card-link">Read More →</a>
</div>
</div>
{% else %}
<div style="grid-column:1/-1; text-align:center; color:var(--n-muted); padding:40px 0;">No blogs available at the moment.</div>
{% endfor %}
</div>
<!-- Pagination -->
{% if totalPages > 1 %}
<div class="n-pagination">
<a href="{{ path('honeybee_blogs', {action: 'create', page: currentPage - 1}) }}"
class="n-pag-btn btn-pag {{ currentPage <= 1 ? 'disabled' : '' }}">
<i class="fa fa-chevron-left"></i> Prev
</a>
<div class="n-page-nums">
{% for p in 1..totalPages %}
{% if p == currentPage %}
<span class="n-page-num page-num active">{{ p }}</span>
{% elseif p == 1 or p == totalPages or (p >= currentPage - 2 and p <= currentPage + 2) %}
<a href="{{ path('honeybee_blogs', {action: 'create', page: p}) }}" class="n-page-num page-num">{{ p }}</a>
{% elseif p == currentPage - 3 or p == currentPage + 3 %}
<span class="n-page-num dots">…</span>
{% endif %}
{% endfor %}
</div>
<a href="{{ path('honeybee_blogs', {action: 'create', page: currentPage + 1}) }}"
class="n-pag-btn btn-pag {{ currentPage >= totalPages ? 'disabled' : '' }}">
Next <i class="fa fa-chevron-right"></i>
</a>
</div>
{% endif %}
</div>
</section>
{% include '@HoneybeeWeb/footer/central_footer.html.twig' %}
<script>
document.addEventListener("DOMContentLoaded", function () {
// Featured blog truncate
const cardText = document.querySelector("#blogSection .card-text");
if (cardText) {
const fullText = cardText.getAttribute("data-full-text") || '';
const words = fullText.trim().split(/\s+/);
cardText.textContent = words.length > 30 ? words.slice(0, 30).join(" ") + "..." : fullText;
}
const ITEMS_PER_PAGE = 6;
let currentPage = 1;
let activeTopicId = 'all';
let searchTerm = '';
const searchInput = document.getElementById('blogSearchInput');
const searchBtn = document.getElementById('blogSearchBtn');
const topicBtns = document.querySelectorAll('.topic-btn');
const allBlogItems = document.querySelectorAll('.filterable-blog-item');
const featuredSection = document.getElementById('blogSection');
const recentSidebar = document.querySelector('.recent-blog-sidebar');
const paginationContainer = document.querySelector('.pagination-container');
const prevBtn = document.getElementById('blogPrevPageBtn');
const nextBtn = document.getElementById('blogNextPageBtn');
const currentPageDisplay = document.getElementById('currentPageDisplay');
const totalPagesDisplay = document.getElementById('totalPagesDisplay');
function getFilteredItems() {
return Array.from(allBlogItems).filter(item => {
const itemTopic = item.getAttribute('data-topic');
const titleText = (item.querySelector('.searchable-title')?.textContent || '').toLowerCase();
const matchesTopic = activeTopicId === 'all' || itemTopic == activeTopicId;
const matchesSearch = titleText.includes(searchTerm);
return matchesTopic && matchesSearch;
});
}
function renderPage() {
const isFiltering = activeTopicId !== 'all' || searchTerm !== '';
if (featuredSection) featuredSection.style.display = isFiltering ? 'none' : '';
if (recentSidebar) recentSidebar.style.display = isFiltering ? 'none' : '';
const filteredItems = getFilteredItems();
const totalPages = Math.max(1, Math.ceil(filteredItems.length / ITEMS_PER_PAGE));
if (currentPage > totalPages) currentPage = totalPages;
if (currentPage < 1) currentPage = 1;
const start = (currentPage - 1) * ITEMS_PER_PAGE;
const end = start + ITEMS_PER_PAGE;
allBlogItems.forEach(item => item.style.display = 'none');
filteredItems.forEach((item, index) => {
item.style.display = (index >= start && index < end) ? '' : 'none';
});
if (currentPageDisplay) currentPageDisplay.textContent = currentPage;
if (totalPagesDisplay) totalPagesDisplay.textContent = totalPages;
if (prevBtn) prevBtn.disabled = currentPage === 1;
if (nextBtn) nextBtn.disabled = currentPage === totalPages;
if (paginationContainer) paginationContainer.style.display = totalPages <= 1 ? 'none' : 'flex';
}
topicBtns.forEach(btn => {
btn.addEventListener('click', function() {
topicBtns.forEach(b => b.classList.remove('active'));
this.classList.add('active');
activeTopicId = this.getAttribute('data-topic-id');
currentPage = 1;
renderPage();
});
});
function onSearch() {
searchTerm = searchInput.value.toLowerCase().trim();
currentPage = 1;
renderPage();
}
if (searchInput) searchInput.addEventListener('input', onSearch);
if (searchBtn) searchBtn.addEventListener('click', onSearch);
if (prevBtn) prevBtn.addEventListener('click', function() {
if (currentPage > 1) { currentPage--; renderPage(); }
});
if (nextBtn) nextBtn.addEventListener('click', function() {
const totalPages = Math.ceil(getFilteredItems().length / ITEMS_PER_PAGE);
if (currentPage < totalPages) { currentPage++; renderPage(); }
});
renderPage();
});
</script>