Files
akademiawodociagowca/docs/instrukcja_nowy_webinar.html
debian 2c3fbc6236
Deploy PROD / deploy (push) Successful in 10s
Skrypt wdrozenia w repozytorium, dokumentacja komend compose
SKRYPT WDROZENIA
`deploy-prod.sh` istnial wylacznie na serwerze, tak samo jak wczesniej cztery
zmienne LiveKit w compose: kto odtwarzalby instancje z repozytorium, nie
dostalby go wcale. Wchodzi do `scripts/` w wersji dzialajacej, z jedna zmiana:
sprawdzeniem czystosci repozytorium na starcie.

Powod jest praktyczny. `git merge --ff-only` odrzuca wdrozenie, gdy na serwerze
sa lokalne zmiany, ale robi to CICHO: deploy konczy sie statusem "sukces",
a kod nie wchodzi na serwer. Zdarzylo sie to dzis i wykrylo dopiero reczne
`git log` na produkcji. Teraz skrypt przerywa i wypisuje pliki.

NAZWA PROJEKTU W KOMENDACH COMPOSE
README uczyl komend produkcyjnych BEZ `-p`, czyli dokladnie tych, ktore
wchodza w cudzy projekt. Katalog aplikacji nazywa sie `app`, wiec compose
bierze z niego nazwe projektu, a na serwerze z kilkoma aplikacjami trafia
w nie te. Zdarzylo sie realnie: `up -d --force-recreate coturn` utworzylo
kontener w projekcie drugiej aplikacji tego samego wlasciciela, walczacy
o ten sam port 3478. Jedynym ostrzezeniem byl komunikat "Found orphan
containers", latwy do przeoczenia.

Poprawione 11 komend produkcyjnych plus dwie nowe sekcje: o nazwie projektu
i o tym, ze `docker restart` NIE przeladowuje zmiennych z `.env` (argumenty
sa zamrozone przy tworzeniu kontenera, wiec po zmianie hasla kontener wstaje
ze stara wartoscia, wygladajac na dzialajacy poprawnie). Komendy lokalne
zostaja bez `-p`: tam nazwa z katalogu nikomu nie wchodzi w droge.

INSTRUKCJA TWORZENIA WEBINARU
Uzupelniona o funkcje dodane w ostatnich dniach: link do sali trafiajacy do
uczestnika automatycznie (potwierdzenie, przypomnienia, strona zgloszenia),
nagrywanie wlaczane przyciskiem w panelu prowadzenia oraz rozmycie tla.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 11:27:30 +02:00

581 lines
25 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<title>Nowy webinar krok po kroku</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400&family=IBM+Plex+Mono:wght@400;500;600&display=swap">
<style>
:root {
/* System HydroNexis: chlodne slate + Hydro Azure, druga farba navy, akcent aqua */
--azure: #2C8ABD;
--azure-strong: #1f6d99;
--navy: #23415B;
--navy-deep: #1a3247;
--aqua: #8AC4DC;
--aqua-soft: #e6f2f8;
--bg: #eef3f8;
--surface: #ffffff;
--surface-alt: #f7fafd;
--border: #d7e2ec;
--border-strong: #c2d2df;
--ink: #122033;
--ink-soft: #40566d;
--ink-faint: #6f849a;
--ok: #1f8b4c;
--ok-bg: #eaf6ee;
--ok-border: #b6ddc4;
--warn: #b06a11;
--warn-bg: #fdf3e3;
--warn-border: #f0d6a8;
--step-ring: var(--azure);
--mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
--sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--shadow: 0 1px 2px rgba(18,32,51,.05), 0 8px 24px -12px rgba(18,32,51,.18);
--radius: 14px;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--azure: #4aa6d6;
--azure-strong: #6bbbe4;
--navy: #9fc3dc;
--navy-deep: #cfe2ef;
--aqua: #7fb9d3;
--aqua-soft: #16303f;
--bg: #0c1620;
--surface: #12212e;
--surface-alt: #162836;
--border: #24404f;
--border-strong: #345066;
--ink: #e8f1f8;
--ink-soft: #a9c0d2;
--ink-faint: #7d94a7;
--ok: #55c07f;
--ok-bg: #12281c;
--ok-border: #275939;
--warn: #e0a34f;
--warn-bg: #2c2211;
--warn-border: #5a441d;
--shadow: 0 1px 2px rgba(0,0,0,.3), 0 12px 30px -14px rgba(0,0,0,.6);
}
}
:root[data-theme="dark"] {
--azure: #4aa6d6;
--azure-strong: #6bbbe4;
--navy: #9fc3dc;
--navy-deep: #cfe2ef;
--aqua: #7fb9d3;
--aqua-soft: #16303f;
--bg: #0c1620;
--surface: #12212e;
--surface-alt: #162836;
--border: #24404f;
--border-strong: #345066;
--ink: #e8f1f8;
--ink-soft: #a9c0d2;
--ink-faint: #7d94a7;
--ok: #55c07f;
--ok-bg: #12281c;
--ok-border: #275939;
--warn: #e0a34f;
--warn-bg: #2c2211;
--warn-border: #5a441d;
--shadow: 0 1px 2px rgba(0,0,0,.3), 0 12px 30px -14px rgba(0,0,0,.6);
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: var(--sans);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
.wrap { max-width: 860px; margin: 0 auto; padding: 32px 20px 80px; }
/* ---------- naglowek ---------- */
.hero {
background:
radial-gradient(120% 140% at 85% -20%, rgba(138,196,220,.28), transparent 55%),
linear-gradient(150deg, var(--navy) 0%, var(--navy-deep) 100%);
color: #f2f8fc;
border-radius: var(--radius);
padding: 30px 30px 26px;
box-shadow: var(--shadow);
position: relative;
overflow: hidden;
}
.hero .eyebrow {
font-family: var(--mono);
font-size: .72rem;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--aqua);
margin: 0 0 10px;
}
.hero h1 {
font-size: clamp(1.7rem, 4.5vw, 2.4rem);
line-height: 1.1;
font-weight: 700;
margin: 0 0 12px;
text-wrap: balance;
letter-spacing: -.01em;
}
.hero p { margin: 0; max-width: 60ch; color: #d6e6f0; font-size: 1.02rem; }
.hero .meta {
margin-top: 18px;
display: flex; flex-wrap: wrap; gap: 8px;
}
.chip {
font-family: var(--mono);
font-size: .74rem;
padding: 5px 11px;
border-radius: 999px;
background: rgba(255,255,255,.1);
border: 1px solid rgba(255,255,255,.18);
color: #eaf4fa;
}
/* ---------- sekcja wprowadzajaca ---------- */
.lead {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 22px 24px;
margin-top: 18px;
box-shadow: var(--shadow);
}
.lead h2 { margin: 0 0 8px; font-size: 1.05rem; color: var(--navy); }
.lead p { margin: 0 0 10px; color: var(--ink-soft); }
.lead p:last-child { margin-bottom: 0; }
.tablist { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 14px; }
.tab {
font-family: var(--mono);
font-size: .74rem;
padding: 5px 10px;
border-radius: 7px;
background: var(--aqua-soft);
color: var(--azure-strong);
border: 1px solid var(--border);
white-space: nowrap;
}
.tab.locked { opacity: .62; }
.tab.locked::after { content: " 🔒"; font-size: .68rem; }
/* ---------- osie krokow ---------- */
.steps { margin-top: 34px; position: relative; }
.step {
position: relative;
padding-left: 60px;
padding-bottom: 26px;
}
.step::before {
/* linia laczaca kroki */
content: "";
position: absolute;
left: 21px; top: 6px; bottom: -6px;
width: 2px;
background: linear-gradient(var(--border-strong), var(--border));
}
.step:last-child::before { display: none; }
.step > .num {
position: absolute;
left: 0; top: 0;
width: 44px; height: 44px;
border-radius: 50%;
display: grid; place-items: center;
font-family: var(--mono);
font-weight: 600;
font-size: 1.05rem;
color: #fff;
background: linear-gradient(150deg, var(--azure), var(--azure-strong));
box-shadow: 0 0 0 5px var(--bg), 0 4px 10px -3px rgba(44,138,189,.5);
z-index: 1;
}
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow: hidden;
}
.card > header {
padding: 16px 22px;
border-bottom: 1px solid var(--border);
background: var(--surface-alt);
display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
}
.card > header h3 { margin: 0; font-size: 1.15rem; color: var(--ink); letter-spacing: -.01em; }
.card > header .where {
font-family: var(--mono);
font-size: .72rem;
color: var(--azure-strong);
background: var(--aqua-soft);
padding: 3px 9px;
border-radius: 6px;
}
.card > .body { padding: 18px 22px 20px; }
.card > .body > *:first-child { margin-top: 0; }
.card > .body > *:last-child { margin-bottom: 0; }
.card p { color: var(--ink-soft); margin: 0 0 12px; }
/* pola formularza opisane */
.field {
border-left: 3px solid var(--aqua);
padding: 2px 0 2px 14px;
margin: 14px 0;
}
.field .name {
font-weight: 600;
color: var(--ink);
}
.field .tech {
font-family: var(--mono);
font-size: .78rem;
color: var(--ink-faint);
margin-left: 6px;
}
.field p { margin: 4px 0 0; font-size: .95rem; }
code {
font-family: var(--mono);
font-size: .85em;
background: var(--aqua-soft);
color: var(--azure-strong);
padding: 1px 6px;
border-radius: 5px;
border: 1px solid var(--border);
}
strong { color: var(--ink); font-weight: 600; }
ul.plain { margin: 10px 0; padding-left: 20px; color: var(--ink-soft); }
ul.plain li { margin-bottom: 6px; }
/* callouty */
.note {
border-radius: 10px;
padding: 12px 15px;
margin: 14px 0;
font-size: .93rem;
display: flex; gap: 10px; align-items: flex-start;
}
.note .ic { flex: none; font-size: 1.05rem; line-height: 1.4; }
.note p { margin: 0; }
.note.tip { background: var(--ok-bg); border: 1px solid var(--ok-border); color: var(--ink); }
.note.tip strong { color: var(--ok); }
.note.warn { background: var(--warn-bg); border: 1px solid var(--warn-border); color: var(--ink); }
.note.warn strong { color: var(--warn); }
/* tabela dostawcow */
.scroll { overflow-x: auto; margin: 12px 0; border: 1px solid var(--border); border-radius: 10px; }
table { border-collapse: collapse; width: 100%; font-size: .9rem; min-width: 460px; }
th, td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
th { background: var(--surface-alt); color: var(--navy); font-weight: 600; font-size: .8rem; letter-spacing: .02em; }
tr:last-child td { border-bottom: none; }
td .prov { font-family: var(--mono); font-weight: 600; color: var(--azure-strong); }
/* stopka */
.end {
margin-top: 30px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 20px 24px;
box-shadow: var(--shadow);
color: var(--ink-soft);
font-size: .92rem;
}
.end h2 { margin: 0 0 8px; color: var(--navy); font-size: 1.05rem; }
.end a { color: var(--azure-strong); }
@media (max-width: 560px) {
.step { padding-left: 50px; }
.step > .num { width: 38px; height: 38px; font-size: .95rem; }
.step::before { left: 18px; }
}
</style>
<div class="wrap">
<header class="hero">
<p class="eyebrow">Panel &rarr; Webinary &rarr; Nowy webinar</p>
<h1>Nowy webinar krok po kroku</h1>
<p>Przewodnik po formularzu tworzenia webinaru: co wpisać w każdej zakładce, w jakiej kolejności i co dzieje się dopiero po pierwszym zapisaniu.</p>
<div class="meta">
<span class="chip">Rola: ADMIN lub MODERATOR</span>
<span class="chip">8 zakładek</span>
<span class="chip">Zapis jako szkic</span>
<span class="chip">Stan na 4 września 2026</span>
</div>
</header>
<section class="lead">
<h2>Zanim zaczniesz — jak działa ten formularz</h2>
<p>Formularz jest podzielony na zakładki, ale to <strong>jeden formularz z jednym przyciskiem „Zapisz”</strong>. Przełączanie zakładek nie gubi wpisanych danych i nie zapisuje po kawałku — zapisujesz wszystko naraz.</p>
<p>Nowy webinar zapisuje się zawsze jako <strong>szkic (DRAFT)</strong> — nie jest jeszcze widoczny publicznie. Publikacja to osobna, świadoma decyzja (krok 8).</p>
<p>Trzy zakładki wypełnisz <strong>dopiero po pierwszym zapisaniu</strong>, bo ich zawartość jest powiązana z istniejącym już webinarem. Do zapisania szkicu wystarczą: tytuł, termin rozpoczęcia i zakończenia.</p>
<div class="tablist" aria-label="Zakładki formularza">
<span class="tab">1 · Podstawowe</span>
<span class="tab">2 · Treść strony</span>
<span class="tab">3 · Termin</span>
<span class="tab">4 · Zapisy</span>
<span class="tab locked">Po webinarze</span>
<span class="tab locked">Nagrania i materiały</span>
<span class="tab locked">Przypomnienia</span>
<span class="tab locked">Wideo</span>
</div>
<p style="margin-top:12px; font-size:.88rem; color:var(--ink-faint)">🔒 dostępne po pierwszym zapisaniu webinaru</p>
</section>
<div class="steps">
<!-- KROK 1 -->
<div class="step">
<div class="num">1</div>
<div class="card">
<header><h3>Podstawowe</h3><span class="where">zakładka „Podstawowe”</span></header>
<div class="body">
<p>Tożsamość webinaru: nazwa, adres strony zapisu i krótka zajawka.</p>
<div class="field">
<span class="name">Tytuł</span> <span class="tech">wymagane</span>
<p>Nazwa webinaru widoczna dla uczestników. Np. <em>„Audyt bezpieczeństwa sieci — jak wykryć luki, zanim zrobi to ktoś inny”</em>.</p>
</div>
<div class="field">
<span class="name">Adres strony zapisu</span> <span class="tech">slug</span>
<p>Końcówka adresu, pod którym uczestnik się zapisze. Podpowiadana automatycznie z tytułu — możesz zostawić lub zmienić. Pełny link (np. <code>https://twoja-domena/w/audyt-sieci</code>) składa się z adresu instancji; obok jest przycisk <strong>Kopiuj</strong>. Konflikt adresu system rozwiązuje sam, dodając numer.</p>
</div>
<div class="field">
<span class="name">Krótki opis</span>
<p>Jedno–dwa zdania, bez formatowania. Trafia do <strong>zajawki linku w komunikatorach</strong> (podgląd Messenger/WhatsApp) i do maili. To nie jest treść strony — tę wpiszesz w kroku 2.</p>
</div>
<div class="note warn">
<span class="ic">⚠️</span>
<p>Jeśli zobaczysz ostrzeżenie o braku <code>APP_PUBLIC_URL</code>, oznacza to, że instancja nie ma ustawionego publicznego adresu — linki w mailach nie zadziałają. Administrator uzupełnia to w <strong>Ustawieniach</strong>.</p>
</div>
</div>
</div>
</div>
<!-- KROK 2 -->
<div class="step">
<div class="num">2</div>
<div class="card">
<header><h3>Treść strony</h3><span class="where">zakładka „Treść strony”</span></header>
<div class="body">
<p>Dłuższa treść strony zapisu, budowana w edytorze wizualnym (pogrubienia, nagłówki, listy, obrazki, linki). Pojawia się <strong>nad formularzem zapisu</strong> — sam formularz zawsze zostaje na dole strony.</p>
<ul class="plain">
<li>Możesz wstawiać obrazki — trafiają do biblioteki mediów instancji, nie w treść jako base64.</li>
<li>Pusta treść oznacza stronę dokładnie taką jak dotychczas: sam formularz zapisu.</li>
<li>Treść jest oczyszczana przy zapisie (dozwolone tylko bezpieczne znaczniki).</li>
</ul>
<div class="note tip">
<span class="ic">💡</span>
<p><strong>Nie mylić z „Krótkim opisem”.</strong> Opis to jedno zdanie do zajawki linku; treść strony to pełny opis wydarzenia widoczny na stronie zapisu.</p>
</div>
</div>
</div>
</div>
<!-- KROK 3 -->
<div class="step">
<div class="num">3</div>
<div class="card">
<header><h3>Termin</h3><span class="where">zakładka „Termin”</span></header>
<div class="body">
<p>Kiedy webinar się odbywa. Godziny podajesz <strong>w strefie czasowej webinaru</strong> — najpierw wybierz strefę, potem godziny.</p>
<div class="field">
<span class="name">Strefa czasowa</span>
<p>Domyślnie <code>Europe/Warsaw</code>. Wszystkie godziny poniżej odnoszą się do tej strefy — także te pokazywane uczestnikom.</p>
</div>
<div class="field">
<span class="name">Rozpoczęcie</span> <span class="tech">wymagane</span>
<p>Data i godzina startu, w wybranej strefie.</p>
</div>
<div class="field">
<span class="name">Zakończenie</span> <span class="tech">wymagane</span>
<p>Data i godzina końca. Podział na „aktywne / zakończone” idzie właśnie po godzinie zakończenia.</p>
</div>
<div class="note tip">
<span class="ic">✅</span>
<p>Po wpisaniu tytułu i obu godzin masz komplet do <strong>zapisania szkicu</strong>. Resztę zakładek możesz uzupełnić teraz albo wrócić do nich później.</p>
</div>
</div>
</div>
</div>
<!-- KROK 4 -->
<div class="step">
<div class="num">4</div>
<div class="card">
<header><h3>Zapisy</h3><span class="where">zakładka „Zapisy”</span></header>
<div class="body">
<p>Kto i do kiedy może się zapisać.</p>
<div class="field">
<span class="name">Zapisy otwarte</span> <span class="tech">przełącznik</span>
<p>Twoja decyzja, czy przyjmować zapisy. <strong>Niezależnie</strong> od niej zapisy zamykają się same po wyczerpaniu miejsc albo po minięciu terminu. Po rezygnacji uczestnika wracają do tego ustawienia — nie otwierają się same.</p>
</div>
<div class="field">
<span class="name">Termin zapisów</span>
<p>Data graniczna zapisów. Puste = brak terminu granicznego (zapisy do startu / do wyczerpania miejsc).</p>
</div>
<div class="field">
<span class="name">Limit miejsc</span>
<p>Maksymalna liczba uczestników. Puste = bez limitu. Limit jest pilnowany przy zapisie, więc nie da się go przekroczyć nawet przy równoczesnych zgłoszeniach.</p>
</div>
<div class="field">
<span class="name">Link do spotkania</span> <span class="tech">joinUrl</span>
<p>Adres, pod którym odbędzie się spotkanie. Wysyłany uczestnikom w <strong>mailu potwierdzającym</strong>, nigdy nie pokazywany publicznie. Możesz go zostawić pusty, jeśli używasz wbudowanego pokoju wideo (krok 7).</p>
</div>
</div>
</div>
</div>
<!-- KROK 5 -->
<div class="step">
<div class="num">5</div>
<div class="card">
<header><h3>Zapisz szkic</h3><span class="where">przycisk „Zapisz”</span></header>
<div class="body">
<p>Kliknij <strong>Zapisz</strong> na dole formularza. Webinar powstaje jako <strong>szkic (DRAFT)</strong> — jeszcze niewidoczny publicznie.</p>
<ul class="plain">
<li>Jeśli któraś zakładka ma błąd (np. brak wymaganego pola), dostanie <strong>kropkę</strong>, a zapis przeskoczy na pierwszą wadliwą — komunikat wskaże, czego brakuje.</li>
<li>Po zapisaniu odblokowują się trzy zakładki poniżej, powiązane z istniejącym już webinarem.</li>
</ul>
<div class="note tip">
<span class="ic">💡</span>
<p>Od tego momentu możesz do webinaru wracać i edytować go dowolnie przed publikacją.</p>
</div>
</div>
</div>
</div>
<!-- KROK 6 -->
<div class="step">
<div class="num">6</div>
<div class="card">
<header><h3>Przypomnienia i treści po webinarze</h3><span class="where">zakładki „Przypomnienia”, „Po webinarze”</span></header>
<div class="body">
<p>Dostępne po pierwszym zapisaniu.</p>
<div class="field">
<span class="name">Przypomnienia</span>
<p>Po zapisaniu webinar dostaje <strong>domyślny zestaw przypomnień</strong> z ustawień instancji. Możesz je zmienić: dodawaj i usuwaj przypomnienia przyciskami, wybieraj odstęp (np. „1 dzień przed”, „godzinę przed”) i kanał (e-mail / SMS). Przy każdym widzisz zdanie opisujące skutek.</p>
</div>
<div class="field">
<span class="name">Po webinarze</span> <span class="tech">adres nagrania, adres materiałów</span>
<p>Oba adresy trafiają do <strong>maila podsumowującego</strong> wysyłanego automatycznie po zakończeniu. Uzupełnij je przed wysyłką podsumowania; jeśli nagranie pojawi się później, jest osobny przycisk do wysłania powiadomienia o nagraniu.</p>
</div>
<div class="note tip">
<span class="ic">💡</span>
<p><strong>Linku do spotkania nie musisz nigdzie wpisywać ani wysyłać.</strong> Uczestnik dostaje go automatycznie: w mailu potwierdzającym zapis, w każdym przypomnieniu oraz na stronie swojego zgłoszenia. Sala otwiera się krótko przed startem &mdash; kto kliknie wcześniej, zobaczy odliczanie zamiast błędu.</p>
<p style="margin-top:8px">Dotyczy to dostawców z własnym pokojem (LiveKit, Jitsi, Daily, Zoom). W trybie <span class="prov">NONE</span> obowiązuje link, który wkleisz w polu „Link do spotkania”.</p>
</div>
<div class="field">
<span class="name">Nagrania i materiały</span>
<p>Osobna zakładka na wgranie plików do własnego składu (po zapisaniu webinaru). Alternatywa dla wklejania adresów zewnętrznych.</p>
</div>
</div>
</div>
</div>
<!-- KROK 7 -->
<div class="step">
<div class="num">7</div>
<div class="card">
<header><h3>Wideo — pokój i prelegenci</h3><span class="where">zakładka „Wideo”</span></header>
<div class="body">
<p>Dostępne po pierwszym zapisaniu. Tu wybierasz, gdzie odbędzie się obraz, i zakładasz pokój u dostawcy.</p>
<ul class="plain">
<li>Domyślnie webinar używa <strong>dostawcy z ustawień instancji</strong> — nie musisz nic wybierać.</li>
<li>Ten webinar może użyć innego dostawcy niż reszta instancji — wybierz go z listy. Przy wyborze jest ikona <strong>„?”</strong> z opisem różnic między dostawcami.</li>
<li>Kliknij <strong>„Utwórz pokój u dostawcy”</strong>. Działa tylko, gdy w ustawieniach instancji podano klucze wybranego dostawcy.</li>
<li>Poniżej dodajesz <strong>prelegentów</strong> i kopiujesz linki wejścia (osobny dla prelegenta, osobny dla uczestnika).</li>
</ul>
<p style="margin-top:14px"><strong>Różnice między dostawcami — co jest realnie egzekwowane:</strong></p>
<div class="scroll">
<table>
<thead>
<tr><th>Dostawca</th><th>Uprawnienia</th><th>Nagrywanie</th></tr>
</thead>
<tbody>
<tr><td><span class="prov">LIVEKIT</span><br><span style="color:var(--ink-faint);font-size:.82rem">domyślny</span></td><td>Egzekwuje serwer (token). Uczestnik nie włączy kamery, nawet podmieniając stronę.</td><td>Prowadzący włącza przyciskiem w panelu prowadzenia. Plik trafia na Wasz serwer (wymaga usług Egress + Redis).</td></tr>
<tr><td><span class="prov">DAILY</span></td><td>Egzekwuje serwer.</td><td>Pobierane od dostawcy po zakończeniu.</td></tr>
<tr><td><span class="prov">ZOOM</span></td><td>Osadzone w oknie. Role jak przy Jitsi.</td><td>Po stronie Zoom.</td></tr>
<tr><td><span class="prov">JITSI</span></td><td>Z JWT: tylko rola moderatora. Bez JWT: pokój otwarty dla każdego, kto zna adres.</td><td>Ręczne wgranie pliku.</td></tr>
<tr><td><span class="prov">NONE</span></td><td>Obraz poza platformą — wklejasz gotowy link w „Link do spotkania”.</td><td>Ręczne wgranie pliku.</td></tr>
</tbody>
</table>
</div>
<div class="note tip">
<span class="ic">💡</span>
<p>Wideo uczestników jest <strong>zawsze wyłączone</strong> na poziomie uprawnień. Prowadzący może w trakcie webinaru włączyć głos wybranej osobie lub — świadomie — kamerę dla wszystkich.</p>
</div>
<p style="margin-top:14px"><strong>Co prowadzący ma pod ręką w trakcie spotkania</strong> (Panel &rarr; Webinary &rarr; „Prowadzenie”):</p>
<ul class="plain">
<li><strong>Otwarty mikrofon</strong> — każdy mówi bez proszenia o głos. Włączenie pyta o potwierdzenie, bo otwiera mikrofony całej sali naraz.</li>
<li><strong>Wideo dla wszystkich</strong> — każdy uczestnik może włączyć kamerę.</li>
<li><strong>Nagraj spotkanie</strong> — start i stop nagrywania. Nagranie <strong>nie startuje samo</strong>: to świadoma decyzja organizatora, a zgoda uczestników na nagranie pozostaje jego sprawą. Gotowy plik pojawia się w zakładce „Nagrania i materiały” w ciągu minuty od zatrzymania.</li>
<li><strong>Kolejka zgłoszeń do głosu</strong>, moderacja pytań i ogłaszanie zadań.</li>
</ul>
<div class="note tip">
<span class="ic">💡</span>
<p><strong>Rozmycie tła.</strong> Każdy, kto ma włączoną kamerę — prowadzący i prelegenci — może rozmyć swoje tło przyciskiem w lewym górnym rogu okna wideo. Włącza się i wyłącza w dowolnym momencie, bez przerywania obrazu. Przydaje się przy prowadzeniu z domu.</p>
</div>
</div>
</div>
</div>
<!-- KROK 8 -->
<div class="step">
<div class="num">8</div>
<div class="card">
<header><h3>Publikacja</h3><span class="where">lista „Webinary” → „Publikuj”</span></header>
<div class="body">
<p>Publikacja to <strong>osobna, świadoma decyzja</strong> — nie zapis formularza. Wróć na listę webinarów (Panel → Webinary), znajdź swój szkic i kliknij <strong>„Publikuj”</strong>.</p>
<ul class="plain">
<li>Od tej chwili strona zapisu jest widoczna publicznie pod adresem z kroku 1.</li>
<li>Webinar wchodzi do liczników i sekcji „nadchodzące” na stronie głównej.</li>
<li>Ruszają przypomnienia zgodnie z harmonogramem.</li>
</ul>
<div class="note warn">
<span class="ic">⚠️</span>
<p>Sprawdź termin i strefę czasową <strong>przed publikacją</strong>. Po opublikowaniu uczestnicy zaczynają dostawać maile — pomyłka w godzinie trafia do ich skrzynek.</p>
</div>
</div>
</div>
</div>
</div>
<section class="end">
<h2>Skrót — najkrótsza droga</h2>
<p><strong>Tytuł → Termin (start i koniec) → Zapisz.</strong> Masz szkic. Dopracuj treść strony, zapisy, wideo i przypomnienia, a gdy wszystko się zgadza — wróć na listę i kliknij „Publikuj”.</p>
</section>
</div>