| Server IP : 127.0.0.1 / Your IP : 216.73.216.109 Web Server : Apache/2.4.54 (Win64) OpenSSL/1.1.1q PHP/8.1.10 System : Windows NT DESKTOP-E5T4RUN 10.0 build 19045 (Windows 10) AMD64 User : SERVERWEB ( 0) PHP Version : 8.1.10 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : C:/laragon/www/sections/ |
Upload File : |
<?php
require_once __DIR__ . '/../app/apps-catalog.php';
$data = gamq_apps_catalog();
$apps = $data['apps'];
$categorias = $data['categorias'];
$estados = $data['estados'];
$tempFilter = isset($_POST['tempFilter']) ? $_POST['tempFilter'] : "all";
?>
<div class="column one">
<div class="categories">
<?php foreach ($categorias as $key => $label): ?>
<div class="category-btn <?= $key === $tempFilter ? 'active' : '' ?>" data-category="<?= $key ?>">
<?= $label ?>
</div>
<?php endforeach; ?>
</div>
<div class="search-bar">
<input type="text" class="search-input" placeholder="Buscar aplicación..." id="searchInput">
</div>
<div class="mobile-filter-row">
<select class="mobile-filter-select" id="mobileFilterSelect">
<?php foreach ($categorias as $key => $label): ?>
<option value="<?= $key ?>" <?= $key === $tempFilter ? 'selected' : '' ?>>
<?= $label ?>
</option>
<?php endforeach; ?>
</select>
<input type="text" class="mobile-search-input" placeholder="Buscar..." id="mobileSearchInput">
</div>
<div class="loading" id="loading">Cargando aplicaciones...</div>
<div class="no-results" id="noResults">No se encontraron aplicaciones que coincidan con tu búsqueda.</div>
<div class="apps-grid">
<?php foreach ($apps as $app): ?>
<div class="app-card <?= !empty($app['url']) ? 'clickable' : '' ?>"
data-category="<?= implode(',', $app['categorias']) ?>"
<?= !empty($app['url']) ? 'data-url="' . gamq_public_app_url($app['id']) . '"' : '' ?>>
<div class="app-header">
<div class="app-logo-container">
<img src="<?= $app['logo'] ?>" alt="<?= $app['nombre'] ?> Logo" loading="lazy">
</div>
<div class="app-title"><?= $app['nombre'] ?></div>
<div class="app-status <?= $estados[$app['estado']]['class'] ?>">
<?= $estados[$app['estado']]['label'] ?>
</div>
</div>
<div class="app-description"><?= $app['descripcion_larga'] ?></div>
</div>
<?php endforeach; ?>
</div>
<div class="mobile-apps-list">
<?php foreach ($apps as $app): ?>
<div class="mobile-app-item <?= !empty($app['url']) ? 'clickable' : '' ?>"
data-category="<?= implode(',', $app['categorias']) ?>"
<?= !empty($app['url']) ? 'data-url="' . gamq_public_app_url($app['id']) . '"' : '' ?>>
<div class="mobile-app-icon">
<img src="<?= $app['logo'] ?>" alt="<?= $app['nombre'] ?>" loading="lazy">
</div>
<div class="mobile-app-content">
<div class="mobile-app-title"><?= $app['nombre'] ?></div>
<div class="mobile-app-status <?= $estados[$app['estado']]['class'] ?>">
<?= $estados[$app['estado']]['label'] ?>
</div>
<div class="mobile-app-desc"><?= $app['descripcion'] ?></div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<script>
(() => {
const defaultCategory = <?= json_encode($tempFilter) ?>;
let currentCategory = defaultCategory || 'all';
let currentSearchTerm = '';
function isMobileView() {
return window.innerWidth <= 576;
}
function filterApps(category, searchTerm) {
category = category || 'all';
searchTerm = (searchTerm || '').toLowerCase();
// Update global state
currentCategory = category;
currentSearchTerm = searchTerm;
let visibleCount = 0;
// Filter mobile apps list
const mobileItems = document.querySelectorAll('.mobile-app-item');
mobileItems.forEach(item => {
const cardCategories = (item.dataset.category || '').split(',');
const title = (item.querySelector('.mobile-app-title')?.textContent || '').toLowerCase();
const description = (item.querySelector('.mobile-app-desc')?.textContent || '').toLowerCase();
const categoryMatch = category === 'all' || cardCategories.includes(category);
const searchMatch = !searchTerm || title.includes(searchTerm) || description.includes(searchTerm);
const isVisible = categoryMatch && searchMatch;
item.style.display = isVisible ? 'flex' : 'none';
if (isVisible) visibleCount++;
});
// Filter desktop grid
const desktopCards = document.querySelectorAll('.app-card');
desktopCards.forEach(card => {
const cardCategories = (card.dataset.category || '').split(',');
const title = (card.querySelector('.app-title')?.textContent || '').toLowerCase();
const description = (card.querySelector('.app-description')?.textContent || '').toLowerCase();
const categoryMatch = category === 'all' || cardCategories.includes(category);
const searchMatch = !searchTerm || title.includes(searchTerm) || description.includes(searchTerm);
const isVisible = categoryMatch && searchMatch;
card.style.display = isVisible ? 'flex' : 'none';
});
// Show/hide no results message
const noResults = document.getElementById('noResults');
if (noResults) {
noResults.style.display = visibleCount === 0 ? 'block' : 'none';
}
}
function syncFilters() {
if (isMobileView()) {
// Sync desktop category to mobile dropdown
const activeDesktopBtn = document.querySelector('.category-btn.active');
const mobileSelect = document.getElementById('mobileFilterSelect');
if (activeDesktopBtn && mobileSelect) {
mobileSelect.value = activeDesktopBtn.dataset.category;
}
// Sync desktop search to mobile search
const desktopSearch = document.getElementById('searchInput');
const mobileSearch = document.getElementById('mobileSearchInput');
if (desktopSearch && mobileSearch) {
mobileSearch.value = desktopSearch.value;
}
} else {
// Sync mobile dropdown to desktop category
const mobileSelect = document.getElementById('mobileFilterSelect');
if (mobileSelect) {
document.querySelectorAll('.category-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.category === mobileSelect.value);
});
}
// Sync mobile search to desktop search
const mobileSearch = document.getElementById('mobileSearchInput');
const desktopSearch = document.getElementById('searchInput');
if (mobileSearch && desktopSearch) {
desktopSearch.value = mobileSearch.value;
}
}
}
function getCurrentFilters() {
if (isMobileView()) {
const mobileSelect = document.getElementById('mobileFilterSelect');
const mobileSearch = document.getElementById('mobileSearchInput');
return {
category: mobileSelect ? mobileSelect.value : currentCategory,
searchTerm: mobileSearch ? mobileSearch.value : ''
};
} else {
const activeBtn = document.querySelector('.category-btn.active');
const desktopSearch = document.getElementById('searchInput');
return {
category: activeBtn ? activeBtn.dataset.category : currentCategory,
searchTerm: desktopSearch ? desktopSearch.value : ''
};
}
}
function initializeAppsFiltering() {
const appsContainer = document.getElementById('apps');
const desktopSearch = document.getElementById('searchInput');
const mobileSelect = document.getElementById('mobileFilterSelect');
const mobileSearch = document.getElementById('mobileSearchInput');
if (mobileSelect) {
mobileSelect.value = currentCategory;
}
document.querySelectorAll('.category-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.category === currentCategory);
});
if (appsContainer?.dataset.appsFilteringInitialized === 'true') {
filterApps(currentCategory, currentSearchTerm);
return;
}
if (appsContainer) {
appsContainer.dataset.appsFilteringInitialized = 'true';
}
// Desktop category buttons
document.querySelectorAll('.category-btn').forEach(btn => {
btn.addEventListener('click', function() {
document.querySelectorAll('.category-btn').forEach(b => b.classList.remove('active'));
this.classList.add('active');
currentCategory = this.dataset.category;
if (mobileSelect) {
mobileSelect.value = currentCategory;
}
const filters = getCurrentFilters();
filterApps(filters.category, filters.searchTerm);
});
});
// Desktop search
if (desktopSearch) {
desktopSearch.addEventListener('input', function() {
currentSearchTerm = this.value;
if (mobileSearch) {
mobileSearch.value = currentSearchTerm;
}
const filters = getCurrentFilters();
filterApps(filters.category, filters.searchTerm);
});
}
// Mobile category dropdown
if (mobileSelect) {
mobileSelect.addEventListener('change', function() {
currentCategory = this.value;
document.querySelectorAll('.category-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.category === currentCategory);
});
const filters = getCurrentFilters();
filterApps(filters.category, filters.searchTerm);
});
}
// Mobile search
if (mobileSearch) {
mobileSearch.addEventListener('input', function() {
currentSearchTerm = this.value;
if (desktopSearch) {
desktopSearch.value = currentSearchTerm;
}
const filters = getCurrentFilters();
filterApps(filters.category, filters.searchTerm);
});
}
// Handle window resize
window.addEventListener('resize', function() {
syncFilters();
filterApps(currentCategory, currentSearchTerm);
});
// Clickable cards
document.querySelectorAll('.app-card.clickable, .mobile-app-item.clickable').forEach(card => {
card.addEventListener('click', function() {
if (this.dataset.url) {
window.open(this.dataset.url, '_blank');
}
});
});
// Initial filter application
filterApps(currentCategory, currentSearchTerm);
}
window.initializeAppsFiltering = initializeAppsFiltering;
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeAppsFiltering);
} else {
initializeAppsFiltering();
}
})();
</script>
<style>
.apps-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 10px;
margin-bottom: 1rem;
}
.app-card {
border-radius: 12px;
overflow: hidden;
display: flex;
flex-direction: column;
background: rgba(255, 255, 255, 0.95);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
backdrop-filter: blur(10px);
text-align: center;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
height: fit-content;
position: relative;
}
.app-card::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
transition: left 0.5s;
}
.app-card:hover::before {
left: 100%;
}
.app-card:hover {
transform: translateY(-5px) scale(1.01);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
}
.app-card:hover .app-description {
max-height: 100px;
}
.app-card.clickable {
cursor: pointer;
}
.app-card.clickable:active {
transform: translateY(-5px) scale(0.98);
}
.app-header {
padding: 10px;
text-align: center;
border-bottom: 1px solid #f0f0f0;
}
.app-logo-container {
width: 60px;
height: 60px;
margin: 0 auto 15px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
padding: 10px;
background: rgba(255, 255, 255, 0.8);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.app-logo-container img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.app-title {
font-size: 1rem;
font-weight: 600;
color: #2d3748;
margin-bottom: 0.4rem;
}
.app-description {
font-size: 13px;
color: #666;
line-height: 1.5;
margin: 0 1rem 1rem 1rem;
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.app-status {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.status-active {
background: #c6f6d5;
color: #22543d;
}
.status-development {
background: #fed7d7;
color: #742a2a;
}
.status-stoped {
background:#f6efc6 ;
color: #742a2a;
}
.categories {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 10px;
margin: 5px 0 0px 0px;
}
.category-btn {
padding: 8px 16px;
background: #f0f2f5;
border-radius: 20px;
color: #333;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;
border: 1px solid #ddd;
}
.category-btn:hover, .category-btn.active {
background: #2c3e50;
color: white;
border-color: #2c3e50;
}
.mobile-filter-row {
display: none;
margin: 15px 0;
gap: 10px;
align-items: center;
}
.mobile-filter-select {
flex: 1;
padding: 12px;
border-radius: 8px;
border: 1px solid #ddd;
font-size: 16px;
min-width: 120px;
}
.mobile-search-input {
flex: 2;
padding: 12px;
border: 1px solid #ddd;
border-radius: 25px;
font-size: 16px;
outline: none;
transition: all 0.3s ease;
}
.mobile-search-input:focus {
border-color: #2c3e50;
box-shadow: 0 0 0 3px rgba(44, 62, 80, 0.1);
}
.search-bar {
margin: 20px 0;
text-align: center;
}
.search-input {
padding: 12px 20px;
width: 100%;
max-width: 500px;
border: 1px solid #ddd;
border-radius: 25px;
font-size: 16px;
outline: none;
transition: all 0.3s ease;
}
.search-input:focus {
border-color: #2c3e50;
box-shadow: 0 0 0 3px rgba(44, 62, 80, 0.1);
}
/* Mobile List View */
.mobile-apps-list {
display: none;
}
.mobile-app-item {
display: flex;
align-items: center;
padding: 15px;
background: white;
border-radius: 8px;
margin-bottom: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transition: all 0.2s ease;
}
.mobile-app-item:hover {
transform: translateX(5px);
}
.mobile-app-icon {
width: 40px;
height: 40px;
margin-right: 15px;
flex-shrink: 0;
}
.mobile-app-icon img {
width: 100%;
height: 100%;
object-fit: contain;
}
.mobile-app-content {
flex: 1;
}
.mobile-app-title {
font-size: 15px;
font-weight: 600;
color: #2c3e50;
margin-bottom: 3px;
}
.mobile-app-status {
display: inline-block;
padding: 2px 8px;
border-radius: 10px;
font-size: 10px;
font-weight: 500;
margin-bottom: 5px;
}
.loading, .no-results {
display: none;
text-align: center;
padding: 30px;
color: #666;
font-size: 16px;
}
@media (max-width: 992px) {
.apps-grid {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
}
@media (max-width: 768px) {
#gobierno-electronico .title {
font-size: 24px;
}
#gobierno-electronico .big {
font-size: 15px;
}
}
@media (max-width: 576px) {
#gobierno {
padding-top: 28px !important;
padding-bottom: 22px !important;
}
#gobierno .section_wrapper {
padding-left: 18px;
padding-right: 18px;
box-sizing: border-box;
}
#gobierno .column,
#gobierno .columns {
margin-bottom: 0;
}
#gobierno .fancy_heading {
margin-bottom: 18px;
}
#gobierno .fancy_heading .title {
font-size: 28px !important;
line-height: 1.15;
margin-bottom: 6px;
}
#gobierno .inside .big {
display: block;
font-size: 16px;
line-height: 1.35;
max-width: 300px;
margin: 0 auto;
}
.categories {
display: none;
}
.apps-grid {
display: none;
}
.mobile-apps-list {
display: block;
}
.search-input {
padding: 10px 15px;
font-size: 15px;
}
.search-bar {
display: none;
}
.mobile-filter-row {
display: flex !important;
flex-wrap: nowrap;
gap: 8px;
margin: 0 auto 18px;
max-width: 390px;
width: 100%;
}
.mobile-filter-select,
.mobile-search-input {
display: block;
min-width: 0;
height: 42px;
box-sizing: border-box;
border-radius: 8px;
font-size: 14px;
margin: 0 !important;
}
.mobile-filter-select {
flex: 0 0 43%;
width: 43% !important;
padding: 0 12px;
font-weight: 600;
}
.mobile-search-input {
flex: 1 1 auto;
width: auto !important;
padding: 0 14px;
}
.mobile-apps-list {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 20px 18px;
max-width: 420px;
margin: 0 auto;
}
.mobile-app-item {
align-items: center;
justify-content: flex-start;
flex-direction: column;
min-height: 118px;
padding: 0;
margin-bottom: 0;
background: transparent;
border-radius: 0;
box-shadow: none;
text-align: center;
}
.mobile-app-item:hover {
transform: translateY(-2px);
}
.mobile-app-icon {
width: 64px;
height: 64px;
margin: 0 0 8px;
padding: 0;
border-radius: 10px;
box-sizing: border-box;
}
.mobile-app-content {
width: 100%;
}
.mobile-app-title {
color: #ffffff;
font-size: 14px;
line-height: 1.25;
margin: 0 auto;
max-width: 145px;
overflow-wrap: anywhere;
}
.mobile-app-status,
.mobile-app-desc {
display: none;
}
}
@media (max-width: 360px) {
.mobile-filter-row {
gap: 6px;
max-width: 320px;
}
.mobile-filter-select {
flex-basis: 45%;
width: 45% !important;
padding-left: 8px;
padding-right: 8px;
}
.mobile-search-input {
padding-left: 10px;
padding-right: 10px;
}
.mobile-apps-list {
gap: 18px 12px;
}
.mobile-app-icon {
width: 58px;
height: 58px;
}
.mobile-app-title {
font-size: 13px;
}
}
</style>