adjust dual urls
This commit is contained in:
@@ -9,6 +9,7 @@ export interface Props {
|
||||
skillLevel: string;
|
||||
accessType: string;
|
||||
url: string;
|
||||
projectUrl?: string;
|
||||
license: string;
|
||||
tags: string[];
|
||||
isHosted: boolean;
|
||||
@@ -20,6 +21,12 @@ const { tool } = Astro.props;
|
||||
|
||||
// Determine card styling
|
||||
const cardClass = tool.isHosted ? 'card card-hosted' : (tool.license !== 'Proprietary' ? 'card card-oss' : 'card');
|
||||
|
||||
// Check if tool has a valid project URL for hosted services
|
||||
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
||||
tool.projectUrl !== null &&
|
||||
tool.projectUrl !== "" &&
|
||||
tool.projectUrl.trim() !== "";
|
||||
---
|
||||
|
||||
<div class={cardClass}>
|
||||
@@ -74,7 +81,26 @@ const cardClass = tool.isHosted ? 'card card-hosted' : (tool.license !== 'Propri
|
||||
))}
|
||||
</div>
|
||||
|
||||
<a href={tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;">
|
||||
{tool.isHosted ? 'Access Service' : 'Visit Website'}
|
||||
</a>
|
||||
<!-- Button section - different layouts for hosted vs non-hosted -->
|
||||
{tool.isHosted && hasValidProjectUrl ? (
|
||||
<!-- Two buttons for self-hosted tools with both URLs -->
|
||||
<div style="display: flex; gap: 0.5rem;">
|
||||
<a href={tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-secondary" style="flex: 1;">
|
||||
Project Page
|
||||
</a>
|
||||
<a href={tool.projectUrl} target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="flex: 1;">
|
||||
Access Service
|
||||
</a>
|
||||
</div>
|
||||
) : tool.isHosted ? (
|
||||
<!-- Single button for self-hosted tools with only project URL -->
|
||||
<a href={tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;">
|
||||
Project Page
|
||||
</a>
|
||||
) : (
|
||||
<!-- Single button for non-hosted tools -->
|
||||
<a href={tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;">
|
||||
Visit Website
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
@@ -12,12 +12,21 @@ const domains = data.domains;
|
||||
const phases = data.phases;
|
||||
const tools = data.tools;
|
||||
|
||||
// Create matrix structure
|
||||
// Separate collaboration tools from domain-specific tools
|
||||
const collaborationTools = tools.filter((tool: any) =>
|
||||
tool.phases.includes('collaboration')
|
||||
);
|
||||
|
||||
const domainTools = tools.filter((tool: any) =>
|
||||
!tool.phases.includes('collaboration')
|
||||
);
|
||||
|
||||
// Create matrix structure for domain-specific tools only
|
||||
const matrix: Record<string, Record<string, any[]>> = {};
|
||||
domains.forEach((domain: any) => {
|
||||
matrix[domain.id] = {};
|
||||
phases.forEach((phase: any) => {
|
||||
matrix[domain.id][phase.id] = tools.filter((tool: any) =>
|
||||
phases.filter((phase: any) => phase.id !== 'collaboration').forEach((phase: any) => {
|
||||
matrix[domain.id][phase.id] = domainTools.filter((tool: any) =>
|
||||
tool.domains.includes(domain.id) && tool.phases.includes(phase.id)
|
||||
);
|
||||
});
|
||||
@@ -25,36 +34,67 @@ domains.forEach((domain: any) => {
|
||||
---
|
||||
|
||||
<div id="matrix-container" class="matrix-wrapper" style="display: none;">
|
||||
<table class="matrix-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 200px;">Domain / Phase</th>
|
||||
{phases.map((phase: any) => (
|
||||
<th>{phase.name}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{domains.map((domain: any) => (
|
||||
<!-- Collaboration Tools Section (compact horizontal layout for matrix view) -->
|
||||
<div id="collaboration-tools-section" style="margin-bottom: 1.5rem;">
|
||||
<h3 style="margin-bottom: 0.75rem; color: var(--color-text); font-size: 1.125rem;">General Tools for Collaboration</h3>
|
||||
<div class="collaboration-tools-compact" id="collaboration-tools-container">
|
||||
{collaborationTools.map((tool: any) => (
|
||||
<div class={`collaboration-tool-compact ${tool.isHosted ? 'hosted' : tool.license !== 'Proprietary' ? 'oss' : ''}`}
|
||||
onclick={`window.showToolDetails('${tool.name}')`}>
|
||||
<div class="tool-compact-header">
|
||||
<h4 style="margin: 0; font-size: 0.875rem; font-weight: 600;">{tool.name}</h4>
|
||||
<div style="display: flex; gap: 0.25rem;">
|
||||
{tool.isHosted && <span class="badge-mini badge-primary">Self-Hosted</span>}
|
||||
{tool.license !== 'Proprietary' && <span class="badge-mini badge-success">OSS</span>}
|
||||
</div>
|
||||
</div>
|
||||
<p style="font-size: 0.75rem; color: var(--color-text-secondary); margin: 0.25rem 0; line-height: 1.3;">
|
||||
{tool.description}
|
||||
</p>
|
||||
<div style="display: flex; gap: 0.75rem; font-size: 0.6875rem; color: var(--color-text-secondary);">
|
||||
<span>{tool.platforms.join(', ')}</span>
|
||||
<span>•</span>
|
||||
<span>{tool.skillLevel}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DFIR Tools Matrix -->
|
||||
<div id="dfir-matrix-section">
|
||||
<h2 style="margin-bottom: 1rem; color: var(--color-text);">DFIR Tools Matrix</h2>
|
||||
<table class="matrix-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{domain.name}</th>
|
||||
{phases.map((phase: any) => (
|
||||
<td class="matrix-cell" data-domain={domain.id} data-phase={phase.id}>
|
||||
{matrix[domain.id][phase.id].map((tool: any) => (
|
||||
<span
|
||||
class={`tool-chip ${tool.isHosted ? 'tool-chip-hosted' : tool.license !== 'Proprietary' ? 'tool-chip-oss' : ''}`}
|
||||
data-tool-name={tool.name}
|
||||
onclick={`window.showToolDetails('${tool.name}')`}
|
||||
>
|
||||
{tool.name}
|
||||
</span>
|
||||
))}
|
||||
</td>
|
||||
<th style="width: 200px;">Domain / Phase</th>
|
||||
{phases.filter((phase: any) => phase.id !== 'collaboration').map((phase: any) => (
|
||||
<th>{phase.name}</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
{domains.map((domain: any) => (
|
||||
<tr>
|
||||
<th>{domain.name}</th>
|
||||
{phases.filter((phase: any) => phase.id !== 'collaboration').map((phase: any) => (
|
||||
<td class="matrix-cell" data-domain={domain.id} data-phase={phase.id}>
|
||||
{matrix[domain.id][phase.id].map((tool: any) => (
|
||||
<span
|
||||
class={`tool-chip ${tool.isHosted ? 'tool-chip-hosted' : tool.license !== 'Proprietary' ? 'tool-chip-oss' : ''}`}
|
||||
data-tool-name={tool.name}
|
||||
onclick={`window.showToolDetails('${tool.name}')`}
|
||||
>
|
||||
{tool.name}
|
||||
</span>
|
||||
))}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tool Details Modal -->
|
||||
@@ -78,12 +118,11 @@ domains.forEach((domain: any) => {
|
||||
|
||||
<div id="tool-tags" style="margin-bottom: 1rem;"></div>
|
||||
|
||||
<a id="tool-link" href="#" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;">
|
||||
Visit Website
|
||||
</a>
|
||||
<!-- Updated button container for dual buttons -->
|
||||
<div id="tool-links" style="display: flex; gap: 0.5rem;"></div>
|
||||
</div>
|
||||
|
||||
<script define:vars={{ toolsData: tools }}>
|
||||
<script define:vars={{ toolsData: tools, collaborationTools, domainTools }}>
|
||||
// Tool details functions
|
||||
window.showToolDetails = function(toolName) {
|
||||
const tool = toolsData.find(t => t.name === toolName);
|
||||
@@ -105,14 +144,16 @@ domains.forEach((domain: any) => {
|
||||
|
||||
// Metadata
|
||||
const metadataContainer = document.getElementById('tool-metadata');
|
||||
const domainsText = tool.domains.length > 0 ? tool.domains.join(', ') : 'Domain-agnostic';
|
||||
const phasesText = tool.phases.join(', ');
|
||||
metadataContainer.innerHTML = `
|
||||
<div style="display: grid; gap: 0.5rem;">
|
||||
<div><strong>Platforms:</strong> ${tool.platforms.join(', ')}</div>
|
||||
<div><strong>Skill Level:</strong> ${tool.skillLevel}</div>
|
||||
<div><strong>License:</strong> ${tool.license}</div>
|
||||
<div><strong>Access Type:</strong> ${tool.accessType}</div>
|
||||
<div><strong>Domains:</strong> ${tool.domains.join(', ')}</div>
|
||||
<div><strong>Phases:</strong> ${tool.phases.join(', ')}</div>
|
||||
<div><strong>Domains:</strong> ${domainsText}</div>
|
||||
<div><strong>Phases:</strong> ${phasesText}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -124,10 +165,38 @@ domains.forEach((domain: any) => {
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Link
|
||||
const linkElement = document.getElementById('tool-link');
|
||||
linkElement.href = tool.url;
|
||||
linkElement.textContent = tool.isHosted ? 'Access Service' : 'Visit Website';
|
||||
// Links - Updated to handle dual buttons for self-hosted tools
|
||||
const linksContainer = document.getElementById('tool-links');
|
||||
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
||||
tool.projectUrl !== null &&
|
||||
tool.projectUrl !== "" &&
|
||||
tool.projectUrl.trim() !== "";
|
||||
|
||||
if (tool.isHosted && hasValidProjectUrl) {
|
||||
// Two buttons for self-hosted tools with both URLs
|
||||
linksContainer.innerHTML = `
|
||||
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-secondary" style="flex: 1;">
|
||||
Project Page
|
||||
</a>
|
||||
<a href="${tool.projectUrl}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="flex: 1;">
|
||||
Access Service
|
||||
</a>
|
||||
`;
|
||||
} else if (tool.isHosted) {
|
||||
// Single button for self-hosted tools with only project URL
|
||||
linksContainer.innerHTML = `
|
||||
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;">
|
||||
Project Page
|
||||
</a>
|
||||
`;
|
||||
} else {
|
||||
// Single button for non-hosted tools
|
||||
linksContainer.innerHTML = `
|
||||
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;">
|
||||
Visit Website
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
// Show modal
|
||||
document.getElementById('modal-overlay').classList.add('active');
|
||||
@@ -145,26 +214,99 @@ domains.forEach((domain: any) => {
|
||||
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
|
||||
|
||||
if (currentView === 'matrix') {
|
||||
// Update matrix cells
|
||||
document.querySelectorAll('.matrix-cell').forEach(cell => {
|
||||
cell.innerHTML = '';
|
||||
});
|
||||
const selectedPhase = document.getElementById('phase-select')?.value;
|
||||
|
||||
// Re-populate with filtered tools
|
||||
filtered.forEach(tool => {
|
||||
tool.domains.forEach(domain => {
|
||||
tool.phases.forEach(phase => {
|
||||
const cell = document.querySelector(`[data-domain="${domain}"][data-phase="${phase}"]`);
|
||||
if (cell) {
|
||||
const chip = document.createElement('span');
|
||||
chip.className = `tool-chip ${tool.isHosted ? 'tool-chip-hosted' : tool.license !== 'Proprietary' ? 'tool-chip-oss' : ''}`;
|
||||
chip.textContent = tool.name;
|
||||
chip.onclick = () => window.showToolDetails(tool.name);
|
||||
cell.appendChild(chip);
|
||||
}
|
||||
// Handle collaboration tools section
|
||||
const collaborationSection = document.getElementById('collaboration-tools-section');
|
||||
const dfirMatrixSection = document.getElementById('dfir-matrix-section');
|
||||
const collaborationContainer = document.getElementById('collaboration-tools-container');
|
||||
|
||||
if (selectedPhase === 'collaboration') {
|
||||
// Show only collaboration tools, hide matrix
|
||||
collaborationSection.style.display = 'block';
|
||||
dfirMatrixSection.style.display = 'none';
|
||||
|
||||
// Filter collaboration tools
|
||||
const filteredCollaboration = filtered.filter(tool => tool.phases.includes('collaboration'));
|
||||
collaborationContainer.innerHTML = '';
|
||||
|
||||
filteredCollaboration.forEach(tool => {
|
||||
const toolCard = createCollaborationToolCardCompact(tool);
|
||||
collaborationContainer.appendChild(toolCard);
|
||||
});
|
||||
} else {
|
||||
// Show matrix, handle collaboration tools visibility
|
||||
dfirMatrixSection.style.display = 'block';
|
||||
|
||||
if (selectedPhase === '' || selectedPhase === null) {
|
||||
// Show collaboration tools when no specific phase is selected
|
||||
collaborationSection.style.display = 'block';
|
||||
|
||||
// Show all collaboration tools that pass general filters
|
||||
const filteredCollaboration = filtered.filter(tool => tool.phases.includes('collaboration'));
|
||||
collaborationContainer.innerHTML = '';
|
||||
|
||||
filteredCollaboration.forEach(tool => {
|
||||
const toolCard = createCollaborationToolCardCompact(tool);
|
||||
collaborationContainer.appendChild(toolCard);
|
||||
});
|
||||
} else {
|
||||
// Hide collaboration tools when specific DFIR phase is selected
|
||||
collaborationSection.style.display = 'none';
|
||||
}
|
||||
|
||||
// Clear and update matrix cells with DFIR tools only
|
||||
document.querySelectorAll('.matrix-cell').forEach(cell => {
|
||||
cell.innerHTML = '';
|
||||
});
|
||||
|
||||
// Re-populate with filtered DFIR tools
|
||||
const filteredDfirTools = filtered.filter(tool => !tool.phases.includes('collaboration'));
|
||||
filteredDfirTools.forEach(tool => {
|
||||
tool.domains.forEach(domain => {
|
||||
tool.phases.forEach(phase => {
|
||||
if (phase !== 'collaboration') {
|
||||
const cell = document.querySelector(`[data-domain="${domain}"][data-phase="${phase}"]`);
|
||||
if (cell) {
|
||||
const chip = document.createElement('span');
|
||||
chip.className = `tool-chip ${tool.isHosted ? 'tool-chip-hosted' : tool.license !== 'Proprietary' ? 'tool-chip-oss' : ''}`;
|
||||
chip.textContent = tool.name;
|
||||
chip.onclick = () => window.showToolDetails(tool.name);
|
||||
cell.appendChild(chip);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Helper function to create compact collaboration tool cards for matrix view
|
||||
function createCollaborationToolCardCompact(tool) {
|
||||
const cardDiv = document.createElement('div');
|
||||
const cardClass = `collaboration-tool-compact ${tool.isHosted ? 'hosted' : tool.license !== 'Proprietary' ? 'oss' : ''}`;
|
||||
cardDiv.className = cardClass;
|
||||
cardDiv.onclick = () => window.showToolDetails(tool.name);
|
||||
|
||||
cardDiv.innerHTML = `
|
||||
<div class="tool-compact-header">
|
||||
<h4 style="margin: 0; font-size: 0.875rem; font-weight: 600;">${tool.name}</h4>
|
||||
<div style="display: flex; gap: 0.25rem;">
|
||||
${tool.isHosted ? '<span class="badge-mini badge-primary">Self-Hosted</span>' : ''}
|
||||
${tool.license !== 'Proprietary' ? '<span class="badge-mini badge-success">OSS</span>' : ''}
|
||||
</div>
|
||||
</div>
|
||||
<p style="font-size: 0.75rem; color: var(--color-text-secondary); margin: 0.25rem 0; line-height: 1.3;">
|
||||
${tool.description}
|
||||
</p>
|
||||
<div style="display: flex; gap: 0.75rem; font-size: 0.6875rem; color: var(--color-text-secondary);">
|
||||
<span>${tool.platforms.join(', ')}</span>
|
||||
<span>•</span>
|
||||
<span>${tool.skillLevel}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return cardDiv;
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user