This commit is contained in:
Mateusz Gruszczyński
2026-03-13 11:03:13 +01:00
commit 35571df778
132 changed files with 11197 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
{% extends 'base.html' %}
{% macro source_switch(name, current, first_value, first_label, second_value, second_label) -%}
<div class="source-switch" data-source-switch>
<input class="btn-check" type="radio" name="{{ name }}" id="{{ name }}-{{ first_value }}" value="{{ first_value }}" autocomplete="off" {{ 'checked' if current == first_value else '' }}>
<label class="btn btn-source" for="{{ name }}-{{ first_value }}">{{ first_label }}</label>
<input class="btn-check" type="radio" name="{{ name }}" id="{{ name }}-{{ second_value }}" value="{{ second_value }}" autocomplete="off" {{ 'checked' if current == second_value else '' }}>
<label class="btn btn-source" for="{{ name }}-{{ second_value }}">{{ second_label }}</label>
</div>
{%- endmacro %}
{% block title %}<i class="fa-solid fa-gear me-2 text-primary"></i>Ustawienia{% endblock %}
{% block content %}
{% set eyebrow='Konfiguracja' %}{% set heading='Ustawienia użytkownika i firmy' %}{% set description='Wybierz, które moduły mają korzystać z profilu globalnego, a które z indywidualnych ustawień użytkownika.' %}
{% include 'partials/page_header.html' with context %}
<div class="row g-3 mb-4">
<div class="col-lg-4"><div class="card h-100"><div class="card-header">Aktywna firma</div><div class="card-body compact-card-body"><div class="fw-semibold">{{ company.name if company else 'Brak przypisanej firmy' }}</div><div class="small text-secondary mt-2">KSeF współdzielony dotyczy aktywnej firmy. SMTP, Pushover i NFZ mogą działać globalnie lub indywidualnie per użytkownik.</div><div class="mt-3 d-flex gap-2 flex-wrap"><span class="badge text-bg-light border">KSeF {{ ksef_environment|upper }}</span><span class="badge text-bg-light border">SMTP {{ 'global' if mail_mode == 'global' else 'indywidualny' }}</span><span class="badge text-bg-light border">Pushover {{ 'global' if notify_mode == 'global' else 'indywidualny' }}</span><span class="badge text-bg-light border">NFZ {{ 'globalny' if nfz_mode == 'global' else 'indywidualny' }}</span></div></div></div></div>
<div class="col-lg-8"><div class="card h-100"><div class="card-header">Wygląd interfejsu</div><div class="card-body compact-card-body"><form method="post" class="row g-3 align-items-end">{{ appearance_form.hidden_tag() }}<div class="col-md-7">{{ appearance_form.theme_preference.label(class='form-label') }}{{ appearance_form.theme_preference(class='form-select') }}</div><div class="col-md-5 d-grid">{{ appearance_form.submit(class='btn btn-primary') }}</div></form></div></div></div>
</div>
<div class="row g-3">
<div class="col-lg-3">
<div class="nav flex-column nav-pills settings-tab gap-2">
{% if can_manage_company_settings %}<button class="nav-link active" data-bs-toggle="pill" data-bs-target="#tab-company" type="button">Firma</button>{% endif %}
<button class="nav-link {{ '' if can_manage_company_settings else 'active' }}" data-bs-toggle="pill" data-bs-target="#tab-ksef" type="button">KSeF</button>
<button class="nav-link" data-bs-toggle="pill" data-bs-target="#tab-mail" type="button">SMTP</button>
<button class="nav-link" data-bs-toggle="pill" data-bs-target="#tab-notify" type="button">Pushover</button>
<button class="nav-link" data-bs-toggle="pill" data-bs-target="#tab-nfz" type="button">NFZ</button>
</div>
</div>
<div class="col-lg-9">
<div class="tab-content">
{% if can_manage_company_settings %}
<div class="tab-pane fade show active" id="tab-company"><div class="card"><div class="card-header">Ustawienia firmy</div><div class="card-body"><form method="post" class="row g-3 align-items-end">{{ company_form.hidden_tag() }}<div class="col-md-6">{{ company_form.name.label(class='form-label') }}{{ company_form.name(class='form-control') }}</div><div class="col-md-3">{{ company_form.tax_id.label(class='form-label') }}{{ company_form.tax_id(class='form-control') }}</div><div class="col-md-3">{{ company_form.sync_interval_minutes.label(class='form-label') }}{{ company_form.sync_interval_minutes(class='form-control') }}</div><div class="col-md-6">{{ company_form.bank_account.label(class='form-label') }}{{ company_form.bank_account(class='form-control', placeholder='np. 11 1111 1111 1111 1111 1111 1111') }}</div><div class="col-md-6 form-check ms-2">{{ company_form.sync_enabled(class='form-check-input') }}{{ company_form.sync_enabled.label(class='form-check-label') }}</div><div class="col-md-5"><div class="form-check form-switch fs-5">{{ company_form.read_only_mode(class='form-check-input') }}{{ company_form.read_only_mode.label(class='form-check-label') }}</div><div class="form-text">Rzeczywisty tryb może być dodatkowo ograniczony globalnie lub uprawnieniami użytkownika.</div></div><div class="col-12"><div class="alert alert-{{ 'warning' if effective_read_only else 'success' }} mb-0">Tryb efektywny: <strong>{{ 'R/O' if effective_read_only else 'R/W' }}</strong>{% if read_only_reasons %}<div class="small mt-2">Źródło: {{ read_only_reasons|join(', ') }}</div>{% endif %}</div></div><div class="col-12 d-grid d-md-flex">{{ company_form.submit(class='btn btn-primary') }}</div></form></div></div></div>
{% endif %}
<div class="tab-pane fade {% if can_manage_company_settings %}{% else %}show active{% endif %}" id="tab-ksef"><div class="card"><div class="card-header">KSeF</div><div class="card-body">
<div class="settings-module-intro"><div><h6 class="mb-1">Model biznesowy KSeF</h6><div class="text-secondary small">Domyślnie użytkownik pracuje na własnym profilu. W razie potrzeby może przełączyć się na współdzielony profil aktywnej firmy przygotowany przez administratora.</div></div><span class="badge text-bg-{{ 'primary' if ksef_mode == 'user' else 'secondary' }}">{{ 'profil indywidualny' if ksef_mode == 'user' else 'profil współdzielony firmy' }}</span></div>
<form method="post" enctype="multipart/form-data" class="row g-3" data-mode-target="ksefFields">{{ ksef_form.hidden_tag() }}{{ source_switch(ksef_form.source_mode.name, ksef_mode, 'user', 'Moje ustawienia KSeF', 'global', 'Użyj profilu współdzielonego firmy') }}<div class="col-12"></div><div class="col-12 source-panel {{ '' if ksef_mode == 'user' else 'd-none' }}" id="ksefFields"><div class="row g-3"><div class="col-md-4">{{ ksef_form.environment.label(class='form-label') }}{{ ksef_form.environment(class='form-select') }}</div><div class="col-md-4">{{ ksef_form.auth_mode.label(class='form-label') }}{{ ksef_form.auth_mode(class='form-select') }}</div><div class="col-md-4">{{ ksef_form.client_id.label(class='form-label') }}{{ ksef_form.client_id(class='form-control') }}</div><div class="col-md-6">{{ ksef_form.token.label(class='form-label') }}{{ ksef_form.token(class='form-control', autocomplete='new-password', placeholder='Podaj nowy token tylko przy zmianie') }}<div class="form-text">{{ 'Token KSeF jest zapisany w konfiguracji tej firmy.' if company_token_configured else ('Token zapisany.' if token_configured else 'Brak zapisanego tokena.') }}</div></div><div class="col-md-6">{{ ksef_form.certificate_file.label(class='form-label') }}{{ ksef_form.certificate_file(class='form-control') }}<div class="form-text">{% if company_certificate_name %}Certyfikat KSeF jest zapisany w konfiguracji tej firmy. Wgrany plik: {{ company_certificate_name }}{% elif certificate_name %}Wgrany plik: {{ certificate_name }}{% elif company_certificate_configured %}Certyfikat KSeF jest zapisany w konfiguracji tej firmy.{% else %}Brak zapisanego certyfikatu.{% endif %}</div></div></div></div><div class="col-12 source-panel-note {{ '' if ksef_mode == 'global' else 'd-none' }}" id="ksefFieldsNote"><div class="alert alert-info mb-0">Po zapisaniu system będzie używał współdzielonego profilu KSeF aktywnej firmy. Parametry i certyfikat konfiguruje administrator w panelu Admin → Ustawienia globalne.</div></div><div class="col-12 d-flex gap-2 flex-wrap">{{ ksef_form.submit(class='btn btn-primary') }}</div></form>
</div></div></div>
<div class="tab-pane fade" id="tab-mail"><div class="card"><div class="card-header">SMTP</div><div class="card-body"><form method="post" class="row g-3" data-mode-target="mailFields">{{ mail_form.hidden_tag() }}{{ source_switch(mail_form.source_mode.name, mail_mode, 'global', 'Użyj ustawień globalnych', 'user', 'Podaj indywidualne ustawienia') }}<div class="col-12"></div><div class="col-12 source-panel {{ '' if mail_mode == 'user' else 'd-none' }}" id="mailFields"><div class="row g-3"><div class="col-md-4">{{ mail_form.server.label(class='form-label') }}{{ mail_form.server(class='form-control') }}</div><div class="col-md-2">{{ mail_form.port.label(class='form-label') }}{{ mail_form.port(class='form-control') }}</div><div class="col-md-3">{{ mail_form.username.label(class='form-label') }}{{ mail_form.username(class='form-control') }}</div><div class="col-md-3">{{ mail_form.password.label(class='form-label') }}{{ mail_form.password(class='form-control') }}</div><div class="col-md-6">{{ mail_form.sender.label(class='form-label') }}{{ mail_form.sender(class='form-control') }}</div><div class="col-md-6">{{ mail_form.test_recipient.label(class='form-label') }}{{ mail_form.test_recipient(class='form-control') }}</div><div class="col-md-4">{{ mail_form.security_mode.label(class='form-label') }}{{ mail_form.security_mode(class='form-select') }}</div></div></div><div class="col-12 source-panel-note {{ '' if mail_mode == 'global' else 'd-none' }}" id="mailFieldsNote"><div class="alert alert-info mb-0">Przy trybie globalnym wiadomości będą wysyłane przez konfigurację ustawioną przez administratora.</div></div><div class="col-12 d-flex gap-2 flex-wrap">{{ mail_form.submit(class='btn btn-primary') }}{{ mail_form.test_submit(class='btn btn-outline-secondary') }}</div></form></div></div></div>
<div class="tab-pane fade" id="tab-notify"><div class="card"><div class="card-header">Pushover</div><div class="card-body"><form method="post" class="row g-3" data-mode-target="notifyFields">{{ notify_form.hidden_tag() }}{{ source_switch(notify_form.source_mode.name, notify_mode, 'global', 'Użyj ustawień globalnych', 'user', 'Podaj indywidualne ustawienia') }}<div class="col-12"></div><div class="col-12 source-panel {{ '' if notify_mode == 'user' else 'd-none' }}" id="notifyFields"><div class="row g-3"><div class="col-md-6">{{ notify_form.pushover_user_key.label(class='form-label') }}{{ notify_form.pushover_user_key(class='form-control') }}</div><div class="col-md-6">{{ notify_form.pushover_api_token.label(class='form-label') }}{{ notify_form.pushover_api_token(class='form-control') }}</div><div class="col-md-4">{{ notify_form.min_amount.label(class='form-label') }}{{ notify_form.min_amount(class='form-control') }}</div><div class="col-md-8">{{ notify_form.quiet_hours.label(class='form-label') }}{{ notify_form.quiet_hours(class='form-control') }}</div><div class="col-12 form-check">{{ notify_form.enabled(class='form-check-input') }}{{ notify_form.enabled.label(class='form-check-label') }}</div></div></div><div class="col-12 source-panel-note {{ '' if notify_mode == 'global' else 'd-none' }}" id="notifyFieldsNote"><div class="alert alert-info mb-0">Przy trybie globalnym powiadomienia trafią według konfiguracji wspólnej systemu.</div></div><div class="col-12 d-flex gap-2 flex-wrap">{{ notify_form.submit(class='btn btn-primary') }}{{ notify_form.test_submit(class='btn btn-outline-secondary') }}</div></form></div></div></div>
<div class="tab-pane fade" id="tab-nfz"><div class="card"><div class="card-header">Moduł NFZ</div><div class="card-body"><form method="post" class="row g-3" data-mode-target="nfzFields">{{ nfz_form.hidden_tag() }}{{ source_switch(nfz_form.source_mode.name, nfz_mode, 'global', 'Użyj ustawień globalnych', 'user', 'Ustaw indywidualnie') }}<div class="col-12"></div><div class="col-12 source-panel {{ '' if nfz_mode == 'user' else 'd-none' }}" id="nfzFields"><div class="form-check form-switch fs-5">{{ nfz_form.enabled(class='form-check-input') }}{{ nfz_form.enabled.label(class='form-check-label') }}</div><div class="form-text">Własne ustawienie użytkownika nadpisze konfigurację globalną tylko dla Twojego konta.</div></div><div class="col-12 source-panel-note {{ '' if nfz_mode == 'global' else 'd-none' }}" id="nfzFieldsNote"><div class="alert alert-info mb-0">Moduł NFZ odziedziczy ustawienie globalne administratora.</div></div><div class="col-md-4 d-grid">{{ nfz_form.submit(class='btn btn-primary') }}</div></form></div></div></div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('[data-source-switch]').forEach(function (group) {
const form = group.closest('form');
if (!form) return;
const targetId = form.dataset.modeTarget;
const fields = targetId ? document.getElementById(targetId) : null;
const note = targetId ? document.getElementById(targetId + 'Note') : null;
const update = function () {
const checked = group.querySelector('input:checked');
const isUser = checked && checked.value === 'user';
if (fields) fields.classList.toggle('d-none', !isUser);
if (note) note.classList.toggle('d-none', isUser);
};
group.querySelectorAll('input').forEach(function (input) { input.addEventListener('change', update); });
update();
});
});
</script>
{% endblock %}