interface improvements
This commit is contained in:
parent
fd65130bac
commit
fcab1485f5
@ -2,10 +2,16 @@
|
||||
import { getToolsData } from '../utils/dataService.js';
|
||||
|
||||
const data = await getToolsData();
|
||||
|
||||
const domains = data.domains;
|
||||
const phases = data.phases;
|
||||
|
||||
// Extract unique values dynamically - NO HARD-CODING
|
||||
const skillLevels = [...new Set(data.tools.map(tool => tool.skillLevel))].filter(Boolean).sort();
|
||||
const platforms = [...new Set(data.tools.flatMap(tool => tool.platforms || []))].filter(Boolean).sort();
|
||||
const licenses = [...new Set(data.tools.map(tool => tool.license))].filter(Boolean).sort();
|
||||
const toolTypes = [...new Set(data.tools.map(tool => tool.type))].filter(Boolean).sort();
|
||||
const accessTypes = [...new Set(data.tools.map(tool => tool.accessType))].filter(Boolean).sort();
|
||||
|
||||
const tagFrequency = data.tools.reduce((acc: Record<string, number>, tool: any) => {
|
||||
tool.tags.forEach((tag: string) => {
|
||||
acc[tag] = (acc[tag] || 0) + 1;
|
||||
@ -19,97 +25,243 @@ const sortedTags = Object.entries(tagFrequency)
|
||||
---
|
||||
|
||||
<div class="filters-container">
|
||||
<div style="margin-bottom: 1.5rem;">
|
||||
<input
|
||||
type="text"
|
||||
id="search-input"
|
||||
placeholder="Suchfeld: Name der Software, Beschreibung oder Tags..."
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="domain-phase-container" style="margin-bottom: 1.5rem;">
|
||||
<div class="domain-section">
|
||||
<label for="domain-select" style="display: block; margin-bottom: 0.5rem; font-weight: 500;">
|
||||
Forensische Domäne
|
||||
</label>
|
||||
<select id="domain-select" style="max-width: 300px;">
|
||||
<option value="">Alle Domänen</option>
|
||||
{domains.map((domain: any) => (
|
||||
<option value={domain.id}>{domain.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="phase-section">
|
||||
<label style="display: block; margin-bottom: 0.75rem; font-weight: 500;">
|
||||
Untersuchungsphase
|
||||
</label>
|
||||
<div class="phase-buttons">
|
||||
{phases.map((phase: any) => (
|
||||
<button
|
||||
class="phase-button"
|
||||
data-phase={phase.id}
|
||||
type="button"
|
||||
>
|
||||
{phase.name}
|
||||
</button>
|
||||
))}
|
||||
<!-- Search Section -->
|
||||
<div class="filter-section">
|
||||
<div class="filter-card-compact">
|
||||
<div class="filter-header-compact">
|
||||
<h3>🔍 Suche</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1.5rem;">
|
||||
<div class="checkbox-wrapper" style="margin-bottom: 1rem;">
|
||||
<input type="checkbox" id="include-proprietary" checked />
|
||||
<label for="include-proprietary">Proprietäre Software mit einschließen</label>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="tag-header">
|
||||
<label style="font-weight: 500;">
|
||||
Nach Tags filtern
|
||||
</label>
|
||||
<button
|
||||
id="tag-cloud-toggle"
|
||||
class="btn-tag-toggle"
|
||||
data-expanded="false"
|
||||
>
|
||||
Mehr zeigen
|
||||
<div class="search-wrapper">
|
||||
<div class="search-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="8"/>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="search-input"
|
||||
placeholder="Software, Beschreibung oder Tags durchsuchen..."
|
||||
class="search-input"
|
||||
/>
|
||||
<button id="clear-search" class="search-clear hidden" title="Suche löschen">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"/>
|
||||
<line x1="6" y1="6" x2="18" y2="18"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="tag-cloud" id="tag-cloud">
|
||||
{sortedTags.map((tag, index) => (
|
||||
<button
|
||||
class="tag-cloud-item"
|
||||
data-tag={tag}
|
||||
data-frequency={tagFrequency[tag]}
|
||||
data-index={index}
|
||||
>
|
||||
{tag}
|
||||
<span class="tag-frequency">({tagFrequency[tag]})</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Primary Filters Section - ONLY Domain and Phase -->
|
||||
<div class="filter-section">
|
||||
<div class="filter-card-compact">
|
||||
<div class="filter-header-compact">
|
||||
<h3>🎯 Primäre Filter</h3>
|
||||
<button class="filter-reset" id="reset-primary" title="Primäre Filter zurücksetzen">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="1 4 1 10 7 10"/>
|
||||
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="filter-grid-compact">
|
||||
<div class="filter-group">
|
||||
<label class="filter-label">Forensische Domäne</label>
|
||||
<select id="domain-select" class="filter-select">
|
||||
<option value="">Alle Domänen</option>
|
||||
{domains.map((domain: any) => (
|
||||
<option value={domain.id}>{domain.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="filter-group">
|
||||
<label class="filter-label">Untersuchungsphase</label>
|
||||
<select id="phase-select" class="filter-select">
|
||||
<option value="">Alle Phasen</option>
|
||||
{phases.map((phase: any) => (
|
||||
<option value={phase.id}>{phase.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 1rem; margin-bottom: 1.5rem; align-items: center;">
|
||||
<button class="btn btn-secondary view-toggle active h-12" data-view="grid">Kachelansicht</button>
|
||||
<button class="btn btn-secondary view-toggle h-12" data-view="matrix">Matrix-Ansicht</button>
|
||||
<!-- Advanced Filters Section -->
|
||||
<div class="filter-section">
|
||||
<div class="filter-card-compact">
|
||||
<div class="filter-header-compact">
|
||||
<h3>⚙️ Erweiterte Filter</h3>
|
||||
<button class="filter-reset" id="reset-advanced" title="Erweiterte Filter zurücksetzen">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="1 4 1 10 7 10"/>
|
||||
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
id="ai-view-toggle"
|
||||
class="btn btn-secondary view-toggle"
|
||||
data-view="ai"
|
||||
style="display: none; background-color: var(--color-accent); color: white; border-color: var(--color-accent);"
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; height:25px">
|
||||
<path d="M9 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2h-4"/>
|
||||
<path d="M9 11V7a3 3 0 0 1 6 0v4"/>
|
||||
</svg>
|
||||
Forensic-AI
|
||||
</button>
|
||||
<div class="advanced-filters-compact">
|
||||
<div class="filter-grid-compact">
|
||||
<div class="filter-group">
|
||||
<label class="filter-label">Tool-Typ</label>
|
||||
<select id="type-select" class="filter-select">
|
||||
<option value="">Alle Typen</option>
|
||||
{toolTypes.map((type: string) => (
|
||||
<option value={type}>{type}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="filter-group">
|
||||
<label class="filter-label">Skill Level</label>
|
||||
<select id="skill-select" class="filter-select">
|
||||
<option value="">Alle Level</option>
|
||||
{skillLevels.map((level: string) => (
|
||||
<option value={level}>{level}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="filter-group">
|
||||
<label class="filter-label">Plattform</label>
|
||||
<select id="platform-select" class="filter-select">
|
||||
<option value="">Alle Plattformen</option>
|
||||
{platforms.map((platform: string) => (
|
||||
<option value={platform}>{platform}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="filter-group">
|
||||
<label class="filter-label">Lizenztyp</label>
|
||||
<select id="license-select" class="filter-select">
|
||||
<option value="">Alle Lizenzen</option>
|
||||
{licenses.map((license: string) => (
|
||||
<option value={license}>{license}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="filter-group">
|
||||
<label class="filter-label">Zugangsart</label>
|
||||
<select id="access-select" class="filter-select">
|
||||
<option value="">Alle Zugangsarten</option>
|
||||
{accessTypes.map((access: string) => (
|
||||
<option value={access}>{access}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-toggles-compact">
|
||||
<label class="toggle-wrapper">
|
||||
<input type="checkbox" id="hosted-only" />
|
||||
<span class="toggle-label">🟣 Nur CC24-Server Tools</span>
|
||||
</label>
|
||||
|
||||
<label class="toggle-wrapper">
|
||||
<input type="checkbox" id="knowledgebase-only" />
|
||||
<span class="toggle-label">📖 Nur Tools mit Knowledgebase</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tag Filters Section -->
|
||||
<div class="filter-section">
|
||||
<div class="filter-card-compact">
|
||||
<div class="filter-header-compact">
|
||||
<h3>🏷️ Tag-Filter</h3>
|
||||
<div class="tag-controls">
|
||||
<button class="filter-reset" id="reset-tags" title="Tags zurücksetzen">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="1 4 1 10 7 10"/>
|
||||
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button id="tag-cloud-toggle" class="tag-toggle" data-expanded="false">
|
||||
Mehr zeigen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tag-section">
|
||||
<div class="selected-tags" id="selected-tags"></div>
|
||||
<div class="tag-cloud" id="tag-cloud">
|
||||
{sortedTags.map((tag, index) => (
|
||||
<button
|
||||
class="tag-cloud-item"
|
||||
data-tag={tag}
|
||||
data-frequency={tagFrequency[tag]}
|
||||
data-index={index}
|
||||
>
|
||||
{tag}
|
||||
<span class="tag-frequency">({tagFrequency[tag]})</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- View Controls Section -->
|
||||
<div class="filter-section">
|
||||
<div class="filter-card-compact">
|
||||
<div class="filter-header-compact">
|
||||
<h3>👁️ Ansicht</h3>
|
||||
<div class="results-count">
|
||||
<span id="results-counter">Alle Tools</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="view-controls-compact">
|
||||
<button class="view-toggle active" data-view="grid">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="3" width="7" height="7"/>
|
||||
<rect x="14" y="3" width="7" height="7"/>
|
||||
<rect x="14" y="14" width="7" height="7"/>
|
||||
<rect x="3" y="14" width="7" height="7"/>
|
||||
</svg>
|
||||
<span>Kacheln</span>
|
||||
</button>
|
||||
|
||||
<button class="view-toggle" data-view="matrix">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="3" width="4" height="4"/>
|
||||
<rect x="10" y="3" width="4" height="4"/>
|
||||
<rect x="17" y="3" width="4" height="4"/>
|
||||
<rect x="3" y="10" width="4" height="4"/>
|
||||
<rect x="10" y="10" width="4" height="4"/>
|
||||
<rect x="17" y="10" width="4" height="4"/>
|
||||
<rect x="3" y="17" width="4" height="4"/>
|
||||
<rect x="10" y="17" width="4" height="4"/>
|
||||
<rect x="17" y="17" width="4" height="4"/>
|
||||
</svg>
|
||||
<span>Matrix</span>
|
||||
</button>
|
||||
|
||||
<button id="ai-view-toggle" class="btn btn-accent view-toggle" data-view="ai" style="display: none;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M9 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2h-4"/>
|
||||
<path d="M9 11V7a3 3 0 0 1 6 0v4"/>
|
||||
</svg>
|
||||
<span>Forensic-AI</span>
|
||||
</button>
|
||||
|
||||
<button class="filter-reset-all" id="reset-all-filters">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="1 4 1 10 7 10"/>
|
||||
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/>
|
||||
</svg>
|
||||
<span>Alle Filter zurücksetzen</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -117,31 +269,55 @@ const sortedTags = Object.entries(tagFrequency)
|
||||
window.toolsData = toolsData;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Cache DOM elements once
|
||||
// Cache DOM elements
|
||||
const elements = {
|
||||
searchInput: document.getElementById('search-input'),
|
||||
clearSearch: document.getElementById('clear-search'),
|
||||
domainSelect: document.getElementById('domain-select'),
|
||||
phaseButtons: document.querySelectorAll('.phase-button'),
|
||||
proprietaryCheckbox: document.getElementById('include-proprietary'),
|
||||
phaseSelect: document.getElementById('phase-select'),
|
||||
typeSelect: document.getElementById('type-select'),
|
||||
skillSelect: document.getElementById('skill-select'),
|
||||
platformSelect: document.getElementById('platform-select'),
|
||||
licenseSelect: document.getElementById('license-select'),
|
||||
accessSelect: document.getElementById('access-select'),
|
||||
hostedOnly: document.getElementById('hosted-only'),
|
||||
knowledgebaseOnly: document.getElementById('knowledgebase-only'),
|
||||
tagCloudItems: document.querySelectorAll('.tag-cloud-item'),
|
||||
tagCloud: document.getElementById('tag-cloud'),
|
||||
tagCloudToggle: document.getElementById('tag-cloud-toggle'),
|
||||
selectedTags: document.getElementById('selected-tags'),
|
||||
viewToggles: document.querySelectorAll('.view-toggle'),
|
||||
aiViewToggle: document.getElementById('ai-view-toggle')
|
||||
resultsCounter: document.getElementById('results-counter'),
|
||||
resetButtons: {
|
||||
primary: document.getElementById('reset-primary'),
|
||||
advanced: document.getElementById('reset-advanced'),
|
||||
tags: document.getElementById('reset-tags'),
|
||||
all: document.getElementById('reset-all-filters')
|
||||
}
|
||||
};
|
||||
|
||||
// Verify critical elements exist
|
||||
if (!elements.searchInput || !elements.domainSelect || !elements.proprietaryCheckbox) {
|
||||
if (!elements.searchInput || !elements.domainSelect) {
|
||||
console.error('Critical filter elements not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// State management
|
||||
let selectedTags = new Set();
|
||||
let selectedPhase = '';
|
||||
let isTagCloudExpanded = false;
|
||||
|
||||
// Helper function to check if tool is hosted
|
||||
function isToolHosted(tool) {
|
||||
return tool.projectUrl !== undefined &&
|
||||
tool.projectUrl !== null &&
|
||||
tool.projectUrl !== "" &&
|
||||
tool.projectUrl.trim() !== "";
|
||||
}
|
||||
|
||||
// Initialize tag cloud
|
||||
function initTagCloud() {
|
||||
const visibleCount = 22;
|
||||
const visibleCount = 20;
|
||||
elements.tagCloudItems.forEach((item, index) => {
|
||||
if (index >= visibleCount) {
|
||||
item.style.display = 'none';
|
||||
@ -149,9 +325,10 @@ const sortedTags = Object.entries(tagFrequency)
|
||||
});
|
||||
}
|
||||
|
||||
// Toggle tag cloud expansion
|
||||
function toggleTagCloud() {
|
||||
isTagCloudExpanded = !isTagCloudExpanded;
|
||||
const visibleCount = 22;
|
||||
const visibleCount = 20;
|
||||
|
||||
if (isTagCloudExpanded) {
|
||||
elements.tagCloud.classList.add('expanded');
|
||||
@ -182,10 +359,11 @@ const sortedTags = Object.entries(tagFrequency)
|
||||
}
|
||||
}
|
||||
|
||||
// Filter tag cloud based on search
|
||||
function filterTagCloud() {
|
||||
const searchTerm = elements.searchInput.value.toLowerCase();
|
||||
let visibleCount = 0;
|
||||
const maxVisibleWhenCollapsed = 22;
|
||||
const maxVisibleWhenCollapsed = 20;
|
||||
|
||||
elements.tagCloudItems.forEach(item => {
|
||||
const tagName = item.getAttribute('data-tag').toLowerCase();
|
||||
@ -211,91 +389,132 @@ const sortedTags = Object.entries(tagFrequency)
|
||||
elements.tagCloudToggle.style.display = hasHiddenTags ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function isToolHosted(tool) {
|
||||
return tool.projectUrl !== undefined &&
|
||||
tool.projectUrl !== null &&
|
||||
tool.projectUrl !== "" &&
|
||||
tool.projectUrl.trim() !== "";
|
||||
}
|
||||
// Update selected tags display
|
||||
function updateSelectedTags() {
|
||||
if (selectedTags.size === 0) {
|
||||
elements.selectedTags.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
function isMethod(tool) {
|
||||
return tool.type === 'method';
|
||||
}
|
||||
elements.selectedTags.style.display = 'block';
|
||||
elements.selectedTags.innerHTML = `
|
||||
<div class="selected-tags-header">Ausgewählte Tags:</div>
|
||||
<div class="selected-tags-list">
|
||||
${Array.from(selectedTags).map(tag => `
|
||||
<span class="selected-tag">
|
||||
${tag}
|
||||
<button class="remove-tag" data-tag="${tag}">×</button>
|
||||
</span>
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
|
||||
function updateMatrixHighlighting() {
|
||||
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
|
||||
if (currentView !== 'matrix') return;
|
||||
|
||||
const matrixTable = document.querySelector('.matrix-table');
|
||||
if (!matrixTable) return;
|
||||
|
||||
matrixTable.querySelectorAll('.highlight-row, .highlight-column').forEach(el => {
|
||||
el.classList.remove('highlight-row', 'highlight-column');
|
||||
// Add event listeners for remove buttons
|
||||
elements.selectedTags.querySelectorAll('.remove-tag').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const tag = btn.getAttribute('data-tag');
|
||||
removeTag(tag);
|
||||
});
|
||||
});
|
||||
|
||||
const selectedDomain = elements.domainSelect.value;
|
||||
|
||||
if (selectedDomain) {
|
||||
const domainRow = matrixTable.querySelector(`tr[data-domain="${selectedDomain}"]`);
|
||||
if (domainRow) {
|
||||
domainRow.classList.add('highlight-row');
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedPhase) {
|
||||
const phaseHeaders = matrixTable.querySelectorAll('thead th[data-phase]');
|
||||
const phaseIndex = Array.from(phaseHeaders).findIndex(th =>
|
||||
th.getAttribute('data-phase') === selectedPhase
|
||||
);
|
||||
|
||||
if (phaseIndex >= 0) {
|
||||
const columnIndex = phaseIndex + 1;
|
||||
matrixTable.querySelectorAll(`tr`).forEach(row => {
|
||||
const cell = row.children[columnIndex];
|
||||
if (cell) {
|
||||
cell.classList.add('highlight-column');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add/remove tags
|
||||
function addTag(tag) {
|
||||
selectedTags.add(tag);
|
||||
document.querySelector(`[data-tag="${tag}"]`).classList.add('active');
|
||||
updateSelectedTags();
|
||||
filterTools();
|
||||
}
|
||||
|
||||
function removeTag(tag) {
|
||||
selectedTags.delete(tag);
|
||||
const tagElement = document.querySelector(`[data-tag="${tag}"]`);
|
||||
if (tagElement) tagElement.classList.remove('active');
|
||||
updateSelectedTags();
|
||||
filterTools();
|
||||
}
|
||||
|
||||
// Update results counter
|
||||
function updateResultsCounter(count) {
|
||||
const total = window.toolsData.length;
|
||||
elements.resultsCounter.textContent = count === total
|
||||
? `${total} Tools`
|
||||
: `${count} von ${total} Tools`;
|
||||
}
|
||||
|
||||
// Main filter function
|
||||
function filterTools() {
|
||||
const searchTerm = elements.searchInput.value.trim();
|
||||
const searchTerm = elements.searchInput.value.trim().toLowerCase();
|
||||
const selectedDomain = elements.domainSelect.value;
|
||||
const includeProprietary = elements.proprietaryCheckbox.checked;
|
||||
const selectedPhaseFromSelect = elements.phaseSelect.value;
|
||||
const selectedType = elements.typeSelect.value;
|
||||
const selectedSkill = elements.skillSelect.value;
|
||||
const selectedPlatform = elements.platformSelect.value;
|
||||
const selectedLicense = elements.licenseSelect.value;
|
||||
const selectedAccess = elements.accessSelect.value;
|
||||
const hostedOnly = elements.hostedOnly.checked;
|
||||
const knowledgebaseOnly = elements.knowledgebaseOnly.checked;
|
||||
|
||||
// Use phase from either dropdown or button selection
|
||||
const activePhase = selectedPhaseFromSelect || selectedPhase;
|
||||
|
||||
const filtered = window.toolsData.filter(tool => {
|
||||
const domains = tool.domains || [];
|
||||
const phases = tool.phases || [];
|
||||
const tags = tool.tags || [];
|
||||
|
||||
// Search filter (keep existing logic)
|
||||
// Search filter
|
||||
if (searchTerm && !(
|
||||
tool.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
tool.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()))
|
||||
tool.name.toLowerCase().includes(searchTerm) ||
|
||||
tool.description.toLowerCase().includes(searchTerm) ||
|
||||
(tool.tags || []).some(tag => tag.toLowerCase().includes(searchTerm))
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Domain filter
|
||||
if (selectedDomain && !domains.includes(selectedDomain)) {
|
||||
if (selectedDomain && !(tool.domains || []).includes(selectedDomain)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Phase filter
|
||||
if (selectedPhase && !phases.includes(selectedPhase)) {
|
||||
if (activePhase && !(tool.phases || []).includes(activePhase)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Proprietary filter
|
||||
if (!includeProprietary && !isMethod(tool) && tool.type !== 'concept' && tool.license === 'Proprietary') {
|
||||
// Type filter
|
||||
if (selectedType && tool.type !== selectedType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skill level filter
|
||||
if (selectedSkill && tool.skillLevel !== selectedSkill) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Platform filter
|
||||
if (selectedPlatform && !(tool.platforms || []).includes(selectedPlatform)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// License filter - NO MORE HARD-CODED LOGIC
|
||||
if (selectedLicense && tool.license !== selectedLicense) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Access type filter
|
||||
if (selectedAccess && tool.accessType !== selectedAccess) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hosted only filter (CC24-Server tools)
|
||||
if (hostedOnly && !isToolHosted(tool)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Knowledgebase only filter
|
||||
if (knowledgebaseOnly && !tool.knowledgebase) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Tag filter
|
||||
if (selectedTags.size > 0 && !Array.from(selectedTags).every(tag => tags.includes(tag))) {
|
||||
if (selectedTags.size > 0 && !Array.from(selectedTags).every(tag => (tool.tags || []).includes(tag))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -307,97 +526,118 @@ const sortedTags = Object.entries(tagFrequency)
|
||||
? window.prioritizeSearchResults(filtered, searchTerm)
|
||||
: filtered;
|
||||
|
||||
updateMatrixHighlighting();
|
||||
updateResultsCounter(finalResults.length);
|
||||
|
||||
// Dispatch event for other components
|
||||
window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: finalResults }));
|
||||
}
|
||||
|
||||
function handleTagClick(tagItem) {
|
||||
const tag = tagItem.getAttribute('data-tag');
|
||||
|
||||
if (selectedTags.has(tag)) {
|
||||
selectedTags.delete(tag);
|
||||
tagItem.classList.remove('active');
|
||||
} else {
|
||||
selectedTags.add(tag);
|
||||
tagItem.classList.add('active');
|
||||
}
|
||||
|
||||
// Reset functions
|
||||
function resetPrimaryFilters() {
|
||||
elements.domainSelect.value = '';
|
||||
elements.phaseSelect.value = '';
|
||||
selectedPhase = '';
|
||||
filterTools();
|
||||
}
|
||||
|
||||
function handlePhaseClick(button) {
|
||||
const phase = button.getAttribute('data-phase');
|
||||
|
||||
if (selectedPhase === phase) {
|
||||
selectedPhase = '';
|
||||
button.classList.remove('active');
|
||||
} else {
|
||||
elements.phaseButtons.forEach(btn => btn.classList.remove('active'));
|
||||
selectedPhase = phase;
|
||||
button.classList.add('active');
|
||||
}
|
||||
|
||||
function resetAdvancedFilters() {
|
||||
elements.typeSelect.value = '';
|
||||
elements.skillSelect.value = '';
|
||||
elements.platformSelect.value = '';
|
||||
elements.licenseSelect.value = '';
|
||||
elements.accessSelect.value = '';
|
||||
elements.hostedOnly.checked = false;
|
||||
elements.knowledgebaseOnly.checked = false;
|
||||
filterTools();
|
||||
}
|
||||
|
||||
function handleViewToggle(view) {
|
||||
elements.viewToggles.forEach(btn => {
|
||||
btn.classList.toggle('active', btn.getAttribute('data-view') === view);
|
||||
});
|
||||
|
||||
window.dispatchEvent(new CustomEvent('viewChanged', { detail: view }));
|
||||
|
||||
if (view === 'hosted') {
|
||||
const hosted = window.toolsData.filter(tool => isToolHosted(tool));
|
||||
window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: hosted }));
|
||||
} else {
|
||||
filterTools();
|
||||
}
|
||||
}
|
||||
|
||||
function clearTagFilters() {
|
||||
function resetTags() {
|
||||
selectedTags.clear();
|
||||
elements.tagCloudItems.forEach(item => item.classList.remove('active'));
|
||||
updateSelectedTags();
|
||||
filterTools();
|
||||
}
|
||||
|
||||
function clearAllFilters() {
|
||||
function resetAllFilters() {
|
||||
elements.searchInput.value = '';
|
||||
elements.domainSelect.value = '';
|
||||
selectedPhase = '';
|
||||
elements.phaseButtons.forEach(btn => btn.classList.remove('active'));
|
||||
clearTagFilters();
|
||||
elements.clearSearch.classList.add('hidden');
|
||||
resetPrimaryFilters();
|
||||
resetAdvancedFilters();
|
||||
resetTags();
|
||||
filterTagCloud();
|
||||
}
|
||||
|
||||
// Event listeners using cached elements
|
||||
elements.searchInput.addEventListener('input', () => {
|
||||
// Event listeners
|
||||
elements.searchInput.addEventListener('input', (e) => {
|
||||
const hasValue = e.target.value.length > 0;
|
||||
elements.clearSearch.classList.toggle('hidden', !hasValue);
|
||||
filterTagCloud();
|
||||
filterTools();
|
||||
});
|
||||
|
||||
elements.domainSelect.addEventListener('change', filterTools);
|
||||
elements.proprietaryCheckbox.addEventListener('change', filterTools);
|
||||
elements.clearSearch.addEventListener('click', () => {
|
||||
elements.searchInput.value = '';
|
||||
elements.clearSearch.classList.add('hidden');
|
||||
filterTagCloud();
|
||||
filterTools();
|
||||
});
|
||||
|
||||
[elements.domainSelect, elements.phaseSelect, elements.typeSelect, elements.skillSelect,
|
||||
elements.platformSelect, elements.licenseSelect, elements.accessSelect].forEach(select => {
|
||||
select.addEventListener('change', filterTools);
|
||||
});
|
||||
|
||||
[elements.hostedOnly, elements.knowledgebaseOnly].forEach(checkbox => {
|
||||
checkbox.addEventListener('change', filterTools);
|
||||
});
|
||||
|
||||
elements.tagCloudToggle.addEventListener('click', toggleTagCloud);
|
||||
|
||||
elements.tagCloudItems.forEach(item => {
|
||||
item.addEventListener('click', () => handleTagClick(item));
|
||||
});
|
||||
|
||||
elements.phaseButtons.forEach(btn => {
|
||||
btn.addEventListener('click', () => handlePhaseClick(btn));
|
||||
item.addEventListener('click', () => {
|
||||
const tag = item.getAttribute('data-tag');
|
||||
if (selectedTags.has(tag)) {
|
||||
removeTag(tag);
|
||||
} else {
|
||||
addTag(tag);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
elements.viewToggles.forEach(btn => {
|
||||
btn.addEventListener('click', () => handleViewToggle(btn.getAttribute('data-view')));
|
||||
btn.addEventListener('click', () => {
|
||||
const view = btn.getAttribute('data-view');
|
||||
|
||||
// Simple toggle like the old version
|
||||
elements.viewToggles.forEach(b => {
|
||||
b.classList.toggle('active', b.getAttribute('data-view') === view);
|
||||
});
|
||||
|
||||
window.dispatchEvent(new CustomEvent('viewChanged', { detail: view }));
|
||||
|
||||
if (view === 'hosted') {
|
||||
const hosted = window.toolsData.filter(tool => isToolHosted(tool));
|
||||
window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: hosted }));
|
||||
} else {
|
||||
filterTools();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
window.clearTagFilters = clearTagFilters;
|
||||
window.clearAllFilters = clearAllFilters;
|
||||
// Reset button listeners
|
||||
elements.resetButtons.primary.addEventListener('click', resetPrimaryFilters);
|
||||
elements.resetButtons.advanced.addEventListener('click', resetAdvancedFilters);
|
||||
elements.resetButtons.tags.addEventListener('click', resetTags);
|
||||
elements.resetButtons.all.addEventListener('click', resetAllFilters);
|
||||
|
||||
// Expose functions globally for backwards compatibility
|
||||
window.clearTagFilters = resetTags;
|
||||
window.clearAllFilters = resetAllFilters;
|
||||
|
||||
// Initialize
|
||||
initTagCloud();
|
||||
filterTagCloud();
|
||||
updateSelectedTags();
|
||||
|
||||
setTimeout(() => {
|
||||
filterTools();
|
||||
|
@ -194,8 +194,8 @@ domains.forEach((domain: any) => {
|
||||
|
||||
<script define:vars={{ toolsData: tools, domainAgnosticSoftware, domainAgnosticTools }}>
|
||||
function getSelectedPhase() {
|
||||
const activePhaseButton = document.querySelector('.phase-button.active');
|
||||
return activePhaseButton ? activePhaseButton.getAttribute('data-phase') : '';
|
||||
const activePhaseChip = document.querySelector('.phase-chip.active');
|
||||
return activePhaseChip ? activePhaseChip.getAttribute('data-phase') : '';
|
||||
}
|
||||
|
||||
function getSelectedDomain() {
|
||||
|
@ -302,35 +302,27 @@ const phases = data.phases;
|
||||
}
|
||||
|
||||
function hideFilterControls() {
|
||||
const elements = [
|
||||
'.domain-phase-container',
|
||||
'#search-input',
|
||||
'.tag-cloud',
|
||||
'.tag-header',
|
||||
'.checkbox-wrapper'
|
||||
];
|
||||
|
||||
elements.forEach(selector => {
|
||||
const element = document.querySelector(selector);
|
||||
if (element) element.style.display = 'none';
|
||||
const filterSections = document.querySelectorAll('.filter-section');
|
||||
filterSections.forEach((section, index) => {
|
||||
if (index < filterSections.length - 1) {
|
||||
section.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
const allInputs = filtersSection.querySelectorAll('input, select, textarea');
|
||||
allInputs.forEach(input => input.style.display = 'none');
|
||||
}
|
||||
|
||||
function showFilterControls() {
|
||||
const domainPhaseContainer = document.querySelector('.domain-phase-container');
|
||||
const filterSections = document.querySelectorAll('.filter-section');
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const tagCloud = document.querySelector('.tag-cloud');
|
||||
const tagHeader = document.querySelector('.tag-header');
|
||||
const tagControls = document.querySelector('.tag-controls');
|
||||
const checkboxWrappers = document.querySelectorAll('.checkbox-wrapper');
|
||||
const allInputs = filtersSection.querySelectorAll('input, select, textarea');
|
||||
|
||||
if (domainPhaseContainer) domainPhaseContainer.style.display = 'grid';
|
||||
filterSections.forEach(section => section.style.display = 'block');
|
||||
|
||||
if (searchInput) searchInput.style.display = 'block';
|
||||
if (tagCloud) tagCloud.style.display = 'flex';
|
||||
if (tagHeader) tagHeader.style.display = 'flex';
|
||||
if (tagControls) tagControls.style.display = 'flex';
|
||||
|
||||
allInputs.forEach(input => input.style.display = 'block');
|
||||
checkboxWrappers.forEach(wrapper => wrapper.style.display = 'flex');
|
||||
|
@ -1217,62 +1217,275 @@ input[type="checkbox"] {
|
||||
}
|
||||
|
||||
/* ===================================================================
|
||||
15. FILTER AND PHASE CONTROLS (CONSOLIDATED)
|
||||
15. FILTER AND PHASE CONTROLS (CONSOLIDATED & FIXED)
|
||||
================================================================= */
|
||||
|
||||
.domain-phase-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 2rem;
|
||||
align-items: start;
|
||||
/* Main Filter Container */
|
||||
.filters-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.phase-buttons {
|
||||
/* Filter Sections */
|
||||
.filter-section {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.filter-card-compact {
|
||||
background-color: var(--color-bg);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem; /* Reduced from 1.5rem */
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.filter-card-compact:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.filter-header-compact {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem; /* Reduced from 1.25rem */
|
||||
padding-bottom: 0.5rem; /* Reduced from 0.75rem */
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.filter-header-compact h3 {
|
||||
margin: 0;
|
||||
font-size: 0.875rem; /* Reduced from 1rem */
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.phase-button {
|
||||
padding: 0.5rem 1rem;
|
||||
/* Search Components */
|
||||
.search-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
left: 0.75rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--color-text-secondary);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding: 0.75rem 2.5rem 0.75rem 2.5rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 0.375rem;
|
||||
font-size: 0.875rem;
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 3px rgb(37 99 235 / 10%);
|
||||
}
|
||||
|
||||
.search-clear {
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--color-text-secondary);
|
||||
cursor: pointer;
|
||||
padding: 0.25rem;
|
||||
border-radius: 0.25rem;
|
||||
transition: var(--transition-fast);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.search-clear:hover {
|
||||
background-color: var(--color-bg-secondary);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Filter Grids & Groups */
|
||||
.filter-grid-compact {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 0.75rem; /* Reduced from 1rem */
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
padding: 0.625rem 0.75rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 0.375rem;
|
||||
font-size: 0.875rem;
|
||||
cursor: pointer;
|
||||
transition: var(--transition-fast);
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.filter-select:focus {
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 2px rgb(37 99 235 / 10%);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Phase Controls */
|
||||
.phase-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.phase-chip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.75rem 1rem;
|
||||
border: 2px solid var(--color-border);
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: var(--transition-fast);
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
min-height: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.phase-button:hover {
|
||||
.phase-chip:hover {
|
||||
border-color: var(--color-primary);
|
||||
background-color: var(--color-bg-secondary);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.phase-button.active {
|
||||
.phase-chip.active {
|
||||
background-color: var(--color-primary);
|
||||
border-color: var(--color-primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.phase-button.active:hover {
|
||||
background-color: var(--color-primary-hover);
|
||||
border-color: var(--color-primary-hover);
|
||||
.advanced-filters-compact {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.filter-toggles-compact {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.toggle-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
cursor: pointer;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--color-border);
|
||||
background-color: var(--color-bg-secondary);
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.toggle-wrapper:hover {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Tag System */
|
||||
.tag-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.selected-tags {
|
||||
display: none;
|
||||
padding: 1rem;
|
||||
background-color: var(--color-bg-secondary);
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.selected-tags-header {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.selected-tags-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.selected-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.375rem 0.75rem;
|
||||
background-color: var(--color-accent);
|
||||
color: white;
|
||||
border-radius: 1rem;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.remove-tag {
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
padding: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.remove-tag:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Tag Cloud */
|
||||
.tag-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.tag-cloud {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@ -1346,34 +1559,136 @@ input[type="checkbox"] {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.btn-tag-toggle {
|
||||
padding: 0.25rem 0.75rem;
|
||||
/* Reset Buttons */
|
||||
.filter-reset, .filter-reset-all {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.375rem 0.75rem;
|
||||
background: none;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 0.25rem;
|
||||
background-color: var(--color-bg-secondary);
|
||||
border-radius: 0.375rem;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.75rem;
|
||||
font-size: 0.8125rem;
|
||||
cursor: pointer;
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.btn-tag-toggle:hover {
|
||||
background-color: var(--color-bg-tertiary);
|
||||
color: var(--color-text);
|
||||
transform: translateY(-1px);
|
||||
.filter-reset:hover {
|
||||
background-color: var(--color-bg-secondary);
|
||||
border-color: var(--color-warning);
|
||||
color: var(--color-warning);
|
||||
}
|
||||
|
||||
/* View Toggle */
|
||||
.view-toggle {
|
||||
.filter-reset-all {
|
||||
background-color: var(--color-warning);
|
||||
color: white;
|
||||
border-color: var(--color-warning);
|
||||
}
|
||||
|
||||
.filter-reset-all:hover {
|
||||
background-color: var(--color-warning);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* Tag Controls */
|
||||
.tag-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.tag-toggle {
|
||||
padding: 0.375rem 0.75rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 0.375rem;
|
||||
background-color: var(--color-bg-secondary);
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.8125rem;
|
||||
cursor: pointer;
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.tag-toggle:hover {
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.view-controls-compact {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.results-count {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
#results-counter {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-secondary);
|
||||
padding: 0.375rem 0.75rem;
|
||||
background-color: var(--color-bg-secondary);
|
||||
border-radius: 1rem;
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
/* View Toggle Buttons */
|
||||
.view-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 0.375rem;
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: var(--transition-fast);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.view-toggle:hover {
|
||||
background-color: var(--color-bg-secondary);
|
||||
border-color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.view-toggle.active {
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.view-toggle.active:hover {
|
||||
background-color: var(--color-primary-hover);
|
||||
border-color: var(--color-primary-hover);
|
||||
}
|
||||
|
||||
#ai-view-toggle {
|
||||
background-color: var(--color-accent) !important;
|
||||
color: white !important;
|
||||
border-color: var(--color-accent) !important;
|
||||
}
|
||||
|
||||
#ai-view-toggle:hover {
|
||||
background-color: var(--color-accent-hover) !important;
|
||||
border-color: var(--color-accent-hover) !important;
|
||||
}
|
||||
|
||||
#ai-view-toggle.active {
|
||||
background-color: var(--color-accent) !important;
|
||||
border-color: var(--color-accent) !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ===================================================================
|
||||
16. AI INTERFACE (CONSOLIDATED)
|
||||
================================================================= */
|
||||
@ -2756,23 +3071,21 @@ footer {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.phase-buttons {
|
||||
gap: 0.375rem;
|
||||
.phase-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.phase-button {
|
||||
padding: 0.375rem 0.75rem;
|
||||
.phase-chip {
|
||||
padding: 0.625rem 0.75rem;
|
||||
font-size: 0.8125rem;
|
||||
min-height: 2.5rem;
|
||||
}
|
||||
|
||||
.tag-cloud {
|
||||
max-height: 100px;
|
||||
}
|
||||
|
||||
.tag-header {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.matrix-table {
|
||||
min-width: 600px;
|
||||
}
|
||||
@ -2899,19 +3212,58 @@ footer {
|
||||
.grid-4 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.filters-container {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.filter-grid-compact {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.filter-card-compact {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.filter-header-compact {
|
||||
margin-bottom: 0.5rem;
|
||||
padding-bottom: 0.375rem;
|
||||
}
|
||||
|
||||
.filter-header-compact h3 {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.advanced-filters-compact {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.filter-toggles-compact {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.toggle-wrapper {
|
||||
padding: 0.625rem 0.875rem;
|
||||
}
|
||||
|
||||
.view-controls-compact {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.view-toggle {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 640px) {
|
||||
.phase-buttons {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.phase-button {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tag-cloud {
|
||||
max-height: 80px;
|
||||
}
|
||||
@ -2970,15 +3322,6 @@ footer {
|
||||
}
|
||||
|
||||
@media (width <= 480px) {
|
||||
.phase-buttons {
|
||||
flex-direction: column;
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.phase-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.phase-tools {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
@ -3030,6 +3373,28 @@ footer {
|
||||
.approach-selector {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.tag-controls {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.phase-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.phase-chip {
|
||||
padding: 0.75rem;
|
||||
min-height: 3rem;
|
||||
}
|
||||
|
||||
.filter-grid-compact {
|
||||
gap: 0.375rem;
|
||||
}
|
||||
|
||||
.filter-card-compact {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user