feat(company): Add company profile editing for owners and employees
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions

Allow company owners/employees to edit marketing fields (descriptions,
services, contacts, social media) directly without admin intervention.
Legal fields (NIP, KRS, name) remain admin-only. Per-tab permission
checks with delegated permissions support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-02-06 06:58:21 +01:00
parent b3270bda84
commit 1dc7f20e9b
5 changed files with 746 additions and 2 deletions

View File

@ -179,6 +179,9 @@ def register_blueprints(app):
# Announcements
'announcements_list': 'public.announcements_list',
'announcement_detail': 'public.announcement_detail',
# Company Edit
'company_edit': 'public.company_edit',
'company_edit_save': 'public.company_edit_save',
})
logger.info("Created public endpoint aliases")
except ImportError as e:

View File

@ -12,3 +12,4 @@ bp = Blueprint('public', __name__)
from . import routes # noqa: E402, F401
from . import routes_zopk # noqa: E402, F401
from . import routes_announcements # noqa: E402, F401
from . import routes_company_edit # noqa: E402, F401

View File

@ -0,0 +1,246 @@
"""
Company Edit Routes
===================
Routes for editing company profiles by authorized users.
"""
from flask import render_template, request, redirect, url_for, flash
from flask_login import login_required, current_user
from blueprints.public import bp
from sqlalchemy import or_
from database import SessionLocal, Company, CompanyContact, CompanySocialMedia, Category
from utils.helpers import sanitize_input, sanitize_html, validate_email, ensure_url
from datetime import datetime
import logging
logger = logging.getLogger(__name__)
EMPLOYEE_COUNT_WHITELIST = ['1-5', '6-10', '11-25', '26-50', '51-100', '101-250', '250+', '']
VALID_SOCIAL_PLATFORMS = ['facebook', 'linkedin', 'instagram', 'youtube', 'twitter', 'tiktok']
EDITABLE_SOURCES = [None, 'manual_edit', 'manual']
@bp.route('/firma/edytuj')
@login_required
def company_edit():
"""Display the company profile edit form."""
if not current_user.can_edit_company():
flash('Nie masz uprawnień do edycji profilu firmy.', 'error')
return redirect(url_for('public.dashboard'))
db = SessionLocal()
try:
company = db.query(Company).get(current_user.company_id)
if not company:
flash('Nie znaleziono firmy.', 'error')
return redirect(url_for('public.dashboard'))
categories = db.query(Category).order_by(Category.name).all()
contacts = db.query(CompanyContact).filter_by(
company_id=company.id
).order_by(
CompanyContact.contact_type,
CompanyContact.is_primary.desc()
).all()
social_media = db.query(CompanySocialMedia).filter_by(
company_id=company.id
).all()
permissions = {
'description': current_user.can_edit_company_field('description'),
'services': current_user.can_edit_company_field('services'),
'contacts': current_user.can_edit_company_field('contacts'),
'social': current_user.can_edit_company_field('social'),
}
editable_contacts = [c for c in contacts if c.source in EDITABLE_SOURCES]
return render_template(
'company_edit.html',
company=company,
categories=categories,
contacts=editable_contacts,
all_contacts=contacts,
social_media=social_media,
permissions=permissions,
)
finally:
db.close()
@bp.route('/firma/edytuj', methods=['POST'])
@login_required
def company_edit_save():
"""Save company profile edits."""
if not current_user.can_edit_company():
flash('Nie masz uprawnień do edycji profilu firmy.', 'error')
return redirect(url_for('public.dashboard'))
db = SessionLocal()
try:
company = db.query(Company).get(current_user.company_id)
if not company:
flash('Nie znaleziono firmy.', 'error')
return redirect(url_for('public.dashboard'))
active_tab = request.form.get('active_tab', 'description')
if active_tab == 'description' and current_user.can_edit_company_field('description'):
_save_description(db, company)
elif active_tab == 'services' and current_user.can_edit_company_field('services'):
_save_services(company)
elif active_tab == 'contacts' and current_user.can_edit_company_field('contacts'):
_save_contacts(db, company)
elif active_tab == 'social' and current_user.can_edit_company_field('social'):
_save_social_media(db, company)
db.commit()
flash('Dane firmy zostały zaktualizowane.', 'success')
return redirect(url_for('public.company_detail', company_id=company.id))
except Exception as e:
db.rollback()
logger.error(f"Error saving company edit for company_id={current_user.company_id}: {e}")
flash('Wystąpił błąd podczas zapisywania zmian. Spróbuj ponownie.', 'error')
return redirect(url_for('public.company_edit'))
finally:
db.close()
def _save_description(db, company):
"""Save description tab fields."""
company.description_short = sanitize_input(
request.form.get('description_short', ''), max_length=500
) or None
company.description_full = sanitize_html(
request.form.get('description_full', '')
) or None
company.founding_history = sanitize_html(
request.form.get('founding_history', '')
) or None
company.core_values = sanitize_html(
request.form.get('core_values', '')
) or None
category_id_raw = request.form.get('category_id', '')
if category_id_raw:
try:
category_id = int(category_id_raw)
category = db.query(Category).get(category_id)
if category:
company.category_id = category_id
except (ValueError, TypeError):
pass
else:
company.category_id = None
def _save_services(company):
"""Save services tab fields."""
company.services_offered = sanitize_html(
request.form.get('services_offered', '')
) or None
company.technologies_used = sanitize_html(
request.form.get('technologies_used', '')
) or None
company.operational_area = sanitize_input(
request.form.get('operational_area', ''), max_length=500
) or None
company.languages_offered = sanitize_input(
request.form.get('languages_offered', ''), max_length=200
) or None
employee_count = request.form.get('employee_count_range', '')
if employee_count in EMPLOYEE_COUNT_WHITELIST:
company.employee_count_range = employee_count or None
def _save_contacts(db, company):
"""Save contacts tab fields."""
website_raw = sanitize_input(request.form.get('website', ''), max_length=500)
company.website = ensure_url(website_raw) if website_raw else None
email_raw = sanitize_input(request.form.get('email', ''), max_length=255)
if email_raw:
if validate_email(email_raw):
company.email = email_raw
else:
company.email = None
phone_raw = sanitize_input(request.form.get('phone', ''), max_length=50)
company.phone = phone_raw or None
company.address_street = sanitize_input(
request.form.get('address_street', ''), max_length=255
) or None
company.address_city = sanitize_input(
request.form.get('address_city', ''), max_length=100
) or None
company.address_postal = sanitize_input(
request.form.get('address_postal', ''), max_length=10
) or None
# Delete existing editable contacts (source is NULL, 'manual_edit', or 'manual')
db.query(CompanyContact).filter(
CompanyContact.company_id == company.id,
or_(
CompanyContact.source.in_(['manual_edit', 'manual']),
CompanyContact.source.is_(None)
)
).delete(synchronize_session='fetch')
# Add new contacts from form
contact_types = request.form.getlist('contact_types[]')
contact_values = request.form.getlist('contact_values[]')
contact_purposes = request.form.getlist('contact_purposes[]')
for i, value in enumerate(contact_values):
value = sanitize_input(value, max_length=255)
if not value:
continue
contact_type = sanitize_input(contact_types[i], max_length=20) if i < len(contact_types) else 'phone'
purpose = sanitize_input(contact_purposes[i], max_length=100) if i < len(contact_purposes) else ''
db.add(CompanyContact(
company_id=company.id,
contact_type=contact_type,
value=value,
purpose=purpose or None,
source='manual_edit',
))
def _save_social_media(db, company):
"""Save social media tab fields."""
# Delete existing editable social media (source is NULL, 'manual_edit', or 'manual')
db.query(CompanySocialMedia).filter(
CompanySocialMedia.company_id == company.id,
or_(
CompanySocialMedia.source.in_(['manual_edit', 'manual']),
CompanySocialMedia.source.is_(None)
)
).delete(synchronize_session='fetch')
# Add new social media from form
social_platforms = request.form.getlist('social_platforms[]')
social_urls = request.form.getlist('social_urls[]')
for i, url in enumerate(social_urls):
url = sanitize_input(url, max_length=500)
if not url:
continue
platform = social_platforms[i] if i < len(social_platforms) else ''
if platform not in VALID_SOCIAL_PLATFORMS:
continue
db.add(CompanySocialMedia(
company_id=company.id,
platform=platform,
url=ensure_url(url),
source='manual_edit',
verified_at=datetime.now(),
))

View File

@ -572,8 +572,17 @@
<h1 class="company-name">{{ company.name }}</h1>
<!-- AI Enrichment Button -->
<div style="margin: var(--spacing-md) 0;">
<!-- AI Enrichment Button + Edit Profile -->
<div style="margin: var(--spacing-md) 0; display: flex; gap: var(--spacing-sm); flex-wrap: wrap; align-items: center;">
{% if can_enrich %}
<a href="{{ url_for('company_edit') }}" class="ai-enrich-btn" style="text-decoration: none;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
</svg>
Edytuj profil
</a>
{% endif %}
<button
id="aiEnrichBtn"
class="ai-enrich-btn"

485
templates/company_edit.html Normal file
View File

@ -0,0 +1,485 @@
{% extends "base.html" %}
{% block title %}Edytuj profil — {{ company.name }}{% endblock %}
{% block extra_css %}
<style>
.company-edit-container {
max-width: 800px;
margin: 0 auto;
padding: var(--spacing-lg);
}
.company-edit-header {
margin-bottom: var(--spacing-lg);
}
.company-edit-header h1 {
margin: 0;
font-size: var(--font-size-2xl, 1.5rem);
}
.company-edit-subtitle {
color: var(--text-secondary);
margin: var(--spacing-xs) 0 0;
font-size: var(--font-size-lg, 1.1rem);
}
/* Tabs */
.edit-tabs {
display: flex;
gap: var(--spacing-xs);
border-bottom: 2px solid var(--border-color, #e5e7eb);
margin-bottom: var(--spacing-lg);
overflow-x: auto;
}
.edit-tab {
padding: var(--spacing-sm) var(--spacing-md);
border: none;
background: none;
cursor: pointer;
font-size: var(--font-size-base, 0.95rem);
color: var(--text-secondary);
border-bottom: 2px solid transparent;
margin-bottom: -2px;
white-space: nowrap;
transition: color 0.2s, border-color 0.2s;
}
.edit-tab:hover {
color: var(--text-primary);
}
.edit-tab.active {
color: var(--primary, #2563eb);
border-bottom-color: var(--primary, #2563eb);
font-weight: 600;
}
/* Tab content */
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
/* Disabled fieldset */
fieldset[disabled] {
opacity: 0.5;
pointer-events: none;
}
fieldset {
border: none;
padding: 0;
margin: 0;
}
/* Textarea styling */
textarea.form-input {
resize: vertical;
min-height: 80px;
font-family: inherit;
}
/* Contact/Social rows */
.contact-row, .social-row {
display: flex;
gap: var(--spacing-sm);
align-items: center;
margin-bottom: var(--spacing-sm);
}
.contact-type-select, .social-platform-select {
flex: 0 0 130px;
}
.contact-value-input, .social-url-input {
flex: 1;
}
.contact-purpose-input {
flex: 0 0 160px;
}
.btn-remove-contact {
flex: 0 0 36px;
height: 36px;
border: 1px solid var(--border-color, #e5e7eb);
background: var(--bg-secondary, #f9fafb);
color: var(--text-secondary);
border-radius: 6px;
cursor: pointer;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.btn-remove-contact:hover {
background: #fee2e2;
color: #dc2626;
border-color: #fca5a5;
}
.btn-sm {
padding: var(--spacing-xs) var(--spacing-sm);
font-size: var(--font-size-sm, 0.85rem);
}
/* Contacts section */
.contacts-section {
margin-top: var(--spacing-lg);
padding-top: var(--spacing-md);
border-top: 1px solid var(--border-color, #e5e7eb);
}
/* Responsive */
@media (max-width: 640px) {
.contact-row, .social-row {
flex-wrap: wrap;
}
.contact-type-select, .social-platform-select {
flex: 1 1 100%;
}
.contact-purpose-input {
flex: 1 1 100%;
}
.form-row {
grid-template-columns: 1fr;
}
}
</style>
{% endblock %}
{% block content %}
<div class="company-edit-container">
<div class="company-edit-header">
<h1>Edytuj profil firmy</h1>
<p class="company-edit-subtitle">{{ company.name }}</p>
<a href="{{ url_for('public.company_detail', company_id=company.id) }}" class="btn btn-outline" style="margin-top: var(--spacing-sm);">← Powrót do profilu</a>
</div>
<!-- Tab navigation -->
<div class="edit-tabs">
<button type="button" class="edit-tab active" data-tab="description">Opis</button>
<button type="button" class="edit-tab" data-tab="services">Usługi</button>
<button type="button" class="edit-tab" data-tab="contacts">Kontakt</button>
<button type="button" class="edit-tab" data-tab="social">Social Media</button>
</div>
<form method="POST" action="{{ url_for('public.company_edit_save') }}" id="companyEditForm">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="active_tab" id="activeTabInput" value="description">
<!-- TAB 1: Opis -->
<div class="tab-content active" id="tab-description">
{% if not permissions.description %}
<div class="info-box" style="background: #fff3cd; border-left: 4px solid #ffc107;">
Nie masz uprawnień do edycji tej sekcji. Skontaktuj się z managerem firmy.
</div>
{% endif %}
<fieldset {% if not permissions.description %}disabled{% endif %}>
<!-- category_id select -->
<div class="form-group">
<label for="category_id" class="form-label">Kategoria</label>
<select id="category_id" name="category_id" class="form-input">
<option value="">— Wybierz kategorię —</option>
{% for cat in categories %}
{% if cat.parent_id is none %}
<optgroup label="{{ cat.name }}">
{% for sub in categories if sub.parent_id == cat.id %}
<option value="{{ sub.id }}" {% if company.category_id == sub.id %}selected{% endif %}>{{ sub.name }}</option>
{% endfor %}
</optgroup>
{% endif %}
{% endfor %}
{# Also show top-level categories as direct options if they have no children #}
{% for cat in categories %}
{% if cat.parent_id is none %}
{% set has_children = categories | selectattr('parent_id', 'equalto', cat.id) | list | length > 0 %}
{% if not has_children %}
<option value="{{ cat.id }}" {% if company.category_id == cat.id %}selected{% endif %}>{{ cat.name }}</option>
{% endif %}
{% endif %}
{% endfor %}
</select>
</div>
<!-- description_short textarea -->
<div class="form-group">
<label for="description_short" class="form-label">Krótki opis</label>
<textarea id="description_short" name="description_short" class="form-input" rows="3" maxlength="500" placeholder="Krótki opis firmy (do 500 znaków)">{{ company.description_short or '' }}</textarea>
<p class="form-help">Widoczny na liście firm i w wynikach wyszukiwania. Max 500 znaków.</p>
</div>
<!-- description_full textarea -->
<div class="form-group">
<label for="description_full" class="form-label">Pełny opis</label>
<textarea id="description_full" name="description_full" class="form-input" rows="8" placeholder="Szczegółowy opis działalności firmy...">{{ company.description_full or '' }}</textarea>
<p class="form-help">Pełny opis na stronie profilu firmy. Możesz użyć formatowania HTML.</p>
</div>
<!-- founding_history textarea -->
<div class="form-group">
<label for="founding_history" class="form-label">Historia firmy</label>
<textarea id="founding_history" name="founding_history" class="form-input" rows="5" placeholder="Historia powstania i rozwoju firmy...">{{ company.founding_history or '' }}</textarea>
</div>
<!-- core_values textarea -->
<div class="form-group">
<label for="core_values" class="form-label">Wartości i misja</label>
<textarea id="core_values" name="core_values" class="form-input" rows="4" placeholder="Kluczowe wartości i misja firmy...">{{ company.core_values or '' }}</textarea>
</div>
</fieldset>
</div>
<!-- TAB 2: Uslugi -->
<div class="tab-content" id="tab-services">
{% if not permissions.services %}
<div class="info-box" style="background: #fff3cd; border-left: 4px solid #ffc107;">
Nie masz uprawnień do edycji tej sekcji. Skontaktuj się z managerem firmy.
</div>
{% endif %}
<fieldset {% if not permissions.services %}disabled{% endif %}>
<div class="form-group">
<label for="services_offered" class="form-label">Oferowane usługi</label>
<textarea id="services_offered" name="services_offered" class="form-input" rows="6" placeholder="Lista usług oferowanych przez firmę...">{{ company.services_offered or '' }}</textarea>
</div>
<div class="form-group">
<label for="technologies_used" class="form-label">Technologie</label>
<textarea id="technologies_used" name="technologies_used" class="form-input" rows="4" placeholder="Używane technologie i narzędzia...">{{ company.technologies_used or '' }}</textarea>
</div>
<div class="form-row">
<div class="form-group">
<label for="operational_area" class="form-label">Obszar działania</label>
<input type="text" id="operational_area" name="operational_area" class="form-input" value="{{ company.operational_area or '' }}" placeholder="np. Wejherowo, Trójmiasto, Pomorskie" maxlength="500">
</div>
<div class="form-group">
<label for="languages_offered" class="form-label">Języki</label>
<input type="text" id="languages_offered" name="languages_offered" class="form-input" value="{{ company.languages_offered or '' }}" placeholder="np. Polski, Angielski" maxlength="200">
</div>
</div>
<div class="form-group">
<label for="employee_count_range" class="form-label">Liczba pracowników</label>
<select id="employee_count_range" name="employee_count_range" class="form-input">
<option value="">— Wybierz —</option>
{% for range_val in ['1-5', '6-10', '11-25', '26-50', '51-100', '101-250', '250+'] %}
<option value="{{ range_val }}" {% if company.employee_count_range == range_val %}selected{% endif %}>{{ range_val }}</option>
{% endfor %}
</select>
</div>
</fieldset>
</div>
<!-- TAB 3: Kontakt -->
<div class="tab-content" id="tab-contacts">
{% if not permissions.contacts %}
<div class="info-box" style="background: #fff3cd; border-left: 4px solid #ffc107;">
Nie masz uprawnień do edycji tej sekcji. Skontaktuj się z managerem firmy.
</div>
{% endif %}
<fieldset {% if not permissions.contacts %}disabled{% endif %}>
<div class="form-row">
<div class="form-group">
<label for="website" class="form-label">Strona WWW</label>
<input type="url" id="website" name="website" class="form-input" value="{{ company.website or '' }}" placeholder="https://www.example.com">
</div>
<div class="form-group">
<label for="email" class="form-label">Email firmowy</label>
<input type="email" id="email" name="email" class="form-input" value="{{ company.email or '' }}" placeholder="kontakt@firma.pl">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="phone" class="form-label">Telefon</label>
<input type="tel" id="phone" name="phone" class="form-input" value="{{ company.phone or '' }}" placeholder="+48 123 456 789" maxlength="50">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="address_street" class="form-label">Ulica i numer</label>
<input type="text" id="address_street" name="address_street" class="form-input" value="{{ company.address_street or '' }}" placeholder="ul. Przykladowa 1" maxlength="255">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="address_postal" class="form-label">Kod pocztowy</label>
<input type="text" id="address_postal" name="address_postal" class="form-input" value="{{ company.address_postal or '' }}" placeholder="84-200" maxlength="10">
</div>
<div class="form-group">
<label for="address_city" class="form-label">Miasto</label>
<input type="text" id="address_city" name="address_city" class="form-input" value="{{ company.address_city or '' }}" placeholder="Wejherowo" maxlength="100">
</div>
</div>
<!-- Dynamic contacts section -->
<div class="contacts-section">
<h3 style="margin-top: var(--spacing-lg); margin-bottom: var(--spacing-sm); font-size: var(--font-size-lg);">Dodatkowe kontakty</h3>
<p class="form-help" style="margin-bottom: var(--spacing-md);">Dodaj numery telefonów, emaile i inne dane kontaktowe.</p>
<div id="contactsList">
{% for contact in contacts %}
<div class="contact-row">
<select name="contact_types[]" class="form-input contact-type-select">
<option value="phone" {% if contact.contact_type == 'phone' %}selected{% endif %}>Telefon</option>
<option value="mobile" {% if contact.contact_type == 'mobile' %}selected{% endif %}>Komórka</option>
<option value="email" {% if contact.contact_type == 'email' %}selected{% endif %}>Email</option>
<option value="fax" {% if contact.contact_type == 'fax' %}selected{% endif %}>Fax</option>
</select>
<input type="text" name="contact_values[]" class="form-input contact-value-input" value="{{ contact.value }}" placeholder="Wartosc">
<input type="text" name="contact_purposes[]" class="form-input contact-purpose-input" value="{{ contact.purpose or '' }}" placeholder="Cel (np. Biuro, Sprzedaz)">
<button type="button" class="btn-remove-contact" onclick="this.parentElement.remove()" title="Usun">&#x2715;</button>
</div>
{% endfor %}
</div>
<button type="button" class="btn btn-outline btn-sm" id="addContactBtn">+ Dodaj kontakt</button>
</div>
</fieldset>
</div>
<!-- TAB 4: Social Media -->
<div class="tab-content" id="tab-social">
{% if not permissions.social %}
<div class="info-box" style="background: #fff3cd; border-left: 4px solid #ffc107;">
Nie masz uprawnień do edycji tej sekcji. Skontaktuj się z managerem firmy.
</div>
{% endif %}
<fieldset {% if not permissions.social %}disabled{% endif %}>
<div id="socialList">
{% for sm in social_media %}
{% if sm.source is none or sm.source == 'manual_edit' or sm.source == 'manual' %}
<div class="social-row">
<select name="social_platforms[]" class="form-input social-platform-select">
<option value="facebook" {% if sm.platform == 'facebook' %}selected{% endif %}>Facebook</option>
<option value="linkedin" {% if sm.platform == 'linkedin' %}selected{% endif %}>LinkedIn</option>
<option value="instagram" {% if sm.platform == 'instagram' %}selected{% endif %}>Instagram</option>
<option value="youtube" {% if sm.platform == 'youtube' %}selected{% endif %}>YouTube</option>
<option value="twitter" {% if sm.platform == 'twitter' %}selected{% endif %}>X (Twitter)</option>
<option value="tiktok" {% if sm.platform == 'tiktok' %}selected{% endif %}>TikTok</option>
</select>
<input type="url" name="social_urls[]" class="form-input social-url-input" value="{{ sm.url }}" placeholder="https://...">
<button type="button" class="btn-remove-contact" onclick="this.parentElement.remove()" title="Usun">&#x2715;</button>
</div>
{% endif %}
{% endfor %}
</div>
{% 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 %}
<div class="info-box" style="margin-top: var(--spacing-md);">
<strong>Profile wykryte automatycznie</strong> (nie podlegają ręcznej edycji):
<ul style="margin: var(--spacing-xs) 0 0 var(--spacing-md); padding: 0;">
{% for sm in social_media %}
{% if sm.source is not none and sm.source != 'manual_edit' and sm.source != 'manual' %}
<li>{{ sm.platform | capitalize }}: <a href="{{ sm.url }}" target="_blank" rel="noopener">{{ sm.url | truncate(50) }}</a> <span style="color: var(--text-secondary); font-size: var(--font-size-sm);">({{ sm.source }})</span></li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
<button type="button" class="btn btn-outline btn-sm" id="addSocialBtn" style="margin-top: var(--spacing-md);">+ Dodaj profil</button>
</fieldset>
</div>
<!-- Form actions -->
<div class="form-actions" style="margin-top: var(--spacing-lg); padding-top: var(--spacing-md); border-top: 1px solid var(--border-color, #e5e7eb);">
<button type="submit" class="btn btn-primary">Zapisz zmiany</button>
<a href="{{ url_for('public.company_detail', company_id=company.id) }}" class="btn btn-outline">Anuluj</a>
</div>
</form>
</div>
{% endblock %}
{% block extra_js %}
// Company Edit - Tab switching and dynamic fields
(function() {
// Tab switching
var tabs = document.querySelectorAll('.edit-tab');
var contents = document.querySelectorAll('.tab-content');
var activeTabInput = document.getElementById('activeTabInput');
tabs.forEach(function(tab) {
tab.addEventListener('click', function() {
var targetTab = this.getAttribute('data-tab');
// Update tab buttons
tabs.forEach(function(t) { t.classList.remove('active'); });
this.classList.add('active');
// Update content
contents.forEach(function(c) { c.classList.remove('active'); });
document.getElementById('tab-' + targetTab).classList.add('active');
// Update hidden input
activeTabInput.value = targetTab;
});
});
// Add Contact button
var addContactBtn = document.getElementById('addContactBtn');
if (addContactBtn) {
addContactBtn.addEventListener('click', function() {
var list = document.getElementById('contactsList');
var row = document.createElement('div');
row.className = 'contact-row';
row.innerHTML = '<select name="contact_types[]" class="form-input contact-type-select">' +
'<option value="phone">Telefon</option>' +
'<option value="mobile">Kom\u00f3rka</option>' +
'<option value="email">Email</option>' +
'<option value="fax">Fax</option>' +
'</select>' +
'<input type="text" name="contact_values[]" class="form-input contact-value-input" placeholder="Warto\u015b\u0107">' +
'<input type="text" name="contact_purposes[]" class="form-input contact-purpose-input" placeholder="Cel (np. Biuro, Sprzeda\u017c)">' +
'<button type="button" class="btn-remove-contact" onclick="this.parentElement.remove()" title="Usu\u0144">\u2715</button>';
list.appendChild(row);
});
}
// Add Social Media button
var addSocialBtn = document.getElementById('addSocialBtn');
if (addSocialBtn) {
addSocialBtn.addEventListener('click', function() {
var list = document.getElementById('socialList');
var row = document.createElement('div');
row.className = 'social-row';
row.innerHTML = '<select name="social_platforms[]" class="form-input social-platform-select">' +
'<option value="facebook">Facebook</option>' +
'<option value="linkedin">LinkedIn</option>' +
'<option value="instagram">Instagram</option>' +
'<option value="youtube">YouTube</option>' +
'<option value="twitter">X (Twitter)</option>' +
'<option value="tiktok">TikTok</option>' +
'</select>' +
'<input type="url" name="social_urls[]" class="form-input social-url-input" placeholder="https://...">' +
'<button type="button" class="btn-remove-contact" onclick="this.parentElement.remove()" title="Usu\u0144">\u2715</button>';
list.appendChild(row);
});
}
// Client-side validation
document.getElementById('companyEditForm').addEventListener('submit', function(e) {
var emailField = document.getElementById('email');
if (emailField && emailField.value) {
var emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailPattern.test(emailField.value)) {
e.preventDefault();
alert('Nieprawid\u0142owy format adresu email.');
emailField.focus();
return;
}
}
var websiteField = document.getElementById('website');
if (websiteField && websiteField.value) {
var val = websiteField.value;
if (val && !val.match(/^https?:\/\//)) {
websiteField.value = 'https://' + val;
}
}
});
})();
{% endblock %}