initial commit

This commit is contained in:
2026-01-28 14:02:01 +01:00
commit 8ad8129a9d
71 changed files with 5601 additions and 0 deletions

208
scripts/cursor.js Normal file
View File

@@ -0,0 +1,208 @@
// cursor.js
document.addEventListener("DOMContentLoaded", function () {
// 1. Проверка на мобильные (там курсор не нужен)
if (window.matchMedia("(pointer: coarse)").matches) return;
// --- SETTINGS ---
const CONFIG = {
tentacleCount: 10,
triggerDist: 20,
maxLength: 450,
connectionDist: 150,
thickness: 1,
color: "rgba(20, 20, 20, 1)",
prediction: 3.5
};
const toggleBtn = document.getElementById('cursorToggle');
const body = document.body;
let isCursorDisabled = localStorage.getItem('venomCursorDisabled') === 'true';
function updateCursorState() {
if (isCursorDisabled) {
body.classList.add('system-cursor');
if (toggleBtn) {
toggleBtn.classList.remove('active');
const icon = toggleBtn.querySelector('.cursor-icon');
if (icon) {
// Check if we're on a page in the sites/ folder
const currentPath = window.location.pathname;
const isInSitesFolder = currentPath.includes('/sites/');
const imagePath = isInSitesFolder ? '../images/additional/cursor.png' : 'images/additional/cursor.png';
icon.src = imagePath;
}
}
} else {
body.classList.remove('system-cursor');
if (toggleBtn) {
toggleBtn.classList.add('active');
const icon = toggleBtn.querySelector('.cursor-icon');
if (icon) {
// Check if we're on a page in the sites/ folder
const currentPath = window.location.pathname;
const isInSitesFolder = currentPath.includes('/sites/');
const imagePath = isInSitesFolder ? '../images/additional/spidy.png' : 'images/additional/spidy.png';
icon.src = imagePath;
}
}
}
}
updateCursorState();
if (toggleBtn) {
toggleBtn.addEventListener('click', () => {
isCursorDisabled = !isCursorDisabled;
localStorage.setItem('venomCursorDisabled', isCursorDisabled);
updateCursorState();
});
}
const canvas = document.createElement("canvas");
canvas.id = "venom-cursor";
document.body.appendChild(canvas);
const ctx = canvas.getContext("2d");
const width = window.innerWidth;
const height = window.innerHeight;
canvas.width = width;
canvas.height = height;
const tentacles = [];
const mouse = { x: 0, y: 0 };
const oldMouse = { x: 0, y: 0 };
document.addEventListener("mousemove", (e) => {
mouse.x = e.clientX;
mouse.y = e.clientY;
});
window.addEventListener("resize", () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
class Tentacle {
constructor(mx, my, targetX, targetY) {
this.dead = false;
this.anchor = { x: targetX, y: targetY };
this.currentDist = 0; // For calculating connection opacity
}
update(currentMouse) {
const dx = currentMouse.x - this.anchor.x;
const dy = currentMouse.y - this.anchor.y;
this.currentDist = Math.sqrt(dx*dx + dy*dy);
if (this.currentDist > CONFIG.maxLength) {
this.dead = true;
}
}
draw(ctx, currentMouse) {
if (this.dead) return;
// Tension (0..1)
const tension = Math.min(this.currentDist / CONFIG.maxLength, 1);
const dynamicThickness = CONFIG.thickness * (1 - tension * 0.9);
// Draw main line (Cursor -> Anchor)
ctx.beginPath();
ctx.moveTo(currentMouse.x, currentMouse.y);
ctx.lineTo(this.anchor.x, this.anchor.y);
ctx.lineWidth = Math.max(0.2, dynamicThickness);
ctx.strokeStyle = CONFIG.color;
ctx.lineCap = "butt";
ctx.stroke();
// Draw anchor point
ctx.beginPath();
ctx.arc(this.anchor.x, this.anchor.y, 1.5 * (1 - tension), 0, Math.PI * 2);
ctx.fillStyle = CONFIG.color;
ctx.fill();
}
}
function render() {
ctx.clearRect(0, 0, width, height);
// 1. Create new tentacles on movement
const distMoved = Math.hypot(mouse.x - oldMouse.x, mouse.y - oldMouse.y);
if (distMoved > CONFIG.triggerDist) {
const vx = mouse.x - oldMouse.x;
const vy = mouse.y - oldMouse.y;
// "Spread" of shots increased slightly for better geometry
const targetX = mouse.x + vx * CONFIG.prediction + (Math.random() - 0.5) * 60;
const targetY = mouse.y + vy * CONFIG.prediction + (Math.random() - 0.5) * 60;
tentacles.push(new Tentacle(mouse.x, mouse.y, targetX, targetY));
oldMouse.x = mouse.x;
oldMouse.y = mouse.y;
}
// Remove old ones (FIFO)
if (tentacles.length > CONFIG.tentacleCount) {
tentacles.shift();
}
// 2. Draw main tentacles
for (let i = tentacles.length - 1; i >= 0; i--) {
const t = tentacles[i];
t.update(mouse);
if (t.dead) {
tentacles.splice(i, 1);
} else {
t.draw(ctx, mouse);
}
}
// 3. DRAW CONNECTIONS BETWEEN ANCHORS (New logic)
// Iterate through all pairs of active tentacles
ctx.beginPath(); // Begin common path for optimization
ctx.lineWidth = 0.5; // Connections are always thin
for (let i = 0; i < tentacles.length; i++) {
for (let j = i + 1; j < tentacles.length; j++) {
const t1 = tentacles[i];
const t2 = tentacles[j];
// Calculate distance between ends of two tentacles
const dx = t1.anchor.x - t2.anchor.x;
const dy = t1.anchor.y - t2.anchor.y;
const dist = Math.sqrt(dx*dx + dy*dy);
// If they are close to each other — connect
if (dist < CONFIG.connectionDist) {
// Opacity depends on how far apart they are
// And how much the tentacles themselves are stretched
const alpha = (1 - dist / CONFIG.connectionDist) * 0.6;
ctx.beginPath(); // New path for each to control opacity
ctx.strokeStyle = `rgba(20, 20, 20, ${alpha})`;
ctx.moveTo(t1.anchor.x, t1.anchor.y);
ctx.lineTo(t2.anchor.x, t2.anchor.y);
ctx.stroke();
}
}
}
// 4. Cursor (Rhombus)
ctx.beginPath();
ctx.fillStyle = CONFIG.color;
ctx.moveTo(mouse.x, mouse.y - 5);
ctx.lineTo(mouse.x + 5, mouse.y);
ctx.lineTo(mouse.x, mouse.y + 5);
ctx.lineTo(mouse.x - 5, mouse.y);
ctx.fill();
requestAnimationFrame(render);
}
render();
});

124
scripts/details.js Normal file
View File

@@ -0,0 +1,124 @@
// details.js
document.addEventListener("DOMContentLoaded", function() {
const serviceDetails = {
'seo-optimierung': {
title: 'SEO Optimierung',
icon: '🔍',
description: 'Verbessern Sie Ihre Sichtbarkeit in Suchmaschinen und erreichen Sie mehr Kunden online.',
fullDescription: 'Unsere SEO-Optimierungsdienste helfen Ihnen, in Suchmaschinen besser gefunden zu werden. Wir analysieren Ihre Website, identifizieren Optimierungspotenziale und implementieren bewährte Strategien, um Ihr Ranking zu verbessern und mehr qualifizierte Besucher anzuziehen.',
features: [
'Keyword-Analyse und -Recherche',
'On-Page Optimierung',
'Technische SEO-Audits',
'Content-Strategie',
'Link-Building',
'Performance-Tracking'
],
benefits: [
'Höhere Sichtbarkeit in Suchmaschinen',
'Mehr qualifizierte Besucher',
'Verbesserte Conversion-Raten',
'Langfristiger ROI'
]
},
'cloud-migration': {
title: 'Cloud Migration',
icon: '☁️',
description: 'Modernisieren Sie Ihre IT-Infrastruktur mit sicheren und skalierbaren Cloud-Lösungen.',
fullDescription: 'Wir begleiten Sie bei der Migration Ihrer IT-Systeme in die Cloud. Von der Analyse Ihrer bestehenden Infrastruktur bis zur vollständigen Implementierung sorgen wir für einen reibungslosen Übergang mit minimalem Geschäftsausfall.',
features: [
'Infrastruktur-Analyse',
'Migrationsplanung',
'Datenübertragung',
'Sicherheit & Compliance',
'Performance-Optimierung',
'Schulung & Support'
],
benefits: [
'Kosteneinsparungen',
'Bessere Skalierbarkeit',
'Erhöhte Sicherheit',
'Flexiblere Arbeitsweise'
]
},
'datenanalyse': {
title: 'Datenanalyse',
icon: '📊',
description: 'Gewinnen Sie wertvolle Einblicke aus Ihren Daten mit unseren Analyse-Lösungen.',
fullDescription: 'Wir helfen Ihnen, Ihre Daten in wertvolle Erkenntnisse umzuwandeln. Mit modernen Analyse-Tools und -Methoden identifizieren wir Trends, Muster und Chancen, die Ihre Geschäftsentscheidungen verbessern.',
features: [
'Datenerfassung & -bereinigung',
'Statistische Analyse',
'Visualisierung & Dashboards',
'Predictive Analytics',
'Berichterstellung',
'Consulting'
],
benefits: [
'Bessere Entscheidungen',
'Prozessoptimierung',
'Kostensenkungen',
'Wettbewerbsvorteile'
]
}
};
const urlParams = new URLSearchParams(window.location.search);
const serviceId = urlParams.get('service');
const detailsContainer = document.getElementById('service-details');
if (serviceId && serviceDetails[serviceId]) {
const service = serviceDetails[serviceId];
detailsContainer.innerHTML = `
<div class="service-header">
<div class="service-icon-large">${service.icon}</div>
<h1 class="service-title-large">${service.title}</h1>
<p class="service-description-large">${service.description}</p>
</div>
<div class="service-content">
<section class="service-section">
<h2 class="section-title">Beschreibung</h2>
<p class="section-text">${service.fullDescription}</p>
</section>
<section class="service-section">
<h2 class="section-title">Unsere Leistungen</h2>
<ul class="feature-list">
${service.features.map(feature => `<li>${feature}</li>`).join('')}
</ul>
</section>
<section class="service-section">
<h2 class="section-title">Ihre Vorteile</h2>
<ul class="benefit-list">
${service.benefits.map(benefit => `<li>${benefit}</li>`).join('')}
</ul>
</section>
<section class="service-section">
<h2 class="section-title">Interesse?</h2>
<p class="section-text">Kontaktieren Sie uns für eine persönliche Beratung und ein maßgeschneidertes Angebot.</p>
<div class="cta-buttons">
<a href="offers.html" class="cta-btn primary">Kontakt aufnehmen</a>
<a href="leads.html" class="cta-btn secondary">Zurück zum Dashboard</a>
</div>
</section>
</div>
`;
// Update page title
document.title = `Profice - ${service.title}`;
} else {
// Fallback if no service specified
detailsContainer.innerHTML = `
<div class="error-message">
<h2>Service nicht gefunden</h2>
<p>Der angeforderte Service konnte nicht gefunden werden.</p>
<a href="leads.html" class="cta-btn secondary">Zurück zum Dashboard</a>
</div>
`;
}
});

203
scripts/lead-details.js Normal file
View File

@@ -0,0 +1,203 @@
// lead-details.js
document.addEventListener("DOMContentLoaded", function() {
const urlParams = new URLSearchParams(window.location.search);
const leadId = urlParams.get('id');
const detailsContainer = document.getElementById('leadDetailsContent');
function getLeads() {
const storedLeads = localStorage.getItem('myLeads');
if (storedLeads) {
return JSON.parse(storedLeads);
} else {
return [];
}
}
function getStatusClass(status) {
const statusClasses = {
'neu': 'status-new',
'in-bearbeitung': 'status-in-progress',
'abgeschlossen': 'status-completed',
'storniert': 'status-cancelled'
};
return statusClasses[status] || 'status-new';
}
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
if (leadId && detailsContainer) {
const leadsData = getLeads();
const lead = leadsData.find(l => l.id == leadId);
if (lead) {
detailsContainer.innerHTML = `
<div class="lead-details-card">
<!-- Status Section -->
<div class="detail-section">
<h2 class="section-title">Status</h2>
<div class="status-container">
<span class="status-badge ${getStatusClass(lead.status)}">${lead.statusText}</span>
<p class="status-info">Letzte Aktualisierung: ${formatDate(lead.datum)}</p>
</div>
</div>
<!-- Contact Information -->
<div class="detail-section">
<h2 class="section-title">Kontaktinformationen</h2>
<div class="info-grid">
<div class="info-item">
<div class="info-label">Name</div>
<div class="info-value">${lead.name || 'Nicht angegeben'}</div>
</div>
<div class="info-item">
<div class="info-label">Organisation</div>
<div class="info-value">${lead.organisation || 'Nicht angegeben'}</div>
</div>
<div class="info-item">
<div class="info-label">Kontakt</div>
<div class="info-value">${lead.contact || 'Nicht angegeben'}</div>
</div>
<div class="info-item">
<div class="info-label">Budget</div>
<div class="info-value">${lead.budget || 'Nicht angegeben'}</div>
</div>
</div>
</div>
<!-- Service Information -->
<div class="detail-section">
<h2 class="section-title">Dienstleistung</h2>
<div class="service-info">
<div class="service-icon">${getServiceIcon(lead.dienstleistung)}</div>
<div class="service-details">
<h3 class="service-title">${lead.dienstleistung}</h3>
<p class="service-description">${getServiceDescription(lead.dienstleistung)}</p>
</div>
</div>
</div>
<!-- Project Description -->
${lead.description ? `
<div class="detail-section">
<h2 class="section-title">Projektbeschreibung</h2>
<div class="description-box">
<p class="description-text">${lead.description}</p>
</div>
</div>
` : ''}
<!-- Timeline -->
<div class="detail-section">
<h2 class="section-title">Zeitstrahl</h2>
<div class="timeline">
<div class="timeline-item active">
<div class="timeline-dot"></div>
<div class="timeline-content">
<h4>Anfrage erhalten</h4>
<p>${formatDate(lead.datum)}</p>
</div>
</div>
<div class="timeline-item ${lead.status !== 'neu' ? 'active' : ''}">
<div class="timeline-dot"></div>
<div class="timeline-content">
<h4>In Bearbeitung</h4>
<p>${lead.status !== 'neu' ? 'Anfrage wird bearbeitet' : 'Ausstehend'}</p>
</div>
</div>
<div class="timeline-item ${lead.status === 'abgeschlossen' ? 'active' : ''}">
<div class="timeline-dot"></div>
<div class="timeline-content">
<h4>Abgeschlossen</h4>
<p>${lead.status === 'abgeschlossen' ? 'Projekt erfolgreich abgeschlossen' : 'Ausstehend'}</p>
</div>
</div>
</div>
</div>
<!-- Actions -->
<div class="detail-section">
<h2 class="section-title">Aktionen</h2>
<div class="action-buttons">
<button class="action-btn primary" onclick="window.print()">
🖨️ Details drucken
</button>
<button class="action-btn secondary" onclick="shareLead()">
📤 Teilen
</button>
<button class="action-btn secondary" onclick="exportLead()">
📄 Exportieren
</button>
</div>
</div>
</div>
`;
// Update page title
document.title = `Profice - Anfrage von ${lead.name || 'Unbekannt'}`;
} else {
detailsContainer.innerHTML = `
<div class="error-message">
<h2>Anfrage nicht gefunden</h2>
<p>Die angeforderte Anfrage konnte nicht gefunden werden.</p>
<a href="leads.html" class="cta-btn secondary">Zurück zum Dashboard</a>
</div>
`;
}
} else {
detailsContainer.innerHTML = `
<div class="error-message">
<h2>Keine Anfrage-ID angegeben</h2>
<p>Bitte navigieren Sie über das Leads Dashboard zu den Details.</p>
<a href="leads.html" class="cta-btn secondary">Zum Dashboard</a>
</div>
`;
}
});
function getServiceIcon(service) {
const icons = {
'Website': '🌐',
'KI Integration': '🤖',
'Automatisation': '⚙️',
'Unabhängige Wahl': '🎯'
};
return icons[service] || '📋';
}
function getServiceDescription(service) {
const descriptions = {
'Website': 'Moderne, responsive Webseiten, die konvertieren und Ihre Marke perfekt repräsentieren.',
'KI Integration': 'Nutzen Sie die Kraft künstlicher Intelligenz, um Ihre Daten besser zu verstehen und Prozesse zu optimieren.',
'Automatisation': 'Sparen Sie Zeit und Ressourcen durch intelligente Workflow-Automatisierungen.',
'Unabhängige Wahl': 'Maßgeschneiderte Lösungen für Ihre spezifischen Anforderungen.'
};
return descriptions[service] || 'Individuelle Dienstleistung.';
}
function shareLead() {
if (navigator.share) {
navigator.share({
title: document.title,
text: 'Details meiner Projektanfrage bei Profice',
url: window.location.href
});
} else {
// Fallback for browsers that don't support Web Share API
navigator.clipboard.writeText(window.location.href);
alert('Link wurde in die Zwischenablage kopiert!');
}
}
function exportLead() {
window.print();
}

75
scripts/leads.js Normal file
View File

@@ -0,0 +1,75 @@
// leads.js
// ==========================================
// 1. MENU TOGGLE
// ==========================================
const menuToggle = document.getElementById('menuToggle');
const slideMenu = document.getElementById('slideMenu');
const overlay = document.getElementById('overlay');
if (menuToggle && slideMenu && overlay) {
function toggleMenu() {
menuToggle.classList.toggle('active');
slideMenu.classList.toggle('active');
overlay.classList.toggle('active');
}
menuToggle.addEventListener('click', toggleMenu);
overlay.addEventListener('click', toggleMenu);
}
// ==========================================
// 2. LEADS TABLE LOGIC
// ==========================================
function getLeads() {
const storedLeads = localStorage.getItem('myLeads');
if (storedLeads) {
return JSON.parse(storedLeads);
} else {
return [];
}
}
function populateLeadsTable() {
const tableBody = document.getElementById('leadsTableBody');
const leadsData = getLeads();
if (!tableBody) return;
if (leadsData.length === 0) {
tableBody.innerHTML = `
<tr>
<td colspan="4" class="empty-state">
<p>Keine Anfragen vorhanden</p>
</td>
</tr>
`;
return;
}
tableBody.innerHTML = leadsData.map(lead => `
<tr>
<td>${lead.datum}</td>
<td>${lead.dienstleistung}</td>
<td>
<span class="status-badge ${lead.status}">${lead.statusText}</span>
</td>
<td>
<a href="#" class="action-btn" data-id="${lead.id}">Details ansehen</a>
</td>
</tr>
`).join('');
document.querySelectorAll('.action-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
const leadId = this.getAttribute('data-id');
// Redirect to lead details page
window.location.href = `lead-details.html?id=${leadId}`;
});
});
}
document.addEventListener('DOMContentLoaded', populateLeadsTable);

207
scripts/login.js Normal file
View File

@@ -0,0 +1,207 @@
// login.js
document.addEventListener("DOMContentLoaded", function() {
console.log('Login page loaded');
const loginForm = document.getElementById('loginForm');
const loginBtn = document.getElementById('loginSubmit');
const successMessage = document.getElementById('successMessage');
const errorMessage = document.getElementById('errorMessage');
const errorText = document.getElementById('errorText');
const registerBtn = document.getElementById('registerBtn');
console.log('Elements found:', {
loginForm: !!loginForm,
loginBtn: !!loginBtn,
successMessage: !!successMessage,
errorMessage: !!errorMessage,
registerBtn: !!registerBtn
});
// Register button functionality
if (registerBtn) {
registerBtn.addEventListener('click', function(e) {
e.preventDefault();
console.log('Register button clicked on login page');
window.location.href = 'register.html';
});
}
// Check if user is already logged in
checkExistingSession();
// Login form submission
if (loginForm) {
loginForm.addEventListener('submit', function(e) {
e.preventDefault();
console.log('Login form submitted');
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
const remember = document.getElementById('remember').checked;
console.log('Login data:', { email, password: '[hidden]', remember });
// Simple validation
if (!email || !password) {
showError('Bitte füllen Sie alle Felder aus.');
return;
}
// Email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
showError('Bitte geben Sie eine gültige E-Mail-Adresse ein.');
return;
}
// Show loading state
setLoadingState(true);
hideMessages();
// Simulate login process (for demo purposes)
setTimeout(() => {
// Store session (for demo purposes)
const sessionData = {
user: { email: email },
loginTime: new Date().toISOString(),
remember: remember
};
if (remember) {
localStorage.setItem('userSession', JSON.stringify(sessionData));
} else {
sessionStorage.setItem('userSession', JSON.stringify(sessionData));
}
// Show success message
showSuccess('Anmeldung erfolgreich! Sie werden weitergeleitet...');
// Redirect after delay
setTimeout(() => {
window.location.href = '../index.html';
}, 2000);
setLoadingState(false);
}, 1500);
});
}
// Loading state management
function setLoadingState(loading) {
if (loginBtn) {
const btnText = loginBtn.querySelector('.btn-text');
const btnLoading = loginBtn.querySelector('.btn-loading');
if (loading) {
loginBtn.disabled = true;
if (btnText) btnText.style.display = 'none';
if (btnLoading) btnLoading.style.display = 'inline-block';
} else {
loginBtn.disabled = false;
if (btnText) btnText.style.display = 'inline-block';
if (btnLoading) btnLoading.style.display = 'none';
}
}
}
// Message display functions
function showSuccess(message) {
if (successMessage) {
const messageElement = successMessage.querySelector('p');
if (messageElement) {
messageElement.textContent = message;
}
successMessage.classList.add('show');
}
}
function showError(message) {
if (errorMessage && errorText) {
errorText.textContent = message;
errorMessage.classList.add('show');
// Hide after 5 seconds
setTimeout(() => {
errorMessage.classList.remove('show');
}, 5000);
}
}
function hideMessages() {
if (successMessage) successMessage.classList.remove('show');
if (errorMessage) errorMessage.classList.remove('show');
}
});
// Check existing session
function checkExistingSession() {
const sessionData = localStorage.getItem('userSession') || sessionStorage.getItem('userSession');
if (sessionData) {
try {
const session = JSON.parse(sessionData);
const loginTime = new Date(session.loginTime);
const now = new Date();
const sessionAge = (now - loginTime) / (1000 * 60 * 60); // hours
// Auto-logout after 24 hours
if (sessionAge < 24) {
// User is still logged in, redirect to dashboard
console.log('User already logged in, redirecting...');
window.location.href = '../index.html';
} else {
// Session expired, remove it
console.log('Session expired, removing...');
localStorage.removeItem('userSession');
sessionStorage.removeItem('userSession');
}
} catch (error) {
console.error('Session parsing error:', error);
localStorage.removeItem('userSession');
sessionStorage.removeItem('userSession');
}
}
}
// Logout function (can be called from other pages)
function logout() {
localStorage.removeItem('userSession');
sessionStorage.removeItem('userSession');
window.location.href = 'sites/login.html';
}
// Get current user (can be called from other pages)
function getCurrentUser() {
const sessionData = localStorage.getItem('userSession') || sessionStorage.getItem('userSession');
if (sessionData) {
try {
const session = JSON.parse(sessionData);
const loginTime = new Date(session.loginTime);
const now = new Date();
const sessionAge = (now - loginTime) / (1000 * 60 * 60); // hours
if (sessionAge < 24) {
return session.user;
} else {
// Session expired
localStorage.removeItem('userSession');
sessionStorage.removeItem('userSession');
return null;
}
} catch (error) {
console.error('Session parsing error:', error);
localStorage.removeItem('userSession');
sessionStorage.removeItem('userSession');
return null;
}
}
return null;
}
// Check if user is logged in (can be called from other pages)
function isLoggedIn() {
return getCurrentUser() !== null;
}

268
scripts/register.js Normal file
View File

@@ -0,0 +1,268 @@
// register.js
document.addEventListener("DOMContentLoaded", function() {
console.log('Register page loaded');
const registerForm = document.getElementById('registerForm');
const registerBtn = document.getElementById('registerSubmit');
const successMessage = document.getElementById('successMessage');
const errorMessage = document.getElementById('errorMessage');
const errorText = document.getElementById('errorText');
const passwordInput = document.getElementById('password');
const confirmPasswordInput = document.getElementById('confirmPassword');
const passwordStrength = document.getElementById('passwordStrength');
console.log('Elements found:', {
registerForm: !!registerForm,
registerBtn: !!registerBtn,
passwordInput: !!passwordInput,
confirmPasswordInput: !!confirmPasswordInput,
passwordStrength: !!passwordStrength
});
// Check if user is already logged in
checkExistingSession();
// Password strength checker
if (passwordInput && passwordStrength) {
passwordInput.addEventListener('input', function() {
const password = this.value;
const strength = checkPasswordStrength(password);
updatePasswordStrength(strength);
});
}
// Password confirmation checker
if (confirmPasswordInput && passwordInput) {
confirmPasswordInput.addEventListener('input', function() {
const password = passwordInput.value;
const confirmPassword = this.value;
if (confirmPassword && password !== confirmPassword) {
this.setCustomValidity('Passwörter stimmen nicht überein');
} else {
this.setCustomValidity('');
}
});
}
// Registration form submission
if (registerForm) {
registerForm.addEventListener('submit', function(e) {
e.preventDefault();
console.log('Register form submitted');
// Get form data
const formData = {
firstName: document.getElementById('firstName').value,
lastName: document.getElementById('lastName').value,
email: document.getElementById('email').value,
phone: document.getElementById('phone').value,
company: document.getElementById('company').value,
password: passwordInput.value,
confirmPassword: confirmPasswordInput.value,
terms: document.getElementById('terms').checked,
newsletter: document.getElementById('newsletter').checked,
registrationTime: new Date().toISOString()
};
console.log('Registration data:', { ...formData, password: '[hidden]', confirmPassword: '[hidden]' });
// Validation
const validation = validateRegistrationForm(formData);
if (!validation.valid) {
showError(validation.message);
return;
}
// Show loading state
setLoadingState(true);
hideMessages();
// Simulate registration (for demo purposes)
setTimeout(() => {
// Store user data (for demo purposes)
const userData = {
firstName: formData.firstName,
lastName: formData.lastName,
email: formData.email,
phone: formData.phone,
company: formData.company,
registrationTime: formData.registrationTime,
newsletter: formData.newsletter
};
// Store in localStorage (for demo purposes)
localStorage.setItem('userData', JSON.stringify(userData));
localStorage.setItem('userRegistered', 'true');
// Show success message
showSuccess('Registrierung erfolgreich! Sie werden zum Login weitergeleitet...');
// Redirect to login page after delay
setTimeout(() => {
window.location.href = 'login.html';
}, 3000);
setLoadingState(false);
}, 2000);
});
}
// Password strength checker function
function checkPasswordStrength(password) {
let strength = 0;
// Length check
if (password.length >= 8) strength++;
if (password.length >= 12) strength++;
// Character variety checks
if (/[a-z]/.test(password)) strength++; // lowercase
if (/[A-Z]/.test(password)) strength++; // uppercase
if (/[0-9]/.test(password)) strength++; // numbers
if (/[^a-zA-Z0-9]/.test(password)) strength++; // special characters
return strength;
}
// Update password strength indicator
function updatePasswordStrength(strength) {
const strengthBar = passwordStrength.querySelector('.strength-bar');
const strengthText = passwordStrength.querySelector('.strength-text');
// Remove all strength classes
passwordStrength.classList.remove('strength-weak', 'strength-medium', 'strength-strong');
if (strength <= 2) {
passwordStrength.classList.add('strength-weak');
strengthText.textContent = 'Schwach';
} else if (strength <= 4) {
passwordStrength.classList.add('strength-medium');
strengthText.textContent = 'Mittel';
} else {
passwordStrength.classList.add('strength-strong');
strengthText.textContent = 'Stark';
}
}
// Form validation
function validateRegistrationForm(data) {
// Required fields check
if (!data.firstName || !data.lastName || !data.email || !data.password) {
return { valid: false, message: 'Bitte füllen Sie alle Pflichtfelder aus.' };
}
// Name validation
if (data.firstName.length < 2 || data.lastName.length < 2) {
return { valid: false, message: 'Vorname und Nachname müssen mindestens 2 Zeichen lang sein.' };
}
// Email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(data.email)) {
return { valid: false, message: 'Bitte geben Sie eine gültige E-Mail-Adresse ein.' };
}
// Password validation
if (data.password.length < 8) {
return { valid: false, message: 'Das Passwort muss mindestens 8 Zeichen lang sein.' };
}
// Password confirmation
if (data.password !== data.confirmPassword) {
return { valid: false, message: 'Die Passwörter stimmen nicht überein.' };
}
// Terms acceptance
if (!data.terms) {
return { valid: false, message: 'Sie müssen die Nutzungsbedingungen akzeptieren.' };
}
// Phone validation (if provided)
if (data.phone) {
const phoneRegex = /^[\d\s\-\+\(\)]+$/;
if (!phoneRegex.test(data.phone)) {
return { valid: false, message: 'Bitte geben Sie eine gültige Telefonnummer ein.' };
}
}
return { valid: true, message: 'Validierung erfolgreich' };
}
// Loading state management
function setLoadingState(loading) {
if (registerBtn) {
const btnText = registerBtn.querySelector('.btn-text');
const btnLoading = registerBtn.querySelector('.btn-loading');
if (loading) {
registerBtn.disabled = true;
if (btnText) btnText.style.display = 'none';
if (btnLoading) btnLoading.style.display = 'inline-block';
} else {
registerBtn.disabled = false;
if (btnText) btnText.style.display = 'inline-block';
if (btnLoading) btnLoading.style.display = 'none';
}
}
}
// Message display functions
function showSuccess(message) {
if (successMessage) {
const messageElement = successMessage.querySelector('p');
if (messageElement) {
messageElement.textContent = message;
}
successMessage.classList.add('show');
}
}
function showError(message) {
if (errorMessage && errorText) {
errorText.textContent = message;
errorMessage.classList.add('show');
// Hide after 5 seconds
setTimeout(() => {
errorMessage.classList.remove('show');
}, 5000);
}
}
function hideMessages() {
if (successMessage) successMessage.classList.remove('show');
if (errorMessage) errorMessage.classList.remove('show');
}
});
// Check existing session (same as login.js)
function checkExistingSession() {
const sessionData = localStorage.getItem('userSession') || sessionStorage.getItem('userSession');
if (sessionData) {
try {
const session = JSON.parse(sessionData);
const loginTime = new Date(session.loginTime);
const now = new Date();
const sessionAge = (now - loginTime) / (1000 * 60 * 60); // hours
// Auto-logout after 24 hours
if (sessionAge < 24) {
// User is still logged in, redirect to dashboard
console.log('User already logged in, redirecting...');
window.location.href = '../index.html';
} else {
// Session expired, remove it
console.log('Session expired, removing...');
localStorage.removeItem('userSession');
sessionStorage.removeItem('userSession');
}
} catch (error) {
console.error('Session parsing error:', error);
localStorage.removeItem('userSession');
sessionStorage.removeItem('userSession');
}
}
}

134
scripts/script.js Normal file
View File

@@ -0,0 +1,134 @@
// script.js
// ==========================================
// 1. MENU TOGGLE LOGIC
// ==========================================
const menuToggle = document.getElementById('menuToggle');
const slideMenu = document.getElementById('slideMenu');
const overlay = document.getElementById('overlay');
const loginBtn = document.getElementById('loginBtn');
if (menuToggle && slideMenu && overlay) {
function toggleMenu() {
menuToggle.classList.toggle('active');
slideMenu.classList.toggle('active');
overlay.classList.toggle('active');
}
menuToggle.addEventListener('click', toggleMenu);
overlay.addEventListener('click', toggleMenu);
}
// Login button functionality
if (loginBtn) {
console.log('Login button found:', loginBtn);
loginBtn.addEventListener('click', function(e) {
e.preventDefault();
console.log('Login button clicked!');
console.log('Current path:', window.location.pathname);
// Check if we're already on the login page
if (window.location.pathname.includes('login.html')) {
// If on login page, go to register page
console.log('Navigating to register page');
window.location.href = 'register.html';
} else if (window.location.pathname.includes('register.html')) {
// If on register page, go to login page
console.log('Navigating to login page from register');
window.location.href = 'login.html';
} else {
// If on any other page, go to login page
console.log('Navigating to login page from other');
window.location.href = 'sites/login.html';
}
});
} else {
console.error('Login button not found in DOM');
}
// ==========================================
// 2. FORM SUBMISSION LOGIC
// ==========================================
const contactForm = document.getElementById('contactForm');
const successMessage = document.getElementById('successMessage');
if (contactForm) {
contactForm.addEventListener('submit', async function(e) {
e.preventDefault();
const nameInput = document.getElementById('name');
const orgInput = document.getElementById('organisation');
const contactInput = document.getElementById('contact');
const serviceSelect = document.getElementById('service');
const budgetInput = document.getElementById('budget');
const descInput = document.getElementById('description');
const selectedServiceText = serviceSelect ? serviceSelect.options[serviceSelect.selectedIndex].text : 'Dienstleistung';
const formData = {
name: nameInput ? nameInput.value : '',
organisation: orgInput ? orgInput.value : '',
contact: contactInput ? contactInput.value : '',
service: serviceSelect ? serviceSelect.value : '',
budget: budgetInput ? budgetInput.value : '',
description: descInput ? descInput.value : '',
timestamp: new Date().toISOString()
};
// --- LOCALSTORAGE ---
try {
const localLead = {
id: Date.now(),
datum: new Date().toLocaleDateString('de-DE'),
dienstleistung: selectedServiceText,
status: 'open',
statusText: 'Offen',
description: formData.description
};
let existingLeads = JSON.parse(localStorage.getItem('myLeads')) || [];
existingLeads.unshift(localLead);
localStorage.setItem('myLeads', JSON.stringify(existingLeads));
} catch (err) {
console.error('Ошибка сохранения в LocalStorage:', err);
}
// false if test, true if production
const USE_PRODUCTION = true;
const WEBHOOK_TEST = 'https://n8n.profice.de/webhook-test/d94ef798-3f43-46dd-8207-1e335e64518f';
const WEBHOOK_PROD = 'https://n8n.profice.de/webhook/d94ef798-3f43-46dd-8207-1e335e64518f';
const N8N_WEBHOOK_URL = USE_PRODUCTION ? WEBHOOK_PROD : WEBHOOK_TEST;
let iframe = document.getElementById('hidden-iframe');
if (!iframe) {
iframe = document.createElement('iframe');
iframe.id = 'hidden-iframe';
iframe.name = 'hidden-iframe';
iframe.style.display = 'none';
document.body.appendChild(iframe);
}
const tempForm = document.createElement('form');
tempForm.method = 'POST';
tempForm.action = N8N_WEBHOOK_URL;
tempForm.target = 'hidden-iframe';
for (const [key, value] of Object.entries(formData)) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = value;
tempForm.appendChild(input);
}
document.body.appendChild(tempForm);
tempForm.submit();
document.body.removeChild(tempForm);
contactForm.style.display = 'none';
if (successMessage) {
successMessage.classList.add('show');
}
console.log('Form submitted successfully');
});
}

83
scripts/scroll-header.js Normal file
View File

@@ -0,0 +1,83 @@
// scroll-header.js
document.addEventListener("DOMContentLoaded", function() {
const topBanner = document.querySelector('.top-banner');
const slideMenu = document.querySelector('.slide-menu');
if (!topBanner) return;
// Scroll threshold to trigger the shrink effect
const scrollThreshold = 50;
let isScrolled = false;
let isTransitioning = false;
let lastScrollY = 0;
let scrollDirection = 'down';
function updateHeaderState(scrolled) {
if (isTransitioning) return;
if (scrolled && !isScrolled) {
isTransitioning = true;
topBanner.classList.add('scrolled');
isScrolled = true;
if (slideMenu) {
slideMenu.style.top = '80px';
}
// Reset transition flag after animation completes
setTimeout(() => {
isTransitioning = false;
}, 250);
} else if (!scrolled && isScrolled) {
isTransitioning = true;
topBanner.classList.remove('scrolled');
isScrolled = false;
if (slideMenu) {
slideMenu.style.top = '110px';
}
// Reset transition flag after animation completes
setTimeout(() => {
isTransitioning = false;
}, 250);
}
}
function handleScroll() {
const currentScrollY = window.pageYOffset || document.documentElement.scrollTop;
// Detect scroll direction
if (currentScrollY > lastScrollY) {
scrollDirection = 'down';
} else if (currentScrollY < lastScrollY) {
scrollDirection = 'up';
}
// Only update when crossing threshold in the right direction
if (scrollDirection === 'down' && currentScrollY > scrollThreshold && !isScrolled) {
updateHeaderState(true);
} else if (scrollDirection === 'up' && currentScrollY <= scrollThreshold && isScrolled) {
updateHeaderState(false);
}
lastScrollY = currentScrollY;
}
// Use requestAnimationFrame for smooth scroll handling
let ticking = false;
function requestTick() {
if (!ticking) {
requestAnimationFrame(handleScroll);
ticking = true;
setTimeout(() => { ticking = false; }, 16); // ~60fps
}
}
// Optimized scroll event listener
window.addEventListener('scroll', requestTick, { passive: true, capture: false });
// Initial check
handleScroll();
});