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
|
- id: specific-os
|
||||||
name: Betriebssysteme
|
name: Betriebssysteme
|
||||||
description: Operating Systems which focus on forensics
|
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 ToolFilters from '../components/ToolFilters.astro';
|
||||||
import ToolMatrix from '../components/ToolMatrix.astro';
|
import ToolMatrix from '../components/ToolMatrix.astro';
|
||||||
import AIQueryInterface from '../components/AIQueryInterface.astro';
|
import AIQueryInterface from '../components/AIQueryInterface.astro';
|
||||||
|
import TargetedScenarios from '../components/TargetedScenarios.astro';
|
||||||
import { getToolsData } from '../utils/dataService.js';
|
import { getToolsData } from '../utils/dataService.js';
|
||||||
|
|
||||||
const data = await getToolsData();
|
const data = await getToolsData();
|
||||||
@ -140,6 +141,8 @@ const phases = data.phases;
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<TargetedScenarios />
|
||||||
|
|
||||||
<section id="filters-section" style="padding: 2rem 0;">
|
<section id="filters-section" style="padding: 2rem 0;">
|
||||||
<div style="text-align: center; margin-bottom: 2rem;">
|
<div style="text-align: center; margin-bottom: 2rem;">
|
||||||
<h3 style="color: var(--color-text); margin-bottom: 0.5rem;">Alle verfügbaren Werkzeuge durchsuchen</h3>
|
<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' });
|
methodologySection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
}
|
}
|
||||||
} else if (approach === 'targeted') {
|
} else if (approach === 'targeted') {
|
||||||
// Show targeted scenarios section (implemented in Phase 3)
|
// Show targeted scenarios section
|
||||||
const targetedSection = document.getElementById('targeted-section');
|
const targetedSection = document.getElementById('targeted-section');
|
||||||
if (targetedSection) {
|
if (targetedSection) {
|
||||||
targetedSection.classList.add('active');
|
targetedSection.classList.add('active');
|
||||||
targetedSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
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;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 0.75rem;
|
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) {
|
@media (width <= 640px) {
|
||||||
@ -2007,6 +2027,20 @@ footer {
|
|||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
flex: 1;
|
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) {
|
@media (width <= 480px) {
|
||||||
@ -2324,6 +2358,80 @@ footer {
|
|||||||
margin-right: 0.5rem;
|
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 {
|
.dismiss-button {
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
background: none;
|
background: none;
|
||||||
@ -2735,3 +2843,78 @@ footer {
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
margin-bottom: 2rem;
|
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(),
|
name: z.string(),
|
||||||
description: z.string().optional()
|
description: z.string().optional()
|
||||||
})).optional().default([]),
|
})).optional().default([]),
|
||||||
|
scenarios: z.array(z.object({
|
||||||
|
id: z.string(),
|
||||||
|
icon: z.string(),
|
||||||
|
friendly_name: z.string()
|
||||||
|
})).optional().default([]),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface ToolsData {
|
interface ToolsData {
|
||||||
@ -46,11 +51,15 @@ interface ToolsData {
|
|||||||
domains: any[];
|
domains: any[];
|
||||||
phases: any[];
|
phases: any[];
|
||||||
'domain-agnostic-software': any[];
|
'domain-agnostic-software': any[];
|
||||||
|
scenarios: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CompressedToolsData extends Omit<ToolsData, 'tools'> {
|
interface CompressedToolsData {
|
||||||
tools: any[];
|
tools: any[];
|
||||||
concepts: any[];
|
concepts: any[];
|
||||||
|
domains: any[];
|
||||||
|
phases: any[];
|
||||||
|
'domain-agnostic-software': any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
let cachedData: ToolsData | null = null;
|
let cachedData: ToolsData | null = null;
|
||||||
@ -144,6 +153,7 @@ export async function getCompressedToolsDataForAI(): Promise<CompressedToolsData
|
|||||||
domains: data.domains,
|
domains: data.domains,
|
||||||
phases: data.phases,
|
phases: data.phases,
|
||||||
'domain-agnostic-software': data['domain-agnostic-software']
|
'domain-agnostic-software': data['domain-agnostic-software']
|
||||||
|
// scenarios intentionally excluded from AI data
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user