API
This commit is contained in:
@@ -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.
|
- **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.
|
- 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.
|
- **[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.
|
- **[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.
|
- 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 |
|
| 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 |
|
| [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 |
|
| [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 |
|
| [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.
|
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 `<html>`, 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 `<img>`, 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.
|
Rule: **one accent color only.** No extra decorative colors; hover/pressed states come from the tonal 100–900 ramp.
|
||||||
|
|
||||||
| Role | Value |
|
| 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.
|
- 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.
|
- 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.
|
- 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.
|
- Team member names — the consulting page lists teams, not people, pending real bios.
|
||||||
- Nothing else. The forms are live — see **Forms** below.
|
- 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.
|
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
|
## 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.
|
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.
|
||||||
|
|||||||
+63
-2
@@ -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
|
### Wpisy opcjonalne
|
||||||
|
|
||||||
```php
|
```php
|
||||||
@@ -145,7 +169,44 @@ szablon endpointu nie zamienia `\n` na `<br>`. `send_email_api.php` jest wasz,
|
|||||||
więc najczystszą poprawką jest tam jedno `nl2br()` na sanityzowanej treści -
|
więc najczystszą poprawką jest tam jedno `nl2br()` na sanityzowanej treści -
|
||||||
nic po stronie tej strony nie wymaga wtedy zmian.
|
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
|
Suwak sam z siebie niczego nie gwarantuje - jest elementem interfejsu, a nie
|
||||||
kryptografią. Blokady, które faktycznie odsiewają automaty, działają na
|
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,
|
Turnstile albo hCaptcha - obie działają na tej samej zasadzie co obecny token,
|
||||||
więc podmiana dotknie tylko `send.php` i `assets/forms.js`.
|
więc podmiana dotknie tylko `send.php` i `assets/forms.js`.
|
||||||
|
|
||||||
## 6. Test po wdrożeniu
|
## 7. Test po wdrożeniu
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Token - powinien zwrócić {"ok":true,"token":"t.szkolenia...."}
|
# 1. Token - powinien zwrócić {"ok":true,"token":"t.szkolenia...."}
|
||||||
|
|||||||
+25
-2
@@ -26,6 +26,14 @@ $config = [
|
|||||||
// Dowolny losowy ciąg, min. 32 znaki. Zmiana unieważnia otwarte formularze.
|
// Dowolny losowy ciąg, min. 32 znaki. Zmiana unieważnia otwarte formularze.
|
||||||
'token_secret' => '',
|
'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ń --------------------------------------------------
|
// --- Adresat zgłoszeń --------------------------------------------------
|
||||||
// Ustawiany wyłącznie po stronie serwera. Nigdy nie pochodzi z formularza -
|
// Ustawiany wyłącznie po stronie serwera. Nigdy nie pochodzi z formularza -
|
||||||
// inaczej endpoint stałby się otwartym przekaźnikiem spamu.
|
// inaczej endpoint stałby się otwartym przekaźnikiem spamu.
|
||||||
@@ -68,8 +76,10 @@ $config = [
|
|||||||
|
|
||||||
// --- 1. Zmienne środowiskowe ------------------------------------------------
|
// --- 1. Zmienne środowiskowe ------------------------------------------------
|
||||||
$fromEnv = [
|
$fromEnv = [
|
||||||
'api_key' => getenv('SOFTSPM_MAIL_API_KEY'),
|
'api_key' => getenv('SOFTSPM_MAIL_API_KEY'),
|
||||||
'token_secret' => getenv('SOFTSPM_FORM_SECRET'),
|
'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) {
|
foreach ($fromEnv as $key => $value) {
|
||||||
if (is_string($value) && $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.
|
// Adresat zgłoszeń - jeden dla wszystkich formularzy.
|
||||||
$value = $pick('SOFTSPM_FORM_RECIPIENT', 'softspm_form_recipient');
|
$value = $pick('SOFTSPM_FORM_RECIPIENT', 'softspm_form_recipient');
|
||||||
if (is_string($value)) {
|
if (is_string($value)) {
|
||||||
|
|||||||
@@ -0,0 +1,266 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SoftSPM - harmonogram szkoleń i webinariów.
|
||||||
|
*
|
||||||
|
* Pośrednik do publicznego API Akademii Wodociągowca
|
||||||
|
* (dokumentacja: akademiawodociagowca/docs/api_public_v1.html).
|
||||||
|
* Klucz X-API-Key zostaje na serwerze - dokumentacja Akademii wprost tego
|
||||||
|
* wymaga (punkt 9: "Trzymaj klucz po stronie serwera").
|
||||||
|
*
|
||||||
|
* GET api/schedule.php[?limit=8]
|
||||||
|
* -> {"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<IfModule !mod_authz_core.c>\nDeny from all\n</IfModule>\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);
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 344" role="img" aria-labelledby="t-akad">
|
||||||
|
<title id="t-akad">Akademia Wodociągowca - obieg kompetencji: praktyka wdrożeniowa i doradcza zasila platformę kursową, a wyszkolone zespoły wracają z pytaniami z rynku</title>
|
||||||
|
<defs>
|
||||||
|
<pattern id="akad-grid" width="20" height="20" patternUnits="userSpaceOnUse">
|
||||||
|
<path d="M20 0H0v20" fill="none" stroke="#5980a6" stroke-opacity=".13" stroke-width="1"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="akad-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||||
|
<path d="M0 0 10 5 0 10z" fill="#5980a6"/>
|
||||||
|
</marker>
|
||||||
|
<style><![CDATA[
|
||||||
|
/* Rysunek powstaje jak na ploterze: linie same się kreślą.
|
||||||
|
pathLength="1" sprawia, że długość ścieżki nie ma znaczenia. */
|
||||||
|
.draw { stroke-dasharray: 1; stroke-dashoffset: 1; animation: akad-draw 1.1s ease-out forwards; }
|
||||||
|
.d1 { animation-delay: .05s } .d2 { animation-delay: .3s }
|
||||||
|
.d3 { animation-delay: .55s } .d4 { animation-delay: .8s }
|
||||||
|
@keyframes akad-draw { to { stroke-dashoffset: 0 } }
|
||||||
|
|
||||||
|
.fade { opacity: 0; animation: akad-fade .5s ease-out forwards; }
|
||||||
|
.f1 { animation-delay: .5s } .f2 { animation-delay: .75s }
|
||||||
|
.f3 { animation-delay: 1s } .f4 { animation-delay: 1.25s }
|
||||||
|
@keyframes akad-fade { to { opacity: 1 } }
|
||||||
|
|
||||||
|
/* Przepływ wiedzy - kreska sunie w stronę odbiorcy. */
|
||||||
|
.flow { stroke-dasharray: 7 7; animation: akad-flow 1.4s linear infinite; }
|
||||||
|
@keyframes akad-flow { to { stroke-dashoffset: -14 } }
|
||||||
|
|
||||||
|
.pulse { animation: akad-pulse 2s ease-in-out infinite; }
|
||||||
|
@keyframes akad-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .3 } }
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.draw, .fade, .flow, .pulse {
|
||||||
|
animation: none; stroke-dashoffset: 0; stroke-dasharray: none; opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></style>
|
||||||
|
</defs>
|
||||||
|
<rect width="720" height="344" fill="#eaeef2"/>
|
||||||
|
<rect width="720" height="344" fill="url(#akad-grid)"/>
|
||||||
|
|
||||||
|
<!-- ── Źródła wiedzy ─────────────────────────────────────────────────── -->
|
||||||
|
<g stroke="#416180" stroke-width="1.5" fill="#f2f2f3">
|
||||||
|
<rect class="draw d1" pathLength="1" x="40" y="66" width="150" height="44"/>
|
||||||
|
<rect class="draw d1" pathLength="1" x="40" y="124" width="150" height="44"/>
|
||||||
|
<rect class="draw d1" pathLength="1" x="40" y="182" width="150" height="44"/>
|
||||||
|
</g>
|
||||||
|
<g fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif" font-size="10.5"
|
||||||
|
text-anchor="middle" class="fade f1">
|
||||||
|
<text x="115" y="92" textLength="120" lengthAdjust="spacingAndGlyphs">WDROŻENIA U KLIENTÓW</text>
|
||||||
|
<text x="115" y="150" textLength="112" lengthAdjust="spacingAndGlyphs">PROJEKTY DORADCZE</text>
|
||||||
|
<text x="115" y="208" textLength="120" lengthAdjust="spacingAndGlyphs">PYTANIA CZYTELNIKÓW</text>
|
||||||
|
</g>
|
||||||
|
<text x="40" y="46" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="11" textLength="90" lengthAdjust="spacingAndGlyphs" opacity=".72" class="fade f1">CO ROBIMY</text>
|
||||||
|
|
||||||
|
<!-- zbiorczy przepływ do Akademii -->
|
||||||
|
<g stroke="#5980a6" stroke-width="1.8" fill="none">
|
||||||
|
<path class="draw d2" pathLength="1" d="M190 88 H228 V146"/>
|
||||||
|
<path class="draw d2" pathLength="1" d="M190 146 H228"/>
|
||||||
|
<path class="draw d2" pathLength="1" d="M190 204 H228 V146"/>
|
||||||
|
<path class="flow" d="M228 146 H266" marker-end="url(#akad-arrow)"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ── Akademia Wodociągowca ─────────────────────────────────────────── -->
|
||||||
|
<rect class="draw d3" pathLength="1" x="270" y="96" width="200" height="150"
|
||||||
|
fill="#5980a6" stroke="#416180" stroke-width="1.5"/>
|
||||||
|
<rect x="270" y="96" width="200" height="34" fill="#1d2d3d" class="fade f2"/>
|
||||||
|
<text x="370" y="119" fill="#f2f2f3" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="14" font-weight="600" text-anchor="middle"
|
||||||
|
textLength="164" lengthAdjust="spacingAndGlyphs" class="fade f2">AKADEMIA WODOCIĄGOWCA</text>
|
||||||
|
|
||||||
|
<!-- znacznik transmisji na żywo -->
|
||||||
|
<g class="fade f3">
|
||||||
|
<rect x="286" y="144" width="46" height="15" fill="#1d2d3d"/>
|
||||||
|
<circle cx="295" cy="151.5" r="3" fill="#f2f2f3" class="pulse"/>
|
||||||
|
<text x="303" y="155" fill="#f2f2f3" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="9.5" textLength="24" lengthAdjust="spacingAndGlyphs">LIVE</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ekran kursu -->
|
||||||
|
<g stroke="#f2f2f3" stroke-width="1.6" fill="none" class="fade f3">
|
||||||
|
<rect x="286" y="170" width="52" height="36"/>
|
||||||
|
<path d="M352 178h102M352 188h74M352 198h102"/>
|
||||||
|
</g>
|
||||||
|
<path d="m305 182 12 6-12 6z" fill="#f2f2f3" class="fade f3"/>
|
||||||
|
<text x="370" y="230" fill="#f2f2f3" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="10" text-anchor="middle" opacity=".92"
|
||||||
|
textLength="176" lengthAdjust="spacingAndGlyphs" class="fade f3">WEBINARIA · KURSY · CERTYFIKATY</text>
|
||||||
|
|
||||||
|
<!-- ── Zespoły w przedsiębiorstwach ──────────────────────────────────── -->
|
||||||
|
<path class="flow" d="M470 171 H516" stroke="#5980a6" stroke-width="1.8" fill="none" marker-end="url(#akad-arrow)"/>
|
||||||
|
<text x="493" y="160" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="9.5" text-anchor="middle" textLength="44" lengthAdjust="spacingAndGlyphs" class="fade f3">WIEDZA</text>
|
||||||
|
|
||||||
|
<rect class="draw d4" pathLength="1" x="520" y="96" width="160" height="150" fill="#f2f2f3" stroke="#416180" stroke-width="1.5"/>
|
||||||
|
<rect x="520" y="96" width="160" height="30" fill="#5980a6" fill-opacity=".2" class="fade f4"/>
|
||||||
|
<text x="600" y="116" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="11" font-weight="600" text-anchor="middle"
|
||||||
|
textLength="132" lengthAdjust="spacingAndGlyphs" class="fade f4">ZESPOŁY W FIRMACH</text>
|
||||||
|
|
||||||
|
<g stroke="#416180" stroke-width="1.5" fill="none" class="fade f4">
|
||||||
|
<circle cx="558" cy="152" r="11"/><path d="M542 182v-12a16 16 0 0 1 32 0v12"/>
|
||||||
|
<circle cx="600" cy="152" r="11"/><path d="M584 182v-12a16 16 0 0 1 32 0v12"/>
|
||||||
|
<circle cx="642" cy="152" r="11"/><path d="M626 182v-12a16 16 0 0 1 32 0v12"/>
|
||||||
|
</g>
|
||||||
|
<g fill="#5980a6" fill-opacity=".24" class="fade f4">
|
||||||
|
<circle cx="558" cy="152" r="11"/><circle cx="600" cy="152" r="11"/><circle cx="642" cy="152" r="11"/>
|
||||||
|
</g>
|
||||||
|
<!-- certyfikat -->
|
||||||
|
<g class="fade f4">
|
||||||
|
<rect x="556" y="200" width="88" height="26" fill="#f2f2f3" stroke="#416180" stroke-width="1.4"/>
|
||||||
|
<path d="m566 213 5 5 9-11" stroke="#5980a6" stroke-width="2" fill="none"/>
|
||||||
|
<text x="608" y="217" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="9.5" text-anchor="middle" textLength="46" lengthAdjust="spacingAndGlyphs">CERTYFIKAT</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ── Pętla zwrotna: rynek podpowiada tematy ────────────────────────── -->
|
||||||
|
<path class="draw d4" pathLength="1" d="M600 246 V310 H115 V232" stroke="#5980a6" stroke-width="1.8"
|
||||||
|
fill="none" marker-end="url(#akad-arrow)"/>
|
||||||
|
<rect x="276" y="298" width="172" height="22" fill="#eaeef2" class="fade f4"/>
|
||||||
|
<text x="362" y="314" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="10.5" text-anchor="middle" textLength="158" lengthAdjust="spacingAndGlyphs" class="fade f4">PYTANIA WRACAJĄ DO PROGRAMU</text>
|
||||||
|
|
||||||
|
<!-- ── krzyżyki rejestracyjne ────────────────────────────────────────── -->
|
||||||
|
<g stroke="#1d1f20" stroke-width="1" opacity=".45">
|
||||||
|
<path d="M263 89v13M257 95h13"/>
|
||||||
|
<path d="M477 89v13M471 95h13"/>
|
||||||
|
<path d="M263 241v13M257 247h13"/>
|
||||||
|
<path d="M477 241v13M471 247h13"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.7 KiB |
@@ -0,0 +1,130 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 344" role="img" aria-labelledby="t-akad">
|
||||||
|
<title id="t-akad">Akademia Wodociagowca - the competence loop: our delivery and advisory practice feeds the course platform, and trained teams come back with questions from the market</title>
|
||||||
|
<defs>
|
||||||
|
<pattern id="akad-grid" width="20" height="20" patternUnits="userSpaceOnUse">
|
||||||
|
<path d="M20 0H0v20" fill="none" stroke="#5980a6" stroke-opacity=".13" stroke-width="1"/>
|
||||||
|
</pattern>
|
||||||
|
<marker id="akad-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||||
|
<path d="M0 0 10 5 0 10z" fill="#5980a6"/>
|
||||||
|
</marker>
|
||||||
|
<style><![CDATA[
|
||||||
|
/* Rysunek powstaje jak na ploterze: linie same sie kresla.
|
||||||
|
pathLength="1" sprawia, ze dlugosc sciezki nie ma znaczenia. */
|
||||||
|
.draw { stroke-dasharray: 1; stroke-dashoffset: 1; animation: akad-draw 1.1s ease-out forwards; }
|
||||||
|
.d1 { animation-delay: .05s } .d2 { animation-delay: .3s }
|
||||||
|
.d3 { animation-delay: .55s } .d4 { animation-delay: .8s }
|
||||||
|
@keyframes akad-draw { to { stroke-dashoffset: 0 } }
|
||||||
|
|
||||||
|
.fade { opacity: 0; animation: akad-fade .5s ease-out forwards; }
|
||||||
|
.f1 { animation-delay: .5s } .f2 { animation-delay: .75s }
|
||||||
|
.f3 { animation-delay: 1s } .f4 { animation-delay: 1.25s }
|
||||||
|
@keyframes akad-fade { to { opacity: 1 } }
|
||||||
|
|
||||||
|
/* Przeplyw wiedzy - kreska sunie w strone odbiorcy. */
|
||||||
|
.flow { stroke-dasharray: 7 7; animation: akad-flow 1.4s linear infinite; }
|
||||||
|
@keyframes akad-flow { to { stroke-dashoffset: -14 } }
|
||||||
|
|
||||||
|
.pulse { animation: akad-pulse 2s ease-in-out infinite; }
|
||||||
|
@keyframes akad-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .3 } }
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.draw, .fade, .flow, .pulse {
|
||||||
|
animation: none; stroke-dashoffset: 0; stroke-dasharray: none; opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></style>
|
||||||
|
</defs>
|
||||||
|
<rect width="720" height="344" fill="#eaeef2"/>
|
||||||
|
<rect width="720" height="344" fill="url(#akad-grid)"/>
|
||||||
|
|
||||||
|
<!-- ── Zrodla wiedzy ─────────────────────────────────────────────────── -->
|
||||||
|
<g stroke="#416180" stroke-width="1.5" fill="#f2f2f3">
|
||||||
|
<rect class="draw d1" pathLength="1" x="40" y="66" width="150" height="44"/>
|
||||||
|
<rect class="draw d1" pathLength="1" x="40" y="124" width="150" height="44"/>
|
||||||
|
<rect class="draw d1" pathLength="1" x="40" y="182" width="150" height="44"/>
|
||||||
|
</g>
|
||||||
|
<g fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif" font-size="10.5"
|
||||||
|
text-anchor="middle" class="fade f1">
|
||||||
|
<text x="115" y="92" textLength="120" lengthAdjust="spacingAndGlyphs">CLIENT ROLLOUTS</text>
|
||||||
|
<text x="115" y="150" textLength="112" lengthAdjust="spacingAndGlyphs">ADVISORY PROJECTS</text>
|
||||||
|
<text x="115" y="208" textLength="120" lengthAdjust="spacingAndGlyphs">READER QUESTIONS</text>
|
||||||
|
</g>
|
||||||
|
<text x="40" y="46" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="11" textLength="90" lengthAdjust="spacingAndGlyphs" opacity=".72" class="fade f1">WHAT WE DO</text>
|
||||||
|
|
||||||
|
<!-- zbiorczy przepływ do Akademii -->
|
||||||
|
<g stroke="#5980a6" stroke-width="1.8" fill="none">
|
||||||
|
<path class="draw d2" pathLength="1" d="M190 88 H228 V146"/>
|
||||||
|
<path class="draw d2" pathLength="1" d="M190 146 H228"/>
|
||||||
|
<path class="draw d2" pathLength="1" d="M190 204 H228 V146"/>
|
||||||
|
<path class="flow" d="M228 146 H266" marker-end="url(#akad-arrow)"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ── Akademia Wodociągowca ─────────────────────────────────────────── -->
|
||||||
|
<rect class="draw d3" pathLength="1" x="270" y="96" width="200" height="150"
|
||||||
|
fill="#5980a6" stroke="#416180" stroke-width="1.5"/>
|
||||||
|
<rect x="270" y="96" width="200" height="34" fill="#1d2d3d" class="fade f2"/>
|
||||||
|
<text x="370" y="119" fill="#f2f2f3" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="14" font-weight="600" text-anchor="middle"
|
||||||
|
textLength="164" lengthAdjust="spacingAndGlyphs" class="fade f2">AKADEMIA WODOCIAGOWCA</text>
|
||||||
|
|
||||||
|
<!-- znacznik transmisji na żywo -->
|
||||||
|
<g class="fade f3">
|
||||||
|
<rect x="286" y="144" width="46" height="15" fill="#1d2d3d"/>
|
||||||
|
<circle cx="295" cy="151.5" r="3" fill="#f2f2f3" class="pulse"/>
|
||||||
|
<text x="303" y="155" fill="#f2f2f3" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="9.5" textLength="24" lengthAdjust="spacingAndGlyphs">LIVE</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ekran kursu -->
|
||||||
|
<g stroke="#f2f2f3" stroke-width="1.6" fill="none" class="fade f3">
|
||||||
|
<rect x="286" y="170" width="52" height="36"/>
|
||||||
|
<path d="M352 178h102M352 188h74M352 198h102"/>
|
||||||
|
</g>
|
||||||
|
<path d="m305 182 12 6-12 6z" fill="#f2f2f3" class="fade f3"/>
|
||||||
|
<text x="370" y="230" fill="#f2f2f3" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="10" text-anchor="middle" opacity=".92"
|
||||||
|
textLength="176" lengthAdjust="spacingAndGlyphs" class="fade f3">WEBINARS · COURSES · CERTIFICATES</text>
|
||||||
|
|
||||||
|
<!-- ── Zespoly w przedsiebiorstwach ──────────────────────────────────── -->
|
||||||
|
<path class="flow" d="M470 171 H516" stroke="#5980a6" stroke-width="1.8" fill="none" marker-end="url(#akad-arrow)"/>
|
||||||
|
<text x="493" y="160" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="9.5" text-anchor="middle" textLength="38" lengthAdjust="spacingAndGlyphs" class="fade f3">KNOW-HOW</text>
|
||||||
|
|
||||||
|
<rect class="draw d4" pathLength="1" x="520" y="96" width="160" height="150" fill="#f2f2f3" stroke="#416180" stroke-width="1.5"/>
|
||||||
|
<rect x="520" y="96" width="160" height="30" fill="#5980a6" fill-opacity=".2" class="fade f4"/>
|
||||||
|
<text x="600" y="116" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="11" font-weight="600" text-anchor="middle"
|
||||||
|
textLength="132" lengthAdjust="spacingAndGlyphs" class="fade f4">TEAMS AT UTILITIES</text>
|
||||||
|
|
||||||
|
<g stroke="#416180" stroke-width="1.5" fill="none" class="fade f4">
|
||||||
|
<circle cx="558" cy="152" r="11"/><path d="M542 182v-12a16 16 0 0 1 32 0v12"/>
|
||||||
|
<circle cx="600" cy="152" r="11"/><path d="M584 182v-12a16 16 0 0 1 32 0v12"/>
|
||||||
|
<circle cx="642" cy="152" r="11"/><path d="M626 182v-12a16 16 0 0 1 32 0v12"/>
|
||||||
|
</g>
|
||||||
|
<g fill="#5980a6" fill-opacity=".24" class="fade f4">
|
||||||
|
<circle cx="558" cy="152" r="11"/><circle cx="600" cy="152" r="11"/><circle cx="642" cy="152" r="11"/>
|
||||||
|
</g>
|
||||||
|
<!-- certyfikat -->
|
||||||
|
<g class="fade f4">
|
||||||
|
<rect x="556" y="200" width="88" height="26" fill="#f2f2f3" stroke="#416180" stroke-width="1.4"/>
|
||||||
|
<path d="m566 213 5 5 9-11" stroke="#5980a6" stroke-width="2" fill="none"/>
|
||||||
|
<text x="608" y="217" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="9.5" text-anchor="middle" textLength="46" lengthAdjust="spacingAndGlyphs">CERTIFICATE</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ── Petla zwrotna: rynek podpowiada tematy ────────────────────────── -->
|
||||||
|
<path class="draw d4" pathLength="1" d="M600 246 V310 H115 V232" stroke="#5980a6" stroke-width="1.8"
|
||||||
|
fill="none" marker-end="url(#akad-arrow)"/>
|
||||||
|
<rect x="276" y="298" width="172" height="22" fill="#eaeef2" class="fade f4"/>
|
||||||
|
<text x="362" y="314" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
|
||||||
|
font-size="10.5" text-anchor="middle" textLength="158" lengthAdjust="spacingAndGlyphs" class="fade f4">QUESTIONS FEED THE PROGRAMME</text>
|
||||||
|
|
||||||
|
<!-- ── krzyzyki rejestracyjne ────────────────────────────────────────── -->
|
||||||
|
<g stroke="#1d1f20" stroke-width="1" opacity=".45">
|
||||||
|
<path d="M263 89v13M257 95h13"/>
|
||||||
|
<path d="M477 89v13M471 95h13"/>
|
||||||
|
<path d="M263 241v13M257 247h13"/>
|
||||||
|
<path d="M477 241v13M471 247h13"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.7 KiB |
@@ -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]); }
|
||||||
|
})();
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -272,6 +272,11 @@ textarea.input { min-height: 110px; resize: vertical; }
|
|||||||
.s-prop { font-weight: 500; }
|
.s-prop { font-weight: 500; }
|
||||||
.s-rem { font-size: 14px; color: color-mix(in srgb, var(--color-text) 68%, transparent); }
|
.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%; }
|
.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-topic { font-family: var(--font-heading); font-weight: 600; font-size: 18px; }
|
||||||
.s-act { text-align: right; white-space: nowrap; }
|
.s-act { text-align: right; white-space: nowrap; }
|
||||||
.sign {
|
.sign {
|
||||||
@@ -475,6 +480,35 @@ textarea.input { min-height: 110px; resize: vertical; }
|
|||||||
color: var(--color-accent-700);
|
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 <html>). 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 --------------------------------------------------- */
|
/* -- newsletter / CTA --------------------------------------------------- */
|
||||||
.cta-band {
|
.cta-band {
|
||||||
position: relative; padding: calc(2 * var(--leading)) var(--space-8);
|
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 td { border: 0; padding: 0; }
|
||||||
.table.sched .s-date { order: 1; font-size: 14px; color: var(--color-accent-700); }
|
.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-form { order: 2; text-align: right; }
|
||||||
.table.sched .s-topic { grid-column: 1 / -1; order: 3; }
|
.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); }
|
.table.sched .s-place { order: 4; font-size: 13.5px; color: color-mix(in srgb, var(--color-text) 60%, transparent); }
|
||||||
|
|||||||
+74
-15
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>SoftSPM - training, consulting and custom software for the water sector</title>
|
<title>SoftSPM - training, consulting and custom software for the water sector</title>
|
||||||
<meta name="description" content="Training, webinars, consulting and turnkey software for municipal water and wastewater utilities. 25+ years in IT, in the water sector since 2014. Web and mobile applications, our own course platform, the Wodociagowiec.pl portal and the Gielda Komunalna marketplace.">
|
<meta name="description" content="Training, webinars, consulting and turnkey software for municipal water and wastewater utilities. 25+ years in IT, in the water sector since 2014. Web and mobile applications, the Akademia Wodociagowca course platform, the Wodociagowiec.pl portal and the Gielda Komunalna marketplace.">
|
||||||
<link rel="icon" href="images/favicon.ico" sizes="any">
|
<link rel="icon" href="images/favicon.ico" sizes="any">
|
||||||
<link rel="stylesheet" href="assets/styles.css">
|
<link rel="stylesheet" href="assets/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<a href="#offer">Offer</a>
|
<a href="#offer">Offer</a>
|
||||||
<a href="#topics">Topics</a>
|
<a href="#topics">Topics</a>
|
||||||
<a href="#dates">Dates</a>
|
<a href="#dates">Dates</a>
|
||||||
<a href="#platform">Platform</a>
|
<a href="#platform">Academy</a>
|
||||||
<a href="#software">Software</a>
|
<a href="#software">Software</a>
|
||||||
<a href="#ecosystem">Ecosystem</a>
|
<a href="#ecosystem">Ecosystem</a>
|
||||||
<a class="nav-keep" href="index.html" title="Wersja polska">PL</a>
|
<a class="nav-keep" href="index.html" title="Wersja polska">PL</a>
|
||||||
@@ -30,8 +30,8 @@
|
|||||||
<section class="hero">
|
<section class="hero">
|
||||||
<div class="hero-grid">
|
<div class="hero-grid">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="display"><span class="line">Water sector</span> <span class="line">know-how since 2014.</span></h1>
|
<h1 class="display"><span class="line">Water sector know-how</span> <span class="line">that stays in-house.</span></h1>
|
||||||
<p class="sub">Training, webinars, consulting and <strong>turnkey software</strong> 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 <strong>Wodociagowiec.pl</strong> portal and <strong>Gielda Komunalna</strong>, a free marketplace where the sector buys, sells and swaps equipment.</p>
|
<p class="sub"><strong>Turnkey software</strong>, 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.</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<a class="btn btn-primary btn-lg" href="#dates">See the programme</a>
|
<a class="btn btn-primary btn-lg" href="#dates">See the programme</a>
|
||||||
<a class="btn btn-ghost btn-lg" href="#contact">Book a consultation</a>
|
<a class="btn btn-ghost btn-lg" href="#contact">Book a consultation</a>
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
<tr><td class="s-num">03</td><td class="s-prop">Specialists trained</td><td class="s-val">480+</td><td class="s-rem">Classroom and online</td></tr>
|
<tr><td class="s-num">03</td><td class="s-prop">Specialists trained</td><td class="s-val">480+</td><td class="s-rem">Classroom and online</td></tr>
|
||||||
<tr><td class="s-num">04</td><td class="s-prop">Webinars per year</td><td class="s-val">24</td><td class="s-rem">On our own course platform</td></tr>
|
<tr><td class="s-num">04</td><td class="s-prop">Webinars per year</td><td class="s-val">24</td><td class="s-rem">On our own course platform</td></tr>
|
||||||
<tr><td class="s-num">05</td><td class="s-prop">Advisory projects</td><td class="s-val">60+</td><td class="s-rem">Tariffs, organisation, procurement, digitisation</td></tr>
|
<tr><td class="s-num">05</td><td class="s-prop">Advisory projects</td><td class="s-val">60+</td><td class="s-rem">Tariffs, organisation, procurement, digitisation</td></tr>
|
||||||
<tr><td class="s-num">06</td><td class="s-prop">Ecosystem reach</td><td class="s-val">2 services</td><td class="s-rem">Wodociagowiec.pl trade portal and Gielda Komunalna marketplace</td></tr>
|
<tr><td class="s-num">06</td><td class="s-prop">Ecosystem reach</td><td class="s-val">3 services</td><td class="s-rem">Wodociagowiec.pl, Gielda Komunalna and Akademia Wodociagowca</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p class="sheet-note">Sample figures - to be confirmed before publication.</p>
|
<p class="sheet-note">Sample figures - to be confirmed before publication.</p>
|
||||||
@@ -270,14 +270,69 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ── 05 · Platform ────────────────────────────────────────────────── -->
|
<!-- ── 05 · Akademia Wodociagowca ───────────────────────────────────── -->
|
||||||
<section class="section" id="platform">
|
<section class="section" id="platform">
|
||||||
<span class="kicker">05 · Course platform</span>
|
<span class="kicker">05 · Akademia Wodociagowca</span>
|
||||||
<hr class="caption-rule">
|
<hr class="caption-rule">
|
||||||
|
<div class="section-intro" data-reveal>
|
||||||
|
<h2>Competence that stays with the utility</h2>
|
||||||
|
<p class="text-muted">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.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<figure class="figure" style="margin: 0 0 calc(1.5 * var(--leading))" data-reveal>
|
||||||
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
|
<img class="plot" src="assets/img/en/akademia.svg" alt="Diagram: client rollouts, advisory projects and reader questions feed Akademia Wodociagowca, and trained teams send questions back into the programme" width="720" height="344">
|
||||||
|
<figcaption>Diagram 01 - The competence loop: practice feeds the Academy, trained teams come back with questions</figcaption>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<div class="stats" data-reveal>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-num" data-count="24">24</span>
|
||||||
|
<span class="stat-label">webinars a year, two every month</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-num" data-count="8">8</span>
|
||||||
|
<span class="stat-label">subject tracks in the catalogue</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-num" data-count="12">12</span>
|
||||||
|
<span class="stat-label">people at most in a group</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-num" data-count="480" data-suffix="+">480+</span>
|
||||||
|
<span class="stat-label">specialists trained across the sector</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="sheet-note" style="margin-bottom: calc(1.5 * var(--leading))">Webinar count and people trained - sample figures, to be confirmed before publication.</p>
|
||||||
|
|
||||||
|
<div class="cells cells-3" style="margin-bottom: calc(2 * var(--leading))">
|
||||||
|
<div class="cell-frame" data-reveal>
|
||||||
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
|
<span class="cell-num">05.1</span>
|
||||||
|
<svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3 2 8l10 5 10-5Z"/><path d="M6 10.5V16c0 1.7 2.7 3 6 3s6-1.3 6-3v-5.5"/></svg>
|
||||||
|
<h3>Taught first-hand</h3>
|
||||||
|
<p>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.</p>
|
||||||
|
</div>
|
||||||
|
<div class="cell-frame" data-reveal>
|
||||||
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
|
<span class="cell-num">05.2</span>
|
||||||
|
<svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="18" height="14" rx="1"/><path d="M3 9h18M8 22h8M12 18v4"/></svg>
|
||||||
|
<h3>Knowledge stays in-house</h3>
|
||||||
|
<p>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.</p>
|
||||||
|
</div>
|
||||||
|
<div class="cell-frame" data-reveal>
|
||||||
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
|
<span class="cell-num">05.3</span>
|
||||||
|
<svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 4v5h-5"/></svg>
|
||||||
|
<h3>Questions feed the programme</h3>
|
||||||
|
<p>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.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="split">
|
<div class="split">
|
||||||
<div>
|
<div data-reveal>
|
||||||
<h2 class="split-title">Webinars and e-learning in one place</h2>
|
<h3 class="split-title">What the utility gets</h3>
|
||||||
<p class="lead">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.</p>
|
<p class="lead">One company account, many employees, and material available when it is actually needed - not only on the day of the course.</p>
|
||||||
<ul class="checks">
|
<ul class="checks">
|
||||||
<li>Live webinars with chat and a Q&A session</li>
|
<li>Live webinars with chat and a Q&A session</li>
|
||||||
<li>Recordings available indefinitely with your access</li>
|
<li>Recordings available indefinitely with your access</li>
|
||||||
@@ -286,9 +341,12 @@
|
|||||||
<li>Learner dashboard with progress and your team's training history</li>
|
<li>Learner dashboard with progress and your team's training history</li>
|
||||||
<li>Company account - one subscription for many employees</li>
|
<li>Company account - one subscription for many employees</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a class="btn btn-primary" href="#">Go to the platform</a>
|
<div class="row" style="display:flex; gap:var(--space-4); flex-wrap:wrap">
|
||||||
|
<a class="btn btn-primary" href="https://akademiawodociagowca.pl/" target="_blank" rel="noopener">Go to the Academy</a>
|
||||||
|
<a class="btn btn-ghost" href="#dates">See upcoming dates</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<figure class="figure split-media" style="margin:0">
|
<figure class="figure split-media" style="margin:0" data-reveal>
|
||||||
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
<img class="plot" src="assets/img/en/platforma.svg" alt="Learner dashboard: course progress, webinar agenda and a live indicator" width="640" height="360">
|
<img class="plot" src="assets/img/en/platforma.svg" alt="Learner dashboard: course progress, webinar agenda and a live indicator" width="640" height="360">
|
||||||
<figcaption>Fig. 02 - Learner dashboard: progress and upcoming webinars</figcaption>
|
<figcaption>Fig. 02 - Learner dashboard: progress and upcoming webinars</figcaption>
|
||||||
@@ -360,13 +418,13 @@
|
|||||||
<hr class="caption-rule">
|
<hr class="caption-rule">
|
||||||
<div class="section-intro">
|
<div class="section-intro">
|
||||||
<h2>A trade portal, a marketplace and a course platform - one loop</h2>
|
<h2>A trade portal, a marketplace and a course platform - one loop</h2>
|
||||||
<p class="text-muted">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.</p>
|
<p class="text-muted">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.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<figure class="figure" style="margin: 0 0 calc(1.5 * var(--leading))">
|
<figure class="figure" style="margin: 0 0 calc(1.5 * var(--leading))">
|
||||||
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
<img class="plot" src="assets/img/en/ekosystem.svg" alt="Diagram: the Wodociagowiec.pl portal and the Gielda Komunalna marketplace exchange topics, practitioners and listings with the course platform at the centre" width="720" height="400">
|
<img class="plot" src="assets/img/en/ekosystem.svg" alt="Diagram: the Wodociagowiec.pl portal and the Gielda Komunalna marketplace exchange topics, practitioners and listings with the course platform at the centre" width="720" height="400">
|
||||||
<figcaption>Diagram 01 - The knowledge loop: the portal and the marketplace feed the platform, graduates return to the market</figcaption>
|
<figcaption>Diagram 02 - The knowledge loop: the portal and the marketplace feed the platform, graduates return to the market</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
<div class="cells cells-2" style="margin-bottom: calc(1.5 * var(--leading))">
|
<div class="cells cells-2" style="margin-bottom: calc(1.5 * var(--leading))">
|
||||||
@@ -481,7 +539,7 @@
|
|||||||
<li><a href="szkolenia.html#webinaria">Webinars (PL)</a></li>
|
<li><a href="szkolenia.html#webinaria">Webinars (PL)</a></li>
|
||||||
<li><a href="konsulting.html">Consulting (PL)</a></li>
|
<li><a href="konsulting.html">Consulting (PL)</a></li>
|
||||||
<li><a href="#software">Custom software</a></li>
|
<li><a href="#software">Custom software</a></li>
|
||||||
<li><a href="#platform">Course platform</a></li>
|
<li><a href="#platform">Akademia Wodociagowca</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -508,6 +566,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="assets/reveal.js" defer></script>
|
||||||
<script src="assets/forms.js" defer></script>
|
<script src="assets/forms.js" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+81
-21
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>SoftSPM - szkolenia, konsulting i aplikacje dla branży wod-kan</title>
|
<title>SoftSPM - szkolenia, konsulting i aplikacje dla branży wod-kan</title>
|
||||||
<meta name="description" content="Szkolenia, webinaria, konsulting i aplikacje pod klucz dla przedsiębiorstw wodociągowych i komunalnych. Ponad 25 lat w IT, od 2014 w branży wod-kan. Systemy webowe i mobilne, platforma kursowa, portal Wodociągowiec.pl i Giełda Komunalna.">
|
<meta name="description" content="Szkolenia, webinaria, konsulting i aplikacje pod klucz dla przedsiębiorstw wodociągowych i komunalnych. Ponad 25 lat w IT, od 2014 w branży wod-kan. Systemy webowe i mobilne, platforma kursowa Akademia Wodociągowca, portal Wodociągowiec.pl i Giełda Komunalna.">
|
||||||
<link rel="icon" href="images/favicon.ico" sizes="any">
|
<link rel="icon" href="images/favicon.ico" sizes="any">
|
||||||
<link rel="stylesheet" href="assets/styles.css">
|
<link rel="stylesheet" href="assets/styles.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<a href="#oferta">Oferta</a>
|
<a href="#oferta">Oferta</a>
|
||||||
<a href="#tematy">Tematy</a>
|
<a href="#tematy">Tematy</a>
|
||||||
<a href="#terminy">Terminy</a>
|
<a href="#terminy">Terminy</a>
|
||||||
<a href="#platforma">Platforma</a>
|
<a href="#platforma">Akademia</a>
|
||||||
<a href="#aplikacje">Aplikacje</a>
|
<a href="#aplikacje">Aplikacje</a>
|
||||||
<a href="#ekosystem">Ekosystem</a>
|
<a href="#ekosystem">Ekosystem</a>
|
||||||
<a href="szkolenia.html">Szkolenia</a>
|
<a href="szkolenia.html">Szkolenia</a>
|
||||||
@@ -32,8 +32,8 @@
|
|||||||
<section class="hero">
|
<section class="hero">
|
||||||
<div class="hero-grid">
|
<div class="hero-grid">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="display"><span class="line">Wiedza wod-kan.</span> <span class="line">W praktyce od 2014.</span></h1>
|
<h1 class="display"><span class="line">Wiedza wod-kan,</span> <span class="line">która zostaje w firmie.</span></h1>
|
||||||
<p class="sub">Szkolenia, webinaria, konsulting i <strong>aplikacje pod klucz</strong> 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 <strong>Wodociągowiec.pl</strong> oraz <strong>Giełdę Komunalną</strong> - bezpłatny marketplace, na którym branża kupuje, sprzedaje i wymienia sprzęt.</p>
|
<p class="sub"><strong>Aplikacje pod klucz</strong>, 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.</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<a class="btn btn-primary btn-lg" href="#terminy">Zobacz program szkoleń</a>
|
<a class="btn btn-primary btn-lg" href="#terminy">Zobacz program szkoleń</a>
|
||||||
<a class="btn btn-ghost btn-lg" href="konsulting.html#kontakt">Umów konsultację</a>
|
<a class="btn btn-ghost btn-lg" href="konsulting.html#kontakt">Umów konsultację</a>
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
<tr><td class="s-num">03</td><td class="s-prop">Przeszkoleni specjaliści</td><td class="s-val">480+</td><td class="s-rem">Szkolenia stacjonarne i online</td></tr>
|
<tr><td class="s-num">03</td><td class="s-prop">Przeszkoleni specjaliści</td><td class="s-val">480+</td><td class="s-rem">Szkolenia stacjonarne i online</td></tr>
|
||||||
<tr><td class="s-num">04</td><td class="s-prop">Webinariów rocznie</td><td class="s-val">24</td><td class="s-rem">Na własnej platformie kursowej</td></tr>
|
<tr><td class="s-num">04</td><td class="s-prop">Webinariów rocznie</td><td class="s-val">24</td><td class="s-rem">Na własnej platformie kursowej</td></tr>
|
||||||
<tr><td class="s-num">05</td><td class="s-prop">Projekty doradcze</td><td class="s-val">60+</td><td class="s-rem">Organizacja, taryfy, cyfryzacja, zamówienia</td></tr>
|
<tr><td class="s-num">05</td><td class="s-prop">Projekty doradcze</td><td class="s-val">60+</td><td class="s-rem">Organizacja, taryfy, cyfryzacja, zamówienia</td></tr>
|
||||||
<tr><td class="s-num">06</td><td class="s-prop">Zasięg ekosystemu</td><td class="s-val">2 serwisy</td><td class="s-rem">Portal Wodociągowiec.pl i marketplace Giełda Komunalna</td></tr>
|
<tr><td class="s-num">06</td><td class="s-prop">Zasięg ekosystemu</td><td class="s-val">3 serwisy</td><td class="s-rem">Wodociągowiec.pl, Giełda Komunalna i Akademia Wodociągowca</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p class="sheet-note">Wartości przykładowe - do potwierdzenia przed publikacją.</p>
|
<p class="sheet-note">Wartości przykładowe - do potwierdzenia przed publikacją.</p>
|
||||||
@@ -251,20 +251,20 @@
|
|||||||
<span class="kicker">04 · Harmonogram</span>
|
<span class="kicker">04 · Harmonogram</span>
|
||||||
<hr class="caption-rule">
|
<hr class="caption-rule">
|
||||||
<div class="section-intro">
|
<div class="section-intro">
|
||||||
<h2>Terminy najbliższej edycji ustalamy</h2>
|
<h2 data-schedule-heading>Terminy najbliższej edycji ustalamy</h2>
|
||||||
<p class="text-muted">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.</p>
|
<p class="text-muted" data-schedule-intro>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.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="plate">
|
<div class="plate">
|
||||||
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
<header class="title-block">
|
<header class="title-block">
|
||||||
<span class="tb-title">Tematy w przygotowaniu</span>
|
<span class="tb-title" data-schedule-title>Tematy w przygotowaniu</span>
|
||||||
<span class="tb-cell">SPM-2026</span>
|
<span class="tb-cell">SPM-2026</span>
|
||||||
<span class="tb-cell">Rev A</span>
|
<span class="tb-cell">Rev A</span>
|
||||||
<span class="tb-cell">Arkusz 02 z 06</span>
|
<span class="tb-cell">Arkusz 02 z 06</span>
|
||||||
</header>
|
</header>
|
||||||
<table class="table sched">
|
<table class="table sched" data-schedule data-schedule-limit="8">
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th scope="col">Termin</th><th scope="col">Temat</th><th scope="col">Forma</th><th scope="col">Miejsce</th><th scope="col"><span class="visually-hidden">Zapisy</span></th></tr>
|
<tr><th scope="col" data-schedule-datehead>Termin</th><th scope="col">Temat</th><th scope="col">Forma</th><th scope="col">Miejsce</th><th scope="col"><span class="visually-hidden">Zapisy</span></th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -325,18 +325,73 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p class="sheet-note">Ustalamy terminy najbliższej edycji. Zostaw kontakt przy wybranym temacie na stronie <a href="szkolenia.html#terminy">Szkolenia i webinaria</a> - odezwiemy się, gdy otworzymy zapisy.</p>
|
<p class="sheet-note" data-schedule-note>Ustalamy terminy najbliższej edycji. Zostaw kontakt przy wybranym temacie na stronie <a href="szkolenia.html#terminy">Szkolenia i webinaria</a> - odezwiemy się, gdy otworzymy zapisy.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ── 05 · Platforma kursowa ───────────────────────────────────────── -->
|
<!-- ── 05 · Akademia Wodociągowca ───────────────────────────────────── -->
|
||||||
<section class="section" id="platforma">
|
<section class="section" id="platforma">
|
||||||
<span class="kicker">05 · Platforma kursowa</span>
|
<span class="kicker">05 · Akademia Wodociągowca</span>
|
||||||
<hr class="caption-rule">
|
<hr class="caption-rule">
|
||||||
|
<div class="section-intro" data-reveal>
|
||||||
|
<h2>Kompetencja, która zostaje w przedsiębiorstwie</h2>
|
||||||
|
<p class="text-muted">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.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<figure class="figure" style="margin: 0 0 calc(1.5 * var(--leading))" data-reveal>
|
||||||
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
|
<img class="plot" src="assets/img/akademia.svg" alt="Schemat: wdrożenia, projekty doradcze i pytania czytelników zasilają Akademię Wodociągowca, przeszkolone zespoły wracają z pytaniami do programu" width="720" height="344">
|
||||||
|
<figcaption>Schemat 01 - Obieg kompetencji: praktyka zasila Akademię, a przeszkolone zespoły wracają z pytaniami</figcaption>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<div class="stats" data-reveal>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-num" data-count="24">24</span>
|
||||||
|
<span class="stat-label">webinaria rocznie, dwa w miesiącu</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-num" data-count="8">8</span>
|
||||||
|
<span class="stat-label">ścieżek tematycznych w katalogu</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-num" data-count="12">12</span>
|
||||||
|
<span class="stat-label">osób maksymalnie w grupie</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-num" data-count="480" data-suffix="+">480+</span>
|
||||||
|
<span class="stat-label">przeszkolonych specjalistów branży</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="sheet-note" style="margin-bottom: calc(1.5 * var(--leading))">Liczba webinariów i przeszkolonych - wartości przykładowe, do potwierdzenia przed publikacją.</p>
|
||||||
|
|
||||||
|
<div class="cells cells-3" style="margin-bottom: calc(2 * var(--leading))">
|
||||||
|
<div class="cell-frame" data-reveal>
|
||||||
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
|
<span class="cell-num">05.1</span>
|
||||||
|
<svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3 2 8l10 5 10-5Z"/><path d="M6 10.5V16c0 1.7 2.7 3 6 3s6-1.3 6-3v-5.5"/></svg>
|
||||||
|
<h3>Uczymy z pierwszej ręki</h3>
|
||||||
|
<p>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.</p>
|
||||||
|
</div>
|
||||||
|
<div class="cell-frame" data-reveal>
|
||||||
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
|
<span class="cell-num">05.2</span>
|
||||||
|
<svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="18" height="14" rx="1"/><path d="M3 9h18M8 22h8M12 18v4"/></svg>
|
||||||
|
<h3>Wiedza zostaje w firmie</h3>
|
||||||
|
<p>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.</p>
|
||||||
|
</div>
|
||||||
|
<div class="cell-frame" data-reveal>
|
||||||
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
|
<span class="cell-num">05.3</span>
|
||||||
|
<svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 4v5h-5"/></svg>
|
||||||
|
<h3>Pytania wracają do programu</h3>
|
||||||
|
<p>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.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="split">
|
<div class="split">
|
||||||
<div>
|
<div data-reveal>
|
||||||
<h2 class="split-title">Webinary i e-learning w jednym miejscu</h2>
|
<h3 class="split-title">Co dostaje przedsiębiorstwo</h3>
|
||||||
<p class="lead">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.</p>
|
<p class="lead">Jedno konto firmowe, wielu pracowników, materiał dostępny wtedy, kiedy jest potrzebny - a nie tylko w dniu szkolenia.</p>
|
||||||
<ul class="checks">
|
<ul class="checks">
|
||||||
<li>Webinaria na żywo z czatem i sesją pytań do prowadzącego</li>
|
<li>Webinaria na żywo z czatem i sesją pytań do prowadzącego</li>
|
||||||
<li>Nagrania dostępne bezterminowo w ramach wykupionego dostępu</li>
|
<li>Nagrania dostępne bezterminowo w ramach wykupionego dostępu</li>
|
||||||
@@ -345,9 +400,12 @@
|
|||||||
<li>Panel kursanta z postępem nauki i historią szkoleń całego zespołu</li>
|
<li>Panel kursanta z postępem nauki i historią szkoleń całego zespołu</li>
|
||||||
<li>Konto firmowe - jeden dostęp dla wielu pracowników</li>
|
<li>Konto firmowe - jeden dostęp dla wielu pracowników</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a class="btn btn-primary" href="#">Przejdź do platformy</a>
|
<div class="row" style="display:flex; gap:var(--space-4); flex-wrap:wrap">
|
||||||
|
<a class="btn btn-primary" href="https://akademiawodociagowca.pl/" target="_blank" rel="noopener">Przejdź do Akademii</a>
|
||||||
|
<a class="btn btn-ghost" href="#terminy">Zobacz najbliższe terminy</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<figure class="figure split-media" style="margin:0">
|
<figure class="figure split-media" style="margin:0" data-reveal>
|
||||||
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
<img class="plot" src="assets/img/platforma.svg" alt="Panel kursanta: postęp kursów, agenda webinariów i znacznik transmisji na żywo" width="640" height="360">
|
<img class="plot" src="assets/img/platforma.svg" alt="Panel kursanta: postęp kursów, agenda webinariów i znacznik transmisji na żywo" width="640" height="360">
|
||||||
<figcaption>Rys. 02 - Panel kursanta: postęp nauki i nadchodzące webinaria</figcaption>
|
<figcaption>Rys. 02 - Panel kursanta: postęp nauki i nadchodzące webinaria</figcaption>
|
||||||
@@ -419,13 +477,13 @@
|
|||||||
<hr class="caption-rule">
|
<hr class="caption-rule">
|
||||||
<div class="section-intro">
|
<div class="section-intro">
|
||||||
<h2>Portal, marketplace i platforma kursowa - jeden obieg</h2>
|
<h2>Portal, marketplace i platforma kursowa - jeden obieg</h2>
|
||||||
<p class="text-muted">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.</p>
|
<p class="text-muted">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.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<figure class="figure" style="margin: 0 0 calc(1.5 * var(--leading))">
|
<figure class="figure" style="margin: 0 0 calc(1.5 * var(--leading))">
|
||||||
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
<img class="plot" src="assets/img/ekosystem.svg" alt="Schemat: portal Wodociągowiec.pl i marketplace Giełda Komunalna wymieniają tematy, praktyków i ogłoszenia z platformą kursową w centrum" width="720" height="400">
|
<img class="plot" src="assets/img/ekosystem.svg" alt="Schemat: portal Wodociągowiec.pl i marketplace Giełda Komunalna wymieniają tematy, praktyków i ogłoszenia z platformą kursową w centrum" width="720" height="400">
|
||||||
<figcaption>Schemat 01 - Obieg wiedzy: portal i marketplace zasilają platformę kursową, absolwenci wracają na rynek</figcaption>
|
<figcaption>Schemat 02 - Obieg wiedzy: portal i marketplace zasilają platformę kursową, absolwenci wracają na rynek</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
<div class="cells cells-2" style="margin-bottom: calc(1.5 * var(--leading))">
|
<div class="cells cells-2" style="margin-bottom: calc(1.5 * var(--leading))">
|
||||||
@@ -541,7 +599,7 @@
|
|||||||
<li><a href="szkolenia.html#webinaria">Webinaria</a></li>
|
<li><a href="szkolenia.html#webinaria">Webinaria</a></li>
|
||||||
<li><a href="konsulting.html">Konsulting</a></li>
|
<li><a href="konsulting.html">Konsulting</a></li>
|
||||||
<li><a href="#aplikacje">Aplikacje pod klucz</a></li>
|
<li><a href="#aplikacje">Aplikacje pod klucz</a></li>
|
||||||
<li><a href="#platforma">Platforma kursowa</a></li>
|
<li><a href="#platforma">Akademia Wodociągowca</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -568,6 +626,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="assets/reveal.js" defer></script>
|
||||||
<script src="assets/forms.js" defer></script>
|
<script src="assets/forms.js" defer></script>
|
||||||
|
<script src="assets/schedule.js" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -353,6 +353,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="assets/reveal.js" defer></script>
|
||||||
<script src="assets/forms.js" defer></script>
|
<script src="assets/forms.js" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+10
-8
@@ -242,20 +242,20 @@
|
|||||||
<span class="kicker">04 · Harmonogram</span>
|
<span class="kicker">04 · Harmonogram</span>
|
||||||
<hr class="caption-rule">
|
<hr class="caption-rule">
|
||||||
<div class="section-intro">
|
<div class="section-intro">
|
||||||
<h2>Terminy najbliższej edycji ustalamy</h2>
|
<h2 data-schedule-heading>Terminy najbliższej edycji ustalamy</h2>
|
||||||
<p class="text-muted">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.</p>
|
<p class="text-muted" data-schedule-intro>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.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="plate">
|
<div class="plate">
|
||||||
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
|
||||||
<header class="title-block">
|
<header class="title-block">
|
||||||
<span class="tb-title">Tematy w przygotowaniu</span>
|
<span class="tb-title" data-schedule-title>Tematy w przygotowaniu</span>
|
||||||
<span class="tb-cell">SPM-2026</span>
|
<span class="tb-cell">SPM-2026</span>
|
||||||
<span class="tb-cell">Rev A</span>
|
<span class="tb-cell">Rev A</span>
|
||||||
<span class="tb-cell">Arkusz 05 z 06</span>
|
<span class="tb-cell">Arkusz 05 z 06</span>
|
||||||
</header>
|
</header>
|
||||||
<table class="table sched">
|
<table class="table sched" data-schedule data-schedule-limit="12">
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th scope="col">Termin</th><th scope="col">Temat</th><th scope="col">Forma</th><th scope="col">Miejsce</th><th scope="col"><span class="visually-hidden">Zapisy</span></th></tr>
|
<tr><th scope="col" data-schedule-datehead>Termin</th><th scope="col">Temat</th><th scope="col">Forma</th><th scope="col">Miejsce</th><th scope="col"><span class="visually-hidden">Zapisy</span></th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td class="s-date"><span class="tag tag-soon">Wkrótce</span></td><td class="s-topic">Wniosek taryfowy - przygotowanie i obrona</td><td class="s-form"><span class="tag tag-accent">Szkolenie</span></td><td class="s-place">Bełchatów</td><td class="s-act"><a class="sign" href="#zgloszenie">Powiadom mnie →</a></td></tr>
|
<tr><td class="s-date"><span class="tag tag-soon">Wkrótce</span></td><td class="s-topic">Wniosek taryfowy - przygotowanie i obrona</td><td class="s-form"><span class="tag tag-accent">Szkolenie</span></td><td class="s-place">Bełchatów</td><td class="s-act"><a class="sign" href="#zgloszenie">Powiadom mnie →</a></td></tr>
|
||||||
@@ -271,7 +271,7 @@
|
|||||||
<tr><td class="s-date"><span class="tag tag-soon">Wkrótce</span></td><td class="s-topic">NIS2 i infrastruktura krytyczna - obowiązki przedsiębiorstwa</td><td class="s-form"><span class="tag tag-outline">Webinar</span></td><td class="s-place">Online</td><td class="s-act"><a class="sign" href="#zgloszenie">Powiadom mnie →</a></td></tr>
|
<tr><td class="s-date"><span class="tag tag-soon">Wkrótce</span></td><td class="s-topic">NIS2 i infrastruktura krytyczna - obowiązki przedsiębiorstwa</td><td class="s-form"><span class="tag tag-outline">Webinar</span></td><td class="s-place">Online</td><td class="s-act"><a class="sign" href="#zgloszenie">Powiadom mnie →</a></td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p class="sheet-note">Ustalamy terminy najbliższej edycji. Zostaw kontakt przy wybranym temacie - poinformujemy Cię, gdy tylko otworzymy zapisy.</p>
|
<p class="sheet-note" data-schedule-note>Ustalamy terminy najbliższej edycji. Zostaw kontakt przy wybranym temacie - poinformujemy Cię, gdy tylko otworzymy zapisy.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -282,10 +282,10 @@
|
|||||||
<div class="split">
|
<div class="split">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="split-title">Pakiet roczny webinariów</h2>
|
<h2 class="split-title">Pakiet roczny webinariów</h2>
|
||||||
<p class="lead">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.</p>
|
<p class="lead">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 <a href="https://akademiawodociagowca.pl/" target="_blank" rel="noopener">Akademia Wodociągowca</a>, więc nagranie i materiały zostają na koncie firmy także wtedy, gdy ktoś nie mógł być na żywo.</p>
|
||||||
<ul class="checks">
|
<ul class="checks">
|
||||||
<li>24 webinaria rocznie na żywo, z sesją pytań do prowadzącego</li>
|
<li>24 webinaria rocznie na żywo, z sesją pytań do prowadzącego</li>
|
||||||
<li>Nagrania i materiały dostępne bezterminowo na platformie kursowej</li>
|
<li>Nagrania i materiały bezterminowo na koncie firmy w Akademii Wodociągowca</li>
|
||||||
<li>Bez limitu uczestników z jednego przedsiębiorstwa</li>
|
<li>Bez limitu uczestników z jednego przedsiębiorstwa</li>
|
||||||
<li>Archiwum wcześniejszych spotkań w cenie pakietu</li>
|
<li>Archiwum wcześniejszych spotkań w cenie pakietu</li>
|
||||||
<li>Możliwość zgłoszenia tematu do programu na kolejny kwartał</li>
|
<li>Możliwość zgłoszenia tematu do programu na kolejny kwartał</li>
|
||||||
@@ -422,6 +422,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="assets/reveal.js" defer></script>
|
||||||
<script src="assets/forms.js" defer></script>
|
<script src="assets/forms.js" defer></script>
|
||||||
|
<script src="assets/schedule.js" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user