iteration UI overhaul
This commit is contained in:
parent
1f9df5c1a8
commit
0d22210040
168
src/components/TargetedScenarios.astro
Normal file
168
src/components/TargetedScenarios.astro
Normal file
@ -0,0 +1,168 @@
|
||||
---
|
||||
import { getToolsData } from '../utils/dataService.js';
|
||||
|
||||
const data = await getToolsData();
|
||||
const scenarios = data.scenarios || [];
|
||||
|
||||
// Configuration
|
||||
const maxDisplayed = 9;
|
||||
const displayedScenarios = scenarios.slice(0, maxDisplayed);
|
||||
---
|
||||
|
||||
<section id="targeted-section" class="targeted-section">
|
||||
<div class="targeted-header">
|
||||
<h3>Gezielte Tool-Suche</h3>
|
||||
<p class="targeted-subtitle">
|
||||
Finden Sie schnell das passende Werkzeug für Ihre spezifische Anforderung
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="search-interface">
|
||||
<div class="search-box">
|
||||
<div class="search-icon">
|
||||
<svg width="20" height="20" 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"
|
||||
class="targeted-search-input"
|
||||
placeholder="z.B. 'Windows Registry analysieren' oder 'PCAP-Dateien auswerten'..."
|
||||
id="targeted-search-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{scenarios.length > 0 && (
|
||||
<div class="search-suggestions" id="scenario-suggestions">
|
||||
{displayedScenarios.map((scenario) => (
|
||||
<div
|
||||
class="suggestion-chip"
|
||||
data-scenario-id={scenario.id}
|
||||
onclick={`applyScenarioSearch('${scenario.id}')`}
|
||||
>
|
||||
<span class="scenario-emoji">{scenario.icon}</span>
|
||||
<span class="scenario-text">{scenario.friendly_name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{scenarios.length > maxDisplayed && (
|
||||
<div class="more-scenarios">
|
||||
<button class="btn-more-scenarios" onclick="toggleAllScenarios()" id="more-scenarios-btn">
|
||||
+ {scenarios.length - maxDisplayed} weitere Szenarien
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div class="targeted-tip">
|
||||
<p>
|
||||
<strong>Tipp:</strong> Die Szenarien durchsuchen automatisch nach passenden Tags und Beschreibungen.
|
||||
Für KI-gestützte Empfehlungen nutzen Sie den entsprechenden Modus.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script define:vars={{ allScenarios: scenarios, maxDisplay: maxDisplayed }}>
|
||||
let showingAllScenarios = false;
|
||||
|
||||
// Apply scenario search using existing search functionality
|
||||
window.applyScenarioSearch = function(scenarioId) {
|
||||
console.log(`Applying scenario search: ${scenarioId}`);
|
||||
|
||||
// Find the main search input (existing)
|
||||
const mainSearchInput = document.getElementById('search-input');
|
||||
if (mainSearchInput) {
|
||||
// Use scenario ID as search term (it should match tool tags)
|
||||
mainSearchInput.value = scenarioId;
|
||||
|
||||
// Trigger existing search functionality
|
||||
const inputEvent = new Event('input', { bubbles: true });
|
||||
mainSearchInput.dispatchEvent(inputEvent);
|
||||
|
||||
// Switch to grid view
|
||||
const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
|
||||
if (gridToggle && !gridToggle.classList.contains('active')) {
|
||||
gridToggle.click();
|
||||
}
|
||||
|
||||
// Scroll to results
|
||||
setTimeout(() => {
|
||||
const toolsGrid = document.getElementById('tools-grid');
|
||||
if (toolsGrid) {
|
||||
toolsGrid.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
// Visual feedback
|
||||
document.querySelectorAll('.suggestion-chip').forEach(chip => {
|
||||
chip.classList.remove('active');
|
||||
});
|
||||
document.querySelector(`[data-scenario-id="${scenarioId}"]`)?.classList.add('active');
|
||||
};
|
||||
|
||||
// Toggle showing all scenarios
|
||||
window.toggleAllScenarios = function() {
|
||||
const suggestionsContainer = document.getElementById('scenario-suggestions');
|
||||
const moreBtn = document.getElementById('more-scenarios-btn');
|
||||
|
||||
if (!showingAllScenarios) {
|
||||
// Show additional scenarios
|
||||
const additionalScenarios = allScenarios.slice(maxDisplay);
|
||||
additionalScenarios.forEach(scenario => {
|
||||
const chip = document.createElement('div');
|
||||
chip.className = 'suggestion-chip additional-scenario';
|
||||
chip.setAttribute('data-scenario-id', scenario.id);
|
||||
chip.onclick = () => applyScenarioSearch(scenario.id);
|
||||
chip.innerHTML = `
|
||||
<span class="scenario-emoji">${scenario.icon}</span>
|
||||
<span class="scenario-text">${scenario.friendly_name}</span>
|
||||
`;
|
||||
suggestionsContainer.appendChild(chip);
|
||||
});
|
||||
|
||||
moreBtn.textContent = 'Weniger anzeigen';
|
||||
showingAllScenarios = true;
|
||||
} else {
|
||||
// Hide additional scenarios
|
||||
document.querySelectorAll('.additional-scenario').forEach(chip => chip.remove());
|
||||
moreBtn.textContent = `+ ${allScenarios.length - maxDisplay} weitere Szenarien`;
|
||||
showingAllScenarios = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Handle targeted search input
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const targetedInput = document.getElementById('targeted-search-input');
|
||||
if (targetedInput) {
|
||||
targetedInput.addEventListener('input', (e) => {
|
||||
const mainSearchInput = document.getElementById('search-input');
|
||||
if (mainSearchInput && e.target.value.length > 2) {
|
||||
mainSearchInput.value = e.target.value;
|
||||
const inputEvent = new Event('input', { bubbles: true });
|
||||
mainSearchInput.dispatchEvent(inputEvent);
|
||||
}
|
||||
});
|
||||
|
||||
targetedInput.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
// Switch to grid view and scroll to results
|
||||
const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
|
||||
if (gridToggle) {
|
||||
gridToggle.click();
|
||||
setTimeout(() => {
|
||||
const toolsGrid = document.getElementById('tools-grid');
|
||||
if (toolsGrid) {
|
||||
toolsGrid.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
@ -2153,3 +2153,34 @@ domain-agnostic-software:
|
||||
- id: specific-os
|
||||
name: Betriebssysteme
|
||||
description: Operating Systems which focus on forensics
|
||||
scenarios:
|
||||
- id: registry
|
||||
icon: 🗃️
|
||||
friendly_name: "Registry-Analyse"
|
||||
- id: memory-forensics
|
||||
icon: 🧠
|
||||
friendly_name: "Memory-Forensik"
|
||||
- id: network-traffic
|
||||
icon: 🌐
|
||||
friendly_name: "Netzwerk-Traffic"
|
||||
- id: mobile-forensik
|
||||
icon: 📱
|
||||
friendly_name: "Mobile Geräte"
|
||||
- id: malware-analysis
|
||||
icon: 🦠
|
||||
friendly_name: "Malware-Analyse"
|
||||
- id: timeline-analysis
|
||||
icon: ⏰
|
||||
friendly_name: "Timeline-Erstellung"
|
||||
- id: file-recovery
|
||||
icon: 💾
|
||||
friendly_name: "Datei-Wiederherstellung"
|
||||
- id: browser-forensik
|
||||
icon: 🌍
|
||||
friendly_name: "Browser-Forensik"
|
||||
- id: email-forensik
|
||||
icon: 📧
|
||||
friendly_name: "E-Mail-Forensik"
|
||||
- id: log-analysis
|
||||
icon: 📊
|
||||
friendly_name: "Log-Analyse"
|
@ -4,6 +4,7 @@ import ToolCard from '../components/ToolCard.astro';
|
||||
import ToolFilters from '../components/ToolFilters.astro';
|
||||
import ToolMatrix from '../components/ToolMatrix.astro';
|
||||
import AIQueryInterface from '../components/AIQueryInterface.astro';
|
||||
import TargetedScenarios from '../components/TargetedScenarios.astro';
|
||||
import { getToolsData } from '../utils/dataService.js';
|
||||
|
||||
const data = await getToolsData();
|
||||
@ -139,7 +140,9 @@ const phases = data.phases;
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<TargetedScenarios />
|
||||
|
||||
<section id="filters-section" style="padding: 2rem 0;">
|
||||
<div style="text-align: center; margin-bottom: 2rem;">
|
||||
<h3 style="color: var(--color-text); margin-bottom: 0.5rem;">Alle verfügbaren Werkzeuge durchsuchen</h3>
|
||||
@ -192,15 +195,11 @@ const phases = data.phases;
|
||||
methodologySection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
} else if (approach === 'targeted') {
|
||||
// Show targeted scenarios section (implemented in Phase 3)
|
||||
// Show targeted scenarios section
|
||||
const targetedSection = document.getElementById('targeted-section');
|
||||
if (targetedSection) {
|
||||
targetedSection.classList.add('active');
|
||||
targetedSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
} else {
|
||||
// For now, show a placeholder message
|
||||
alert('Gezielte Szenarien werden in Phase 3 implementiert. Verwenden Sie vorerst die Standard-Werkzeugauswahl unten.');
|
||||
document.getElementById('filters-section').scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1928,6 +1928,26 @@ footer {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.search-suggestions {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.suggestion-chip {
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.scenario-emoji {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.targeted-search-input {
|
||||
padding: 0.875rem 0.875rem 0.875rem 2.75rem;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
left: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 640px) {
|
||||
@ -2007,6 +2027,20 @@ footer {
|
||||
padding: 0.5rem 1rem;
|
||||
flex: 1;
|
||||
}
|
||||
.search-suggestions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.suggestion-chip {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
max-width: calc(50% - 0.25rem);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.targeted-section {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 480px) {
|
||||
@ -2324,6 +2358,80 @@ footer {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.suggestion-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1rem;
|
||||
background-color: var(--color-bg-secondary);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 2rem;
|
||||
cursor: pointer;
|
||||
transition: var(--transition-fast);
|
||||
font-size: 0.875rem;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.suggestion-chip:hover {
|
||||
background-color: var(--color-accent);
|
||||
border-color: var(--color-accent);
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.suggestion-chip.active {
|
||||
background-color: var(--color-accent);
|
||||
border-color: var(--color-accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.scenario-emoji {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.scenario-text {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.more-scenarios {
|
||||
text-align: center;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.btn-more-scenarios {
|
||||
background: none;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 1rem;
|
||||
padding: 0.5rem 1rem;
|
||||
color: var(--color-text-secondary);
|
||||
cursor: pointer;
|
||||
font-size: 0.8125rem;
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.btn-more-scenarios:hover {
|
||||
background-color: var(--color-bg-secondary);
|
||||
border-color: var(--color-accent);
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.targeted-tip {
|
||||
background-color: var(--color-bg-secondary);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.targeted-tip p {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.dismiss-button {
|
||||
align-self: flex-end;
|
||||
background: none;
|
||||
@ -2735,3 +2843,78 @@ footer {
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.targeted-section {
|
||||
background: var(--color-bg);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 0.75rem;
|
||||
padding: 2rem;
|
||||
margin: 2rem 0;
|
||||
display: none;
|
||||
animation: fadeInUp 0.5s ease-out;
|
||||
}
|
||||
|
||||
.targeted-section.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.targeted-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.targeted-header h3 {
|
||||
color: var(--color-accent);
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.targeted-subtitle {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.875rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search-interface {
|
||||
max-width: 800px;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: relative;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--color-text-secondary);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.targeted-search-input {
|
||||
width: 100%;
|
||||
padding: 1rem 1rem 1rem 3rem;
|
||||
border: 2px solid var(--color-border);
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.targeted-search-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-accent);
|
||||
box-shadow: 0 0 0 3px rgb(5 150 105 / 10%);
|
||||
}
|
||||
|
||||
.search-suggestions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
justify-content: center;
|
||||
}
|
@ -39,6 +39,11 @@ const ToolsDataSchema = z.object({
|
||||
name: z.string(),
|
||||
description: z.string().optional()
|
||||
})).optional().default([]),
|
||||
scenarios: z.array(z.object({
|
||||
id: z.string(),
|
||||
icon: z.string(),
|
||||
friendly_name: z.string()
|
||||
})).optional().default([]),
|
||||
});
|
||||
|
||||
interface ToolsData {
|
||||
@ -46,11 +51,15 @@ interface ToolsData {
|
||||
domains: any[];
|
||||
phases: any[];
|
||||
'domain-agnostic-software': any[];
|
||||
scenarios: any[];
|
||||
}
|
||||
|
||||
interface CompressedToolsData extends Omit<ToolsData, 'tools'> {
|
||||
interface CompressedToolsData {
|
||||
tools: any[];
|
||||
concepts: any[];
|
||||
concepts: any[];
|
||||
domains: any[];
|
||||
phases: any[];
|
||||
'domain-agnostic-software': any[];
|
||||
}
|
||||
|
||||
let cachedData: ToolsData | null = null;
|
||||
@ -144,6 +153,7 @@ export async function getCompressedToolsDataForAI(): Promise<CompressedToolsData
|
||||
domains: data.domains,
|
||||
phases: data.phases,
|
||||
'domain-agnostic-software': data['domain-agnostic-software']
|
||||
// scenarios intentionally excluded from AI data
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user