From 6d31656ce486111796e95b04e7becb4aca2f4a4a Mon Sep 17 00:00:00 2001 From: Przemek Zatorski Date: Thu, 3 Sep 2026 21:57:48 +0200 Subject: [PATCH] API --- CLAUDE.md | 14 +- api/README.md | 65 ++++++++- api/config.php | 27 +++- api/schedule.php | 266 +++++++++++++++++++++++++++++++++++++ assets/img/akademia.svg | 130 ++++++++++++++++++ assets/img/en/akademia.svg | 130 ++++++++++++++++++ assets/reveal.js | 55 ++++++++ assets/schedule.js | 208 +++++++++++++++++++++++++++++ assets/styles.css | 36 +++++ index-en.html | 89 ++++++++++--- index.html | 102 +++++++++++--- konsulting.html | 1 + szkolenia.html | 18 +-- 13 files changed, 1090 insertions(+), 51 deletions(-) create mode 100644 api/schedule.php create mode 100644 assets/img/akademia.svg create mode 100644 assets/img/en/akademia.svg create mode 100644 assets/reveal.js create mode 100644 assets/schedule.js diff --git a/CLAUDE.md b/CLAUDE.md index 4fcbdfb..29dab25 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,6 +25,7 @@ SoftSPM is a Polish software house (**25+ years in IT**) that has served the wat - **Offer card order is fixed**: aplikacje pod klucz, konsulting, szkolenia, webinaria. - The **ecosystem is a headline theme**, not a footnote. `index.html#ekosystem` carries it with a diagram and three "loop" cards. - **[wodociagowiec.pl](https://wodociagowiec.pl/)** - trade portal: articles, cases, regulatory analysis, reader Q&A. Reader questions become webinar topics. + - **[akademiawodociagowca.pl](https://akademiawodociagowca.pl/)** - the **course platform**, added 2026-09-03. Live webinars, on-demand courses, tests, certificates, one company account. `index.html#platforma` (section 05) is its marketing home; it also feeds the schedule tables via its public API. Section 05 carries the "knowledge propagation" argument: taught first-hand, knowledge stays in-house, questions feed the programme. - **[gieldakomunalna.pl](https://gieldakomunalna.pl/)** - a **new, free marketplace** for the municipal sector (owner brief, 2026-08-31). Two jobs, and copy must reflect both: (1) users **list, buy, sell and swap free of charge** the stock **sitting idle in their warehouses** - project surplus, equipment replaced in upgrades, spares for discontinued machines; (2) it **brings together manufacturers and contractors** in a genuinely **niche, specialist** trade where there may be only a handful nationwide. It is **not** a tender/RFQ portal - do not describe it as "zapytania ofertowe", "miejsce wymiany ofert" or a procurement channel. - Content language is **Polish**, plus an English mirror of the homepage. @@ -36,7 +37,7 @@ SoftSPM is a Polish software house (**25+ years in IT**) that has served the wat | Page | Sections | |---|---| -| [index.html](index.html) | Hero · By the numbers (spec sheet) · Offer · Subject tracks · Upcoming dates · Course platform · **Ecosystem** · Testimonial · Newsletter | +| [index.html](index.html) | Hero · By the numbers (spec sheet) · Offer · Subject tracks · Upcoming dates · **Akademia Wodociągowca** (05) · Turnkey software · **Ecosystem** · Testimonial · Newsletter | | [szkolenia.html](szkolenia.html) | Hero · Training format (spec sheet) · Course catalogue (tracks 01-08 with prices, plus an RFQ workshop) · Schedule · Annual webinar package · Signup form | | [konsulting.html](konsulting.html) | Hero · 7 service areas (02.7 is `.cell-span` full-width) · 3-step process · Deliverables + projects (spec sheet) · Team · Testimonial · Contact form | | [index-en.html](index-en.html) | English mirror of the homepage - same eight tracks, same ecosystem loop cards; keep it in step when the Polish homepage changes | @@ -47,6 +48,8 @@ The **eight** subject tracks on `index.html#tematy`, in fixed order: 01 water la A **light wireframe/blueprint** aesthetic: light technical ground, a single steel-blue accent, square corners with registration-cross ("+") marks, cards drawn as line art with no fill, photos duotoned in the accent. Numbers and parameters are presented as **technical spec sheets** — a table with a title block carrying a drawing code, revision, and sheet number. +Motion: entrance animations are opt-in via `data-reveal` + [assets/reveal.js](assets/reveal.js) (IntersectionObserver, counters in `.stats`). The hiding styles only apply once the script adds `.js-reveal` to ``, so content stays visible without JavaScript, and everything is disabled under `prefers-reduced-motion`. `assets/img/akademia.svg` animates itself: line-drawing via `pathLength="1"` plus dashoffset, a flowing arrow dash and a pulsing LIVE dot, all declared inside the file so they run from an ``, with the same reduced-motion guard. + Rule: **one accent color only.** No extra decorative colors; hover/pressed states come from the tonal 100–900 ramp. | Role | Value | @@ -90,9 +93,8 @@ The spec lists these as unconfirmed — do not present them as fact, and flag th - Stats in "Doświadczenie w liczbach" (480+ trained, 24 webinars/year, 60+ advisory projects) — sample values. - Testimonials and quotes — samples, to be replaced with real references. -- Course platform name and URL (the "Przejdź do platformy" links point at `#`). - Course prices and the annual webinar package price. -- **Training dates are deliberately absent.** The schedule tables carry a `.tag-soon` "Wkrótce" / "Soon" chip instead of a date, and the CTA is "Powiadom mnie" / "Notify me" — the site collects interest rather than taking bookings. Do not reintroduce concrete dates until real ones are confirmed; if they are, restore the `Data`/`Date` column heading and the "Zapisz się" CTAs together with them. +- **The static schedule rows are the fallback, not the plan.** The `.tag-soon` "Wkrótce" / "Soon" rows in `index.html` (8) and `szkolenia.html` (11) show only when the Akademia API returns nothing or is unreachable — see **Schedule** below. Keep them plausible and on-topic; they are what visitors see when the platform has no open dates. - Team member names — the consulting page lists teams, not people, pending real bios. - Nothing else. The forms are live — see **Forms** below. @@ -106,6 +108,12 @@ Known real details: SoftSPM · Ławy, 97-400 Bełchatów · info@softspm.com. The folder also holds AI-generated photos from the previous direction (SCADA, control rooms, dashboards, blog imagery). **The built pages no longer reference any of them** — they belong to the dropped pump/monitoring theme. [images/README.md](images/README.md) is inherited from an even earlier "AquaNexis" branding and is stale; verify a file exists before referencing it. +## Schedule + +The `04 · Harmonogram` tables in `index.html` and `szkolenia.html` are filled at runtime by [assets/schedule.js](assets/schedule.js) from [api/schedule.php](api/schedule.php), which proxies the **Akademia Wodociągowca** public API v1 (`/webinars` + `/courses`, `X-API-Key` header, docs at `akademiawodociagowca/docs/api_public_v1.html`). Key stays server-side, response cached 5 min. Setup is in [api/README.md](api/README.md). + +When live items arrive the script swaps the rows, the `Termin` → `Data` column heading, the section heading, the intro and the sheet note (`data-schedule-*` hooks mark all of them). When the API is empty, unreachable or unconfigured it does nothing, so the static rows in the HTML stay — never leave that table without plausible static rows. + ## Forms All four forms — newsletter (`index.html`), contact (`index-en.html`), training signup (`szkolenia.html`), consulting (`konsulting.html`) — post JSON to [api/send.php](api/send.php), which relays to `api.softspm.pl/send_email_api.php` ([doc/manual_send_email.html](doc/manual_send_email.html)). **Never call that API from the browser**: it authenticates with a Bearer key, and a key in client-side JS is a public key. The proxy keeps it server-side and pins the recipient to server config, so the endpoint cannot be used to mail arbitrary addresses. diff --git a/api/README.md b/api/README.md index 5f7d0c2..5ec9036 100644 --- a/api/README.md +++ b/api/README.md @@ -43,6 +43,30 @@ return [ ]; ``` +### Akademia Wodociągowca (harmonogram w sekcji 04) + +Tabela terminów na stronie głównej i na `szkolenia.html` pobiera webinary +i kursy z publicznego API Akademii. Dopisz klucz wygenerowany w panelu +Akademii (Ustawienia -> Klucze API - pokazywany tylko raz): + +```php +// wariant ze stałymi +define('SOFTSPM_AKADEMIA_API_KEY', 'tutaj_klucz_z_panelu_akademii'); + +// wariant z tablicą +'softspm_akademia_api_key' => 'tutaj_klucz_z_panelu_akademii', +``` + +Adres API domyślnie to `https://akademiawodociagowca.pl/api/public/v1`. +Zmieniasz go tylko wtedy, gdy instancja stoi pod innym adresem: + +```php +define('SOFTSPM_AKADEMIA_API_URL', 'https://inna-domena/api/public/v1'); +``` + +Bez tego klucza nic się nie psuje: `api/schedule.php` zwraca +`source: "error"`, a w tabeli zostają statyczne wiersze wpisane w HTML. + ### Wpisy opcjonalne ```php @@ -145,7 +169,44 @@ szablon endpointu nie zamienia `\n` na `
`. `send_email_api.php` jest wasz, więc najczystszą poprawką jest tam jedno `nl2br()` na sanityzowanej treści - nic po stronie tej strony nie wymaga wtedy zmian. -## 5. Zabezpieczenia przed botami +## 5. Harmonogram z Akademii Wodociągowca + +[schedule.php](schedule.php) pobiera `GET /webinars` i `GET /courses` z API +Akademii (dokumentacja: `akademiawodociagowca/docs/api_public_v1.html`), scala +je w jedną listę i oddaje stronie jako JSON. Klucz `X-API-Key` zostaje na +serwerze - punkt 9 dokumentacji Akademii wprost tego wymaga. + +- **Cache:** odpowiedź leży w `api/storage/cache/schedule.json` przez + `akademia_cache_ttl` (domyślnie 300 s, zgodnie z zaleceniem 1-5 minut). + Pusty wynik i błąd trzymane są krócej, maksymalnie minutę. +- **Kolejność:** webinary z terminem rosnąco, potem kursy na żądanie. +- **Linki:** akceptujemy wyłącznie adresy prowadzące do domeny Akademii - + gdyby API zwróciło cokolwiek innego, link jest pomijany. +- **Limit:** `?limit=` (1-40). Strona główna prosi o 8 pozycji, + `szkolenia.html` o 12. + +Odpowiedź ma pole `source`: + +| `source` | Znaczenie | Co widzi użytkownik | +|---|---|---| +| `api` | Są pozycje | Tabela, nagłówki i notatka podmienione na dane z Akademii; kolumna wraca z „Termin" na „Data" | +| `empty` | API odpowiedziało pustą tablicą | Statyczne wiersze z HTML-a zostają nietknięte | +| `error` | Brak klucza albo API nie odpowiada | Jak wyżej | + +Statyczne wiersze to pełnoprawny plan awaryjny: działają bez JavaScriptu, +bez klucza i przy niedostępnym API. Strona nigdy nie pokazuje pustej tabeli. +Są w `index.html` (8 pozycji) i `szkolenia.html` (11 pozycji) - jeśli chcesz +zmienić te tematy zastępcze, edytujesz je wprost w HTML. + +Test: + +```bash +curl -s "https://softspm.com/api/schedule.php?limit=5" | head -c 400 +``` + +Oczekiwane: `{"ok":true,"source":"api",...}` z tablicą `items`. + +## 6. Zabezpieczenia przed botami Suwak sam z siebie niczego nie gwarantuje - jest elementem interfejsu, a nie kryptografią. Blokady, które faktycznie odsiewają automaty, działają na @@ -166,7 +227,7 @@ Jeśli mimo to zacznie przechodzić spam, następnym krokiem jest Cloudflare Turnstile albo hCaptcha - obie działają na tej samej zasadzie co obecny token, więc podmiana dotknie tylko `send.php` i `assets/forms.js`. -## 6. Test po wdrożeniu +## 7. Test po wdrożeniu ```bash # 1. Token - powinien zwrócić {"ok":true,"token":"t.szkolenia...."} diff --git a/api/config.php b/api/config.php index 31245d2..e7355ff 100644 --- a/api/config.php +++ b/api/config.php @@ -26,6 +26,14 @@ $config = [ // Dowolny losowy ciąg, min. 32 znaki. Zmiana unieważnia otwarte formularze. 'token_secret' => '', + // --- Akademia Wodociągowca: publiczne API v1 (webinary i kursy) -------- + // Dokumentacja: akademiawodociagowca/docs/api_public_v1.html + // Klucz z panelu Akademii: Ustawienia -> Klucze API. Pokazywany raz. + 'akademia_api_url' => 'https://akademiawodociagowca.pl/api/public/v1', + 'akademia_api_key' => '', + // Bufor odpowiedzi w sekundach. Dokumentacja Akademii zaleca 1-5 minut. + 'akademia_cache_ttl' => 300, + // --- Adresat zgłoszeń -------------------------------------------------- // Ustawiany wyłącznie po stronie serwera. Nigdy nie pochodzi z formularza - // inaczej endpoint stałby się otwartym przekaźnikiem spamu. @@ -68,8 +76,10 @@ $config = [ // --- 1. Zmienne środowiskowe ------------------------------------------------ $fromEnv = [ - 'api_key' => getenv('SOFTSPM_MAIL_API_KEY'), - 'token_secret' => getenv('SOFTSPM_FORM_SECRET'), + 'api_key' => getenv('SOFTSPM_MAIL_API_KEY'), + 'token_secret' => getenv('SOFTSPM_FORM_SECRET'), + 'akademia_api_key' => getenv('SOFTSPM_AKADEMIA_API_KEY'), + 'akademia_api_url' => getenv('SOFTSPM_AKADEMIA_API_URL'), ]; foreach ($fromEnv as $key => $value) { if (is_string($value) && $value !== '') { @@ -133,6 +143,19 @@ if (is_file($sharedPath) && is_readable($sharedPath)) { } } + // Klucz do publicznego API Akademii Wodociągowca. + if ($config['akademia_api_key'] === '') { + $value = $pick('SOFTSPM_AKADEMIA_API_KEY', 'softspm_akademia_api_key'); + if (is_string($value)) { + $config['akademia_api_key'] = $value; + } + } + // Adres API Akademii - tylko gdy instancja stoi pod innym adresem. + $value = $pick('SOFTSPM_AKADEMIA_API_URL', 'softspm_akademia_api_url'); + if (is_string($value)) { + $config['akademia_api_url'] = $value; + } + // Adresat zgłoszeń - jeden dla wszystkich formularzy. $value = $pick('SOFTSPM_FORM_RECIPIENT', 'softspm_form_recipient'); if (is_string($value)) { diff --git a/api/schedule.php b/api/schedule.php new file mode 100644 index 0000000..abe713e --- /dev/null +++ b/api/schedule.php @@ -0,0 +1,266 @@ + {"ok":true,"source":"api|empty|error","items":[...]} + * + * source = "api" - są pozycje, strona podmienia tabelę + * source = "empty" - API odpowiedziało pustą tablicą + * source = "error" - API niedostępne albo brak klucza + * W obu ostatnich przypadkach strona zostawia statyczne wiersze z HTML-a. + */ + +$config = require __DIR__ . '/config.php'; + +header('Content-Type: application/json; charset=utf-8'); +header('X-Content-Type-Options: nosniff'); + +function sched_respond(int $status, array $payload): void +{ + http_response_code($status); + echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + exit; +} + +function sched_storage(array $config): string +{ + $root = rtrim((string) $config['storage_dir'], '/\\'); + $dir = $root . '/cache'; + if (!is_dir($dir)) { + @mkdir($dir, 0770, true); + } + $guard = $root . '/.htaccess'; + if (!is_file($guard)) { + @file_put_contents($guard, "Require all denied\n\nDeny from all\n\n"); + } + + return $dir; +} + +/** @return array{status:int, body:string} */ +function sched_get(string $url, string $key, int $timeout): array +{ + $headers = ['Accept: application/json', 'X-API-Key: ' . $key]; + + if (function_exists('curl_init')) { + $ch = curl_init($url); + curl_setopt_array($ch, [ + CURLOPT_HTTPHEADER => $headers, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CONNECTTIMEOUT => 6, + CURLOPT_TIMEOUT => $timeout, + ]); + $body = curl_exec($ch); + $status = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE); + curl_close($ch); + + return ['status' => $status, 'body' => is_string($body) ? $body : '']; + } + + $context = stream_context_create([ + 'http' => [ + 'method' => 'GET', + 'header' => implode("\r\n", $headers), + 'timeout' => $timeout, + 'ignore_errors' => true, + ], + ]); + $body = @file_get_contents($url, false, $context); + $status = 0; + foreach ($http_response_header ?? [] as $line) { + if (preg_match('#^HTTP/\S+\s+(\d{3})#', $line, $m) === 1) { + $status = (int) $m[1]; + } + } + + return ['status' => $status, 'body' => is_string($body) ? $body : '']; +} + +function sched_text($value, int $max = 300): string +{ + if (!is_string($value)) { + return ''; + } + $value = preg_replace('/[\x00-\x1F\x7F]/u', ' ', $value) ?? ''; + $value = trim(preg_replace('/\s{2,}/u', ' ', $value) ?? ''); + + return mb_substr($value, 0, $max); +} + +/** Link trafia na stronę, więc musi prowadzić do Akademii, nie gdziekolwiek. */ +function sched_url($value, array $allowedHosts): ?string +{ + if (!is_string($value) || $value === '') { + return null; + } + $parts = parse_url($value); + if (!is_array($parts) || !isset($parts['scheme'], $parts['host'])) { + return null; + } + if (!in_array(strtolower($parts['scheme']), ['http', 'https'], true)) { + return null; + } + $host = strtolower($parts['host']); + foreach ($allowedHosts as $allowed) { + $allowed = strtolower((string) $allowed); + if ($allowed === '') { + continue; + } + if ($host === $allowed || substr($host, -strlen('.' . $allowed)) === '.' . $allowed) { + return $value; + } + } + + return null; +} + +// --------------------------------------------------------------------------- +if (strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'GET') { + sched_respond(405, ['ok' => false, 'source' => 'error', 'error' => 'method_not_allowed', 'items' => []]); +} + +$limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 12; +$limit = max(1, min(40, $limit)); + +$base = rtrim((string) $config['akademia_api_url'], '/'); +$key = (string) $config['akademia_api_key']; + +if ($base === '' || $key === '') { + sched_respond(200, ['ok' => true, 'source' => 'error', 'error' => 'not_configured', 'items' => []]); +} + +$linkHosts = []; +$hostFromBase = parse_url($base, PHP_URL_HOST); +if (is_string($hostFromBase) && $hostFromBase !== '') { + $linkHosts[] = preg_replace('/^www\./', '', $hostFromBase); +} + +// --- Cache ------------------------------------------------------------------ +// Dokumentacja Akademii zaleca buforowanie 1-5 minut; dane zmieniają się rzadko. +$ttl = (int) $config['akademia_cache_ttl']; +$dir = sched_storage($config); +$cache = $dir . '/schedule.json'; + +if ($ttl > 0 && is_file($cache) && (time() - (int) @filemtime($cache)) < $ttl) { + $raw = @file_get_contents($cache); + if (is_string($raw) && $raw !== '') { + $cached = json_decode($raw, true); + if (is_array($cached) && isset($cached['items']) && is_array($cached['items'])) { + $cached['items'] = array_slice($cached['items'], 0, $limit); + $cached['cached'] = true; + sched_respond(200, $cached); + } + } +} + +// --- Pobranie danych -------------------------------------------------------- +$items = []; +$failed = 0; + +$webinars = sched_get($base . '/webinars', $key, (int) $config['timeout']); +if ($webinars['status'] === 200) { + $decoded = json_decode($webinars['body'], true); + if (is_array($decoded)) { + foreach ($decoded as $row) { + if (!is_array($row)) { + continue; + } + $title = sched_text($row['title'] ?? '', 160); + $start = sched_text($row['startsAt'] ?? '', 40); + if ($title === '' || $start === '') { + continue; + } + $capacity = isset($row['capacity']) && is_numeric($row['capacity']) ? (int) $row['capacity'] : null; + $registered = isset($row['registeredCount']) && is_numeric($row['registeredCount']) ? (int) $row['registeredCount'] : 0; + + $items[] = [ + 'kind' => 'webinar', + 'title' => $title, + 'startsAt' => $start, + 'endsAt' => sched_text($row['endsAt'] ?? '', 40), + 'timezone' => sched_text($row['timezone'] ?? '', 64), + 'url' => sched_url($row['registrationUrl'] ?? null, $linkHosts), + 'open' => !isset($row['registrationOpen']) || (bool) $row['registrationOpen'], + 'soldOut' => $capacity !== null && $registered >= $capacity, + 'seatsLeft' => $capacity !== null ? max(0, $capacity - $registered) : null, + ]; + } + } +} else { + $failed++; +} + +$courses = sched_get($base . '/courses', $key, (int) $config['timeout']); +if ($courses['status'] === 200) { + $decoded = json_decode($courses['body'], true); + if (is_array($decoded)) { + foreach ($decoded as $row) { + if (!is_array($row)) { + continue; + } + $title = sched_text($row['title'] ?? '', 160); + if ($title === '') { + continue; + } + $level = strtoupper(sched_text($row['level'] ?? '', 20)); + $items[] = [ + 'kind' => 'course', + 'title' => $title, + 'subtitle' => sched_text($row['subtitle'] ?? '', 160), + 'category' => sched_text($row['category'] ?? '', 80), + 'level' => in_array($level, ['BASIC', 'INTERMEDIATE', 'ADVANCED'], true) ? $level : null, + 'minutes' => isset($row['durationMinutes']) && is_numeric($row['durationMinutes']) ? (int) $row['durationMinutes'] : null, + 'free' => !empty($row['free']), + 'url' => sched_url($row['courseUrl'] ?? null, $linkHosts), + ]; + } + } +} else { + $failed++; +} + +// Webinary z terminem idą pierwsze i rosnąco, kursy na żądanie na końcu. +usort($items, static function (array $a, array $b): int { + $aDated = $a['kind'] === 'webinar'; + $bDated = $b['kind'] === 'webinar'; + if ($aDated !== $bDated) { + return $aDated ? -1 : 1; + } + if ($aDated) { + return strcmp((string) $a['startsAt'], (string) $b['startsAt']); + } + + return 0; +}); + +$source = 'api'; +if ($items === []) { + $source = $failed > 0 ? 'error' : 'empty'; +} + +$payload = [ + 'ok' => true, + 'source' => $source, + 'fetchedAt' => gmdate('c'), + 'items' => $items, +]; + +if ($ttl > 0) { + @file_put_contents($cache, json_encode($payload, JSON_UNESCAPED_UNICODE), LOCK_EX); + // Pusty wynik i błąd trzymamy krócej, żeby świeże pozycje pojawiły się szybko. + if ($source !== 'api') { + $short = min($ttl, 60); + @touch($cache, time() - ($ttl - $short)); + } +} + +$payload['items'] = array_slice($payload['items'], 0, $limit); +sched_respond(200, $payload); diff --git a/assets/img/akademia.svg b/assets/img/akademia.svg new file mode 100644 index 0000000..ce81fb8 --- /dev/null +++ b/assets/img/akademia.svg @@ -0,0 +1,130 @@ + +Akademia Wodociągowca - obieg kompetencji: praktyka wdrożeniowa i doradcza zasila platformę kursową, a wyszkolone zespoły wracają z pytaniami z rynku + + + + + + + + + + + + + + + + + + + + WDROŻENIA U KLIENTÓW + PROJEKTY DORADCZE + PYTANIA CZYTELNIKÓW + +CO ROBIMY + + + + + + + + + + + + +AKADEMIA WODOCIĄGOWCA + + + + + + LIVE + + + + + + + + +WEBINARIA · KURSY · CERTYFIKATY + + + +WIEDZA + + + +ZESPOŁY W FIRMACH + + + + + + + + + + + + + + CERTYFIKAT + + + + + +PYTANIA WRACAJĄ DO PROGRAMU + + + + + + + + + diff --git a/assets/img/en/akademia.svg b/assets/img/en/akademia.svg new file mode 100644 index 0000000..03e382e --- /dev/null +++ b/assets/img/en/akademia.svg @@ -0,0 +1,130 @@ + +Akademia Wodociagowca - the competence loop: our delivery and advisory practice feeds the course platform, and trained teams come back with questions from the market + + + + + + + + + + + + + + + + + + + + CLIENT ROLLOUTS + ADVISORY PROJECTS + READER QUESTIONS + +WHAT WE DO + + + + + + + + + + + + +AKADEMIA WODOCIAGOWCA + + + + + + LIVE + + + + + + + + +WEBINARS · COURSES · CERTIFICATES + + + +KNOW-HOW + + + +TEAMS AT UTILITIES + + + + + + + + + + + + + + CERTIFICATE + + + + + +QUESTIONS FEED THE PROGRAMME + + + + + + + + + diff --git a/assets/reveal.js b/assets/reveal.js new file mode 100644 index 0000000..9d05c47 --- /dev/null +++ b/assets/reveal.js @@ -0,0 +1,55 @@ +/* ============================================================================ + SoftSPM - wejście bloków i liczniki + Delikatne pojawianie się sekcji przy przewijaniu oraz naliczanie liczb + w pasku Akademii. Wszystko wyłącza się przy prefers-reduced-motion, + a bez JavaScriptu treść i tak jest widoczna - styl startowy nakładamy + dopiero po dodaniu klasy js-reveal. + ========================================================================= */ +(function () { + 'use strict'; + + var supported = 'IntersectionObserver' in window && 'classList' in document.documentElement; + var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; + + if (!supported || reduced) { + return; + } + + document.documentElement.classList.add('js-reveal'); + + function countUp(node) { + var target = parseInt(node.getAttribute('data-count'), 10); + if (isNaN(target)) { return; } + var suffix = node.getAttribute('data-suffix') || ''; + var duration = 900; + var started = null; + + function step(now) { + if (started === null) { started = now; } + var progress = Math.min(1, (now - started) / duration); + // wyhamowanie na końcu - liczba dobija do wartości, nie przeskakuje + var eased = 1 - Math.pow(1 - progress, 3); + node.textContent = Math.round(target * eased) + suffix; + if (progress < 1) { requestAnimationFrame(step); } + } + + node.textContent = '0' + suffix; + requestAnimationFrame(step); + } + + var observer = new IntersectionObserver(function (entries) { + for (var i = 0; i < entries.length; i++) { + var entry = entries[i]; + if (!entry.isIntersecting) { continue; } + var node = entry.target; + node.classList.add('is-in'); + observer.unobserve(node); + + var counters = node.querySelectorAll('[data-count]'); + for (var c = 0; c < counters.length; c++) { countUp(counters[c]); } + } + }, { rootMargin: '0px 0px -12% 0px', threshold: 0.12 }); + + var blocks = document.querySelectorAll('[data-reveal]'); + for (var i = 0; i < blocks.length; i++) { observer.observe(blocks[i]); } +})(); diff --git a/assets/schedule.js b/assets/schedule.js new file mode 100644 index 0000000..9420a05 --- /dev/null +++ b/assets/schedule.js @@ -0,0 +1,208 @@ +/* ============================================================================ + SoftSPM - harmonogram z Akademii Wodociągowca + Podmienia wiersze tabeli terminów danymi z api/schedule.php. + Gdy API nie odpowiada albo nie ma pozycji, statyczne wiersze z HTML-a + zostają nietknięte - strona nigdy nie pokazuje pustej tabeli. + ========================================================================= */ +(function () { + 'use strict'; + + var DICT = { + pl: { + dateHead: 'Data', + webinar: 'Webinar', + training: 'Szkolenie', + online: 'Online', + elearning: 'Online · e-learning', + onDemand: 'Na żądanie', + signUp: 'Zapisz się →', + viewCourse: 'Zobacz kurs →', + soldOut: 'Brak miejsc', + soon: 'Zapisy wkrótce', + note: 'Terminy pobierane na bieżąco z platformy kursowej Akademia Wodociągowca. Zapisy prowadzi Akademia.', + plateTitle: 'Terminy i kursy otwarte', + heading: 'Najbliższe szkolenia i webinaria', + intro: 'Poniżej pozycje otwarte w tej chwili na naszej platformie kursowej. Zapisy prowadzi Akademia Wodociągowca.', + locale: 'pl-PL', + levels: { BASIC: 'podstawowy', INTERMEDIATE: 'średni', ADVANCED: 'zaawansowany' } + }, + en: { + dateHead: 'Date', + webinar: 'Webinar', + training: 'Training', + online: 'Online', + elearning: 'Online · e-learning', + onDemand: 'On demand', + signUp: 'Sign up →', + viewCourse: 'View course →', + soldOut: 'Fully booked', + soon: 'Registration soon', + note: 'Dates are pulled live from our Akademia Wodociagowca course platform, which also handles registration.', + plateTitle: 'Open dates and courses', + heading: 'Upcoming training and webinars', + intro: 'Everything currently open on our course platform. Registration is handled by Akademia Wodociagowca.', + locale: 'en-GB', + levels: { BASIC: 'basic', INTERMEDIATE: 'intermediate', ADVANCED: 'advanced' } + } + }; + + var lang = (document.documentElement.getAttribute('lang') || 'pl').slice(0, 2).toLowerCase(); + var t = DICT[lang] || DICT.pl; + + function el(tag, className, text) { + var node = document.createElement(tag); + if (className) { node.className = className; } + if (text !== undefined && text !== null) { node.textContent = text; } + return node; + } + + function formatDate(iso, timezone) { + var date = new Date(iso); + if (isNaN(date.getTime())) { return null; } + var zone = timezone || 'Europe/Warsaw'; + var opts = { day: '2-digit', month: '2-digit', year: 'numeric', timeZone: zone }; + var timeOpts = { hour: '2-digit', minute: '2-digit', timeZone: zone }; + try { + return { + day: new Intl.DateTimeFormat(t.locale, opts).format(date), + hour: new Intl.DateTimeFormat(t.locale, timeOpts).format(date) + }; + } catch (e) { + // Nieznana strefa albo stary silnik - pokazujemy datę bez strefy. + return { day: date.toLocaleDateString(t.locale), hour: date.toLocaleTimeString(t.locale) }; + } + } + + function actionCell(item) { + var cell = el('td', 's-act'); + if (item.kind === 'webinar' && item.soldOut) { + cell.appendChild(el('span', 'text-muted', t.soldOut)); + return cell; + } + if (item.kind === 'webinar' && item.open === false) { + cell.appendChild(el('span', 'text-muted', t.soon)); + return cell; + } + if (!item.url) { + cell.appendChild(el('span', 'text-muted', item.kind === 'webinar' ? t.soon : '')); + return cell; + } + var link = el('a', 'sign', item.kind === 'webinar' ? t.signUp : t.viewCourse); + link.href = item.url; + link.target = '_blank'; + link.rel = 'noopener'; + cell.appendChild(link); + return cell; + } + + function buildRow(item) { + var row = document.createElement('tr'); + + var date = el('td', 's-date'); + if (item.kind === 'webinar') { + var parts = formatDate(item.startsAt, item.timezone); + if (parts) { + date.appendChild(document.createTextNode(parts.day)); + date.appendChild(el('span', 's-hour', parts.hour)); + } else { + date.appendChild(el('span', 'tag tag-soon', t.onDemand)); + } + } else { + date.appendChild(el('span', 'tag tag-soon', t.onDemand)); + } + row.appendChild(date); + + // textContent, nie innerHTML - tytuły przychodzą z innego systemu. + row.appendChild(el('td', 's-topic', item.title)); + + var form = el('td', 's-form'); + form.appendChild(el( + 'span', + item.kind === 'webinar' ? 'tag tag-outline' : 'tag tag-accent', + item.kind === 'webinar' ? t.webinar : t.training + )); + row.appendChild(form); + + var place = item.kind === 'webinar' ? t.online : t.elearning; + if (item.kind === 'course' && item.level && t.levels[item.level]) { + place = t.elearning + ' · ' + t.levels[item.level]; + } + row.appendChild(el('td', 's-place', place)); + + row.appendChild(actionCell(item)); + return row; + } + + function render(table, items) { + var body = table.querySelector('tbody'); + if (!body) { return; } + + var fresh = document.createDocumentFragment(); + for (var i = 0; i < items.length; i++) { + fresh.appendChild(buildRow(items[i])); + } + body.innerHTML = ''; + body.appendChild(fresh); + + // Mamy realne daty, więc nagłówek kolumny wraca z "Termin" na "Data". + var head = table.querySelector('[data-schedule-datehead]'); + if (head) { head.textContent = t.dateHead; } + + // Przy realnych terminach nagłówki mówiące "ustalamy terminy" przestają + // być prawdziwe, więc lecą razem z wierszami. + var section = table.closest ? table.closest('section') : null; + if (section) { + var heading = section.querySelector('[data-schedule-heading]'); + if (heading) { heading.textContent = t.heading; } + var intro = section.querySelector('[data-schedule-intro]'); + if (intro) { intro.textContent = t.intro; } + } + + var plate = table.closest ? table.closest('.plate') : null; + var plateTitle = plate ? plate.querySelector('[data-schedule-title]') : null; + if (plateTitle) { plateTitle.textContent = t.plateTitle; } + + var note = plate ? plate.querySelector('[data-schedule-note]') : null; + if (note) { + note.textContent = t.note + ' '; + var link = el('a', null, 'akademiawodociagowca.pl'); + link.href = 'https://akademiawodociagowca.pl/'; + link.target = '_blank'; + link.rel = 'noopener'; + note.appendChild(link); + } + table.setAttribute('data-schedule-state', 'live'); + } + + function boot() { + var tables = document.querySelectorAll('table[data-schedule]'); + if (!tables.length) { return; } + + var limit = 0; + for (var i = 0; i < tables.length; i++) { + limit = Math.max(limit, parseInt(tables[i].getAttribute('data-schedule-limit'), 10) || 12); + } + + fetch('api/schedule.php?limit=' + limit, { + credentials: 'same-origin', + headers: { 'Accept': 'application/json' } + }) + .then(function (response) { return response.json(); }) + .then(function (data) { + if (!data || data.source !== 'api' || !data.items || !data.items.length) { return; } + for (var i = 0; i < tables.length; i++) { + var own = parseInt(tables[i].getAttribute('data-schedule-limit'), 10) || data.items.length; + render(tables[i], data.items.slice(0, own)); + } + }) + .catch(function () { + // Cisza jest właściwą reakcją: w tabeli zostają statyczne wiersze. + }); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', boot); + } else { + boot(); + } +})(); diff --git a/assets/styles.css b/assets/styles.css index cb89b3d..518d313 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -272,6 +272,11 @@ textarea.input { min-height: 110px; resize: vertical; } .s-prop { font-weight: 500; } .s-rem { font-size: 14px; color: color-mix(in srgb, var(--color-text) 68%, transparent); } .s-date { white-space: nowrap; font-weight: 500; width: 1%; } +/* godzina webinaru pod datą - pobierana z Akademii Wodociągowca */ +.s-hour { + display: block; font-weight: 400; font-size: 12.5px; + color: color-mix(in srgb, var(--color-text) 55%, transparent); +} .s-topic { font-family: var(--font-heading); font-weight: 600; font-size: 18px; } .s-act { text-align: right; white-space: nowrap; } .sign { @@ -475,6 +480,35 @@ textarea.input { min-height: 110px; resize: vertical; } color: var(--color-accent-700); } +/* -- pasek liczb (Akademia) --------------------------------------------- */ +.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: calc(1.5 * var(--leading)); } +@media (max-width: 820px) { .stats { grid-template-columns: repeat(2, 1fr); } } +.stat { padding-top: var(--space-4); border-top: 2px solid var(--color-accent); } +.stat-num { + display: block; + font-family: var(--font-heading); font-weight: 700; + font-size: clamp(38px, 4.4vw, 52px); line-height: 1; + color: var(--color-accent-700); + font-feature-settings: "tnum" 1; +} +.stat-label { + display: block; margin-top: var(--space-3); + font-size: 13.5px; line-height: var(--half); + color: color-mix(in srgb, var(--color-text) 62%, transparent); +} + +/* -- wejście bloków przy przewijaniu ----------------------------------- + Styl startowy nakładamy dopiero, gdy skrypt zgłosi gotowość (klasa + js-reveal na ). Bez JavaScriptu treść jest widoczna od razu. */ +.js-reveal [data-reveal] { + opacity: 0; transform: translateY(16px); + transition: opacity .55s ease-out, transform .55s ease-out; +} +.js-reveal [data-reveal].is-in { opacity: 1; transform: none; } +@media (prefers-reduced-motion: reduce) { + .js-reveal [data-reveal] { opacity: 1; transform: none; transition: none; } +} + /* -- newsletter / CTA --------------------------------------------------- */ .cta-band { position: relative; padding: calc(2 * var(--leading)) var(--space-8); @@ -608,6 +642,8 @@ textarea.input { min-height: 110px; resize: vertical; } } .table.sched td { border: 0; padding: 0; } .table.sched .s-date { order: 1; font-size: 14px; color: var(--color-accent-700); } + /* w karcie mobilnej data i godzina mieszczą się w jednej linii */ + .table.sched .s-hour { display: inline; font-size: 13px; margin-left: var(--space-2); } .table.sched .s-form { order: 2; text-align: right; } .table.sched .s-topic { grid-column: 1 / -1; order: 3; } .table.sched .s-place { order: 4; font-size: 13.5px; color: color-mix(in srgb, var(--color-text) 60%, transparent); } diff --git a/index-en.html b/index-en.html index f1d35cf..043b1c7 100644 --- a/index-en.html +++ b/index-en.html @@ -4,7 +4,7 @@ SoftSPM - training, consulting and custom software for the water sector - + @@ -16,7 +16,7 @@ Offer Topics Dates - Platform + Academy Software Ecosystem PL @@ -30,8 +30,8 @@
-

Water sector know-how since 2014.

-

Training, webinars, consulting and turnkey software for municipal water and wastewater utilities. We teach what we do ourselves - we have been building software for over 25 years, and doing it for the water sector since 2014. We run the Wodociagowiec.pl portal and Gielda Komunalna, a free marketplace where the sector buys, sells and swaps equipment.

+

Water sector know-how that stays in-house.

+

Turnkey software, consulting, training and webinars for municipal water and wastewater utilities. Over 25 years in IT, in the water sector since 2014. We do not leave a slide deck to gather dust: what stays behind is a working system, a list of decisions to take, or competence that keeps working once we are gone.

See the programme Book a consultation @@ -71,7 +71,7 @@ 03Specialists trained480+Classroom and online 04Webinars per year24On our own course platform 05Advisory projects60+Tariffs, organisation, procurement, digitisation - 06Ecosystem reach2 servicesWodociagowiec.pl trade portal and Gielda Komunalna marketplace + 06Ecosystem reach3 servicesWodociagowiec.pl, Gielda Komunalna and Akademia Wodociagowca

Sample figures - to be confirmed before publication.

@@ -270,14 +270,69 @@
- +
- 05 · Course platform + 05 · Akademia Wodociagowca
+
+

Competence that stays with the utility

+

A training course ends on Friday; the competence is supposed to last for years. That is why we run our own course platform: live webinars, courses to work through at your own pace, working materials and certificates - all under a single company account. It is how we keep knowledge circulating around the sector instead of settling in one person's notebook.

+
+ +
+ + Diagram: client rollouts, advisory projects and reader questions feed Akademia Wodociagowca, and trained teams send questions back into the programme +
Diagram 01 - The competence loop: practice feeds the Academy, trained teams come back with questions
+
+ +
+
+ 24 + webinars a year, two every month +
+
+ 8 + subject tracks in the catalogue +
+
+ 12 + people at most in a group +
+
+ 480+ + specialists trained across the sector +
+
+

Webinar count and people trained - sample figures, to be confirmed before publication.

+ +
+
+ + 05.1 + +

Taught first-hand

+

Our trainers walk into the room straight from rollouts and advisory projects. The example on the slide has a date and consequences; it is not an illustration copied from a textbook.

+
+
+ + 05.2 + +

Knowledge stays in-house

+

Recordings, document templates and checklists stay on the company account. When someone leaves, the competence stays - the next person starts from the material, not from scratch.

+
+
+ + 05.3 + +

Questions feed the programme

+

A topic that comes up for the third time in a webinar chat or at the Wodociagowiec desk goes into the catalogue. The market writes the programme; we just put it in order.

+
+
+
-
-

Webinars and e-learning in one place

-

Our own platform for running courses: live webinars, on-demand courses, tests and certificates. The material stays with you - come back to the recordings and documents whenever you need them.

+
+

What the utility gets

+

One company account, many employees, and material available when it is actually needed - not only on the day of the course.

  • Live webinars with chat and a Q&A session
  • Recordings available indefinitely with your access
  • @@ -286,9 +341,12 @@
  • Learner dashboard with progress and your team's training history
  • Company account - one subscription for many employees
- Go to the platform +
-
+
Learner dashboard: course progress, webinar agenda and a live indicator
Fig. 02 - Learner dashboard: progress and upcoming webinars
@@ -360,13 +418,13 @@

A trade portal, a marketplace and a course platform - one loop

-

We do not start from a blank page. We run two services the Polish water sector uses daily, and they tell us what to teach. Readers' questions become webinar topics, listings on the marketplace show what the sector actually needs, and course participants meet the manufacturers and contractors they used to hunt for through word of mouth.

+

We do not start from a blank page. We run three services the Polish water sector uses daily, and they tell us what to teach. Readers' questions become webinar topics, listings on the marketplace show what the sector actually needs, and course participants meet the manufacturers and contractors they used to hunt for through word of mouth.

Diagram: the Wodociagowiec.pl portal and the Gielda Komunalna marketplace exchange topics, practitioners and listings with the course platform at the centre -
Diagram 01 - The knowledge loop: the portal and the marketplace feed the platform, graduates return to the market
+
Diagram 02 - The knowledge loop: the portal and the marketplace feed the platform, graduates return to the market
@@ -508,6 +566,7 @@
+ diff --git a/index.html b/index.html index 4c8cbd6..8d830e6 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ SoftSPM - szkolenia, konsulting i aplikacje dla branży wod-kan - + @@ -16,7 +16,7 @@ Oferta Tematy Terminy - Platforma + Akademia Aplikacje Ekosystem Szkolenia @@ -32,8 +32,8 @@
-

Wiedza wod-kan. W praktyce od 2014.

-

Szkolenia, webinaria, konsulting i aplikacje pod klucz dla przedsiębiorstw wodociągowych i komunalnych. Uczymy tego, co sami robimy - bo od ponad 25 lat piszemy oprogramowanie, a od 2014 roku robimy to dla branży wod-kan. Prowadzimy portal Wodociągowiec.pl oraz Giełdę Komunalną - bezpłatny marketplace, na którym branża kupuje, sprzedaje i wymienia sprzęt.

+

Wiedza wod-kan, która zostaje w firmie.

+

Aplikacje pod klucz, konsulting, szkolenia i webinaria dla przedsiębiorstw wodociągowych i komunalnych. Ponad 25 lat w IT, od 2014 roku w wod-kan. Nie zostawiamy prezentacji do odłożenia na półkę: zostaje działający system, lista decyzji do podjęcia albo kompetencja, która pracuje dalej, gdy nas już nie ma.

Zobacz program szkoleń Umów konsultację @@ -73,7 +73,7 @@ 03Przeszkoleni specjaliści480+Szkolenia stacjonarne i online 04Webinariów rocznie24Na własnej platformie kursowej 05Projekty doradcze60+Organizacja, taryfy, cyfryzacja, zamówienia - 06Zasięg ekosystemu2 serwisyPortal Wodociągowiec.pl i marketplace Giełda Komunalna + 06Zasięg ekosystemu3 serwisyWodociągowiec.pl, Giełda Komunalna i Akademia Wodociągowca

Wartości przykładowe - do potwierdzenia przed publikacją.

@@ -251,20 +251,20 @@ 04 · Harmonogram
-

Terminy najbliższej edycji ustalamy

-

Poniżej tematy, które wchodzą do programu jako pierwsze. Daty podajemy, gdy domkniemy salę i skład prowadzących - zwykle z sześciotygodniowym wyprzedzeniem.

+

Terminy najbliższej edycji ustalamy

+

Poniżej tematy, które wchodzą do programu jako pierwsze. Daty podajemy, gdy domkniemy salę i skład prowadzących - zwykle z sześciotygodniowym wyprzedzeniem.

- Tematy w przygotowaniu + Tematy w przygotowaniu SPM-2026 Rev A Arkusz 02 z 06
- +
- + @@ -325,18 +325,73 @@
TerminTematFormaMiejsceZapisy
TerminTematFormaMiejsceZapisy
-

Ustalamy terminy najbliższej edycji. Zostaw kontakt przy wybranym temacie na stronie Szkolenia i webinaria - odezwiemy się, gdy otworzymy zapisy.

+

Ustalamy terminy najbliższej edycji. Zostaw kontakt przy wybranym temacie na stronie Szkolenia i webinaria - odezwiemy się, gdy otworzymy zapisy.

- +
- 05 · Platforma kursowa + 05 · Akademia Wodociągowca
+
+

Kompetencja, która zostaje w przedsiębiorstwie

+

Szkolenie kończy się w piątek, kompetencja ma zostać na lata. Dlatego prowadzimy własną platformę kursową: webinaria na żywo, kursy do przerobienia we własnym tempie, materiały robocze i certyfikaty - wszystko pod jednym firmowym kontem. To nasz sposób na to, żeby wiedza krążyła po branży, zamiast osiadać w notatkach pojedynczych osób.

+
+ +
+ + Schemat: wdrożenia, projekty doradcze i pytania czytelników zasilają Akademię Wodociągowca, przeszkolone zespoły wracają z pytaniami do programu +
Schemat 01 - Obieg kompetencji: praktyka zasila Akademię, a przeszkolone zespoły wracają z pytaniami
+
+ +
+
+ 24 + webinaria rocznie, dwa w miesiącu +
+
+ 8 + ścieżek tematycznych w katalogu +
+
+ 12 + osób maksymalnie w grupie +
+
+ 480+ + przeszkolonych specjalistów branży +
+
+

Liczba webinariów i przeszkolonych - wartości przykładowe, do potwierdzenia przed publikacją.

+ +
+
+ + 05.1 + +

Uczymy z pierwszej ręki

+

Prowadzący wracają na salę prosto z wdrożeń i projektów doradczych. Przykład na zajęciach ma datę i konsekwencje, nie jest ilustracją przepisaną z podręcznika.

+
+
+ + 05.2 + +

Wiedza zostaje w firmie

+

Nagrania, wzory pism i listy kontrolne zostają na koncie przedsiębiorstwa. Gdy odchodzi pracownik, kompetencja zostaje - następna osoba zaczyna od materiału, nie od zera.

+
+
+ + 05.3 + +

Pytania wracają do programu

+

Temat, który trzeci raz wraca w czacie webinaru albo w redakcji Wodociągowca, wchodzi do katalogu. Program pisze rynek, my go tylko porządkujemy.

+
+
+
-
-

Webinary i e-learning w jednym miejscu

-

Nasza własna platforma do prowadzenia kursów: webinaria na żywo, kursy on-demand, testy i certyfikaty. Materiał zostaje z Tobą - wracasz do nagrań i dokumentów, kiedy ich potrzebujesz.

+
+

Co dostaje przedsiębiorstwo

+

Jedno konto firmowe, wielu pracowników, materiał dostępny wtedy, kiedy jest potrzebny - a nie tylko w dniu szkolenia.

  • Webinaria na żywo z czatem i sesją pytań do prowadzącego
  • Nagrania dostępne bezterminowo w ramach wykupionego dostępu
  • @@ -345,9 +400,12 @@
  • Panel kursanta z postępem nauki i historią szkoleń całego zespołu
  • Konto firmowe - jeden dostęp dla wielu pracowników
- Przejdź do platformy +
-
+
Panel kursanta: postęp kursów, agenda webinariów i znacznik transmisji na żywo
Rys. 02 - Panel kursanta: postęp nauki i nadchodzące webinaria
@@ -419,13 +477,13 @@

Portal, marketplace i platforma kursowa - jeden obieg

-

Nie zaczynamy od pustej kartki. Prowadzimy dwa serwisy, z których branża wod-kan korzysta na co dzień - i to one podpowiadają nam, czego uczyć. Pytania czytelników portalu stają się tematami webinariów, ogłoszenia na giełdzie pokazują, czego rynkowi realnie brakuje, a uczestnicy szkoleń poznają tam producentów i wykonawców, których wcześniej szukali po znajomych.

+

Nie zaczynamy od pustej kartki. Prowadzimy trzy serwisy, z których branża wod-kan korzysta na co dzień - i to one podpowiadają nam, czego uczyć. Pytania czytelników portalu stają się tematami webinariów, ogłoszenia na giełdzie pokazują, czego rynkowi realnie brakuje, a uczestnicy szkoleń poznają tam producentów i wykonawców, których wcześniej szukali po znajomych.

Schemat: portal Wodociągowiec.pl i marketplace Giełda Komunalna wymieniają tematy, praktyków i ogłoszenia z platformą kursową w centrum -
Schemat 01 - Obieg wiedzy: portal i marketplace zasilają platformę kursową, absolwenci wracają na rynek
+
Schemat 02 - Obieg wiedzy: portal i marketplace zasilają platformę kursową, absolwenci wracają na rynek
@@ -568,6 +626,8 @@
+ + diff --git a/konsulting.html b/konsulting.html index 6202ad5..d72978e 100644 --- a/konsulting.html +++ b/konsulting.html @@ -353,6 +353,7 @@
+ diff --git a/szkolenia.html b/szkolenia.html index 673b397..7f4007b 100644 --- a/szkolenia.html +++ b/szkolenia.html @@ -242,20 +242,20 @@ 04 · Harmonogram
-

Terminy najbliższej edycji ustalamy

-

Poniżej tematy, które wchodzą do programu jako pierwsze. Daty podamy, gdy domkniemy salę i skład prowadzących - zwykle z sześciotygodniowym wyprzedzeniem. Zostaw kontakt przy interesującym Cię temacie, a odezwiemy się z terminem, zanim ogłosimy go publicznie.

+

Terminy najbliższej edycji ustalamy

+

Poniżej tematy, które wchodzą do programu jako pierwsze. Daty podamy, gdy domkniemy salę i skład prowadzących - zwykle z sześciotygodniowym wyprzedzeniem. Zostaw kontakt przy interesującym Cię temacie, a odezwiemy się z terminem, zanim ogłosimy go publicznie.

- Tematy w przygotowaniu + Tematy w przygotowaniu SPM-2026 Rev A Arkusz 05 z 06
- +
- + @@ -271,7 +271,7 @@
TerminTematFormaMiejsceZapisy
TerminTematFormaMiejsceZapisy
WkrótceWniosek taryfowy - przygotowanie i obronaSzkolenieBełchatówPowiadom mnie →
WkrótceNIS2 i infrastruktura krytyczna - obowiązki przedsiębiorstwaWebinarOnlinePowiadom mnie →
-

Ustalamy terminy najbliższej edycji. Zostaw kontakt przy wybranym temacie - poinformujemy Cię, gdy tylko otworzymy zapisy.

+

Ustalamy terminy najbliższej edycji. Zostaw kontakt przy wybranym temacie - poinformujemy Cię, gdy tylko otworzymy zapisy.

@@ -282,10 +282,10 @@

Pakiet roczny webinariów

-

Dwadzieścia cztery spotkania w roku - dwa w miesiącu, po dwie godziny. Jedno o przepisach i taryfach, jedno o organizacji i rynku. Jedna opłata dla całego przedsiębiorstwa, bez limitu uczestników.

+

Dwadzieścia cztery spotkania w roku - dwa w miesiącu, po dwie godziny. Jedno o przepisach i taryfach, jedno o organizacji i rynku. Jedna opłata dla całego przedsiębiorstwa, bez limitu uczestników. Wszystko prowadzimy na własnej platformie Akademia Wodociągowca, więc nagranie i materiały zostają na koncie firmy także wtedy, gdy ktoś nie mógł być na żywo.

  • 24 webinaria rocznie na żywo, z sesją pytań do prowadzącego
  • -
  • Nagrania i materiały dostępne bezterminowo na platformie kursowej
  • +
  • Nagrania i materiały bezterminowo na koncie firmy w Akademii Wodociągowca
  • Bez limitu uczestników z jednego przedsiębiorstwa
  • Archiwum wcześniejszych spotkań w cenie pakietu
  • Możliwość zgłoszenia tematu do programu na kolejny kwartał
  • @@ -422,6 +422,8 @@
+ +