{% extends "@UVDeskSupportCenter/Templates/layout.html.twig" %}
{% block title %}Base de Conocimientos{% endblock %}
{% block body %}
<div class="modern-kb-container" id="modern-kb-container">
<div class="modern-kb-wrapper">
<!-- Sidebar Navigation -->
<aside class="modern-kb-sidebar">
<div class="modern-kb-sidebar-header">
<h2>{{ 'Base de Conocimientos'|trans }}</h2>
<a href="{{ path('helpdesk_knowledgebase') }}" class="kb-back-to-topics" title="{{ 'Volver a temas frecuentes'|trans }}">
← {{ 'Temas frecuentes'|trans }}
</a>
<div class="kb-search-container">
<input type="text"
id="kb-search-input"
class="kb-search-input"
placeholder="{{ 'Buscar artículos...'|trans }}"
autocomplete="off">
<span class="kb-search-icon">🔍</span>
<button type="button" class="kb-search-clear" id="kb-search-clear" style="display: none;">✕</button>
</div>
<div class="kb-search-results-count" id="kb-search-results-count" style="display: none;">
<span id="kb-results-number">0</span> {{ 'resultados encontrados'|trans }}
</div>
</div>
<nav class="modern-kb-nav" id="kb-navigation">
{% if structure|length == 0 %}
<div style="padding: 20px; color: #bdc3c7; text-align: center;">
<p>No hay carpetas disponibles</p>
</div>
{% else %}
{% for folder in structure %}
<div class="modern-kb-folder-section">
<div class="modern-kb-folder-header" data-folder-id="{{ folder.id }}">
{% if folder.solutionImage %}
<img src="{{ app.request.scheme ~'://' ~ app.request.httpHost ~ asset('') }}{{ folder.solutionImage }}"
class="folder-icon" alt="{{ folder.name }}">
{% else %}
<span class="folder-icon-placeholder">{{ folder.name|slice(0, 2)|upper }}</span>
{% endif %}
<span class="folder-name">{{ folder.name }}</span>
<span class="folder-toggle">▼</span>
</div>
<div class="modern-kb-folder-content" id="folder-{{ folder.id }}" style="max-height: 0px; opacity: 0; overflow: hidden; padding-top: 0; padding-bottom: 0;">
{% if folder.categories|length > 0 %}
{% for category in folder.categories %}
<div class="modern-kb-category-section">
<div class="modern-kb-category-header" data-category-id="{{ category.id }}">
<span class="category-name">{{ category.name }}</span>
<span class="category-count">({{ category.articleCount }})</span>
<span class="category-toggle">▶</span>
</div>
<ul class="modern-kb-articles-list" id="category-{{ category.id }}" style="display: none;">
{% for article in category.articles %}
{% set articleId = article.id is defined ? article.id : (article.getId is defined ? article.getId() : null) %}
{% set articleName = article.name is defined ? article.name : (article.getName is defined ? article.getName() : 'Sin título') %}
{% set articleSlug = article.slug is defined ? article.slug : (article.getSlug is defined ? article.getSlug() : '') %}
{% set articleStared = article.stared is defined ? article.stared : (article.getStared is defined ? article.getStared() : false) %}
<li class="modern-kb-article-item">
<a href="#"
class="kb-article-link"
data-article-slug="{{ articleSlug }}"
data-article-id="{{ articleId }}">
{% if articleStared %}<span class="article-star">⭐</span>{% endif %}
{{ articleName }}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
{% endif %}
{% if folder.directArticles|length > 0 %}
<div class="modern-kb-direct-articles">
<div class="modern-kb-category-header" data-category-id="direct-{{ folder.id }}" style="cursor: pointer;">
<span class="category-name">{{ 'Artículos Directos'|trans }}</span>
<span class="category-count">({{ folder.directArticles|length }})</span>
<span class="category-toggle">▶</span>
</div>
<ul class="modern-kb-articles-list" id="category-direct-{{ folder.id }}" style="display: none;">
{% for article in folder.directArticles %}
{% set articleId = article.id is defined ? article.id : (article.getId is defined ? article.getId() : null) %}
{% set articleName = article.name is defined ? article.name : (article.getName is defined ? article.getName() : 'Sin título') %}
{% set articleSlug = article.slug is defined ? article.slug : (article.getSlug is defined ? article.getSlug() : '') %}
{% set articleStared = article.stared is defined ? article.stared : (article.getStared is defined ? article.getStared() : false) %}
<li class="modern-kb-article-item">
<a href="#"
class="kb-article-link"
data-article-slug="{{ articleSlug }}"
data-article-id="{{ articleId }}">
{% if articleStared %}<span class="article-star">⭐</span>{% endif %}
{{ articleName }}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
</div>
{% endfor %}
{% endif %}
</nav>
</aside>
<!-- Main Content Area -->
<main class="modern-kb-content">
<!-- Content Search Bar -->
<div class="kb-content-search-bar">
<div class="kb-content-search-container">
<input type="text"
id="kb-content-search-input"
class="kb-content-search-input"
placeholder="{{ 'Buscar en el contenido de los artículos...'|trans }}"
autocomplete="off">
<span class="kb-content-search-icon">🔍</span>
<button type="button" class="kb-content-search-clear" id="kb-content-search-clear" style="display: none;">✕</button>
</div>
<div class="kb-content-search-results-count" id="kb-content-search-results-count" style="display: none;">
<span id="kb-content-results-number">0</span> {{ 'artículos encontrados'|trans }}
</div>
</div>
<div id="kb-article-viewer">
<!-- Welcome Screen -->
<div class="kb-welcome-screen" id="welcome-screen">
<div class="kb-welcome-content">
<h1>{{ 'Bienvenido a la Base de Conocimientos'|trans }}</h1>
<p>{{ 'Seleccione un artículo del menú lateral para comenzar o navegue por todo el contenido'|trans }}</p>
<div class="kb-stats">
{% set totalArticles = 0 %}
{% for folder in structure %}
{% set totalArticles = totalArticles + folder.totalArticles %}
{% endfor %}
<div class="kb-stat-item">
<span class="kb-stat-number">{{ structure|length }}</span>
<span class="kb-stat-label">{{ 'Carpetas'|trans }}</span>
</div>
<div class="kb-stat-item">
<span class="kb-stat-number">{{ totalArticles }}</span>
<span class="kb-stat-label">{{ 'Artículos'|trans }}</span>
</div>
</div>
</div>
</div>
<!-- All Articles Content -->
<div class="kb-all-articles-content">
{% for folder in structure %}
<div class="kb-folder-content-section" data-folder-id="{{ folder.id }}">
<div class="kb-folder-title-section">
<h2 class="kb-folder-title">{{ folder.name }}</h2>
{% if folder.description %}
<p class="kb-folder-description">{{ folder.description }}</p>
{% endif %}
</div>
{% if folder.categories|length > 0 %}
{% for category in folder.categories %}
<div class="kb-category-content-section" data-category-id="{{ category.id }}">
<h3 class="kb-category-title">{{ category.name }}</h3>
{% if category.description %}
<p class="kb-category-description">{{ category.description }}</p>
{% endif %}
<div class="kb-category-articles">
{% for article in category.articles %}
{% set articleId = article.id is defined ? article.id : (article.getId is defined ? article.getId() : null) %}
{% set articleName = article.name is defined ? article.name : (article.getName is defined ? article.getName() : 'Sin título') %}
{% set articleSlug = article.slug is defined ? article.slug : (article.getSlug is defined ? article.getSlug() : '') %}
{% set articleContent = article.content is defined ? article.content : (article.getContent is defined ? article.getContent() : '') %}
{% set articleDateAdded = article.dateAdded is defined ? article.dateAdded : (article.getDateAdded is defined ? article.getDateAdded() : null) %}
<article class="kb-article-display" id="article-{{ articleSlug }}" data-article-slug="{{ articleSlug }}" data-article-id="{{ articleId }}">
<header class="kb-article-header">
<h1>{{ articleName }}</h1>
{% if articleDateAdded %}
<div class="kb-article-meta">
<span class="kb-article-date">
{{ 'Publicado el'|trans }} {{ articleDateAdded }}
</span>
</div>
{% endif %}
</header>
<div class="kb-article-body">
{{ articleContent|raw }}
</div>
{% if article.subarticles is defined and article.subarticles is not empty %}
<hr>
<h2>{{ 'Related sections'|trans }}</h2>
<div class="uv-subarticles-list">
{% for sub in article.subarticles %}
<article class="uv-subarticle">
<h3>{{ sub.name }}</h3>
<div class="uv-subarticle-content">{{ sub.content|raw }}</div>
</article>
{% endfor %}
</div>
{% endif %}
</article>
{% endfor %}
</div>
</div>
{% endfor %}
{% endif %}
{% if folder.directArticles|length > 0 %}
<div class="kb-direct-articles-section" data-folder-id="{{ folder.id }}">
<h3 class="kb-category-title">{{ 'Artículos Directos'|trans }}</h3>
<div class="kb-category-articles">
{% for article in folder.directArticles %}
{% set articleId = article.id is defined ? article.id : (article.getId is defined ? article.getId() : null) %}
{% set articleName = article.name is defined ? article.name : (article.getName is defined ? article.getName() : 'Sin título') %}
{% set articleSlug = article.slug is defined ? article.slug : (article.getSlug is defined ? article.getSlug() : '') %}
{% set articleContent = article.content is defined ? article.content : (article.getContent is defined ? article.getContent() : '') %}
{% set articleDateAdded = article.dateAdded is defined ? article.dateAdded : (article.getDateAdded is defined ? article.getDateAdded() : null) %}
<article class="kb-article-display" id="article-{{ articleSlug }}" data-article-slug="{{ articleSlug }}" data-article-id="{{ articleId }}">
<header class="kb-article-header">
<h1>{{ articleName }}</h1>
{% if articleDateAdded %}
<div class="kb-article-meta">
<span class="kb-article-date">
{{ 'Publicado el'|trans }} {{ articleDateAdded }}
</span>
</div>
{% endif %}
</header>
<div class="kb-article-body">
{{ articleContent|raw }}
</div>
{% if article.subarticles is defined and article.subarticles is not empty %}
<hr>
<h2>{{ 'Related sections'|trans }}</h2>
<div class="uv-subarticles-list">
{% for sub in article.subarticles %}
<article class="uv-subarticle">
<h3>{{ sub.name }}</h3>
<div class="uv-subarticle-content">{{ sub.content|raw }}</div>
</article>
{% endfor %}
</div>
{% endif %}
</article>
{% endfor %}
</div>
</div>
{% endif %}
</div>
{% endfor %}
</div>
</div>
</main>
</div>
</div>
<style>
.modern-kb-container {
width: 100%;
min-height: calc(100vh - 200px);
background: #f8f9fa;
padding: 20px 0;
}
.modern-kb-wrapper {
display: flex;
max-width: 1400px;
margin: 0 auto;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
overflow: hidden;
height: calc(100vh - 250px);
min-height: calc(100vh - 250px);
max-height: calc(100vh - 250px);
}
/* Sidebar Styles */
.modern-kb-sidebar {
width: 320px;
background: #2c3e50;
color: #ecf0f1;
overflow: hidden;
flex-shrink: 0;
height: 100%;
display: flex;
flex-direction: column;
}
/* Sidebar scrollbar */
.modern-kb-sidebar::-webkit-scrollbar,
.modern-kb-nav::-webkit-scrollbar {
width: 8px;
}
.modern-kb-sidebar::-webkit-scrollbar-track,
.modern-kb-nav::-webkit-scrollbar-track {
background: #1a252f;
}
.modern-kb-sidebar::-webkit-scrollbar-thumb,
.modern-kb-nav::-webkit-scrollbar-thumb {
background: #34495e;
border-radius: 4px;
}
.modern-kb-sidebar::-webkit-scrollbar-thumb:hover,
.modern-kb-nav::-webkit-scrollbar-thumb:hover {
background: #4a5f7a;
}
.modern-kb-sidebar-header {
padding: 20px;
background: #34495e;
border-bottom: 2px solid #1a252f;
flex-shrink: 0;
}
.modern-kb-sidebar-header h2 {
margin: 0 0 15px 0;
font-size: 18px;
font-weight: 600;
color: #ecf0f1;
}
.kb-back-to-topics {
display: inline-block;
margin-top: 10px;
padding: 8px 12px;
background: rgba(52, 152, 219, 0.2);
color: #3498db;
text-decoration: none;
border-radius: 4px;
font-size: 13px;
transition: background 0.2s, color 0.2s;
}
.kb-back-to-topics:hover {
background: rgba(52, 152, 219, 0.3);
color: #5dade2;
}
/* Search Styles */
.kb-search-container {
position: relative;
margin-bottom: 10px;
}
.kb-search-input {
width: 100%;
padding: 10px 35px 10px 35px;
background: #1a252f;
border: 1px solid #34495e;
border-radius: 4px;
color: #ecf0f1;
font-size: 14px;
box-sizing: border-box;
transition: border-color 0.2s, background 0.2s;
}
.kb-search-input:not(:placeholder-shown) {
padding-right: 50px; /* More space when clear button is visible */
}
.kb-search-input:focus {
outline: none;
border-color: #3498db;
background: #2c3e50;
}
.kb-search-input::placeholder {
color: #7f8c8d;
}
.kb-search-icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: #7f8c8d;
pointer-events: none;
font-size: 14px;
}
.kb-search-clear {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
background: transparent;
border: none;
color: #7f8c8d;
cursor: pointer;
font-size: 16px;
padding: 4px 8px;
border-radius: 3px;
transition: color 0.2s, background 0.2s;
}
.kb-search-clear:hover {
color: #ecf0f1;
background: #34495e;
}
.kb-search-results-count {
margin-top: 8px;
font-size: 12px;
color: #95a5a6;
text-align: center;
}
.kb-search-results-count span {
font-weight: 600;
color: #3498db;
}
/* Highlight styles for search matches */
.kb-search-highlight {
background: rgba(52, 152, 219, 0.3);
padding: 2px 4px;
border-radius: 2px;
}
/* Styles for filtered/hidden items */
.modern-kb-folder-section.hidden-by-search,
.modern-kb-category-section.hidden-by-search,
.modern-kb-article-item.hidden-by-search,
.kb-article-display.hidden-by-content-search,
.kb-folder-content-section.hidden-by-content-search,
.kb-category-content-section.hidden-by-content-search {
display: none !important;
}
.modern-kb-folder-section.visible-by-search,
.modern-kb-category-section.visible-by-search {
display: block !important;
}
/* Welcome screen visibility */
.kb-welcome-screen.hidden-by-content-search {
display: none !important;
}
/* Content search highlight */
.kb-content-search-highlight,
mark.kb-content-search-highlight {
background: rgba(255, 235, 59, 0.5);
padding: 2px 4px;
border-radius: 3px;
font-weight: 500;
color: #2c3e50;
}
.modern-kb-nav {
padding: 10px 0;
flex: 1;
overflow-y: auto;
overflow-x: hidden;
position: relative;
width: 100%;
}
.modern-kb-folder-section {
border-bottom: 1px solid #34495e;
position: relative;
width: 100%;
display: block;
margin: 0;
padding: 0;
}
.modern-kb-folder-header {
padding: 12px 20px;
cursor: pointer;
display: flex;
align-items: center;
gap: 12px;
transition: background 0.2s;
user-select: none;
color: #ecf0f1 !important;
visibility: visible !important;
opacity: 1 !important;
position: relative;
width: 100%;
box-sizing: border-box;
min-height: 48px;
z-index: 1;
}
.modern-kb-folder-header:hover {
background: #34495e;
}
.modern-kb-folder-header.active {
background: #3498db;
}
.folder-icon,
.folder-icon-placeholder {
width: 32px;
height: 32px;
border-radius: 4px;
flex-shrink: 0;
}
.folder-icon {
object-fit: cover;
}
.folder-icon-placeholder {
background: #3498db;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
color: #fff !important;
visibility: visible !important;
}
.folder-name {
flex: 1;
min-width: 0;
font-weight: 500;
color: #ecf0f1 !important;
visibility: visible !important;
opacity: 1 !important;
font-size: 14px;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.folder-toggle {
font-size: 10px;
transition: transform 0.2s;
color: #ecf0f1 !important;
visibility: visible !important;
opacity: 1 !important;
flex-shrink: 0;
width: 16px;
text-align: center;
margin-left: 4px;
}
.modern-kb-folder-header.active .folder-toggle {
transform: rotate(180deg);
}
.modern-kb-folder-content {
background: #1a252f;
overflow: hidden;
transition: max-height 0.3s ease-out, opacity 0.3s ease-out, padding 0.3s ease-out;
color: #bdc3c7;
position: relative;
width: 100%;
display: block;
box-sizing: border-box;
margin: 0;
padding: 0;
will-change: max-height;
clear: both;
}
.modern-kb-folder-content.expanded {
color: #bdc3c7 !important;
}
.modern-kb-folder-content * {
visibility: visible !important;
color: #bdc3c7 !important;
text-shadow: none !important;
position: relative !important;
}
.modern-kb-folder-content.expanded * {
color: #bdc3c7 !important;
visibility: visible !important;
opacity: 1 !important;
}
.modern-kb-folder-content span {
color: #bdc3c7 !important;
opacity: 1 !important;
visibility: visible !important;
}
/* Asegurar que no haya elementos superpuestos */
.modern-kb-folder-content > * {
position: relative !important;
float: none !important;
clear: both !important;
}
.modern-kb-category-section {
border-bottom: 1px solid #2c3e50;
position: relative;
width: 100%;
display: block;
margin: 0;
padding: 0;
}
.modern-kb-category-header {
padding: 10px 20px 10px 50px;
cursor: pointer;
display: flex !important;
align-items: center;
gap: 8px;
transition: background 0.2s;
user-select: none;
font-size: 14px !important;
color: #bdc3c7 !important;
visibility: visible !important;
opacity: 1 !important;
position: relative !important;
z-index: 1 !important;
width: 100%;
box-sizing: border-box;
margin: 0;
clear: both;
min-height: 40px;
}
.modern-kb-category-header .category-name,
.modern-kb-category-header .category-count,
.modern-kb-category-header .category-toggle {
color: #bdc3c7 !important;
visibility: visible !important;
opacity: 1 !important;
display: inline-block !important;
font-size: 14px !important;
position: relative;
z-index: 2;
}
.modern-kb-category-header:hover {
background: #2c3e50;
}
.category-name {
flex: 1;
min-width: 0;
color: #bdc3c7 !important;
font-size: 14px !important;
visibility: visible !important;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
position: relative;
z-index: 1;
display: block;
max-width: calc(100% - 80px);
}
.category-count {
font-size: 12px !important;
color: #95a5a6 !important;
visibility: visible !important;
flex-shrink: 0;
margin-left: 4px;
}
.category-toggle {
font-size: 10px !important;
transition: transform 0.2s;
color: #bdc3c7 !important;
visibility: visible !important;
flex-shrink: 0;
width: 12px;
text-align: center;
margin-left: 4px;
}
.modern-kb-category-header.active .category-toggle {
transform: rotate(90deg);
}
.modern-kb-articles-list {
list-style: none;
padding: 0;
margin: 0;
background: #1a252f;
visibility: visible !important;
opacity: 1 !important;
display: block !important;
position: relative;
width: 100%;
box-sizing: border-box;
clear: both;
min-height: 0;
}
.modern-kb-articles-list[style*="display: none"] {
display: none !important;
}
.modern-kb-article-item {
border-bottom: 1px solid #2c3e50;
position: relative;
width: 100%;
display: block;
margin: 0;
padding: 0;
}
.modern-kb-article-item:last-child {
border-bottom: none;
}
.kb-article-link {
display: block !important;
padding: 10px 20px 10px 70px;
color: #bdc3c7 !important;
text-decoration: none;
transition: all 0.2s;
font-size: 13px;
line-height: 1.5;
visibility: visible !important;
opacity: 1 !important;
cursor: pointer;
width: 100%;
box-sizing: border-box;
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
clear: both;
z-index: 1;
}
.kb-article-link:hover {
background: #2c3e50;
color: #3498db;
padding-left: 75px;
white-space: normal;
overflow: visible;
text-overflow: clip;
}
.kb-article-link.active {
background: #3498db;
color: #fff;
font-weight: 500;
}
.article-star {
margin-right: 6px;
font-size: 12px;
}
.modern-kb-direct-articles {
padding: 10px 0;
}
.modern-kb-direct-articles .modern-kb-category-header {
padding-left: 50px;
cursor: pointer;
}
.modern-kb-direct-articles .modern-kb-category-header:hover {
background: #2c3e50;
}
/* Main Content Styles */
.modern-kb-content {
flex: 1;
padding: 0;
background: #fff;
overflow-y: scroll;
overflow-x: hidden;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
}
/* Force scrollbar to be visible always */
.modern-kb-content {
overflow-y: scroll !important;
}
.modern-kb-content::-webkit-scrollbar {
width: 12px;
}
.modern-kb-content::-webkit-scrollbar-track {
background: #f1f1f1;
border-left: 1px solid #e0e0e0;
}
.modern-kb-content::-webkit-scrollbar-thumb {
background: #888;
border-radius: 6px;
border: 2px solid #f1f1f1;
}
.modern-kb-content::-webkit-scrollbar-thumb:hover {
background: #555;
}
/* Firefox scrollbar */
.modern-kb-content {
scrollbar-width: thin;
scrollbar-color: #888 #f1f1f1;
}
/* Content Search Bar Styles */
.kb-content-search-bar {
padding: 20px;
background: #f8f9fa;
border-bottom: 2px solid #e0e0e0;
flex-shrink: 0;
position: sticky;
top: 0;
z-index: 10;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.kb-content-search-container {
position: relative;
max-width: 800px;
margin: 0 auto;
}
.kb-content-search-input {
width: 100%;
padding: 12px 45px 12px 45px;
background: #fff;
border: 2px solid #ddd;
border-radius: 6px;
color: #2c3e50;
font-size: 16px;
box-sizing: border-box;
transition: border-color 0.2s, box-shadow 0.2s;
}
.kb-content-search-input:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
.kb-content-search-input::placeholder {
color: #95a5a6;
}
.kb-content-search-input:not(:placeholder-shown) {
padding-right: 60px;
}
.kb-content-search-icon {
position: absolute;
left: 15px;
top: 50%;
transform: translateY(-50%);
color: #7f8c8d;
pointer-events: none;
font-size: 16px;
}
.kb-content-search-clear {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: transparent;
border: none;
color: #7f8c8d;
cursor: pointer;
font-size: 18px;
padding: 5px 10px;
border-radius: 4px;
transition: color 0.2s, background 0.2s;
}
.kb-content-search-clear:hover {
color: #2c3e50;
background: #ecf0f1;
}
.kb-content-search-results-count {
margin-top: 10px;
font-size: 14px;
color: #7f8c8d;
text-align: center;
}
.kb-content-search-results-count span {
font-weight: 600;
color: #3498db;
}
#kb-article-viewer {
padding: 0;
min-height: 100%;
background: #fff;
width: 100%;
overflow: visible;
flex: 1;
}
/* Asegurar que el contenido tenga altura suficiente para scroll */
.kb-welcome-screen,
.kb-article-display {
min-height: calc(100vh - 250px);
padding-bottom: 60px;
}
/* Estilos para el contenido completo de artículos */
.kb-all-articles-content {
padding: 20px 0;
}
.kb-folder-content-section {
margin-bottom: 60px;
padding-bottom: 40px;
border-bottom: 2px solid #ecf0f1;
}
.kb-folder-content-section:last-child {
border-bottom: none;
}
.kb-folder-title-section {
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid #ecf0f1;
}
.kb-folder-title {
font-size: 32px;
color: #2c3e50;
margin: 0 0 10px 0;
font-weight: 600;
}
.kb-folder-description {
font-size: 16px;
color: #7f8c8d;
margin: 0;
}
.kb-category-content-section {
margin-bottom: 40px;
}
.kb-category-title {
font-size: 24px;
color: #34495e;
margin: 30px 0 15px 0;
font-weight: 600;
padding-left: 20px;
border-left: 4px solid #3498db;
}
.kb-category-description {
font-size: 14px;
color: #7f8c8d;
margin: 0 0 20px 20px;
}
.kb-category-articles {
padding-left: 20px;
}
.kb-direct-articles-section {
margin-top: 40px;
padding-top: 40px;
border-top: 1px solid #ecf0f1;
}
/* Asegurar espaciado entre artículos */
.kb-article-display {
margin-bottom: 60px;
padding-bottom: 40px;
border-bottom: 1px solid #f0f0f0;
}
.kb-article-display:last-child {
border-bottom: none;
margin-bottom: 0;
}
/* Highlight para artículo activo */
.kb-article-display.active {
background: #f8f9fa;
padding: 30px;
border-radius: 8px;
border-left: 4px solid #3498db;
margin-left: -30px;
padding-left: 30px;
}
.kb-loading {
padding: 40px;
text-align: center;
color: #7f8c8d;
font-size: 16px;
}
.kb-welcome-screen {
display: flex;
align-items: flex-start;
justify-content: center;
min-height: calc(100vh - 250px);
padding: 60px 20px 40px 20px;
width: 100%;
box-sizing: border-box;
}
.kb-welcome-content {
text-align: center;
max-width: 600px;
width: 100%;
padding: 20px;
box-sizing: border-box;
}
.kb-welcome-content h1 {
font-size: 32px;
color: #2c3e50;
margin-bottom: 16px;
}
.kb-welcome-content > p {
font-size: 18px;
color: #7f8c8d;
margin-bottom: 40px;
}
.kb-stats {
display: flex;
justify-content: center;
gap: 40px;
margin-top: 40px;
}
.kb-stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.kb-stat-number {
font-size: 48px;
font-weight: bold;
color: #3498db;
line-height: 1;
}
.kb-stat-label {
font-size: 14px;
color: #7f8c8d;
margin-top: 8px;
}
/* Article Display Styles */
.kb-article-display {
padding: 40px;
max-width: 900px;
margin: 0 auto;
}
.kb-article-header {
border-bottom: 2px solid #ecf0f1;
padding-bottom: 20px;
margin-bottom: 30px;
}
.kb-article-header h1 {
font-size: 32px;
color: #2c3e50;
margin: 0 0 16px 0;
line-height: 1.3;
}
.kb-article-meta {
display: flex;
gap: 20px;
font-size: 14px;
color: #7f8c8d;
}
.kb-article-author {
font-weight: 500;
}
.kb-article-date {
color: #95a5a6;
}
.kb-article-body {
font-size: 16px;
line-height: 1.8;
color: #34495e;
}
.kb-article-body h2 {
font-size: 24px;
color: #2c3e50;
margin-top: 32px;
margin-bottom: 16px;
}
.kb-article-body h3 {
font-size: 20px;
color: #34495e;
margin-top: 24px;
margin-bottom: 12px;
}
.kb-article-body p {
margin-bottom: 16px;
}
.kb-article-body ul,
.kb-article-body ol {
margin-bottom: 16px;
padding-left: 30px;
}
.kb-article-body li {
margin-bottom: 8px;
}
.kb-article-body code {
background: #f4f4f4;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Courier New', monospace;
font-size: 14px;
}
.kb-article-body pre {
background: #2c3e50;
color: #ecf0f1;
padding: 16px;
border-radius: 4px;
overflow-x: auto;
margin-bottom: 16px;
}
.kb-article-body pre code {
background: transparent;
padding: 0;
color: inherit;
}
.kb-article-body img {
max-width: 100%;
height: auto;
border-radius: 4px;
margin: 20px 0;
}
.kb-article-body table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
.kb-article-body table th,
.kb-article-body table td {
padding: 12px;
border: 1px solid #ddd;
text-align: left;
}
.kb-article-body table th {
background: #f8f9fa;
font-weight: 600;
}
.kb-article-body blockquote {
border-left: 4px solid #3498db;
padding-left: 16px;
margin: 20px 0;
color: #7f8c8d;
font-style: italic;
}
/* Loading State */
.kb-loading {
display: flex;
align-items: center;
justify-content: center;
min-height: 400px;
font-size: 18px;
color: #7f8c8d;
}
.kb-loading::before {
content: '';
width: 40px;
height: 40px;
border: 4px solid #ecf0f1;
border-top-color: #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-right: 16px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Responsive */
@media (max-width: 768px) {
.modern-kb-wrapper {
flex-direction: column;
}
.modern-kb-sidebar {
width: 100%;
max-height: 300px;
}
.modern-kb-content {
max-height: none;
}
}
</style>
<script>
(function() {
'use strict';
// Función para inicializar todos los event listeners
function initializeKB() {
console.log('Initializing Knowledge Base...');
// Debug: Log structure data
const nav = document.getElementById('kb-navigation');
if (nav) {
const folders = nav.querySelectorAll('.modern-kb-folder-section');
console.log('Total folders found in DOM:', folders.length);
folders.forEach(function(folder, index) {
const folderHeader = folder.querySelector('.modern-kb-folder-header');
const folderName = folderHeader ? folderHeader.querySelector('.folder-name').textContent : 'Unknown';
const categories = folder.querySelectorAll('.modern-kb-category-section');
const directArticles = folder.querySelectorAll('.modern-kb-direct-articles');
console.log(`Folder ${index + 1}: "${folderName}" - Categories: ${categories.length}, Direct Articles sections: ${directArticles.length}`);
});
}
// Toggle folder expansion
const folderHeaders = document.querySelectorAll('.modern-kb-folder-header');
console.log('Found folder headers:', folderHeaders.length);
folderHeaders.forEach(function(header) {
header.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('Folder header clicked:', this.dataset.folderId);
const folderId = this.dataset.folderId;
const content = document.getElementById('folder-' + folderId);
if (!content) {
console.error('Folder content not found for ID:', folderId);
return;
}
const isExpanded = content.classList.contains('expanded');
console.log('Current state - isExpanded:', isExpanded);
console.log('Content scrollHeight:', content.scrollHeight);
// Toggle this folder
this.classList.toggle('active');
// Force toggle with direct style manipulation
if (isExpanded) {
// Collapse
content.classList.remove('expanded');
content.style.maxHeight = '0px';
content.style.opacity = '0';
content.style.paddingTop = '0px';
content.style.paddingBottom = '0px';
content.style.overflow = 'hidden';
content.style.marginTop = '0';
content.style.marginBottom = '0';
console.log('Folder collapsed');
} else {
// Expand
content.classList.add('expanded');
// First, reset all styles to ensure clean state
content.style.opacity = '1';
content.style.paddingTop = '10px';
content.style.paddingBottom = '10px';
content.style.overflow = 'hidden';
// Temporarily remove max-height restriction to measure natural height
const originalMaxHeight = content.style.maxHeight;
content.style.maxHeight = 'none';
content.style.height = 'auto';
// Force a reflow to ensure layout updates
void content.offsetHeight;
// Now measure the natural height with padding
const naturalHeight = content.scrollHeight;
console.log('Natural height (with padding):', naturalHeight);
// Calculate total height needed (add buffer for safety)
const buffer = 50; // Extra buffer to prevent cutting off content
const finalHeight = naturalHeight + buffer;
// Apply the max-height for smooth transition
content.style.maxHeight = finalHeight + 'px';
content.style.height = 'auto';
// Force another reflow to ensure layout updates and push elements down
void content.offsetHeight;
// After transition completes, ensure overflow is visible
setTimeout(function() {
if (content.classList.contains('expanded')) {
// Keep overflow hidden to prevent content from spilling out
// but allow enough height for all content
const actualHeight = content.scrollHeight;
if (actualHeight > finalHeight - buffer) {
content.style.maxHeight = (actualHeight + buffer) + 'px';
}
}
}, 350);
console.log('Applied maxHeight:', content.style.maxHeight);
console.log('Content visible:', content.style.opacity);
console.log('Children count:', content.children.length);
console.log('Final height:', finalHeight);
}
});
});
// Toggle category expansion and load articles - Using event delegation
// Reutilizar nav que ya fue declarado arriba
if (nav) {
nav.addEventListener('click', function(e) {
console.log('Click detected in nav:', e.target);
// First check if it's a category header or any child element
const categoryHeader = e.target.closest('.modern-kb-category-header');
if (categoryHeader && categoryHeader.dataset.categoryId) {
e.preventDefault();
e.stopPropagation();
const categoryId = categoryHeader.dataset.categoryId;
console.log('Category header clicked:', categoryId);
// Handle both regular categories and direct articles
let articlesList = document.getElementById('category-' + categoryId);
// If not found, try direct articles format
if (!articlesList && categoryId.startsWith('direct-')) {
const folderId = categoryId.replace('direct-', '');
articlesList = document.getElementById('category-direct-' + folderId);
}
if (articlesList) {
// Check current visibility state
const computedStyle = window.getComputedStyle(articlesList);
const isCurrentlyVisible = computedStyle.display !== 'none' && computedStyle.visibility !== 'hidden';
// Find parent folder content to recalculate height
const folderContent = articlesList.closest('.modern-kb-folder-content');
if (isCurrentlyVisible) {
// Hide
articlesList.style.setProperty('display', 'none', 'important');
articlesList.style.setProperty('visibility', 'hidden', 'important');
categoryHeader.classList.remove('active');
console.log('Category collapsed');
// Recalculate parent folder height after hiding articles
if (folderContent && folderContent.classList.contains('expanded')) {
setTimeout(function() {
const naturalHeight = folderContent.scrollHeight;
const buffer = 50;
folderContent.style.maxHeight = (naturalHeight + buffer) + 'px';
console.log('Recalculated folder height after collapse:', naturalHeight + buffer);
}, 50);
}
} else {
// Show
articlesList.style.setProperty('display', 'block', 'important');
articlesList.style.setProperty('visibility', 'visible', 'important');
articlesList.style.setProperty('opacity', '1', 'important');
categoryHeader.classList.add('active');
const articleCount = articlesList.querySelectorAll('.kb-article-link').length;
console.log('Category expanded, articles visible:', articleCount, 'articles');
// Recalculate parent folder height after showing articles
if (folderContent && folderContent.classList.contains('expanded')) {
setTimeout(function() {
const naturalHeight = folderContent.scrollHeight;
const buffer = 50;
folderContent.style.maxHeight = (naturalHeight + buffer) + 'px';
console.log('Recalculated folder height after expand:', naturalHeight + buffer);
}, 50);
}
}
} else {
console.error('Articles list not found for category:', categoryId);
}
return; // Exit early to prevent article link handler
}
// Then check if it's an article link
const articleLink = e.target.closest('.kb-article-link');
if (articleLink) {
e.preventDefault();
e.stopPropagation();
console.log('Article link clicked');
// Remove active class from all links and articles
document.querySelectorAll('.kb-article-link').forEach(function(l) {
l.classList.remove('active');
});
document.querySelectorAll('.kb-article-display').forEach(function(a) {
a.classList.remove('active');
});
// Add active class to clicked link
articleLink.classList.add('active');
const articleSlug = articleLink.dataset.articleSlug;
const articleId = articleLink.dataset.articleId;
console.log('Scrolling to article:', articleSlug);
if (!articleSlug) {
console.error('No article slug found');
return;
}
// Update URL without reload
const url = new URL(window.location);
url.searchParams.set('article', articleSlug);
window.history.pushState({article: articleSlug}, '', url);
// Find the article in the main content area
const articleElement = document.getElementById('article-' + articleSlug);
const contentArea = document.querySelector('.modern-kb-content');
if (articleElement && contentArea) {
// Add active class to highlight the article
articleElement.classList.add('active');
// Calculate scroll position (offset from top of content area)
const contentRect = contentArea.getBoundingClientRect();
const articleRect = articleElement.getBoundingClientRect();
const scrollTop = contentArea.scrollTop;
const articleTop = articleRect.top - contentRect.top + scrollTop;
// Scroll to article with smooth behavior
contentArea.scrollTo({
top: articleTop - 20, // 20px offset from top
behavior: 'smooth'
});
console.log('Scrolled to article:', articleSlug);
} else {
console.error('Article element not found:', 'article-' + articleSlug);
// If article not found, scroll to welcome screen
const welcomeScreen = document.getElementById('welcome-screen');
if (welcomeScreen && contentArea) {
contentArea.scrollTo({
top: 0,
behavior: 'smooth'
});
}
}
}
});
}
// Count article links after a short delay to ensure all are rendered
setTimeout(function() {
console.log('Found article links:', document.querySelectorAll('.kb-article-link').length);
console.log('Found category headers:', document.querySelectorAll('.modern-kb-category-header').length);
}, 500);
// Handle browser back/forward buttons
window.addEventListener('popstate', function(e) {
if (e.state && e.state.article) {
const link = document.querySelector('[data-article-slug="' + e.state.article + '"]');
if (link) {
link.click();
}
}
});
// Handle article selection from URL parameter
const urlParams = new URLSearchParams(window.location.search);
const articleParam = urlParams.get('article');
if (articleParam) {
setTimeout(function() {
const articleElement = document.getElementById('article-' + articleParam);
const activeLink = document.querySelector('[data-article-slug="' + articleParam + '"]');
const contentArea = document.querySelector('.modern-kb-content');
if (articleElement && contentArea) {
// Add active classes
if (activeLink) {
activeLink.classList.add('active');
}
articleElement.classList.add('active');
// Expand parent folder and category
if (activeLink) {
let articlesList = activeLink.closest('.modern-kb-articles-list');
if (articlesList) {
articlesList.style.setProperty('display', 'block', 'important');
let categoryHeader = articlesList.previousElementSibling;
if (categoryHeader && categoryHeader.classList.contains('modern-kb-category-header')) {
categoryHeader.classList.add('active');
// Expand parent folder
let folderContent = categoryHeader.closest('.modern-kb-folder-content');
if (folderContent) {
let folderHeader = folderContent.previousElementSibling;
if (folderHeader && folderHeader.classList.contains('modern-kb-folder-header')) {
folderHeader.classList.add('active');
folderContent.classList.add('expanded');
folderContent.style.maxHeight = '9999px';
folderContent.style.opacity = '1';
folderContent.style.overflow = 'visible';
folderContent.style.paddingTop = '10px';
folderContent.style.paddingBottom = '10px';
}
}
}
}
}
// Scroll to article
const contentRect = contentArea.getBoundingClientRect();
const articleRect = articleElement.getBoundingClientRect();
const scrollTop = contentArea.scrollTop;
const articleTop = articleRect.top - contentRect.top + scrollTop;
contentArea.scrollTo({
top: articleTop - 20,
behavior: 'smooth'
});
}
}, 300);
}
// Auto-expand selected topic folder and category, then scroll
{% if selectedTopic %}
setTimeout(function() {
const topicId = {{ selectedTopic.id }};
const firstCategoryId = {{ selectedTopic.firstCategoryId ?: 'null' }};
// Hide welcome screen
const welcomeScreen = document.getElementById('welcome-screen');
if (welcomeScreen) {
welcomeScreen.style.display = 'none';
}
// Find and expand the folder (solution)
const folderHeader = document.querySelector('.modern-kb-folder-header[data-folder-id="' + topicId + '"]');
if (folderHeader) {
const folderContent = document.getElementById('folder-' + topicId);
if (folderContent) {
// Expand folder
folderHeader.classList.add('active');
folderContent.classList.add('expanded');
folderContent.style.opacity = '1';
folderContent.style.paddingTop = '10px';
folderContent.style.paddingBottom = '10px';
folderContent.style.overflow = 'hidden';
// Calculate natural height
folderContent.style.maxHeight = 'none';
const naturalHeight = folderContent.scrollHeight;
const buffer = 50;
folderContent.style.maxHeight = (naturalHeight + buffer) + 'px';
console.log('Expanded folder:', topicId);
// If there's a first category, expand it too
if (firstCategoryId) {
setTimeout(function() {
const categoryHeader = document.querySelector('.modern-kb-category-header[data-category-id="' + firstCategoryId + '"]');
const articlesList = document.getElementById('category-' + firstCategoryId);
if (categoryHeader && articlesList) {
articlesList.style.setProperty('display', 'block', 'important');
articlesList.style.setProperty('visibility', 'visible', 'important');
articlesList.style.setProperty('opacity', '1', 'important');
categoryHeader.classList.add('active');
console.log('Expanded category:', firstCategoryId);
// Scroll to the category section in main content
setTimeout(function() {
const categorySection = document.querySelector('.kb-category-content-section[data-category-id="' + firstCategoryId + '"]');
const contentArea = document.querySelector('.modern-kb-content');
if (categorySection && contentArea) {
// Wait a bit more for layout to settle
setTimeout(function() {
const contentRect = contentArea.getBoundingClientRect();
const sectionRect = categorySection.getBoundingClientRect();
const scrollTop = contentArea.scrollTop;
const sectionTop = sectionRect.top - contentRect.top + scrollTop;
contentArea.scrollTo({
top: Math.max(0, sectionTop - 120), // 120px offset from top
behavior: 'smooth'
});
console.log('Scrolled to category section');
}, 100);
}
}, 400);
}
}, 300);
} else {
// If no category, scroll to the folder section
setTimeout(function() {
const folderSection = document.querySelector('.kb-folder-content-section[data-folder-id="' + topicId + '"]');
const contentArea = document.querySelector('.modern-kb-content');
if (folderSection && contentArea) {
setTimeout(function() {
const contentRect = contentArea.getBoundingClientRect();
const sectionRect = folderSection.getBoundingClientRect();
const scrollTop = contentArea.scrollTop;
const sectionTop = sectionRect.top - contentRect.top + scrollTop;
contentArea.scrollTo({
top: Math.max(0, sectionTop - 120),
behavior: 'smooth'
});
console.log('Scrolled to folder section');
}, 100);
}
}, 300);
}
}
} else {
console.warn('Folder not found for topic ID:', topicId);
}
}, 500);
{% elseif not selectedArticle %}
// Auto-expand first folder on load if no article is selected and no topic
const firstFolder = document.querySelector('.modern-kb-folder-header');
if (firstFolder) {
setTimeout(function() {
firstFolder.click();
}, 100);
}
{% endif %}
console.log('Knowledge Base initialized successfully');
}
// Search functionality
function initializeSearch() {
const searchInput = document.getElementById('kb-search-input');
const searchClear = document.getElementById('kb-search-clear');
const resultsCount = document.getElementById('kb-search-results-count');
const resultsNumber = document.getElementById('kb-results-number');
if (!searchInput) return;
// Function to highlight text
function highlightText(text, searchTerm) {
if (!searchTerm) return text;
const regex = new RegExp(`(${searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
return text.replace(regex, '<span class="kb-search-highlight">$1</span>');
}
// Function to perform search
function performSearch(searchTerm) {
const term = searchTerm.trim().toLowerCase();
let matchCount = 0;
// Show/hide clear button
if (term.length > 0) {
searchClear.style.display = 'block';
resultsCount.style.display = 'block';
} else {
searchClear.style.display = 'none';
resultsCount.style.display = 'none';
}
if (term.length === 0) {
// Reset all - show everything
document.querySelectorAll('.modern-kb-folder-section').forEach(function(section) {
section.classList.remove('hidden-by-search', 'visible-by-search');
});
document.querySelectorAll('.modern-kb-category-section').forEach(function(section) {
section.classList.remove('hidden-by-search', 'visible-by-search');
});
document.querySelectorAll('.modern-kb-article-item').forEach(function(item) {
item.classList.remove('hidden-by-search');
const link = item.querySelector('.kb-article-link');
if (link && link.dataset.originalText) {
link.innerHTML = link.dataset.originalText; // Restore original text
}
});
document.querySelectorAll('.kb-article-display').forEach(function(article) {
article.classList.remove('hidden-by-search');
const header = article.querySelector('.kb-article-header h1');
if (header) {
// Store original text if not already stored
if (!header.dataset.originalText) {
header.dataset.originalText = header.textContent;
}
header.innerHTML = header.dataset.originalText; // Restore original text
}
});
// Restore category names
document.querySelectorAll('.category-name').forEach(function(catName) {
if (catName.dataset.originalText) {
catName.innerHTML = catName.dataset.originalText;
}
});
resultsNumber.textContent = '0';
return;
}
// First, search in category names (sidebar)
const categoryMatches = new Set();
document.querySelectorAll('.category-name').forEach(function(catName) {
// Store original text if not already stored
if (!catName.dataset.originalText) {
catName.dataset.originalText = catName.textContent;
}
const originalText = catName.dataset.originalText;
const categoryName = originalText.toLowerCase();
if (categoryName.includes(term)) {
// Category name matches - mark it and show all its articles
const catSection = catName.closest('.modern-kb-category-section');
if (catSection) {
categoryMatches.add(catSection);
// Show all articles in this category
const articles = catSection.querySelectorAll('.modern-kb-article-item');
articles.forEach(function(article) {
article.classList.remove('hidden-by-search');
});
matchCount += articles.length;
}
// Highlight the matching text in the category name
catName.innerHTML = highlightText(originalText, searchTerm);
} else {
// Restore original text without highlights
catName.innerHTML = originalText;
}
});
// Search in sidebar article links
document.querySelectorAll('.kb-article-link').forEach(function(link) {
// Store original text if not already stored
if (!link.dataset.originalText) {
link.dataset.originalText = link.textContent;
}
const originalText = link.dataset.originalText;
const articleName = originalText.toLowerCase();
const articleItem = link.closest('.modern-kb-article-item');
// Check if this article is in a matching category
const catSection = articleItem.closest('.modern-kb-category-section');
const isInMatchingCategory = catSection && categoryMatches.has(catSection);
if (articleName.includes(term) || isInMatchingCategory) {
articleItem.classList.remove('hidden-by-search');
if (!isInMatchingCategory) {
matchCount++;
}
// Highlight the matching text in the link
if (articleName.includes(term)) {
link.innerHTML = highlightText(originalText, searchTerm);
} else {
link.innerHTML = originalText;
}
} else {
articleItem.classList.add('hidden-by-search');
// Restore original text without highlights
link.innerHTML = originalText;
}
});
// Search in main content articles
document.querySelectorAll('.kb-article-display').forEach(function(article) {
const header = article.querySelector('.kb-article-header h1');
const body = article.querySelector('.kb-article-body');
// Store original header text if not already stored
if (header && !header.dataset.originalText) {
header.dataset.originalText = header.textContent;
}
const originalHeaderText = header && header.dataset.originalText ? header.dataset.originalText : '';
const articleName = originalHeaderText.toLowerCase();
const articleContent = body ? body.textContent.toLowerCase() : '';
if (articleName.includes(term) || articleContent.includes(term)) {
article.classList.remove('hidden-by-search');
matchCount++;
// Highlight in header
if (header && articleName.includes(term)) {
header.innerHTML = highlightText(originalHeaderText, searchTerm);
} else if (header) {
// Restore original if no match in header
header.innerHTML = originalHeaderText;
}
} else {
article.classList.add('hidden-by-search');
// Restore original header text
if (header && header.dataset.originalText) {
header.innerHTML = header.dataset.originalText;
}
}
});
// Show/hide folders and categories based on matches
document.querySelectorAll('.modern-kb-folder-section').forEach(function(folderSection) {
const folderContent = folderSection.querySelector('.modern-kb-folder-content');
const visibleArticles = folderContent ?
folderContent.querySelectorAll('.modern-kb-article-item:not(.hidden-by-search)') : [];
// Check if category has visible articles or matching category names
let hasVisibleContent = visibleArticles.length > 0;
if (!hasVisibleContent && folderContent) {
folderContent.querySelectorAll('.modern-kb-category-section').forEach(function(catSection) {
const catArticles = catSection.querySelectorAll('.modern-kb-article-item:not(.hidden-by-search)');
const catName = catSection.querySelector('.category-name');
const categoryNameMatches = catName && catName.textContent.toLowerCase().includes(term);
if (catArticles.length > 0 || categoryNameMatches) {
hasVisibleContent = true;
}
});
}
if (hasVisibleContent) {
folderSection.classList.remove('hidden-by-search');
folderSection.classList.add('visible-by-search');
// Auto-expand folder if it has matches
if (folderContent && !folderContent.classList.contains('expanded')) {
const folderHeader = folderSection.querySelector('.modern-kb-folder-header');
if (folderHeader) {
folderHeader.classList.add('active');
folderContent.classList.add('expanded');
folderContent.style.maxHeight = '9999px';
folderContent.style.opacity = '1';
folderContent.style.paddingTop = '10px';
folderContent.style.paddingBottom = '10px';
}
}
} else {
folderSection.classList.add('hidden-by-search');
folderSection.classList.remove('visible-by-search');
}
});
// Show/hide categories based on matches
document.querySelectorAll('.modern-kb-category-section').forEach(function(catSection) {
const visibleArticles = catSection.querySelectorAll('.modern-kb-article-item:not(.hidden-by-search)');
const catName = catSection.querySelector('.category-name');
const categoryNameMatches = catName && catName.textContent.toLowerCase().includes(term);
if (visibleArticles.length > 0 || categoryNameMatches) {
catSection.classList.remove('hidden-by-search');
catSection.classList.add('visible-by-search');
// Auto-expand category and show articles list
const articlesList = catSection.querySelector('.modern-kb-articles-list');
const categoryHeader = catSection.querySelector('.modern-kb-category-header');
if (articlesList && categoryHeader) {
articlesList.style.setProperty('display', 'block', 'important');
articlesList.style.setProperty('visibility', 'visible', 'important');
articlesList.style.setProperty('opacity', '1', 'important');
categoryHeader.classList.add('active');
}
} else {
catSection.classList.add('hidden-by-search');
catSection.classList.remove('visible-by-search');
}
});
// Update results count
resultsNumber.textContent = matchCount;
}
// Event listeners
let searchTimeout;
searchInput.addEventListener('input', function(e) {
clearTimeout(searchTimeout);
const term = e.target.value;
// Debounce search for better performance
searchTimeout = setTimeout(function() {
performSearch(term);
}, 300);
});
searchInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
clearTimeout(searchTimeout);
performSearch(e.target.value);
}
});
searchClear.addEventListener('click', function() {
searchInput.value = '';
searchInput.focus();
performSearch('');
});
// Initial search if URL has search parameter
const urlParams = new URLSearchParams(window.location.search);
const searchParam = urlParams.get('search');
if (searchParam) {
searchInput.value = searchParam;
performSearch(searchParam);
}
}
// Content Search functionality
function initializeContentSearch() {
const contentSearchInput = document.getElementById('kb-content-search-input');
const contentSearchClear = document.getElementById('kb-content-search-clear');
const contentResultsCount = document.getElementById('kb-content-search-results-count');
const contentResultsNumber = document.getElementById('kb-content-results-number');
const welcomeScreen = document.getElementById('welcome-screen');
if (!contentSearchInput) return;
// Function to highlight text in content
function highlightContentText(text, searchTerm) {
if (!searchTerm) return text;
const regex = new RegExp(`(${searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
return text.replace(regex, '<mark class="kb-content-search-highlight">$1</mark>');
}
// Function to perform content search
function performContentSearch(searchTerm) {
const term = searchTerm.trim().toLowerCase();
let matchCount = 0;
// Show/hide clear button and results count
if (term.length > 0) {
contentSearchClear.style.display = 'block';
contentResultsCount.style.display = 'block';
} else {
contentSearchClear.style.display = 'none';
contentResultsCount.style.display = 'none';
}
if (term.length === 0) {
// Reset all - show everything including welcome screen
document.querySelectorAll('.kb-article-display').forEach(function(article) {
article.classList.remove('hidden-by-content-search');
const header = article.querySelector('.kb-article-header h1');
if (header && header.dataset.originalText) {
header.innerHTML = header.dataset.originalText;
}
// Remove highlights from body content
const body = article.querySelector('.kb-article-body');
if (body && body.dataset.originalHtml) {
body.innerHTML = body.dataset.originalHtml;
}
});
document.querySelectorAll('.kb-folder-content-section').forEach(function(section) {
section.classList.remove('hidden-by-content-search');
});
document.querySelectorAll('.kb-category-content-section').forEach(function(section) {
section.classList.remove('hidden-by-content-search');
});
// Restore category titles
document.querySelectorAll('.kb-category-title').forEach(function(catTitle) {
if (catTitle.dataset.originalText) {
catTitle.innerHTML = catTitle.dataset.originalText;
}
});
// Show welcome screen
if (welcomeScreen) {
welcomeScreen.classList.remove('hidden-by-content-search');
}
contentResultsNumber.textContent = '0';
return;
}
// Hide welcome screen when searching
if (welcomeScreen) {
welcomeScreen.classList.add('hidden-by-content-search');
}
// First, search in category titles (main content)
const categoryContentMatches = new Set();
document.querySelectorAll('.kb-category-title').forEach(function(catTitle) {
// Store original text if not already stored
if (!catTitle.dataset.originalText) {
catTitle.dataset.originalText = catTitle.textContent;
}
const originalText = catTitle.dataset.originalText;
const categoryName = originalText.toLowerCase();
if (categoryName.includes(term)) {
// Category name matches - mark it and show all its articles
const catSection = catTitle.closest('.kb-category-content-section');
if (catSection) {
categoryContentMatches.add(catSection);
// Show all articles in this category
const articles = catSection.querySelectorAll('.kb-article-display');
articles.forEach(function(article) {
article.classList.remove('hidden-by-content-search');
});
matchCount += articles.length;
}
// Highlight the matching text in the category title
catTitle.innerHTML = highlightContentText(originalText, searchTerm);
} else {
// Restore original text without highlights
catTitle.innerHTML = originalText;
}
});
// Search in all articles in the main content
document.querySelectorAll('.kb-article-display').forEach(function(article) {
const header = article.querySelector('.kb-article-header h1');
const body = article.querySelector('.kb-article-body');
// Check if this article is in a matching category
const catSection = article.closest('.kb-category-content-section');
const isInMatchingCategory = catSection && categoryContentMatches.has(catSection);
// Store original header text if not already stored
if (header && !header.dataset.originalText) {
header.dataset.originalText = header.textContent;
}
// Store original body HTML if not already stored
if (body && !body.dataset.originalHtml) {
body.dataset.originalHtml = body.innerHTML;
}
const originalHeaderText = header && header.dataset.originalText ? header.dataset.originalText : '';
const articleName = originalHeaderText.toLowerCase();
const articleContent = body ? body.textContent.toLowerCase() : '';
if (articleName.includes(term) || articleContent.includes(term) || isInMatchingCategory) {
article.classList.remove('hidden-by-content-search');
if (!isInMatchingCategory) {
matchCount++;
}
// Highlight in header if matches
if (header && articleName.includes(term)) {
header.innerHTML = highlightContentText(originalHeaderText, searchTerm);
} else if (header) {
header.innerHTML = originalHeaderText;
}
// Highlight in body content if matches
if (body && articleContent.includes(term) && body.dataset.originalHtml) {
// Simple approach: highlight text in HTML while preserving structure
// Escape special regex characters in search term
const escapedTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(`(${escapedTerm})`, 'gi');
// Replace text nodes only (preserve HTML tags)
// This is a simplified approach - replace in text content areas
let htmlContent = body.dataset.originalHtml;
// Only replace outside of HTML tags
htmlContent = htmlContent.replace(regex, function(match) {
// Check if we're inside an HTML tag
return '<mark class="kb-content-search-highlight">' + match + '</mark>';
});
body.innerHTML = htmlContent;
} else if (body && body.dataset.originalHtml) {
body.innerHTML = body.dataset.originalHtml;
}
} else {
article.classList.add('hidden-by-content-search');
// Restore original content
if (header && header.dataset.originalText) {
header.innerHTML = header.dataset.originalText;
}
if (body && body.dataset.originalHtml) {
body.innerHTML = body.dataset.originalHtml;
}
}
});
// Hide/show folder and category sections based on visible articles or matching category names
document.querySelectorAll('.kb-folder-content-section').forEach(function(folderSection) {
const visibleArticles = folderSection.querySelectorAll('.kb-article-display:not(.hidden-by-content-search)');
const matchingCategories = folderSection.querySelectorAll('.kb-category-title');
let hasMatchingCategory = false;
matchingCategories.forEach(function(catTitle) {
if (catTitle.textContent.toLowerCase().includes(term)) {
hasMatchingCategory = true;
}
});
if (visibleArticles.length > 0 || hasMatchingCategory) {
folderSection.classList.remove('hidden-by-content-search');
} else {
folderSection.classList.add('hidden-by-content-search');
}
});
document.querySelectorAll('.kb-category-content-section').forEach(function(catSection) {
const visibleArticles = catSection.querySelectorAll('.kb-article-display:not(.hidden-by-content-search)');
const catTitle = catSection.querySelector('.kb-category-title');
const categoryNameMatches = catTitle && catTitle.textContent.toLowerCase().includes(term);
if (visibleArticles.length > 0 || categoryNameMatches) {
catSection.classList.remove('hidden-by-content-search');
} else {
catSection.classList.add('hidden-by-content-search');
}
});
// Update results count
contentResultsNumber.textContent = matchCount;
}
// Event listeners
let contentSearchTimeout;
contentSearchInput.addEventListener('input', function(e) {
clearTimeout(contentSearchTimeout);
const term = e.target.value;
// Debounce search for better performance
contentSearchTimeout = setTimeout(function() {
performContentSearch(term);
}, 300);
});
contentSearchInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
clearTimeout(contentSearchTimeout);
performContentSearch(e.target.value);
}
});
contentSearchClear.addEventListener('click', function() {
contentSearchInput.value = '';
contentSearchInput.focus();
performContentSearch('');
});
}
// Initialize when DOM is ready (only once)
let initialized = false;
function initOnce() {
if (!initialized) {
initialized = true;
initializeKB();
initializeSearch();
initializeContentSearch();
// Scroll to container if topic is selected (after everything is initialized)
{% if selectedTopic %}
setTimeout(function() {
const kbContainer = document.getElementById('modern-kb-container');
if (kbContainer) {
const containerRect = kbContainer.getBoundingClientRect();
const scrollY = window.scrollY || window.pageYOffset;
const containerTop = containerRect.top + scrollY;
window.scrollTo({
top: Math.max(0, containerTop - 20), // 20px offset from top
behavior: 'smooth'
});
console.log('Scrolled page to modern-kb-container');
}
}, 800); // Wait a bit more for all content to render
{% endif %}
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initOnce);
} else {
// DOM is already ready
initOnce();
}
// Also try with jQuery if available (for compatibility)
if (typeof jQuery !== 'undefined') {
jQuery(document).ready(function($) {
console.log('jQuery ready, initializing...');
initOnce();
});
}
// Also scroll on window load (after all resources are loaded)
{% if selectedTopic %}
window.addEventListener('load', function() {
setTimeout(function() {
const kbContainer = document.getElementById('modern-kb-container');
if (kbContainer) {
const containerRect = kbContainer.getBoundingClientRect();
const scrollY = window.scrollY || window.pageYOffset;
const containerTop = containerRect.top + scrollY;
window.scrollTo({
top: Math.max(0, containerTop - 20),
behavior: 'smooth'
});
}
}, 200);
});
{% endif %}
})();
</script>
{% endblock %}