{% extends "base.html" %} {% block title %}Rejestracja - Norda Biznes Hub{% endblock %} {% block container_class %}container-narrow{% endblock %} {% block extra_css %} {% endblock %} {% block content %}

Utwórz konto

Dołącz do społeczności Norda Biznes

Wpisz nazwę firmy członkowskiej - lista filtruje się automatycznie
  • Minimum 8 znaków
  • Wielka litera
  • Mała litera
  • Cyfra
{% endblock %} {% block extra_js %} // Version: 2025-11-24 14:00 - Live checkbox validation console.log('🔧 Password validation loaded - Version 2025-11-24 14:00'); const passwordInput = document.getElementById('password'); const strengthBar = document.getElementById('strengthBar'); const submitBtn = document.getElementById('submitBtn'); // Password strength checker passwordInput.addEventListener('input', function() { const password = this.value; let strength = 0; let validCount = 0; // Check requirements const hasLength = password.length >= 8; const hasUpper = /[A-Z]/.test(password); const hasLower = /[a-z]/.test(password); const hasDigit = /\d/.test(password); // Update UI for each requirement updateRequirement('req-length', hasLength); updateRequirement('req-upper', hasUpper); updateRequirement('req-lower', hasLower); updateRequirement('req-digit', hasDigit); // Calculate strength if (hasLength) { strength++; validCount++; } if (hasUpper) { strength++; validCount++; } if (hasLower) { strength++; validCount++; } if (hasDigit) { strength++; validCount++; } // Update strength bar strengthBar.className = 'password-strength-bar'; if (strength === 1 || strength === 2) { strengthBar.classList.add('weak'); } else if (strength === 3) { strengthBar.classList.add('medium'); } else if (strength === 4) { strengthBar.classList.add('strong'); } // Enable submit button only if all requirements met submitBtn.disabled = validCount < 4; }); function updateRequirement(id, valid) { const el = document.getElementById(id); console.log(`Updating ${id}: ${valid ? 'VALID' : 'invalid'}`); // DEBUG if (valid) { el.classList.add('valid'); } else { el.classList.remove('valid'); } } // Form validation document.querySelector('form').addEventListener('submit', function(e) { const name = document.getElementById('name'); const email = document.getElementById('email'); const password = document.getElementById('password'); let valid = true; // Name validation if (!name.value || name.value.length < 2) { name.classList.add('error'); valid = false; } else { name.classList.remove('error'); } // Email validation if (!email.value || !email.value.includes('@')) { email.classList.add('error'); valid = false; } else { email.classList.remove('error'); } // Password validation const hasLength = password.value.length >= 8; const hasUpper = /[A-Z]/.test(password.value); const hasLower = /[a-z]/.test(password.value); const hasDigit = /\d/.test(password.value); if (!hasLength || !hasUpper || !hasLower || !hasDigit) { password.classList.add('error'); valid = false; } else { password.classList.remove('error'); } if (!valid) { e.preventDefault(); } }); // Email validation and availability check const emailInput = document.getElementById('email'); const emailStatus = document.getElementById('emailStatus'); let emailCheckTimeout; let emailAvailable = false; emailInput.addEventListener('input', function() { const email = this.value.trim(); // Clear previous timeout clearTimeout(emailCheckTimeout); // Basic format validation if (!email) { emailStatus.style.display = 'none'; emailAvailable = false; return; } // Proper email format validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/; if (!emailRegex.test(email)) { showEmailStatus('taken', '❌ Nieprawidłowy format email'); emailAvailable = false; return; } // Check availability after 500ms of no typing emailCheckTimeout = setTimeout(() => { checkEmailAvailability(email); }, 500); }); function checkEmailAvailability(email) { showEmailStatus('checking', '⏳ Sprawdzam dostępność...'); const csrfToken = document.querySelector('input[name="csrf_token"]').value; fetch('/api/check-email', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ email: email }) }) .then(response => response.json()) .then(data => { if (data.available) { showEmailStatus('available', '✅ Email dostępny'); emailAvailable = true; } else { showEmailStatus('taken', '❌ Email jest już zarejestrowany'); emailAvailable = false; } }) .catch(error => { console.error('Email check error:', error); emailStatus.style.display = 'none'; emailAvailable = false; }); } function showEmailStatus(statusClass, message) { emailStatus.className = 'email-status ' + statusClass; emailStatus.textContent = message; emailStatus.style.display = 'block'; } // ============================================ // Company Autocomplete with Real-time Filtering // ============================================ const companySearch = document.getElementById('company_search'); const companyDropdown = document.getElementById('companyDropdown'); const companySelected = document.getElementById('companySelected'); const selectedCompanyName = document.getElementById('selectedCompanyName'); const selectedCompanyNip = document.getElementById('selectedCompanyNip'); const clearCompanyBtn = document.getElementById('clearCompanyBtn'); const nipHiddenInput = document.getElementById('company_nip'); let companies = []; // Will be loaded from API let selectedCompany = null; // Load companies on page load loadCompanies(); async function loadCompanies() { try { const response = await fetch('/api/companies'); const data = await response.json(); companies = data.companies || data; console.log(`Loaded ${companies.length} companies for autocomplete`); } catch (error) { console.error('Failed to load companies:', error); companies = []; } } // Filter and show dropdown on input companySearch.addEventListener('input', function() { const query = this.value.trim().toLowerCase(); if (query.length === 0) { hideDropdown(); return; } // Filter companies - match anywhere in name const filtered = companies.filter(company => company.name.toLowerCase().includes(query) ).slice(0, 10); // Limit to 10 results showDropdown(filtered, query); }); // Show dropdown on focus if there's text companySearch.addEventListener('focus', function() { if (this.value.trim().length > 0 && !selectedCompany) { const query = this.value.trim().toLowerCase(); const filtered = companies.filter(c => c.name.toLowerCase().includes(query)).slice(0, 10); showDropdown(filtered, query); } }); function showDropdown(filteredCompanies, query) { if (filteredCompanies.length === 0) { companyDropdown.innerHTML = '
Nie znaleziono firmy o nazwie "' + escapeHtml(query) + '"
'; companyDropdown.classList.add('show'); return; } companyDropdown.innerHTML = filteredCompanies.map(company => `
${highlightMatch(company.name, query)} ${company.city || ''}
`).join(''); // Add click handlers companyDropdown.querySelectorAll('.company-option').forEach(option => { option.addEventListener('click', function() { selectCompany(this.dataset.name, this.dataset.nip); }); }); companyDropdown.classList.add('show'); } function hideDropdown() { companyDropdown.classList.remove('show'); } function selectCompany(name, nip) { selectedCompany = { name, nip }; // Update hidden NIP field nipHiddenInput.value = nip; // Show selected company card selectedCompanyName.textContent = name; selectedCompanyNip.textContent = nip; companySelected.style.display = 'flex'; // Hide search input and dropdown companySearch.style.display = 'none'; hideDropdown(); console.log(`Selected company: ${name} (NIP: ${nip})`); } // Clear selection clearCompanyBtn.addEventListener('click', function() { selectedCompany = null; nipHiddenInput.value = ''; companySelected.style.display = 'none'; companySearch.style.display = 'block'; companySearch.value = ''; companySearch.focus(); }); // Hide dropdown when clicking outside document.addEventListener('click', function(e) { if (!companySearch.contains(e.target) && !companyDropdown.contains(e.target)) { hideDropdown(); } }); // Keyboard navigation companySearch.addEventListener('keydown', function(e) { const options = companyDropdown.querySelectorAll('.company-option'); const active = companyDropdown.querySelector('.company-option:hover, .company-option.active'); if (e.key === 'ArrowDown') { e.preventDefault(); if (options.length > 0) { const next = active ? active.nextElementSibling || options[0] : options[0]; options.forEach(o => o.classList.remove('active')); next.classList.add('active'); next.scrollIntoView({ block: 'nearest' }); } } else if (e.key === 'ArrowUp') { e.preventDefault(); if (options.length > 0) { const prev = active ? active.previousElementSibling || options[options.length - 1] : options[options.length - 1]; options.forEach(o => o.classList.remove('active')); prev.classList.add('active'); prev.scrollIntoView({ block: 'nearest' }); } } else if (e.key === 'Enter') { e.preventDefault(); const activeOption = companyDropdown.querySelector('.company-option.active'); if (activeOption) { selectCompany(activeOption.dataset.name, activeOption.dataset.nip); } } else if (e.key === 'Escape') { hideDropdown(); } }); // Helper functions function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function highlightMatch(text, query) { if (!query) return escapeHtml(text); const regex = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi'); return escapeHtml(text).replace(regex, '$1'); } {% endblock %}