consolidation of auth mechanism

This commit is contained in:
overcuriousity
2025-07-24 12:30:59 +02:00
parent 32fca8a06f
commit 72bcc04309
5 changed files with 107 additions and 176 deletions

View File

@@ -6,7 +6,6 @@ import ToolMatrix from '../components/ToolMatrix.astro';
import AIQueryInterface from '../components/AIQueryInterface.astro';
import { getToolsData } from '../utils/dataService.js';
// Load tools data
const data = await getToolsData();
const tools = data.tools;
@@ -53,8 +52,8 @@ const tools = data.tools;
KI befragen
</button>
<!-- NEW: Contribution Button -->
<a href="/contribute" class="btn" style="padding: 0.75rem 1.5rem; background-color: var(--color-warning); color: white; border-color: var(--color-warning);" data-contribute-button="new">
<!-- Contribution Button - FIXED: Use data-contribute-button -->
<a href="/contribute" class="btn" style="padding: 0.75rem 1.5rem; background-color: var(--color-warning); color: white; border-color: var(--color-warning);" data-contribute-button>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem;">
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
<circle cx="8.5" cy="7" r="4"/>
@@ -78,13 +77,12 @@ const tools = data.tools;
<!-- Filters Section -->
<section id="filters-section" style="padding: 2rem 0;">
<ToolFilters />
<ToolFilters data={data} />
</section>
<!-- AI Query Interface -->
<AIQueryInterface />
<!-- Tools Grid -->
<section id="tools-grid" style="padding-bottom: 2rem;">
<div class="grid-auto-fit" id="tools-container">
@@ -100,36 +98,22 @@ const tools = data.tools;
</section>
<!-- Matrix View -->
<ToolMatrix />
<ToolMatrix data={data} />
</BaseLayout>
<script>
// Extend Window interface for custom properties
declare global {
interface Window {
toolsData: any[];
showToolDetails: (toolName: string, modalType?: string) => void;
hideToolDetails: (modalType?: string) => void;
hideAllToolDetails: () => void;
clearAllFilters?: () => void;
restoreAIResults?: () => void;
switchToAIView?: () => void;
showShareDialog: (shareButton: HTMLElement) => void;
navigateToGrid: (toolName: string) => void;
navigateToMatrix: (toolName: string) => void;
}
}
<script define:vars={{ toolsData: data.tools }}>
// Store tools data globally
window.toolsData = toolsData;
import { requireClientAuth } from '../utils/auth.js';
// Handle view changes and filtering
document.addEventListener('DOMContentLoaded', () => {
const toolsContainer = document.getElementById('tools-container') as HTMLElement;
const toolsGrid = document.getElementById('tools-grid') as HTMLElement;
const matrixContainer = document.getElementById('matrix-container') as HTMLElement;
const aiInterface = document.getElementById('ai-interface') as HTMLElement;
const filtersSection = document.getElementById('filters-section') as HTMLElement;
const noResults = document.getElementById('no-results') as HTMLElement;
const aiQueryBtn = document.getElementById('ai-query-btn') as HTMLButtonElement;
const toolsContainer = document.getElementById('tools-container');
const toolsGrid = document.getElementById('tools-grid');
const matrixContainer = document.getElementById('matrix-container');
const aiInterface = document.getElementById('ai-interface');
const filtersSection = document.getElementById('filters-section');
const noResults = document.getElementById('no-results');
const aiQueryBtn = document.getElementById('ai-query-btn');
// Guard against null elements
if (!toolsContainer || !toolsGrid || !matrixContainer || !noResults || !aiInterface || !filtersSection) {
@@ -138,7 +122,7 @@ const tools = data.tools;
}
// Simple sorting function
function sortTools(tools: any[], sortBy = 'default') {
function sortTools(tools, sortBy = 'default') {
const sorted = [...tools];
switch (sortBy) {
@@ -147,12 +131,12 @@ const tools = data.tools;
case 'difficulty':
const difficultyOrder = { 'novice': 0, 'beginner': 1, 'intermediate': 2, 'advanced': 3, 'expert': 4 };
return sorted.sort((a, b) =>
(difficultyOrder[a.skillLevel as keyof typeof difficultyOrder] || 999) - (difficultyOrder[b.skillLevel as keyof typeof difficultyOrder] || 999)
(difficultyOrder[a.skillLevel] || 999) - (difficultyOrder[b.skillLevel] || 999)
);
case 'type':
const typeOrder = { 'concept': 0, 'method': 1, 'software': 2 };
return sorted.sort((a, b) =>
(typeOrder[a.type as keyof typeof typeOrder] || 999) - (typeOrder[b.type as keyof typeof typeOrder] || 999)
(typeOrder[a.type] || 999) - (typeOrder[b.type] || 999)
);
case 'default':
default:
@@ -160,34 +144,22 @@ const tools = data.tools;
}
}
// Authentication check function
async function checkAuthentication() {
try {
const response = await fetch('/api/auth/status');
const data = await response.json();
return {
authenticated: data.authenticated,
authRequired: data.authRequired
};
} catch (error) {
console.error('Auth check failed:', error);
return {
authenticated: false,
authRequired: true
};
}
}
// AI Query Button Handler
// FIXED: AI Query Button Handler using global client-side auth function
if (aiQueryBtn) {
aiQueryBtn.addEventListener('click', async () => {
await requireClientAuth(() => switchToView('ai'), `${window.location.pathname}?view=ai`);
// Wait for client-side auth functions to be available
if (typeof window.requireClientAuth === 'function') {
await window.requireClientAuth(() => switchToView('ai'), `${window.location.pathname}?view=ai`);
} else {
console.error('requireClientAuth not available - client-auth.js may not be loaded');
// Fallback - try switching anyway
switchToView('ai');
}
});
}
// Function to switch between different views
function switchToView(view: string) {
function switchToView(view) {
// Hide all views first
toolsGrid.style.display = 'none';
matrixContainer.style.display = 'none';
@@ -209,7 +181,7 @@ const tools = data.tools;
if (window.restoreAIResults) {
window.restoreAIResults();
}
const aiInput = document.getElementById('ai-query-input') as HTMLTextAreaElement;
const aiInput = document.getElementById('ai-query-input');
if (aiInput) {
setTimeout(() => aiInput.focus(), 100);
}
@@ -243,19 +215,19 @@ const tools = data.tools;
];
elements.forEach(selector => {
const element = document.querySelector(selector) as HTMLElement;
const element = document.querySelector(selector);
if (element) element.style.display = 'none';
});
const allInputs = filtersSection.querySelectorAll('input, select, textarea');
allInputs.forEach(input => (input as HTMLElement).style.display = 'none');
allInputs.forEach(input => input.style.display = 'none');
}
function showFilterControls() {
const domainPhaseContainer = document.querySelector('.domain-phase-container') as HTMLElement;
const searchInput = document.getElementById('search-input') as HTMLElement;
const tagCloud = document.querySelector('.tag-cloud') as HTMLElement;
const tagHeader = document.querySelector('.tag-header') as HTMLElement;
const domainPhaseContainer = document.querySelector('.domain-phase-container');
const searchInput = document.getElementById('search-input');
const tagCloud = document.querySelector('.tag-cloud');
const tagHeader = document.querySelector('.tag-header');
const checkboxWrappers = document.querySelectorAll('.checkbox-wrapper');
const allInputs = filtersSection.querySelectorAll('input, select, textarea');
@@ -264,12 +236,12 @@ const tools = data.tools;
if (tagCloud) tagCloud.style.display = 'flex';
if (tagHeader) tagHeader.style.display = 'flex';
allInputs.forEach(input => (input as HTMLElement).style.display = 'block');
checkboxWrappers.forEach(wrapper => (wrapper as HTMLElement).style.display = 'flex');
allInputs.forEach(input => input.style.display = 'block');
checkboxWrappers.forEach(wrapper => wrapper.style.display = 'flex');
}
// Create tool slug from name
function createToolSlug(toolName: string): string {
function createToolSlug(toolName) {
return toolName.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
@@ -278,15 +250,15 @@ const tools = data.tools;
}
// Find tool by name or slug
function findTool(identifier: string) {
function findTool(identifier) {
return window.toolsData.find(tool =>
tool.name === identifier ||
createToolSlug(tool.name) === identifier.toLowerCase()
);
}
// Navigation functions for sharing
window.navigateToGrid = function(toolName: string) {
// RESTORED: Navigation functions for sharing - EXACT ORIGINAL VERSIONS
window.navigateToGrid = function(toolName) {
console.log('Navigating to grid for tool:', toolName);
// Switch to grid view first
@@ -302,7 +274,7 @@ const tools = data.tools;
// Wait for filters to clear and re-render
setTimeout(() => {
const toolCards = document.querySelectorAll('.tool-card');
let targetCard: Element | null = null;
let targetCard = null;
toolCards.forEach(card => {
const cardTitle = card.querySelector('h3');
@@ -317,13 +289,12 @@ const tools = data.tools;
if (targetCard) {
console.log('Found target card, scrolling...');
// Cast to Element to fix TypeScript issue
(targetCard as Element).scrollIntoView({ behavior: 'smooth', block: 'center' });
(targetCard as HTMLElement).style.animation = 'highlight-flash 2s ease-out';
targetCard.scrollIntoView({ behavior: 'smooth', block: 'center' });
targetCard.style.animation = 'highlight-flash 2s ease-out';
setTimeout(() => {
if (targetCard) {
(targetCard as HTMLElement).style.animation = '';
targetCard.style.animation = '';
}
}, 2000);
} else {
@@ -333,7 +304,7 @@ const tools = data.tools;
}, 200);
};
window.navigateToMatrix = function(toolName: string) {
window.navigateToMatrix = function(toolName) {
console.log('Navigating to matrix for tool:', toolName);
// Switch to matrix view
@@ -342,7 +313,7 @@ const tools = data.tools;
// Wait for view switch and matrix to render
setTimeout(() => {
const toolChips = document.querySelectorAll('.tool-chip');
let firstMatch: Element | null = null;
let firstMatch = null;
let matchCount = 0;
toolChips.forEach(chip => {
@@ -350,7 +321,7 @@ const tools = data.tools;
const chipText = chip.textContent?.replace(/📖/g, '').replace(/[^\w\s\-\.]/g, '').trim();
if (chipText === toolName) {
// Highlight this occurrence
(chip as HTMLElement).style.animation = 'highlight-flash 2s ease-out';
chip.style.animation = 'highlight-flash 2s ease-out';
matchCount++;
// Remember the first match for scrolling
@@ -360,22 +331,21 @@ const tools = data.tools;
// Clean up animation after it completes
setTimeout(() => {
(chip as HTMLElement).style.animation = '';
chip.style.animation = '';
}, 8000);
}
});
if (firstMatch) {
console.log(`Found ${matchCount} occurrences of tool, highlighting all and scrolling to first`);
// Cast to Element to fix TypeScript issue
(firstMatch as Element).scrollIntoView({ behavior: 'smooth', block: 'center' });
firstMatch.scrollIntoView({ behavior: 'smooth', block: 'center' });
} else {
console.warn('Tool chip not found in matrix:', toolName);
}
}, 500);
};
// Handle URL parameters on page load
// RESTORED: Handle URL parameters on page load - EXACT ORIGINAL VERSION
function handleSharedURL() {
const urlParams = new URLSearchParams(window.location.search);
const toolParam = urlParams.get('tool');
@@ -425,8 +395,7 @@ const tools = data.tools;
// Handle filtered results
window.addEventListener('toolsFiltered', (event) => {
const customEvent = event as CustomEvent;
const filtered = customEvent.detail;
const filtered = event.detail;
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
if (currentView === 'matrix' || currentView === 'ai') {
@@ -443,7 +412,7 @@ const tools = data.tools;
const sortedTools = sortTools(filtered, 'default');
sortedTools.forEach((tool: any) => {
sortedTools.forEach((tool) => {
const toolCard = createToolCard(tool);
toolsContainer.appendChild(toolCard);
});
@@ -452,8 +421,7 @@ const tools = data.tools;
// Handle view changes
window.addEventListener('viewChanged', (event) => {
const customEvent = event as CustomEvent;
const view = customEvent.detail;
const view = event.detail;
switchToView(view);
});
@@ -461,7 +429,7 @@ const tools = data.tools;
window.switchToAIView = () => switchToView('ai');
// Tool card creation function
function createToolCard(tool: any): HTMLElement {
function createToolCard(tool) {
const isMethod = tool.type === 'method';
const isConcept = tool.type === 'concept';
const hasValidProjectUrl = tool.projectUrl !== undefined &&
@@ -545,7 +513,7 @@ const tools = data.tools;
</div>
<div class="tool-tags-container">
${(tool.tags || []).slice(0, 8).map((tag: string) => `<span class="tag">${tag}</span>`).join('')}
${(tool.tags || []).slice(0, 8).map((tag) => `<span class="tag">${tag}</span>`).join('')}
</div>
<div class="tool-card-buttons" onclick="event.stopPropagation();">
@@ -577,7 +545,7 @@ const tools = data.tools;
return cardDiv;
}
// Initialize URL handling
// RESTORED: Initialize URL handling - EXACT ORIGINAL
handleSharedURL();
});
</script>