remove dev comments

This commit is contained in:
overcuriousity
2025-07-26 15:14:02 +02:00
parent 86d2370976
commit f24531d86d
34 changed files with 71 additions and 693 deletions

View File

@@ -1,14 +1,13 @@
---
// src/pages/contribute/index.astro - Consolidated Auth
import BaseLayout from '../../layouts/BaseLayout.astro';
import { withAuth } from '../../utils/auth.js'; // Note: .js extension!
import { withAuth } from '../../utils/auth.js';
export const prerender = false;
// CONSOLIDATED: Replace 15+ lines with single function call
const authResult = await withAuth(Astro, 'contributions');
if (authResult instanceof Response) {
return authResult; // Redirect to login
return authResult;
}
const { authenticated, userEmail, userId } = authResult;
@@ -16,7 +15,6 @@ const { authenticated, userEmail, userId } = authResult;
<BaseLayout title="Contribute" description="Inhalte zum ForensicPathways beitragen">
<section style="padding: 2rem 0;">
<!-- Header -->
<div style="text-align: center; margin-bottom: 3rem; padding: 2rem; background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%); color: white; border-radius: 1rem; border: 1px solid var(--color-border);">
<h1 style="margin-bottom: 1rem; font-size: 2.5rem;">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.75rem; vertical-align: middle;">
@@ -38,8 +36,6 @@ const { authenticated, userEmail, userId } = authResult;
)}
</div>
<!-- Contribution Options -->
<!-- WRAPPER -->
<div
style="
display:grid;
@@ -50,9 +46,6 @@ const { authenticated, userEmail, userId } = authResult;
"
>
<!-- src/pages/contribute/index.astro - Replace the Tools/Methods/Concepts card -->
<!-- Tools, Methods & Concepts - IMPROVED UX -->
<div class="card"
style="padding: 2rem; border-left: 4px solid var(--color-primary); transition: var(--transition-fast);
display:flex; flex-direction:column;">
@@ -85,7 +78,6 @@ const { authenticated, userEmail, userId } = authResult;
Neuer Eintrag
</a>
<!-- IMPROVED: Clear guidance instead of confusing button -->
<div style="background-color: var(--color-bg-secondary); padding: 1.25rem; border-radius: 0.5rem; border-left: 3px solid var(--color-accent);">
<div style="display: flex; align-items: start; gap: 0.75rem;">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--color-accent)" stroke-width="2" style="flex-shrink: 0; margin-top: 0.125rem;">
@@ -112,7 +104,6 @@ const { authenticated, userEmail, userId } = authResult;
</div>
</div>
</div>
<!-- Knowledgebase Articles -->
<div class="card"
style="padding: 2rem; border-left: 4px solid var(--color-accent); cursor: pointer; transition: var(--transition-fast);
display:flex; flex-direction:column;"
@@ -148,7 +139,6 @@ const { authenticated, userEmail, userId } = authResult;
</div>
</div>
<!-- Issues & Improvements -->
<div class="card"
style="padding: 2rem; border-left: 4px solid var(--color-accent); cursor: pointer; transition: var(--transition-fast);
display:flex; flex-direction:column;">
@@ -187,14 +177,12 @@ const { authenticated, userEmail, userId } = authResult;
</div>
</div>
<!-- Push this actions block down if you add more later -->
<div style="margin-top:auto;"></div>
</div>
</div>
<!-- Guidelines -->
<div class="card" style="margin-bottom: 2rem;">
<h3 style="margin-bottom: 1.5rem; color: var(--color-text);">Richtlinien</h3>
@@ -236,7 +224,6 @@ const { authenticated, userEmail, userId } = authResult;
</div>
<script>
// Add hover effects for cards
document.querySelectorAll('.card[onclick]').forEach((card) => {
const cardEl = card as HTMLElement;
cardEl.addEventListener('mouseenter', function() {

View File

@@ -6,7 +6,6 @@ import { getToolsData } from '../../utils/dataService.js';
export const prerender = false;
// Check authentication
const authResult = await withAuth(Astro, 'contributions');
if (authResult instanceof Response) {
return authResult;
@@ -14,7 +13,6 @@ if (authResult instanceof Response) {
const { authenticated, userEmail, userId } = authResult;
// Load tools for reference (optional dropdown)
const data = await getToolsData();
const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.name));
---
@@ -22,18 +20,15 @@ const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.n
<BaseLayout title="Contribute Knowledge Base Article">
<div class="container" style="max-width: 900px; margin: 0 auto; padding: 2rem 1rem;">
<!-- Header -->
<div style="text-align: center; margin-bottom: 2rem; padding: 2rem; background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%); color: white; border-radius: 1rem;">
<h1 style="margin-bottom: 1rem; font-size: 2rem;">Knowledgebase-Artikel</h1>
<p style="margin: 0.5rem 0; opacity: 0.9;">Danke für deinen Beitrag!</p>
{userEmail && <p style="margin: 0.5rem 0; opacity: 0.8;"><strong>Eingeloggt als:</strong> {userEmail}</p>}
</div>
<!-- Main Form -->
<div class="card">
<form id="kb-form" novalidate>
<!-- Basic Information -->
<div class="form-section">
<h3 class="section-title">Grundinformationen</h3>
@@ -87,7 +82,6 @@ const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.n
</div>
</div>
<!-- Content -->
<div class="form-section">
<h3 class="section-title">Inhalt</h3>
@@ -114,7 +108,6 @@ const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.n
</div>
</div>
<!-- File Upload -->
<div class="form-section">
<h3 class="section-title">Dateien hochladen</h3>
@@ -139,7 +132,6 @@ const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.n
</div>
</div>
<!-- Additional Information -->
<div class="form-section">
<h3 class="section-title">Zusatzinformation</h3>
@@ -181,7 +173,6 @@ const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.n
</div>
</div>
<!-- Submit Button -->
<div class="form-actions">
<a href="/" class="btn btn-secondary">Abbruch</a>
<button type="submit" id="submit-btn" class="btn btn-accent">
@@ -192,7 +183,6 @@ const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.n
</form>
</div>
<!-- Success Modal -->
<div id="success-modal"
style="display:none; position:fixed; top:0; left:0; width:100%; height:100%;
background:rgba(0,0,0,.5); z-index:1000; align-items:center; justify-content:center;">
@@ -209,7 +199,6 @@ const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.n
</div>
</div>
<!-- Message Container -->
<div id="message-container" class="message-container"></div>
</div>
</BaseLayout>
@@ -239,7 +228,6 @@ class KnowledgebaseForm {
}
private init() {
// Get elements
this.elements = {
form: document.getElementById('kb-form'),
submitBtn: document.getElementById('submit-btn'),
@@ -262,7 +250,6 @@ class KnowledgebaseForm {
}
private setupEventListeners() {
// Form submission
this.elements.form?.addEventListener('submit', (e) => {
e.preventDefault();
if (!this.isSubmitting) {
@@ -381,7 +368,6 @@ class KnowledgebaseForm {
this.isSubmitting = true;
// Update UI
(this.elements.submitBtn as HTMLButtonElement).disabled = true;
(this.elements.submitText as HTMLElement).textContent = 'Submitting...';
(this.elements.submitSpinner as HTMLElement).style.display = 'inline';
@@ -389,7 +375,6 @@ class KnowledgebaseForm {
try {
const formData = new FormData(this.elements.form as HTMLFormElement);
// Process categories and tags
const categoriesValue = (formData.get('categories') as string) || '';
const tagsValue = (formData.get('tags') as string) || '';
@@ -398,7 +383,6 @@ class KnowledgebaseForm {
formData.set('categories', JSON.stringify(categories));
formData.set('tags', JSON.stringify(tags));
// Add uploaded files
formData.set('uploadedFiles', JSON.stringify(this.uploadedFiles.filter(f => f.uploaded)));
const response = await fetch('/api/contribute/knowledgebase', {
@@ -440,7 +424,6 @@ class KnowledgebaseForm {
(this.elements.successModal as HTMLElement).style.display = 'flex';
// Reset form
(this.elements.form as HTMLFormElement).reset();
this.uploadedFiles = [];
this.renderFileList();
@@ -458,23 +441,19 @@ class KnowledgebaseForm {
setTimeout(() => messageEl.remove(), 5000);
}
// Public method for file removal
public removeFileById(fileId: string) {
this.removeFile(fileId);
}
}
// Global instance
let formInstance: KnowledgebaseForm;
// Global function for file removal
window.removeFile = (fileId: string) => {
if (formInstance) {
formInstance.removeFileById(fileId);
}
};
// Initialize form
document.addEventListener('DOMContentLoaded', () => {
formInstance = new KnowledgebaseForm();
});

View File

@@ -1,12 +1,11 @@
---
// src/pages/contribute/tool.astro - COMPLETE REWRITE
// src/pages/contribute/tool.astro
import BaseLayout from '../../layouts/BaseLayout.astro';
import { withAuth } from '../../utils/auth.js';
import { getToolsData } from '../../utils/dataService.js';
export const prerender = false;
// Check authentication
const authResult = await withAuth(Astro, 'contributions');
if (authResult instanceof Response) {
return authResult;
@@ -14,14 +13,12 @@ if (authResult instanceof Response) {
const { authenticated, userEmail, userId } = authResult;
// Load existing data
const data = await getToolsData();
const domains = data.domains;
const phases = data.phases;
const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
const existingTools = data.tools;
// Check if this is an edit operation
const editToolName = Astro.url.searchParams.get('edit');
const editTool = editToolName ? existingTools.find(tool => tool.name === editToolName) : null;
const isEdit = !!editTool;
@@ -30,7 +27,6 @@ const isEdit = !!editTool;
<BaseLayout title={isEdit ? `Edit ${editTool?.name}` : 'Contribute Tool'}>
<div class="container" style="max-width: 900px; margin: 0 auto; padding: 2rem 1rem;">
<!-- Header -->
<div style="text-align: center; margin-bottom: 2rem; padding: 2rem; background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%); color: white; border-radius: 1rem;">
<h1 style="margin-bottom: 1rem; font-size: 2rem;">{isEdit ? `Edit: ${editTool?.name}` : 'Tool / Methode / Konzept beitragen'}</h1>
<p style="margin: 0.5rem 0; opacity: 0.9;">
@@ -42,17 +38,14 @@ const isEdit = !!editTool;
{userEmail && <p style="margin: 0.5rem 0; opacity: 0.8;"><strong>Eingeloggt als:</strong> {userEmail}</p>}
</div>
<!-- Validation Error Display -->
<div id="validation-errors" class="card" style="display: none; background-color: var(--color-error); color: white; margin-bottom: 2rem;">
<h3 style="margin: 0 0 1rem 0;">⚠️ Please fix the following issues:</h3>
<ul id="error-list" style="margin: 0; padding-left: 1.5rem;"></ul>
</div>
<!-- Main Form -->
<div class="card">
<form id="contribution-form" novalidate style="padding: 2rem;">
<!-- Basic Information -->
<div style="border: 1px solid var(--color-border); border-radius: 0.5rem; padding: 1.5rem; margin-bottom: 2rem;">
<h3 style="margin: 0 0 1.5rem 0; color: var(--color-primary); border-bottom: 1px solid var(--color-border); padding-bottom: 0.5rem;">Grundlegende Infos</h3>
@@ -109,7 +102,6 @@ const isEdit = !!editTool;
</div>
</div>
<!-- Categories -->
<div style="border: 1px solid var(--color-border); border-radius: 0.5rem; padding: 1.5rem; margin-bottom: 2rem;">
<h3 style="margin: 0 0 1.5rem 0; color: var(--color-primary); border-bottom: 1px solid var(--color-border); padding-bottom: 0.5rem;">Kategorien</h3>
@@ -142,7 +134,6 @@ const isEdit = !!editTool;
</div>
</div>
<!-- Software-Specific Fields -->
<div id="software-fields" style="border: 1px solid var(--color-border); border-radius: 0.5rem; padding: 1.5rem; margin-bottom: 2rem; display: none;">
<h3 style="margin: 0 0 1.5rem 0; color: var(--color-primary); border-bottom: 1px solid var(--color-border); padding-bottom: 0.5rem;">Software Details</h3>
@@ -203,7 +194,6 @@ const isEdit = !!editTool;
</div>
</div>
<!-- Related Concepts -->
<div id="concepts-fields" style="border: 1px solid var(--color-border); border-radius: 0.5rem; padding: 1.5rem; margin-bottom: 2rem; display: none;">
<h3 style="margin: 0 0 1.5rem 0; color: var(--color-primary); border-bottom: 1px solid var(--color-border); padding-bottom: 0.5rem;">Konzepte im Zusammenhang</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0.5rem;">
@@ -217,7 +207,6 @@ const isEdit = !!editTool;
</div>
</div>
<!-- Additional Information -->
<div style="border: 1px solid var(--color-border); border-radius: 0.5rem; padding: 1.5rem; margin-bottom: 2rem;">
<h3 style="margin: 0 0 1.5rem 0; color: var(--color-primary); border-bottom: 1px solid var(--color-border); padding-bottom: 0.5rem;">Zusatzinfos</h3>
@@ -245,7 +234,6 @@ const isEdit = !!editTool;
</div>
</div>
<!-- YAML Preview -->
<div style="border: 1px solid var(--color-border); border-radius: 0.5rem; padding: 1.5rem; margin-bottom: 2rem;">
<h3 style="margin: 0 0 1.5rem 0; color: var(--color-primary); border-bottom: 1px solid var(--color-border); padding-bottom: 0.5rem;">Preview</h3>
<div style="border: 1px solid var(--color-border); border-radius: 0.375rem; overflow: hidden;">
@@ -253,7 +241,6 @@ const isEdit = !!editTool;
</div>
</div>
<!-- Submit -->
<div style="display: flex; gap: 1rem; justify-content: flex-end; margin-top: 2rem; padding-top: 2rem; border-top: 1px solid var(--color-border);">
<a href="/" class="btn btn-secondary">Cancel</a>
<button type="submit" id="submit-btn" class="btn btn-primary">
@@ -264,7 +251,6 @@ const isEdit = !!editTool;
</form>
</div>
<!-- Success Modal -->
<div id="success-modal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 1000; align-items: center; justify-content: center;">
<div class="card" style="max-width: 500px; width: 90%; margin: 2rem; text-align: center;">
<div style="font-size: 3rem; margin-bottom: 1rem;">✅</div>
@@ -280,7 +266,6 @@ const isEdit = !!editTool;
</BaseLayout>
<script define:vars={{ isEdit, editTool, domains, phases, domainAgnosticSoftware }}>
// FIXED: Prevent duplicate form submissions
console.log('[FORM] Script loaded, initializing...');
class ContributionForm {
@@ -288,14 +273,13 @@ class ContributionForm {
this.isEdit = isEdit;
this.editTool = editTool;
this.elements = {};
this.isSubmitting = false; // NEW: Prevent concurrent submissions
this.isSubmitting = false;
this.init();
}
init() {
console.log('[FORM] Starting initialization...');
// Get all form elements
this.elements = {
form: document.getElementById('contribution-form'),
submitBtn: document.getElementById('submit-btn'),
@@ -320,19 +304,16 @@ class ContributionForm {
licenseInput: document.getElementById('license')
};
// Verify critical elements
if (!this.elements.form || !this.elements.submitBtn) {
console.error('[FORM] Critical elements missing!');
return;
}
// FIXED: Check if already initialized
if (this.elements.form.hasAttribute('data-form-initialized')) {
console.log('[FORM] Form already initialized, skipping...');
return;
}
// Mark as initialized
this.elements.form.setAttribute('data-form-initialized', 'true');
console.log('[FORM] Setting up handlers...');
@@ -345,13 +326,11 @@ class ContributionForm {
}
setupEventListeners() {
// Type change handler
this.elements.typeSelect.addEventListener('change', () => {
this.updateFieldVisibility();
this.updateYAMLPreview();
});
// Form input handlers
this.elements.form.addEventListener('input', () => {
this.debounce(() => this.updateYAMLPreview(), 300);
});
@@ -360,12 +339,10 @@ class ContributionForm {
this.updateYAMLPreview();
});
// FIXED: Single submit handler with double-submission prevention
this.elements.form.addEventListener('submit', (e) => {
e.preventDefault();
e.stopPropagation();
// Prevent double submission
if (this.isSubmitting) {
console.log('[FORM] Submission already in progress, ignoring...');
return;
@@ -380,15 +357,12 @@ class ContributionForm {
updateFieldVisibility() {
const type = this.elements.typeSelect.value;
// Hide all conditional fields
this.elements.softwareFields.style.display = 'none';
this.elements.conceptsFields.style.display = 'none';
// Hide required indicators
if (this.elements.platformsRequired) this.elements.platformsRequired.style.display = 'none';
if (this.elements.licenseRequired) this.elements.licenseRequired.style.display = 'none';
// Show relevant fields based on type
if (type === 'software') {
this.elements.softwareFields.style.display = 'block';
this.elements.conceptsFields.style.display = 'block';
@@ -437,12 +411,10 @@ updateYAMLPreview() {
url: formData.get('url') || 'https://example.com'
};
// Add icon if provided
if (formData.get('icon')) {
tool.icon = formData.get('icon');
}
// Add software-specific fields
if (tool.type === 'software') {
tool.platforms = formData.getAll('platforms');
tool.license = formData.get('license') || 'Unknown';
@@ -455,7 +427,6 @@ updateYAMLPreview() {
}
}
// Add optional fields
if (formData.has('knowledgebase')) {
tool.knowledgebase = true;
}
@@ -470,7 +441,6 @@ updateYAMLPreview() {
tool.related_concepts = relatedConcepts;
}
// Generate YAML
const yaml = this.generateYAML(tool);
this.elements.yamlPreview.textContent = yaml;
@@ -515,7 +485,6 @@ validateForm() {
const errors = [];
const formData = new FormData(this.elements.form);
// Required field validation
const name = formData.get('name')?.trim();
if (!name) {
errors.push('Tool name is required');
@@ -549,7 +518,6 @@ validateForm() {
}
}
// Software-specific validation
if (type === 'software') {
const platforms = formData.getAll('platforms');
if (platforms.length === 0) {
@@ -571,26 +539,21 @@ showValidationErrors(errors) {
return;
}
// Clear previous errors
this.elements.errorList.innerHTML = '';
// Add each error as list item
errors.forEach(error => {
const li = document.createElement('li');
li.textContent = error;
this.elements.errorList.appendChild(li);
});
// Show error container
this.elements.validationErrors.style.display = 'block';
// Scroll to top to show errors
this.elements.validationErrors.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
async handleSubmit() {
console.log('[FORM] Submit handler called!');
// FIXED: Immediate submission lock
if (this.isSubmitting) {
console.log('[FORM] Already submitting, aborting...');
return;
@@ -598,19 +561,16 @@ showValidationErrors(errors) {
this.isSubmitting = true;
// Validate before submitting
const validationErrors = this.validateForm();
if (validationErrors.length > 0) {
console.log('[FORM] Validation failed:', validationErrors);
this.showValidationErrors(validationErrors);
this.isSubmitting = false; // Reset lock
this.isSubmitting = false;
return;
}
// Hide validation errors
this.elements.validationErrors.style.display = 'none';
// Immediate UI feedback
this.elements.submitBtn.disabled = true;
this.elements.submitText.textContent = this.isEdit ? 'Updating...' : 'Submitting...';
this.elements.submitSpinner.style.display = 'inline';
@@ -618,7 +578,6 @@ showValidationErrors(errors) {
try {
const formData = new FormData(this.elements.form);
// Build submission object
const submission = {
action: this.isEdit ? 'edit' : 'add',
tool: {
@@ -637,11 +596,9 @@ showValidationErrors(errors) {
}
};
// Add optional fields
if (formData.get('icon')) submission.tool.icon = formData.get('icon');
if (formData.has('knowledgebase')) submission.tool.knowledgebase = true;
// Add software-specific fields
if (submission.tool.type === 'software') {
submission.tool.platforms = formData.getAll('platforms');
submission.tool.license = formData.get('license');
@@ -654,7 +611,6 @@ showValidationErrors(errors) {
}
}
// Add related concepts
if (submission.tool.type !== 'concept') {
const related = formData.getAll('relatedConcepts');
if (related.length > 0) {
@@ -664,7 +620,6 @@ showValidationErrors(errors) {
console.log('[FORM] Sending submission:', submission);
// Submit to API
const response = await fetch('/api/contribute/tool', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -685,7 +640,6 @@ showValidationErrors(errors) {
console.error('[FORM] Submission error:', error);
alert(`Submission failed: ${error.message}\n\nPlease try again or contact support if the problem persists.`);
} finally {
// FIXED: Always reset submission state
this.isSubmitting = false;
this.elements.submitBtn.disabled = false;
this.elements.submitText.textContent = this.isEdit ? 'Update Tool' : 'Submit Contribution';
@@ -694,13 +648,11 @@ showValidationErrors(errors) {
}
showSuccess(result) {
// Update success message
const successMessage = document.getElementById('success-message');
if (successMessage) {
successMessage.textContent = `Your ${this.isEdit ? 'update' : 'contribution'} has been submitted as an issue and will be reviewed by maintainers.`;
}
// Show issue link if available
if (result.issueUrl) {
const prLink = document.getElementById('pr-link');
if (prLink) {
@@ -710,7 +662,6 @@ showValidationErrors(errors) {
}
}
// Show modal
this.elements.successModal.style.display = 'flex';
}
@@ -723,7 +674,6 @@ showValidationErrors(errors) {
}
}
// FIXED: Single initialization only
function initializeForm() {
const form = document.getElementById('contribution-form');
if (!form) {
@@ -740,7 +690,6 @@ function initializeForm() {
new ContributionForm();
}
// FIXED: Simple initialization
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeForm);
} else {