remove dev comments
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user