nordabiz/templates/company_edit.html
Maciej Pienczyn b3982a3309
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
fix(company-edit): host Quill editor locally instead of CDN
CDN (cdn.jsdelivr.net) was being blocked, causing all WYSIWYG editors
to fail silently — description, history, values, and services fields
were not editable. Now served from static/js/vendor/ like D3 and Chart.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 18:12:39 +01:00

1552 lines
71 KiB
HTML

{% extends "base.html" %}
{% block title %}Edytuj profil — {{ company.name }}{% endblock %}
{% block head_extra %}
<link href="{{ url_for('static', filename='css/quill.snow.css') }}" rel="stylesheet">
<script src="{{ url_for('static', filename='js/vendor/quill.js') }}"></script>
{% endblock %}
{% block extra_css %}
<style>
.ce-container {
max-width: 1400px;
margin: 0 auto;
padding: var(--spacing-md) var(--spacing-lg);
}
/* Split layout: editor + preview */
.ce-layout {
display: grid;
grid-template-columns: 1fr 380px;
gap: var(--spacing-lg);
align-items: start;
}
/* Preview panel */
.ce-preview {
position: sticky;
top: 80px;
max-height: calc(100vh - 100px);
overflow-y: auto;
background: var(--surface, #fff);
border-radius: var(--radius-lg, 0.75rem);
box-shadow: var(--shadow);
padding: var(--spacing-lg);
}
.ce-preview-title {
font-size: var(--font-size-sm);
font-weight: 600;
color: white;
text-transform: uppercase;
letter-spacing: 0.05em;
margin: calc(-1 * var(--spacing-lg)) calc(-1 * var(--spacing-lg)) var(--spacing-md);
padding: var(--spacing-md) var(--spacing-lg);
background: linear-gradient(135deg, var(--primary, #2E4872), #4a6999);
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.ce-preview-title svg { width: 16px; height: 16px; }
/* Preview sections */
.preview-company-name {
font-size: var(--font-size-xl, 1.25rem);
font-weight: 700;
color: var(--text-primary, #303030);
margin-bottom: var(--spacing-sm);
}
.preview-short-desc {
font-size: var(--font-size-sm);
color: var(--text-secondary, #464646);
margin-bottom: var(--spacing-lg);
font-style: italic;
}
.preview-section {
margin-bottom: var(--spacing-lg);
padding-bottom: var(--spacing-md);
border-bottom: 1px solid var(--border, #e0e4eb);
}
.preview-section:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.preview-section-label {
font-size: var(--font-size-sm);
font-weight: 600;
color: var(--text-primary, #303030);
margin-bottom: var(--spacing-sm);
display: flex;
align-items: center;
gap: 6px;
}
.preview-section-label::before {
content: '';
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
/* Preview dot colors matching tabs */
#previewDescriptionSection .preview-section-label::before,
#previewHistorySection .preview-section-label::before,
#previewValuesSection .preview-section-label::before { background: #6366f1; }
#previewServicesSection .preview-section-label::before { background: #f59e0b; }
#previewContactSection .preview-section-label::before { background: #10b981; }
#previewSocialSection .preview-section-label::before { background: #ec4899; }
.preview-section-content {
font-size: var(--font-size-sm);
color: var(--text-primary, #303030);
line-height: 1.7;
}
.preview-section-content p { margin-bottom: var(--spacing-sm); }
.preview-section-content ul, .preview-section-content ol {
padding-left: var(--spacing-lg);
margin-bottom: var(--spacing-sm);
}
.preview-section-content li { margin-bottom: 2px; }
.preview-section-content a { color: var(--primary, #2E4872); }
.preview-section-content strong { font-weight: 600; }
.preview-empty {
color: var(--text-secondary, #464646);
font-style: italic;
font-size: var(--font-size-sm);
opacity: 0.6;
}
/* Preview contact and social items */
.preview-contact-item {
display: flex;
align-items: center;
gap: var(--spacing-sm);
font-size: var(--font-size-sm);
margin-bottom: var(--spacing-xs);
}
.preview-contact-item svg { width: 14px; height: 14px; color: var(--primary); flex-shrink: 0; }
.preview-social-item {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: var(--font-size-sm);
color: var(--primary, #2E4872);
margin-right: var(--spacing-md);
margin-bottom: var(--spacing-xs);
}
/* Mobile preview button & bottom sheet */
.ce-preview-mobile-btn {
display: none;
position: fixed;
bottom: var(--spacing-lg);
right: var(--spacing-lg);
z-index: 900;
padding: 12px 20px;
background: var(--primary, #2E4872);
color: white;
border: none;
border-radius: 50px;
font-size: var(--font-size-sm);
font-weight: 600;
font-family: var(--font-family);
cursor: pointer;
box-shadow: 0 4px 12px rgba(46, 72, 114, 0.3);
align-items: center;
gap: var(--spacing-sm);
transition: all 0.2s;
}
.ce-preview-mobile-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(46, 72, 114, 0.4);
}
.ce-preview-mobile-btn svg { width: 18px; height: 18px; }
.ce-preview-sheet-overlay {
display: none;
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.5);
z-index: 1100;
}
.ce-preview-sheet-overlay.active { display: block; }
.ce-preview-sheet {
position: fixed;
bottom: 0; left: 0; right: 0;
max-height: 80vh;
background: var(--surface, #fff);
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
box-shadow: 0 -4px 20px rgba(0,0,0,0.15);
z-index: 1200;
overflow-y: auto;
padding: var(--spacing-lg);
transform: translateY(100%);
transition: transform 0.3s ease;
}
.ce-preview-sheet-overlay.active .ce-preview-sheet {
transform: translateY(0);
}
.ce-preview-sheet-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-md);
padding-bottom: var(--spacing-sm);
border-bottom: 1px solid var(--border, #e0e4eb);
}
.ce-preview-sheet-close {
background: none;
border: 1px solid var(--border, #e0e4eb);
border-radius: var(--radius);
padding: 6px 12px;
cursor: pointer;
font-size: var(--font-size-sm);
font-family: var(--font-family);
color: var(--text-secondary);
}
/* Header card */
.ce-header {
background: var(--surface, #fff);
border-radius: var(--radius-lg, 0.75rem);
padding: var(--spacing-xl);
box-shadow: var(--shadow);
margin-bottom: var(--spacing-lg);
display: flex;
align-items: center;
gap: var(--spacing-lg);
}
.ce-header-icon {
width: 56px;
height: 56px;
border-radius: 50%;
background: linear-gradient(135deg, var(--primary, #2E4872), var(--primary-light, #4a6999));
color: white;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.ce-header-icon svg { width: 28px; height: 28px; }
.ce-header-info h1 {
font-size: var(--font-size-2xl, 1.5rem);
color: var(--text-primary, #303030);
margin: 0 0 var(--spacing-xs);
}
.ce-header-info p {
color: var(--text-secondary, #464646);
margin: 0;
font-size: var(--font-size-sm);
}
.ce-header-actions {
margin-left: auto;
}
/* Main form card */
.ce-card {
background: var(--surface, #fff);
border-radius: var(--radius-lg, 0.75rem);
box-shadow: var(--shadow);
overflow: hidden;
}
/* Tabs — colored per section */
.ce-tabs {
display: flex;
background: var(--background, #EDF0F5);
border-bottom: 1px solid var(--border, #e0e4eb);
overflow-x: auto;
}
.ce-tab {
display: flex;
align-items: center;
gap: var(--spacing-sm);
padding: var(--spacing-md) var(--spacing-lg);
border: none;
background: none;
cursor: pointer;
font-size: var(--font-size-sm);
font-family: var(--font-family);
color: var(--text-secondary, #464646);
white-space: nowrap;
transition: all 0.2s;
border-bottom: 3px solid transparent;
margin-bottom: -1px;
font-weight: 500;
}
.ce-tab svg { width: 18px; height: 18px; flex-shrink: 0; opacity: 0.5; }
.ce-tab:hover {
color: var(--text-primary, #303030);
background: rgba(255,255,255,0.5);
}
.ce-tab:hover svg { opacity: 0.8; }
.ce-tab.active { background: var(--surface, #fff); font-weight: 600; }
.ce-tab.active svg { opacity: 1; }
/* Per-tab colors */
.ce-tab[data-tab="description"].active { color: #6366f1; border-bottom-color: #6366f1; }
.ce-tab[data-tab="description"].active svg { color: #6366f1; }
.ce-tab[data-tab="services"].active { color: #f59e0b; border-bottom-color: #f59e0b; }
.ce-tab[data-tab="services"].active svg { color: #f59e0b; }
.ce-tab[data-tab="contacts"].active { color: #10b981; border-bottom-color: #10b981; }
.ce-tab[data-tab="contacts"].active svg { color: #10b981; }
.ce-tab[data-tab="social"].active { color: #ec4899; border-bottom-color: #ec4899; }
.ce-tab[data-tab="social"].active svg { color: #ec4899; }
.ce-tab[data-tab="visibility"].active { color: #8b5cf6; border-bottom-color: #8b5cf6; }
.ce-tab[data-tab="visibility"].active svg { color: #8b5cf6; }
/* Tab content */
.ce-tab-content { display: none; padding: var(--spacing-xl); }
.ce-tab-content.active { display: block; }
/* Section headers inside tabs */
.ce-section-title {
font-size: var(--font-size-lg, 1.125rem);
font-weight: 600;
color: var(--text-primary, #303030);
margin: 0 0 var(--spacing-md);
padding: var(--spacing-sm) var(--spacing-md);
background: linear-gradient(90deg, rgba(16, 185, 129, 0.08), transparent);
border-left: 3px solid #10b981;
border-radius: 0 var(--radius) var(--radius) 0;
display: block;
}
/* Form overrides for this page */
.ce-card .form-group { margin-bottom: var(--spacing-lg); }
.ce-card .form-label {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
margin-bottom: var(--spacing-sm);
color: var(--text-primary, #303030);
font-size: var(--font-size-sm);
}
.ce-card .form-label::before {
content: '';
width: 3px;
height: 14px;
border-radius: 2px;
flex-shrink: 0;
}
/* Label accent colors per tab */
#tab-description .form-label::before { background: #6366f1; }
#tab-services .form-label::before { background: #f59e0b; }
#tab-contacts .form-label::before { background: #10b981; }
#tab-social .form-label::before { background: #ec4899; }
.ce-card .form-input {
width: 100%;
padding: 10px 14px;
border: 1px solid var(--border, #e0e4eb);
border-radius: var(--radius, 0.5rem);
font-size: var(--font-size-base, 1rem);
font-family: var(--font-family);
transition: var(--transition);
background: var(--surface, #fff);
color: var(--text-primary, #303030);
box-sizing: border-box;
}
.ce-card .form-input:focus {
outline: none;
border-color: var(--primary, #2E4872);
box-shadow: 0 0 0 3px rgba(46, 72, 114, 0.1);
}
.ce-card .form-input:disabled {
background: var(--background, #EDF0F5);
color: var(--text-secondary);
cursor: not-allowed;
}
.ce-card select.form-input {
cursor: pointer;
appearance: auto;
}
.ce-card textarea.form-input {
resize: vertical;
min-height: 100px;
line-height: 1.6;
}
.ce-card .form-help {
font-size: 0.8rem;
color: var(--text-secondary, #464646);
margin-top: var(--spacing-xs);
padding: 4px 10px;
background: #f8fafc;
border-radius: var(--radius, 0.5rem);
border-left: 2px solid var(--border, #e0e4eb);
}
.ce-card .form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-md);
}
/* Char counter */
.char-counter {
font-size: var(--font-size-sm);
color: var(--text-secondary);
text-align: right;
margin-top: var(--spacing-xs);
}
.char-counter.warning { color: var(--warning, #f59e0b); }
.char-counter.error { color: var(--error, #ef4444); }
/* Fieldset */
fieldset { border: none; padding: 0; margin: 0; }
fieldset[disabled] { opacity: 0.5; pointer-events: none; }
/* Permission warning */
.ce-no-permission {
background: linear-gradient(135deg, #fef3c7, #fde68a20);
border: 1px solid #fde68a;
border-left: 3px solid #f59e0b;
border-radius: var(--radius);
padding: var(--spacing-md);
margin-bottom: var(--spacing-lg);
font-size: var(--font-size-sm);
color: #92400e;
display: flex;
align-items: center;
gap: var(--spacing-sm);
font-weight: 500;
}
.ce-no-permission svg { width: 20px; height: 20px; flex-shrink: 0; color: #f59e0b; }
/* Info box */
.ce-info-box {
background: linear-gradient(135deg, #f0f9ff, #e0f2fe);
border: 1px solid #bae6fd;
border-left: 3px solid #0ea5e9;
border-radius: var(--radius);
padding: var(--spacing-md);
margin-bottom: var(--spacing-lg);
font-size: var(--font-size-sm);
color: #0369a1;
}
.ce-info-box strong { display: block; margin-bottom: var(--spacing-xs); }
.ce-info-box ul { margin: var(--spacing-xs) 0 0; padding-left: var(--spacing-lg); }
.ce-info-box li { margin-bottom: 2px; }
.ce-info-box a { color: #0369a1; font-weight: 500; }
/* Dynamic rows (contacts, social) */
.ce-dynamic-section {
margin-top: var(--spacing-lg);
padding-top: var(--spacing-lg);
border-top: 2px dashed var(--border, #e0e4eb);
}
.ce-dynamic-title {
font-size: var(--font-size-base);
font-weight: 600;
color: var(--text-primary);
margin: 0 0 var(--spacing-xs);
}
.ce-dynamic-help {
font-size: var(--font-size-sm);
color: var(--text-secondary);
margin: 0 0 var(--spacing-md);
}
.contact-row, .social-row {
display: flex;
gap: var(--spacing-sm);
align-items: center;
margin-bottom: var(--spacing-sm);
padding: var(--spacing-sm);
background: var(--background, #EDF0F5);
border-radius: var(--radius);
border: 1px solid var(--border, #e0e4eb);
}
.contact-type-select, .social-platform-select { flex: 0 0 140px; }
.contact-value-input, .social-url-input { flex: 1; }
.contact-purpose-input { flex: 0 0 170px; }
.btn-remove {
flex: 0 0 36px;
height: 36px;
border: 1px solid var(--border, #e0e4eb);
background: var(--surface, #fff);
color: var(--text-secondary);
border-radius: var(--radius);
cursor: pointer;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.btn-remove:hover {
background: #fee2e2;
color: #dc2626;
border-color: #fca5a5;
}
.btn-add {
display: inline-flex;
align-items: center;
gap: var(--spacing-xs);
padding: var(--spacing-sm) var(--spacing-md);
background: linear-gradient(135deg, rgba(46, 72, 114, 0.04), rgba(46, 72, 114, 0.08));
border: 1px dashed var(--primary, #2E4872);
border-radius: var(--radius);
color: var(--primary, #2E4872);
font-size: var(--font-size-sm);
font-weight: 500;
font-family: var(--font-family);
cursor: pointer;
transition: all 0.2s;
margin-top: var(--spacing-sm);
}
.btn-add:hover {
background: rgba(46, 72, 114, 0.05);
border-style: solid;
}
.btn-add svg { width: 16px; height: 16px; }
/* Social platform icon hints */
.social-row .social-platform-select { font-weight: 500; }
/* Website type select */
.website-type-select { flex: 0 0 140px; font-weight: 500; }
/* Visibility tab */
.visibility-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--spacing-md);
border-bottom: 1px solid var(--border, #e0e4eb);
}
.visibility-row:last-child { border-bottom: none; }
.visibility-info { flex: 1; min-width: 0; }
.visibility-label {
display: block;
font-weight: 500;
color: var(--text-primary, #303030);
font-size: var(--font-size-base);
}
.visibility-desc {
display: block;
font-size: var(--font-size-sm);
color: var(--text-secondary, #464646);
margin-top: 2px;
}
.visibility-toggle {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 14px;
border: 1px solid var(--border, #e0e4eb);
border-radius: var(--radius);
background: #ecfdf5;
color: #059669;
font-size: var(--font-size-sm);
font-family: var(--font-family);
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
flex-shrink: 0;
}
.visibility-toggle svg { width: 18px; height: 18px; }
.visibility-toggle:hover { opacity: 0.8; }
.visibility-toggle.hidden-state {
background: #fef3c7;
color: #92400e;
border-color: #fde68a;
}
.visibility-toggle.saving {
opacity: 0.5;
pointer-events: none;
}
/* Sub-section rows */
.visibility-sub {
padding-left: calc(var(--spacing-md) + 24px);
background: #f8fafc;
border-bottom-color: #f1f5f9;
}
.visibility-sub .visibility-label {
font-size: var(--font-size-sm);
font-weight: 400;
color: var(--text-secondary, #464646);
}
.visibility-toggle-sub {
padding: 4px 10px;
font-size: 0.8rem;
}
.visibility-toggle-sub svg { width: 15px; height: 15px; }
.visibility-toggle-sub.hidden-state {
background: #fef9c3;
color: #a16207;
border-color: #fde68a;
}
/* Website primary radio */
.website-primary-label {
display: flex;
align-items: center;
gap: 4px;
flex: 0 0 auto;
font-size: var(--font-size-sm);
color: var(--text-secondary);
cursor: pointer;
white-space: nowrap;
}
.website-primary-label input[type="radio"]:checked + span {
color: var(--primary, #2E4872);
font-weight: 600;
}
/* Form actions (sticky bottom) */
.ce-actions {
display: flex;
gap: var(--spacing-md);
padding: var(--spacing-lg) var(--spacing-xl);
border-top: 1px solid var(--border, #e0e4eb);
background: linear-gradient(180deg, #f8fafc, var(--background, #EDF0F5));
}
.ce-actions .btn-primary {
background: linear-gradient(135deg, var(--primary, #2E4872), #4a6999);
border: none;
color: white;
padding: 10px 28px;
font-weight: 600;
border-radius: var(--radius);
cursor: pointer;
font-size: var(--font-size-base);
font-family: var(--font-family);
transition: all 0.2s;
box-shadow: 0 2px 8px rgba(46, 72, 114, 0.25);
}
.ce-actions .btn-primary:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(46, 72, 114, 0.35);
}
/* Quill editor overrides */
.quill-container {
border: 1px solid var(--border, #e0e4eb);
border-radius: var(--radius, 0.5rem);
overflow: hidden;
background: var(--surface, #fff);
transition: var(--transition);
}
.quill-container:focus-within {
border-color: var(--primary, #2E4872);
box-shadow: 0 0 0 3px rgba(46, 72, 114, 0.1);
}
.quill-container .ql-toolbar {
border: none !important;
border-bottom: 1px solid var(--border, #e0e4eb) !important;
background: linear-gradient(180deg, #f1f5f9, var(--background, #EDF0F5));
font-family: var(--font-family) !important;
padding: 6px 10px !important;
}
.quill-container .ql-toolbar button:hover {
color: var(--primary, #2E4872) !important;
}
.quill-container .ql-toolbar button.ql-active {
color: var(--primary, #2E4872) !important;
}
.quill-container .ql-container {
border: none !important;
font-family: var(--font-family) !important;
font-size: var(--font-size-base, 1rem) !important;
}
.quill-container .ql-editor {
min-height: 120px;
line-height: 1.7;
color: var(--text-primary, #303030);
}
.quill-container .ql-editor.ql-blank::before {
color: var(--text-secondary, #464646);
opacity: 0.5;
font-style: italic;
}
.quill-container.quill-tall .ql-editor {
min-height: 200px;
}
fieldset[disabled] .quill-container {
opacity: 0.5;
pointer-events: none;
}
/* Responsive */
@media (max-width: 1024px) {
.ce-layout {
grid-template-columns: 1fr;
}
.ce-preview {
display: none;
}
.ce-preview-mobile-btn {
display: flex;
}
}
@media (max-width: 768px) {
.ce-header { flex-direction: column; text-align: center; }
.ce-header-actions { margin-left: 0; }
.ce-card .form-row { grid-template-columns: 1fr; }
.contact-row, .social-row { flex-wrap: wrap; }
.contact-type-select, .social-platform-select { flex: 1 1 100%; }
.contact-purpose-input { flex: 1 1 100%; }
.ce-tab { padding: var(--spacing-sm) var(--spacing-md); font-size: 13px; }
.ce-tab span.tab-label { display: none; }
}
</style>
{% endblock %}
{% block content %}
<div class="ce-container">
<!-- Header -->
<div class="ce-header">
<div class="ce-header-icon">
<svg 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>
</div>
<div class="ce-header-info">
<h1>{{ company.name }}</h1>
<p>Edytuj dane profilu firmy widoczne w katalogu Izby NORDA</p>
</div>
<div class="ce-header-actions">
<a href="{{ url_for('public.company_detail', company_id=company.id) }}" class="btn btn-outline btn-sm">
← Powrót do profilu
</a>
</div>
</div>
<!-- Split layout: editor + preview -->
<div class="ce-layout">
<!-- Main card -->
<div class="ce-card">
<!-- Tabs -->
<div class="ce-tabs">
<button type="button" class="ce-tab active" data-tab="description">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
<span class="tab-label">Opis</span>
</button>
<button type="button" class="ce-tab" data-tab="services">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
<span class="tab-label">Usługi</span>
</button>
<button type="button" class="ce-tab" data-tab="contacts">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
<span class="tab-label">Kontakt</span>
</button>
<button type="button" class="ce-tab" data-tab="social">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>
<span class="tab-label">Social Media</span>
</button>
<button type="button" class="ce-tab" data-tab="visibility">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
<span class="tab-label">Widoczność</span>
</button>
</div>
<form method="POST" action="{{ url_for('public.company_edit_save', company_id=company.id) }}" 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="ce-tab-content active" id="tab-description">
{% if not permissions.description %}
<div class="ce-no-permission">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
Nie masz uprawnień do edycji tej sekcji. Skontaktuj się z managerem firmy.
</div>
{% endif %}
<fieldset {% if not permissions.description %}disabled{% endif %}>
<div class="form-group">
<label for="category_id" class="form-label">Kategoria działalności</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 %}
{% set children = categories | selectattr('parent_id', 'equalto', cat.id) | list %}
{% if children %}
<optgroup label="{{ cat.name }}">
{% for sub in children %}
<option value="{{ sub.id }}" {% if company.category_id == sub.id %}selected{% endif %}>{{ sub.name }}</option>
{% endfor %}
</optgroup>
{% else %}
<option value="{{ cat.id }}" {% if company.category_id == cat.id %}selected{% endif %}>{{ cat.name }}</option>
{% endif %}
{% endif %}
{% endfor %}
</select>
<p class="form-help">Główna kategoria widoczna przy nazwie firmy w katalogu</p>
</div>
<div class="form-group">
<label for="year_established" class="form-label">Rok założenia firmy</label>
<input type="number" id="year_established" name="year_established" class="form-input" style="max-width: 200px;"
min="1800" max="2030" placeholder="np. 2015"
value="{{ company.year_established or '' }}"
{% if company.krs_registration_date or company.business_start_date %}readonly{% endif %}>
{% if company.krs_registration_date or company.business_start_date %}
<p class="form-help">Rok pochodzi z rejestru {% if company.krs_registration_date %}KRS{% else %}CEIDG{% endif %} — aby zmienić, skontaktuj się z administratorem</p>
{% else %}
<p class="form-help">Rok rozpoczęcia działalności firmy</p>
{% endif %}
</div>
<div class="form-group">
<label for="description_short" class="form-label">Krótki opis firmy</label>
<textarea id="description_short" name="description_short" class="form-input" rows="3" maxlength="500" placeholder="Np. Specjalizujemy się w usługach IT dla firm z Pomorza...">{{ company.description_short or '' }}</textarea>
<div class="char-counter" id="shortDescCounter">
<span id="shortDescCount">{{ (company.description_short or '') | length }}</span>/500 znaków
</div>
<p class="form-help">Widoczny na liście firm i w wynikach wyszukiwania</p>
</div>
<div class="form-group">
<label class="form-label">Pełny opis działalności</label>
<div class="quill-container quill-tall" id="quill-description_full"></div>
<textarea id="description_full" name="description_full" style="display:none;">{{ company.description_full or '' }}</textarea>
<p class="form-help">Użyj paska narzędzi do formatowania tekstu</p>
</div>
<div class="form-group">
<label class="form-label">Historia i doświadczenie</label>
<div class="quill-container" id="quill-founding_history"></div>
<textarea id="founding_history" name="founding_history" style="display:none;">{{ company.founding_history or '' }}</textarea>
</div>
<div class="form-group">
<label class="form-label">Wartości i misja</label>
<div class="quill-container" id="quill-core_values"></div>
<textarea id="core_values" name="core_values" style="display:none;">{{ company.core_values or '' }}</textarea>
</div>
</fieldset>
</div>
<!-- TAB 2: Usługi -->
<div class="ce-tab-content" id="tab-services">
{% if not permissions.services %}
<div class="ce-no-permission">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
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 class="form-label">Oferowane usługi i produkty</label>
<div class="quill-container quill-tall" id="quill-services_offered"></div>
<textarea id="services_offered" name="services_offered" style="display:none;">{{ company.services_offered or '' }}</textarea>
<p class="form-help">Użyj paska narzędzi do formatowania tekstu</p>
</div>
<div class="form-group">
<label for="technologies_used" class="form-label">Technologie i specjalizacje</label>
<textarea id="technologies_used" name="technologies_used" class="form-input" rows="4" placeholder="Używane technologie, narzędzia, certyfikaty...">{{ 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, cała Polska" maxlength="500">
<p class="form-help">Gdzie firma świadczy usługi</p>
</div>
<div class="form-group">
<label for="languages_offered" class="form-label">Języki obsługi</label>
<input type="text" id="languages_offered" name="languages_offered" class="form-input" value="{{ company.languages_offered or '' }}" placeholder="np. Polski, Angielski, Niemiecki" 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" style="max-width: 300px;">
<option value="">— Wybierz przedział —</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 }} pracowników</option>
{% endfor %}
</select>
</div>
</fieldset>
</div>
<!-- TAB 3: Kontakt -->
<div class="ce-tab-content" id="tab-contacts">
{% if not permissions.contacts %}
<div class="ce-no-permission">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
Nie masz uprawnień do edycji tej sekcji. Skontaktuj się z managerem firmy.
</div>
{% endif %}
<fieldset {% if not permissions.contacts %}disabled{% endif %}>
<span class="ce-section-title">Dane kontaktowe</span>
<div class="form-group" style="margin-bottom: var(--spacing-lg);">
<label class="form-label">Strony internetowe <span style="font-weight: normal; color: var(--text-secondary);">(max 5)</span></label>
<div id="websiteList">
{% for w in company_websites %}
<div class="social-row website-row">
<select name="website_types[]" class="form-input website-type-select">
<option value="website" {% if w.website_type == 'website' or not w.website_type %}selected{% endif %}>Strona firmowa</option>
<option value="store" {% if w.website_type == 'store' %}selected{% endif %}>Sklep</option>
<option value="booking" {% if w.website_type == 'booking' %}selected{% endif %}>Rezerwacje</option>
<option value="blog" {% if w.website_type == 'blog' %}selected{% endif %}>Blog</option>
<option value="portfolio" {% if w.website_type == 'portfolio' %}selected{% endif %}>Portfolio</option>
<option value="other" {% if w.website_type == 'other' %}selected{% endif %}>Inna</option>
</select>
<input type="url" name="website_urls[]" class="form-input social-url-input" value="{{ w.url }}" placeholder="https://www.twojafirma.pl">
<input type="text" name="website_labels[]" class="form-input" style="flex: 0 0 120px;" value="{{ w.label or '' }}" placeholder="Etykieta">
<label class="website-primary-label" title="Strona główna">
<input type="radio" name="website_primary" value="{{ loop.index0 }}" {% if w.is_primary %}checked{% endif %}>
<span>Gł.</span>
</label>
<button type="button" class="btn-remove" onclick="removeWebsiteRow(this)" title="Usuń">&#x2715;</button>
</div>
{% endfor %}
{% if not company_websites %}
{% if company.website %}
<div class="social-row website-row">
<select name="website_types[]" class="form-input website-type-select">
<option value="website" selected>Strona firmowa</option>
<option value="store">Sklep</option>
<option value="booking">Rezerwacje</option>
<option value="blog">Blog</option>
<option value="portfolio">Portfolio</option>
<option value="other">Inna</option>
</select>
<input type="url" name="website_urls[]" class="form-input social-url-input" value="{{ company.website }}" placeholder="https://www.twojafirma.pl">
<input type="text" name="website_labels[]" class="form-input" style="flex: 0 0 120px;" value="" placeholder="Etykieta">
<label class="website-primary-label" title="Strona główna">
<input type="radio" name="website_primary" value="0" checked>
<span>Gł.</span>
</label>
<button type="button" class="btn-remove" onclick="removeWebsiteRow(this)" title="Usuń">&#x2715;</button>
</div>
{% endif %}
{% endif %}
</div>
<button type="button" class="btn-add" id="addWebsiteBtn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Dodaj stronę WWW
</button>
</div>
<div class="form-row">
<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@twojafirma.pl">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="phone" class="form-label">Telefon główny</label>
<input type="tel" id="phone" name="phone" class="form-input" value="{{ company.phone or '' }}" placeholder="+48 58 123 45 67" maxlength="50">
</div>
<div class="form-group"></div>
</div>
<span class="ce-section-title" style="margin-top: var(--spacing-lg);">Adres siedziby</span>
<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. Dworcowa 7/3" maxlength="255">
</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" style="max-width: 150px;">
</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 -->
<div class="ce-dynamic-section">
<p class="ce-dynamic-title">Dodatkowe numery i adresy email</p>
<p class="ce-dynamic-help">Dodaj kolejne numery telefonów, adresy email lub fax z opisem przeznaczenia.</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="Numer lub adres">
<input type="text" name="contact_purposes[]" class="form-input contact-purpose-input" value="{{ contact.purpose or '' }}" placeholder="np. Biuro, Sprzedaż">
<button type="button" class="btn-remove" onclick="this.parentElement.remove()" title="Usuń">&#x2715;</button>
</div>
{% endfor %}
</div>
<button type="button" class="btn-add" id="addContactBtn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Dodaj kontakt
</button>
</div>
</fieldset>
</div>
<!-- TAB 4: Social Media -->
<div class="ce-tab-content" id="tab-social">
{% if not permissions.social %}
<div class="ce-no-permission">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
Nie masz uprawnień do edycji tej sekcji. Skontaktuj się z managerem firmy.
</div>
{% endif %}
<fieldset {% if not permissions.social %}disabled{% endif %}>
<p class="ce-dynamic-help" style="margin-top: 0;">Dodaj linki do profili firmy w mediach społecznościowych.</p>
<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://facebook.com/twojafirma">
<button type="button" class="btn-remove" onclick="this.parentElement.remove()" title="Usuń">&#x2715;</button>
</div>
{% endif %}
{% endfor %}
</div>
<button type="button" class="btn-add" id="addSocialBtn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Dodaj profil
</button>
{% 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="ce-info-box" style="margin-top: var(--spacing-lg);">
<strong>Profile wykryte automatycznie</strong>
Te profile zostały wykryte przez system i nie podlegają ręcznej edycji:
<ul>
{% for sm in social_media %}
{% if sm.source is not none and sm.source != 'manual_edit' and sm.source != 'manual' %}
<li><strong>{{ sm.platform | capitalize }}</strong>: <a href="{{ sm.url }}" target="_blank" rel="noopener">{{ sm.url | truncate(60) }}</a></li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
</fieldset>
</div>
<!-- TAB 5: Widoczność -->
<div class="ce-tab-content" id="tab-visibility">
<div class="ce-info-box">
<strong>Zarządzaj widocznością sekcji profilu</strong>
Ukryte sekcje nie będą widoczne dla odwiedzających Twój profil.
Ty i kadra zarządzająca nadal widzicie ukryte sekcje z oznaczeniem.
</div>
<div id="visibilitySections" style="margin-top: var(--spacing-lg);">
{% for key, label, description, subs in section_definitions %}
<div class="visibility-row" data-section="{{ key }}">
<div class="visibility-info">
<span class="visibility-label">{{ label }}</span>
<span class="visibility-desc">{{ description }}</span>
</div>
<button type="button" class="visibility-toggle {% if company.is_section_hidden(key) %}hidden-state{% endif %}"
onclick="toggleSection('{{ key }}', this)" title="Kliknij aby zmienić widoczność">
{% if company.is_section_hidden(key) %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
<span>Ukryta</span>
{% else %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
<span>Widoczna</span>
{% endif %}
</button>
</div>
{% if subs %}
{% for sub_key, sub_label in subs %}
<div class="visibility-row visibility-sub" data-section="{{ sub_key }}">
<div class="visibility-info">
<span class="visibility-label">{{ sub_label }}</span>
</div>
<button type="button" class="visibility-toggle visibility-toggle-sub {% if company.is_section_hidden(sub_key) %}hidden-state{% endif %}"
onclick="toggleSection('{{ sub_key }}', this)" title="Kliknij aby zmienić widoczność">
{% if company.is_section_hidden(sub_key) %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
<span>Ukryta</span>
{% else %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
<span>Widoczna</span>
{% endif %}
</button>
</div>
{% endfor %}
{% endif %}
{% endfor %}
</div>
<div id="visibilityStatus" style="margin-top: var(--spacing-md); display: none;" class="ce-info-box"></div>
</div>
<!-- Form actions -->
<div class="ce-actions">
<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>
<div class="ce-preview" id="livePreview">
<div class="ce-preview-title">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
Podgląd profilu
</div>
<div class="preview-company-name">{{ company.name }}</div>
<div class="preview-short-desc" id="previewShortDesc">{{ company.description_short or 'Brak krótkiego opisu' }}</div>
<div class="preview-section" id="previewDescriptionSection">
<div class="preview-section-label">Opis firmy</div>
<div class="preview-section-content" id="previewDescFull">
{% if company.description_full %}{{ company.description_full | safe }}{% else %}<span class="preview-empty">Uzupełnij opis firmy...</span>{% endif %}
</div>
</div>
<div class="preview-section" id="previewHistorySection">
<div class="preview-section-label">Historia i doświadczenie</div>
<div class="preview-section-content" id="previewHistory">
{% if company.founding_history %}{{ company.founding_history | safe }}{% else %}<span class="preview-empty">Uzupełnij historię...</span>{% endif %}
</div>
</div>
<div class="preview-section" id="previewValuesSection">
<div class="preview-section-label">Wartości i misja</div>
<div class="preview-section-content" id="previewValues">
{% if company.core_values %}{{ company.core_values | safe }}{% else %}<span class="preview-empty">Uzupełnij wartości...</span>{% endif %}
</div>
</div>
<div class="preview-section" id="previewServicesSection">
<div class="preview-section-label">Oferowane usługi</div>
<div class="preview-section-content" id="previewServices">
{% if company.services_offered %}{{ company.services_offered | safe }}{% else %}<span class="preview-empty">Uzupełnij usługi...</span>{% endif %}
</div>
</div>
<div class="preview-section" id="previewContactSection">
<div class="preview-section-label">Dane kontaktowe</div>
<div class="preview-section-content" id="previewContact">
{% if company.email %}<div class="preview-contact-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>{{ company.email }}</div>{% endif %}
{% if company.phone %}<div class="preview-contact-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/></svg>{{ company.phone }}</div>{% endif %}
{% if company.website %}<div class="preview-contact-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>{{ company.website }}</div>{% endif %}
{% if not company.email and not company.phone and not company.website %}<span class="preview-empty">Brak danych kontaktowych</span>{% endif %}
</div>
</div>
<div class="preview-section" id="previewSocialSection">
<div class="preview-section-label">Social Media</div>
<div class="preview-section-content" id="previewSocial">
{% for sm in social_media %}
<span class="preview-social-item">{{ sm.platform | capitalize }}</span>
{% endfor %}
{% if not social_media %}<span class="preview-empty">Brak profili social media</span>{% endif %}
</div>
</div>
</div>
</div><!-- /ce-layout -->
</div>
<!-- Mobile preview button -->
<button type="button" class="ce-preview-mobile-btn" id="mobilePreviewBtn" onclick="openMobilePreview()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
Podgląd
</button>
<!-- Mobile preview bottom sheet -->
<div class="ce-preview-sheet-overlay" id="previewSheetOverlay">
<div class="ce-preview-sheet" id="previewSheet">
<div class="ce-preview-sheet-header">
<span style="font-weight: 600;">Podgląd profilu</span>
<button type="button" class="ce-preview-sheet-close" onclick="closeMobilePreview()">Zamknij</button>
</div>
<div id="mobilePreviewContent"></div>
</div>
</div>
{% 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 === '<p><br></p>') ? '' : 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 !== '<p><br></p>') {
el.innerHTML = value;
el.classList.remove('preview-empty');
} else {
el.innerHTML = '<span class="preview-empty">' + (emptyTexts[fieldName] || '') + '</span>';
}
}
// 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 += '<div class="preview-contact-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>' + email + '</div>';
if (phone) html += '<div class="preview-contact-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/></svg>' + phone + '</div>';
el.innerHTML = html || '<span class="preview-empty">Brak danych kontaktowych</span>';
}
['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);
});
});
// 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 =
'<select name="contact_types[]" class="form-input contact-type-select">' +
'<option value="phone">\uD83D\uDCDE Telefon</option>' +
'<option value="mobile">\uD83D\uDCF1 Kom\u00f3rka</option>' +
'<option value="email">\u2709\uFE0F Email</option>' +
'<option value="fax">\uD83D\uDCE0 Fax</option>' +
'</select>' +
'<input type="text" name="contact_values[]" class="form-input contact-value-input" placeholder="Numer lub adres">' +
'<input type="text" name="contact_purposes[]" class="form-input contact-purpose-input" placeholder="np. Biuro, Sprzeda\u017c">' +
'<button type="button" class="btn-remove" onclick="this.parentElement.remove()" title="Usu\u0144">\u2715</button>';
return row;
}
// Helper: create social row HTML
function makeSocialRow() {
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" onclick="this.parentElement.remove()" title="Usu\u0144">\u2715</button>';
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 = '<select name="website_types[]" class="form-input website-type-select"><option value="website">Strona firmowa</option><option value="store">Sklep</option><option value="booking">Rezerwacje</option><option value="blog">Blog</option><option value="portfolio">Portfolio</option><option value="other">Inna</option></select>'
+ '<input type="url" name="website_urls[]" class="form-input social-url-input" value="" placeholder="https://www.twojafirma.pl">'
+ '<input type="text" name="website_labels[]" class="form-input" style="flex: 0 0 120px;" value="" placeholder="Etykieta">'
+ '<label class="website-primary-label" title="Strona główna"><input type="radio" name="website_primary" value="' + idx + '"><span>Gł.</span></label>'
+ '<button type="button" class="btn-remove" onclick="removeWebsiteRow(this)" title="Usuń">&#x2715;</button>';
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
? '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg><span>Ukryta</span>'
: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg><span>Widoczna</span>';
} 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();
});
}
{% endblock %}