{% extends "base.html" %} {% block title %}Edytuj profil — {{ company.name }}{% endblock %} {% block head_extra %} {% endblock %} {% block extra_css %} {% endblock %} {% block content %}

{{ company.name }}

Edytuj dane profilu firmy widoczne w katalogu Izby NORDA

Co możesz zmienić: opis firmy, historię, usługi, kompetencje, dane kontaktowe (telefon, e-mail), social media i logo.
Dane rejestrowe (NIP, REGON, KRS, adres siedziby) są pobierane automatycznie z rejestrów urzędowych i nie wymagają ręcznej edycji.
{% if current_user.can_manage_company(company.id) %} {% endif %}
{% if not permissions.description %}
Nie masz uprawnień do edycji tej sekcji. Skontaktuj się z managerem firmy.
{% endif %}

Główna kategoria widoczna przy nazwie firmy w katalogu

Logo

PNG, JPG, SVG lub WebP. Pliki graficzne zostaną automatycznie przekonwertowane do formatu WebP. Zalecany rozmiar: min. 200x200px.

{% if company.krs_registration_date or company.business_start_date %}

Rok pochodzi z rejestru {% if company.krs_registration_date %}KRS{% else %}CEIDG{% endif %} — aby zmienić, skontaktuj się z administratorem

{% else %}

Rok rozpoczęcia działalności firmy

{% endif %}
{{ (company.description_short or '') | length }}/500 znaków

Widoczny na liście firm i w wynikach wyszukiwania · Wykorzystywany przez NordaGPT

Użyj paska narzędzi do formatowania tekstu · Wykorzystywany przez NordaGPT

Wykorzystywany przez NordaGPT — historia firmy, założyciele, doświadczenie

Wykorzystywany przez NordaGPT — wartości i misja firmy

{% if not permissions.services %}
Nie masz uprawnień do edycji tej sekcji. Skontaktuj się z managerem firmy.
{% endif %}

Użyj paska narzędzi do formatowania tekstu · Wykorzystywany przez NordaGPT

Wykorzystywany przez NordaGPT — technologie i specjalizacje

Gdzie firma świadczy usługi

{% if not permissions.contacts %}
Nie masz uprawnień do edycji tej sekcji. Skontaktuj się z managerem firmy.
{% endif %}
Dane kontaktowe
{% for w in company_websites %} {% endfor %} {% if not company_websites %} {% if company.website %} {% endif %} {% endif %}
Adres siedziby

Dodatkowe numery i adresy email

Dodaj kolejne numery telefonów, adresy email lub fax z opisem przeznaczenia.

{% for contact in contacts %}
{% endfor %}
{% if not permissions.social %}
Nie masz uprawnień do edycji tej sekcji. Skontaktuj się z managerem firmy.
{% endif %}

Dodaj linki do profili firmy w mediach społecznościowych.

{% for sm in social_media %} {% if sm.source is none or sm.source == 'manual_edit' or sm.source == 'manual' %} {% endif %} {% endfor %}
{% set ns = namespace(has_auto=false) %} {% for sm in social_media %} {% if sm.source is not none and sm.source != 'manual_edit' and sm.source != 'manual' %} {% set ns.has_auto = true %} {% endif %} {% endfor %} {% if ns.has_auto %}
Profile wykryte automatycznie Te profile zostały wykryte przez system i nie podlegają ręcznej edycji:
    {% for sm in social_media %} {% if sm.source is not none and sm.source != 'manual_edit' and sm.source != 'manual' %}
  • {{ sm.platform | capitalize }}: {{ sm.url | truncate(60) }}
  • {% endif %} {% endfor %}
{% endif %}
Zarządzaj widocznością sekcji profilu Ukryte sekcje nie będą widoczne dla odwiedzających Twój profil. Ty i kadra zarządzająca nadal widzicie ukryte sekcje z oznaczeniem.
{% for key, label, description, subs in section_definitions %}
{{ label }} {{ description }}
{% if subs %} {% for sub_key, sub_label in subs %}
{{ sub_label }}
{% endfor %} {% endif %} {% endfor %}
Anuluj
{% if current_user.can_manage_company(company.id) %}

Zespół firmy

Ładowanie zespołu...
{% endif %}
Podgląd profilu
{{ company.name }}
{{ company.description_short or 'Brak krótkiego opisu' }}
{% if company.description_full %}{{ company.description_full | safe }}{% else %}Uzupełnij opis firmy...{% endif %}
{% if company.founding_history %}{{ company.founding_history | safe }}{% else %}Uzupełnij historię...{% endif %}
{% if company.core_values %}{{ company.core_values | safe }}{% else %}Uzupełnij wartości...{% endif %}
{% if company.services_offered %}{{ company.services_offered | safe }}{% else %}Uzupełnij usługi...{% endif %}
{% if company.email %}
{{ company.email }}
{% endif %} {% if company.phone %}
{{ company.phone }}
{% endif %} {% if company.website %}
{{ company.website }}
{% endif %} {% if not company.email and not company.phone and not company.website %}Brak danych kontaktowych{% endif %}
{% for sm in social_media %} {{ sm.platform | capitalize }} {% endfor %} {% if not social_media %}Brak profili social media{% endif %}
Podgląd profilu
{% endblock %} {% block extra_js %} // ============================================ // Quill.js WYSIWYG Initialization // ============================================ var quillInstances = {}; var QUILL_TOOLBAR = [ ['bold', 'italic'], [{ 'list': 'ordered'}, { 'list': 'bullet' }], ['link'], ['clean'] ]; function initQuillEditor(fieldName, placeholder) { var container = document.getElementById('quill-' + fieldName); var textarea = document.getElementById(fieldName); if (!container || !textarea) return null; var quill = new Quill(container, { theme: 'snow', modules: { toolbar: QUILL_TOOLBAR }, placeholder: placeholder || 'Wpisz tekst...' }); // Load existing content from hidden textarea var existing = textarea.value.trim(); if (existing) { quill.root.innerHTML = existing; } // Sync to hidden textarea on every change + update preview quill.on('text-change', function() { var html = quill.root.innerHTML; textarea.value = (html === '


') ? '' : html; updatePreview(fieldName, textarea.value); }); quillInstances[fieldName] = quill; return quill; } // Initialize all Quill editors if (typeof Quill !== 'undefined') { initQuillEditor('description_full', 'Szczegółowy opis tego czym zajmuje się firma...'); initQuillEditor('founding_history', 'Kiedy firma powstała, jakie ma doświadczenie...'); initQuillEditor('core_values', 'Kluczowe wartości firmy, misja...'); initQuillEditor('services_offered', 'Wymień główne usługi i produkty...'); } // ============================================ // Live Preview Updates // ============================================ var previewDebounceTimers = {}; function updatePreview(fieldName, value) { clearTimeout(previewDebounceTimers[fieldName]); previewDebounceTimers[fieldName] = setTimeout(function() { doUpdatePreview(fieldName, value); }, 300); } function doUpdatePreview(fieldName, value) { var mapping = { 'description_short': 'previewShortDesc', 'description_full': 'previewDescFull', 'founding_history': 'previewHistory', 'core_values': 'previewValues', 'services_offered': 'previewServices' }; var emptyTexts = { 'description_short': 'Brak krótkiego opisu', 'description_full': 'Uzupełnij opis firmy...', 'founding_history': 'Uzupełnij historię...', 'core_values': 'Uzupełnij wartości...', 'services_offered': 'Uzupełnij usługi...' }; var targetId = mapping[fieldName]; if (!targetId) return; var el = document.getElementById(targetId); if (!el) return; if (value && value.trim() && value !== '


') { el.innerHTML = value; el.classList.remove('preview-empty'); } else { el.innerHTML = '' + (emptyTexts[fieldName] || '') + ''; } } // Hook plain text fields to preview var shortDescField = document.getElementById('description_short'); if (shortDescField) { shortDescField.addEventListener('input', function() { updatePreview('description_short', this.value); }); } // Hook contact fields to preview function updateContactPreview() { var email = (document.getElementById('email') || {}).value || ''; var phone = (document.getElementById('phone') || {}).value || ''; var el = document.getElementById('previewContact'); if (!el) return; var html = ''; if (email) html += '
' + email + '
'; if (phone) html += '
' + phone + '
'; el.innerHTML = html || 'Brak danych kontaktowych'; } ['email', 'phone'].forEach(function(id) { var field = document.getElementById(id); if (field) field.addEventListener('input', updateContactPreview); }); // Highlight preview section matching active tab function highlightPreviewTab(tabName) { var sections = document.querySelectorAll('.ce-preview .preview-section'); sections.forEach(function(s) { s.style.opacity = '0.4'; s.style.transition = 'opacity 0.3s'; }); var tabToSections = { 'description': ['previewDescriptionSection', 'previewHistorySection', 'previewValuesSection'], 'services': ['previewServicesSection'], 'contacts': ['previewContactSection'], 'social': ['previewSocialSection'] }; var active = tabToSections[tabName] || []; active.forEach(function(id) { var el = document.getElementById(id); if (el) el.style.opacity = '1'; }); if (tabName === 'visibility') { sections.forEach(function(s) { s.style.opacity = '1'; }); } } // Initial highlight for default tab highlightPreviewTab('description'); // Company Edit — tabs, dynamic fields, validation (function() { // Tab switching var tabs = document.querySelectorAll('.ce-tab'); var contents = document.querySelectorAll('.ce-tab-content'); var activeTabInput = document.getElementById('activeTabInput'); tabs.forEach(function(tab) { tab.addEventListener('click', function() { var target = this.getAttribute('data-tab'); tabs.forEach(function(t) { t.classList.remove('active'); }); this.classList.add('active'); contents.forEach(function(c) { c.classList.remove('active'); }); document.getElementById('tab-' + target).classList.add('active'); activeTabInput.value = target; highlightPreviewTab(target); // Hide form actions and preview on team tab (team saves via AJAX) var actions = document.querySelector('.ce-actions'); var preview = document.getElementById('livePreview'); if (actions) actions.style.display = target === 'team' ? 'none' : ''; if (preview) preview.style.display = target === 'team' ? 'none' : ''; }); }); // Character counter for short description var shortDesc = document.getElementById('description_short'); var shortCount = document.getElementById('shortDescCount'); var shortCounter = document.getElementById('shortDescCounter'); if (shortDesc && shortCount) { shortDesc.addEventListener('input', function() { var len = this.value.length; shortCount.textContent = len; shortCounter.className = 'char-counter' + (len > 450 ? (len >= 500 ? ' error' : ' warning') : ''); }); } // Helper: create contact row HTML function makeContactRow() { var row = document.createElement('div'); row.className = 'contact-row'; row.innerHTML = '' + '' + '' + ''; return row; } // Helper: create social row HTML function makeSocialRow() { var row = document.createElement('div'); row.className = 'social-row'; row.innerHTML = '' + '' + ''; return row; } // Add contact var addContactBtn = document.getElementById('addContactBtn'); if (addContactBtn) { addContactBtn.addEventListener('click', function() { document.getElementById('contactsList').appendChild(makeContactRow()); }); } // Add social var addSocialBtn = document.getElementById('addSocialBtn'); if (addSocialBtn) { addSocialBtn.addEventListener('click', function() { document.getElementById('socialList').appendChild(makeSocialRow()); }); } // Client-side validation document.getElementById('companyEditForm').addEventListener('submit', function(e) { var emailField = document.getElementById('email'); if (emailField && emailField.value.trim()) { var emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; if (!emailPattern.test(emailField.value.trim())) { e.preventDefault(); emailField.focus(); emailField.style.borderColor = 'var(--error, #ef4444)'; emailField.style.boxShadow = '0 0 0 3px rgba(239,68,68,0.1)'; return; } } // Auto-prefix https:// for all website URL inputs document.querySelectorAll('#websiteList input[name="website_urls[]"]').forEach(function(f) { if (f.value.trim() && !f.value.match(/^https?:\/\//)) { f.value = 'https://' + f.value.trim(); } }); }); })(); // Website list management function removeWebsiteRow(btn) { btn.closest('.website-row').remove(); reindexWebsiteRadios(); toggleWebsiteBtn(); } function reindexWebsiteRadios() { var rows = document.querySelectorAll('#websiteList .website-row'); var hadChecked = false; rows.forEach(function(row, i) { var radio = row.querySelector('input[type="radio"]'); radio.value = i; if (radio.checked) hadChecked = true; }); if (!hadChecked && rows.length > 0) { rows[0].querySelector('input[type="radio"]').checked = true; } } function toggleWebsiteBtn() { var btn = document.getElementById('addWebsiteBtn'); if (btn) { btn.style.display = document.querySelectorAll('#websiteList .website-row').length >= 5 ? 'none' : ''; } } (function() { var addBtn = document.getElementById('addWebsiteBtn'); if (addBtn) { addBtn.addEventListener('click', function() { var list = document.getElementById('websiteList'); var idx = list.querySelectorAll('.website-row').length; if (idx >= 5) return; var row = document.createElement('div'); row.className = 'social-row website-row'; row.innerHTML = '' + '' + '' + '' + ''; list.appendChild(row); if (idx === 0) row.querySelector('input[type="radio"]').checked = true; toggleWebsiteBtn(); }); toggleWebsiteBtn(); } })(); // Section visibility toggle (AJAX) var hiddenSections = {{ company.hidden_sections | tojson }}; function toggleSection(key, btn) { btn.classList.add('saving'); var idx = hiddenSections.indexOf(key); if (idx >= 0) { hiddenSections.splice(idx, 1); } else { hiddenSections.push(key); } fetch('{{ url_for("public.company_edit_visibility", company_id=company.id) }}', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token() }}' }, body: JSON.stringify({ hidden_sections: hiddenSections }) }) .then(function(r) { return r.json(); }) .then(function(data) { btn.classList.remove('saving'); if (data.success) { hiddenSections = data.hidden_sections; var isHidden = hiddenSections.indexOf(key) >= 0; btn.className = 'visibility-toggle' + (isHidden ? ' hidden-state' : ''); btn.innerHTML = isHidden ? 'Ukryta' : 'Widoczna'; } else { // Revert on error if (idx >= 0) hiddenSections.push(key); else hiddenSections.splice(hiddenSections.indexOf(key), 1); } }) .catch(function() { btn.classList.remove('saving'); if (idx >= 0) hiddenSections.push(key); else hiddenSections.splice(hiddenSections.indexOf(key), 1); }); } // Mobile preview function openMobilePreview() { var mobileContent = document.getElementById('mobilePreviewContent'); var desktopPreview = document.getElementById('livePreview'); if (mobileContent && desktopPreview) { var clone = desktopPreview.cloneNode(true); var title = clone.querySelector('.ce-preview-title'); if (title) title.remove(); mobileContent.innerHTML = clone.innerHTML; } document.getElementById('previewSheetOverlay').classList.add('active'); document.body.style.overflow = 'hidden'; } function closeMobilePreview() { document.getElementById('previewSheetOverlay').classList.remove('active'); document.body.style.overflow = ''; } var previewOverlay = document.getElementById('previewSheetOverlay'); if (previewOverlay) { previewOverlay.addEventListener('click', function(e) { if (e.target === this) closeMobilePreview(); }); } // Toast notifications function showToast(message, type, duration) { type = type || 'info'; duration = duration || 4000; var toast = document.createElement('div'); toast.className = 'flash flash-' + type; toast.innerHTML = '' + message + ''; toast.style.cssText = 'position:fixed;top:20px;right:20px;z-index:9999;min-width:280px;max-width:480px;animation:fadeIn 0.3s;'; document.body.appendChild(toast); setTimeout(function() { if (toast.parentElement) toast.remove(); }, duration); } {% if current_user.can_manage_company(company.id) %} // ===== Team Management ===== var TEAM_COMPANY_ID = {{ company.id }}; var teamLoaded = false; var CSRF_TOKEN = '{{ csrf_token() }}'; var PERM_LABELS = { 'can_edit_description': { label: 'Opis i historia firmy', hint: 'Zakładka „Opis" w profilu firmy' }, 'can_edit_services': { label: 'Usługi i kompetencje', hint: 'Zakładka „Usługi" w profilu firmy' }, 'can_edit_contacts': { label: 'Telefony i adresy email', hint: 'Zakładka „Kontakt" w profilu firmy' }, 'can_edit_social': { label: 'Social media i strona www', hint: 'Zakładka „Social Media" w profilu firmy' }, 'can_manage_classifieds': { label: 'Ogłoszenia B2B firmy', hint: 'Publikowanie ogłoszeń na Tablicy B2B' }, 'can_post_forum': { label: 'Posty na forum', hint: 'Pisanie postów na forum w imieniu firmy' }, 'can_view_analytics': { label: 'Statystyki firmy', hint: 'Wyświetlenia profilu, wyszukiwania, trendy' } }; var ROLE_INFO = { 'VIEWER': { label: 'Obserwator', desc: 'Widzi dashboard firmy, ale nie może niczego edytować' }, 'EMPLOYEE': { label: 'Pracownik', desc: 'Może edytować wybrane sekcje profilu firmy (wg uprawnień poniżej)' }, 'MANAGER': { label: 'Kadra zarządzająca', desc: 'Pełna kontrola nad profilem firmy i zarządzanie zespołem' } }; // Hook into tab switcher — lazy load team on first click document.addEventListener('click', function(e) { var tab = e.target.closest('.ce-tab[data-tab="team"]'); if (tab && !teamLoaded) { teamLoaded = true; loadTeamMembers(); } }); function toggleAddForm() { var form = document.getElementById('teamAddForm'); form.style.display = form.style.display === 'none' ? 'block' : 'none'; if (form.style.display === 'block') document.getElementById('teamEmail').focus(); } function loadTeamMembers() { var list = document.getElementById('teamMembersList'); list.innerHTML = '
Ładowanie...
'; fetch('/firma/' + TEAM_COMPANY_ID + '/zespol') .then(function(r) { return r.json(); }) .then(function(data) { if (!data.success) { list.innerHTML = '
Błąd: ' + (data.error || '') + '
'; return; } if (!data.members.length) { list.innerHTML = '
Brak członków zespołu. Dodaj pierwszą osobę.
'; return; } var html = ''; data.members.forEach(function(m) { var initials = (m.name || '?').split(' ').map(function(w) { return w[0]; }).join('').substring(0, 2).toUpperCase(); var roleClass = m.role === 'MANAGER' ? 'manager' : (m.role === 'EMPLOYEE' ? 'employee' : 'viewer'); var ri = ROLE_INFO[m.role] || { label: m.role, desc: '' }; html += '
'; html += '
' + initials + '
'; html += '
'; html += '
' + escapeHtml(m.name || m.email); if (m.is_current_user) html += 'Ty'; html += '
'; html += '
' + escapeHtml(m.email) + '
'; // Login status if (!m.is_current_user) { if (m.last_login) { html += '
Ostatnie logowanie: ' + formatDate(m.last_login) + '
'; } else { html += '
Nie zalogował/a się jeszcze'; html += ' '; html += '
'; } } html += '
'; html += '
'; if (!m.is_current_user) { html += ''; html += ''; } else { html += '' + ri.label + ''; } html += '
'; // Role description html += '
' + ri.desc + '
'; // Permissions for EMPLOYEE if (m.role === 'EMPLOYEE' && !m.is_current_user && m.permissions) { html += '
'; html += '
Co może edytować w profilu firmy:
'; html += '
'; Object.keys(PERM_LABELS).forEach(function(key) { var p = PERM_LABELS[key]; var checked = m.permissions[key] ? ' checked' : ''; html += ''; }); html += '
'; } html += '
'; }); list.innerHTML = html; }) .catch(function() { list.innerHTML = '
Nie udało się załadować zespołu.
'; }); } function addTeamMember() { var email = document.getElementById('teamEmail').value.trim(); var name = document.getElementById('teamName').value.trim(); var role = document.getElementById('teamRole').value; if (!email) { showToast('Podaj adres email', 'error'); return; } if (!name) { showToast('Podaj imię i nazwisko', 'error'); return; } fetch('/firma/' + TEAM_COMPANY_ID + '/zespol/dodaj', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': CSRF_TOKEN }, body: JSON.stringify({ email: email, name: name, role: role }) }) .then(function(r) { return r.json(); }) .then(function(data) { if (data.success) { showToast(data.message, 'success'); document.getElementById('teamEmail').value = ''; document.getElementById('teamName').value = ''; document.getElementById('teamAddForm').style.display = 'none'; loadTeamMembers(); } else { showToast(data.error || 'Błąd', 'error'); } }) .catch(function() { showToast('Błąd połączenia', 'error'); }); } function changeRole(userId, newRole) { fetch('/firma/' + TEAM_COMPANY_ID + '/zespol/' + userId + '/rola', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': CSRF_TOKEN }, body: JSON.stringify({ role: newRole }) }) .then(function(r) { return r.json(); }) .then(function(data) { if (data.success) { showToast(data.message, 'success'); loadTeamMembers(); // Re-render to update permissions section } else { showToast(data.error || 'Błąd', 'error'); loadTeamMembers(); // Revert select } }) .catch(function() { showToast('Błąd połączenia', 'error'); }); } function togglePermission(userId, permKey, value) { fetch('/firma/' + TEAM_COMPANY_ID + '/zespol/' + userId + '/uprawnienia', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': CSRF_TOKEN }, body: JSON.stringify({ permission: permKey, value: value }) }) .then(function(r) { return r.json(); }) .then(function(data) { if (!data.success) { showToast(data.error || 'Błąd', 'error'); loadTeamMembers(); } }) .catch(function() { showToast('Błąd połączenia', 'error'); }); } function removeTeamMember(userId, userName) { nordaConfirm( 'Usunąć ' + escapeHtml(userName) + ' z zespołu?
Konto użytkownika nie zostanie usunięte — tylko powiązanie z firmą.', function() { fetch('/firma/' + TEAM_COMPANY_ID + '/zespol/' + userId + '/usun', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': CSRF_TOKEN } }) .then(function(r) { return r.json(); }) .then(function(data) { if (data.success) { showToast(data.message, 'success'); loadTeamMembers(); } else showToast(data.error || 'Błąd', 'error'); }) .catch(function() { showToast('Błąd połączenia', 'error'); }); } ); } function resendInvite(userId, email) { fetch('/firma/' + TEAM_COMPANY_ID + '/zespol/' + userId + '/zaproszenie', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': CSRF_TOKEN } }) .then(function(r) { return r.json(); }) .then(function(data) { if (data.success) showToast('Zaproszenie wysłane ponownie na ' + email, 'success'); else showToast(data.error || 'Błąd', 'error'); }) .catch(function() { showToast('Błąd połączenia', 'error'); }); } function formatDate(isoStr) { if (!isoStr) return ''; var d = new Date(isoStr); var day = d.getDate(); var months = ['sty', 'lut', 'mar', 'kwi', 'maj', 'cze', 'lip', 'sie', 'wrz', 'paź', 'lis', 'gru']; return day + ' ' + months[d.getMonth()] + ' ' + d.getFullYear(); } function escapeHtml(str) { var div = document.createElement('div'); div.textContent = str; return div.innerHTML; } {% endif %} {% endblock %}