Compare commits
7 Commits
rcp-mobile
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af946d338a | ||
| 2d749776fc | |||
| 351898cdc5 | |||
| b3a24f821b | |||
| 9648214675 | |||
| 8ed8d3bb42 | |||
| a9327760d0 |
80
header-telemetria.php
Normal file
80
header-telemetria.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
$basePath = '';
|
||||
if ($_SERVER['HTTP_HOST'] === 'localhost' || $_SERVER['HTTP_HOST'] === '127.0.0.1') {
|
||||
$basePath = '/magico-prototype';
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" class="light-style layout-navbar-fixed layout-menu-fixed layout-compact" dir="ltr"
|
||||
data-theme="theme-default" data-assets-path="<?= $basePath ?>/assets/"
|
||||
data-template="vertical-menu-template-no-customizer-starter">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
|
||||
|
||||
<title>telemetria.magico</title>
|
||||
|
||||
<meta name="description" content="" />
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="<?= $basePath ?>/assets/img/favicon/favicon.ico" />
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
|
||||
rel="stylesheet" />
|
||||
|
||||
<link rel="stylesheet" href="<?= $basePath ?>/assets/vendor/fonts/boxicons.css" />
|
||||
<!-- <link rel="stylesheet" href="<?= $basePath ?>/assets/vendor/fonts/fontawesome.css" /> -->
|
||||
<!-- <link rel="stylesheet" href="<?= $basePath ?>/assets/vendor/fonts/flag-icons.css" /> -->
|
||||
|
||||
<!-- Core CSS -->
|
||||
<link rel="stylesheet" href="<?= $basePath ?>/assets/vendor/css/rtl/core.css" />
|
||||
<link rel="stylesheet" href="<?= $basePath ?>/assets/vendor/css/rtl/theme-default.css" />
|
||||
<link rel="stylesheet" href="<?= $basePath ?>/assets/css/demo.css" />
|
||||
|
||||
<!-- Vendors CSS -->
|
||||
<link rel="stylesheet" href="<?= $basePath ?>/assets/vendor/libs/perfect-scrollbar/perfect-scrollbar.css" />
|
||||
|
||||
<!-- Page CSS -->
|
||||
|
||||
<!-- Helpers -->
|
||||
<script src="<?= $basePath ?>/assets/vendor/js/helpers.js"></script>
|
||||
<!--! Template customizer & Theme config files MUST be included after core stylesheets and helpers.js in the <head> section -->
|
||||
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
|
||||
<script src="<?= $basePath ?>/assets/js/config.js"></script>
|
||||
|
||||
<?php if (!empty($enablePrototypeComments) && $enablePrototypeComments): ?>
|
||||
<link rel="stylesheet" href="<?= $basePath ?>/assets/css/comments.css">
|
||||
<?php endif; ?>
|
||||
|
||||
<style>
|
||||
@media (min-width: 1200px) {
|
||||
|
||||
.layout-menu-fixed .layout-menu,
|
||||
.layout-menu-fixed-offcanvas .layout-menu {
|
||||
padding-top: 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Layout wrapper -->
|
||||
<div class="layout-wrapper layout-content-navbar">
|
||||
<div class="layout-container">
|
||||
<!-- Menu -->
|
||||
|
||||
<?php include 'menu.php'; ?>
|
||||
<!-- / Menu -->
|
||||
|
||||
<!-- Layout container -->
|
||||
<div class="layout-page pt-0" style="padding-top: 0 !important;">
|
||||
|
||||
<!-- Content wrapper -->
|
||||
<div class="content-wrapper">
|
||||
196
prototype/telemetria/README.MD
Normal file
196
prototype/telemetria/README.MD
Normal file
@@ -0,0 +1,196 @@
|
||||
Opis Biznesowy Aplikacji: telemetry.magico (Telemetry Manager)
|
||||
1. Wstęp i Cel Projektu
|
||||
telemetry.magico to nowoczesna, przemysłowa platforma klasy IoT (Internet of Things) oraz czasu rzeczywistego (Real-Time Monitoring), zintegrowana z ekosystemem biznesowym magico.pro.
|
||||
|
||||
Głównym celem aplikacji jest agregacja, wizualizacja oraz analiza danych telemetrycznych pochodzących z rozproszonych urządzeń pomiarowych, sensorów i sterowników przemysłowych. Kluczowym założeniem biznesowym jest pełna agnostyczność danych (generyczność) – system projektowany jest w taki sposób, aby rodzaj monitorowanej infrastruktury (np. poziom gazu w zbiornikach, mikroklimat w kurnikach, parametry pracy maszyn HVAC) nie wpływał na strukturę rdzenia aplikacji, a jedynie na warstwę prezentacji danych i konfiguracji urządzeń.
|
||||
|
||||
2. Grupa Docelowa i Zastosowanie Biznesowe
|
||||
Aplikacja odpowiada na potrzeby przedsiębiorstw zarządzających rozproszoną infrastrukturą techniczną. Przykładowe scenariusze wdrożeniowe obejmują:
|
||||
|
||||
Sektor Energetyczny / Gazowy (Pierwsze wdrożenie): Monitoring poziomu napełnienia zbiorników LPG/CNG, ciśnienia oraz detekcja anomalii i wycieków. Optimale planowanie logistyki dostaw surowca.
|
||||
|
||||
Agrotechnika (Smart Farming): Monitorowanie temperatury, wilgotności, poziomu CO2 czy zużycia paszy w obiektach inwentarskich (kurniki, chłodnie).
|
||||
|
||||
Smart Building & Industry: Monitorowanie parametrów pracy maszyn, zużycia energii elektrycznej, statusów online/offline urządzeń produkcyjnych.
|
||||
|
||||
3. Kluczowe Korzyści Biznesowe (Value Proposition)
|
||||
Redukcja kosztów operacyjnych: Automatyczna kontrola stanu urządzeń eliminuje konieczność fizycznych i rutynowych inspekcji obiektów przez pracowników.
|
||||
|
||||
Zapobieganie awariom i przestojom: System wczesnego ostrzegania informuje o stanach krytycznych (np. drastyczny spadek ciśnienia, odcięcie zasilania), zanim wpłyną one na ciągłość biznesową.
|
||||
|
||||
Optymalizacja logistyczna: Na podstawie trendów zużycia (np. gazu) system pozwala precyzyjnie planować terminy kolejnych dostaw lub serwisów.
|
||||
|
||||
Skalowalność środowiska magico.pro: Moduł telemetryczny wymienia dane z innymi aplikacjami w ekosystemie (np. generowanie automatycznych ticketów w helpdesk.magico przy awarii sensora lub fakturowanie zużycia przez invoice.magico).
|
||||
|
||||
4. Architektura Funkcjonalna Systemu (Struktura Modułowa)
|
||||
Aplikacja logicznie dzieli się na 5 głównych obszarów funkcjonalnych, które stanowią podstawę do zaprojektowania interfejsu użytkownika (UI):
|
||||
|
||||
4.1. Dashboard (Pulpit Zarządczy)
|
||||
Centrum dowodzenia menedżera operacyjnego. Zapewnia natychmiastowy wgląd w kondycję całego parku maszynowego/sieci czujników bez konieczności przeklikiwania się przez poszczególne obiekty.
|
||||
|
||||
KPI Statusów: Zagregowane liczniki urządzeń (Wszystkie, Online, Offline, W trakcie serwisu).
|
||||
|
||||
Sekcja Alertów: Lista obiektów, które przekroczyły zdefiniowane normy bezpieczeństwa (kolorystyka czerwona/pomarańczowa).
|
||||
|
||||
Geolokalizacja: Mapa z oznaczonymi punktami pomiarowymi (opcjonalnie, na podstawie koordynatów GPS wysyłanych przez minikomputery).
|
||||
|
||||
4.2. Moduł: Urządzenia (Infrastruktura i Integracje)
|
||||
Miejsce zarządzania fizycznymi punktami pomiarowymi. Moduł opiera się na sztywnych definicjach typów urządzeń, pisanych przez deweloperów.
|
||||
|
||||
Katalog urządzeń: Przeglądanie, filtrowanie po lokalizacji i typie.
|
||||
|
||||
Kreator urządzeń (Provisioning): 1. Wybór predefiniowanego typu (np. Sensor-LPG-v2).
|
||||
2. Dynamiczny formularz konfiguracyjny (system podpowiada pola unikalne dla typu: np. maksymalna pojemność w litrach, adres IP, częstotliwość raportowania).
|
||||
3. Wygenerowanie bezpiecznego tokenu dostępowego API (Bearer Token) dla minikomputera brzegowego.
|
||||
|
||||
4.3. Moduł: Aktualny Stan (Live Monitor)
|
||||
Narzędzie dedykowane dla operatorów i techników. Pozwala na podgląd zachowania wybranego obiektu w czasie rzeczywistym.
|
||||
|
||||
Dynamiczny Grid Interfejsu: Layout dopasowuje się do typu urządzenia. Jeśli urządzenie mierzy 2 parametry, wyświetlane są 2 kafelki/wskaźniki zegarowe (gauges). Jeśli mierzy 10 parametrów – interfejs generuje adekwatną liczbę kontenerów.
|
||||
|
||||
Wykresy Live: Wizualizacja trendu z ostatnich minut/godzin z automatycznym odświeżaniem danych.
|
||||
|
||||
4.4. Moduł: Raporty i Historia (Analityka)
|
||||
Narzędzie służące do retrospektywnej analizy danych i wyciągania wniosków biznesowych.
|
||||
|
||||
Agregacja danych: Filtrowanie według ram czasowych (dni, tygodnie, miesiące) oraz konkretnych parametrów.
|
||||
|
||||
Eksport danych: Generowanie zestawień do formatów analitycznych (CSV, Excel) w celu dalszej obróbki lub audytów.
|
||||
|
||||
Analiza trendów: Narzędzia wykresów liniowych umożliwiające nałożenie na siebie różnych metryk w celu znalezienia korelacji (np. wpływ temperatury otoczenia na ciśnienie w zbiorniku).
|
||||
|
||||
4.5. Moduł: Ustawienia (Administracja)
|
||||
Konfiguracja zachowania systemu oraz progów bezpieczeństwa.
|
||||
|
||||
Zarządzanie alertami (Progi krytyczne): Definiowanie reguł biznesowych typu: „Jeśli parametr gas_level na dowolnym urządzeniu typu gas_tank spadnie poniżej 15%, wyślij powiadomienie”.
|
||||
|
||||
Zarządzanie uprawnieniami: Integracja z systemem uprawnień magico.pro (kto może tylko przeglądać wykresy, a kto ma prawo dodawać nowe urządzenia).
|
||||
|
||||
5. Model Operacyjny (Jak to działa w praktyce?)
|
||||
Warstwa Fizyczna (Edge): Przy zbiorniku lub w kurniku znajduje się minikomputer (np. sterownik przemysłowy, Raspberry Pi, brama Teltonika). Odpytuje on lokalne czujniki fizyczne (poprzez Modbus, OneWire itp.).
|
||||
|
||||
Warstwa Transmisyjna (API): Minikomputer formatuje uzyskane dane do ujednoliconego formatu JSON i za pomocą zapytania HTTP POST (zabezpieczonego tokenem) wypycha je do chmury telemetry.magico.
|
||||
|
||||
Warstwa Przetwarzania (Core): Aplikacja identyfikuje urządzenie po tokenie, sprawdza jakie metryki zostały przesłane, zapisuje je w bazie danych serii czasowych (Time-Series format) i ewentualnie uruchamia procesy sprawdzania alertów.
|
||||
|
||||
Warstwa Prezentacji (UI): Użytkownik końcowy widzi przetworzone, czytelne dane na wykresach i widgetach w panelu HTML.
|
||||
|
||||
|
||||
# Dokumentacja Funkcjonalna Modułów: telemetry.magico
|
||||
|
||||
Ten dokument zawiera szczegółowy opis wymagań, logiki biznesowej oraz struktury interfejsu (UI) dla poszczególnych modułów aplikacji. Służy jako bezpośredni brief do przygotowania makiety HTML (Bootstrap) w ramach środowiska magico.pro.
|
||||
|
||||
---
|
||||
|
||||
## 1. Moduł: Dashboard (Pulpit Zarządczy)
|
||||
|
||||
### 1.1. Cel biznesowy
|
||||
Zapewnienie użytkownikowi (zarządcy, dyspozytorowi) błyskawicznej oceny stanu technicznego i operacyjnego całej infrastruktury urządzeń bez konieczności wchodzenia w szczegóły każdego z nich.
|
||||
|
||||
### 1.2. Struktura i Układ UI (Makieta HTML)
|
||||
* **Górny pasek statystyk (KPI Cards):** Cztery małe, sąsiadujące kafelki (`.card`) w jednym rzędzie:
|
||||
* **Wszystkie urządzenia:** Łączna liczba zarejestrowanych maszyn (kolor neutralny / podstawowy magico).
|
||||
* **Online:** Urządzenia, od których odebrano dane w zdefiniowanym oknie czasowym (zielony badge/tekst, ikona sygnału).
|
||||
* **Offline:** Urządzenia, które utraciły łączność (czerwony badge/tekst, ikona wykrzyknika).
|
||||
* **Aktywne Alerty:** Liczba urządzeń, na których aktualnie przekroczone są parametry krytyczne (pomarańczowy/czerwony puls).
|
||||
* **Główna sekcja (Układ dwukolumnowy - `row` z podziałem `col-md-8` i `col-md-4`):**
|
||||
* **Kolumna Lewa (Szeroka): Tabela "Krytyczne stany i alerty".** Lista urządzeń wymagających natychmiastowej uwagi. Kolumny: Nazwa urządzenia, Typ, Lokalizacja, Parametr przekroczony (np. *Poziom gazu: 8%*), Czas wystąpienia, Akcja (Przejdź do Live Monitora).
|
||||
* **Kolumna Prawa (Wąska): Widget "Ostatnie zdarzenia" (Activity Log).** Lista typu timeline (oś czasu) pokazująca ostatnie zmiany statusów (np. *10:15 - Urządzenie 'Zbiornik 3' przeszło w tryb OFFLINE*, *09:42 - Urządzenie 'Kurnik B' zarejestrowało spadek temperatury*).
|
||||
|
||||
### 1.3. Logika i Zachowanie (JS / Backend)
|
||||
* **Definicja Offline:** Urządzenie zmienia status na Offline automatycznie, jeśli czas od ostatniego odczytu (`last_seen_at`) jest większy niż dwukrotność jego zadeklarowanego interwału wysyłki danych.
|
||||
* **Interakcja:** Kliknięcie w dowolny wiersz na liście alertów przenosi użytkownika bezpośrednio do widoku "Aktualny stan" (Live Monitor) z wybranym już tym konkretnym urządzeniem.
|
||||
|
||||
---
|
||||
|
||||
## 2. Moduł: Urządzenia (Device Management & Provisioning)
|
||||
|
||||
### 2.1. Cel biznesowy
|
||||
Zarządzanie bazą fizycznych urządzeń oraz procesem ich bezpiecznego uwierzytelniania w chmurze magico.pro.
|
||||
|
||||
### 2.2. Struktura i Układ UI (Makieta HTML)
|
||||
Moduł składa się z dwóch głównych widoków: **Listy urządzeń** oraz **Kreatora dodawania**.
|
||||
|
||||
#### Widok: Lista Urządzeń (Tabela główna)
|
||||
* **Filtry nad tabelą:** Wyszukiwarka tekstowa, dropdown z filtrem po "Typie urządzenia" (Wszystkie, Zbiornik Gazu, Kurnik itp.) oraz filtr statusu (Online/Offline).
|
||||
* **Tabela danych:**
|
||||
* Nazwa (np. *Zbiornik LPG #1 - Stalowa Wola, ul. Przemysłowa*).
|
||||
* Typ urządzenia (Badge z ikoną dedykowaną dla typu, np. ikona płomienia dla gazu, ikona termometru dla kurnika).
|
||||
* Ostatni kontakt (Względny format daty, np. *Przed 2 min*, *3 dni temu*).
|
||||
* Status (`.badge-success` dla Online, `.badge-danger` dla Offline).
|
||||
* Akcje: Przycisk "Podgląd Live" (ikona oka), "Edycja" (ikona ołówka).
|
||||
|
||||
#### Widok: Kreator dodawania (Step-by-Step Wizard)
|
||||
Zrealizowany za pomocą zakładek (`nav-tabs` lub niestandardowy stepper komponentu Bootstrap).
|
||||
* **Krok 1: Wybór typu urządzenia.** Kafelki z ikonami i opisami predefiniowanych integracji (np. `Karta 1: Zbiornik Gazu standard (Modbus/LPG)`, `Karta 2: Sterownik Mikroklimatu Kurnika v1`). Użytkownik klika jeden z nich i przechodzi dalej.
|
||||
* **Krok 2: Konfiguracja parametrów (Dynamiczny formularz).** Pola ładują się w zależności od wyboru w Kroku 1:
|
||||
* *Dla Zbiornika Gazu:* Pole tekstowe: Pojemność nominalna (litry), Maksymalne ciśnienie bezpieczne (bar), Stały adres IP minikomputera (opcjonalnie).
|
||||
* *Dla Kurnika:* Pole liczbowe: Liczba stref grzewczych, Liczba wentylatorów, Interwał próbkowania (w sekundach).
|
||||
* **Krok 3: Generowanie tokenu.** Ekran podsumowania, na którym system wyświetla wygenerowany unikalny klucz API (`Device API Token`). Zawiera przycisk "Kopiuj do schowka".
|
||||
|
||||
### 2.3. Logika i Zachowanie (JS / Backend)
|
||||
* **Bezpieczeństwo tokenów:** Token API (`api_token`) jest widoczny w pełnej krasie tylko raz – podczas tworzenia urządzenia (Krok 3). Potem w bazie jest haszowany lub maskowany (np. `mag_tlm_•••••••••1a2b`).
|
||||
* **Dynamiczne renderowanie:** W makiety HTML warto wbudować mechanizm (np. proste przełączanie klasami `d-none`), który zasymuluje, jak zmienia się formularz w Kroku 2 w zależności od wybranego w Kroku 1 typu urządzenia.
|
||||
|
||||
---
|
||||
|
||||
## 3. Moduł: Aktualny Stan (Live Monitor)
|
||||
|
||||
### 3.1. Cel biznesowy
|
||||
Umożliwienie technikowi lub operatorowi zdalnego podglądu parametrów pracy wybranego obiektu w trybie "tu i teraz" w celu weryfikacji awarii lub kontroli procesów.
|
||||
|
||||
### 3.2. Struktura i Układ UI (Makieta HTML)
|
||||
* **Górny pasek kontekstowy:** Duży Dropdown (`<select>` z obsługą wyszukiwania, np. Select2 w Bootstrapie) do wyboru urządzenia. Obok dropdownu wyświetla się duży badge aktualnego statusu wybranego urządzenia (Online/Offline) oraz data i godzina ostatniego odebranego pakietu danych.
|
||||
* **Siatka wskaźników (Dynamic Grid):** Rząd kafelków (`row-cols-1 row-cols-md-3 g-4`), gdzie każdy kafelek prezentuje **jedną metrykę/kanał danych**.
|
||||
* *Przykład komponentu wskaźnika:* Duża wartość numeryczna w centrum (np. **22.4 °C** lub **74.5 %**), pod nią nazwa metryki ("Temperatura strefa A", "Poziom napełnienia"), a na dole mały badge informujący o trendzie w ciągu ostatniej godziny (np. zielona strzałka w górę "Wzrost o 1.2%").
|
||||
* **Dolna sekcja: Mini-wykres Live.** Wykres liniowy (zajmujący pełną szerokość `col-12`), pokazujący dane z wybranej kluczowej metryki z ostatnich 2 godzin, odświeżany automatycznie bez przeładowania strony.
|
||||
|
||||
### 3.3. Logika i Zachowanie (JS / Backend)
|
||||
* **Agnostyczność interfejsu:** Layout HTML musi być przygotowany na to, że kafelków wskaźników może być 2 (dla prostego zbiornika), a może być 12 (dla rozbudowanego kurnika). Kafelki powinny płynnie zawijać się do kolejnych rzędów dzięki klasom flexbox Bootstrapa.
|
||||
* **Odświeżanie danych:** Widok symuluje połączenie WebSocket lub regularny odpytywacz (Polling AJAX) co 10-30 sekund, który aktualizuje wartości liczbowe w kafelkach oraz dopisuje nowy punkt na mini-wykresie.
|
||||
|
||||
---
|
||||
|
||||
## 4. Moduł: Raporty i Historia (Analityka Historyczna)
|
||||
|
||||
### 4.1. Cel biznesowy
|
||||
Dostarczanie twardych danych analitycznych do celów optymalizacyjnych (planowanie dostaw gazu, audyty warunków chowu drobiu) oraz eksport danych do zewnętrznych systemów lub arkuszy kalkulacyjnych.
|
||||
|
||||
### 4.2. Struktura i Układ UI (Makieta HTML)
|
||||
* **Pasek filtrów (Górny horyzontalny formularz card):**
|
||||
* Wybór urządzenia (Dropdown).
|
||||
* Wybór metryk (Multiselect checkbox / dropdown – np. użytkownik chce zobaczyć tylko poziom gazu, ignorując napięcie baterii minikomputera).
|
||||
* Zakres dat (Komponent Date-Range Picker: Od, Do, wraz z szybkimi filtrami: *Dzisiaj, Ostatnie 7 dni, Ten miesiąc*).
|
||||
* Przycisk akcji: "Generuj raport" (niebieski) oraz "Eksportuj do CSV" (zielony).
|
||||
* **Sekcja Główna - Wykres Analityczny:** Duży kontener wykresu liniowego (np. przy użyciu biblioteki Chart.js wbudowanej w layout Bootstrap). Wykres musi obsługiwać wiele osi Y, jeśli użytkownik porównuje różne jednostki (np. lewa oś dla temperatury w `°C`, prawa dla wilgotności w `%`).
|
||||
* **Sekcja Dolna - Tabela Surowych Danych:** Tabela z paginacją (np. stylizowana pod DataTables). Kolumny: Data i godzina (Timestamp), Nazwa metryki, Wartość, Jednostka.
|
||||
|
||||
### 4.3. Logika i Zachowanie (JS / Backend)
|
||||
* **Agregacja danych (Downsampling):** Ponieważ dane telemetryczne przyrastają w ogromnym tempie, backend przy raportach z długich okresów (np. 3 miesiące) nie może przesłać milionów rekordów do JS. System musi agregować dane w locie (np. średnia godzinowa lub średnia dobowa) w zależności od wybranego zakresu czasu.
|
||||
|
||||
---
|
||||
|
||||
## 5. Moduł: Ustawienia (Settings & Alert Rules)
|
||||
|
||||
### 5.1. Cel biznesowy
|
||||
Konfiguracja zachowań automatycznych systemu, w tym definiowanie progów bezpieczeństwa dla zbieranych danych.
|
||||
|
||||
### 5.2. Struktura i Układ UI (Makieta HTML)
|
||||
Zorganizowany w formie pionowych zakładek (List-group tabs po lewej stronie, zawartość po prawej).
|
||||
|
||||
#### Zakładka 1: Reguły Alertów (Alerts Configuration)
|
||||
* **Lista aktywnych reguł:** Tabela pokazująca zdefiniowane progi.
|
||||
* **Przycisk "Dodaj nową regułę"**, który otwiera modal (okno pop-up):
|
||||
* Dropdown: "Jeśli urządzenie typu..." (Wybór typu, np. *Zbiornik Gazu*).
|
||||
* Dropdown: "...zarejestruje parametr..." (Dynamiczna lista metryk dla typu, np. *gas_level*).
|
||||
* Dropdown warunku: (Mniejsze niż, Większe niż, Równe).
|
||||
* Pole numeryczne: Wartość progu (np. *15*).
|
||||
* Sekcja akcji: Checkboxy "Wyślij e-mail do administratora", "Załóż ticket w helpdesk.magico".
|
||||
|
||||
#### Zakładka 2: Globalna Konfiguracja Telemetrii
|
||||
* Formularze konfiguracyjne:
|
||||
* Czas retencji danych (Dropdown: *Przechowuj surowe dane przez: 3 miesiące / 6 miesięcy / rok*).
|
||||
* Domyślny interwał sprawdzania statusów offline (np. *Co ile minut system ma weryfikować brak kontaktu z minikomputerami*).
|
||||
|
||||
### 5.3. Logika i Zachowanie (JS / Backend)
|
||||
* **Integracja wewnątrzmagico:** Powiązanie akcji alertu z modułem `helpdesk.magico` musi opierać się na wewnętrznym API ekosystemu magico.pro. W makiecie należy przewidzieć elementy UI (np. selecty grup użytkowników, kategorie ticketów), które odzwierciedlają tę integrację.
|
||||
174
prototype/telemetria/devices.php
Normal file
174
prototype/telemetria/devices.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include '../../header-telemetria.php';
|
||||
?>
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="d-flex justify-content-between align-items-center py-3 mb-4">
|
||||
<h4 class="fw-bold mb-0">
|
||||
Telemetria <span class="text-muted fw-light">/ Urządzenia (Sprzęt)</span>
|
||||
</h4>
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addDeviceModal">
|
||||
<i class="bx bx-plus me-1"></i> Dodaj urządzenie
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tabela Urządzeń -->
|
||||
<div class="card">
|
||||
<div class="table-responsive text-nowrap">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Adres MAC</th>
|
||||
<th>Nazwa własna</th>
|
||||
<th>Status</th>
|
||||
<th>Ostatnia aktywność</th>
|
||||
<th>Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table-border-bottom-0">
|
||||
<tr>
|
||||
<td><strong>1024</strong></td>
|
||||
<td class="text-monospace">00:1B:44:11:3A:B7</td>
|
||||
<td>Nadajnik Główny #1</td>
|
||||
<td><span class="badge bg-label-success"><i class="bx bx-wifi me-1"></i> Online</span></td>
|
||||
<td>Dzisiaj, 14:52:10</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-icon btn-outline-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#editDeviceModal" title="Edytuj">
|
||||
<i class="bx bx-pencil"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-icon btn-outline-danger btn-sm" title="Usuń">
|
||||
<i class="bx bx-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>1025</strong></td>
|
||||
<td class="text-monospace">00:1B:44:11:4F:C1</td>
|
||||
<td>Moduł zapasowy 1</td>
|
||||
<td><span class="badge bg-label-success"><i class="bx bx-wifi me-1"></i> Online</span></td>
|
||||
<td>Dzisiaj, 14:50:05</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-icon btn-outline-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#editDeviceModal" title="Edytuj">
|
||||
<i class="bx bx-pencil"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-icon btn-outline-danger btn-sm" title="Usuń">
|
||||
<i class="bx bx-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>1026</strong></td>
|
||||
<td class="text-monospace">00:1B:44:22:9C:E2</td>
|
||||
<td>Sterownik Klimatu Alpha</td>
|
||||
<td><span class="badge bg-label-danger"><i class="bx bx-wifi-off me-1"></i> Offline</span></td>
|
||||
<td>Wczoraj, 18:22:00</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-icon btn-outline-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#editDeviceModal" title="Edytuj">
|
||||
<i class="bx bx-pencil"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-icon btn-outline-danger btn-sm" title="Usuń">
|
||||
<i class="bx bx-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>1027</strong></td>
|
||||
<td class="text-monospace">00:1B:44:55:00:AA</td>
|
||||
<td>Nowy czujnik testowy</td>
|
||||
<td><span class="badge bg-label-secondary"><i class="bx bx-time me-1"></i> Brak danych</span></td>
|
||||
<td>-</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-icon btn-outline-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#editDeviceModal" title="Edytuj">
|
||||
<i class="bx bx-pencil"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-icon btn-outline-danger btn-sm" title="Usuń">
|
||||
<i class="bx bx-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Dodawania Urządzenia -->
|
||||
<div class="modal fade" id="addDeviceModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Rejestracja nowego urządzenia</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-primary" role="alert">
|
||||
<i class="bx bx-info-circle me-1"></i> Numer ID zostanie wygenerowany automatycznie po zapisaniu.
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Adres MAC sprzętu <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control text-uppercase" placeholder="np. 00:1B:44:11:3A:B7">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nazwa własna</label>
|
||||
<input type="text" class="form-control" placeholder="np. Nadajnik Północ">
|
||||
<div class="form-text">Przyjazna nazwa ułatwiająca identyfikację.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Notatki</label>
|
||||
<textarea class="form-control" rows="3" placeholder="Dodatkowe informacje serwisowe, uwagi..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Anuluj</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Zapisz urządzenie</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Edycji Urządzenia -->
|
||||
<div class="modal fade" id="editDeviceModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Edycja urządzenia</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ID</label>
|
||||
<input type="text" class="form-control" value="1024" disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Adres MAC sprzętu <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control text-uppercase" value="00:1B:44:11:3A:B7" disabled>
|
||||
<div class="form-text">Adresu fizycznego nie można zmienić. Skontaktuj się ze wsparciem.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nazwa własna</label>
|
||||
<input type="text" class="form-control" value="Nadajnik Główny #1">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Notatki</label>
|
||||
<textarea class="form-control" rows="3" placeholder="Dodatkowe informacje serwisowe, uwagi...">Wymieniona bateria 10.02.2025.</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer d-flex justify-content-between">
|
||||
<button type="button" class="btn btn-outline-danger">Resetuj Token</button>
|
||||
<div>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Anuluj</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Zapisz zmiany</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-backdrop fade"></div>
|
||||
|
||||
<?php include '../../footer.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
326
prototype/telemetria/index.php
Normal file
326
prototype/telemetria/index.php
Normal file
@@ -0,0 +1,326 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include '../../header-telemetria.php';
|
||||
?>
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="d-flex justify-content-between align-items-center py-3 mb-4">
|
||||
<h4 class="fw-bold mb-0">
|
||||
Telemetria <span class="text-muted fw-light">/ Dashboard główny</span>
|
||||
</h4>
|
||||
<div class="d-flex align-items-center">
|
||||
<button type="button" class="btn btn-primary me-2">
|
||||
<i class="bx bx-plus me-1"></i> Nowy dashboard
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary me-2" id="toggleDashboardEditBtn" onclick="toggleDashboardEditMode()">
|
||||
<i class="bx bx-customize me-1"></i> Tryb edycji
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#shareModal">
|
||||
<i class="bx bx-share-alt me-1"></i> Udostępnij
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Instalacja 1: Prawidłowa -->
|
||||
<div class="col-xl-4 col-lg-6 col-md-6">
|
||||
<div
|
||||
class="card h-100 shadow-sm border-0 border-top border-warning border-3 hover-border-primary transition-all position-relative">
|
||||
<div class="edit-mode-overlay d-none position-absolute top-0 start-0 w-100 h-100 bg-white bg-opacity-75 zindex-2 d-flex flex-column justify-content-center align-items-center border border-2 border-dashed border-primary rounded" style="cursor: move;">
|
||||
<i class="bx bx-move fs-1 text-primary mb-2"></i>
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="this.closest('.col-xl-4').remove()"><i class="bx bx-trash"></i> Usuń z widoku</button>
|
||||
</div>
|
||||
<div class="card-header d-flex justify-content-between align-items-start border-bottom pb-3">
|
||||
<div>
|
||||
<h6 class="mb-1 fw-bold text-dark">Zbiornik LPG #1</h6>
|
||||
<small class="text-muted"><i class="bx bx-map"></i> Stalowa Wola, ul. Przemysłowa 5</small>
|
||||
</div>
|
||||
<span class="badge bg-success"><i class="bx bx-wifi"></i> ONLINE</span>
|
||||
</div>
|
||||
<div class="card-body pt-4 d-flex">
|
||||
<div class="me-4">
|
||||
<div class="tank-container position-relative" style="width: 70px; height: 160px;">
|
||||
<svg viewBox="0 0 100 300" width="100%" height="100%">
|
||||
<path d="M 35 260 L 30 295 M 65 260 L 70 295" stroke="#a1acb8" stroke-width="6"
|
||||
stroke-linecap="round" />
|
||||
<line x1="20" y1="295" x2="80" y2="295" stroke="#a1acb8" stroke-width="4"
|
||||
stroke-linecap="round" />
|
||||
<path d="M 20 40 A 30 20 0 0 1 80 40 L 80 250 A 30 20 0 0 1 20 250 Z" fill="#f8f9fa"
|
||||
stroke="#d9dee3" stroke-width="4" />
|
||||
<clipPath id="fillClip1">
|
||||
<path d="M 22 42 A 28 18 0 0 1 78 42 L 78 248 A 28 18 0 0 1 22 248 Z" />
|
||||
</clipPath>
|
||||
<defs>
|
||||
<linearGradient id="gasGradient1" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#ffab00" />
|
||||
<stop offset="100%" stop-color="#e69a00" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g clip-path="url(#fillClip1)">
|
||||
<rect x="0" y="120" width="100" height="250" fill="url(#gasGradient1)" />
|
||||
</g>
|
||||
<path d="M 20 90 A 30 10 0 0 0 80 90" stroke="#d9dee3" stroke-width="2" fill="none" />
|
||||
<path d="M 20 150 A 30 10 0 0 0 80 150" stroke="#d9dee3" stroke-width="2" fill="none" />
|
||||
<path d="M 20 210 A 30 10 0 0 0 80 210" stroke="#d9dee3" stroke-width="2" fill="none" />
|
||||
</svg>
|
||||
<div class="position-absolute top-50 start-50 translate-middle text-center"
|
||||
style="margin-top: -10px;">
|
||||
<h5 class="mb-0 fw-bolder text-dark"
|
||||
style="text-shadow: 0px 0px 5px rgba(255,255,255,0.8);">60%</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow-1 d-flex flex-column justify-content-center">
|
||||
<div class="mb-3">
|
||||
<span class="text-muted d-block small text-uppercase fw-bold mb-1">Objętość</span>
|
||||
<h4 class="mb-0 text-dark">1 620 <small class="text-muted fs-6 fw-normal">l</small></h4>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<span class="text-muted d-block small text-uppercase fw-bold mb-1">Ciśnienie</span>
|
||||
<h5 class="mb-0 text-dark">5.24 <small class="text-muted fs-6 fw-normal">bar</small></h5>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted d-block small text-uppercase fw-bold mb-1">Bateria</span>
|
||||
<h6 class="mb-0 text-dark"><i class='bx bx-battery text-success me-1'></i> 12.4 V</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-top">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted"><i class="bx bx-time-five"></i> Odczyt: 2 min temu</small>
|
||||
<a href="installation-live.php" class="btn btn-sm btn-primary">Szczegóły</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Instalacja 2: Alarm Niski Poziom -->
|
||||
<div class="col-xl-4 col-lg-6 col-md-6">
|
||||
<div class="card h-100 shadow-sm border-0 border-top border-danger border-3 position-relative">
|
||||
<div class="edit-mode-overlay d-none position-absolute top-0 start-0 w-100 h-100 bg-white bg-opacity-75 zindex-2 d-flex flex-column justify-content-center align-items-center border border-2 border-dashed border-primary rounded" style="cursor: move;">
|
||||
<i class="bx bx-move fs-1 text-primary mb-2"></i>
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="this.closest('.col-xl-4').remove()"><i class="bx bx-trash"></i> Usuń z widoku</button>
|
||||
</div>
|
||||
<div class="card-header d-flex justify-content-between align-items-start border-bottom pb-3">
|
||||
<div>
|
||||
<h6 class="mb-1 fw-bold text-dark">Zbiornik LPG #2</h6>
|
||||
<small class="text-muted"><i class="bx bx-map"></i> Rzeszów, ul. Podkarpacka 12</small>
|
||||
</div>
|
||||
<span class="badge bg-danger"><i class="bx bx-error-circle"></i> ALARM</span>
|
||||
</div>
|
||||
<div class="card-body pt-4 d-flex">
|
||||
<div class="me-4">
|
||||
<div class="tank-container position-relative" style="width: 70px; height: 160px;">
|
||||
<svg viewBox="0 0 100 300" width="100%" height="100%">
|
||||
<path d="M 35 260 L 30 295 M 65 260 L 70 295" stroke="#a1acb8" stroke-width="6"
|
||||
stroke-linecap="round" />
|
||||
<line x1="20" y1="295" x2="80" y2="295" stroke="#a1acb8" stroke-width="4"
|
||||
stroke-linecap="round" />
|
||||
<path d="M 20 40 A 30 20 0 0 1 80 40 L 80 250 A 30 20 0 0 1 20 250 Z" fill="#f8f9fa"
|
||||
stroke="#d9dee3" stroke-width="4" />
|
||||
<clipPath id="fillClip2">
|
||||
<path d="M 22 42 A 28 18 0 0 1 78 42 L 78 248 A 28 18 0 0 1 22 248 Z" />
|
||||
</clipPath>
|
||||
<defs>
|
||||
<linearGradient id="gasGradient2" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#ff3e1d" />
|
||||
<stop offset="100%" stop-color="#cc3217" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g clip-path="url(#fillClip2)">
|
||||
<rect x="0" y="215" width="100" height="250" fill="url(#gasGradient2)" />
|
||||
</g>
|
||||
<path d="M 20 90 A 30 10 0 0 0 80 90" stroke="#d9dee3" stroke-width="2" fill="none" />
|
||||
<path d="M 20 150 A 30 10 0 0 0 80 150" stroke="#d9dee3" stroke-width="2" fill="none" />
|
||||
<path d="M 20 210 A 30 10 0 0 0 80 210" stroke="#d9dee3" stroke-width="2" fill="none" />
|
||||
</svg>
|
||||
<div class="position-absolute top-50 start-50 translate-middle text-center"
|
||||
style="margin-top: 30px;">
|
||||
<h5 class="mb-0 fw-bolder text-danger"
|
||||
style="text-shadow: 0px 0px 5px rgba(255,255,255,0.8);">12%</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow-1 d-flex flex-column justify-content-center">
|
||||
<div class="mb-3">
|
||||
<span class="text-muted d-block small text-uppercase fw-bold mb-1">Objętość</span>
|
||||
<h4 class="mb-0 text-danger fw-bold">324 <small class="text-muted fs-6 fw-normal">l</small>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<span class="text-muted d-block small text-uppercase fw-bold mb-1">Ciśnienie</span>
|
||||
<h5 class="mb-0 text-dark">3.10 <small class="text-muted fs-6 fw-normal">bar</small></h5>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted d-block small text-uppercase fw-bold mb-1">Bateria</span>
|
||||
<h6 class="mb-0 text-dark"><i class='bx bx-battery text-success me-1'></i> 12.3 V</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-top">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-danger fw-bold"><i class="bx bx-error"></i> Niski poziom gazu!</small>
|
||||
<a href="installation-live.php" class="btn btn-sm btn-outline-danger">Zarządzaj</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Instalacja 3: Offline -->
|
||||
<div class="col-xl-4 col-lg-6 col-md-6">
|
||||
<div class="card h-100 shadow-sm border-0 border-top border-secondary border-3 position-relative">
|
||||
<div class="edit-mode-overlay d-none position-absolute top-0 start-0 w-100 h-100 bg-white bg-opacity-75 zindex-2 d-flex flex-column justify-content-center align-items-center border border-2 border-dashed border-primary rounded" style="cursor: move;">
|
||||
<i class="bx bx-move fs-1 text-primary mb-2"></i>
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="this.closest('.col-xl-4').remove()"><i class="bx bx-trash"></i> Usuń z widoku</button>
|
||||
</div>
|
||||
<div class="card-header d-flex justify-content-between align-items-start border-bottom pb-3">
|
||||
<div>
|
||||
<h6 class="mb-1 fw-bold text-dark">Zbiornik LPG #3</h6>
|
||||
<small class="text-muted"><i class="bx bx-map"></i> Lublin, Al. Kraśnicka 100</small>
|
||||
</div>
|
||||
<span class="badge bg-secondary"><i class="bx bx-wifi-off"></i> OFFLINE</span>
|
||||
</div>
|
||||
<div class="card-body pt-4 d-flex">
|
||||
<div class="me-4" style="opacity: 0.5; filter: grayscale(100%);">
|
||||
<div class="tank-container position-relative" style="width: 70px; height: 160px;">
|
||||
<svg viewBox="0 0 100 300" width="100%" height="100%">
|
||||
<path d="M 35 260 L 30 295 M 65 260 L 70 295" stroke="#a1acb8" stroke-width="6"
|
||||
stroke-linecap="round" />
|
||||
<line x1="20" y1="295" x2="80" y2="295" stroke="#a1acb8" stroke-width="4"
|
||||
stroke-linecap="round" />
|
||||
<path d="M 20 40 A 30 20 0 0 1 80 40 L 80 250 A 30 20 0 0 1 20 250 Z" fill="#f8f9fa"
|
||||
stroke="#d9dee3" stroke-width="4" />
|
||||
<clipPath id="fillClip3">
|
||||
<path d="M 22 42 A 28 18 0 0 1 78 42 L 78 248 A 28 18 0 0 1 22 248 Z" />
|
||||
</clipPath>
|
||||
<defs>
|
||||
<linearGradient id="gasGradient3" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#8592a3" />
|
||||
<stop offset="100%" stop-color="#697a8d" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g clip-path="url(#fillClip3)">
|
||||
<rect x="0" y="60" width="100" height="250" fill="url(#gasGradient3)" />
|
||||
</g>
|
||||
<path d="M 20 90 A 30 10 0 0 0 80 90" stroke="#d9dee3" stroke-width="2" fill="none" />
|
||||
<path d="M 20 150 A 30 10 0 0 0 80 150" stroke="#d9dee3" stroke-width="2" fill="none" />
|
||||
<path d="M 20 210 A 30 10 0 0 0 80 210" stroke="#d9dee3" stroke-width="2" fill="none" />
|
||||
</svg>
|
||||
<div class="position-absolute top-50 start-50 translate-middle text-center"
|
||||
style="margin-top: -30px;">
|
||||
<h5 class="mb-0 fw-bolder text-dark"
|
||||
style="text-shadow: 0px 0px 5px rgba(255,255,255,0.8);">85%</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow-1 d-flex flex-column justify-content-center">
|
||||
<div class="mb-3">
|
||||
<span class="text-muted d-block small text-uppercase fw-bold mb-1">Objętość
|
||||
(Ostatnia)</span>
|
||||
<h4 class="mb-0 text-muted">2 295 <small class="text-muted fs-6 fw-normal">l</small></h4>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<span class="text-muted d-block small text-uppercase fw-bold mb-1">Ciśnienie
|
||||
(Ostatnie)</span>
|
||||
<h5 class="mb-0 text-muted">6.12 <small class="text-muted fs-6 fw-normal">bar</small></h5>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted d-block small text-uppercase fw-bold mb-1">Bateria</span>
|
||||
<h6 class="mb-0 text-muted"><i class='bx bx-battery text-danger me-1'></i> 10.5 V (Low)</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-top">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted"><i class="bx bx-time-five"></i> Odczyt: Wczoraj, 18:40</small>
|
||||
<a href="installation-live.php" class="btn btn-sm btn-outline-secondary">Szczegóły</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Dodaj kartę (Edit mode only) -->
|
||||
<div class="col-xl-4 col-lg-6 col-md-6 edit-mode-item d-none">
|
||||
<div class="card h-100 shadow-sm border-2 border-dashed border-primary bg-transparent d-flex justify-content-center align-items-center cursor-pointer hover-border-primary transition-all" style="min-height: 250px;">
|
||||
<div class="text-center text-primary">
|
||||
<i class="bx bx-plus-circle fs-1 mb-2"></i>
|
||||
<h6 class="text-primary mb-0">Dodaj instalację do dashboardu</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleDashboardEditMode() {
|
||||
document.body.classList.toggle('dashboard-edit-mode');
|
||||
const isEdit = document.body.classList.contains('dashboard-edit-mode');
|
||||
|
||||
document.querySelectorAll('.edit-mode-overlay, .edit-mode-item').forEach(el => {
|
||||
if(isEdit) {
|
||||
el.classList.remove('d-none');
|
||||
} else {
|
||||
el.classList.add('d-none');
|
||||
}
|
||||
});
|
||||
|
||||
const toggleBtn = document.getElementById('toggleDashboardEditBtn');
|
||||
if(isEdit) {
|
||||
toggleBtn.classList.replace('btn-outline-secondary', 'btn-success');
|
||||
toggleBtn.innerHTML = '<i class="bx bx-check me-1"></i> Zakończ edycję';
|
||||
} else {
|
||||
toggleBtn.classList.replace('btn-success', 'btn-outline-secondary');
|
||||
toggleBtn.innerHTML = '<i class="bx bx-customize me-1"></i> Tryb edycji';
|
||||
}
|
||||
}
|
||||
|
||||
function toggleLinkVisibility(checkbox) {
|
||||
const linkContainer = document.getElementById('shareLinkContainer');
|
||||
if (checkbox.checked) {
|
||||
linkContainer.classList.remove('d-none');
|
||||
} else {
|
||||
linkContainer.classList.add('d-none');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Modal Udostępniania -->
|
||||
<div class="modal fade" id="shareModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Udostępnianie Dashboardu</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Widoczność</label>
|
||||
<div class="form-check form-switch mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="publicDashboardSwitch" onchange="toggleLinkVisibility(this)">
|
||||
<label class="form-check-label" for="publicDashboardSwitch">Dashboard publiczny (dostęp przez link)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 d-none" id="shareLinkContainer">
|
||||
<label class="form-label">Link do udostępnienia</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<input type="text" class="form-control" value="https://telemetria.magico/d/xyz123" readonly>
|
||||
<span class="input-group-text cursor-pointer" onclick="alert('Skopiowano link!')"><i class="bx bx-copy"></i></span>
|
||||
</div>
|
||||
<small class="text-muted mt-1 d-block">Osoby posiadające ten link będą mogły zobaczyć dashboard tylko do odczytu.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Zamknij</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Zapisz</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-backdrop fade"></div>
|
||||
|
||||
<?php include '../../footer.php'; ?>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
581
prototype/telemetria/installation-add.php
Normal file
581
prototype/telemetria/installation-add.php
Normal file
@@ -0,0 +1,581 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include '../../header-telemetria.php';
|
||||
?>
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<h4 class="fw-bold py-3 mb-4">
|
||||
Telemetria <span class="text-muted fw-light">/ Dodaj instalację</span>
|
||||
</h4>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
|
||||
<!-- Stepper / Wizard nawigacja -->
|
||||
<div class="d-flex justify-content-between mb-4 border-bottom pb-3">
|
||||
<div class="text-center w-100 wizard-step active-step" id="wizard-tab-1">
|
||||
<div class="badge bg-primary rounded-pill mb-2 p-2"><i class="bx bx-category fs-4"></i>
|
||||
</div>
|
||||
<h6 class="mb-0 text-primary fw-bold">1. Typ instalacji</h6>
|
||||
</div>
|
||||
<div class="text-center w-100 wizard-step opacity-50" id="wizard-tab-2">
|
||||
<div class="badge bg-secondary rounded-pill mb-2 p-2"><i
|
||||
class="bx bx-slider-alt fs-4"></i></div>
|
||||
<h6 class="mb-0 text-muted">2. Konfiguracja</h6>
|
||||
</div>
|
||||
<div class="text-center w-100 wizard-step opacity-50" id="wizard-tab-3">
|
||||
<div class="badge bg-secondary rounded-pill mb-2 p-2"><i class="bx bx-check-shield fs-4"></i>
|
||||
</div>
|
||||
<h6 class="mb-0 text-muted">3. Zakończenie</h6>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- KROK 1 -->
|
||||
<div id="wizard-step-1" class="wizard-content">
|
||||
<h5 class="mb-4 text-center">Wybierz typ integracji dla nowej instalacji</h5>
|
||||
<div class="row g-4 justify-content-center">
|
||||
<div class="col-md-5">
|
||||
<div class="card border border-2 border-primary shadow-sm h-100 cursor-pointer"
|
||||
onclick="goToStep2('gas')">
|
||||
<div class="card-body text-center">
|
||||
<div
|
||||
class="avatar avatar-xl bg-label-warning mx-auto mb-3 rounded-circle d-flex align-items-center justify-content-center">
|
||||
<i class="bx bxs-flame fs-1"></i>
|
||||
</div>
|
||||
<h5 class="card-title mb-2">Zbiornik Gazu (LPG/CNG)</h5>
|
||||
<p class="text-muted small">Standardowy czujnik Modbus dla zbiorników paliw
|
||||
i gazu z pomiarem poziomu napełnienia i ciśnienia.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="card border border-2 border-transparent hover-border-primary shadow-sm h-100 cursor-pointer"
|
||||
onclick="goToStep2('farm')">
|
||||
<div class="card-body text-center">
|
||||
<div
|
||||
class="avatar avatar-xl bg-label-info mx-auto mb-3 rounded-circle d-flex align-items-center justify-content-center">
|
||||
<i class="bx bxs-thermometer fs-1"></i>
|
||||
</div>
|
||||
<h5 class="card-title mb-2">Sterownik Mikroklimatu (Kurnik)</h5>
|
||||
<p class="text-muted small">Integracja z kontrolerami wentylacji i
|
||||
ogrzewania. Obsługa wielu stref temperaturowych i CO2.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- KROK 2 -->
|
||||
<div id="wizard-step-2" class="wizard-content d-none">
|
||||
<h5 class="mb-4">Konfiguracja specyficzna dla instalacji</h5>
|
||||
|
||||
<!-- Formularz wspólny -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Nazwa instalacji (Własna)</label>
|
||||
<input type="text" class="form-control" placeholder="np. Zbiornik Główny Północ">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Lokalizacja / Adres</label>
|
||||
<input type="text" class="form-control" placeholder="np. ul. Fabryczna 1, Warszawa">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<h6 class="text-muted mb-1">Przypięte Urządzenia (Hardware)</h6>
|
||||
<p class="small text-muted mb-0">Wybierz fizyczne urządzenia z magazynu i przypnij je do tej instalacji.</p>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#assignDeviceModal">
|
||||
<i class="bx bx-link"></i> Przypisz urządzenie
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Parametry Zbiornik (pokazywane dynamicznie) -->
|
||||
<div id="form-gas" class="dynamic-form-part">
|
||||
<div class="card bg-lighter border mb-4">
|
||||
<div class="card-header border-bottom d-flex justify-content-between align-items-center py-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="avatar avatar-sm bg-white border me-2 d-flex justify-content-center align-items-center">
|
||||
<i class="bx bx-chip text-primary"></i>
|
||||
</div>
|
||||
<h6 class="mb-0">Nadajnik Główny #1 <span class="text-muted fw-normal ms-1">(ID: 1024, MAC: 00:1B:44:11:3A:B7)</span></h6>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-danger"><i class="bx bx-unlink"></i> Odłącz</button>
|
||||
</div>
|
||||
<div class="card-body pt-3 pb-0">
|
||||
<p class="small text-muted mb-2">Parametry obsługiwane przez to urządzenie:</p>
|
||||
<div class="table-responsive text-nowrap mb-2">
|
||||
<table class="table table-sm table-bordered bg-white">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Klucz</th>
|
||||
<th>Nazwa</th>
|
||||
<th>Ostatnia wartość</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="text-monospace text-muted">gas_level</span></td>
|
||||
<td>Poziom napełnienia</td>
|
||||
<td><span class="badge bg-label-secondary">Brak danych</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="text-monospace text-muted">gas_pressure</span></td>
|
||||
<td>Ciśnienie</td>
|
||||
<td><span class="badge bg-label-secondary">Brak danych</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card bg-lighter border mb-4">
|
||||
<div class="card-header border-bottom d-flex justify-content-between align-items-center py-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="avatar avatar-sm bg-white border me-2 d-flex justify-content-center align-items-center">
|
||||
<i class="bx bx-chip text-primary"></i>
|
||||
</div>
|
||||
<h6 class="mb-0">Nadajnik Zapasowy #2 <span class="text-muted fw-normal ms-1">(ID: 1025, MAC: 00:1B:44:11:4F:C1)</span></h6>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-danger"><i class="bx bx-unlink"></i> Odłącz</button>
|
||||
</div>
|
||||
<div class="card-body pt-3 pb-0">
|
||||
<p class="small text-muted mb-2">Parametry obsługiwane przez to urządzenie:</p>
|
||||
<div class="table-responsive text-nowrap mb-2">
|
||||
<table class="table table-sm table-bordered bg-white">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Klucz</th>
|
||||
<th>Nazwa</th>
|
||||
<th>Ostatnia wartość</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="text-monospace text-muted">gps_lat</span></td>
|
||||
<td>Szerokość geogr. (GPS)</td>
|
||||
<td><span class="badge bg-label-secondary">Brak sygnału</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="text-monospace text-muted">gps_lon</span></td>
|
||||
<td>Długość geogr. (GPS)</td>
|
||||
<td><span class="badge bg-label-secondary">Brak sygnału</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Parametry Kurnik (pokazywane dynamicznie) -->
|
||||
<div id="form-farm" class="dynamic-form-part d-none">
|
||||
<div class="card bg-lighter border mb-4">
|
||||
<div class="card-header border-bottom d-flex justify-content-between align-items-center py-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="avatar avatar-sm bg-white border me-2 d-flex justify-content-center align-items-center">
|
||||
<i class="bx bx-chip text-primary"></i>
|
||||
</div>
|
||||
<h6 class="mb-0">Sterownik Klimatu Alpha <span class="text-muted fw-normal ms-1">(ID: 1026, MAC: 00:1B:44:22:9C:E2)</span></h6>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-danger"><i class="bx bx-unlink"></i> Odłącz</button>
|
||||
</div>
|
||||
<div class="card-body pt-3 pb-0">
|
||||
<p class="small text-muted mb-2">Parametry obsługiwane przez to urządzenie:</p>
|
||||
<div class="table-responsive text-nowrap mb-2">
|
||||
<table class="table table-sm table-bordered bg-white">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Klucz</th>
|
||||
<th>Nazwa</th>
|
||||
<th>Ostatnia wartość</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="text-monospace text-muted">temp_zone_1</span></td>
|
||||
<td>Temperatura (Strefa 1)</td>
|
||||
<td><span class="badge bg-label-secondary">Brak danych</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="text-monospace text-muted">vent_status</span></td>
|
||||
<td>Status wentylacji</td>
|
||||
<td><span class="badge bg-label-secondary">Brak danych</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="text-muted mb-0">Konfigurator powiadomień (Alerty)</h6>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="addAlertBlock()">
|
||||
<i class="bx bx-plus"></i> Dodaj alert
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="alerts-container">
|
||||
<!-- Pojedynczy blok alertu -->
|
||||
<div class="card bg-lighter border mb-4 alert-block">
|
||||
<div class="card-header border-bottom d-flex justify-content-between align-items-center py-3">
|
||||
<h6 class="mb-0 text-danger"><i class="bx bx-bell"></i> Nowy Alert</h6>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.alert-block').remove()"><i class="bx bx-trash"></i> Usuń ten alert</button>
|
||||
</div>
|
||||
<div class="card-body pt-3 pb-2">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<p class="small text-muted fw-bold mb-0 me-3">Zasada łączenia warunków:</p>
|
||||
<select class="form-select form-select-sm w-auto border-primary text-primary fw-bold">
|
||||
<option value="AND">Spełnione WSZYSTKIE (AND)</option>
|
||||
<option value="OR">Spełniony DOWOLNY (OR)</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="small text-muted fw-bold mb-2">Warunki (Wyzwalacze):</p>
|
||||
<div class="conditions-container mb-3">
|
||||
<div class="row align-items-end mb-2 condition-row">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Parametr</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option>Poziom gazu (%)</option>
|
||||
<option>Ciśnienie (bar)</option>
|
||||
<option>Temperatura (°C)</option>
|
||||
<option>Brak sygnału</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Znak</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option value="<">Mniejszy niż (<)</option>
|
||||
<option value=">">Większy niż (>)</option>
|
||||
<option value="=">Równy (=)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Wartość</label>
|
||||
<input type="number" step="0.1" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.condition-row').remove()">
|
||||
<i class="bx bx-x"></i> Usuń
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-label-secondary mb-4" onclick="addCondition(this)">
|
||||
<i class="bx bx-plus"></i> Dodaj warunek
|
||||
</button>
|
||||
|
||||
<hr>
|
||||
<p class="small text-muted fw-bold mb-2">Akcje po spełnieniu warunków:</p>
|
||||
<div class="actions-container mb-3">
|
||||
<div class="row align-items-end mb-2 action-row">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Kanał wysyłki</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option>E-mail</option>
|
||||
<option>SMS</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small">Odbiorca (Numer lub Email)</label>
|
||||
<input type="text" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.action-row').remove()">
|
||||
<i class="bx bx-x"></i> Usuń
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-label-secondary" onclick="addActionChannel(this)">
|
||||
<i class="bx bx-plus"></i> Dodaj kanał
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-4 border-top pt-4">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
onclick="goToStep1()">Wróć</button>
|
||||
<button type="button" class="btn btn-primary" onclick="goToStep3()">Zapisz i Zakończ</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- KROK 3 -->
|
||||
<div id="wizard-step-3" class="wizard-content d-none text-center py-4">
|
||||
<div
|
||||
class="avatar avatar-xl bg-label-success mx-auto mb-4 rounded-circle d-flex align-items-center justify-content-center">
|
||||
<i class="bx bx-check fs-1"></i>
|
||||
</div>
|
||||
<h4 class="mb-2">Instalacja zarejestrowana!</h4>
|
||||
<p class="text-muted mb-4">Instalacja została pomyślnie dodana do ekosystemu.</p>
|
||||
|
||||
<div class="bg-lighter rounded p-5 mx-auto mb-4 border border-dashed text-center" style="max-width: 600px;">
|
||||
<i class="bx bx-cog fs-1 text-muted mb-3"></i>
|
||||
<h6 class="text-muted text-uppercase mb-2">Miejsce na dane uwierzytelniające</h6>
|
||||
<p class="small text-muted mb-0">W zależności od ostatecznych ustaleń architektonicznych, w tym miejscu system wygeneruje dla Ciebie <strong>Token API</strong> lub <strong>Gotowy plik konfiguracyjny</strong> do załadowania na sprzęt.</p>
|
||||
</div>
|
||||
|
||||
<a href="installations.php" class="btn btn-primary mt-2">Przejdź do listy instalacji</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-backdrop fade"></div>
|
||||
|
||||
<?php include '../../footer.php'; ?>
|
||||
|
||||
<script>
|
||||
// Prosta logika wizarda
|
||||
function goToStep1() {
|
||||
document.querySelectorAll('.wizard-content').forEach(el => el.classList.add('d-none'));
|
||||
document.getElementById('wizard-step-1').classList.remove('d-none');
|
||||
|
||||
updateTabsUI(1);
|
||||
}
|
||||
|
||||
function goToStep2(type) {
|
||||
document.querySelectorAll('.wizard-content').forEach(el => el.classList.add('d-none'));
|
||||
document.getElementById('wizard-step-2').classList.remove('d-none');
|
||||
|
||||
// Pokaż odpowiednie pola
|
||||
document.querySelectorAll('.dynamic-form-part').forEach(el => el.classList.add('d-none'));
|
||||
if (type === 'gas') {
|
||||
document.getElementById('form-gas').classList.remove('d-none');
|
||||
document.getElementById('type-label-name').innerText = 'Zbiornik Gazu';
|
||||
} else {
|
||||
document.getElementById('form-farm').classList.remove('d-none');
|
||||
document.getElementById('type-label-name').innerText = 'Sterownik Mikroklimatu';
|
||||
}
|
||||
|
||||
updateTabsUI(2);
|
||||
}
|
||||
|
||||
function goToStep3() {
|
||||
document.querySelectorAll('.wizard-content').forEach(el => el.classList.add('d-none'));
|
||||
document.getElementById('wizard-step-3').classList.remove('d-none');
|
||||
|
||||
updateTabsUI(3);
|
||||
}
|
||||
|
||||
function updateTabsUI(step) {
|
||||
// Reset
|
||||
document.querySelectorAll('.wizard-step').forEach(el => {
|
||||
el.classList.remove('active-step');
|
||||
el.classList.add('opacity-50');
|
||||
el.querySelector('.badge').classList.replace('bg-primary', 'bg-secondary');
|
||||
el.querySelector('h6').classList.remove('text-primary', 'fw-bold');
|
||||
el.querySelector('h6').classList.add('text-muted');
|
||||
});
|
||||
|
||||
// Set active
|
||||
let activeTab = document.getElementById('wizard-tab-' + step);
|
||||
activeTab.classList.remove('opacity-50');
|
||||
activeTab.classList.add('active-step');
|
||||
activeTab.querySelector('.badge').classList.replace('bg-secondary', 'bg-primary');
|
||||
activeTab.querySelector('h6').classList.remove('text-muted');
|
||||
activeTab.querySelector('h6').classList.add('text-primary', 'fw-bold');
|
||||
}
|
||||
|
||||
function copyToken() {
|
||||
var copyText = document.getElementById("apiTokenInput");
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 99999);
|
||||
navigator.clipboard.writeText(copyText.value);
|
||||
alert("Token skopiowany do schowka!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Modal Przypisywania Urządzenia
|
||||
function openAssignModal() {
|
||||
var myModal = new bootstrap.Modal(document.getElementById('assignDeviceModal'));
|
||||
myModal.show();
|
||||
}
|
||||
|
||||
function addAlertBlock() {
|
||||
const container = document.getElementById('alerts-container');
|
||||
const alertHTML = `
|
||||
<div class="card bg-lighter border mb-4 alert-block">
|
||||
<div class="card-header border-bottom d-flex justify-content-between align-items-center py-3">
|
||||
<h6 class="mb-0 text-danger"><i class="bx bx-bell"></i> Nowy Alert</h6>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.alert-block').remove()"><i class="bx bx-trash"></i> Usuń ten alert</button>
|
||||
</div>
|
||||
<div class="card-body pt-3 pb-2">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<p class="small text-muted fw-bold mb-0 me-3">Zasada łączenia warunków:</p>
|
||||
<select class="form-select form-select-sm w-auto border-primary text-primary fw-bold">
|
||||
<option value="AND">Spełnione WSZYSTKIE (AND)</option>
|
||||
<option value="OR">Spełniony DOWOLNY (OR)</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="small text-muted fw-bold mb-2">Warunki (Wyzwalacze):</p>
|
||||
<div class="conditions-container mb-3">
|
||||
<div class="row align-items-end mb-2 condition-row">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Parametr</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option>Poziom gazu (%)</option>
|
||||
<option>Ciśnienie (bar)</option>
|
||||
<option>Temperatura (°C)</option>
|
||||
<option>Brak sygnału</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Znak</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option value="<">Mniejszy niż (<)</option>
|
||||
<option value=">">Większy niż (>)</option>
|
||||
<option value="=">Równy (=)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Wartość</label>
|
||||
<input type="number" step="0.1" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.condition-row').remove()">
|
||||
<i class="bx bx-x"></i> Usuń
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-label-secondary mb-4" onclick="addCondition(this)">
|
||||
<i class="bx bx-plus"></i> Dodaj warunek
|
||||
</button>
|
||||
|
||||
<hr>
|
||||
<p class="small text-muted fw-bold mb-2">Akcje po spełnieniu warunków:</p>
|
||||
<div class="actions-container mb-3">
|
||||
<div class="row align-items-end mb-2 action-row">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Kanał wysyłki</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option>E-mail</option>
|
||||
<option>SMS</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small">Odbiorca (Numer lub Email)</label>
|
||||
<input type="text" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.action-row').remove()">
|
||||
<i class="bx bx-x"></i> Usuń
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-label-secondary" onclick="addActionChannel(this)">
|
||||
<i class="bx bx-plus"></i> Dodaj kanał
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
container.insertAdjacentHTML('beforeend', alertHTML);
|
||||
}
|
||||
|
||||
function addCondition(btn) {
|
||||
const conditionsContainer = btn.previousElementSibling;
|
||||
const conditionHTML = `
|
||||
<div class="row align-items-end mb-2 condition-row">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Parametr</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option>Poziom gazu (%)</option>
|
||||
<option>Ciśnienie (bar)</option>
|
||||
<option>Temperatura (°C)</option>
|
||||
<option>Brak sygnału</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Znak</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option value="<">Mniejszy niż (<)</option>
|
||||
<option value=">">Większy niż (>)</option>
|
||||
<option value="=">Równy (=)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Wartość</label>
|
||||
<input type="number" step="0.1" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.condition-row').remove()">
|
||||
<i class="bx bx-x"></i> Usuń
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
conditionsContainer.insertAdjacentHTML('beforeend', conditionHTML);
|
||||
}
|
||||
|
||||
function addActionChannel(btn) {
|
||||
const actionsContainer = btn.previousElementSibling;
|
||||
const actionHTML = `
|
||||
<div class="row align-items-end mb-2 action-row">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Kanał wysyłki</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option>E-mail</option>
|
||||
<option>SMS</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small">Odbiorca (Numer lub Email)</label>
|
||||
<input type="text" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.action-row').remove()">
|
||||
<i class="bx bx-x"></i> Usuń
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
actionsContainer.insertAdjacentHTML('beforeend', actionHTML);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.hover-border-primary:hover {
|
||||
border-color: #696cff !important;
|
||||
transition: 0.3s;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Modal Przypisywania Urządzenia -->
|
||||
<div class="modal fade" id="assignDeviceModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Przypisz urządzenie do instalacji</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Wybierz z magazynu (tylko wolne urządzenia)</label>
|
||||
<select class="form-select">
|
||||
<option value="">-- Wybierz urządzenie --</option>
|
||||
<option value="1027">1027 - Nowy czujnik testowy</option>
|
||||
<option value="1030">1030 - Moduł WiFi Zewnętrzny</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Anuluj</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Przypisz urządzenie</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
429
prototype/telemetria/installation-edit.php
Normal file
429
prototype/telemetria/installation-edit.php
Normal file
@@ -0,0 +1,429 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include '../../header-telemetria.php';
|
||||
?>
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<h4 class="fw-bold py-3 mb-4">
|
||||
Telemetria <span class="text-muted fw-light">/ Edycja instalacji</span>
|
||||
</h4>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
|
||||
<div class="d-flex align-items-center mb-4">
|
||||
<div class="avatar avatar-lg bg-label-warning me-3">
|
||||
<i class="bx bxs-flame fs-3"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-0">Edytujesz: Zbiornik LPG #1</h5>
|
||||
<small class="text-muted">Typ: Zbiornik Gazu (LPG/CNG) | ID: mag_tlm_837492</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Formularz wspólny -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Nazwa instalacji (Własna)</label>
|
||||
<input type="text" class="form-control" value="Zbiornik LPG #1 (Stalowa Wola)">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Lokalizacja / Adres</label>
|
||||
<input type="text" class="form-control" value="Stalowa Wola, ul. Przemysłowa">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<h6 class="text-muted mb-1">Przypięte Urządzenia (Hardware)</h6>
|
||||
<p class="small text-muted mb-0">Wybierz fizyczne urządzenia i skonfiguruj, jakie parametry mają przesyłać.</p>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#assignDeviceModal">
|
||||
<i class="bx bx-link"></i> Przypisz urządzenie
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card bg-lighter border mb-4">
|
||||
<div class="card-header border-bottom d-flex justify-content-between align-items-center py-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="avatar avatar-sm bg-white border me-2 d-flex justify-content-center align-items-center">
|
||||
<i class="bx bx-chip text-primary"></i>
|
||||
</div>
|
||||
<h6 class="mb-0">Nadajnik Główny #1 <span class="text-muted fw-normal ms-1">(ID: 1024, MAC: 00:1B:44:11:3A:B7)</span></h6>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-danger"><i class="bx bx-unlink"></i> Odłącz</button>
|
||||
</div>
|
||||
<div class="card-body pt-3 pb-0">
|
||||
<p class="small text-muted mb-2">Parametry obsługiwane przez to urządzenie:</p>
|
||||
<div class="table-responsive text-nowrap mb-2">
|
||||
<table class="table table-sm table-bordered bg-white">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Klucz</th>
|
||||
<th>Nazwa</th>
|
||||
<th>Ostatnia wartość</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="text-monospace text-muted">gas_level</span></td>
|
||||
<td>Poziom napełnienia</td>
|
||||
<td><span class="badge bg-label-primary">45%</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="text-monospace text-muted">gas_pressure</span></td>
|
||||
<td>Ciśnienie</td>
|
||||
<td><span class="badge bg-label-info">12.5 bar</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="text-monospace text-muted">gas_temp</span></td>
|
||||
<td>Temperatura</td>
|
||||
<td><span class="badge bg-label-warning">15.2 °C</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card bg-lighter border mb-4">
|
||||
<div class="card-header border-bottom d-flex justify-content-between align-items-center py-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="avatar avatar-sm bg-white border me-2 d-flex justify-content-center align-items-center">
|
||||
<i class="bx bx-chip text-primary"></i>
|
||||
</div>
|
||||
<h6 class="mb-0">Nadajnik Zapasowy #2 <span class="text-muted fw-normal ms-1">(ID: 1025, MAC: 00:1B:44:11:4F:C1)</span></h6>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-danger"><i class="bx bx-unlink"></i> Odłącz</button>
|
||||
</div>
|
||||
<div class="card-body pt-3 pb-0">
|
||||
<p class="small text-muted mb-2">Parametry obsługiwane przez to urządzenie:</p>
|
||||
<div class="table-responsive text-nowrap mb-2">
|
||||
<table class="table table-sm table-bordered bg-white">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Klucz</th>
|
||||
<th>Nazwa</th>
|
||||
<th>Ostatnia wartość</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="text-monospace text-muted">gps_lat</span></td>
|
||||
<td>Szerokość geogr. (GPS)</td>
|
||||
<td><span class="badge bg-label-secondary">Brak sygnału</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="text-monospace text-muted">gps_lon</span></td>
|
||||
<td>Długość geogr. (GPS)</td>
|
||||
<td><span class="badge bg-label-secondary">Brak sygnału</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Konfigurator Powiadomień -->
|
||||
<hr class="my-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="text-muted mb-0">Konfigurator powiadomień (Alerty)</h6>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="addAlertBlock()">
|
||||
<i class="bx bx-plus"></i> Dodaj alert
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="alerts-container">
|
||||
<!-- Pojedynczy blok alertu -->
|
||||
<div class="card bg-lighter border mb-4 alert-block">
|
||||
<div class="card-header border-bottom d-flex justify-content-between align-items-center py-3">
|
||||
<h6 class="mb-0 text-danger"><i class="bx bx-bell"></i> Alert Systemowy #1</h6>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.alert-block').remove()"><i class="bx bx-trash"></i> Usuń ten alert</button>
|
||||
</div>
|
||||
<div class="card-body pt-3 pb-2">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<p class="small text-muted fw-bold mb-0 me-3">Zasada łączenia warunków:</p>
|
||||
<select class="form-select form-select-sm w-auto border-primary text-primary fw-bold">
|
||||
<option value="AND">Spełnione WSZYSTKIE (AND)</option>
|
||||
<option value="OR">Spełniony DOWOLNY (OR)</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="small text-muted fw-bold mb-2">Warunki (Wyzwalacze):</p>
|
||||
<div class="conditions-container mb-3">
|
||||
<div class="row align-items-end mb-2 condition-row">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Parametr</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option selected>Poziom gazu (%)</option>
|
||||
<option>Ciśnienie (bar)</option>
|
||||
<option>Temperatura (°C)</option>
|
||||
<option>Brak sygnału</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Znak</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option value="<" selected>Mniejszy niż (<)</option>
|
||||
<option value=">">Większy niż (>)</option>
|
||||
<option value="=">Równy (=)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Wartość</label>
|
||||
<input type="number" step="0.1" class="form-control form-control-sm" value="15">
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.condition-row').remove()">
|
||||
<i class="bx bx-x"></i> Usuń
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-end mb-2 condition-row">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Parametr</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option>Poziom gazu (%)</option>
|
||||
<option selected>Ciśnienie (bar)</option>
|
||||
<option>Temperatura (°C)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Znak</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option value="<">Mniejszy niż (<)</option>
|
||||
<option value=">" selected>Większy niż (>)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Wartość</label>
|
||||
<input type="number" step="0.1" class="form-control form-control-sm" value="10">
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.condition-row').remove()">
|
||||
<i class="bx bx-x"></i> Usuń
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-label-secondary mb-4" onclick="addCondition(this)">
|
||||
<i class="bx bx-plus"></i> Dodaj warunek
|
||||
</button>
|
||||
|
||||
<hr>
|
||||
<p class="small text-muted fw-bold mb-2">Akcje po spełnieniu warunków:</p>
|
||||
<div class="actions-container mb-3">
|
||||
<div class="row align-items-end mb-2 action-row">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Kanał wysyłki</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option>E-mail</option>
|
||||
<option selected>SMS</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small">Odbiorca (Numer lub Email)</label>
|
||||
<input type="text" class="form-control form-control-sm" value="+48 123 456 789">
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.action-row').remove()">
|
||||
<i class="bx bx-x"></i> Usuń
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-label-secondary" onclick="addActionChannel(this)">
|
||||
<i class="bx bx-plus"></i> Dodaj kanał
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-5 border-top pt-4">
|
||||
<a href="installations.php" class="btn btn-outline-secondary">Anuluj</a>
|
||||
<a href="installations.php" class="btn btn-primary">Zapisz zmiany</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-backdrop fade"></div>
|
||||
|
||||
<?php include '../../footer.php'; ?>
|
||||
|
||||
<script>
|
||||
function addAlertBlock() {
|
||||
const container = document.getElementById('alerts-container');
|
||||
const alertHTML = `
|
||||
<div class="card bg-lighter border mb-4 alert-block">
|
||||
<div class="card-header border-bottom d-flex justify-content-between align-items-center py-3">
|
||||
<h6 class="mb-0 text-danger"><i class="bx bx-bell"></i> Nowy Alert</h6>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.alert-block').remove()"><i class="bx bx-trash"></i> Usuń ten alert</button>
|
||||
</div>
|
||||
<div class="card-body pt-3 pb-2">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<p class="small text-muted fw-bold mb-0 me-3">Zasada łączenia warunków:</p>
|
||||
<select class="form-select form-select-sm w-auto border-primary text-primary fw-bold">
|
||||
<option value="AND">Spełnione WSZYSTKIE (AND)</option>
|
||||
<option value="OR">Spełniony DOWOLNY (OR)</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="small text-muted fw-bold mb-2">Warunki (Wyzwalacze):</p>
|
||||
<div class="conditions-container mb-3">
|
||||
<div class="row align-items-end mb-2 condition-row">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Parametr</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option>Poziom gazu (%)</option>
|
||||
<option>Ciśnienie (bar)</option>
|
||||
<option>Temperatura (°C)</option>
|
||||
<option>Brak sygnału</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Znak</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option value="<">Mniejszy niż (<)</option>
|
||||
<option value=">">Większy niż (>)</option>
|
||||
<option value="=">Równy (=)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Wartość</label>
|
||||
<input type="number" step="0.1" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.condition-row').remove()">
|
||||
<i class="bx bx-x"></i> Usuń
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-label-secondary mb-4" onclick="addCondition(this)">
|
||||
<i class="bx bx-plus"></i> Dodaj warunek
|
||||
</button>
|
||||
|
||||
<hr>
|
||||
<p class="small text-muted fw-bold mb-2">Akcje po spełnieniu warunków:</p>
|
||||
<div class="actions-container mb-3">
|
||||
<div class="row align-items-end mb-2 action-row">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Kanał wysyłki</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option>E-mail</option>
|
||||
<option>SMS</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small">Odbiorca (Numer lub Email)</label>
|
||||
<input type="text" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.action-row').remove()">
|
||||
<i class="bx bx-x"></i> Usuń
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-label-secondary" onclick="addActionChannel(this)">
|
||||
<i class="bx bx-plus"></i> Dodaj kanał
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
container.insertAdjacentHTML('beforeend', alertHTML);
|
||||
}
|
||||
|
||||
function addCondition(btn) {
|
||||
const conditionsContainer = btn.previousElementSibling;
|
||||
const conditionHTML = `
|
||||
<div class="row align-items-end mb-2 condition-row">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Parametr</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option>Poziom gazu (%)</option>
|
||||
<option>Ciśnienie (bar)</option>
|
||||
<option>Temperatura (°C)</option>
|
||||
<option>Brak sygnału</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small">Znak</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option value="<">Mniejszy niż (<)</option>
|
||||
<option value=">">Większy niż (>)</option>
|
||||
<option value="=">Równy (=)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Wartość</label>
|
||||
<input type="number" step="0.1" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.condition-row').remove()">
|
||||
<i class="bx bx-x"></i> Usuń
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
conditionsContainer.insertAdjacentHTML('beforeend', conditionHTML);
|
||||
}
|
||||
|
||||
function addActionChannel(btn) {
|
||||
const actionsContainer = btn.previousElementSibling;
|
||||
const actionHTML = `
|
||||
<div class="row align-items-end mb-2 action-row">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Kanał wysyłki</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option>E-mail</option>
|
||||
<option>SMS</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small">Odbiorca (Numer lub Email)</label>
|
||||
<input type="text" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('.action-row').remove()">
|
||||
<i class="bx bx-x"></i> Usuń
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
actionsContainer.insertAdjacentHTML('beforeend', actionHTML);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<!-- Modal Przypisywania Urządzenia -->
|
||||
<div class="modal fade" id="assignDeviceModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Przypisz urządzenie do instalacji</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Wybierz z magazynu (tylko wolne urządzenia)</label>
|
||||
<select class="form-select">
|
||||
<option value="">-- Wybierz urządzenie --</option>
|
||||
<option value="1027">1027 - Nowy czujnik testowy</option>
|
||||
<option value="1030">1030 - Moduł WiFi Zewnętrzny</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Anuluj</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Przypisz urządzenie</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
441
prototype/telemetria/installation-live.php
Normal file
441
prototype/telemetria/installation-live.php
Normal file
@@ -0,0 +1,441 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include '../../header-telemetria.php';
|
||||
?>
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="d-flex justify-content-between align-items-center py-3 mb-4">
|
||||
<h4 class="fw-bold mb-0">
|
||||
Telemetria <span class="text-muted fw-light">/ Podgląd Live</span>
|
||||
</h4>
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge bg-success me-2 px-3 py-2 fs-6"><i class="bx bx-wifi"></i> ONLINE</span>
|
||||
<small class="text-muted d-none d-md-block">Ostatni kontakt: Dzisiaj, 14:52:10</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Header z wyborem instalacji -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-body d-flex flex-column flex-md-row justify-content-between align-items-md-center">
|
||||
<div class="d-flex align-items-center mb-3 mb-md-0">
|
||||
<div class="avatar avatar-lg bg-label-warning me-3">
|
||||
<i class="bx bxs-flame fs-3"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-0">Zbiornik LPG #1 (Stalowa Wola)</h5>
|
||||
<small class="text-muted">ID: mag_tlm_837492</small>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="btn btn-outline-primary me-2" id="toggleEditModeBtn" onclick="toggleEditMode()">
|
||||
<i class="bx bx-customize me-1"></i> Edytuj dashboard
|
||||
</button>
|
||||
<a href="installation-edit.php" class="btn btn-outline-secondary">
|
||||
<i class="bx bx-pencil me-1"></i> Edytuj instalację
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4 mb-4">
|
||||
<!-- SVG Zbiornika -->
|
||||
<div class="col-xl-4 col-lg-5 col-md-12">
|
||||
<div class="card border-0 shadow-sm mb-4 position-relative">
|
||||
<button type="button" class="btn btn-sm btn-icon btn-primary position-absolute top-0 end-0 m-2 widget-settings-btn d-none zindex-1" onclick="openWidgetConfig('Wizualizacja stanu napełnienia')">
|
||||
<i class="bx bx-cog"></i>
|
||||
</button>
|
||||
<div class="card-header border-bottom">
|
||||
<h5 class="card-title m-0">Wizualizacja stanu napełnienia</h5>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column align-items-center justify-content-center p-5">
|
||||
|
||||
<!-- SVG Silos -->
|
||||
<div class="tank-container position-relative mb-3 mx-auto" style="width: 140px; height: 350px;">
|
||||
<svg viewBox="0 0 100 300" width="100%" height="100%">
|
||||
<!-- Nogi silosu -->
|
||||
<path d="M 35 260 L 30 295 M 65 260 L 70 295" stroke="#a1acb8" stroke-width="6"
|
||||
stroke-linecap="round" />
|
||||
<line x1="20" y1="295" x2="80" y2="295" stroke="#a1acb8" stroke-width="4"
|
||||
stroke-linecap="round" />
|
||||
|
||||
<!-- Zewnętrzna krawędź obudowy (Silos stojący) -->
|
||||
<path id="siloOutline" d="M 20 40 A 30 20 0 0 1 80 40 L 80 250 A 30 20 0 0 1 20 250 Z"
|
||||
fill="#f8f9fa" stroke="#d9dee3" stroke-width="4" />
|
||||
|
||||
<!-- Animowane wypełnienie (Mask/Clip) -->
|
||||
<clipPath id="fillClip">
|
||||
<path d="M 22 42 A 28 18 0 0 1 78 42 L 78 248 A 28 18 0 0 1 22 248 Z" />
|
||||
</clipPath>
|
||||
|
||||
<!-- Płyn - gaz -->
|
||||
<defs>
|
||||
<linearGradient id="gasGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#ffab00" />
|
||||
<stop offset="100%" stop-color="#e69a00" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Ten prostokąt porusza się w górę i w dół, maskowany przez kształt silosu -->
|
||||
<g clip-path="url(#fillClip)">
|
||||
<rect id="liquidFill" x="0" y="120" width="100" height="250" fill="url(#gasGradient)" />
|
||||
</g>
|
||||
|
||||
<!-- Pierścienie wzmacniające silosu -->
|
||||
<path d="M 20 90 A 30 10 0 0 0 80 90" stroke="#d9dee3" stroke-width="2" fill="none" />
|
||||
<path d="M 20 150 A 30 10 0 0 0 80 150" stroke="#d9dee3" stroke-width="2" fill="none" />
|
||||
<path d="M 20 210 A 30 10 0 0 0 80 210" stroke="#d9dee3" stroke-width="2" fill="none" />
|
||||
|
||||
<!-- Efekt szkła/odblasku dla głębi 3D -->
|
||||
<path d="M 26 50 Q 35 150 26 240" stroke="#ffffff" stroke-width="4" fill="none"
|
||||
opacity="0.6" stroke-linecap="round" />
|
||||
<path d="M 74 50 Q 65 150 74 240" stroke="#000000" stroke-width="3" fill="none"
|
||||
opacity="0.05" stroke-linecap="round" />
|
||||
|
||||
<!-- Podziałka na zbiorniku -->
|
||||
<line x1="85" y1="90" x2="95" y2="90" stroke="#a1acb8" stroke-width="2" />
|
||||
<text x="80" y="93" font-size="8" fill="#a1acb8" text-anchor="end">75%</text>
|
||||
|
||||
<line x1="85" y1="150" x2="95" y2="150" stroke="#a1acb8" stroke-width="2" />
|
||||
<text x="80" y="153" font-size="8" fill="#a1acb8" text-anchor="end">50%</text>
|
||||
|
||||
<line x1="85" y1="210" x2="95" y2="210" stroke="#a1acb8" stroke-width="2" />
|
||||
<text x="80" y="213" font-size="8" fill="#a1acb8" text-anchor="end">25%</text>
|
||||
</svg>
|
||||
|
||||
<!-- Główna etykieta procentowa nałożona na grafikę -->
|
||||
<div class="position-absolute top-50 start-50 translate-middle text-center"
|
||||
style="margin-top: -15px;">
|
||||
<h1 class="mb-0 fw-bolder text-dark"
|
||||
style="text-shadow: 0px 0px 10px rgba(255,255,255,0.8);" id="mainPercentage">60%</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header border-bottom d-flex justify-content-between align-items-center">
|
||||
<h5 class="card-title m-0">Skonfigurowane alerty</h5>
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary"><i class="bx bx-cog"></i></button>
|
||||
</div>
|
||||
<div class="card-body pt-4">
|
||||
<ul class="list-unstyled mb-0">
|
||||
<li class="mb-3 d-flex align-items-center">
|
||||
<span class="badge bg-label-danger me-3 p-2"><i class="bx bx-down-arrow-alt"></i></span>
|
||||
<div>
|
||||
<h6 class="mb-0">Niski poziom gazu</h6>
|
||||
<small class="text-muted">Gdy poziom < 15%</small>
|
||||
</div>
|
||||
</li>
|
||||
<li class="mb-3 d-flex align-items-center">
|
||||
<span class="badge bg-label-danger me-3 p-2"><i class="bx bx-up-arrow-alt"></i></span>
|
||||
<div>
|
||||
<h6 class="mb-0">Nadciśnienie</h6>
|
||||
<small class="text-muted">Gdy ciśnienie > 7.5 bar</small>
|
||||
</div>
|
||||
</li>
|
||||
<li class="d-flex align-items-center">
|
||||
<span class="badge bg-label-warning me-3 p-2"><i class="bx bx-wifi-off"></i></span>
|
||||
<div>
|
||||
<h6 class="mb-0">Brak łączności</h6>
|
||||
<small class="text-muted">Gdy brak sygnału > 10 min</small>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Parametry pomiarowe - Kafeleki -->
|
||||
<div class="col-xl-8 col-lg-7 col-md-12">
|
||||
<div class="row row-cols-1 row-cols-sm-2 g-4 align-content-start mb-4">
|
||||
|
||||
<!-- Poziom gazu w litrach -->
|
||||
<div class="col">
|
||||
<div class="card shadow-sm border-0 border-start border-warning border-3 position-relative">
|
||||
<button type="button" class="btn btn-sm btn-icon btn-primary position-absolute top-0 end-0 m-2 widget-settings-btn d-none zindex-1" onclick="openWidgetConfig('Poziom gazu (Wartość / litry)')">
|
||||
<i class="bx bx-cog"></i>
|
||||
</button>
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div class="content-left">
|
||||
<span class="text-muted text-uppercase small fw-bold"
|
||||
style="letter-spacing: 0.5px;">Szacowana objętość</span>
|
||||
<div class="d-flex align-items-end mt-2">
|
||||
<h2 class="mb-0 me-2 text-dark" id="volumeValue">1 620</h2>
|
||||
<span class="fs-6 fw-normal text-muted mb-1">litrów</span>
|
||||
</div>
|
||||
<small class="text-success fw-semibold"><i class="bx bx-up-arrow-alt"></i> +25 l w
|
||||
ciągu 5 min</small>
|
||||
</div>
|
||||
<span class="badge bg-label-warning rounded p-2">
|
||||
<i class="bx bxs-flame fs-3"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ciśnienie -->
|
||||
<div class="col">
|
||||
<div class="card shadow-sm border-0 position-relative">
|
||||
<button type="button" class="btn btn-sm btn-icon btn-primary position-absolute top-0 end-0 m-2 widget-settings-btn d-none zindex-1" onclick="openWidgetConfig('Ciśnienie operacyjne')">
|
||||
<i class="bx bx-cog"></i>
|
||||
</button>
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div class="content-left">
|
||||
<span class="text-muted text-uppercase small fw-bold"
|
||||
style="letter-spacing: 0.5px;">Ciśnienie operacyjne</span>
|
||||
<div class="d-flex align-items-end mt-2">
|
||||
<h2 class="mb-0 me-2 text-dark" id="pressureValue">5.24</h2>
|
||||
<span class="fs-6 fw-normal text-muted mb-1">bar</span>
|
||||
</div>
|
||||
<small class="text-muted">Norma: 3.0 - 7.0 bar</small>
|
||||
</div>
|
||||
<span class="badge bg-label-info rounded p-2">
|
||||
<i class="bx bx-tachometer fs-3"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Temperatura -->
|
||||
<div class="col">
|
||||
<div class="card shadow-sm border-0 position-relative">
|
||||
<button type="button" class="btn btn-sm btn-icon btn-primary position-absolute top-0 end-0 m-2 widget-settings-btn d-none zindex-1" onclick="openWidgetConfig('Temperatura otoczenia')">
|
||||
<i class="bx bx-cog"></i>
|
||||
</button>
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div class="content-left">
|
||||
<span class="text-muted text-uppercase small fw-bold"
|
||||
style="letter-spacing: 0.5px;">Temperatura otoczenia</span>
|
||||
<div class="d-flex align-items-end mt-2">
|
||||
<h2 class="mb-0 me-2 text-dark">14.5</h2>
|
||||
<span class="fs-6 fw-normal text-muted mb-1">°C</span>
|
||||
</div>
|
||||
<small class="text-muted">Trend stabilny</small>
|
||||
</div>
|
||||
<span class="badge bg-label-primary rounded p-2">
|
||||
<i class="bx bxs-thermometer fs-3"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Zasilanie i sieć -->
|
||||
<div class="col">
|
||||
<div class="card shadow-sm border-0 position-relative">
|
||||
<button type="button" class="btn btn-sm btn-icon btn-primary position-absolute top-0 end-0 m-2 widget-settings-btn d-none zindex-1" onclick="openWidgetConfig('Hardware & Sieć')">
|
||||
<i class="bx bx-cog"></i>
|
||||
</button>
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start justify-content-between mb-3">
|
||||
<span class="text-muted text-uppercase small fw-bold"
|
||||
style="letter-spacing: 0.5px;">Hardware & Sieć</span>
|
||||
<span class="badge bg-label-secondary rounded p-2"><i
|
||||
class="bx bx-chip fs-4"></i></span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="text-muted"><i class="bx bx-battery me-1"></i> Bateria
|
||||
zew.</span>
|
||||
<span class="fw-semibold">12.4 V</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="text-muted"><i class="bx bx-signal-4 me-1"></i> Sygnał
|
||||
GSM</span>
|
||||
<span class="fw-semibold">82%</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="text-muted"><i class="bx bx-time-five me-1"></i> Ostatni
|
||||
kontakt</span>
|
||||
<span class="fw-semibold">14:52:10</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header border-bottom">
|
||||
<h5 class="card-title m-0">Historia ostatnich odczytów</h5>
|
||||
</div>
|
||||
<div class="card-body pt-4 pb-0" style="max-height: 400px; overflow-y: auto;">
|
||||
<ul class="timeline mb-0">
|
||||
<li class="timeline-item timeline-item-transparent">
|
||||
<span class="timeline-point timeline-point-primary"></span>
|
||||
<div class="timeline-event">
|
||||
<div class="timeline-header mb-1">
|
||||
<h6 class="mb-0">Regularny odczyt</h6>
|
||||
<small class="text-muted">Dzisiaj, 14:52:10</small>
|
||||
</div>
|
||||
<p class="mb-0 small">Poziom: 60% (1620 l) | Ciśnienie: 5.24 bar | Bateria: 12.4V</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-item timeline-item-transparent">
|
||||
<span class="timeline-point timeline-point-danger"></span>
|
||||
<div class="timeline-event">
|
||||
<div class="timeline-header mb-1">
|
||||
<h6 class="mb-0 text-danger">ALERT: Zbyt wysokie ciśnienie!</h6>
|
||||
<small class="text-muted">Dzisiaj, 13:10:05</small>
|
||||
</div>
|
||||
<p class="mb-0 small">Zarejestrowano skok ciśnienia: <strong>7.8 bar</strong> (Próg: 7.5
|
||||
bar). Wysłano powiadomienie SMS do serwisu.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-item timeline-item-transparent">
|
||||
<span class="timeline-point timeline-point-primary"></span>
|
||||
<div class="timeline-event">
|
||||
<div class="timeline-header mb-1">
|
||||
<h6 class="mb-0">Regularny odczyt</h6>
|
||||
<small class="text-muted">Dzisiaj, 12:52:10</small>
|
||||
</div>
|
||||
<p class="mb-0 small">Poziom: 61% (1640 l) | Ciśnienie: 6.8 bar | Bateria: 12.4V</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-item timeline-item-transparent">
|
||||
<span class="timeline-point timeline-point-primary"></span>
|
||||
<div class="timeline-event">
|
||||
<div class="timeline-header mb-1">
|
||||
<h6 class="mb-0">Regularny odczyt</h6>
|
||||
<small class="text-muted">Dzisiaj, 10:52:10</small>
|
||||
</div>
|
||||
<p class="mb-0 small">Poziom: 62% (1665 l) | Ciśnienie: 6.5 bar | Bateria: 12.5V</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-item timeline-item-transparent">
|
||||
<span class="timeline-point timeline-point-info"></span>
|
||||
<div class="timeline-event">
|
||||
<div class="timeline-header mb-1">
|
||||
<h6 class="mb-0 text-info">Zakończono tankowanie</h6>
|
||||
<small class="text-muted">Wczoraj, 18:30:00</small>
|
||||
</div>
|
||||
<p class="mb-0 small">Przyrost poziomu z 15% na 65%.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-item timeline-item-transparent border-transparent">
|
||||
<span class="timeline-point timeline-point-primary"></span>
|
||||
<div class="timeline-event">
|
||||
<div class="timeline-header mb-1">
|
||||
<h6 class="mb-0">Regularny odczyt</h6>
|
||||
<small class="text-muted">Wczoraj, 16:52:10</small>
|
||||
</div>
|
||||
<p class="mb-0 small">Poziom: 14% (370 l) | Ciśnienie: 4.5 bar | Bateria: 12.5V</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* CSS dla animacji zbiornika */
|
||||
#liquidFill {
|
||||
/* Symulacja falowania i wznoszenia płynu */
|
||||
animation: fillTank 5s ease-in-out infinite alternate;
|
||||
transform-origin: bottom;
|
||||
}
|
||||
|
||||
@keyframes fillTank {
|
||||
0% {
|
||||
y: 135;
|
||||
}
|
||||
|
||||
100% {
|
||||
y: 125;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function toggleEditMode() {
|
||||
document.body.classList.toggle('dashboard-edit-mode');
|
||||
const isEdit = document.body.classList.contains('dashboard-edit-mode');
|
||||
|
||||
document.querySelectorAll('.widget-settings-btn').forEach(btn => {
|
||||
if(isEdit) {
|
||||
btn.classList.remove('d-none');
|
||||
} else {
|
||||
btn.classList.add('d-none');
|
||||
}
|
||||
});
|
||||
|
||||
const toggleBtn = document.getElementById('toggleEditModeBtn');
|
||||
if(isEdit) {
|
||||
toggleBtn.classList.replace('btn-outline-primary', 'btn-primary');
|
||||
toggleBtn.innerHTML = '<i class="bx bx-check me-1"></i> Zakończ edycję';
|
||||
} else {
|
||||
toggleBtn.classList.replace('btn-primary', 'btn-outline-primary');
|
||||
toggleBtn.innerHTML = '<i class="bx bx-customize me-1"></i> Edytuj dashboard';
|
||||
}
|
||||
}
|
||||
|
||||
function openWidgetConfig(widgetName) {
|
||||
document.getElementById('configWidgetName').innerText = widgetName;
|
||||
var myModal = new bootstrap.Modal(document.getElementById('widgetConfigModal'));
|
||||
myModal.show();
|
||||
}
|
||||
|
||||
// Prosty skrypt symulujący ruch danych (Live)
|
||||
setInterval(() => {
|
||||
// Losuj delikatnie ciśnienie
|
||||
let currentPressure = parseFloat(document.getElementById('pressureValue').innerText);
|
||||
let diff = (Math.random() * 0.04 - 0.02);
|
||||
document.getElementById('pressureValue').innerText = (currentPressure + diff).toFixed(2);
|
||||
|
||||
// Czasami zaktualizuj litry
|
||||
if (Math.random() > 0.5) {
|
||||
let currentVol = parseInt(document.getElementById('volumeValue').innerText.replace(/\s/g, ''));
|
||||
currentVol += Math.floor(Math.random() * 3);
|
||||
document.getElementById('volumeValue').innerText = currentVol.toLocaleString('pl-PL');
|
||||
}
|
||||
}, 2000);
|
||||
</script>
|
||||
|
||||
<!-- Modal Konfiguracji Widgetu -->
|
||||
<div class="modal fade" id="widgetConfigModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Skonfiguruj widget: <br><span id="configWidgetName" class="text-primary fs-6 fw-bold"></span></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Wybierz parametr z urządzenia</label>
|
||||
<select class="form-select">
|
||||
<option value="">-- Wybierz --</option>
|
||||
<optgroup label="Urządzenie: 1027 - Moduł WiFi Zewnętrzny">
|
||||
<option>Poziom gazu (%)</option>
|
||||
<option>Ciśnienie (bar)</option>
|
||||
<option>Temperatura otoczenia (°C)</option>
|
||||
</optgroup>
|
||||
<optgroup label="Urządzenie: 1030 - Moduł Zasilania">
|
||||
<option>Napięcie baterii (V)</option>
|
||||
<option>Zasięg GSM (%)</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Anuluj</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Zapisz ustawienia</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-backdrop fade"></div>
|
||||
|
||||
<?php include '../../footer.php'; ?>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
107
prototype/telemetria/installations.php
Normal file
107
prototype/telemetria/installations.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include '../../header-telemetria.php';
|
||||
?>
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<h4 class="fw-bold py-3 mb-4">
|
||||
Telemetria <span class="text-muted fw-light">/ Instalacje</span>
|
||||
</h4>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header border-bottom d-flex flex-column flex-md-row justify-content-between align-items-md-center">
|
||||
<input type="text" class="form-control form-control" placeholder="Szukaj instalacji..." style="width: 200px;">
|
||||
<div class="d-flex flex-column flex-sm-row gap-2 mt-3 mt-md-0">
|
||||
<select class="form-select form-select" style="width: 150px;">
|
||||
<option value="">Wszystkie typy</option>
|
||||
<option value="gas">Zbiornik Gazu</option>
|
||||
<option value="farm">Kurnik / Farma</option>
|
||||
</select>
|
||||
<select class="form-select form-select" style="width: 150px;">
|
||||
<option value="">Każdy status</option>
|
||||
<option value="online">Online</option>
|
||||
<option value="offline">Offline</option>
|
||||
</select>
|
||||
<a href="installation-add.php" class="btn btn-primary">
|
||||
<i class="bx bx-plus-circle me-1"></i> Dodaj nową instalację
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive text-nowrap">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Instalacja</th>
|
||||
<th>Typ</th>
|
||||
<th>Ostatni kontakt</th>
|
||||
<th>Status</th>
|
||||
<th>Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table-border-bottom-0">
|
||||
<!-- Element 1 -->
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Zbiornik LPG #1</strong><br>
|
||||
<small class="text-muted">Stalowa Wola, ul. Przemysłowa</small>
|
||||
</td>
|
||||
<td><span class="badge bg-label-warning"><i class="bx bxs-flame me-1"></i> Zbiornik Gazu</span></td>
|
||||
<td>Przed 2 min</td>
|
||||
<td><span class="badge bg-success">Online</span></td>
|
||||
<td>
|
||||
<a href="installation-live.php" class="btn btn-sm btn-icon btn-outline-primary" title="Podgląd Live">
|
||||
<i class="bx bx-show"></i>
|
||||
</a>
|
||||
<a href="installation-edit.php" class="btn btn-sm btn-icon btn-outline-secondary" title="Edycja">
|
||||
<i class="bx bx-pencil"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Element 2 -->
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Kurnik B (Sektor 2)</strong><br>
|
||||
<small class="text-muted">Farma Wola, Obiekt Główny</small>
|
||||
</td>
|
||||
<td><span class="badge bg-label-info"><i class="bx bxs-thermometer me-1"></i> Sterownik Kurnika</span></td>
|
||||
<td>Przed 1 min</td>
|
||||
<td><span class="badge bg-success">Online</span></td>
|
||||
<td>
|
||||
<a href="installation-live.php" class="btn btn-sm btn-icon btn-outline-primary" title="Podgląd Live">
|
||||
<i class="bx bx-show"></i>
|
||||
</a>
|
||||
<a href="installation-edit.php" class="btn btn-sm btn-icon btn-outline-secondary" title="Edycja">
|
||||
<i class="bx bx-pencil"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Element 3 -->
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Zbiornik LPG #2</strong><br>
|
||||
<small class="text-muted">Rzeszów, Baza Północ</small>
|
||||
</td>
|
||||
<td><span class="badge bg-label-warning"><i class="bx bxs-flame me-1"></i> Zbiornik Gazu</span></td>
|
||||
<td>3 dni temu</td>
|
||||
<td><span class="badge bg-danger">Offline</span></td>
|
||||
<td>
|
||||
<a href="installation-live.php" class="btn btn-sm btn-icon btn-outline-primary" title="Podgląd Live">
|
||||
<i class="bx bx-show"></i>
|
||||
</a>
|
||||
<a href="installation-edit.php" class="btn btn-sm btn-icon btn-outline-secondary" title="Edycja">
|
||||
<i class="bx bx-pencil"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-backdrop fade"></div>
|
||||
|
||||
<?php include '../../footer.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
64
prototype/telemetria/menu.php
Normal file
64
prototype/telemetria/menu.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
$currentPage = basename($_SERVER['PHP_SELF']);
|
||||
?>
|
||||
<aside id="layout-menu" class="layout-menu menu-vertical menu bg-menu-theme">
|
||||
<div class="app-brand demo">
|
||||
<a href="index.php" class="app-brand-link">
|
||||
<span class="app-brand-text demo menu-text fw-bold ms-2">telemetria.magico</span>
|
||||
</a>
|
||||
<a href="javascript:void(0);" class="layout-menu-toggle menu-link text-large ms-auto">
|
||||
<i class="bx bx-chevron-left bx-sm align-middle"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="menu-inner-shadow"></div>
|
||||
|
||||
<ul class="menu-inner py-1">
|
||||
|
||||
<li class="menu-header small text-uppercase">
|
||||
<span class="menu-header-text" data-i18n="Telemetria">Telemetria</span>
|
||||
</li>
|
||||
|
||||
<li class="menu-item <?php echo ($currentPage == 'index.php' || $currentPage == 'dashboard-2.php') ? 'active open' : ''; ?>">
|
||||
<a href="javascript:void(0);" class="menu-link menu-toggle">
|
||||
<i class="menu-icon tf-icons bx bx-home-circle"></i>
|
||||
<div class="text-truncate" data-i18n="Dashboardy">Dashboardy</div>
|
||||
</a>
|
||||
<ul class="menu-sub">
|
||||
<li class="menu-item <?php echo ($currentPage == 'index.php') ? 'active' : ''; ?>">
|
||||
<a href="index.php" class="menu-link">
|
||||
<div class="text-truncate" data-i18n="Główny">Główny</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<a href="#" class="menu-link">
|
||||
<div class="text-truncate" data-i18n="Tylko LPG">Tylko LPG</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<a href="#" class="menu-link">
|
||||
<div class="text-truncate" data-i18n="Raport Dzienny">Raport Dzienny</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="menu-item <?php echo ($currentPage == 'installations.php') ? 'active' : ''; ?>">
|
||||
<a href="installations.php" class="menu-link">
|
||||
<i class="menu-icon tf-icons bx bx-buildings"></i>
|
||||
<div class="text-truncate" data-i18n="Instalacje">Instalacje</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="menu-item <?php echo ($currentPage == 'devices.php') ? 'active' : ''; ?>">
|
||||
<a href="devices.php" class="menu-link">
|
||||
<i class="menu-icon tf-icons bx bx-chip"></i>
|
||||
<div class="text-truncate" data-i18n="Urządzenia">Urządzenia (Sprzęt)</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="menu-item <?php echo ($currentPage == 'reports.php') ? 'active' : ''; ?>">
|
||||
<a href="reports.php" class="menu-link">
|
||||
<i class="menu-icon tf-icons bx bx-bar-chart-alt-2"></i>
|
||||
<div class="text-truncate" data-i18n="Raporty">Raporty</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
366
prototype/telemetria/report-consumption.php
Normal file
366
prototype/telemetria/report-consumption.php
Normal file
@@ -0,0 +1,366 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include '../../header-telemetria.php';
|
||||
?>
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<!-- Vendors CSS dla Select2 i Flatpickr -->
|
||||
<link rel="stylesheet" href="../../assets/vendor/libs/select2/select2.css" />
|
||||
<link rel="stylesheet" href="../../assets/vendor/libs/flatpickr/flatpickr.css" />
|
||||
|
||||
<!-- Nagłówek i przyciski akcji -->
|
||||
<div
|
||||
class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center mb-4 gap-3">
|
||||
<div>
|
||||
<h4 class="fw-bold mb-1">
|
||||
<span class="text-muted fw-light">Raporty /</span> Historia zużycia medium
|
||||
</h4>
|
||||
<p class="text-muted mb-0">Zestawienie konsumpcji gazu / ubytków poziomu dla wskazanej instalacji we wskazanym okresie.</p>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-label-secondary">
|
||||
<i class="bx bx-export me-1"></i> Eksportuj CSV
|
||||
</button>
|
||||
<button class="btn btn-primary">
|
||||
<i class="bx bx-printer me-1"></i> Drukuj raport
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Karta filtrów -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<form class="row g-3">
|
||||
<div class="col-12 col-md-3">
|
||||
<label class="form-label" for="reportInstallation">Instalacja</label>
|
||||
<select id="reportInstallation" class="form-select select2">
|
||||
<option value="all">Wszystkie zebrane razem</option>
|
||||
<option value="1" selected>Zbiornik LPG #1 (Stalowa Wola)</option>
|
||||
<option value="2">Zbiornik LPG #2 (Rzeszów)</option>
|
||||
<option value="3">Kurnik Główny</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12 col-md-2">
|
||||
<label class="form-label d-block"> </label>
|
||||
<div class="btn-group w-100">
|
||||
<button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bx bx-calendar me-1"></i> Zakres
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><h6 class="dropdown-header text-uppercase">Dni</h6></li>
|
||||
<li><a class="dropdown-item preset-date-range" href="javascript:void(0);" data-range="today">Dziś</a></li>
|
||||
<li><a class="dropdown-item preset-date-range" href="javascript:void(0);" data-range="this-week">Ten tydzień</a></li>
|
||||
<li><a class="dropdown-item preset-date-range" href="javascript:void(0);" data-range="last-week">Poprzedni tydzień</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><h6 class="dropdown-header text-uppercase">Miesiące</h6></li>
|
||||
<li><a class="dropdown-item preset-date-range" href="javascript:void(0);" data-range="this-month">Ten miesiąc</a></li>
|
||||
<li><a class="dropdown-item preset-date-range" href="javascript:void(0);" data-range="last-month">Poprzedni miesiąc</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><h6 class="dropdown-header text-uppercase">Lata</h6></li>
|
||||
<li><a class="dropdown-item preset-date-range" href="javascript:void(0);" data-range="this-year">Ten rok</a></li>
|
||||
<li><a class="dropdown-item preset-date-range" href="javascript:void(0);" data-range="last-year">Poprzedni rok</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-2">
|
||||
<label class="form-label" for="reportDateFrom">Data od</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span class="input-group-text"><i class="bx bx-calendar"></i></span>
|
||||
<input type="text" id="reportDateFrom" class="form-control flatpickr-date"
|
||||
placeholder="YYYY-MM-DD" value="2026-01-01" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-2">
|
||||
<label class="form-label" for="reportDateTo">Data do</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span class="input-group-text"><i class="bx bx-calendar"></i></span>
|
||||
<input type="text" id="reportDateTo" class="form-control flatpickr-date"
|
||||
placeholder="YYYY-MM-DD" value="2026-06-30" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-2">
|
||||
<label class="form-label" for="reportGranularity">Szczegółowość</label>
|
||||
<select id="reportGranularity" class="form-select select2">
|
||||
<option value="days">Dni</option>
|
||||
<option value="weeks">Tygodnie</option>
|
||||
<option value="months" selected>Miesiące</option>
|
||||
</select>
|
||||
<small id="daysLimitInfo" class="text-muted d-none mt-1 d-block">
|
||||
<i class="bx bx-info-circle fs-6 align-middle me-1"></i>Max 366 dni.
|
||||
</small>
|
||||
</div>
|
||||
<div class="col-12 col-md-1 d-flex align-items-end">
|
||||
<button type="button" class="btn btn-primary w-100 w-md-auto px-3" title="Generuj raport">
|
||||
<i class="bx bx-refresh"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Karta z wykresem -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="card-title m-0">Zużycie medium w czasie (litry)</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="consumptionReportChart" style="min-height: 350px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Karta z tabelą danych -->
|
||||
<div class="card">
|
||||
<div class="card-header border-bottom">
|
||||
<h5 class="card-title m-0">Szczegółowe zestawienie</h5>
|
||||
</div>
|
||||
<div class="table-responsive text-nowrap">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Okres</th>
|
||||
<th class="text-center">Liczba odczytów</th>
|
||||
<th class="text-end">Stan początkowy</th>
|
||||
<th class="text-end">Stan końcowy</th>
|
||||
<th class="text-end">Całkowite zużycie</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="fw-medium">Styczeń 2026</td>
|
||||
<td class="text-center">744</td>
|
||||
<td class="text-end">2100 l (77%)</td>
|
||||
<td class="text-end">1550 l (57%)</td>
|
||||
<td class="text-end fw-bold text-danger">-550 l</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-medium">Luty 2026</td>
|
||||
<td class="text-center">672</td>
|
||||
<td class="text-end">1550 l (57%)</td>
|
||||
<td class="text-end">850 l (31%)</td>
|
||||
<td class="text-end fw-bold text-danger">-700 l</td>
|
||||
</tr>
|
||||
<tr class="table-primary bg-label-primary">
|
||||
<td class="fw-medium">Marzec 2026 <i class="bx bx-gas-pump ms-1 text-primary" title="Zarejestrowano tankowanie"></i></td>
|
||||
<td class="text-center">744</td>
|
||||
<td class="text-end">850 l (31%)</td>
|
||||
<td class="text-end">2500 l (92%)</td>
|
||||
<td class="text-end fw-bold text-success">+1650 l</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-medium">Kwiecień 2026</td>
|
||||
<td class="text-center">720</td>
|
||||
<td class="text-end">2500 l (92%)</td>
|
||||
<td class="text-end">2100 l (77%)</td>
|
||||
<td class="text-end fw-bold text-danger">-400 l</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-medium">Maj 2026</td>
|
||||
<td class="text-center">744</td>
|
||||
<td class="text-end">2100 l (77%)</td>
|
||||
<td class="text-end">1800 l (66%)</td>
|
||||
<td class="text-end fw-bold text-danger">-300 l</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-medium">Czerwiec 2026</td>
|
||||
<td class="text-center">360</td>
|
||||
<td class="text-end">1800 l (66%)</td>
|
||||
<td class="text-end">1620 l (60%)</td>
|
||||
<td class="text-end fw-bold text-danger">-180 l</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot class="table-light fw-bold">
|
||||
<tr>
|
||||
<td>Podsumowanie</td>
|
||||
<td class="text-center">3984</td>
|
||||
<td class="text-end text-muted">-</td>
|
||||
<td class="text-end text-muted">-</td>
|
||||
<td class="text-end text-primary fs-5">Zbilansowano: -480 l</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php include '../../footer.php'; ?>
|
||||
|
||||
<!-- Vendors JS -->
|
||||
<script src="../../assets/vendor/libs/select2/select2.js"></script>
|
||||
<script src="../../assets/vendor/libs/flatpickr/flatpickr.js"></script>
|
||||
|
||||
<!-- ApexCharts scripts and local chart init -->
|
||||
<script src="../../assets/vendor/libs/apex-charts/apexcharts.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// 1. Inicjalizacja Flatpickr dla zakresu dat
|
||||
if (typeof flatpickr !== 'undefined') {
|
||||
const dateInputs = document.querySelectorAll('.flatpickr-date');
|
||||
let fpInstances = [];
|
||||
dateInputs.forEach(el => {
|
||||
fpInstances.push(flatpickr(el, {
|
||||
dateFormat: 'Y-m-d',
|
||||
locale: 'pl'
|
||||
}));
|
||||
});
|
||||
|
||||
if (fpInstances.length === 2 && typeof $ !== 'undefined') {
|
||||
const fpFrom = fpInstances[0];
|
||||
const fpTo = fpInstances[1];
|
||||
|
||||
document.querySelectorAll('.preset-date-range').forEach(item => {
|
||||
item.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
const rangeType = this.getAttribute('data-range');
|
||||
|
||||
const today = new Date();
|
||||
let startDate = new Date();
|
||||
let endDate = new Date();
|
||||
|
||||
switch (rangeType) {
|
||||
case 'today':
|
||||
break;
|
||||
case 'this-week':
|
||||
const day = today.getDay() || 7;
|
||||
startDate.setDate(today.getDate() - day + 1);
|
||||
break;
|
||||
case 'last-week':
|
||||
const lastWeek = new Date(today);
|
||||
lastWeek.setDate(today.getDate() - 7);
|
||||
const dayLW = lastWeek.getDay() || 7;
|
||||
startDate = new Date(lastWeek);
|
||||
startDate.setDate(lastWeek.getDate() - dayLW + 1);
|
||||
endDate = new Date(lastWeek);
|
||||
endDate.setDate(lastWeek.getDate() - dayLW + 7);
|
||||
break;
|
||||
case 'this-month':
|
||||
startDate = new Date(today.getFullYear(), today.getMonth(), 1);
|
||||
endDate = new Date(today.getFullYear(), today.getMonth() + 1, 0);
|
||||
break;
|
||||
case 'last-month':
|
||||
startDate = new Date(today.getFullYear(), today.getMonth() - 1, 1);
|
||||
endDate = new Date(today.getFullYear(), today.getMonth(), 0);
|
||||
break;
|
||||
case 'this-year':
|
||||
startDate = new Date(today.getFullYear(), 0, 1);
|
||||
endDate = new Date(today.getFullYear(), 11, 31);
|
||||
break;
|
||||
case 'last-year':
|
||||
startDate = new Date(today.getFullYear() - 1, 0, 1);
|
||||
endDate = new Date(today.getFullYear() - 1, 11, 31);
|
||||
break;
|
||||
}
|
||||
|
||||
if (startDate && endDate) {
|
||||
fpFrom.setDate(startDate);
|
||||
fpTo.setDate(endDate);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Inicjalizacja Select2
|
||||
if (typeof $ !== 'undefined' && $('.select2').length) {
|
||||
$('.select2').select2({
|
||||
minimumResultsForSearch: 5
|
||||
});
|
||||
|
||||
// Obsługa pojawiania się podpowiedzi po wybraniu z selecta
|
||||
$('#reportGranularity').on('change', function () {
|
||||
const daysInfo = document.getElementById('daysLimitInfo');
|
||||
if (this.value === 'days') {
|
||||
daysInfo.classList.remove('d-none');
|
||||
} else {
|
||||
daysInfo.classList.add('d-none');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Inicjalizacja Wykresu
|
||||
const chartEl = document.querySelector('#consumptionReportChart');
|
||||
if (chartEl && typeof ApexCharts !== 'undefined') {
|
||||
const chartConfig = {
|
||||
chart: {
|
||||
height: 350,
|
||||
type: 'area', // Zmieniono na area, by ładnie pokazywać spadek/wzrost litrów
|
||||
parentHeightOffset: 0,
|
||||
toolbar: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
width: 3
|
||||
},
|
||||
series: [{
|
||||
name: 'Stan początkowy w danym miesiącu (litry)',
|
||||
data: [2100, 1550, 850, 2500, 2100, 1800]
|
||||
}],
|
||||
xaxis: {
|
||||
categories: ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec'],
|
||||
axisBorder: {
|
||||
show: false
|
||||
},
|
||||
axisTicks: {
|
||||
show: false
|
||||
},
|
||||
labels: {
|
||||
style: {
|
||||
colors: '#a1acb8',
|
||||
fontSize: '13px'
|
||||
}
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
formatter: function (val) {
|
||||
return val.toLocaleString('pl-PL') + " l";
|
||||
},
|
||||
style: {
|
||||
colors: '#a1acb8',
|
||||
fontSize: '13px'
|
||||
}
|
||||
}
|
||||
},
|
||||
colors: ['#00cfdd'], // Kolor np. cyan
|
||||
fill: {
|
||||
type: 'gradient',
|
||||
gradient: {
|
||||
shadeIntensity: 1,
|
||||
opacityFrom: 0.5,
|
||||
opacityTo: 0.1,
|
||||
stops: [0, 90, 100]
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
borderColor: '#e9ecef',
|
||||
strokeDashArray: 4,
|
||||
padding: {
|
||||
top: -20,
|
||||
bottom: -10,
|
||||
left: 20,
|
||||
right: 20
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
y: {
|
||||
formatter: function (val) {
|
||||
return val.toLocaleString('pl-PL') + " l";
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const consumptionChart = new ApexCharts(chartEl, chartConfig);
|
||||
consumptionChart.render();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
134
prototype/telemetria/reports.php
Normal file
134
prototype/telemetria/reports.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include '../../header-telemetria.php';
|
||||
?>
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<!-- Header with description -->
|
||||
<div class="card p-0 mb-4">
|
||||
<div class="card-body d-flex flex-column flex-md-row justify-content-between p-0">
|
||||
|
||||
<!-- Left image -->
|
||||
<div class="d-none d-md-flex app-academy-md-25 card-body py-0 pt-3 ps-5 align-items-center">
|
||||
<img src="../../assets/img/illustrations/bulb-light.png" width="90" class="img-fluid" alt="Bulb"
|
||||
data-app-light-img="illustrations/bulb-light.png" data-app-dark-img="illustrations/bulb-dark.png">
|
||||
</div>
|
||||
|
||||
<!-- Center context -->
|
||||
<div class="app-academy-md-50 card-body d-flex align-items-md-center flex-column text-md-center mb-1 py-5">
|
||||
<h4 class="card-title mb-4 px-md-12 lh-base fw-bold">
|
||||
Analizuj dane z instalacji<br>
|
||||
w <span class="text-primary text-nowrap">przejrzystych raportach</span>.
|
||||
</h4>
|
||||
<p class="mb-4 col-md-10 text-muted">
|
||||
Poniżej prezentujemy dostępne raporty telemetryczne. Wybierz zestawienie, aby
|
||||
przeanalizować historię zużycia medium, zidentyfikować anomalie oraz zweryfikować
|
||||
stabilność połączeń na Twoich obiektach.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Right image -->
|
||||
<div class="d-none d-md-flex app-academy-md-25 align-items-end justify-content-end pe-4 pb-2">
|
||||
<img src="../../assets/img/illustrations/pencil-rocket.png" alt="pencil rocket" height="150"
|
||||
class="scaleX-n1-rtl">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Reports Gallery -->
|
||||
<div class="card p-4">
|
||||
<div class="row">
|
||||
|
||||
<!-- Raport historii zużycia -->
|
||||
<div class="col-sm-6 col-lg-4 mb-4">
|
||||
<div class="card p-2 h-100 shadow-none border border-primary">
|
||||
<div class="card-body p-4 pt-2 d-flex flex-column">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<span class="badge bg-label-primary">Eksploatacja</span>
|
||||
</div>
|
||||
<a href="report-consumption.php" class="h5 mb-2 fw-semibold text-primary">Historia zużycia medium</a>
|
||||
<p class="mt-1 text-muted mb-4 flex-grow-1">
|
||||
Wykres i tabela prezentująca ubytki / konsumpcję gazu (lub innego medium) dla wskazanej instalacji w danym przedziale czasowym. Oblicza dzienne i miesięczne zużycie.
|
||||
</p>
|
||||
<div class="d-flex flex-column flex-md-row gap-4 mt-auto">
|
||||
<a class="w-100 btn btn-primary d-flex align-items-center justify-content-center"
|
||||
href="report-consumption.php">
|
||||
<span class="me-2">Wygeneruj</span>
|
||||
<i class="bx bx-chevron-right icon-sm lh-1 scaleX-n1-rtl"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dziennik alertów i przekroczeń -->
|
||||
<div class="col-sm-6 col-lg-4 mb-4">
|
||||
<div class="card p-2 h-100 shadow-none border">
|
||||
<div class="card-body p-4 pt-2 d-flex flex-column">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<span class="badge bg-label-danger">Bezpieczeństwo</span>
|
||||
</div>
|
||||
<a href="javascript:void(0);" class="h5 mb-2 fw-semibold">Dziennik alertów</a>
|
||||
<p class="mt-1 text-muted mb-4 flex-grow-1">
|
||||
Ewidencja wszystkich wygenerowanych alarmów, w tym przekroczeń ciśnienia, krytycznie niskich stanów, z datami ich wystąpienia i powiadomionymi osobami.
|
||||
</p>
|
||||
<div class="d-flex flex-column flex-md-row gap-4 mt-auto">
|
||||
<a class="w-100 btn btn-label-primary d-flex align-items-center justify-content-center"
|
||||
href="javascript:void(0);">
|
||||
<span class="me-2">Wygeneruj</span>
|
||||
<i class="bx bx-chevron-right icon-sm lh-1 scaleX-n1-rtl"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Analiza stabilności infrastruktury -->
|
||||
<div class="col-sm-6 col-lg-4 mb-4">
|
||||
<div class="card p-2 h-100 shadow-none border">
|
||||
<div class="card-body p-4 pt-2 d-flex flex-column">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<span class="badge bg-label-warning">Serwis i Sprzęt</span>
|
||||
</div>
|
||||
<a href="javascript:void(0);" class="h5 mb-2 fw-semibold">Analiza stabilności sieci</a>
|
||||
<p class="mt-1 text-muted mb-4 flex-grow-1">
|
||||
Monitorowanie przerw w łączności GSM, skoków napięcia i krzywej rozładowania baterii. Służy do planowania prac serwisowych (Predictive Maintenance).
|
||||
</p>
|
||||
<div class="d-flex flex-column flex-md-row gap-4 mt-auto">
|
||||
<a class="w-100 btn btn-label-primary d-flex align-items-center justify-content-center"
|
||||
href="javascript:void(0);">
|
||||
<span class="me-2">Wygeneruj</span>
|
||||
<i class="bx bx-chevron-right icon-sm lh-1 scaleX-n1-rtl"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sumaryczny bilans instalacji -->
|
||||
<div class="col-sm-6 col-lg-4 mb-4">
|
||||
<div class="card p-2 h-100 shadow-none border">
|
||||
<div class="card-body p-4 pt-2 d-flex flex-column">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<span class="badge bg-label-info">Podsumowanie całkowite</span>
|
||||
</div>
|
||||
<a href="javascript:void(0);" class="h5 mb-2 fw-semibold">Bilans flotowy</a>
|
||||
<p class="mt-1 text-muted mb-4 flex-grow-1">
|
||||
Całkowite zestawienie posiadanych zasobów na dany dzień – sumuje dostępne litry gazu we wszystkich połączonych zbiornikach, ułatwiając logistykę tankowania.
|
||||
</p>
|
||||
<div class="d-flex flex-column flex-md-row gap-4 mt-auto">
|
||||
<a class="w-100 btn btn-label-primary d-flex align-items-center justify-content-center"
|
||||
href="javascript:void(0);">
|
||||
<span class="me-2">Wygeneruj</span>
|
||||
<i class="bx bx-chevron-right icon-sm lh-1 scaleX-n1-rtl"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include '../../footer.php'; ?>
|
||||
Reference in New Issue
Block a user