Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d17cba2bbb | ||
|
|
8c42888f52 | ||
|
|
373bffa573 | ||
|
|
af946d338a | ||
|
|
2d749776fc | ||
|
|
351898cdc5 | ||
|
|
b3a24f821b | ||
|
|
9648214675 | ||
|
|
8ed8d3bb42 | ||
|
|
a9327760d0 | ||
|
|
caaf914865 | ||
|
|
83d3499164 | ||
|
|
83c055cc09 | ||
|
|
9b3c3848a7 |
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "My Server",
|
||||
"host": "prototype.s6.magicosoft.pl",
|
||||
"protocol": "ftp",
|
||||
"port": 21,
|
||||
"username": "bartek@prototype.s6.magicosoft.pl",
|
||||
"remotePath": "/public_html/",
|
||||
"password": "dZURD2M9RyMTh63cEGQ5",
|
||||
"uploadOnSave": true,
|
||||
"useTempFile": false,
|
||||
"openSsh": false,
|
||||
"watcher": {
|
||||
"files": "prototype/**/*",
|
||||
"autoUpload": true,
|
||||
"autoDelete": false
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 60 KiB |
@@ -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">
|
||||
@@ -0,0 +1,420 @@
|
||||
/**
|
||||
* Superpilot Web — Logika Wyszukiwarki i Filtrów w Stylu Pracuj.pl
|
||||
* z Multiselectem Krajów w Górnej Belce oraz Bazą w Lewym Sidebarze
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
initPracujFilters();
|
||||
initTopDestMultiselect();
|
||||
initDateRangeModal();
|
||||
initModals();
|
||||
});
|
||||
|
||||
function initPracujFilters() {
|
||||
const searchInput = document.getElementById('mainSearchInput');
|
||||
const executeBtn = document.getElementById('btnExecuteSearch');
|
||||
const sidebarCheckboxes = document.querySelectorAll('.filter-checkbox');
|
||||
const clearBtn = document.getElementById('btnClearAllFilters');
|
||||
const sidebarCountSpan = document.getElementById('sidebarCountSpan');
|
||||
const offersCountText = document.getElementById('offersCountText');
|
||||
const noOffersAlert = document.getElementById('noOffersAlert');
|
||||
const pilotCards = document.querySelectorAll('.pilot-grid-item');
|
||||
|
||||
window.currentDateFilter = '16-24-sep-2027';
|
||||
|
||||
function applyFilters() {
|
||||
const query = searchInput ? searchInput.value.toLowerCase().trim() : '';
|
||||
|
||||
// Pobierz zaznaczone kraje z GÓRNEJ BELKI (Multiselect)
|
||||
const selectedTopDests = Array.from(document.querySelectorAll('.top-dest-checkbox:checked')).map(cb => cb.value.toLowerCase());
|
||||
|
||||
// Pobierz zaznaczone checkboxy z SIDEBARA (w tym nowa Baza!)
|
||||
const selectedBases = Array.from(document.querySelectorAll('.filter-checkbox[data-type="base"]:checked')).map(cb => cb.value.toLowerCase());
|
||||
const selectedSpecs = Array.from(document.querySelectorAll('.filter-checkbox[data-type="spec"]:checked')).map(cb => cb.value.toLowerCase());
|
||||
const selectedLangs = Array.from(document.querySelectorAll('.filter-checkbox[data-type="lang"]:checked')).map(cb => cb.value.toLowerCase());
|
||||
const selectedCerts = Array.from(document.querySelectorAll('.filter-checkbox[data-type="cert"]:checked')).map(cb => cb.value.toLowerCase());
|
||||
|
||||
let visibleCount = 0;
|
||||
|
||||
pilotCards.forEach(card => {
|
||||
const name = (card.getAttribute('data-name') || '').toLowerCase();
|
||||
const base = (card.getAttribute('data-base') || '').toLowerCase();
|
||||
const dests = (card.getAttribute('data-destinations') || '').toLowerCase();
|
||||
const langs = (card.getAttribute('data-languages') || '').toLowerCase();
|
||||
const tags = (card.getAttribute('data-tags') || '').toLowerCase();
|
||||
const rating = parseFloat(card.getAttribute('data-rating') || '0');
|
||||
const verified = card.getAttribute('data-verified') === '1';
|
||||
const kpp = card.getAttribute('data-kpp') === '1';
|
||||
const datesFree = (card.getAttribute('data-dates-free') || '');
|
||||
|
||||
let match = true;
|
||||
|
||||
// 1. Wyszukiwanie tekstowe
|
||||
if (query && !name.includes(query) && !dests.includes(query) && !tags.includes(query) && !langs.includes(query) && !base.includes(query)) {
|
||||
match = false;
|
||||
}
|
||||
|
||||
// 2. Destynacje z Multiselecta u góry
|
||||
if (selectedTopDests.length > 0) {
|
||||
const hasAnyDest = selectedTopDests.some(d => dests.includes(d));
|
||||
if (!hasAnyDest) match = false;
|
||||
}
|
||||
|
||||
// 3. Baza wypadowa z sidebara
|
||||
if (selectedBases.length > 0) {
|
||||
const hasAnyBase = selectedBases.some(b => base.includes(b));
|
||||
if (!hasAnyBase) match = false;
|
||||
}
|
||||
|
||||
// 4. Specjalizacje z sidebara
|
||||
if (selectedSpecs.length > 0) {
|
||||
const hasAnySpec = selectedSpecs.some(s => tags.includes(s));
|
||||
if (!hasAnySpec) match = false;
|
||||
}
|
||||
|
||||
// 5. Języki z sidebara
|
||||
if (selectedLangs.length > 0) {
|
||||
const hasAnyLang = selectedLangs.some(l => langs.includes(l));
|
||||
if (!hasAnyLang) match = false;
|
||||
}
|
||||
|
||||
// 6. Certyfikaty z sidebara
|
||||
if (selectedCerts.includes('verified') && !verified) match = false;
|
||||
if (selectedCerts.includes('kpp') && !kpp) match = false;
|
||||
if (selectedCerts.includes('rating-48') && rating < 4.8) match = false;
|
||||
|
||||
// 7. Kalendarz
|
||||
if (window.currentDateFilter === '16-24-sep-2027') {
|
||||
if (!datesFree.includes('16-24-sep') && name !== 'oskar kaczmarek') {
|
||||
match = false;
|
||||
}
|
||||
} else if (window.currentDateFilter === '05-15-sep-2027') {
|
||||
if (!datesFree.includes('05-15-sep')) {
|
||||
match = false;
|
||||
}
|
||||
} else if (window.currentDateFilter === 'październik-2027') {
|
||||
if (!datesFree.includes('październik-2027')) {
|
||||
match = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
card.style.display = 'flex';
|
||||
visibleCount++;
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Aktualizacja liczników
|
||||
if (sidebarCountSpan) sidebarCountSpan.innerText = visibleCount;
|
||||
if (offersCountText) offersCountText.innerText = visibleCount + (visibleCount === 1 ? ' oferta' : (visibleCount >= 2 && visibleCount <= 4 ? ' oferty' : ' ofert'));
|
||||
if (noOffersAlert) noOffersAlert.style.display = (visibleCount === 0) ? 'block' : 'none';
|
||||
|
||||
// Aktualizacja paska aktywnych filtrów (Chips)
|
||||
updateActiveFilterChips(selectedTopDests, selectedBases);
|
||||
}
|
||||
|
||||
if (searchInput) searchInput.addEventListener('input', applyFilters);
|
||||
if (executeBtn) executeBtn.addEventListener('click', applyFilters);
|
||||
|
||||
sidebarCheckboxes.forEach(cb => {
|
||||
cb.addEventListener('change', applyFilters);
|
||||
});
|
||||
|
||||
const applySidebarBtn = document.getElementById('btnApplySidebarFilters');
|
||||
if (applySidebarBtn) {
|
||||
applySidebarBtn.addEventListener('click', function () {
|
||||
applyFilters();
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
});
|
||||
}
|
||||
|
||||
if (clearBtn) {
|
||||
clearBtn.addEventListener('click', function () {
|
||||
if (searchInput) searchInput.value = '';
|
||||
sidebarCheckboxes.forEach(cb => cb.checked = false);
|
||||
document.querySelectorAll('.top-dest-checkbox').forEach(cb => cb.checked = false);
|
||||
updateDestTriggerLabel();
|
||||
window.currentDateFilter = 'all';
|
||||
const display = document.getElementById('selectedDateDisplay');
|
||||
if (display) display.value = 'Dowolny termin';
|
||||
applyFilters();
|
||||
});
|
||||
}
|
||||
|
||||
// Obsługa usunięcia chipa bazy
|
||||
const removeBaseChip = document.getElementById('removeBaseChip');
|
||||
if (removeBaseChip) {
|
||||
removeBaseChip.addEventListener('click', function () {
|
||||
document.querySelectorAll('.filter-checkbox[data-type="base"]').forEach(cb => cb.checked = false);
|
||||
applyFilters();
|
||||
});
|
||||
}
|
||||
|
||||
window.triggerSuperpilotFilter = applyFilters;
|
||||
}
|
||||
|
||||
function initTopDestMultiselect() {
|
||||
const destCheckboxes = document.querySelectorAll('.top-dest-checkbox');
|
||||
const triggerText = document.getElementById('destTriggerText');
|
||||
const resetBtn = document.getElementById('btnResetDestMultiselect');
|
||||
const confirmBtn = document.getElementById('btnConfirmDestDropdown');
|
||||
|
||||
function updateLabel() {
|
||||
const checked = Array.from(document.querySelectorAll('.top-dest-checkbox:checked')).map(cb => cb.value);
|
||||
if (!triggerText) return;
|
||||
|
||||
if (checked.length === 0) {
|
||||
triggerText.innerText = 'Wszystkie kraje';
|
||||
triggerText.style.color = '#94a3b8';
|
||||
} else if (checked.length === 1) {
|
||||
const flag = getCountryFlag(checked[0]);
|
||||
triggerText.innerText = flag + ' ' + checked[0];
|
||||
triggerText.style.color = 'var(--dark-navy)';
|
||||
} else if (checked.length === 2) {
|
||||
triggerText.innerText = checked.map(c => getCountryFlag(c) + ' ' + c).join(', ');
|
||||
triggerText.style.color = 'var(--dark-navy)';
|
||||
} else {
|
||||
triggerText.innerText = getCountryFlag(checked[0]) + ' ' + checked[0] + ' (+' + (checked.length - 1) + ' inne)';
|
||||
triggerText.style.color = 'var(--dark-navy)';
|
||||
}
|
||||
|
||||
if (window.triggerSuperpilotFilter) window.triggerSuperpilotFilter();
|
||||
}
|
||||
|
||||
window.updateDestTriggerLabel = updateLabel;
|
||||
|
||||
destCheckboxes.forEach(cb => {
|
||||
cb.addEventListener('change', updateLabel);
|
||||
});
|
||||
|
||||
if (resetBtn) {
|
||||
resetBtn.addEventListener('click', function () {
|
||||
destCheckboxes.forEach(cb => cb.checked = false);
|
||||
updateLabel();
|
||||
});
|
||||
}
|
||||
|
||||
if (confirmBtn) {
|
||||
confirmBtn.addEventListener('click', function () {
|
||||
const dropdownEl = document.getElementById('destDropdownTrigger');
|
||||
const dropdown = bootstrap.Dropdown.getInstance(dropdownEl);
|
||||
if (dropdown) dropdown.hide();
|
||||
});
|
||||
}
|
||||
|
||||
const removeDestChip = document.getElementById('removeDestChip');
|
||||
if (removeDestChip) {
|
||||
removeDestChip.addEventListener('click', function () {
|
||||
destCheckboxes.forEach(cb => cb.checked = false);
|
||||
updateLabel();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateActiveFilterChips(selectedDests, selectedBases) {
|
||||
const chipDest = document.getElementById('chipDestFilter');
|
||||
const chipDestText = document.getElementById('chipDestText');
|
||||
const chipDate = document.getElementById('chipDateFilter');
|
||||
const chipDateText = document.getElementById('chipDateText');
|
||||
const chipBase = document.getElementById('chipBaseFilter');
|
||||
const chipBaseText = document.getElementById('chipBaseText');
|
||||
|
||||
if (chipDest && chipDestText) {
|
||||
if (selectedDests && selectedDests.length > 0) {
|
||||
chipDest.style.display = 'inline-flex';
|
||||
chipDestText.innerText = selectedDests.map(d => capitalize(d)).join(', ');
|
||||
} else {
|
||||
chipDest.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
if (chipDate && chipDateText) {
|
||||
if (window.currentDateFilter && window.currentDateFilter !== 'all') {
|
||||
chipDate.style.display = 'inline-flex';
|
||||
if (window.currentDateFilter === '16-24-sep-2027') chipDateText.innerText = '16–24.09.2027';
|
||||
else if (window.currentDateFilter === '05-15-sep-2027') chipDateText.innerText = '05–15.09.2027';
|
||||
else if (window.currentDateFilter === 'październik-2027') chipDateText.innerText = 'Październik 2027';
|
||||
} else {
|
||||
chipDate.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
if (chipBase && chipBaseText) {
|
||||
if (selectedBases && selectedBases.length > 0) {
|
||||
chipBase.style.display = 'inline-flex';
|
||||
chipBaseText.innerText = 'Baza: ' + selectedBases.map(b => capitalize(b)).join(', ');
|
||||
} else {
|
||||
chipBase.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initDateRangeModal() {
|
||||
const presets = document.querySelectorAll('.preset-chip');
|
||||
const selectedText = document.getElementById('modalSelectedDateText');
|
||||
const displayInput = document.getElementById('selectedDateDisplay');
|
||||
const resetDateBtn = document.getElementById('btnResetDateDialog');
|
||||
const applyDateBtn = document.getElementById('btnApplyDateDialog');
|
||||
const removeDateChip = document.getElementById('removeDateChip');
|
||||
|
||||
presets.forEach(p => {
|
||||
p.addEventListener('click', function () {
|
||||
presets.forEach(item => item.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
|
||||
const presetVal = this.getAttribute('data-preset');
|
||||
window.currentDateFilter = presetVal;
|
||||
|
||||
if (presetVal === '16-24-sep-2027') {
|
||||
selectedText.innerText = '16.09.2027 – 24.09.2027 (Wycieczka #12)';
|
||||
displayInput.value = '16.09.2027 – 24.09.2027';
|
||||
} else if (presetVal === '05-15-sep-2027') {
|
||||
selectedText.innerText = '05.09.2027 – 15.09.2027 (Wolne okienko)';
|
||||
displayInput.value = '05.09.2027 – 15.09.2027';
|
||||
} else if (presetVal === 'październik-2027') {
|
||||
selectedText.innerText = '01.10.2027 – 31.10.2027 (Październik)';
|
||||
displayInput.value = 'Październik 2027';
|
||||
} else {
|
||||
selectedText.innerText = 'Dowolny termin';
|
||||
displayInput.value = 'Dowolny termin';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (resetDateBtn) {
|
||||
resetDateBtn.addEventListener('click', function () {
|
||||
presets.forEach(item => item.classList.remove('active'));
|
||||
const allPreset = document.querySelector('.preset-chip[data-preset="all"]');
|
||||
if (allPreset) allPreset.classList.add('active');
|
||||
window.currentDateFilter = 'all';
|
||||
selectedText.innerText = 'Dowolny termin';
|
||||
displayInput.value = 'Dowolny termin';
|
||||
if (window.triggerSuperpilotFilter) window.triggerSuperpilotFilter();
|
||||
const modalEl = document.getElementById('dateRangeModal');
|
||||
const modalInstance = bootstrap.Modal.getInstance(modalEl);
|
||||
if (modalInstance) modalInstance.hide();
|
||||
});
|
||||
}
|
||||
|
||||
if (applyDateBtn) {
|
||||
applyDateBtn.addEventListener('click', function () {
|
||||
if (window.triggerSuperpilotFilter) window.triggerSuperpilotFilter();
|
||||
});
|
||||
}
|
||||
|
||||
if (removeDateChip) {
|
||||
removeDateChip.addEventListener('click', function () {
|
||||
window.currentDateFilter = 'all';
|
||||
displayInput.value = 'Dowolny termin';
|
||||
if (window.triggerSuperpilotFilter) window.triggerSuperpilotFilter();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function initModals() {
|
||||
window.openBookingModal = function (pilotId, pilotName, dates) {
|
||||
const modal = new bootstrap.Modal(document.getElementById('bookingModal'));
|
||||
const nameInput = document.getElementById('bookingPilotName');
|
||||
const nameDisplay = document.getElementById('bookingPilotNameDisplay');
|
||||
const idInput = document.getElementById('bookingPilotId');
|
||||
const datesInput = document.getElementById('bookingDates');
|
||||
|
||||
const name = pilotName || 'Oskar Kaczmarek';
|
||||
if (nameInput) nameInput.value = name;
|
||||
if (nameDisplay) nameDisplay.innerText = name;
|
||||
if (idInput) idInput.value = pilotId || '1';
|
||||
if (datesInput && dates) datesInput.value = dates;
|
||||
|
||||
modal.show();
|
||||
};
|
||||
|
||||
window.openSosModal = function (defaultCountry) {
|
||||
const modal = new bootstrap.Modal(document.getElementById('sosModal'));
|
||||
if (defaultCountry && document.getElementById('sosCountrySelect')) {
|
||||
document.getElementById('sosCountrySelect').value = defaultCountry;
|
||||
}
|
||||
modal.show();
|
||||
};
|
||||
|
||||
const bookingForm = document.getElementById('pilotBookingForm');
|
||||
if (bookingForm) {
|
||||
bookingForm.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
const submitBtn = bookingForm.querySelector('button[type="submit"]');
|
||||
const pilotName = document.getElementById('bookingPilotName').value;
|
||||
const originalText = submitBtn.innerHTML;
|
||||
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = "<span class=\"spinner-border spinner-border-sm me-1\"></span> Wysyłanie powiadomienia PUSH...";
|
||||
|
||||
setTimeout(() => {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = originalText;
|
||||
const modalEl = document.getElementById('bookingModal');
|
||||
const modalInstance = bootstrap.Modal.getInstance(modalEl);
|
||||
if (modalInstance) modalInstance.hide();
|
||||
|
||||
showToastNotification(
|
||||
'Pomyślnie wysłano zapytanie!',
|
||||
'Powiadomienie PUSH trafiło do aplikacji mobilnej pilota (' + pilotName + '). Pilot ma 24h na akceptację w aplikacji.'
|
||||
);
|
||||
}, 900);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function showToastNotification(title, message) {
|
||||
let container = document.getElementById('toastContainer');
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
container.id = 'toastContainer';
|
||||
container.style.position = 'fixed';
|
||||
container.style.top = '24px';
|
||||
container.style.right = '24px';
|
||||
container.style.zIndex = '99999';
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'toast show align-items-center text-white bg-dark border-0 mb-2 shadow-lg';
|
||||
toast.style.minWidth = '340px';
|
||||
toast.style.borderRadius = '12px';
|
||||
toast.style.overflow = 'hidden';
|
||||
toast.innerHTML = `
|
||||
<div class="d-flex p-3" style="background: linear-gradient(135deg, #1f2244 0%, #312e81 100%);">
|
||||
<div class="me-3 fs-3 text-warning">
|
||||
<i class='bx bxs-bell-ring'></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="fw-bold mb-1 text-white" style="font-size: 0.92rem;">${title}</h6>
|
||||
<div style="font-size: 0.8rem; line-height: 1.4; color: #cbd5e1;">${message}</div>
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white me-1 m-auto" onclick="this.closest('.toast').remove()"></button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
container.appendChild(toast);
|
||||
|
||||
setTimeout(() => {
|
||||
toast.remove();
|
||||
}, 6000);
|
||||
}
|
||||
|
||||
function getCountryFlag(country) {
|
||||
const map = {
|
||||
'Włochy': '🇮🇹',
|
||||
'Grecja': '🇬🇷',
|
||||
'Hiszpania': '🇪🇸',
|
||||
'Chorwacja': '🇭🇷',
|
||||
'Norwegia': '🇳🇴',
|
||||
'Austria': '🇦🇹',
|
||||
'Japonia': '🇯🇵'
|
||||
};
|
||||
return map[country] || '🌍';
|
||||
}
|
||||
|
||||
function capitalize(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
<!-- Modal Rezerwacji / Zapytania Ofertowego (Clean & Streamlined UX 2026) -->
|
||||
<div class="modal fade" id="bookingModal" tabindex="-1" aria-labelledby="bookingModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-md" style="max-width: 580px;">
|
||||
<div class="modal-content border-0 shadow-lg" style="border-radius: var(--radius-md); overflow: hidden;">
|
||||
|
||||
<!-- Czysty, nowoczesny nagłówek modala -->
|
||||
<div class="modal-header bg-white border-bottom py-3 px-4 d-flex align-items-center justify-content-between">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center" style="width: 32px; height: 32px; background: var(--primary-light); color: var(--primary); font-size: 1.1rem;">
|
||||
<i class='bx bx-calendar-check'></i>
|
||||
</div>
|
||||
<h5 class="modal-title fw-bold text-navy mb-0" id="bookingModalLabel" style="font-size: 1rem;">
|
||||
Zapytanie o termin
|
||||
</h5>
|
||||
</div>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zamknij"></button>
|
||||
</div>
|
||||
|
||||
<form id="pilotBookingForm">
|
||||
<div class="modal-body p-4">
|
||||
|
||||
<!-- Podsumowanie wybranego pilota (Dyskretny pasek) -->
|
||||
<div class="d-flex align-items-center justify-content-between p-2 px-3 rounded-2 mb-3" style="background: #f8fafd; border: 1px solid var(--border-color);">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/oskar-avatar.jpg" alt="Pilot" class="rounded-circle border" style="width: 32px; height: 32px; object-fit: cover;">
|
||||
<div>
|
||||
<span class="fw-bold text-dark" id="bookingPilotNameDisplay" style="font-size: 0.88rem;">Oskar Kaczmarek</span>
|
||||
<span class="text-muted small ms-1" style="font-size: 0.75rem;">• Pilot Wycieczek</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-success-subtle text-success border border-success-subtle py-1 px-2" style="font-size: 0.72rem;">
|
||||
<i class='bx bx-check-circle me-1'></i>Dostępny dla biura
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<input type="hidden" id="bookingPilotId" value="1">
|
||||
<input type="hidden" id="bookingPilotName" value="Oskar Kaczmarek">
|
||||
|
||||
<!-- Sekcja 1: Wybór imprezy z Travel Managera -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold text-dark small mb-1" for="bookingTourSelect">
|
||||
Impreza z Travel Managera:
|
||||
</label>
|
||||
<select class="form-select form-select-sm fw-semibold" id="bookingTourSelect" style="font-size: 0.84rem; padding: 0.45rem 0.75rem;">
|
||||
<option value="12" data-days="9" data-dates="16.09.2027 - 24.09.2027" data-dest="🇮🇹 Włochy (Wenecja - Florencja - Rzym)" selected>Wycieczka #12: Klasyczne Włochy (16.09 – 24.09.2027) • 44 os.</option>
|
||||
<option value="15" data-days="8" data-dates="05.10.2027 - 12.10.2027" data-dest="🇮🇹 Toskania i Rzym">Wycieczka #15: Magia Toskanii i Rzym (05.10 – 12.10.2027) • 38 os.</option>
|
||||
<option value="21" data-days="8" data-dates="12.11.2027 - 19.11.2027" data-dest="🇪🇸 Andaluzja i Gibraltar">Wycieczka #21: Andaluzja i Gibraltar (12.11 – 19.11.2027) • 40 os.</option>
|
||||
<option value="custom" data-days="7" data-dates="" data-dest="">Wpisz termin niestandardowy / inną trasę...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Sekcja 2: Termin i Trasa (2 kolumny) -->
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-6">
|
||||
<label class="form-label fw-bold text-dark small mb-1" for="bookingDates">Termin zlecenia:</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text bg-light text-muted"><i class='bx bx-calendar'></i></span>
|
||||
<input type="text" class="form-control fw-semibold" id="bookingDates" value="16.09.2027 - 24.09.2027" required style="font-size: 0.8rem;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<label class="form-label fw-bold text-dark small mb-1" for="bookingDestination">Destynacja / Trasa:</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text bg-light text-muted"><i class='bx bx-map-pin'></i></span>
|
||||
<input type="text" class="form-control fw-semibold" id="bookingDestination" value="🇮🇹 Włochy (Wenecja - Florencja - Rzym)" required style="font-size: 0.8rem;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sekcja 3: Finanse i Świadczenia (2 kolumny) -->
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-6">
|
||||
<label class="form-label fw-bold text-dark small mb-1" for="bookingRate">Stawka dzienna netto:</label>
|
||||
<div class="input-group input-group-sm mb-1">
|
||||
<input type="number" class="form-control fw-bold text-dark" id="bookingRate" value="550" step="50" min="200" style="font-size: 0.85rem;">
|
||||
<span class="input-group-text bg-light text-muted" style="font-size: 0.74rem;">PLN/dzień</span>
|
||||
</div>
|
||||
<div class="text-muted" style="font-size: 0.73rem;">
|
||||
Szacunkowo: <strong class="text-dark" id="bookingTotalEstimate">4 950 PLN netto</strong> <span id="bookingDaysBadge">(9 dni)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<label class="form-label fw-bold text-dark small mb-1">Zapewniane świadczenia:</label>
|
||||
<div class="d-flex flex-column gap-1 pt-1" style="font-size: 0.76rem;">
|
||||
<label class="form-check-label d-inline-flex align-items-center gap-2 text-dark">
|
||||
<input class="form-check-input mt-0" type="checkbox" id="benefitHotel" checked> Pokój 1-osobowy w hotelach
|
||||
</label>
|
||||
<label class="form-check-label d-inline-flex align-items-center gap-2 text-dark">
|
||||
<input class="form-check-input mt-0" type="checkbox" id="benefitMeals" checked> Wyżywienie pilota
|
||||
</label>
|
||||
<label class="form-check-label d-inline-flex align-items-center gap-2 text-dark">
|
||||
<input class="form-check-input mt-0" type="checkbox" id="benefitInsurance" checked> Ubezpieczenie KL/NNW
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sekcja 4: Wiadomość / Uwagi -->
|
||||
<div class="mb-1">
|
||||
<label class="form-label fw-bold text-dark small mb-1" for="bookingMessage">Wiadomość do pilota (opcjonalnie):</label>
|
||||
<textarea class="form-control" id="bookingMessage" rows="2" style="font-size: 0.8rem; line-height: 1.4;" placeholder="np. godzina zbiórki na lotnisku, specyfika grupy, preferowany język przewodnictwa..."></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer bg-light px-4 py-2 border-top d-flex justify-content-between align-items-center">
|
||||
<span class="text-muted d-inline-flex align-items-center gap-1" style="font-size: 0.75rem;">
|
||||
<i class='bx bx-mobile-alt text-primary'></i> Pilot ma 24h na akceptację w aplikacji
|
||||
</span>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm px-3" data-bs-dismiss="modal" style="font-size: 0.8rem;">
|
||||
Anuluj
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary btn-sm px-4 fw-semibold" style="font-size: 0.8rem; border-radius: var(--radius-sm);">
|
||||
<i class='bx bx-paper-plane me-1'></i> Wyślij zapytanie
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const tourSelect = document.getElementById('bookingTourSelect');
|
||||
const datesInput = document.getElementById('bookingDates');
|
||||
const destInput = document.getElementById('bookingDestination');
|
||||
const rateInput = document.getElementById('bookingRate');
|
||||
const totalEstimate = document.getElementById('bookingTotalEstimate');
|
||||
const daysBadge = document.getElementById('bookingDaysBadge');
|
||||
|
||||
let currentDays = 9;
|
||||
|
||||
function recalculateTotal() {
|
||||
const rate = parseFloat(rateInput ? rateInput.value : 550) || 550;
|
||||
const total = rate * currentDays;
|
||||
if (totalEstimate) totalEstimate.innerText = total.toLocaleString('pl-PL') + ' PLN netto';
|
||||
if (daysBadge) daysBadge.innerText = '(' + currentDays + ' ' + (currentDays === 1 ? 'dzień' : (currentDays < 5 ? 'dni' : 'dni')) + ')';
|
||||
}
|
||||
|
||||
if (tourSelect) {
|
||||
tourSelect.addEventListener('change', function() {
|
||||
const selectedOpt = this.options[this.selectedIndex];
|
||||
const dates = selectedOpt.getAttribute('data-dates');
|
||||
const dest = selectedOpt.getAttribute('data-dest');
|
||||
const days = parseInt(selectedOpt.getAttribute('data-days'), 10) || 7;
|
||||
|
||||
if (dates && datesInput) datesInput.value = dates;
|
||||
if (dest && destInput) destInput.value = dest;
|
||||
currentDays = days;
|
||||
recalculateTotal();
|
||||
});
|
||||
}
|
||||
|
||||
if (rateInput) {
|
||||
rateInput.addEventListener('input', recalculateTotal);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
$pageTitle = 'Superpilot - Superpilot Academy & Certyfikaty dla Biur';
|
||||
$enablePrototypeComments = true;
|
||||
include __DIR__ . '/header.php';
|
||||
?>
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y" style="padding-top: 1.5rem !important;">
|
||||
|
||||
<!-- Breadcrumb -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb mb-0" style="font-size: 0.8rem;">
|
||||
<li class="breadcrumb-item"><a href="index.php" class="text-muted">Superpilot</a></li>
|
||||
<li class="breadcrumb-item active text-primary fw-bold" aria-current="page">Superpilot Academy & Certyfikaty</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<span class="badge bg-label-primary px-3 py-1 rounded-pill" style="font-size: 0.76rem;">
|
||||
Oficjalny Program Akredytacji Pilotów
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Hero Card Superpilot Academy -->
|
||||
<div class="search-hero-card mb-4" style="background: linear-gradient(135deg, #1e1b4b 0%, #312e81 100%);">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-8">
|
||||
<span class="badge bg-warning text-dark fw-bold mb-2 px-3 py-1 rounded-pill" style="font-size: 0.72rem;">
|
||||
SZKOLENIA & AKREDYTACJE DLA BRANŻY TURYSTYCZNEJ
|
||||
</span>
|
||||
<h3 class="fw-bold text-white mb-2">Superpilot Academy & Program Certyfikacji</h3>
|
||||
<p class="text-white-50 mb-3" style="font-size: 0.92rem; max-width: 650px;">
|
||||
Podnoś jakość kadr wyjazdowych. Piloci z certyfikatami Superpilot Academy otrzymują <strong>odznakę zaufania</strong>, wyższą pozycję w wyszukiwarce biur oraz ubezpieczenie OC pilotażu.
|
||||
</p>
|
||||
<div class="d-flex align-items-center gap-3 flex-wrap">
|
||||
<span class="text-white small d-flex align-items-center gap-1">
|
||||
<i class='bx bx-check-circle text-success'></i> Dofinansowanie z TravelPay
|
||||
</span>
|
||||
<span class="text-white small d-flex align-items-center gap-1">
|
||||
<i class='bx bx-check-circle text-success'></i> Egzaminy praktyczne w trasie
|
||||
</span>
|
||||
<span class="text-white small d-flex align-items-center gap-1">
|
||||
<i class='bx bx-check-circle text-success'></i> Weryfikacja cyfrowa blockchain
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 text-lg-end mt-3 mt-lg-0">
|
||||
<a href="../travelleader-mobile/courses.php" target="_blank" class="btn btn-outline-light btn-sm" style="font-size: 0.78rem;">
|
||||
<i class='bx bx-mobile-alt'></i> Zobacz widok szkoleń pilota (Mobile) →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Narzędzie: Weryfikator certyfikatu online -->
|
||||
<div class="card border-0 shadow-sm mb-4" style="border-radius: var(--sp-radius-md); background: #f8fafc; border: 1px solid var(--sp-border-color);">
|
||||
<div class="card-body p-4">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-4">
|
||||
<h6 class="fw-bold text-navy mb-1"><i class='bx bx-search-alt text-primary me-1'></i> Weryfikator Certyfikatu Online</h6>
|
||||
<small class="text-muted">Sprawdź autentyczność certyfikatu kandydata na pilota po numerze</small>
|
||||
</div>
|
||||
<div class="col-lg-5 col-md-8 mt-2 mt-lg-0">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-white"><i class='bx bx-barcode'></i></span>
|
||||
<input type="text" id="certNumberInput" class="form-control" value="SPA-2026-OK92" placeholder="Wpisz numer, np. SPA-2026-OK92">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-4 mt-2 mt-lg-0">
|
||||
<button type="button" class="btn btn-primary w-100 fw-bold" onclick="verifyCert()">
|
||||
Zweryfikuj w rejestrze
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wynik weryfikacji -->
|
||||
<div id="certResultBox" class="mt-3 p-3 rounded-3 bg-white border d-flex justify-content-between align-items-center flex-wrap gap-2" style="display: flex;">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="rounded-circle bg-success-subtle text-success d-flex align-items-center justify-content-center" style="width: 40px; height: 40px; font-size: 1.3rem;">
|
||||
<i class='bx bx-check-double'></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-bold text-navy" style="font-size: 0.92rem;">
|
||||
Certyfikat Autentyczny: Oskar Kaczmarek (ID #1)
|
||||
</div>
|
||||
<small class="text-muted">Superpilot Academy 2026 • Akredytacja Pilotażu Europy Południowej (Włochy, Grecja, Hiszpania) • Status: <strong>Aktywny i ważny</strong></small>
|
||||
</div>
|
||||
</div>
|
||||
<a href="pilot-profile.php" class="btn btn-sm btn-outline-primary fw-semibold">
|
||||
Zobacz zweryfikowany profil →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Katalog Szkoleń Dostępnych dla Branży -->
|
||||
<h5 class="fw-bold text-navy mb-3">Programy Szkoleniowe i Certyfikaty Partnerskie</h5>
|
||||
|
||||
<div class="row g-4">
|
||||
|
||||
<!-- Kurs 1: Superpilot Academy 2027 -->
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="academy-card h-100 d-flex flex-column">
|
||||
<div class="p-4 flex-grow-1">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="academy-card-badge">
|
||||
<i class='bx bx-star'></i> Flagowy Program
|
||||
</span>
|
||||
<span class="fw-bold text-navy">1 290 PLN</span>
|
||||
</div>
|
||||
|
||||
<h5 class="fw-bold text-navy mb-2">Superpilot Academy 2027: Europa Południowa</h5>
|
||||
<p class="text-secondary small mb-3">
|
||||
Praktyczny certyfikat pilotażu tras autokarowych i lotniczych we Włoszech, Grecji i Hiszpanii. Standardy Magico i TravelManager.
|
||||
</p>
|
||||
|
||||
<div class="d-flex flex-column gap-1 small text-muted mb-3">
|
||||
<div><i class='bx bx-time text-primary me-1'></i> Czas trwania: <strong>32h (Online + Warsztat Rzym)</strong></div>
|
||||
<div><i class='bx bx-trending-up text-success me-1'></i> Wzrost pozycji w bazie biur: <strong>+350%</strong></div>
|
||||
<div><i class='bx bx-wallet text-warning me-1'></i> Dofinansowanie biura przez TravelPay: <strong>do 50%</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-3 bg-light border-top d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">Absolwenci: <strong>64 pilotów</strong></small>
|
||||
<button type="button" class="btn btn-sm btn-superpilot" onclick="alert('Otwarto formularz dofinansowania szkolenia dla pilota przez TravelPay.');">
|
||||
Dofinansuj pilotowi
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Kurs 2: KPP w Turystyce -->
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="academy-card h-100 d-flex flex-column">
|
||||
<div class="p-4 flex-grow-1">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="badge bg-danger-subtle text-danger rounded-pill fw-bold">
|
||||
<i class='bx bx-plus-medical'></i> Ratownictwo Medyczne
|
||||
</span>
|
||||
<span class="fw-bold text-navy">890 PLN</span>
|
||||
</div>
|
||||
|
||||
<h5 class="fw-bold text-navy mb-2">Kwalifikowana Pierwsza Pomoc (KPP) w Trasie</h5>
|
||||
<p class="text-secondary small mb-3">
|
||||
Państwowy kurs pierwszej pomocy dla pilotów wycieczek szkolnych, trekkingowych i autokarowych. Egzamin państwowy z tytułem Ratownika.
|
||||
</p>
|
||||
|
||||
<div class="d-flex flex-column gap-1 small text-muted mb-3">
|
||||
<div><i class='bx bx-time text-primary me-1'></i> Czas trwania: <strong>66 godzin (Weekendowo)</strong></div>
|
||||
<div><i class='bx bx-shield-check text-success me-1'></i> Ważność uprawnień: <strong>3 lata</strong></div>
|
||||
<div><i class='bx bx-badge text-info me-1'></i> Wymóg na trasy szkolne i młodzieżowe</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-3 bg-light border-top d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">Certyfikat państwowy</small>
|
||||
<button type="button" class="btn btn-sm btn-superpilot-outline" onclick="alert('Szczegóły kursu KPP przesłano na e-mail koordynatora.');">
|
||||
Szczegóły programu
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Kurs 3: Standardy VIP i Incentive -->
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="academy-card h-100 d-flex flex-column">
|
||||
<div class="p-4 flex-grow-1">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="badge bg-info-subtle text-info rounded-pill fw-bold">
|
||||
<i class='bx bx-diamond'></i> Korporacje & VIP
|
||||
</span>
|
||||
<span class="fw-bold text-navy">1 450 PLN</span>
|
||||
</div>
|
||||
|
||||
<h5 class="fw-bold text-navy mb-2">Pilotaż Wyjazdów VIP & Incentive 2027</h5>
|
||||
<p class="text-secondary small mb-3">
|
||||
Etykieta biznesowa, dyplomacja w hotelach 5*, obsługa zarządów korporacji i eventów zagranicznych. Najwyższe stawki dzienne w branży.
|
||||
</p>
|
||||
|
||||
<div class="d-flex flex-column gap-1 small text-muted mb-3">
|
||||
<div><i class='bx bx-time text-primary me-1'></i> Czas trwania: <strong>20h Masterclass</strong></div>
|
||||
<div><i class='bx bx-award text-success me-1'></i> Dedykowana odznaka: <strong>VIP Guide Pro</strong></div>
|
||||
<div><i class='bx bx-user-voice text-primary me-1'></i> Prowadzący: Doświadczeni dyrektorzy operacyjni biur</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-3 bg-light border-top d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">Dla doświadczonych pilotów</small>
|
||||
<button type="button" class="btn btn-sm btn-superpilot-outline" onclick="alert('Zgłoszono zapotrzebowanie na szkolenie VIP.');">
|
||||
Szczegóły programu
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div><!-- /.container-xxl -->
|
||||
|
||||
<script>
|
||||
function verifyCert() {
|
||||
const num = document.getElementById('certNumberInput').value.trim();
|
||||
const res = document.getElementById('certResultBox');
|
||||
if (num === 'SPA-2026-OK92') {
|
||||
res.innerHTML = `
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="rounded-circle bg-success-subtle text-success d-flex align-items-center justify-content-center" style="width: 40px; height: 40px; font-size: 1.3rem;">
|
||||
<i class='bx bx-check-double'></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-bold text-navy" style="font-size: 0.92rem;">
|
||||
Certyfikat Autentyczny: Oskar Kaczmarek (ID #1)
|
||||
</div>
|
||||
<small class="text-muted">Superpilot Academy 2026 • Akredytacja Pilotażu Europy Południowej (Włochy, Grecja, Hiszpania) • Status: <strong>Aktywny i ważny</strong></small>
|
||||
</div>
|
||||
</div>
|
||||
<a href="pilot-profile.php" class="btn btn-sm btn-outline-primary fw-semibold">
|
||||
Zobacz zweryfikowany profil →
|
||||
</a>
|
||||
`;
|
||||
} else {
|
||||
res.innerHTML = `
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="rounded-circle bg-info-subtle text-info d-flex align-items-center justify-content-center" style="width: 40px; height: 40px; font-size: 1.3rem;">
|
||||
<i class='bx bx-info-circle'></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-bold text-navy" style="font-size: 0.92rem;">
|
||||
Certyfikat w Rejestrze: ${num}
|
||||
</div>
|
||||
<small class="text-muted">Superpilot Academy Standard • Certyfikat w trakcie corocznej resertyfikacji.</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-label-info">Zweryfikowano</span>
|
||||
`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include __DIR__ . '/footer.php'; ?>
|
||||
@@ -0,0 +1,176 @@
|
||||
<!-- Reusable Modals -->
|
||||
<?php include __DIR__ . '/booking-modal.php'; ?>
|
||||
<?php include __DIR__ . '/sos-modal.php'; ?>
|
||||
|
||||
<!-- Modal Wideo-Wizytówki Pilota (Autoprezentacja) -->
|
||||
<div class="modal fade" id="videoPitchModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content border-0 shadow-lg" style="border-radius: var(--sp-radius-md); overflow: hidden; background: #0f172a; color: white;">
|
||||
|
||||
<div class="modal-header border-0 pb-0">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="badge bg-danger rounded-pill" style="font-size: 0.7rem;">WIDEO AUTOPREZENTACJA</span>
|
||||
<h6 class="modal-title fw-bold text-white mb-0" id="videoModalPilotName">Oskar Kaczmarek</h6>
|
||||
<span class="text-white-50 small" id="videoModalLength">(1:15 min)</span>
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Zamknij"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body p-4 text-center">
|
||||
|
||||
<!-- Ekran symulowanego wideo playera -->
|
||||
<div class="position-relative rounded-3 overflow-hidden d-flex flex-column align-items-center justify-content-center p-5 mb-3" style="background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%); min-height: 320px; border: 1px solid rgba(255,255,255,0.1);">
|
||||
|
||||
<!-- Próbka audio/video avatara -->
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center mb-3 shadow-lg" style="width: 84px; height: 84px; background: linear-gradient(135deg, var(--sp-primary) 0%, var(--sp-violet) 100%); font-size: 2.2rem; font-weight: 800;">
|
||||
OK
|
||||
</div>
|
||||
|
||||
<h5 class="fw-bold text-white mb-1">„Buongiorno! Nazywam się Oskar Kaczmarek...”</h5>
|
||||
<p class="text-white-50 small mb-3" style="max-width: 520px;">
|
||||
Posłuchaj próbki autoprezentacji: dykcja, prezencja, płynne przejście na język włoski i angielski, opanowanie i entuzjazm do pracy z grupami wycieczkowymi.
|
||||
</p>
|
||||
|
||||
<!-- Pasek postępu odtwarzacza -->
|
||||
<div class="w-100 progress bg-dark" style="height: 6px; max-width: 480px;">
|
||||
<div id="videoMockProgress" class="progress-bar bg-primary" style="width: 45%;"></div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between text-white-50 small mt-1 w-100" style="max-width: 480px; font-size: 0.72rem;">
|
||||
<span>0:34</span>
|
||||
<span>1:15</span>
|
||||
</div>
|
||||
|
||||
<!-- Fale dźwiękowe w tle -->
|
||||
<div class="d-flex align-items-center gap-1 mt-3">
|
||||
<span class="badge bg-label-primary px-3 py-1 rounded-pill"><i class='bx bx-volume-full'></i> Dźwięk HD</span>
|
||||
<span class="badge bg-label-success px-3 py-1 rounded-pill"><i class='bx bx-check-shield'></i> Zweryfikowany przez Superpilot</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Podsumowanie kompetencji zweryfikowanych w wideo -->
|
||||
<div class="row g-2 text-start">
|
||||
<div class="col-md-4">
|
||||
<div class="p-2 rounded bg-white bg-opacity-10">
|
||||
<div class="text-success small fw-bold"><i class='bx bx-check'></i> Język Włoski</div>
|
||||
<small class="text-white-50" style="font-size: 0.74rem;">Płynna wymowa i słownictwo branżowe</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="p-2 rounded bg-white bg-opacity-10">
|
||||
<div class="text-success small fw-bold"><i class='bx bx-check'></i> Dyplomacja & Grupa</div>
|
||||
<small class="text-white-50" style="font-size: 0.74rem;">Naturalne podejście do trudnych sytuacji</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="p-2 rounded bg-white bg-opacity-10">
|
||||
<div class="text-success small fw-bold"><i class='bx bx-check'></i> Prezencja Osobista</div>
|
||||
<small class="text-white-50" style="font-size: 0.74rem;">Wysoka kultura osobista, schludność</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer border-0 pt-0 pb-4 px-4 justify-content-between">
|
||||
<button type="button" class="btn btn-outline-light btn-sm" data-bs-dismiss="modal">Zamknij</button>
|
||||
<button type="button" class="btn btn-superpilot" onclick="bootstrap.Modal.getInstance(document.getElementById('videoPitchModal')).hide(); openBookingModal('1', 'Oskar Kaczmarek', '16.09.2027 - 24.09.2027');">
|
||||
<i class='bx bx-calendar-check'></i> Zarezerwuj pilota po obejrzeniu
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Galerii (Lightbox Pro - Czysty & Nowoczesny UX) -->
|
||||
<div class="modal fade" id="photoLightboxModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-xl">
|
||||
<div class="modal-content gallery-modal-content text-white">
|
||||
<!-- Górny minimalistyczny pasek: Filtry po lewej, licznik i zamknięcie po prawej -->
|
||||
<div class="modal-header border-0 py-3 px-3 px-md-4 d-flex align-items-center justify-content-between">
|
||||
<div class="modal-gallery-filters" id="modalGalleryFilters">
|
||||
<button type="button" class="modal-filter-btn active" data-filter="all">Wszystkie <span class="count">6</span></button>
|
||||
<button type="button" class="modal-filter-btn" data-filter="wlochy">🇮🇹 Włochy <span class="count">3</span></button>
|
||||
<button type="button" class="modal-filter-btn" data-filter="hiszpania">🇪🇸 Hiszpania <span class="count">2</span></button>
|
||||
<button type="button" class="modal-filter-btn" data-filter="grecja">🇬🇷 Grecja <span class="count">1</span></button>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<span class="text-white-50 small fw-semibold" id="galleryModalCounter" style="font-size: 0.82rem; letter-spacing: 0.04em;">1 / 6</span>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Zamknij"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Główna scena ze zdjęciem -->
|
||||
<div class="modal-body p-0 px-3 px-md-4 pb-3">
|
||||
<div class="gallery-stage mb-2">
|
||||
<button type="button" class="gallery-nav-btn prev" id="galleryBtnPrev" title="Poprzednie (strzałka w lewo)">
|
||||
<i class='bx bx-chevron-left'></i>
|
||||
</button>
|
||||
<img id="galleryModalImg" src="" alt="Zdjęcie z trasy" class="img-fluid">
|
||||
<button type="button" class="gallery-nav-btn next" id="galleryBtnNext" title="Następne (strzałka w prawo)">
|
||||
<i class='bx bx-chevron-right'></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Zwięzły, jednoliniowy podpis kadru -->
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2 py-2 px-1">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="badge bg-primary-subtle text-primary border border-primary-subtle px-2 py-1" id="galleryModalCountryBadge" style="font-size: 0.72rem;">🇮🇹 Włochy</span>
|
||||
<span class="fw-semibold text-white" id="galleryModalTitle" style="font-size: 0.92rem;">Tytuł zdjęcia</span>
|
||||
</div>
|
||||
<span class="text-white-50 small" id="galleryModalSub" style="font-size: 0.78rem;">Wrzesień 2026</span>
|
||||
</div>
|
||||
|
||||
<!-- Dolna taśma miniaturek (Filmstrip) -->
|
||||
<div class="modal-gallery-thumbs" id="modalGalleryThumbs">
|
||||
<!-- Generowane dynamicznie przez JS w zależności od aktywnego filtra -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stopka desktopowa -->
|
||||
<footer class="content-footer footer bg-white border-top py-3 mt-5">
|
||||
<div class="container-xxl d-flex flex-wrap justify-content-between align-items-center py-2 flex-md-row flex-column">
|
||||
<div class="mb-2 mb-md-0 small text-muted">
|
||||
© <?= date('Y') ?> <strong>Superpilot</strong> — Ekosystem TravelManager & PrzezMorze dla Biur Podróży.
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3 small">
|
||||
<span class="text-success fw-semibold"><i class='bx bx-shield-quarter'></i> Certyfikacja Superpilot Academy</span>
|
||||
<a href="courses.php" class="text-secondary text-decoration-none">Programy Szkoleń</a>
|
||||
<a href="../travelleader-mobile/profile.php" target="_blank" class="text-primary text-decoration-none">Aplikacja Mobilna Pilota</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<div class="content-backdrop fade"></div>
|
||||
</div><!-- /.content-wrapper -->
|
||||
</div><!-- /.layout-page -->
|
||||
</div><!-- /.layout-container -->
|
||||
</div><!-- /.layout-wrapper -->
|
||||
|
||||
<!-- Sneat Core JS -->
|
||||
<script src="<?= $basePath ?>/assets/vendor/libs/jquery/jquery.js"></script>
|
||||
<script src="<?= $basePath ?>/assets/vendor/libs/popper/popper.js"></script>
|
||||
<script src="<?= $basePath ?>/assets/vendor/js/bootstrap.js"></script>
|
||||
<script src="<?= $basePath ?>/assets/vendor/libs/perfect-scrollbar/perfect-scrollbar.js"></script>
|
||||
<script src="<?= $basePath ?>/assets/vendor/libs/hammer/hammer.js"></script>
|
||||
<script src="<?= $basePath ?>/assets/vendor/js/menu.js"></script>
|
||||
<script src="<?= $basePath ?>/assets/js/main.js"></script>
|
||||
|
||||
<!-- Superpilot Custom JS -->
|
||||
<script src="assets/js/superpilot.js"></script>
|
||||
|
||||
<!-- Magico Prototype Comments -->
|
||||
<?php if (!empty($enablePrototypeComments)): ?>
|
||||
<script>
|
||||
const MAGICO_BASE_URL = <?= json_encode($basePath); ?>;
|
||||
</script>
|
||||
<script src="<?= $basePath ?>/assets/js/comments.js"></script>
|
||||
<?php endif; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
$basePath = '';
|
||||
if (isset($_SERVER['HTTP_HOST']) && ($_SERVER['HTTP_HOST'] === 'localhost' || $_SERVER['HTTP_HOST'] === '127.0.0.1')) {
|
||||
$basePath = '/magico-prototype';
|
||||
}
|
||||
|
||||
if (!isset($pageTitle)) {
|
||||
$pageTitle = 'Superpilot — Baza Pilotów dla Biur Podróży';
|
||||
}
|
||||
if (!isset($enablePrototypeComments)) {
|
||||
$enablePrototypeComments = true;
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl" class="light-style layout-menu-fixed" dir="ltr" data-theme="theme-default" data-assets-path="<?= $basePath ?>/assets/">
|
||||
<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><?= htmlspecialchars($pageTitle) ?></title>
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="<?= $basePath ?>/assets/img/favicon/favicon.ico" />
|
||||
|
||||
<!-- Google Fonts: Public Sans -->
|
||||
<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;0,800;1,400&display=swap" rel="stylesheet" />
|
||||
|
||||
<!-- Icons & Core Sneat CSS -->
|
||||
<link rel="stylesheet" href="<?= $basePath ?>/assets/vendor/fonts/boxicons.css" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" />
|
||||
<link rel="stylesheet" href="<?= $basePath ?>/assets/vendor/css/core.css" />
|
||||
<link rel="stylesheet" href="<?= $basePath ?>/assets/vendor/css/theme-default.css" />
|
||||
<link rel="stylesheet" href="<?= $basePath ?>/assets/css/demo.css" />
|
||||
|
||||
<!-- Superpilot Dedicated Modern Stylesheet (Clean 2026 Design) -->
|
||||
<link rel="stylesheet" href="assets/css/superpilot.css" />
|
||||
|
||||
<!-- Prototype Comments -->
|
||||
<?php if (!empty($enablePrototypeComments)): ?>
|
||||
<link rel="stylesheet" href="<?= $basePath ?>/assets/css/comments.css">
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Helpers -->
|
||||
<script src="<?= $basePath ?>/assets/vendor/js/helpers.js"></script>
|
||||
<script src="<?= $basePath ?>/assets/js/config.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Layout wrapper -->
|
||||
<div class="layout-wrapper layout-navbar-full layout-horizontal layout-without-menu">
|
||||
<div class="layout-container">
|
||||
|
||||
<!-- Navbar główny (Spokojny, lekki, profesjonalny) -->
|
||||
<nav class="layout-navbar navbar navbar-expand-xl align-items-center bg-white" id="layout-navbar" style="position: sticky; top: 0; z-index: 1020; border-bottom: 1px solid var(--border-color);">
|
||||
<div class="container-xxl d-flex align-items-center justify-content-between py-2">
|
||||
|
||||
<!-- Brand & Subtitle -->
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<a href="index.php" class="brand-superpilot">
|
||||
<div class="logo-icon">
|
||||
<i class='bx bx-compass'></i>
|
||||
</div>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="lh-1">Super<span style="color: var(--primary);">pilot</span></span>
|
||||
<small class="text-muted" style="font-size: 0.68rem; font-weight: 500;">Baza Pilotów dla Biur Podróży</small>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Prawa strona paska nawigacji: Zastępstwo SOS, Aplikacja Pilota, Biuro -->
|
||||
<div class="d-flex align-items-center gap-2 gap-md-3">
|
||||
|
||||
<!-- Spokojny przycisk SOS -->
|
||||
<button type="button" class="btn-sos" onclick="openSosModal('Włochy')" title="Szybkie znalezienie pilota na cito">
|
||||
<span class="sos-dot"></span>
|
||||
<span>Dyżur SOS</span>
|
||||
</button>
|
||||
|
||||
<!-- Przejście do aplikacji mobilnej pilota -->
|
||||
<a href="../travelleader-mobile/profile.php" target="_blank" class="btn btn-sm btn-light text-muted d-none d-md-inline-flex align-items-center gap-1 border" style="font-size: 0.78rem;" title="Podgląd z perspektywy pilota">
|
||||
<i class='bx bx-mobile-alt'></i>
|
||||
<span>Aplikacja Pilota</span>
|
||||
<i class='bx bx-link-external' style="font-size: 0.65rem;"></i>
|
||||
</a>
|
||||
|
||||
<!-- Powrót do Travel Managera -->
|
||||
<a href="../travel/index.php" class="btn btn-sm btn-link text-muted d-none d-lg-inline-block text-decoration-none p-0" style="font-size: 0.78rem;">
|
||||
Travel Manager
|
||||
</a>
|
||||
|
||||
<!-- Profil zalogowanego biura (Przejście do zapytań i zleceń) -->
|
||||
<a href="inquiries.php" class="d-flex align-items-center gap-2 ps-2 border-start text-decoration-none" title="Moje zlecenia i wysłane zapytania">
|
||||
<div class="position-relative">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center fw-bold" style="width: 36px; height: 36px; background: var(--primary-light); color: var(--primary); font-size: 0.85rem; border: 1px solid var(--primary-border); transition: all 0.15s ease;">
|
||||
AL
|
||||
</div>
|
||||
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-warning text-dark border border-white" style="font-size: 0.6rem; padding: 2px 5px;" title="1 oczekujące zapytanie">
|
||||
1
|
||||
</span>
|
||||
</div>
|
||||
<div class="d-none d-xl-block text-start lh-sm">
|
||||
<div class="fw-bold" style="font-size: 0.82rem; color: var(--dark-navy);">Biuro Almatur</div>
|
||||
<small class="text-muted" style="font-size: 0.7rem;">Zlecenia & zapytania →</small>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<!-- / Navbar -->
|
||||
|
||||
<!-- Layout container -->
|
||||
<div class="layout-page" style="padding-top: 0px !important;">
|
||||
<div class="content-wrapper">
|
||||
|
||||
@@ -0,0 +1,920 @@
|
||||
<?php
|
||||
$pageTitle = 'Superpilot — Baza Pilotów Wycieczek (Katalog i Wyszukiwarka)';
|
||||
$enablePrototypeComments = true;
|
||||
include __DIR__ . '/header.php';
|
||||
?>
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y" style="padding-top: 1.5rem !important;">
|
||||
|
||||
<!-- ================= GŁÓWNA BELKA WYSZUKIWARKI W STYLU PRACUJ.PL Z MULTISELECTEM KRAJÓW ================= -->
|
||||
<div class="search-bar-pracuj">
|
||||
|
||||
<!-- 1. Słowa kluczowe / Nazwisko -->
|
||||
<div class="search-field-item">
|
||||
<i class='bx bx-search field-icon'></i>
|
||||
<div class="field-content">
|
||||
<div class="field-label">Pilot / Specjalizacja / Słowo kluczowe</div>
|
||||
<input type="text" id="mainSearchInput" placeholder="Np. Oskar Kaczmarek, Rzym, Trekking...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. Kraj / Destynacje — MULTISELECT POPUP -->
|
||||
<div class="search-field-item">
|
||||
<i class='bx bx-globe field-icon'></i>
|
||||
<div class="field-content dropdown">
|
||||
<div class="field-label">Destynacje (Multiselect)</div>
|
||||
<button type="button" class="multiselect-dest-trigger dropdown-toggle" id="destDropdownTrigger" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false">
|
||||
<span class="trigger-text" id="destTriggerText">🇮🇹 Włochy</span>
|
||||
<i class='bx bx-chevron-down text-muted ms-1'></i>
|
||||
</button>
|
||||
|
||||
<div class="dropdown-menu multiselect-dropdown-menu shadow-lg" aria-labelledby="destDropdownTrigger">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2 pb-1 border-bottom">
|
||||
<span class="small fw-bold text-navy">Wybierz obsługiwane kraje:</span>
|
||||
<button type="button" class="btn btn-sm btn-link p-0 text-muted text-decoration-none small" id="btnResetDestMultiselect" style="font-size: 0.72rem;">Wyczyść</button>
|
||||
</div>
|
||||
|
||||
<label class="multiselect-item-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="top-dest-checkbox" value="Włochy" checked>
|
||||
<span>🇮🇹 Włochy</span>
|
||||
</div>
|
||||
<span class="text-muted small">(24)</span>
|
||||
</label>
|
||||
|
||||
<label class="multiselect-item-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="top-dest-checkbox" value="Grecja">
|
||||
<span>🇬🇷 Grecja</span>
|
||||
</div>
|
||||
<span class="text-muted small">(18)</span>
|
||||
</label>
|
||||
|
||||
<label class="multiselect-item-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="top-dest-checkbox" value="Hiszpania">
|
||||
<span>🇪🇸 Hiszpania</span>
|
||||
</div>
|
||||
<span class="text-muted small">(16)</span>
|
||||
</label>
|
||||
|
||||
<label class="multiselect-item-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="top-dest-checkbox" value="Chorwacja">
|
||||
<span>🇭🇷 Chorwacja</span>
|
||||
</div>
|
||||
<span class="text-muted small">(12)</span>
|
||||
</label>
|
||||
|
||||
<label class="multiselect-item-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="top-dest-checkbox" value="Norwegia">
|
||||
<span>🇳🇴 Norwegia</span>
|
||||
</div>
|
||||
<span class="text-muted small">(9)</span>
|
||||
</label>
|
||||
|
||||
<label class="multiselect-item-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="top-dest-checkbox" value="Austria">
|
||||
<span>🇦🇹 Austria</span>
|
||||
</div>
|
||||
<span class="text-muted small">(7)</span>
|
||||
</label>
|
||||
|
||||
<label class="multiselect-item-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="top-dest-checkbox" value="Japonia">
|
||||
<span>🇯🇵 Japonia & Azja</span>
|
||||
</div>
|
||||
<span class="text-muted small">(5)</span>
|
||||
</label>
|
||||
|
||||
<div class="pt-2 mt-2 border-top">
|
||||
<button type="button" class="btn btn-sm btn-primary w-100 fw-semibold" id="btnConfirmDestDropdown">
|
||||
Zastosuj wybrane kraje
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. Termin wycieczki (Dialog kalendarza) -->
|
||||
<div class="search-field-item" data-bs-toggle="modal" data-bs-target="#dateRangeModal" style="cursor: pointer;">
|
||||
<i class='bx bx-calendar field-icon text-primary'></i>
|
||||
<div class="field-content">
|
||||
<div class="field-label">Termin wycieczki (Kalendarz)</div>
|
||||
<input type="text" id="selectedDateDisplay" value="16.09.2027 – 24.09.2027" readonly style="cursor: pointer;">
|
||||
</div>
|
||||
<i class='bx bx-chevron-down text-muted'></i>
|
||||
</div>
|
||||
|
||||
<!-- 4. Przycisk Szukaj -->
|
||||
<button type="button" class="btn-search-pracuj" id="btnExecuteSearch">
|
||||
<i class='bx bx-search'></i> Szukaj
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ================= GŁÓWNY UKŁAD 2-KOLUMNOWY ================= -->
|
||||
<div class="row g-4">
|
||||
|
||||
<!-- ================= LEWA KOLUMNA: SIDEBAR FILTRÓW ================= -->
|
||||
<div class="col-lg-4 col-xl-3">
|
||||
<div class="filters-sidebar">
|
||||
|
||||
<div class="filter-sidebar-header">
|
||||
<h6 class="filter-sidebar-title">
|
||||
<i class='bx bx-filter-alt text-primary'></i> Filtry
|
||||
</h6>
|
||||
<button type="button" id="btnClearAllFilters" class="btn btn-sm btn-link text-muted p-0 text-decoration-none" style="font-size: 0.76rem;">
|
||||
Wyczyść filtry
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- NOWY AKORDEON: Baza wypadowa pilota -->
|
||||
<div class="filter-accordion-item">
|
||||
<div class="filter-accordion-header" data-bs-toggle="collapse" data-bs-target="#filterBaseCollapse">
|
||||
<span class="filter-accordion-title">
|
||||
<i class='bx bx-building-house'></i> Baza pilota
|
||||
</span>
|
||||
<i class='bx bx-chevron-down text-muted'></i>
|
||||
</div>
|
||||
<div class="collapse show filter-accordion-body" id="filterBaseCollapse">
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="base" value="warszawa">
|
||||
<span>Warszawa</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(48)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="base" value="kraków">
|
||||
<span>Kraków</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(26)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="base" value="katowice">
|
||||
<span>Katowice / Śląsk</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(22)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="base" value="poznań">
|
||||
<span>Poznań</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(15)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="base" value="wrocław">
|
||||
<span>Wrocław</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(14)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="base" value="gdańsk">
|
||||
<span>Gdańsk / Trójmiasto</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(12)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Akordeon 2: Specjalizacje -->
|
||||
<div class="filter-accordion-item">
|
||||
<div class="filter-accordion-header" data-bs-toggle="collapse" data-bs-target="#filterSpecCollapse">
|
||||
<span class="filter-accordion-title">
|
||||
<i class='bx bx-category'></i> Specjalizacje
|
||||
</span>
|
||||
<i class='bx bx-chevron-down text-muted'></i>
|
||||
</div>
|
||||
<div class="collapse show filter-accordion-body" id="filterSpecCollapse">
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="spec" value="objazdówki">
|
||||
<span>Wycieczki objazdowe</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(38)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="spec" value="trekking">
|
||||
<span>Trekking & góry</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(14)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="spec" value="miejski">
|
||||
<span>Przewodnik miejski</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(21)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="spec" value="młodzież">
|
||||
<span>Grupy szkolne</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(19)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="spec" value="vip">
|
||||
<span>VIP & Incentive</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(11)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="spec" value="kulinaria">
|
||||
<span>Enoturystyka & kulinaria</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(8)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Akordeon 3: Języki obce -->
|
||||
<div class="filter-accordion-item">
|
||||
<div class="filter-accordion-header" data-bs-toggle="collapse" data-bs-target="#filterLangCollapse">
|
||||
<span class="filter-accordion-title">
|
||||
<i class='bx bx-message-dots'></i> Języki obce
|
||||
</span>
|
||||
<i class='bx bx-chevron-down text-muted'></i>
|
||||
</div>
|
||||
<div class="collapse show filter-accordion-body" id="filterLangCollapse">
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="lang" value="Angielski">
|
||||
<span>Angielski (B2 / C1 / C2)</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(45)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="lang" value="Włoski">
|
||||
<span>Włoski (min. B2)</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(18)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="lang" value="Hiszpański">
|
||||
<span>Hiszpański (min. B2)</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(14)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="lang" value="Norweski">
|
||||
<span>Norweski (C1)</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(4)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Akordeon 4: Certyfikaty i Ranga -->
|
||||
<div class="filter-accordion-item">
|
||||
<div class="filter-accordion-header" data-bs-toggle="collapse" data-bs-target="#filterCertCollapse">
|
||||
<span class="filter-accordion-title">
|
||||
<i class='bx bx-award'></i> Certyfikaty & Ranga
|
||||
</span>
|
||||
<i class='bx bx-chevron-down text-muted'></i>
|
||||
</div>
|
||||
<div class="collapse show filter-accordion-body" id="filterCertCollapse">
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="cert" value="verified">
|
||||
<span>Superpilot Academy</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(32)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="cert" value="kpp">
|
||||
<span>Ratownik KPP</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(16)</span>
|
||||
</label>
|
||||
<label class="filter-checkbox-row">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<input type="checkbox" class="filter-checkbox" data-type="cert" value="rating-48">
|
||||
<span>Ocena min. 4.8 ★</span>
|
||||
</div>
|
||||
<span class="filter-count-badge">(27)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-sidebar-footer">
|
||||
<button type="button" class="btn-apply-filters" id="btnApplySidebarFilters">
|
||||
<i class='bx bx-check'></i> Pokaż pilotów (<span id="sidebarCountSpan">5</span>)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================= PRAWA KOLUMNA: KATALOG OFERT PILOTÓW ================= -->
|
||||
<div class="col-lg-8 col-xl-9">
|
||||
|
||||
<!-- Pasek aktywnych filtrów (Chips) -->
|
||||
<div class="active-filters-bar" id="activeFiltersBar">
|
||||
<span class="text-muted small me-1">Aktywne filtry:</span>
|
||||
<span class="active-filter-chip" id="chipDestFilter">
|
||||
<i class='bx bx-globe text-primary'></i>
|
||||
<span id="chipDestText">Włochy</span>
|
||||
<i class='bx bx-x chip-remove' id="removeDestChip" title="Usuń filtr"></i>
|
||||
</span>
|
||||
<span class="active-filter-chip" id="chipDateFilter">
|
||||
<i class='bx bx-calendar text-primary'></i>
|
||||
<span id="chipDateText">16–24.09.2027</span>
|
||||
<i class='bx bx-x chip-remove' id="removeDateChip" title="Usuń filtr"></i>
|
||||
</span>
|
||||
<span class="active-filter-chip" id="chipBaseFilter" style="display: none;">
|
||||
<i class='bx bx-building-house text-primary'></i>
|
||||
<span id="chipBaseText">Warszawa</span>
|
||||
<i class='bx bx-x chip-remove' id="removeBaseChip" title="Usuń filtr"></i>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Belka podsumowania nad listą -->
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
||||
<div>
|
||||
<span class="fw-bold text-navy" style="font-size: 1.05rem;">Dostępni piloci wycieczek</span>
|
||||
<span class="text-muted small ms-1">— <span id="offersCountText">5 ofert</span></span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="text-muted small">Sortuj:</span>
|
||||
<select class="form-select form-select-sm border" style="width: auto; font-size: 0.8rem; font-weight: 600;">
|
||||
<option selected>Dla Ciebie (Rekomendowani)</option>
|
||||
<option>Najwyższa ocena biur</option>
|
||||
<option>Staż pilotażu</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Komunikat o braku ofert -->
|
||||
<div id="noOffersAlert" class="alert alert-light border text-center p-4 my-4" style="display: none; border-radius: var(--radius-md);">
|
||||
<i class='bx bx-search-alt fs-1 text-muted mb-2'></i>
|
||||
<h6 class="fw-bold text-navy mb-1">Brak pilotów spełniających podane kryteria</h6>
|
||||
<p class="text-muted small mb-3">Zmień wybrane destynacje, specjalizacje w lewym panelu lub zresetuj filtry.</p>
|
||||
<button type="button" class="btn btn-sm btn-primary-calm" onclick="document.getElementById('btnClearAllFilters').click();">
|
||||
Wyczyść wszystkie filtry
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- LISTA POZIOMYCH KART PILOTÓW (UPORZĄDKOWANA, CZYSTA) -->
|
||||
<div id="pilotOffersList">
|
||||
|
||||
<!-- ================= OFERTA 1: OSKAR KACZMAREK ================= -->
|
||||
<div class="pilot-offer-card pilot-grid-item"
|
||||
data-name="Oskar Kaczmarek"
|
||||
data-base="warszawa"
|
||||
data-destinations="Włochy Grecja Hiszpania Chorwacja Norwegia Austria"
|
||||
data-languages="Polski Angielski Włoski"
|
||||
data-tags="objazdówki miejski rzym florencja młodzież vip kulinaria"
|
||||
data-rating="4.9"
|
||||
data-verified="1"
|
||||
data-kpp="1"
|
||||
data-dates-free="05-15-sep 25-30-sep październik-2027">
|
||||
|
||||
<div class="pilot-offer-avatar">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/oskar-avatar.jpg" alt="Oskar Kaczmarek" class="pilot-avatar-img">
|
||||
<div class="verified-badge-mini" title="Zweryfikowany przez Superpilot Academy">
|
||||
<i class='bx bx-check'></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-content">
|
||||
|
||||
<!-- Rząd 1: Imię + Rola + Gwiazdka i Ocena -->
|
||||
<div class="pilot-offer-header-row">
|
||||
<div>
|
||||
<h5 class="pilot-offer-name">
|
||||
<a href="pilot-profile.php">Oskar Kaczmarek</a>
|
||||
</h5>
|
||||
<div class="pilot-offer-headline">
|
||||
Licencjonowany Pilot Wycieczek & Przewodnik Miejski
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="pilot-rating-badge">
|
||||
<i class='bx bxs-star'></i> 4.9 <span class="text-muted fw-normal">(18 opinii)</span>
|
||||
</div>
|
||||
<button type="button" class="btn-save-pilot" title="Zapisz do ulubionych" onclick="this.classList.toggle('saved');">
|
||||
<i class='bx bx-star'></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rząd 2: Metadane logistyczne (Baza, Staż, Języki) -->
|
||||
<div class="pilot-offer-meta-row">
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-map-pin text-muted'></i> Baza: <strong>Warszawa</strong>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-time-five text-muted'></i> Staż: <strong>7 lat</strong>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-conversation text-muted'></i> Języki: <strong>Włoski (B2), Angielski (C1)</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rząd 3: Uporządkowane tagi -->
|
||||
<div class="pilot-tags-row">
|
||||
<span class="tech-tag primary-dest">🇮🇹 Włochy (Ekspert)</span>
|
||||
<span class="tech-tag primary-dest">🇬🇷 Grecja</span>
|
||||
<span class="tech-tag primary-dest">🇪🇸 Hiszpania</span>
|
||||
<span class="tech-tag">Objazdówki autokarowe</span>
|
||||
<span class="tech-tag">VIP Incentive</span>
|
||||
</div>
|
||||
|
||||
<!-- Rząd 4: Zwięzłe Bio -->
|
||||
<div class="pilot-offer-bio">
|
||||
Pasjonat kultury śródziemnomorskiej z wykształceniem historycznym (UW). Od 2019 r. z sukcesami prowadzi wycieczki objazdowe autokarowe i lotnicze we Włoszech, Hiszpanii i Grecji. Wyróżnia go wysokie opanowanie w sytuacjach kryzysowych.
|
||||
</div>
|
||||
|
||||
<!-- Miniaturki zdjęć z tras -->
|
||||
<div class="pilot-card-gallery-preview">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/tuscany.jpg" alt="Toskania" title="Toskania & Florencja">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/rome.jpg" alt="Rzym" title="Rzym & Koloseum">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/greece.jpg" alt="Grecja" title="Grecja & Cyklady">
|
||||
<span class="gallery-more-badge">+2 zdjęcia z tras</span>
|
||||
</div>
|
||||
|
||||
<!-- Rząd 5: Stopka karty -->
|
||||
<div class="pilot-offer-footer">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="pilot-academy-badge">
|
||||
<i class='bx bx-award'></i> Superpilot Academy 2026
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<a href="pilot-profile.php" class="btn-offer-details">
|
||||
Szczegóły profilu
|
||||
</a>
|
||||
<button type="button" class="btn-offer-book" onclick="openBookingModal('1', 'Oskar Kaczmarek', '16.09.2027 – 24.09.2027')">
|
||||
<i class='bx bx-calendar-plus'></i> Zarezerwuj termin
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================= OFERTA 2: ANNA LEWANDOWSKA ================= -->
|
||||
<div class="pilot-offer-card pilot-grid-item"
|
||||
data-name="Anna Lewandowska"
|
||||
data-base="kraków"
|
||||
data-destinations="Norwegia Islandia Szwecja"
|
||||
data-languages="Polski Angielski Norweski"
|
||||
data-tags="trekking góry zorza polarna kameralne"
|
||||
data-rating="5.0"
|
||||
data-verified="1"
|
||||
data-kpp="1"
|
||||
data-dates-free="05-15-sep 16-24-sep październik-2027">
|
||||
|
||||
<div class="pilot-offer-avatar">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/anna-avatar.jpg" alt="Anna Lewandowska" class="pilot-avatar-img">
|
||||
<div class="verified-badge-mini" title="Zweryfikowany przez Superpilot Academy">
|
||||
<i class='bx bx-check'></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-content">
|
||||
<div class="pilot-offer-header-row">
|
||||
<div>
|
||||
<h5 class="pilot-offer-name">
|
||||
<a href="pilot-profile.php?id=2">Anna Lewandowska</a>
|
||||
</h5>
|
||||
<div class="pilot-offer-headline">
|
||||
Przewodnik Wysokogórski UIMLA & Pilot Północy
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="pilot-rating-badge">
|
||||
<i class='bx bxs-star'></i> 5.0 <span class="text-muted fw-normal">(24 opinie)</span>
|
||||
</div>
|
||||
<button type="button" class="btn-save-pilot" title="Zapisz do ulubionych" onclick="this.classList.toggle('saved');">
|
||||
<i class='bx bx-star'></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-meta-row">
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-map-pin text-muted'></i> Baza: <strong>Kraków</strong>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-time-five text-muted'></i> Staż: <strong>9 lat</strong>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-conversation text-muted'></i> Języki: <strong>Norweski (C1), Angielski (C2)</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pilot-tags-row">
|
||||
<span class="tech-tag primary-dest">🇳🇴 Norwegia (Fiordy)</span>
|
||||
<span class="tech-tag primary-dest">🇮🇸 Islandia</span>
|
||||
<span class="tech-tag primary-dest">🇸🇪 Szwecja</span>
|
||||
<span class="tech-tag">Trekking & góry</span>
|
||||
<span class="tech-tag">Zorza polarna</span>
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-bio">
|
||||
Specjalistka tras po Norwegii, Lofotach i lodowcach Islandii. Certyfikowana przewodniczka międzynarodowa UIMLA. Znakomita orientacja w logistyce promowej i trudnych warunkach pogodowych.
|
||||
</div>
|
||||
|
||||
<!-- Miniaturki zdjęć z tras -->
|
||||
<div class="pilot-card-gallery-preview">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/norway.jpg" alt="Norwegia" title="Norwegia & Fiordy">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/iceland.jpg" alt="Islandia" title="Islandia & Zorza">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/trekking.jpg" alt="Trekking" title="Trekking wysokogórski">
|
||||
<span class="gallery-more-badge">+4 zdjęcia z wypraw</span>
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-footer">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="pilot-academy-badge">
|
||||
<i class='bx bx-award'></i> Superpilot Academy 2026
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<a href="pilot-profile.php?id=2" class="btn-offer-details">
|
||||
Szczegóły profilu
|
||||
</a>
|
||||
<button type="button" class="btn-offer-book" onclick="openBookingModal('2', 'Anna Lewandowska', '16.09.2027 – 24.09.2027')">
|
||||
<i class='bx bx-calendar-plus'></i> Zarezerwuj termin
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================= OFERTA 3: MICHAŁ WIŚNIEWSKI ================= -->
|
||||
<div class="pilot-offer-card pilot-grid-item"
|
||||
data-name="Michał Wiśniewski"
|
||||
data-base="katowice"
|
||||
data-destinations="Hiszpania Portugalia Austria"
|
||||
data-languages="Polski Hiszpański Angielski"
|
||||
data-tags="objazdówki narty młodzież autokar"
|
||||
data-rating="4.7"
|
||||
data-verified="0"
|
||||
data-kpp="0"
|
||||
data-dates-free="październik-2027">
|
||||
|
||||
<div class="pilot-offer-avatar" style="background: linear-gradient(135deg, #d97706 0%, #fbbf24 100%);">
|
||||
MW
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-content">
|
||||
<div class="pilot-offer-header-row">
|
||||
<div>
|
||||
<h5 class="pilot-offer-name">
|
||||
<a href="pilot-profile.php?id=3">Michał Wiśniewski</a>
|
||||
</h5>
|
||||
<div class="pilot-offer-headline">
|
||||
Pilot Wycieczek Autokarowych & Instruktor Narciarstwa
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="pilot-rating-badge">
|
||||
<i class='bx bxs-star'></i> 4.7 <span class="text-muted fw-normal">(11 opinii)</span>
|
||||
</div>
|
||||
<button type="button" class="btn-save-pilot" title="Zapisz do ulubionych" onclick="this.classList.toggle('saved');">
|
||||
<i class='bx bx-star'></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-meta-row">
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-map-pin text-muted'></i> Baza: <strong>Katowice</strong>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-time-five text-muted'></i> Staż: <strong>5 lat</strong>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-conversation text-muted'></i> Języki: <strong>Hiszpański (B2), Angielski (B2)</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pilot-tags-row">
|
||||
<span class="tech-tag primary-dest">🇪🇸 Hiszpania & Andaluzja</span>
|
||||
<span class="tech-tag primary-dest">🇵🇹 Portugalia</span>
|
||||
<span class="tech-tag primary-dest">🇦🇹 Austria</span>
|
||||
<span class="tech-tag">Objazdówki autokarowe</span>
|
||||
<span class="tech-tag">Narty & Alpy</span>
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-bio">
|
||||
Dynamiczny pilot specjalizujący się w długich trasach autokarowych na Półwysep Iberyjski oraz zimowych wyjazdach narciarskich do Austrii i Włoch. Bardzo dobry kontakt z młodzieżą i grupami dorosłymi.
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-footer">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="badge bg-light text-muted border small">
|
||||
Instruktor SITN-PZN
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<a href="pilot-profile.php?id=3" class="btn-offer-details">
|
||||
Szczegóły profilu
|
||||
</a>
|
||||
<button type="button" class="btn-offer-book" onclick="openBookingModal('3', 'Michał Wiśniewski', '05.10.2027 – 12.10.2027')">
|
||||
<i class='bx bx-calendar-plus'></i> Zarezerwuj termin
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================= OFERTA 4: KATARZYNA ZIELIŃSKA ================= -->
|
||||
<div class="pilot-offer-card pilot-grid-item"
|
||||
data-name="Katarzyna Zielińska"
|
||||
data-base="poznań wrocław"
|
||||
data-destinations="Chorwacja Włochy Grecja"
|
||||
data-languages="Polski Angielski Chorwacki Włoski"
|
||||
data-tags="młodzież grupy szkolne kpp pierwsza pomoc bałkany"
|
||||
data-rating="4.8"
|
||||
data-verified="1"
|
||||
data-kpp="1"
|
||||
data-dates-free="05-15-sep 16-24-sep październik-2027">
|
||||
|
||||
<div class="pilot-offer-avatar" style="background: linear-gradient(135deg, #0284c7 0%, #38bdf8 100%);">
|
||||
KZ
|
||||
<div class="verified-badge-mini">
|
||||
<i class='bx bx-check'></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-content">
|
||||
<div class="pilot-offer-header-row">
|
||||
<div>
|
||||
<h5 class="pilot-offer-name">
|
||||
<a href="pilot-profile.php?id=4">Katarzyna Zielińska</a>
|
||||
</h5>
|
||||
<div class="pilot-offer-headline">
|
||||
Pilot Grup Szkolnych & Młodzieżowych • Ratownik KPP
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="pilot-rating-badge">
|
||||
<i class='bx bxs-star'></i> 4.8 <span class="text-muted fw-normal">(15 opinii)</span>
|
||||
</div>
|
||||
<button type="button" class="btn-save-pilot" title="Zapisz do ulubionych" onclick="this.classList.toggle('saved');">
|
||||
<i class='bx bx-star'></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-meta-row">
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-map-pin text-muted'></i> Baza: <strong>Poznań / Wrocław</strong>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-time-five text-muted'></i> Staż: <strong>6 lat</strong>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-conversation text-muted'></i> Języki: <strong>Chorwacki (B2), Angielski (C1)</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pilot-tags-row">
|
||||
<span class="tech-tag primary-dest">🇭🇷 Chorwacja</span>
|
||||
<span class="tech-tag primary-dest">🇮🇹 Włochy Północne</span>
|
||||
<span class="tech-tag primary-dest">🇬🇷 Grecja</span>
|
||||
<span class="tech-tag">Grupy szkolne</span>
|
||||
<span class="tech-tag">Kwalifikowana Pierwsza Pomoc</span>
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-bio">
|
||||
Pedagog z wieloletnim doświadczeniem w pilotowaniu wycieczek szkolnych i kolonii zagranicznych. Uprawnienia Kwalifikowanej Pierwszej Pomocy. Doskonała komunikacja z kierowcami autokarów i nauczycielami.
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-footer">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="pilot-academy-badge">
|
||||
<i class='bx bx-award'></i> Superpilot Academy 2026
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<a href="pilot-profile.php?id=4" class="btn-offer-details">
|
||||
Szczegóły profilu
|
||||
</a>
|
||||
<button type="button" class="btn-offer-book" onclick="openBookingModal('4', 'Katarzyna Zielińska', '16.09.2027 – 24.09.2027')">
|
||||
<i class='bx bx-calendar-plus'></i> Zarezerwuj termin
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================= OFERTA 5: PAWEŁ DĄBROWSKI ================= -->
|
||||
<div class="pilot-offer-card pilot-grid-item"
|
||||
data-name="Paweł Dąbrowski"
|
||||
data-base="warszawa"
|
||||
data-destinations="Japonia Azja"
|
||||
data-languages="Polski Angielski Japoński"
|
||||
data-tags="vip incentive egzotyka korporacje kulinaria"
|
||||
data-rating="4.9"
|
||||
data-verified="1"
|
||||
data-kpp="0"
|
||||
data-dates-free="październik-2027">
|
||||
|
||||
<div class="pilot-offer-avatar" style="background: linear-gradient(135deg, #7c3aed 0%, #a78bfa 100%);">
|
||||
PD
|
||||
<div class="verified-badge-mini">
|
||||
<i class='bx bx-check'></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-content">
|
||||
<div class="pilot-offer-header-row">
|
||||
<div>
|
||||
<h5 class="pilot-offer-name">
|
||||
<a href="pilot-profile.php?id=5">Paweł Dąbrowski</a>
|
||||
</h5>
|
||||
<div class="pilot-offer-headline">
|
||||
Ekspert Wyjazdów Dalekobieżnych & VIP Incentive (Japonia)
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="pilot-rating-badge">
|
||||
<i class='bx bxs-star'></i> 4.9 <span class="text-muted fw-normal">(30 opinii)</span>
|
||||
</div>
|
||||
<button type="button" class="btn-save-pilot" title="Zapisz do ulubionych" onclick="this.classList.toggle('saved');">
|
||||
<i class='bx bx-star'></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-meta-row">
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-map-pin text-muted'></i> Baza: <strong>Warszawa</strong>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-time-five text-muted'></i> Staż: <strong>11 lat</strong>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<i class='bx bx-conversation text-muted'></i> Języki: <strong>Japoński (B2), Angielski (C2)</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pilot-tags-row">
|
||||
<span class="tech-tag primary-dest">🇯🇵 Japonia</span>
|
||||
<span class="tech-tag primary-dest">🇹🇭 Tajlandia</span>
|
||||
<span class="tech-tag primary-dest">🇺🇸 USA</span>
|
||||
<span class="tech-tag">VIP Incentive</span>
|
||||
<span class="tech-tag">Wyjazdy firmowe</span>
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-bio">
|
||||
Dyrektor operacyjny i pilot z 11-letnim doświadczeniem w wyjazdach incentive dla zarządów firm i grup VIP. Biegle posługuje się językiem japońskim i angielskim. Pełna znajomość protokołu biznesowego.
|
||||
</div>
|
||||
|
||||
<div class="pilot-offer-footer">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="pilot-academy-badge">
|
||||
<i class='bx bx-award'></i> Superpilot Academy 2026
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<a href="pilot-profile.php?id=5" class="btn-offer-details">
|
||||
Szczegóły profilu
|
||||
</a>
|
||||
<button type="button" class="btn-offer-book" onclick="openBookingModal('5', 'Paweł Dąbrowski', '10.10.2027 – 24.10.2027')">
|
||||
<i class='bx bx-calendar-plus'></i> Zarezerwuj termin
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /#pilotOffersList -->
|
||||
|
||||
</div><!-- /.col-lg-8 -->
|
||||
|
||||
</div><!-- /.row -->
|
||||
|
||||
</div><!-- /.container-xxl -->
|
||||
|
||||
<!-- ================= MODAL KALENDARZA: WYBÓR ZAKRESU DAT WYCIECZKI ================= -->
|
||||
<div class="modal fade date-picker-modal" id="dateRangeModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" style="max-width: 480px;">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header border-bottom py-3">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center bg-primary-subtle text-primary" style="width: 36px; height: 36px;">
|
||||
<i class='bx bx-calendar fs-5'></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="modal-title fw-bold text-navy mb-0">Wybierz termin wycieczki</h6>
|
||||
<small class="text-muted" style="font-size: 0.74rem;">Sprawdź wolne okienka w grafikach pilotów</small>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zamknij"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body p-3">
|
||||
|
||||
<div class="date-quick-presets">
|
||||
<span class="preset-chip active" data-preset="16-24-sep-2027">16 – 24 września 2027 (Wycieczka #12)</span>
|
||||
<span class="preset-chip" data-preset="05-15-sep-2027">05 – 15 września 2027 (Wolne okienko)</span>
|
||||
<span class="preset-chip" data-preset="październik-2027">Październik 2027</span>
|
||||
<span class="preset-chip" data-preset="all">Dowolny termin</span>
|
||||
</div>
|
||||
|
||||
<div class="p-2 border rounded-3 bg-light">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2 px-1">
|
||||
<button class="btn btn-sm btn-white p-1 rounded-circle border"><i class='bx bx-chevron-left'></i></button>
|
||||
<strong class="text-navy" style="font-size: 0.88rem;">Wrzesień 2027</strong>
|
||||
<button class="btn btn-sm btn-white p-1 rounded-circle border"><i class='bx bx-chevron-right'></i></button>
|
||||
</div>
|
||||
|
||||
<div class="date-picker-grid">
|
||||
<div class="date-picker-day-head">Pn</div>
|
||||
<div class="date-picker-day-head">Wt</div>
|
||||
<div class="date-picker-day-head">Śr</div>
|
||||
<div class="date-picker-day-head">Cz</div>
|
||||
<div class="date-picker-day-head">Pt</div>
|
||||
<div class="date-picker-day-head">Sb</div>
|
||||
<div class="date-picker-day-head">Nd</div>
|
||||
|
||||
<div class="date-picker-cell other-month">30</div>
|
||||
<div class="date-picker-cell other-month">31</div>
|
||||
<div class="date-picker-cell">1</div>
|
||||
<div class="date-picker-cell">2</div>
|
||||
<div class="date-picker-cell">3</div>
|
||||
<div class="date-picker-cell">4</div>
|
||||
<div class="date-picker-cell">5</div>
|
||||
|
||||
<div class="date-picker-cell">6</div>
|
||||
<div class="date-picker-cell">7</div>
|
||||
<div class="date-picker-cell">8</div>
|
||||
<div class="date-picker-cell">9</div>
|
||||
<div class="date-picker-cell">10</div>
|
||||
<div class="date-picker-cell">11</div>
|
||||
<div class="date-picker-cell">12</div>
|
||||
|
||||
<div class="date-picker-cell">13</div>
|
||||
<div class="date-picker-cell">14</div>
|
||||
<div class="date-picker-cell">15</div>
|
||||
<div class="date-picker-cell range-start" data-day="16">16</div>
|
||||
<div class="date-picker-cell selected-range" data-day="17">17</div>
|
||||
<div class="date-picker-cell selected-range" data-day="18">18</div>
|
||||
<div class="date-picker-cell selected-range" data-day="19">19</div>
|
||||
|
||||
<div class="date-picker-cell selected-range" data-day="20">20</div>
|
||||
<div class="date-picker-cell selected-range" data-day="21">21</div>
|
||||
<div class="date-picker-cell selected-range" data-day="22">22</div>
|
||||
<div class="date-picker-cell selected-range" data-day="23">23</div>
|
||||
<div class="date-picker-cell range-end" data-day="24">24</div>
|
||||
<div class="date-picker-cell">25</div>
|
||||
<div class="date-picker-cell">26</div>
|
||||
|
||||
<div class="date-picker-cell">27</div>
|
||||
<div class="date-picker-cell">28</div>
|
||||
<div class="date-picker-cell">29</div>
|
||||
<div class="date-picker-cell">30</div>
|
||||
<div class="date-picker-cell other-month">1</div>
|
||||
<div class="date-picker-cell other-month">2</div>
|
||||
<div class="date-picker-cell other-month">3</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 p-2 rounded-2 bg-white border text-center small text-muted">
|
||||
Wybrany termin: <strong id="modalSelectedDateText" class="text-primary">16.09.2027 – 24.09.2027 (9 dni)</strong>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer border-top py-2 px-3 justify-content-between">
|
||||
<button type="button" class="btn btn-sm btn-link text-muted text-decoration-none" id="btnResetDateDialog">
|
||||
Wyczyść termin
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-primary-calm" id="btnApplyDateDialog" data-bs-dismiss="modal">
|
||||
Zastosuj termin wycieczki
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include __DIR__ . '/footer.php'; ?>
|
||||
@@ -0,0 +1,325 @@
|
||||
<?php
|
||||
$pageTitle = 'Superpilot — Moje Zlecenia i Zapytania Ofertowe';
|
||||
$enablePrototypeComments = true;
|
||||
include __DIR__ . '/header.php';
|
||||
?>
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y" style="padding-top: 1.25rem !important;">
|
||||
|
||||
<!-- Dyskretny powrót do katalogu -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
|
||||
<a href="index.php" class="text-muted text-decoration-none small d-inline-flex align-items-center gap-1">
|
||||
<i class='bx bx-arrow-back'></i> Wróć do katalogu pilotów
|
||||
</a>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button type="button" class="btn-sos" onclick="openSosModal('Włochy')" title="Pilne zastępstwo SOS">
|
||||
<span class="sos-dot"></span>
|
||||
<span>Dyżur SOS</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary btn-sm px-3 fw-semibold d-inline-flex align-items-center gap-1" style="font-size: 0.8rem; border-radius: var(--radius-sm);" onclick="openBookingModal('1', 'Oskar Kaczmarek', '16.09.2027 - 24.09.2027')">
|
||||
<i class='bx bx-plus'></i> Nowe zapytanie o termin
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nagłówek widoku -->
|
||||
<div class="mb-4">
|
||||
<h3 class="fw-bold text-navy mb-1" style="font-size: 1.45rem;">Moje Zlecenia & Wysłane Zapytania</h3>
|
||||
<p class="text-muted small mb-0">
|
||||
Monitoring statusu obsady tras biura Almatur w aplikacji mobilnej pilotów (<code>TravelLeader Mobile</code>) oraz umowy.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Pasek 4 podsumowań (Spójny z profile-stat-box) -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-lg-3 col-sm-6">
|
||||
<div class="profile-stat-box">
|
||||
<div class="stat-val">4</div>
|
||||
<div class="stat-lbl">Wszystkie zlecenia w historii</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-sm-6">
|
||||
<div class="profile-stat-box">
|
||||
<div class="stat-val text-warning">1</div>
|
||||
<div class="stat-lbl">Oczekuje na akceptację w mobile</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-sm-6">
|
||||
<div class="profile-stat-box">
|
||||
<div class="stat-val text-success">2</div>
|
||||
<div class="stat-lbl">Zaakceptowane & obsadzone</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-sm-6">
|
||||
<div class="profile-stat-box">
|
||||
<div class="stat-val" style="color: var(--primary);">1</div>
|
||||
<div class="stat-lbl">Zrealizowana i oceniona</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Główna karta tabeli zleceń -->
|
||||
<div class="profile-section-card">
|
||||
|
||||
<!-- Nagłówek tabeli z filtrami -->
|
||||
<div class="profile-section-header flex-wrap gap-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class='bx bx-list-check fs-5 text-primary'></i>
|
||||
<h5 class="profile-section-title mb-0">Lista Zapytań Ofertowych Biura Podróży</h5>
|
||||
</div>
|
||||
|
||||
<!-- Filtry statusów (Pastylki) -->
|
||||
<div class="d-flex gap-1 flex-wrap" id="inquiryStatusFilters">
|
||||
<button type="button" class="btn btn-sm btn-primary filter-tab active" data-filter="all" style="font-size: 0.76rem;">
|
||||
Wszystkie (4)
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-light border filter-tab" data-filter="pending" style="font-size: 0.76rem;">
|
||||
Oczekujące (1)
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-light border filter-tab" data-filter="confirmed" style="font-size: 0.76rem;">
|
||||
Zaakceptowane (2)
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-light border filter-tab" data-filter="completed" style="font-size: 0.76rem;">
|
||||
Zrealizowane (1)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0" style="width: 100%;">
|
||||
<thead class="bg-light text-muted" style="font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.03em;">
|
||||
<tr>
|
||||
<th style="padding: 12px 16px; width: 28%; min-width: 190px;">Wycieczka & Termin</th>
|
||||
<th style="padding: 12px 14px; width: 20%; min-width: 160px;">Pilot</th>
|
||||
<th style="padding: 12px 14px; width: 14%; min-width: 110px;">Stawka</th>
|
||||
<th style="padding: 12px 14px; width: 20%; min-width: 160px;">Status</th>
|
||||
<th style="padding: 12px 14px; width: 9%; min-width: 80px;">Wysłano</th>
|
||||
<th class="text-end" style="padding: 12px 16px; width: 9%; min-width: 110px;">Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody style="font-size: 0.85rem;" id="inquiriesTableBody">
|
||||
|
||||
<!-- Pozycja 1: Wycieczka #15 (Oskar Kaczmarek - oczekuje) -->
|
||||
<tr class="inquiry-row" data-status="pending">
|
||||
<td style="padding: 12px 16px;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="rounded-2 d-flex align-items-center justify-content-center bg-warning-subtle text-warning fw-bold" style="width: 32px; height: 32px; font-size: 0.78rem; flex-shrink: 0;">
|
||||
#15
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-bold text-navy" style="font-size: 0.86rem;">Wycieczka #15: Magia Toskanii i Rzym</div>
|
||||
<small class="text-muted" style="font-size: 0.74rem;"><i class='bx bx-calendar'></i> 05.10 – 12.10.2027 (8 dni)</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/oskar-avatar.jpg" alt="Oskar Kaczmarek" class="rounded-circle border" style="width: 32px; height: 32px; object-fit: cover; flex-shrink: 0;">
|
||||
<div>
|
||||
<a href="pilot-profile.php" class="fw-bold text-navy text-decoration-none" style="font-size: 0.85rem;">Oskar Kaczmarek</a>
|
||||
<small class="text-muted d-block" style="font-size: 0.72rem;">★ 4.9 • Warszawa</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<strong class="text-navy">550 PLN</strong> <small class="text-muted">/ dzień</small>
|
||||
<div class="text-muted small" style="font-size: 0.7rem;">Suma: 4 400 PLN</div>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<span class="badge bg-warning-subtle text-warning fw-semibold px-2 py-1 rounded-pill d-inline-flex align-items-center gap-1" style="font-size: 0.73rem;">
|
||||
<i class='bx bx-time-five'></i> Oczekuje na akceptację
|
||||
</span>
|
||||
<small class="text-muted d-block mt-1" style="font-size: 0.7rem;">18h na odpowiedź w mobile</small>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<span class="text-muted small">Dziś, 09:15</span>
|
||||
</td>
|
||||
<td class="text-end" style="padding: 12px 16px;">
|
||||
<div class="d-flex align-items-center justify-content-end gap-1 text-nowrap">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" style="font-size: 0.75rem; padding: 2px 8px;" onclick="showToastNotification('Przypomnienie PUSH wysłane!', 'Wysłano ponowny alert na telefon pilota Oskara Kaczmarka.');">
|
||||
<i class='bx bx-bell'></i> Przypomnij
|
||||
</button>
|
||||
<a href="pilot-profile.php" class="btn btn-sm btn-light border text-muted" style="font-size: 0.75rem; padding: 2px 8px;" title="Profil pilota">
|
||||
Profil
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Pozycja 2: Wycieczka #12 (Oskar Kaczmarek - potwierdzona) -->
|
||||
<tr class="inquiry-row" data-status="confirmed">
|
||||
<td style="padding: 12px 16px;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="rounded-2 d-flex align-items-center justify-content-center bg-primary-subtle text-primary fw-bold" style="width: 32px; height: 32px; font-size: 0.78rem; flex-shrink: 0;">
|
||||
#12
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-bold text-navy" style="font-size: 0.86rem;">Wycieczka #12: Klasyczne Włochy</div>
|
||||
<small class="text-muted" style="font-size: 0.74rem;"><i class='bx bx-calendar'></i> 16.09 – 24.09.2027 (9 dni)</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/oskar-avatar.jpg" alt="Oskar Kaczmarek" class="rounded-circle border" style="width: 32px; height: 32px; object-fit: cover; flex-shrink: 0;">
|
||||
<div>
|
||||
<a href="pilot-profile.php" class="fw-bold text-navy text-decoration-none" style="font-size: 0.85rem;">Oskar Kaczmarek</a>
|
||||
<small class="text-muted d-block" style="font-size: 0.72rem;">★ 4.9 • Warszawa</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<strong class="text-navy">550 PLN</strong> <small class="text-muted">/ dzień</small>
|
||||
<div class="text-muted small" style="font-size: 0.7rem;">Suma: 4 950 PLN</div>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<span class="badge bg-success-subtle text-success fw-semibold px-2 py-1 rounded-pill d-inline-flex align-items-center gap-1" style="font-size: 0.73rem;">
|
||||
<i class='bx bx-check-circle'></i> Zaakceptowane przez pilota
|
||||
</span>
|
||||
<small class="text-muted d-block mt-1" style="font-size: 0.7rem;">Potwierdzone w mobile</small>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<span class="text-muted small">Wczoraj, 14:22</span>
|
||||
</td>
|
||||
<td class="text-end" style="padding: 12px 16px;">
|
||||
<div class="d-flex align-items-center justify-content-end gap-1 text-nowrap">
|
||||
<button type="button" class="btn btn-sm btn-light border text-muted" style="font-size: 0.75rem; padding: 2px 8px;" onclick="alert('Generowanie umowy zlecenia dla pilota Oskara Kaczmarka (Klasyczne Włochy)...');">
|
||||
<i class='bx bx-file'></i> Umowa
|
||||
</button>
|
||||
<a href="pilot-profile.php" class="btn btn-sm btn-light border text-muted" style="font-size: 0.75rem; padding: 2px 8px;">
|
||||
Profil
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Pozycja 3: Norwegia (Anna Lewandowska - potwierdzona) -->
|
||||
<tr class="inquiry-row" data-status="confirmed">
|
||||
<td style="padding: 12px 16px;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="rounded-2 d-flex align-items-center justify-content-center bg-info-subtle text-info fw-bold" style="width: 32px; height: 32px; font-size: 0.78rem; flex-shrink: 0;">
|
||||
#18
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-bold text-navy" style="font-size: 0.86rem;">Wycieczka #18: Fiordy Norwegii & Bergen</div>
|
||||
<small class="text-muted" style="font-size: 0.74rem;"><i class='bx bx-calendar'></i> 12.08 – 19.08.2027 (8 dni)</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/anna-avatar.jpg" alt="Anna Lewandowska" class="rounded-circle border" style="width: 32px; height: 32px; object-fit: cover; flex-shrink: 0;">
|
||||
<div>
|
||||
<span class="fw-bold text-navy" style="font-size: 0.85rem;">Anna Lewandowska</span>
|
||||
<small class="text-muted d-block" style="font-size: 0.72rem;">★ 5.0 • Kraków</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<strong class="text-navy">650 PLN</strong> <small class="text-muted">/ dzień</small>
|
||||
<div class="text-muted small" style="font-size: 0.7rem;">Suma: 5 200 PLN</div>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<span class="badge bg-success-subtle text-success fw-semibold px-2 py-1 rounded-pill d-inline-flex align-items-center gap-1" style="font-size: 0.73rem;">
|
||||
<i class='bx bx-check-circle'></i> Zaakceptowane przez pilota
|
||||
</span>
|
||||
<small class="text-muted d-block mt-1" style="font-size: 0.7rem;">Grafik obsadzony w 100%</small>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<span class="text-muted small">3 dni temu</span>
|
||||
</td>
|
||||
<td class="text-end" style="padding: 12px 16px;">
|
||||
<div class="d-flex align-items-center justify-content-end gap-1 text-nowrap">
|
||||
<button type="button" class="btn btn-sm btn-light border text-muted" style="font-size: 0.75rem; padding: 2px 8px;" onclick="alert('Generowanie umowy zlecenia dla pilotki Anny Lewandowskiej...');">
|
||||
<i class='bx bx-file'></i> Umowa
|
||||
</button>
|
||||
<a href="pilot-profile.php?id=2" class="btn btn-sm btn-light border text-muted" style="font-size: 0.75rem; padding: 2px 8px;">
|
||||
Profil
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Pozycja 4: Wycieczka zrealizowana (Do oceny / Zrecenzowana) -->
|
||||
<tr class="inquiry-row" data-status="completed">
|
||||
<td style="padding: 12px 16px;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="rounded-2 d-flex align-items-center justify-content-center bg-secondary-subtle text-secondary fw-bold" style="width: 32px; height: 32px; font-size: 0.78rem; flex-shrink: 0;">
|
||||
#8
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-bold text-navy" style="font-size: 0.86rem;">Wycieczka #8: Klasyczne Włochy (Maj 2026)</div>
|
||||
<small class="text-muted" style="font-size: 0.74rem;"><i class='bx bx-calendar'></i> Zrealizowana pomyślnie</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/oskar-avatar.jpg" alt="Oskar Kaczmarek" class="rounded-circle border" style="width: 32px; height: 32px; object-fit: cover; flex-shrink: 0;">
|
||||
<div>
|
||||
<a href="pilot-profile.php" class="fw-bold text-navy text-decoration-none" style="font-size: 0.85rem;">Oskar Kaczmarek</a>
|
||||
<small class="text-muted d-block" style="font-size: 0.72rem;">★ 4.9 • Warszawa</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<strong class="text-navy">500 PLN</strong> <small class="text-muted">/ dzień</small>
|
||||
<div class="text-success small" style="font-size: 0.7rem;">Rozliczona i opłacona</div>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<span class="badge bg-primary-subtle text-primary fw-semibold px-2 py-1 rounded-pill d-inline-flex align-items-center gap-1" style="font-size: 0.73rem;">
|
||||
<i class='bx bxs-star text-warning'></i> Wystawiono referencję (5.0 ★)
|
||||
</span>
|
||||
<small class="text-muted d-block mt-1" style="font-size: 0.7rem;">Pilot dodał oficjalną odpowiedź</small>
|
||||
</td>
|
||||
<td style="padding: 12px 14px;">
|
||||
<span class="text-muted small">Maj 2026</span>
|
||||
</td>
|
||||
<td class="text-end" style="padding: 12px 16px;">
|
||||
<div class="d-flex align-items-center justify-content-end text-nowrap">
|
||||
<a href="pilot-profile.php#opinie" class="btn btn-sm btn-outline-primary" style="font-size: 0.75rem; padding: 2px 10px;">
|
||||
Zobacz opinię →
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div><!-- /.container-xxl -->
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const filterButtons = document.querySelectorAll('#inquiryStatusFilters .filter-tab');
|
||||
const rows = document.querySelectorAll('.inquiry-row');
|
||||
|
||||
filterButtons.forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
filterButtons.forEach(b => {
|
||||
b.classList.remove('btn-primary', 'active');
|
||||
b.classList.add('btn-light', 'border');
|
||||
});
|
||||
this.classList.remove('btn-light', 'border');
|
||||
this.classList.add('btn-primary', 'active');
|
||||
|
||||
const filter = this.getAttribute('data-filter');
|
||||
|
||||
rows.forEach(row => {
|
||||
const status = row.getAttribute('data-status');
|
||||
if (filter === 'all' || status === filter) {
|
||||
row.style.display = '';
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include __DIR__ . '/footer.php'; ?>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
$currentPage = basename($_SERVER['PHP_SELF']);
|
||||
?>
|
||||
<aside id="layout-menu" class="layout-menu-horizontal menu-horizontal menu bg-white flex-grow-0 border-bottom" style="position: relative; top: 0; padding-top: 0; z-index: 1010; border-color: var(--border-color) !important;">
|
||||
<div class="container-xxl d-flex h-100">
|
||||
<ul class="menu-inner py-1">
|
||||
|
||||
<!-- Główny katalog -->
|
||||
<li class="menu-item <?= ($currentPage === 'index.php') ? 'active' : '' ?>">
|
||||
<a href="index.php" class="menu-link">
|
||||
<i class="menu-icon tf-icons bx bx-search"></i>
|
||||
<div data-i18n="Baza Pilotów">Katalog Pilotów</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Profil pilota -->
|
||||
<li class="menu-item <?= ($currentPage === 'pilot-profile.php' || $currentPage === 'pilot-reviews.php') ? 'active' : '' ?>">
|
||||
<a href="pilot-profile.php" class="menu-link">
|
||||
<i class="menu-icon tf-icons bx bx-user"></i>
|
||||
<div data-i18n="Profil Pilota">Profil: Oskar Kaczmarek</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Moje Zapytania i Rezerwacje -->
|
||||
<li class="menu-item <?= ($currentPage === 'inquiries.php') ? 'active' : '' ?>">
|
||||
<a href="inquiries.php" class="menu-link">
|
||||
<i class="menu-icon tf-icons bx bx-paper-plane"></i>
|
||||
<div data-i18n="Zapytania">Zapytania & Rezerwacje</div>
|
||||
<span class="badge rounded-pill bg-light text-primary border ms-2" style="font-size: 0.68rem;">3 aktywne</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Szkolenia i certyfikaty Superpilot Academy -->
|
||||
<li class="menu-item <?= ($currentPage === 'courses.php') ? 'active' : '' ?>">
|
||||
<a href="courses.php" class="menu-link">
|
||||
<i class="menu-icon tf-icons bx bx-award"></i>
|
||||
<div data-i18n="Szkolenia">Szkolenia & Certyfikaty</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
@@ -0,0 +1,896 @@
|
||||
<?php
|
||||
$pageTitle = 'Superpilot - Profil Pilota: Oskar Kaczmarek';
|
||||
$enablePrototypeComments = true;
|
||||
include __DIR__ . '/header.php';
|
||||
|
||||
// Dane Oskara Kaczmarka w 100% spójne z aplikacją mobilną travelleader-mobile
|
||||
$startYear = 2019;
|
||||
$currentYear = (int)date('Y');
|
||||
$experienceYears = max(1, $currentYear - $startYear);
|
||||
?>
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y" style="padding-top: 1.25rem !important;">
|
||||
|
||||
<!-- Dyskretny powrót do listy -->
|
||||
<div class="mb-3">
|
||||
<a href="index.php" class="text-muted text-decoration-none small d-inline-flex align-items-center gap-1">
|
||||
<i class='bx bx-arrow-back'></i> Wróć do listy pilotów
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Główny Hero Profilu (Spokojny, uporządkowany design 2026) -->
|
||||
<div class="profile-desktop-hero">
|
||||
<div class="profile-hero-top">
|
||||
|
||||
<!-- Lewa strona Hero: Awatar, dane, ranga -->
|
||||
<div class="d-flex align-items-start gap-4">
|
||||
<div class="profile-avatar-large">
|
||||
<img src="<?= $basePath ?>/assets/img/pilots/oskar-avatar.jpg" alt="Oskar Kaczmarek" class="profile-avatar-img">
|
||||
<div class="large-verified-badge" title="Zweryfikowany profil w Superpilot Academy">
|
||||
<i class='bx bx-check'></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="d-flex align-items-center gap-2 mb-1 flex-wrap">
|
||||
<h2 class="profile-name mb-0">Oskar Kaczmarek</h2>
|
||||
</div>
|
||||
|
||||
<div class="text-muted mb-2" style="font-size: 0.88rem;">
|
||||
<i class='bx bx-compass text-primary'></i> Pilot Wycieczek & Przewodnik
|
||||
</div>
|
||||
|
||||
<!-- Pasek kluczowych metadanych (Spójny z kartami index.php) -->
|
||||
<div class="profile-meta-row">
|
||||
<a href="#opinie" class="profile-meta-rating">
|
||||
<i class='bx bxs-star text-warning'></i>
|
||||
<span>4.9</span>
|
||||
<span class="text-muted fw-normal">(18 opinii)</span>
|
||||
</a>
|
||||
|
||||
<div class="profile-meta-item">
|
||||
<i class='bx bx-map-pin text-muted'></i>
|
||||
<span>Baza: <strong>Warszawa</strong> (Dojazd w całej Polsce)</span>
|
||||
</div>
|
||||
|
||||
<div class="profile-meta-item">
|
||||
<i class='bx bx-briefcase-alt text-muted'></i>
|
||||
<span>Staż: <strong><?= $experienceYears ?> lat</strong> (od <?= $startYear ?> r.)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Prawa strona Hero: Zbalansowany pasek akcji (spójna wysokość i hierarchia) -->
|
||||
<div class="d-flex align-items-center gap-2 flex-wrap justify-content-end">
|
||||
<button type="button" class="btn btn-outline-secondary d-inline-flex align-items-center justify-content-center" style="height: 38px; width: 38px; border-radius: var(--radius-sm); padding: 0;" title="Zapisz do ulubionych" onclick="this.classList.toggle('text-warning'); this.innerHTML = (this.classList.contains('text-warning') ? '<i class=\'bx bxs-bookmark fs-5\'></i>' : '<i class=\'bx bx-bookmark fs-5\'></i>');">
|
||||
<i class='bx bx-bookmark fs-5'></i>
|
||||
</button>
|
||||
|
||||
<a href="tel:+48600123456" class="btn btn-outline-secondary d-inline-flex align-items-center gap-2 fw-medium" style="height: 38px; font-size: 0.84rem; border-radius: var(--radius-sm); padding: 0 14px;">
|
||||
<i class='bx bx-phone'></i> +48 600 123 456
|
||||
</a>
|
||||
|
||||
<button type="button" class="btn btn-primary d-inline-flex align-items-center gap-2 fw-semibold" style="height: 38px; font-size: 0.86rem; border-radius: var(--radius-sm); padding: 0 18px;" onclick="openBookingModal('1', 'Oskar Kaczmarek', '16.09.2027 - 24.09.2027')">
|
||||
<i class='bx bx-calendar-plus'></i> Zarezerwuj termin
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Główny układ 2-kolumnowy (2/3 Zawartość merytoryczna + 1/3 Sticky Panel Rezerwacji i Kalendarza) -->
|
||||
<div class="row">
|
||||
|
||||
<!-- ================= LEWA KOLUMNA (2/3) ================= -->
|
||||
<div class="col-lg-8">
|
||||
|
||||
<!-- Karta 1: O mnie & profil zawodowy -->
|
||||
<div class="profile-section-card">
|
||||
<div class="profile-section-header">
|
||||
<h5 class="profile-section-title">
|
||||
<i class='bx bx-user'></i> O mnie i profil zawodowy
|
||||
</h5>
|
||||
<span class="badge bg-light text-muted border">Profil pilota</span>
|
||||
</div>
|
||||
<div class="profile-section-body">
|
||||
<p class="text-secondary mb-3" style="font-size: 0.9rem; line-height: 1.65;">
|
||||
Pasjonat kultury śródziemnomorskiej z wykształceniem historycznym (Uniwersytet Warszawski). Od <strong><?= $startYear ?> roku</strong> z sukcesami pilotuję wycieczki objazdowe po Włoszech, Hiszpanii i Bałkanach. Wyróżnia mnie wysokie opanowanie w sytuacjach kryzysowych (nagłe zmiany rozkładów, sytuacje medyczne, awarie autokarów), doskonały kontakt z grupą oraz płynna znajomość języka włoskiego i angielskiego.
|
||||
</p>
|
||||
<p class="text-secondary mb-0" style="font-size: 0.9rem; line-height: 1.65;">
|
||||
Jako absolwent <strong>Superpilot Academy 2026</strong> pracuję według rygorystycznych standardów logistycznych: bieżący kontakt z dyspozytornią biura, codzienne raportowanie statusu trasy, skrupulatne rozliczanie zaliczek dewizowych oraz dbałość o najwyższe oceny satysfakcji klientów końcowych.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Karta 2: Destynacje, Języki i Specjalizacje -->
|
||||
<div class="profile-section-card">
|
||||
<div class="profile-section-header">
|
||||
<h5 class="profile-section-title">
|
||||
<i class='bx bx-globe'></i> Destynacje, Języki & Specjalizacje
|
||||
</h5>
|
||||
</div>
|
||||
<div class="profile-section-body">
|
||||
|
||||
<!-- Destynacje (Spójne z tagami na liście ofert) -->
|
||||
<div class="mb-4">
|
||||
<div class="small fw-bold text-muted text-uppercase mb-2" style="letter-spacing: 0.03em;">Obsługiwane kraje i regiony:</div>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<span class="tech-tag primary-dest">🇮🇹 Włochy (Ekspert — Toskania, Rzym, Wenecja, Sycylia)</span>
|
||||
<span class="tech-tag primary-dest">🇬🇷 Grecja & Cyklady</span>
|
||||
<span class="tech-tag primary-dest">🇪🇸 Hiszpania & Andaluzja</span>
|
||||
<span class="tech-tag">🇭🇷 Chorwacja</span>
|
||||
<span class="tech-tag">🇳🇴 Norwegia (Fiordy)</span>
|
||||
<span class="tech-tag">🇦🇹 Austria</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Języki obce (Kompaktowe, proporcjonalne znaczniki) -->
|
||||
<div class="mb-4">
|
||||
<div class="small fw-bold text-muted text-uppercase mb-2" style="letter-spacing: 0.03em;">Znajomość języków obcych:</div>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<span class="tech-tag d-inline-flex align-items-center gap-2 py-1 px-3" style="font-size: 0.82rem;">
|
||||
<span>🇵🇱</span> <strong>Polski</strong> <span class="text-muted small">(ojczysty)</span>
|
||||
</span>
|
||||
<span class="tech-tag d-inline-flex align-items-center gap-2 py-1 px-3" style="font-size: 0.82rem;">
|
||||
<span>🇬🇧</span> <strong>Angielski</strong> <span class="badge bg-primary text-white" style="font-size: 0.65rem; padding: 2px 6px;">C1</span>
|
||||
</span>
|
||||
<span class="tech-tag d-inline-flex align-items-center gap-2 py-1 px-3" style="font-size: 0.82rem;">
|
||||
<span>🇮🇹</span> <strong>Włoski</strong> <span class="badge bg-primary text-white" style="font-size: 0.65rem; padding: 2px 6px;">B2</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Specjalizacje imprez -->
|
||||
<div class="mb-4">
|
||||
<div class="small fw-bold text-muted text-uppercase mb-2" style="letter-spacing: 0.03em;">Specjalizacje wycieczek:</div>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<span class="tech-tag"><i class='bx bx-bus me-1'></i> Wycieczki objazdowe</span>
|
||||
<span class="tech-tag"><i class='bx bx-walk me-1'></i> Trekking & góry</span>
|
||||
<span class="tech-tag"><i class='bx bx-buildings me-1'></i> Przewodnik miejski (Rzym, Florencja)</span>
|
||||
<span class="tech-tag"><i class='bx bx-book-open me-1'></i> Grupy młodzieżowe / szkolne</span>
|
||||
<span class="tech-tag"><i class='bx bx-diamond me-1'></i> Wyjazdy VIP / Incentive</span>
|
||||
<span class="tech-tag"><i class='bx bx-wine me-1'></i> Enoturystyka & kulinaria</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Certyfikaty i Uprawnienia -->
|
||||
<div>
|
||||
<div class="small fw-bold text-muted text-uppercase mb-2" style="letter-spacing: 0.03em;">Oficjalne certyfikaty i uprawnienia:</div>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<div class="p-3 rounded-3 border d-flex align-items-center justify-content-between" style="background: #f0fdf4;">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="rounded-circle bg-success text-white d-flex align-items-center justify-content-center" style="width: 38px; height: 38px; font-size: 1.2rem; flex-shrink: 0;">
|
||||
<i class='bx bx-badge-check'></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-bold text-dark" style="font-size: 0.88rem;">Superpilot Academy 2026 — Akredytacja Pilotażu Europy Południowej</div>
|
||||
<div class="text-muted" style="font-size: 0.74rem;">Certyfikat nr: <strong>SPA-2026-OK92</strong> • Standard obsługi grup autokarowych i lotniczych</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-success" style="font-size: 0.7rem;">Zweryfikowany</span>
|
||||
</div>
|
||||
|
||||
<div class="p-3 rounded-3 border d-flex align-items-center justify-content-between" style="background: #f8fafc;">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="rounded-circle bg-primary text-white d-flex align-items-center justify-content-center" style="width: 38px; height: 38px; font-size: 1.15rem; flex-shrink: 0;">
|
||||
<i class='bx bx-plus-medical'></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-bold text-dark" style="font-size: 0.88rem;">Kwalifikowana Pierwsza Pomoc (KPP)</div>
|
||||
<div class="text-muted" style="font-size: 0.74rem;">Uprawnienia ratownicze ważne do: <strong>04.2028 r.</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-light text-dark border" style="font-size: 0.7rem;">Ważny</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Karta 3: Autoprezentacja Wideo & Fotorelacje z Tras -->
|
||||
<div class="profile-section-card">
|
||||
<div class="profile-section-header flex-wrap gap-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class='bx bx-camera text-primary fs-5'></i>
|
||||
<h5 class="profile-section-title mb-0">Zdjęcia i Materiały z Tras</h5>
|
||||
</div>
|
||||
|
||||
<!-- Przycisk otwarcia pełnej galerii z filtrami w oknie -->
|
||||
<button type="button" class="btn btn-sm btn-outline-primary fw-semibold d-inline-flex align-items-center gap-1" onclick="openGalleryModal(0, 'all')">
|
||||
<i class='bx bx-images'></i> Otwórz galerię w oknie (6 zdjęć)
|
||||
</button>
|
||||
</div>
|
||||
<div class="profile-section-body">
|
||||
<div class="row g-3">
|
||||
<!-- Wideo-wizytówka (lewa strona) -->
|
||||
<div class="col-md-5">
|
||||
<div class="profile-video-box h-100" style="min-height: 198px; background: linear-gradient(rgba(15, 23, 42, 0.4), rgba(15, 23, 42, 0.75)), url('<?= $basePath ?>/assets/img/pilots/oskar-video-cover.jpg') center/cover no-repeat;" onclick="openVideoModal('Oskar Kaczmarek', '1:15 min')">
|
||||
<div class="d-flex flex-column align-items-center text-center p-3">
|
||||
<div class="profile-video-play-btn mb-2">
|
||||
<i class='bx bx-play' style="margin-left: 3px;"></i>
|
||||
</div>
|
||||
<div class="fw-bold text-white mb-1" style="font-size: 0.9rem;">Autoprezentacja warsztatu pilota</div>
|
||||
<div class="text-white-50 small" style="font-size: 0.75rem;">Florencja / Rzym • Włoski & Angielski • 1:15 min</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Siatka zdjęć z tras (kliknięcie otwiera pełną galerię z filtrami w modal) -->
|
||||
<div class="col-md-7">
|
||||
<div class="row g-2" id="profileGalleryGrid">
|
||||
<!-- 1. Włochy - Toskania -->
|
||||
<div class="col-6 col-sm-4 gallery-photo-item" data-country="wlochy">
|
||||
<div class="profile-photo-thumb" style="background: linear-gradient(to top, rgba(15, 23, 42, 0.82) 0%, rgba(15, 23, 42, 0.1) 60%, transparent 100%), url('<?= $basePath ?>/assets/img/pilots/tuscany.jpg') center/cover no-repeat;" onclick="openGalleryModal(0, 'all')">
|
||||
<div class="photo-zoom-hint"><i class='bx bx-expand-alt'></i></div>
|
||||
<div class="photo-tag-title">🇮🇹 Toskania</div>
|
||||
<div class="photo-tag-sub">Praca z grupą</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. Włochy - Rzym -->
|
||||
<div class="col-6 col-sm-4 gallery-photo-item" data-country="wlochy">
|
||||
<div class="profile-photo-thumb" style="background: linear-gradient(to top, rgba(15, 23, 42, 0.82) 0%, rgba(15, 23, 42, 0.1) 60%, transparent 100%), url('<?= $basePath ?>/assets/img/pilots/rome.jpg') center/cover no-repeat;" onclick="openGalleryModal(1, 'all')">
|
||||
<div class="photo-zoom-hint"><i class='bx bx-expand-alt'></i></div>
|
||||
<div class="photo-tag-title">🇮🇹 Rzym</div>
|
||||
<div class="photo-tag-sub">Zbiórka grupy</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. Włochy - Liguria -->
|
||||
<div class="col-6 col-sm-4 gallery-photo-item" data-country="wlochy">
|
||||
<div class="profile-photo-thumb" style="background: linear-gradient(to top, rgba(15, 23, 42, 0.82) 0%, rgba(15, 23, 42, 0.1) 60%, transparent 100%), url('<?= $basePath ?>/assets/img/pilots/chianti.jpg') center/cover no-repeat;" onclick="openGalleryModal(2, 'all')">
|
||||
<div class="photo-zoom-hint"><i class='bx bx-expand-alt'></i></div>
|
||||
<div class="photo-tag-title">🇮🇹 Liguria</div>
|
||||
<div class="photo-tag-sub">Trasa objazdowa</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4. Grecja - Cyklady -->
|
||||
<div class="col-6 col-sm-4 gallery-photo-item" data-country="grecja">
|
||||
<div class="profile-photo-thumb" style="background: linear-gradient(to top, rgba(15, 23, 42, 0.82) 0%, rgba(15, 23, 42, 0.1) 60%, transparent 100%), url('<?= $basePath ?>/assets/img/pilots/greece.jpg') center/cover no-repeat;" onclick="openGalleryModal(3, 'all')">
|
||||
<div class="photo-zoom-hint"><i class='bx bx-expand-alt'></i></div>
|
||||
<div class="photo-tag-title">🇬🇷 Grecja</div>
|
||||
<div class="photo-tag-sub">Odprawa promowa</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5. Hiszpania - Andaluzja -->
|
||||
<div class="col-6 col-sm-4 gallery-photo-item" data-country="hiszpania">
|
||||
<div class="profile-photo-thumb" style="background: linear-gradient(to top, rgba(15, 23, 42, 0.82) 0%, rgba(15, 23, 42, 0.1) 60%, transparent 100%), url('<?= $basePath ?>/assets/img/pilots/spain.jpg') center/cover no-repeat;" onclick="openGalleryModal(4, 'all')">
|
||||
<div class="photo-zoom-hint"><i class='bx bx-expand-alt'></i></div>
|
||||
<div class="photo-tag-title">🇪🇸 Andaluzja</div>
|
||||
<div class="photo-tag-sub">Objazdówka</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 6. Hiszpania - Granada -->
|
||||
<div class="col-6 col-sm-4 gallery-photo-item" data-country="hiszpania">
|
||||
<div class="profile-photo-thumb" style="background: linear-gradient(to top, rgba(15, 23, 42, 0.82) 0%, rgba(15, 23, 42, 0.1) 60%, transparent 100%), url('<?= $basePath ?>/assets/img/pilots/granada.jpg') center/cover no-repeat;" onclick="openGalleryModal(5, 'all')">
|
||||
<div class="photo-zoom-hint"><i class='bx bx-expand-alt'></i></div>
|
||||
<div class="photo-tag-title">🇪🇸 Granada</div>
|
||||
<div class="photo-tag-sub">Przewodnictwo</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Karta 4: Opinie i Referencje od Biur Podróży -->
|
||||
<div class="profile-section-card" id="opinie">
|
||||
<div class="profile-section-header flex-wrap gap-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class='bx bxs-star text-warning fs-5'></i>
|
||||
<h5 class="profile-section-title mb-0">Opinie i Referencje od Biur Podróży</h5>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<a href="pilot-reviews.php" class="btn btn-sm btn-outline-secondary" style="font-size: 0.78rem;">
|
||||
Wszystkie 18 opinii →
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-primary" style="font-size: 0.78rem;" data-bs-toggle="modal" data-bs-target="#newReviewModal">
|
||||
<i class='bx bx-edit-alt me-1'></i> Wystaw opinię po wycieczce
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-section-body">
|
||||
|
||||
<!-- Rozbicie ocen -->
|
||||
<!-- Rozbicie ocen (Przejrzyste, nowoczesne zestawienie) -->
|
||||
<div class="rating-breakdown-card mb-4">
|
||||
<div class="row align-items-center g-3">
|
||||
<!-- Główna ocena -->
|
||||
<div class="col-md-3 text-center border-end pe-md-3">
|
||||
<div class="fw-bold text-navy mb-0" style="font-size: 2.6rem; line-height: 1;">4.9</div>
|
||||
<div class="text-warning my-1" style="font-size: 1.15rem;">
|
||||
<i class='bx bxs-star'></i><i class='bx bxs-star'></i><i class='bx bxs-star'></i><i class='bx bxs-star'></i><i class='bx bxs-star-half'></i>
|
||||
</div>
|
||||
<div class="text-muted small" style="font-size: 0.76rem;">18 opinii od 6 biur</div>
|
||||
</div>
|
||||
|
||||
<!-- Wykres gwiazdek -->
|
||||
<div class="col-md-5 px-md-4 border-end">
|
||||
<div class="d-flex flex-column gap-2" style="font-size: 0.78rem;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span style="width: 24px;" class="fw-semibold text-muted">5★</span>
|
||||
<div class="progress flex-grow-1" style="height: 6px; background-color: #edf0f8;">
|
||||
<div class="progress-bar" style="width: 89%; background-color: #f59e0b;"></div>
|
||||
</div>
|
||||
<span class="text-muted small" style="width: 52px; text-align: right;">16 (89%)</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span style="width: 24px;" class="fw-semibold text-muted">4★</span>
|
||||
<div class="progress flex-grow-1" style="height: 6px; background-color: #edf0f8;">
|
||||
<div class="progress-bar" style="width: 11%; background-color: #f59e0b;"></div>
|
||||
</div>
|
||||
<span class="text-muted small" style="width: 52px; text-align: right;">2 (11%)</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span style="width: 24px;" class="fw-semibold text-muted">3★</span>
|
||||
<div class="progress flex-grow-1" style="height: 6px; background-color: #edf0f8;">
|
||||
<div class="progress-bar" style="width: 0%; background-color: #f59e0b;"></div>
|
||||
</div>
|
||||
<span class="text-muted small" style="width: 52px; text-align: right;">0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Oceny cząstkowe z perspektywy biura podróży -->
|
||||
<div class="col-md-4 ps-md-3">
|
||||
<div class="d-flex flex-column gap-2" style="font-size: 0.78rem;">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="text-muted">Współpraca z biurem:</span>
|
||||
<strong class="text-navy">5.0 / 5.0</strong>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="text-muted">Rozliczenie imprezy:</span>
|
||||
<strong class="text-navy">4.9 / 5.0</strong>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="text-muted">Realizacja programu:</span>
|
||||
<strong class="text-navy">5.0 / 5.0</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lista recenzji biur (spójna w 100% z aplikacją mobilną) -->
|
||||
<div id="agencyReviewsList">
|
||||
|
||||
<!-- Opinia 1: Almatur (Z oficjalną odpowiedzią pilota) -->
|
||||
<div class="review-card-item">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="review-agency-avatar">
|
||||
AL
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="fw-bold mb-0 text-navy" style="font-size: 0.88rem;">Biuro Podróży Almatur</h6>
|
||||
<small class="text-muted" style="font-size: 0.72rem;">Wycieczka: Klasyczne Włochy • Maj 2026</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="profile-meta-rating" style="font-size: 0.75rem;">
|
||||
<i class='bx bxs-star text-warning'></i> 5.0 / 5.0
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="mb-2 text-secondary" style="font-size: 0.85rem; line-height: 1.5;">
|
||||
"Pan Oskar prowadził 8-dniowy objazd po Włoszech. Klienci byli zachwyceni jego wiedzą i empatią. Brak jakichkolwiek problemów organizacyjnych. Bardzo chętnie zarezerwujemy kolejne terminy na jesień."
|
||||
</p>
|
||||
|
||||
<!-- Oficjalna odpowiedź pilota (Z aplikacji mobilnej!) -->
|
||||
<div class="pilot-reply-box">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<div class="d-flex align-items-center gap-1 text-primary fw-bold" style="font-size: 0.76rem;">
|
||||
<i class='bx bx-reply'></i> Oficjalna odpowiedź pilota (Oskar Kaczmarek)
|
||||
</div>
|
||||
<small class="text-muted" style="font-size: 0.7rem;">3 dni temu</small>
|
||||
</div>
|
||||
<p class="mb-0 text-secondary" style="font-size: 0.8rem; line-height: 1.45;">
|
||||
Dziękuję serdecznie za zaufanie! Współpraca z koordynatorem trasy i grupą układała się wzorowo. Do zobaczenia na kolejnych wyjazdach!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Opinia 2: Logos Tour Warszawa -->
|
||||
<div class="review-card-item">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="review-agency-avatar" style="background: #e0f2fe; color: #0284c7;">
|
||||
LT
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="fw-bold mb-0 text-navy" style="font-size: 0.88rem;">Logos Tour Warszawa</h6>
|
||||
<small class="text-muted" style="font-size: 0.72rem;">Wycieczka: Andaluzja Express • Kwiecień 2026</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="profile-meta-rating" style="font-size: 0.75rem;">
|
||||
<i class='bx bxs-star text-warning'></i> 5.0 / 5.0
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="mb-0 text-secondary" style="font-size: 0.85rem; line-height: 1.5;">
|
||||
"Znakomite przygotowanie merytoryczne. Świetny kontakt z hiszpańskimi przewodnikami lokalnymi. Rekomendujemy bez wahania."
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Opinia 3: Rainbow Tours -->
|
||||
<div class="review-card-item">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="review-agency-avatar" style="background: #fef3c7; color: #b45309;">
|
||||
RT
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="fw-bold mb-0 text-navy" style="font-size: 0.88rem;">Rainbow Tours</h6>
|
||||
<small class="text-muted" style="font-size: 0.72rem;">Wycieczka: Magia Toskanii • Wrzesień 2025</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="profile-meta-rating" style="font-size: 0.75rem;">
|
||||
<i class='bx bxs-star text-warning'></i> 4.8 / 5.0
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="mb-0 text-secondary" style="font-size: 0.85rem; line-height: 1.5;">
|
||||
"Wszystkie punkty programu zrealizowane punktualnie, świetna reakcja na deszczowy dzień we Florencji i szybka rearanżacja czasu wolnego."
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /.col-lg-8 -->
|
||||
|
||||
<!-- ================= PRAWA KOLUMNA: STICKY PANEL KALENDARZA & REZERWACJI (1/3) ================= -->
|
||||
<div class="col-lg-4">
|
||||
<div class="booking-sticky-panel">
|
||||
|
||||
<!-- Karta kalendarza dostępności (Spójna w 100% z availability.php pilota) -->
|
||||
<div class="calendar-widget-card">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="fw-bold mb-0 text-navy" style="font-size: 0.92rem;">Kalendarz Dostępności</h6>
|
||||
<span class="text-muted small">Wrzesień 2027</span>
|
||||
</div>
|
||||
|
||||
<!-- Nagłówek miesiąca -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-2 pb-2 border-bottom">
|
||||
<button class="btn btn-sm btn-light p-1 rounded-circle" type="button"><i class='bx bx-chevron-left'></i></button>
|
||||
<strong class="text-navy" style="font-size: 0.88rem;">Wrzesień 2027</strong>
|
||||
<button class="btn btn-sm btn-light p-1 rounded-circle" type="button"><i class='bx bx-chevron-right'></i></button>
|
||||
</div>
|
||||
|
||||
<!-- Siatka kalendarza -->
|
||||
<div class="sp-calendar-grid mb-3">
|
||||
<div class="sp-cal-header">Pn</div>
|
||||
<div class="sp-cal-header">Wt</div>
|
||||
<div class="sp-cal-header">Śr</div>
|
||||
<div class="sp-cal-header">Cz</div>
|
||||
<div class="sp-cal-header">Pt</div>
|
||||
<div class="sp-cal-header">Sb</div>
|
||||
<div class="sp-cal-header">Nd</div>
|
||||
|
||||
<!-- Dni sierpnia -->
|
||||
<div class="sp-cal-day other-month">30</div>
|
||||
<div class="sp-cal-day other-month">31</div>
|
||||
|
||||
<!-- 1-4.09: Urlop prywatny (zablokowany) -->
|
||||
<div class="sp-cal-day blocked" title="Urlop prywatny pilota">1</div>
|
||||
<div class="sp-cal-day blocked" title="Urlop prywatny pilota">2</div>
|
||||
<div class="sp-cal-day blocked" title="Urlop prywatny pilota">3</div>
|
||||
<div class="sp-cal-day blocked" title="Urlop prywatny pilota">4</div>
|
||||
|
||||
<!-- 5-15.09: Wolne terminy dla biur -->
|
||||
<div class="sp-cal-day free" data-day="5" title="Wolny termin (5.09)">5 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="6" title="Wolny termin (6.09)">6 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="7" title="Wolny termin (7.09)">7 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="8" title="Wolny termin (8.09)">8 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="9" title="Wolny termin (9.09)">9 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="10" title="Wolny termin (10.09)">10 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="11" title="Wolny termin (11.09)">11 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="12" title="Wolny termin (12.09)">12 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="13" title="Wolny termin (13.09)">13 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="14" title="Wolny termin (14.09)">14 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="15" title="Wolny termin (15.09)">15 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
|
||||
<!-- 16-24.09: Wycieczka potwierdzona (#12 oskar prod) -->
|
||||
<div class="sp-cal-day trip" title="Zajęte: Wycieczka #12 oskar prod">16 <span class="sp-cal-dot" style="background:#5c60f5;"></span></div>
|
||||
<div class="sp-cal-day trip" title="Zajęte: Wycieczka #12 oskar prod">17 <span class="sp-cal-dot" style="background:#5c60f5;"></span></div>
|
||||
<div class="sp-cal-day trip" title="Zajęte: Wycieczka #12 oskar prod">18 <span class="sp-cal-dot" style="background:#5c60f5;"></span></div>
|
||||
<div class="sp-cal-day trip" title="Zajęte: Wycieczka #12 oskar prod">19 <span class="sp-cal-dot" style="background:#5c60f5;"></span></div>
|
||||
<div class="sp-cal-day trip" title="Zajęte: Wycieczka #12 oskar prod">20 <span class="sp-cal-dot" style="background:#5c60f5;"></span></div>
|
||||
<div class="sp-cal-day trip" title="Zajęte: Wycieczka #12 oskar prod">21 <span class="sp-cal-dot" style="background:#5c60f5;"></span></div>
|
||||
<div class="sp-cal-day trip" title="Zajęte: Wycieczka #12 oskar prod">22 <span class="sp-cal-dot" style="background:#5c60f5;"></span></div>
|
||||
<div class="sp-cal-day trip" title="Zajęte: Wycieczka #12 oskar prod">23 <span class="sp-cal-dot" style="background:#5c60f5;"></span></div>
|
||||
<div class="sp-cal-day trip" title="Zajęte: Wycieczka #12 oskar prod">24 <span class="sp-cal-dot" style="background:#5c60f5;"></span></div>
|
||||
|
||||
<!-- 25-30.09: Wolne -->
|
||||
<div class="sp-cal-day free" data-day="25" title="Wolny termin (25.09)">25 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="26" title="Wolny termin (26.09)">26 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="27" title="Wolny termin (27.09)">27 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="28" title="Wolny termin (28.09)">28 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="29" title="Wolny termin (29.09)">29 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
<div class="sp-cal-day free" data-day="30" title="Wolny termin (30.09)">30 <span class="sp-cal-dot" style="background:#10b981;"></span></div>
|
||||
</div>
|
||||
|
||||
<!-- Legenda -->
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 pt-2 border-top small" style="font-size: 0.72rem;">
|
||||
<span class="d-flex align-items-center gap-1">
|
||||
<span style="width:7px; height:7px; background:#10b981; border-radius:50%;"></span> Wolny
|
||||
</span>
|
||||
<span class="d-flex align-items-center gap-1">
|
||||
<span style="width:7px; height:7px; background:#5c60f5; border-radius:50%;"></span> Zajęty
|
||||
</span>
|
||||
<span class="d-flex align-items-center gap-1 text-muted">
|
||||
<span style="width:7px; height:7px; background:#cbd5e1; border-radius:50%;"></span> Urlop
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div id="calSelectedContainer" class="mt-2 text-center text-muted" style="font-size: 0.76rem; min-height: 18px;">
|
||||
<span id="calSelectedRange"></span>
|
||||
</div>
|
||||
|
||||
<!-- Główny przycisk rezerwacji pod kalendarzem -->
|
||||
<button type="button" id="btnBookFromCalendar" class="btn btn-primary w-100 py-2 mt-2 fw-semibold d-flex align-items-center justify-content-center gap-2" style="font-size: 0.88rem; border-radius: var(--radius-sm);" onclick="openBookingModal('1', 'Oskar Kaczmarek', window.selectedProfileDates || '16.09.2027 - 24.09.2027')">
|
||||
<i class='bx bx-calendar-plus'></i> Zarezerwuj termin
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div><!-- /.col-lg-4 -->
|
||||
|
||||
</div><!-- /.row -->
|
||||
|
||||
</div><!-- /.container-xxl -->
|
||||
|
||||
<!-- Modal Wystawienia Nowej Opinii po Wycieczce (Zgodny ze stonowanym designem) -->
|
||||
<div class="modal fade" id="newReviewModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content border-0 shadow-lg" style="border-radius: var(--radius-md);">
|
||||
<div class="modal-header bg-white border-bottom py-3">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class='bx bxs-star text-warning fs-4'></i>
|
||||
<div>
|
||||
<h5 class="modal-title fw-bold mb-0 text-navy" style="font-size: 1.05rem;">Wystaw Opinię i Referencję dla Pilota</h5>
|
||||
<small class="text-muted" style="font-size: 0.76rem;">Ocena trafi do profilu pilota i aplikacji mobilnej</small>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zamknij"></button>
|
||||
</div>
|
||||
|
||||
<form id="addReviewForm">
|
||||
<div class="modal-body p-4">
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold small text-muted">Wycieczka z bazy biura:</label>
|
||||
<select class="form-select" required>
|
||||
<option value="">Wybierz zakończoną wycieczkę...</option>
|
||||
<option value="12" selected>Wycieczka #12: Klasyczne Włochy (Almatur)</option>
|
||||
<option value="8">Wycieczka #8: Toskania & Rzym (Maj 2026)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold small text-muted">Ocena ogólna:</label>
|
||||
<select class="form-select fw-bold text-warning" required>
|
||||
<option value="5" selected>★★★★★ 5.0 — Rewelacyjny (Pełna rekomendacja)</option>
|
||||
<option value="4">★★★★☆ 4.0 — Bardzo dobry</option>
|
||||
<option value="3">★★★☆☆ 3.0 — Przeciętny</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Oceny cząstkowe z perspektywy biura -->
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold small text-muted">Kryteria oceny przez biuro podróży:</label>
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<div class="p-2 border rounded bg-light text-center">
|
||||
<div class="small fw-semibold text-dark">Współpraca z biurem</div>
|
||||
<div class="text-muted" style="font-size: 0.68rem;">Kontakt & dyspozytornia</div>
|
||||
<div class="text-warning fs-5 mt-1">★★★★★</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="p-2 border rounded bg-light text-center">
|
||||
<div class="small fw-semibold text-dark">Rozliczenie imprezy</div>
|
||||
<div class="text-muted" style="font-size: 0.68rem;">Zaliczki & faktury</div>
|
||||
<div class="text-warning fs-5 mt-1">★★★★★</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="p-2 border rounded bg-light text-center">
|
||||
<div class="small fw-semibold text-dark">Realizacja programu</div>
|
||||
<div class="text-muted" style="font-size: 0.68rem;">Brak reklamacji & logistyka</div>
|
||||
<div class="text-warning fs-5 mt-1">★★★★★</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold small text-muted">Treść referencji / recenzji:</label>
|
||||
<textarea class="form-control" rows="4" placeholder="Opisz jak pilot poradził sobie z trasą, grupą i programem..." required>Pan Oskar doskonale poprowadził trasę po Włoszech. Bardzo wysoka kultura osobista i biegły język włoski rozwiązały problem z opóźnieniem autokaru. Turyści zachwyceni.</textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="recommendCheck" checked>
|
||||
<label class="form-check-label fw-bold small text-dark" for="recommendCheck">
|
||||
Polecam tego pilota innym koordynatorom biur podróży
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer bg-light px-4 py-3 border-top">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Anuluj</button>
|
||||
<button type="submit" class="btn btn-primary fw-semibold px-4">
|
||||
<i class='bx bx-check me-1'></i> Opublikuj oficjalną referencję
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openVideoModal(pilotName, duration) {
|
||||
const modalEl = document.getElementById('videoPitchModal');
|
||||
if (modalEl) {
|
||||
if (pilotName && document.getElementById('videoModalPilotName')) {
|
||||
document.getElementById('videoModalPilotName').innerText = pilotName;
|
||||
}
|
||||
if (duration && document.getElementById('videoModalLength')) {
|
||||
document.getElementById('videoModalLength').innerText = '(' + duration + ')';
|
||||
}
|
||||
const modal = new bootstrap.Modal(modalEl);
|
||||
modal.show();
|
||||
}
|
||||
}
|
||||
|
||||
// Baza danych kadrów z tras pilota z podziałem na kraje
|
||||
const pilotGalleryData = [
|
||||
{
|
||||
country: 'wlochy',
|
||||
countryName: 'Włochy',
|
||||
flag: '🇮🇹',
|
||||
title: 'Toskania — Florencja & Chianti',
|
||||
subtitle: 'Wrzesień 2026 • Grupa objazdowa Almatur',
|
||||
img: '<?= $basePath ?>/assets/img/pilots/tuscany.jpg'
|
||||
},
|
||||
{
|
||||
country: 'wlochy',
|
||||
countryName: 'Włochy',
|
||||
flag: '🇮🇹',
|
||||
title: 'Rzym — Koloseum & Forum Romanum',
|
||||
subtitle: 'Październik 2026 • Koordynacja biletowa Tour Guide',
|
||||
img: '<?= $basePath ?>/assets/img/pilots/rome.jpg'
|
||||
},
|
||||
{
|
||||
country: 'wlochy',
|
||||
countryName: 'Włochy',
|
||||
flag: '🇮🇹',
|
||||
title: 'Liguria — Wybrzeże & Cinque Terre',
|
||||
subtitle: 'Maj 2026 • Trasa autokarowo-kolejowa',
|
||||
img: '<?= $basePath ?>/assets/img/pilots/chianti.jpg'
|
||||
},
|
||||
{
|
||||
country: 'grecja',
|
||||
countryName: 'Grecja',
|
||||
flag: '🇬🇷',
|
||||
title: 'Cyklady & Ateny — Odprawa promowa',
|
||||
subtitle: 'Czerwiec 2026 • Port Pireus & Akropol',
|
||||
img: '<?= $basePath ?>/assets/img/pilots/greece.jpg'
|
||||
},
|
||||
{
|
||||
country: 'hiszpania',
|
||||
countryName: 'Hiszpania',
|
||||
flag: '🇪🇸',
|
||||
title: 'Andaluzja — Sewilla & Kordoba',
|
||||
subtitle: 'Kwiecień 2026 • Trasa objazdowa Mezquita',
|
||||
img: '<?= $basePath ?>/assets/img/pilots/spain.jpg'
|
||||
},
|
||||
{
|
||||
country: 'hiszpania',
|
||||
countryName: 'Hiszpania',
|
||||
flag: '🇪🇸',
|
||||
title: 'Granada — Alhambra & Generalife',
|
||||
subtitle: 'Maj 2026 • Wejścia imienne & logistyka',
|
||||
img: '<?= $basePath ?>/assets/img/pilots/granada.jpg'
|
||||
}
|
||||
];
|
||||
|
||||
let activeGalleryFilter = 'all';
|
||||
let filteredPhotoIndices = [0, 1, 2, 3, 4, 5];
|
||||
let activeFilteredIndex = 0;
|
||||
|
||||
function renderGalleryCurrentPhoto() {
|
||||
if (filteredPhotoIndices.length === 0) return;
|
||||
const globalIdx = filteredPhotoIndices[activeFilteredIndex];
|
||||
const photo = pilotGalleryData[globalIdx];
|
||||
|
||||
const imgEl = document.getElementById('galleryModalImg');
|
||||
const titleEl = document.getElementById('galleryModalTitle');
|
||||
const subEl = document.getElementById('galleryModalSub');
|
||||
const badgeEl = document.getElementById('galleryModalCountryBadge');
|
||||
const counterEl = document.getElementById('galleryModalCounter');
|
||||
|
||||
if (imgEl) {
|
||||
imgEl.style.opacity = '0.35';
|
||||
imgEl.src = photo.img;
|
||||
setTimeout(() => { imgEl.style.opacity = '1'; }, 60);
|
||||
}
|
||||
if (titleEl) titleEl.innerText = photo.title;
|
||||
if (subEl) subEl.innerText = photo.subtitle;
|
||||
if (badgeEl) badgeEl.innerText = photo.flag + ' ' + photo.countryName;
|
||||
if (counterEl) {
|
||||
counterEl.innerText = (activeFilteredIndex + 1) + ' / ' + filteredPhotoIndices.length;
|
||||
}
|
||||
|
||||
// Podświetlenie aktywnej miniaturki w dolnym pasku
|
||||
const thumbs = document.querySelectorAll('.modal-gallery-thumb-item');
|
||||
thumbs.forEach((thumb, idx) => {
|
||||
if (idx === activeFilteredIndex) {
|
||||
thumb.classList.add('active');
|
||||
thumb.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' });
|
||||
} else {
|
||||
thumb.classList.remove('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderGalleryThumbnails() {
|
||||
const thumbsContainer = document.getElementById('modalGalleryThumbs');
|
||||
if (!thumbsContainer) return;
|
||||
thumbsContainer.innerHTML = '';
|
||||
|
||||
filteredPhotoIndices.forEach((globalIdx, filterIdx) => {
|
||||
const photo = pilotGalleryData[globalIdx];
|
||||
const thumb = document.createElement('div');
|
||||
thumb.className = 'modal-gallery-thumb-item' + (filterIdx === activeFilteredIndex ? ' active' : '');
|
||||
thumb.style.backgroundImage = 'url(' + photo.img + ')';
|
||||
thumb.title = photo.title;
|
||||
thumb.addEventListener('click', function() {
|
||||
activeFilteredIndex = filterIdx;
|
||||
renderGalleryCurrentPhoto();
|
||||
});
|
||||
thumbsContainer.appendChild(thumb);
|
||||
});
|
||||
}
|
||||
|
||||
function setGalleryFilter(filterKey, preferredGlobalIdx) {
|
||||
activeGalleryFilter = filterKey;
|
||||
|
||||
// Aktualizacja przycisków filtrów wewnątrz modala
|
||||
const filterButtons = document.querySelectorAll('#modalGalleryFilters .modal-filter-btn');
|
||||
filterButtons.forEach(btn => {
|
||||
if (btn.getAttribute('data-filter') === filterKey) {
|
||||
btn.classList.add('active');
|
||||
} else {
|
||||
btn.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Filtrowanie indeksów
|
||||
filteredPhotoIndices = [];
|
||||
pilotGalleryData.forEach((p, idx) => {
|
||||
if (filterKey === 'all' || p.country === filterKey) {
|
||||
filteredPhotoIndices.push(idx);
|
||||
}
|
||||
});
|
||||
|
||||
if (filteredPhotoIndices.length === 0) return;
|
||||
|
||||
if (preferredGlobalIdx !== undefined && filteredPhotoIndices.includes(preferredGlobalIdx)) {
|
||||
activeFilteredIndex = filteredPhotoIndices.indexOf(preferredGlobalIdx);
|
||||
} else {
|
||||
activeFilteredIndex = 0;
|
||||
}
|
||||
|
||||
renderGalleryThumbnails();
|
||||
renderGalleryCurrentPhoto();
|
||||
}
|
||||
|
||||
function galleryPrev() {
|
||||
if (filteredPhotoIndices.length <= 1) return;
|
||||
activeFilteredIndex = (activeFilteredIndex - 1 + filteredPhotoIndices.length) % filteredPhotoIndices.length;
|
||||
renderGalleryCurrentPhoto();
|
||||
}
|
||||
|
||||
function galleryNext() {
|
||||
if (filteredPhotoIndices.length <= 1) return;
|
||||
activeFilteredIndex = (activeFilteredIndex + 1) % filteredPhotoIndices.length;
|
||||
renderGalleryCurrentPhoto();
|
||||
}
|
||||
|
||||
function openGalleryModal(globalIndex, initialFilter) {
|
||||
const modalEl = document.getElementById('photoLightboxModal');
|
||||
if (modalEl) {
|
||||
const targetFilter = initialFilter || 'all';
|
||||
setGalleryFilter(targetFilter, globalIndex !== undefined ? globalIndex : 0);
|
||||
const modal = bootstrap.Modal.getOrCreateInstance(modalEl);
|
||||
modal.show();
|
||||
}
|
||||
}
|
||||
|
||||
function openPhotoLightbox(caption, imgSrc) {
|
||||
openGalleryModal(0, 'all');
|
||||
}
|
||||
|
||||
// Skróty klawiszowe dla modala galerii
|
||||
document.addEventListener('keydown', function(e) {
|
||||
const modalEl = document.getElementById('photoLightboxModal');
|
||||
if (modalEl && modalEl.classList.contains('show')) {
|
||||
if (e.key === 'ArrowLeft') {
|
||||
galleryPrev();
|
||||
} else if (e.key === 'ArrowRight') {
|
||||
galleryNext();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
window.selectedProfileDates = '16.09.2027 - 24.09.2027';
|
||||
|
||||
// Przełączniki filtrów wewnątrz modala galerii
|
||||
const modalFilterBtns = document.querySelectorAll('#modalGalleryFilters .modal-filter-btn');
|
||||
modalFilterBtns.forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
const filter = this.getAttribute('data-filter');
|
||||
setGalleryFilter(filter);
|
||||
});
|
||||
});
|
||||
|
||||
// Strzałki nawigacji w modalu galerii
|
||||
const btnPrev = document.getElementById('galleryBtnPrev');
|
||||
const btnNext = document.getElementById('galleryBtnNext');
|
||||
if (btnPrev) btnPrev.addEventListener('click', galleryPrev);
|
||||
if (btnNext) btnNext.addEventListener('click', galleryNext);
|
||||
|
||||
// Interakcja z dniami kalendarza
|
||||
const calDays = document.querySelectorAll('.sp-cal-day.free');
|
||||
const selectedRangeSpan = document.getElementById('calSelectedRange');
|
||||
|
||||
calDays.forEach(day => {
|
||||
day.addEventListener('click', function() {
|
||||
calDays.forEach(d => d.classList.remove('selected'));
|
||||
this.classList.add('selected');
|
||||
const dayNum = parseInt(this.getAttribute('data-day'), 10);
|
||||
const dayFormatted = (dayNum < 10 ? '0' + dayNum : dayNum) + '.09.2027';
|
||||
window.selectedProfileDates = dayFormatted;
|
||||
if (selectedRangeSpan) {
|
||||
selectedRangeSpan.innerHTML = '<span class="badge bg-success-subtle text-success border border-success-subtle py-1 px-2">Wybrany termin: <strong>' + dayFormatted + '</strong></span>';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Obsługa formularza wystawienia opinii
|
||||
const reviewForm = document.getElementById('addReviewForm');
|
||||
if (reviewForm) {
|
||||
reviewForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const modalEl = document.getElementById('newReviewModal');
|
||||
const modal = bootstrap.Modal.getInstance(modalEl);
|
||||
if (modal) modal.hide();
|
||||
|
||||
if (typeof showToastNotification === 'function') {
|
||||
showToastNotification('Referencja opublikowana!', 'Dziękujemy. Twoja opinia została dodana do profilu pilota i rankingu Superpilot.');
|
||||
} else {
|
||||
alert('Dziękujemy! Twoja opinia została opublikowana.');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include __DIR__ . '/footer.php'; ?>
|
||||
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
$pageTitle = 'Superpilot - Opinie i Referencje Biur Podróży: Oskar Kaczmarek';
|
||||
$enablePrototypeComments = true;
|
||||
include __DIR__ . '/header.php';
|
||||
?>
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y" style="padding-top: 1.5rem !important;">
|
||||
|
||||
<!-- Pasek powrotu i akcje -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb mb-0" style="font-size: 0.8rem;">
|
||||
<li class="breadcrumb-item"><a href="index.php" class="text-muted"><i class='bx bx-arrow-back'></i> Katalog Pilotów</a></li>
|
||||
<li class="breadcrumb-item"><a href="pilot-profile.php" class="text-muted">Oskar Kaczmarek</a></li>
|
||||
<li class="breadcrumb-item active text-primary fw-bold" aria-current="page">Opinie i Referencje Biur</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<a href="../travelleader-mobile/profile-reviews.php" target="_blank" class="btn btn-sm btn-outline-secondary d-inline-flex align-items-center gap-1" style="font-size: 0.78rem;">
|
||||
<i class='bx bx-mobile'></i> Zobacz w aplikacji mobilnej pilota <i class='bx bx-link-external' style="font-size: 0.7rem;"></i>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-superpilot" onclick="openBookingModal('1', 'Oskar Kaczmarek', '16.09.2027 - 24.09.2027')">
|
||||
<i class='bx bx-calendar-plus'></i> Zarezerwuj pilota
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nagłówek i Karta Pilota -->
|
||||
<div class="card border-0 shadow-sm mb-4" style="border-radius: var(--sp-radius-md);">
|
||||
<div class="card-body p-4">
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="rounded-3 d-flex align-items-center justify-content-center text-white fw-bold shadow-sm" style="width: 58px; height: 58px; background: linear-gradient(135deg, #4f46e5, #7c3aed); font-size: 1.5rem;">
|
||||
OK
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<h4 class="fw-bold mb-0 text-navy">Oskar Kaczmarek — Opinie Biur Podróży</h4>
|
||||
<span class="badge bg-success-subtle text-success fw-bold">Superpilot Verified</span>
|
||||
</div>
|
||||
<small class="text-muted">Wszystkie referencje wystawione przez zweryfikowanych koordynatorów w ekosystemie TravelManager i PrzezMorze</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button type="button" class="btn btn-warning fw-bold text-dark px-3 py-2" data-bs-toggle="modal" data-bs-target="#newReviewModal">
|
||||
<i class='bx bx-edit-alt'></i> + Wystaw nową opinię
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rating Summary Hero & Rozbicie gwiazdek -->
|
||||
<div class="row g-4 mb-4">
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card border-0 shadow-sm h-100 text-center p-4" style="border-radius: var(--sp-radius-md);">
|
||||
<div class="display-3 fw-bold text-navy mb-1" style="font-size: 3.5rem; line-height: 1;">4.9</div>
|
||||
<div class="text-warning fs-3 mb-2">
|
||||
<i class='bx bxs-star'></i><i class='bx bxs-star'></i><i class='bx bxs-star'></i><i class='bx bxs-star'></i><i class='bx bxs-star-half'></i>
|
||||
</div>
|
||||
<div class="text-muted small mb-3">Średnia z 18 referencji od 6 biur podróży</div>
|
||||
|
||||
<div class="badge bg-success-subtle text-success p-2 rounded-pill fw-bold mb-3" style="font-size: 0.8rem;">
|
||||
<i class='bx bx-check-shield'></i> 100% biur poleca pilota na kolejne wycieczki
|
||||
</div>
|
||||
|
||||
<div class="pt-3 border-top text-start text-muted small">
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<span>Almatur:</span> <strong class="text-dark">5.0 ★ (8 wycieczek)</strong>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<span>Logos Tour:</span> <strong class="text-dark">5.0 ★ (4 wycieczki)</strong>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<span>Rainbow:</span> <strong class="text-dark">4.8 ★ (6 wycieczek)</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<div class="card border-0 shadow-sm h-100 p-4" style="border-radius: var(--sp-radius-md);">
|
||||
<h6 class="fw-bold text-navy mb-3">Szczegółowy rozkład ocen i kategorii:</h6>
|
||||
|
||||
<!-- Słupki gwiazdek -->
|
||||
<div class="d-flex flex-column gap-2 mb-4" style="font-size: 0.84rem;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span style="width: 35px;" class="fw-semibold">5 ★</span>
|
||||
<div class="progress flex-grow-1" style="height: 10px;">
|
||||
<div class="progress-bar bg-warning" style="width: 89%;"></div>
|
||||
</div>
|
||||
<span class="text-muted" style="width: 70px;">16 opinii (89%)</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span style="width: 35px;" class="fw-semibold">4 ★</span>
|
||||
<div class="progress flex-grow-1" style="height: 10px;">
|
||||
<div class="progress-bar bg-warning" style="width: 11%;"></div>
|
||||
</div>
|
||||
<span class="text-muted" style="width: 70px;">2 opinie (11%)</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span style="width: 35px;" class="fw-semibold">3 ★</span>
|
||||
<div class="progress flex-grow-1" style="height: 10px;">
|
||||
<div class="progress-bar bg-warning" style="width: 0%;"></div>
|
||||
</div>
|
||||
<span class="text-muted" style="width: 70px;">0 opinii</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Kluczowe wymiary oceny biura podróży -->
|
||||
<div class="row g-3 pt-3 border-top">
|
||||
<div class="col-md-4 col-12">
|
||||
<div class="p-3 rounded-3 bg-light text-center">
|
||||
<div class="text-warning fs-5 fw-bold">5.0 ★</div>
|
||||
<strong class="text-dark d-block mt-1" style="font-size: 0.85rem;">Współpraca z biurem</strong>
|
||||
<small class="text-muted d-block" style="font-size: 0.72rem;">Bieżący kontakt i dyspozycyjność</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-6">
|
||||
<div class="p-3 rounded-3 bg-light text-center">
|
||||
<div class="text-warning fs-5 fw-bold">4.9 ★</div>
|
||||
<strong class="text-dark d-block mt-1" style="font-size: 0.85rem;">Rozliczenie imprezy</strong>
|
||||
<small class="text-muted d-block" style="font-size: 0.72rem;">Zaliczki, faktury i dokumenty</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-6">
|
||||
<div class="p-3 rounded-3 bg-light text-center">
|
||||
<div class="text-warning fs-5 fw-bold">5.0 ★</div>
|
||||
<strong class="text-dark d-block mt-1" style="font-size: 0.85rem;">Realizacja programu</strong>
|
||||
<small class="text-muted d-block" style="font-size: 0.72rem;">Logistyka i brak reklamacji</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Filtry destynacji opinii -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-sm btn-primary">Wszystkie opinie (18)</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary">🇮🇹 Włochy (12)</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary">🇪🇸 Hiszpania (4)</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary">🇬🇷 Grecja (2)</button>
|
||||
</div>
|
||||
<small class="text-muted">Sortowanie: Najnowsze opinie</small>
|
||||
</div>
|
||||
|
||||
<!-- Lista opinii biur podróży -->
|
||||
<div id="agencyReviewsList">
|
||||
|
||||
<!-- Recenzja 1: Almatur -->
|
||||
<div class="review-card-item">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="review-agency-avatar">
|
||||
AL
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<h6 class="fw-bold mb-0 text-navy">Biuro Podróży Almatur</h6>
|
||||
<span class="badge bg-label-primary small">Zweryfikowane zlecenie #12</span>
|
||||
</div>
|
||||
<small class="text-muted">Wycieczka: Klasyczne Włochy (8 dni) • Realizacja: Maj 2026 • Koordynator: Tomasz W.</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-warning-subtle text-warning fw-bold px-3 py-2 rounded-pill fs-6">
|
||||
<i class='bx bxs-star'></i> 5.0 / 5.0
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="mb-3 text-secondary" style="font-size: 0.92rem; line-height: 1.55;">
|
||||
"Pan Oskar prowadził 8-dniowy objazd po Włoszech. Klienci byli zachwyceni jego wiedzą i empatią. Brak jakichkolwiek problemów organizacyjnych. Doskonała reakcja w trakcie korka na autostradzie pod Bolonią — natychmiast skontaktował się z restauracją i przesunął kolację bez utraty komfortu turystów. Bardzo chętnie zarezerwujemy kolejne terminy na jesień."
|
||||
</p>
|
||||
|
||||
<!-- Oficjalna odpowiedź pilota -->
|
||||
<div class="pilot-reply-box">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<div class="d-flex align-items-center gap-1 text-primary fw-bold" style="font-size: 0.8rem;">
|
||||
<i class='bx bx-reply fs-5'></i> Oficjalna odpowiedź pilota (Oskar Kaczmarek)
|
||||
</div>
|
||||
<small class="text-muted">3 dni temu z aplikacji mobilnej</small>
|
||||
</div>
|
||||
<p class="mb-0 text-secondary" style="font-size: 0.84rem; line-height: 1.45;">
|
||||
Dziękuję serdecznie za zaufanie! Współpraca z koordynatorem trasy i grupą układała się wzorowo. Do zobaczenia na kolejnych wyjazdach!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recenzja 2: Logos Tour -->
|
||||
<div class="review-card-item">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="review-agency-avatar" style="background: #e0f2fe; color: #0284c7;">
|
||||
LT
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<h6 class="fw-bold mb-0 text-navy">Logos Tour Warszawa</h6>
|
||||
<span class="badge bg-label-info small">Zweryfikowane zlecenie</span>
|
||||
</div>
|
||||
<small class="text-muted">Wycieczka: Andaluzja Express • Realizacja: Kwiecień 2026</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-warning-subtle text-warning fw-bold px-3 py-2 rounded-pill fs-6">
|
||||
<i class='bx bxs-star'></i> 5.0 / 5.0
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="mb-0 text-secondary" style="font-size: 0.92rem; line-height: 1.55;">
|
||||
"Znakomite przygotowanie merytoryczne. Świetny kontakt z hiszpańskimi przewodnikami lokalnymi. Oskar potrafi utrzymać dyscyplinę czasową w grupie 45 osób w sposób całkowicie bezstresowy. Rekomendujemy bez wahania."
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Recenzja 3: Rainbow Tours -->
|
||||
<div class="review-card-item">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="review-agency-avatar" style="background: #fef3c7; color: #b45309;">
|
||||
RT
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<h6 class="fw-bold mb-0 text-navy">Rainbow Tours</h6>
|
||||
<span class="badge bg-label-warning small">Zweryfikowane zlecenie</span>
|
||||
</div>
|
||||
<small class="text-muted">Wycieczka: Magia Toskanii • Realizacja: Wrzesień 2025</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-warning-subtle text-warning fw-bold px-3 py-2 rounded-pill fs-6">
|
||||
<i class='bx bxs-star'></i> 4.8 / 5.0
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="mb-0 text-secondary" style="font-size: 0.92rem; line-height: 1.55;">
|
||||
"Wszystkie punkty programu zrealizowane punktualnie, świetna reakcja na deszczowy dzień we Florencji i szybka rearanżacja czasu wolnego. Jeden z najbardziej profesjonalnych pilotów w naszej bazie."
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div><!-- /.container-xxl -->
|
||||
|
||||
<!-- Modal dodania opinii -->
|
||||
<div class="modal fade" id="newReviewModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content border-0 shadow-lg" style="border-radius: var(--sp-radius-md);">
|
||||
<div class="modal-header bg-warning text-dark">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class='bx bxs-star fs-3'></i>
|
||||
<div>
|
||||
<h5 class="modal-title fw-bold mb-0">Wystaw Opinię i Referencję dla Pilota</h5>
|
||||
<small class="text-dark-50" style="font-size: 0.76rem;">Ocena zostanie uwzględniona w rankingu Superpilot i trafi do aplikacji pilota</small>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zamknij"></button>
|
||||
</div>
|
||||
|
||||
<form id="addReviewForm">
|
||||
<div class="modal-body p-4">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold small text-muted">Wycieczka z bazy biura:</label>
|
||||
<select class="form-select" required>
|
||||
<option value="">Wybierz zakończoną wycieczkę...</option>
|
||||
<option value="12" selected>Wycieczka #12: Klasyczne Włochy (Almatur)</option>
|
||||
<option value="8">Wycieczka #8: Toskania & Rzym (Maj 2026)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold small text-muted">Ocena ogólna:</label>
|
||||
<select class="form-select fw-bold text-warning" required>
|
||||
<option value="5" selected>★★★★★ 5.0 — Rewelacyjny (Pełna rekomendacja)</option>
|
||||
<option value="4">★★★★☆ 4.0 — Bardzo dobry</option>
|
||||
<option value="3">★★★☆☆ 3.0 — Przeciętny</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold small text-muted">Treść referencji / recenzji:</label>
|
||||
<textarea class="form-control" rows="4" placeholder="Opisz jak pilot poradził sobie z trasą, grupą i programem..." required>Wzorowa realizacja wyjazdu. Pilot doskonale poradził sobie ze zmianą harmonogramu z powodu pogody. Grupa była zachwycona. Zdecydowanie polecamy!</textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="recommendCheck" checked>
|
||||
<label class="form-check-label fw-bold small" for="recommendCheck">
|
||||
Polecam tego pilota innym koordynatorom biur podróży
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer bg-light px-4 py-3 border-top">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Anuluj</button>
|
||||
<button type="submit" class="btn btn-warning fw-bold px-4">
|
||||
<i class='bx bx-check'></i> Opublikuj oficjalną referencję
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include __DIR__ . '/footer.php'; ?>
|
||||
@@ -0,0 +1,138 @@
|
||||
<!-- Modal Pilnego Zastępstwa SOS -->
|
||||
<div class="modal fade" id="sosModal" tabindex="-1" aria-labelledby="sosModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content border-0 shadow-lg" style="border-radius: var(--sp-radius-md); overflow: hidden;">
|
||||
|
||||
<div class="modal-header text-white" style="background: linear-gradient(135deg, #ef4444 0%, #b91c1c 100%);">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="rounded-circle bg-white text-danger d-flex align-items-center justify-content-center" style="width: 40px; height: 40px; font-size: 1.4rem;">
|
||||
<i class='bx bxs-alarm-exclamation'></i>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="modal-title fw-bold text-white mb-0" id="sosModalLabel">🚨 Tryb Awaryjny: Pilne Zastępstwo SOS</h5>
|
||||
<small class="text-white-50" style="font-size: 0.76rem;">Pilot zrezygnował w ostatniej chwili? Znajdź natychmiastowe zastępstwo w 15 minut</small>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Zamknij"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body p-4">
|
||||
|
||||
<!-- Kryteria nagłego wyjazdu -->
|
||||
<form id="sosRequestForm">
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-5">
|
||||
<label class="form-label fw-bold small text-muted">Kierunek wycieczki</label>
|
||||
<select class="form-select fw-semibold" id="sosCountrySelect">
|
||||
<option value="Włochy" selected>🇮🇹 Włochy (Objazdówka / Rzym)</option>
|
||||
<option value="Grecja">🇬🇷 Grecja (Cyklady / Peloponez)</option>
|
||||
<option value="Hiszpania">🇪🇸 Hiszpania (Andaluzja / Barcelona)</option>
|
||||
<option value="Chorwacja">🇭🇷 Chorwacja (Istria / Dalmacja)</option>
|
||||
<option value="Norwegia">🇳🇴 Norwegia (Fiordy)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label fw-bold small text-muted">Kiedy rusza autokar / lot?</label>
|
||||
<select class="form-select fw-semibold" id="sosTimeUrgency">
|
||||
<option value="24h" selected>⚡ W ciągu 24h (Dziś w nocy / Jutro rano)</option>
|
||||
<option value="48h">⏱️ W ciągu 48h</option>
|
||||
<option value="72h">📅 W ciągu 3 dni</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label fw-bold small text-muted">Miejsce zbiórki</label>
|
||||
<input type="text" class="form-control fw-semibold" value="Warszawa / Katowice">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lista pilotów w trybie gotowości od zaraz (Standby Ready) -->
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<h6 class="fw-bold mb-0 text-navy" style="font-size: 0.9rem;">Piloci dyżurujący z natychmiastową dyspozycyjnością (4 dostępnych):</h6>
|
||||
<span class="badge bg-success-subtle text-success" style="font-size: 0.72rem;">🟢 Gotowi do spakowania w 2h</span>
|
||||
</div>
|
||||
|
||||
<!-- Pilot 1: Oskar Kaczmarek (Spójny) -->
|
||||
<div class="card border mb-2 p-3 shadow-none hover-border-primary" style="background: #ffffff; border-radius: 10px;">
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="rounded-3 d-flex align-items-center justify-content-center text-white fw-bold" style="width: 44px; height: 44px; background: linear-gradient(135deg, #4f46e5, #7c3aed); font-size: 1.1rem;">
|
||||
OK
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<h6 class="fw-bold mb-0 text-navy" style="font-size: 0.9rem;">Oskar Kaczmarek</h6>
|
||||
<span class="badge bg-success" style="font-size: 0.65rem;">Superpilot Verified</span>
|
||||
<span class="text-warning small fw-bold"><i class='bx bxs-star'></i> 4.9 (18 opinii)</span>
|
||||
</div>
|
||||
<small class="text-muted" style="font-size: 0.75rem;">Specjalizacja: Włochy (Ekspert) • Języki: Włoski (B2), Angielski (C1) • Paszport i uprawnienia aktualne</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<a href="tel:+48600123456" class="btn btn-sm btn-outline-success fw-bold d-flex align-items-center gap-1" style="font-size: 0.78rem;">
|
||||
<i class='bx bx-phone-call'></i> Zadzwoń: 600 123 456
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-danger fw-bold" onclick="openBookingModal('1', 'Oskar Kaczmarek', 'Pilne zastępstwo SOS: Jutro rano')">
|
||||
<i class='bx bx-zap'></i> Wyślij SOS
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pilot 2: Katarzyna Zielińska -->
|
||||
<div class="card border mb-2 p-3 shadow-none" style="background: #ffffff; border-radius: 10px;">
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="rounded-3 d-flex align-items-center justify-content-center text-white fw-bold" style="width: 44px; height: 44px; background: linear-gradient(135deg, #0ea5e9, #0284c7); font-size: 1.1rem;">
|
||||
KZ
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<h6 class="fw-bold mb-0 text-navy" style="font-size: 0.9rem;">Katarzyna Zielińska</h6>
|
||||
<span class="badge bg-info-subtle text-info" style="font-size: 0.65rem;">Kwalifikowana Pierwsza Pomoc</span>
|
||||
<span class="text-warning small fw-bold"><i class='bx bxs-star'></i> 4.8 (15 opinii)</span>
|
||||
</div>
|
||||
<small class="text-muted" style="font-size: 0.75rem;">Specjalizacja: Bałkany, Włochy Północne • Języki: Angielski (C1), Włoski (B1) • Dyspozycyjna od zaraz</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<a href="tel:+48601987654" class="btn btn-sm btn-outline-success fw-bold d-flex align-items-center gap-1" style="font-size: 0.78rem;">
|
||||
<i class='bx bx-phone-call'></i> Zadzwoń: 601 987 654
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-danger fw-bold" onclick="openBookingModal('4', 'Katarzyna Zielińska', 'Pilne zastępstwo SOS: Jutro rano')">
|
||||
<i class='bx bx-zap'></i> Wyślij SOS
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Masowy broadcast SOS -->
|
||||
<div class="p-3 rounded-3 mt-3 text-white" style="background: linear-gradient(135deg, #1e1b4b 0%, #312e81 100%);">
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||
<div>
|
||||
<div class="fw-bold" style="font-size: 0.9rem;">
|
||||
<i class='bx bx-broadcast text-warning me-1'></i> Masowy Broadcast SOS do wszystkich wolnych pilotów na kierunek Włochy
|
||||
</div>
|
||||
<small class="text-white-50" style="font-size: 0.74rem;">Wyśle równoległy alert Push & SMS do 12 pilotów spełniających wymagania. Pierwszy, który zaakceptuje, przejmuje zlecenie.</small>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-warning fw-bold px-4 py-2" style="font-size: 0.85rem;">
|
||||
<i class='bx bx-send'></i> Roześlij masowy alert SOS
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer bg-light px-4 py-2 border-top">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Zamknij</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -121,6 +121,24 @@ include '../../header-invoice.php';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">Rodzaj faktury (KSeF)</label>
|
||||
<select class="form-select">
|
||||
<option value="podstawowa" selected>Podstawowa</option>
|
||||
<option value="zaliczkowa">Zaliczkowa</option>
|
||||
<option value="rozliczeniowa">Rozliczeniowa</option>
|
||||
<option value="uproszczona">Uproszczona</option>
|
||||
<option value="korygujaca">Korygująca</option>
|
||||
<option value="korygujaca_zaliczkowa">Korygująca fakturę zaliczkową</option>
|
||||
<option value="korygujaca_rozliczeniowa">Korygująca fakturę rozliczeniową</option>
|
||||
<option value="podstawowa_pef">Podstawowa PEF</option>
|
||||
<option value="specjalizowana_pef">Specjalizowana PEF</option>
|
||||
<option value="korygujaca_pef">Korygująca PEF</option>
|
||||
<option value="pierwotna_rr">Pierwotna RR</option>
|
||||
<option value="korygujaca_rr">Korygująca RR</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Numer faktury</label>
|
||||
|
||||
@@ -211,6 +211,26 @@ include '../../header-invoice.php';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-12">
|
||||
<label class="form-label">Rodzaj faktury</label>
|
||||
<select class="form-select">
|
||||
<option value="podstawowa" selected>Podstawowa</option>
|
||||
<option value="zaliczkowa">Zaliczkowa</option>
|
||||
<option value="rozliczeniowa">Rozliczeniowa</option>
|
||||
<option value="uproszczona">Uproszczona</option>
|
||||
<option value="korygujaca">Korygująca</option>
|
||||
<option value="korygujaca_zaliczkowa">Korygująca fakturę zaliczkową</option>
|
||||
<option value="korygujaca_rozliczeniowa">Korygująca fakturę rozliczeniową</option>
|
||||
<option value="podstawowa_pef">Podstawowa PEF</option>
|
||||
<option value="specjalizowana_pef">Specjalizowana PEF</option>
|
||||
<option value="korygujaca_pef">Korygująca PEF</option>
|
||||
<option value="pierwotna_rr">Pierwotna RR</option>
|
||||
<option value="korygujaca_rr">Korygująca RR</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Data wystawienia</label>
|
||||
@@ -375,8 +395,8 @@ include '../../header-invoice.php';
|
||||
</td>
|
||||
<td>1</td>
|
||||
<td>
|
||||
<span class="fw-medium">FZ/2026/02/001</span>
|
||||
<br><small class="text-muted">Hosting i domeny</small>
|
||||
<span class="fw-medium d-block mb-1">FZ/2026/02/001</span>
|
||||
<small class="text-muted">Podstawowa | Hosting i domeny</small>
|
||||
</td>
|
||||
<td>
|
||||
<span class="text-heading text-truncate fw-medium">OVH Sp. z o.o.</span>
|
||||
@@ -431,8 +451,8 @@ include '../../header-invoice.php';
|
||||
</td>
|
||||
<td>2</td>
|
||||
<td>
|
||||
<span class="fw-medium">FV/TAURON/22/05</span>
|
||||
<br><small class="text-muted">Energia elektryczna</small>
|
||||
<span class="fw-medium d-block mb-1">FV/TAURON/22/05</span>
|
||||
<small class="text-muted">Podstawowa | Energia elektryczna</small>
|
||||
</td>
|
||||
<td>
|
||||
<span class="text-heading text-truncate fw-medium">Tauron Polska</span>
|
||||
@@ -491,8 +511,8 @@ include '../../header-invoice.php';
|
||||
</td>
|
||||
<td>3</td>
|
||||
<td>
|
||||
<span class="fw-medium">102/K/2026</span>
|
||||
<br><small class="text-muted">Materiały biurowe</small>
|
||||
<span class="fw-medium d-block mb-1">102/K/2026</span>
|
||||
<small class="text-muted">Korygująca | Materiały biurowe</small>
|
||||
</td>
|
||||
<td>
|
||||
<span class="text-heading text-truncate fw-medium">Papierniczy S.A.</span>
|
||||
@@ -553,8 +573,8 @@ include '../../header-invoice.php';
|
||||
</td>
|
||||
<td>4</td>
|
||||
<td>
|
||||
<span class="fw-medium">FV/22/09/2026</span>
|
||||
<br><small class="text-muted">Usługi programistyczne</small>
|
||||
<span class="fw-medium d-block mb-1">FV/22/09/2026</span>
|
||||
<small class="text-muted">Podstawowa | Usługi programistyczne</small>
|
||||
</td>
|
||||
<td>
|
||||
<span class="text-heading text-truncate fw-medium">DevHouse Sp. z o.o.</span>
|
||||
@@ -648,6 +668,25 @@ include '../../header-invoice.php';
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Rodzaj faktury</label>
|
||||
<select class="form-select">
|
||||
<option value="">Wszystkie</option>
|
||||
<option value="podstawowa">Podstawowa</option>
|
||||
<option value="zaliczkowa">Zaliczkowa</option>
|
||||
<option value="rozliczeniowa">Rozliczeniowa</option>
|
||||
<option value="uproszczona">Uproszczona</option>
|
||||
<option value="korygujaca">Korygująca</option>
|
||||
<option value="korygujaca_zaliczkowa">Korygująca fakturę zaliczkową</option>
|
||||
<option value="korygujaca_rozliczeniowa">Korygująca fakturę rozliczeniową</option>
|
||||
<option value="podstawowa_pef">Podstawowa PEF</option>
|
||||
<option value="specjalizowana_pef">Specjalizowana PEF</option>
|
||||
<option value="korygujaca_pef">Korygująca PEF</option>
|
||||
<option value="pierwotna_rr">Pierwotna RR</option>
|
||||
<option value="korygujaca_rr">Korygująca RR</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Status płatności</label>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
@@ -847,6 +886,7 @@ include '../../header-invoice.php';
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<h4 class="modal-title fw-bold mb-0">FZ/2026/02/001</h4>
|
||||
<span class="badge bg-label-success fs-6">Opłacona</span>
|
||||
<span class="badge bg-label-secondary fs-6">Podstawowa</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
|
||||
@@ -922,6 +962,10 @@ include '../../header-invoice.php';
|
||||
<h6 class="card-title m-0 mb-3"><i class="bx bx-info-circle me-2"></i>Szczegóły</h6>
|
||||
<table class="table table-borderless table-sm m-0 px-0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-muted ps-0 py-1">Rodzaj faktury:</td>
|
||||
<td class="fw-medium text-end pe-0 py-1">Podstawowa</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted ps-0 py-1">Data zakupu:</td>
|
||||
<td class="fw-medium text-end pe-0 py-1">2026-02-15</td>
|
||||
|
||||
@@ -0,0 +1,342 @@
|
||||
:root {
|
||||
--primary-color: #696cff;
|
||||
--primary-hover: #5f61e6;
|
||||
--bg-light: #f5f5f9;
|
||||
--text-dark: #32475c;
|
||||
--text-muted: #8592a3;
|
||||
--white: #ffffff;
|
||||
--border-color: #e4e6f4;
|
||||
--shadow-sm: 0 0.125rem 0.25rem rgba(161, 172, 184, 0.2);
|
||||
--shadow-md: 0 0.25rem 0.5rem rgba(161, 172, 184, 0.3);
|
||||
--radius-md: 0.5rem;
|
||||
--radius-lg: 0.75rem;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-light);
|
||||
color: var(--text-dark);
|
||||
font-family: 'Public Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
box-shadow: 0 0 20px rgba(0,0,0,0.05);
|
||||
min-height: 100vh;
|
||||
}
|
||||
html {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
.text-primary-custom { color: var(--primary-color) !important; }
|
||||
.bg-primary-custom { background-color: var(--primary-color) !important; color: var(--white); }
|
||||
|
||||
/* Buttons */
|
||||
.btn-primary {
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.6rem 1.2rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.btn-primary:hover, .btn-primary:focus {
|
||||
background-color: var(--primary-hover);
|
||||
border-color: var(--primary-hover);
|
||||
}
|
||||
|
||||
/* Cards & Tiles */
|
||||
.card-custom {
|
||||
background: var(--white);
|
||||
border: none;
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.card-custom:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
/* Form inputs */
|
||||
.form-control {
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.75rem 1rem;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
.form-control:focus {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 0.25rem rgba(105, 108, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Topbar */
|
||||
.app-topbar {
|
||||
background: var(--white);
|
||||
box-shadow: var(--shadow-sm);
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.app-topbar .app-title {
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
margin: 0;
|
||||
color: var(--text-dark);
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(105, 108, 255, 0.1);
|
||||
color: var(--primary-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
/* RCP Hero Button */
|
||||
.btn-rcp {
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.25rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
box-shadow: 0 0.5rem 1rem rgba(105, 108, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Grid Tiles */
|
||||
.tile {
|
||||
text-decoration: none;
|
||||
color: var(--text-dark);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1.5rem 1rem;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
}
|
||||
.tile .tile-icon {
|
||||
font-size: 2.5rem;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.75rem;
|
||||
background: rgba(105, 108, 255, 0.1);
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.tile .tile-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Select Company List */
|
||||
.company-list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
text-decoration: none;
|
||||
color: var(--text-dark);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
.company-list-item:hover {
|
||||
background-color: var(--bg-light);
|
||||
}
|
||||
.company-list-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.company-logo-placeholder {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: var(--radius-md);
|
||||
background-color: var(--primary-color);
|
||||
color: var(--white);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
/* Location Loader & Animations */
|
||||
.location-loader {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.location-loader .pulse {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(105, 108, 255, 0.4);
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
animation: pulse-animation 1.5s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
|
||||
@keyframes pulse-animation {
|
||||
0% { transform: scale(0.5); opacity: 1; }
|
||||
100% { transform: scale(1.5); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes bounce-pin {
|
||||
0%, 100% { transform: translateY(0) rotate(0deg); }
|
||||
25% { transform: translateY(-15px) rotate(-10deg); }
|
||||
50% { transform: translateY(0) rotate(0deg); }
|
||||
75% { transform: translateY(-5px) rotate(10deg); }
|
||||
}
|
||||
|
||||
.bouncing-pin {
|
||||
animation: bounce-pin 1.2s infinite ease-in-out;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Text Fade Transition */
|
||||
.fade-transition {
|
||||
transition: opacity 0.4s ease-in-out;
|
||||
}
|
||||
.fade-out {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
/* Calendar Grid */
|
||||
.calendar-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 5px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.calendar-header-day {
|
||||
text-align: center;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.calendar-day {
|
||||
aspect-ratio: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--radius-md);
|
||||
background-color: var(--white);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-dark);
|
||||
position: relative;
|
||||
box-shadow: 0 2px 4px rgba(161, 172, 184, 0.1);
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, background-color 0.2s;
|
||||
}
|
||||
.calendar-day.empty {
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
cursor: default;
|
||||
}
|
||||
.calendar-day:active:not(.empty) {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
.calendar-day.active {
|
||||
background-color: var(--primary-color);
|
||||
color: var(--white);
|
||||
}
|
||||
.calendar-day .shift-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
margin-top: 3px;
|
||||
}
|
||||
.calendar-day .shift-dot.blue { background-color: var(--primary-color); }
|
||||
.calendar-day .shift-dot.green { background-color: #71dd37; }
|
||||
.calendar-day .shift-dot.purple { background-color: #e83e8c; }
|
||||
.calendar-day.active .shift-dot { background-color: var(--white) !important; }
|
||||
|
||||
/* Custom Badges (Sneat style) */
|
||||
.bg-label-primary {
|
||||
background-color: rgba(105, 108, 255, 0.16) !important;
|
||||
color: var(--primary-color) !important;
|
||||
}
|
||||
.bg-label-success {
|
||||
background-color: rgba(113, 221, 55, 0.16) !important;
|
||||
color: #71dd37 !important;
|
||||
}
|
||||
.bg-label-warning {
|
||||
background-color: rgba(255, 171, 0, 0.16) !important;
|
||||
color: #ffab00 !important;
|
||||
}
|
||||
.bg-label-danger {
|
||||
background-color: rgba(255, 62, 29, 0.16) !important;
|
||||
color: #ff3e1d !important;
|
||||
}
|
||||
.bg-label-secondary {
|
||||
background-color: rgba(133, 146, 163, 0.16) !important;
|
||||
color: #8592a3 !important;
|
||||
}
|
||||
|
||||
/* Custom Nav Pills */
|
||||
.nav-pills .nav-link {
|
||||
background-color: var(--white);
|
||||
color: var(--text-muted);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||
background-color: var(--primary-color) !important;
|
||||
color: var(--white) !important;
|
||||
box-shadow: 0 0.125rem 0.25rem rgba(105, 108, 255, 0.4) !important;
|
||||
}
|
||||
|
||||
/* Floating Action Button (FAB) */
|
||||
.fab-wrapper {
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 100%;
|
||||
max-width: 500px; /* same as body max-width */
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-right: 20px;
|
||||
z-index: 1050;
|
||||
pointer-events: none; /* Allows clicking through the wrapper */
|
||||
}
|
||||
|
||||
.fab-btn {
|
||||
pointer-events: auto;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2rem;
|
||||
box-shadow: 0 0.5rem 1rem rgba(105, 108, 255, 0.4);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.fab-btn:active {
|
||||
transform: scale(0.92);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Magico Feedback JS -->
|
||||
<script>
|
||||
const MAGICO_BASE_URL = <?= json_encode($basePath); ?>;
|
||||
</script>
|
||||
<?php if (!empty($enablePrototypeComments) && $enablePrototypeComments): ?>
|
||||
<script src="<?= $basePath ?>/assets/js/comments.js"></script>
|
||||
<?php endif; ?>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
$basePath = '';
|
||||
if ($_SERVER['HTTP_HOST'] === 'localhost' || $_SERVER['HTTP_HOST'] === '127.0.0.1') {
|
||||
$basePath = '/magico-prototype';
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>Pracownik Magico</title>
|
||||
|
||||
<!-- 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:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Boxicons -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/boxicons@2.1.4/css/boxicons.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom CSS -->
|
||||
<link rel="stylesheet" href="assets/css/style.css">
|
||||
|
||||
<!-- Magico Feedback CSS -->
|
||||
<?php if (!empty($enablePrototypeComments) && $enablePrototypeComments): ?>
|
||||
<link rel="stylesheet" href="<?= $basePath ?>/assets/css/comments.css">
|
||||
<?php endif; ?>
|
||||
</head>
|
||||
<body>
|
||||
@@ -0,0 +1,357 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="app-topbar">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="d-flex justify-content-center align-items-center bg-primary-custom rounded-circle me-2" style="width: 32px; height: 32px;">
|
||||
<i class='bx bx-cube-alt text-white'></i>
|
||||
</div>
|
||||
<h1 class="app-title">Acme Corp</h1>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<!-- Powiadomienia -->
|
||||
<div class="dropdown">
|
||||
<div class="position-relative" style="cursor: pointer;" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class='bx bx-bell fs-4 text-muted'></i>
|
||||
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger border border-white" style="font-size: 0.65rem; padding: 0.3em 0.45em;">
|
||||
1
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-end p-0 border-0 mt-2" style="border-radius: 16px; width: 340px; box-shadow: 0 0.5rem 1.5rem rgba(105, 108, 255, 0.15); overflow: hidden; z-index: 1050; margin-right: -40px;">
|
||||
<div class="p-3 border-bottom d-flex justify-content-between align-items-center" style="background-color: #fcfcfd;">
|
||||
<h6 class="mb-0 fw-bold" style="color: #566A7F;">Powiadomienia</h6>
|
||||
<span class="badge bg-label-danger rounded-pill">1 nowe</span>
|
||||
</div>
|
||||
|
||||
<div class="list-group list-group-flush" style="max-height: 350px; overflow-y: auto;">
|
||||
|
||||
<!-- Nieprzeczytane -->
|
||||
<a href="vacations.php" class="list-group-item list-group-item-action d-flex align-items-start p-3 border-0" style="background-color: rgba(105, 108, 255, 0.03); border-bottom: 1px solid rgba(0,0,0,0.03) !important;">
|
||||
<div class="d-flex justify-content-center align-items-center rounded-circle flex-shrink-0 me-3" style="width: 40px; height: 40px; background-color: rgba(255, 62, 29, 0.1); color: #ff3e1d;">
|
||||
<i class='bx bx-x-circle fs-5'></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-1 fw-bold" style="font-size: 0.9rem; color: #566A7F;">Wniosek odrzucony</h6>
|
||||
<p class="mb-1 small text-muted" style="line-height: 1.4;">Kierownik odrzucił Twój wniosek o urlop na żądanie (05 Wrz).</p>
|
||||
<small class="text-primary-custom fw-semibold" style="font-size: 0.75rem;">10 min temu</small>
|
||||
</div>
|
||||
<div class="flex-shrink-0 ms-2 mt-2">
|
||||
<div style="width: 8px; height: 8px; background-color: #696cff; border-radius: 50%;"></div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<!-- Przeczytane -->
|
||||
<a href="vacations.php" class="list-group-item list-group-item-action d-flex align-items-start p-3 border-0">
|
||||
<div class="d-flex justify-content-center align-items-center rounded-circle flex-shrink-0 me-3" style="width: 40px; height: 40px; background-color: rgba(113, 221, 55, 0.1); color: #71dd37;">
|
||||
<i class='bx bx-check-circle fs-5'></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-1 fw-semibold" style="font-size: 0.9rem; color: #566A7F;">Wniosek zaakceptowany</h6>
|
||||
<p class="mb-1 small text-muted" style="line-height: 1.4;">Twój urlop wypoczynkowy (12-16 Sie) został pomyślnie zatwierdzony.</p>
|
||||
<small class="text-muted" style="font-size: 0.75rem;">Wczoraj</small>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="p-3 border-top text-center" style="background-color: #fcfcfd;">
|
||||
<button class="btn btn-primary btn-sm w-100 fw-bold" style="border-radius: 8px;">Zobacz wszystkie</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="user-avatar shadow-sm">
|
||||
<i class='bx bx-user'></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container py-4">
|
||||
<!-- Hero / RCP Button -->
|
||||
<div class="mb-4" id="rcp-container">
|
||||
<h5 class="fw-bold mb-3">Dzień dobry, Marcin 👋</h5>
|
||||
|
||||
<!-- Stan 1: Rozpocznij -->
|
||||
<div id="rcp-start-state">
|
||||
<button class="btn btn-primary w-100 btn-rcp" data-bs-toggle="modal" data-bs-target="#rcpModal">
|
||||
<i class='bx bx-fingerprint fs-3'></i>
|
||||
Rozpocznij pracę
|
||||
</button>
|
||||
<div class="text-center mt-2">
|
||||
<small class="text-muted">Zarejestruj wejście do firmy</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stan 2: W trakcie pracy -->
|
||||
<div id="rcp-active-state" class="d-none">
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-outline-warning w-50 btn-rcp" style="padding: 1rem; font-size: 1rem;"
|
||||
data-bs-toggle="modal" data-bs-target="#breakModal">
|
||||
<i class='bx bx-coffee fs-4'></i>
|
||||
<span>Przerwa</span>
|
||||
</button>
|
||||
<button class="btn btn-danger w-50 btn-rcp" style="padding: 1rem; font-size: 1rem;"
|
||||
data-bs-toggle="modal" data-bs-target="#endWorkModal">
|
||||
<i class='bx bx-log-out fs-4'></i>
|
||||
Zakończ
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-center mt-2">
|
||||
<small class="text-success fw-bold"><i class='bx bx-check-circle'></i> Pracujesz od 08:00</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Grid -->
|
||||
<div class="row g-3">
|
||||
<div class="col-6">
|
||||
<a href="schedule.php" class="card-custom tile">
|
||||
<div class="tile-icon">
|
||||
<i class='bx bx-calendar-event'></i>
|
||||
</div>
|
||||
<h2 class="tile-title">Grafik Pracy</h2>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<a href="vacations.php" class="card-custom tile">
|
||||
<div class="tile-icon text-success" style="background: rgba(113, 221, 55, 0.1);">
|
||||
<i class='bx bx-sun'></i>
|
||||
</div>
|
||||
<h2 class="tile-title mt-2">Urlopy</h2>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal RCP -->
|
||||
<div class="modal fade" id="rcpModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content border-0 shadow" style="border-radius: var(--radius-lg);">
|
||||
<div class="modal-body p-4 text-center">
|
||||
|
||||
<!-- Krok 1: Potwierdzenie -->
|
||||
<div id="rcp-step-confirm">
|
||||
<div class="mb-3">
|
||||
<i class='bx bx-map-pin text-primary' style="font-size: 4rem;"></i>
|
||||
</div>
|
||||
<h4 class="fw-bold mb-2">Rozpocząć pracę?</h4>
|
||||
<p class="text-muted mb-4">System zweryfikuje Twoją lokalizację GPS z dozwoloną strefą dla tej
|
||||
firmy.</p>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-outline-secondary w-50"
|
||||
data-bs-dismiss="modal">Anuluj</button>
|
||||
<button type="button" class="btn btn-primary w-50" id="btn-confirm-start">Tak,
|
||||
rozpocznij</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Krok 2: Ładowanie (Lokalizacja) -->
|
||||
<div id="rcp-step-loading" class="d-none py-3">
|
||||
<div class="location-loader mb-4">
|
||||
<div class="pulse"></div>
|
||||
<i class='bx bxs-map-pin text-primary bouncing-pin'
|
||||
style="font-size: 3.5rem; position: relative; z-index: 2;"></i>
|
||||
</div>
|
||||
<h5 class="fw-bold mb-1">Weryfikacja lokalizacji...</h5>
|
||||
<p class="text-muted small fade-transition" id="loading-text-dynamic" style="min-height: 20px;">
|
||||
Rozglądamy się za Tobą 🛰️</p>
|
||||
</div>
|
||||
|
||||
<!-- Krok 3: Sukces -->
|
||||
<div id="rcp-step-success" class="d-none py-3">
|
||||
<div class="mb-3">
|
||||
<i class='bx bx-check-circle text-success' style="font-size: 4rem;"></i>
|
||||
</div>
|
||||
<h4 class="fw-bold mb-1">Zalogowano pomyślnie!</h4>
|
||||
<p class="text-muted">Twoja praca została rozpoczęta.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Przerwy -->
|
||||
<div class="modal fade" id="breakModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content border-0 shadow" style="border-radius: var(--radius-lg);">
|
||||
<div class="modal-body p-4 text-center">
|
||||
<div class="mb-3">
|
||||
<i class='bx bx-coffee text-warning' style="font-size: 4rem;" id="break-modal-icon"></i>
|
||||
</div>
|
||||
<h4 class="fw-bold mb-2" id="break-modal-title">Rozpocząć przerwę?</h4>
|
||||
<p class="text-muted mb-4" id="break-modal-desc">Twój czas pracy zostanie wstrzymany na czas trwania
|
||||
przerwy.</p>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-outline-secondary w-50" data-bs-dismiss="modal">Anuluj</button>
|
||||
<button type="button" class="btn btn-warning w-50 text-white" id="btn-confirm-break"
|
||||
data-bs-dismiss="modal">Tak, rozpocznij</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Zakończenia Pracy -->
|
||||
<div class="modal fade" id="endWorkModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content border-0 shadow" style="border-radius: var(--radius-lg);">
|
||||
<div class="modal-body p-4 text-center">
|
||||
<div class="mb-3">
|
||||
<i class='bx bx-log-out-circle text-danger' style="font-size: 4rem;"></i>
|
||||
</div>
|
||||
<h4 class="fw-bold mb-2">Zakończyć pracę?</h4>
|
||||
<p class="text-muted mb-4">Twój dzisiejszy czas pracy zostanie podsumowany i zapisany.</p>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-outline-secondary w-50" data-bs-dismiss="modal">Anuluj</button>
|
||||
<button type="button" class="btn btn-danger w-50" id="btn-confirm-end" data-bs-dismiss="modal">Tak,
|
||||
zakończ</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const btnConfirm = document.getElementById('btn-confirm-start');
|
||||
const stepConfirm = document.getElementById('rcp-step-confirm');
|
||||
const stepLoading = document.getElementById('rcp-step-loading');
|
||||
const stepSuccess = document.getElementById('rcp-step-success');
|
||||
|
||||
const stateStart = document.getElementById('rcp-start-state');
|
||||
const stateActive = document.getElementById('rcp-active-state');
|
||||
|
||||
const rcpModalEl = document.getElementById('rcpModal');
|
||||
let rcpModal;
|
||||
|
||||
if (typeof bootstrap !== 'undefined') {
|
||||
rcpModal = new bootstrap.Modal(rcpModalEl);
|
||||
}
|
||||
|
||||
// Po zamknięciu modala, resetuj widoki, jeśli był w trakcie itp
|
||||
rcpModalEl.addEventListener('hidden.bs.modal', event => {
|
||||
// Resetujemy tylko jeśli nie jesteśmy w trakcie pokazywania sukcesu
|
||||
if (stepSuccess.classList.contains('d-none')) {
|
||||
stepConfirm.classList.remove('d-none');
|
||||
stepLoading.classList.add('d-none');
|
||||
stepSuccess.classList.add('d-none');
|
||||
}
|
||||
});
|
||||
|
||||
btnConfirm.addEventListener('click', function () {
|
||||
// Pokaż ładowanie
|
||||
stepConfirm.classList.add('d-none');
|
||||
stepLoading.classList.remove('d-none');
|
||||
|
||||
const textElement = document.getElementById('loading-text-dynamic');
|
||||
const messages = [
|
||||
"Rozglądamy się za Tobą 🛰️",
|
||||
"Sprawdzamy współrzędne GPS 🛰️",
|
||||
"Kalibrujemy pozycję 📍",
|
||||
"Łączymy się z satelitą 📡",
|
||||
"Weryfikujemy strefę pracy 🗺️",
|
||||
"Analizujemy zasięg sygnału 📶",
|
||||
"Pobieramy dane lokalizacyjne ⏳",
|
||||
"Jeszcze chwilę... prawie gotowe! 🚀"
|
||||
];
|
||||
let msgIndex = 0;
|
||||
textElement.textContent = messages[0];
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
// Rozpocznij zanikanie
|
||||
textElement.classList.add('fade-out');
|
||||
|
||||
setTimeout(() => {
|
||||
// Po zaniknięciu (400ms), zmień tekst i pokaż ponownie
|
||||
msgIndex = (msgIndex + 1) % messages.length;
|
||||
textElement.textContent = messages[msgIndex];
|
||||
textElement.classList.remove('fade-out');
|
||||
}, 400);
|
||||
}, 2500);
|
||||
|
||||
// Symulacja weryfikacji GPS (Wydłużona do 8.5 sekund)
|
||||
setTimeout(() => {
|
||||
clearInterval(intervalId);
|
||||
stepLoading.classList.add('d-none');
|
||||
stepSuccess.classList.remove('d-none');
|
||||
|
||||
// Zamknij modal po 1.5 sekundy od sukcesu i zmień stan
|
||||
setTimeout(() => {
|
||||
if (rcpModal) rcpModal.hide();
|
||||
stateStart.classList.add('d-none');
|
||||
stateActive.classList.remove('d-none');
|
||||
|
||||
// Przywróć stan początkowy modala do ponownego użycia w przyszłości
|
||||
setTimeout(() => {
|
||||
stepConfirm.classList.remove('d-none');
|
||||
stepLoading.classList.add('d-none');
|
||||
stepSuccess.classList.add('d-none');
|
||||
}, 500);
|
||||
}, 1500);
|
||||
|
||||
}, 8500);
|
||||
});
|
||||
|
||||
// Obsługa przerwy
|
||||
const btnConfirmBreak = document.getElementById('btn-confirm-break');
|
||||
const breakBtnElement = document.querySelector('button[data-bs-target="#breakModal"]');
|
||||
let isBreakActive = false;
|
||||
|
||||
btnConfirmBreak.addEventListener('click', function () {
|
||||
if (!isBreakActive) {
|
||||
// Rozpocznij przerwę
|
||||
isBreakActive = true;
|
||||
breakBtnElement.innerHTML = "<i class='bx bx-play-circle fs-4'></i><span>Wznów</span>";
|
||||
breakBtnElement.classList.replace('btn-outline-warning', 'btn-warning');
|
||||
breakBtnElement.classList.add('text-white');
|
||||
|
||||
// Zmiana tekstów modala na "Powrót z przerwy"
|
||||
document.getElementById('break-modal-icon').className = "bx bx-play-circle text-warning";
|
||||
document.getElementById('break-modal-title').textContent = "Zakończyć przerwę?";
|
||||
document.getElementById('break-modal-desc').textContent = "Twój czas pracy zostanie wznowiony.";
|
||||
btnConfirmBreak.textContent = "Tak, wznów";
|
||||
} else {
|
||||
// Zakończ przerwę
|
||||
isBreakActive = false;
|
||||
breakBtnElement.innerHTML = "<i class='bx bx-coffee fs-4'></i><span>Przerwa</span>";
|
||||
breakBtnElement.classList.replace('btn-warning', 'btn-outline-warning');
|
||||
breakBtnElement.classList.remove('text-white');
|
||||
|
||||
// Zmiana tekstów modala z powrotem na "Rozpocznij przerwę"
|
||||
document.getElementById('break-modal-icon').className = "bx bx-coffee text-warning";
|
||||
document.getElementById('break-modal-title').textContent = "Rozpocząć przerwę?";
|
||||
document.getElementById('break-modal-desc').textContent = "Twój czas pracy zostanie wstrzymany na czas trwania przerwy.";
|
||||
btnConfirmBreak.textContent = "Tak, rozpocznij";
|
||||
}
|
||||
});
|
||||
|
||||
// Obsługa zakończenia pracy
|
||||
const btnConfirmEnd = document.getElementById('btn-confirm-end');
|
||||
btnConfirmEnd.addEventListener('click', function () {
|
||||
// Wróć do ekranu startowego
|
||||
stateActive.classList.add('d-none');
|
||||
stateStart.classList.remove('d-none');
|
||||
|
||||
// Resetuj stan przerwy jeśli była aktywna
|
||||
if (isBreakActive) {
|
||||
isBreakActive = false;
|
||||
breakBtnElement.innerHTML = "<i class='bx bx-coffee fs-4'></i><span>Przerwa</span>";
|
||||
breakBtnElement.classList.replace('btn-warning', 'btn-outline-warning');
|
||||
breakBtnElement.classList.remove('text-white');
|
||||
|
||||
document.getElementById('break-modal-icon').className = "bx bx-coffee text-warning";
|
||||
document.getElementById('break-modal-title').textContent = "Rozpocząć przerwę?";
|
||||
document.getElementById('break-modal-desc').textContent = "Twój czas pracy zostanie wstrzymany na czas trwania przerwy.";
|
||||
btnConfirmBreak.textContent = "Tak, rozpocznij";
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="container d-flex flex-column justify-content-center align-items-center min-vh-100 py-5">
|
||||
<div class="text-center mb-5">
|
||||
<div class="d-inline-flex align-items-center justify-content-center bg-primary-custom rounded-circle mb-3"
|
||||
style="width: 80px; height: 80px;">
|
||||
<i class='bx bx-cube-alt text-white' style="font-size: 3rem;"></i>
|
||||
</div>
|
||||
<h2 class="fw-bold mb-1">Pracownik Magico</h2>
|
||||
<p class="text-muted">Zaloguj się do swojego konta</p>
|
||||
</div>
|
||||
|
||||
<div class="card-custom w-100 p-4" style="max-width: 400px;">
|
||||
<form action="select-company.php" method="GET">
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label text-muted small fw-semibold text-uppercase">Adres e-mail</label>
|
||||
<input type="email" class="form-control" id="email" placeholder="jan.kowalski@firma.pl"
|
||||
value="jan@magico.pl">
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="d-flex justify-content-between">
|
||||
<label for="password" class="form-label text-muted small fw-semibold text-uppercase">Hasło</label>
|
||||
<a href="#" class="text-primary-custom small text-decoration-none">Zapomniałeś hasła?</a>
|
||||
</div>
|
||||
<div class="input-group input-group-merge">
|
||||
<input type="password" id="password" class="form-control"
|
||||
placeholder="············"
|
||||
value="fdsaf45tfsgtyr">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="remember-me" checked>
|
||||
<label class="form-check-label" for="remember-me"> Zapamiętaj mnie </label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary w-100" type="submit">Zaloguj się</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<!-- Główny Topbar -->
|
||||
<div class="app-topbar">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="d-flex justify-content-center align-items-center bg-primary-custom rounded-circle me-2" style="width: 32px; height: 32px;">
|
||||
<i class='bx bx-cube-alt text-white'></i>
|
||||
</div>
|
||||
<h1 class="app-title">Acme Corp</h1>
|
||||
</div>
|
||||
<div class="user-avatar shadow-sm">
|
||||
<i class='bx bx-user'></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pasek tytułowy sekcji -->
|
||||
<div class="container pt-4">
|
||||
<div class="d-flex justify-content-between align-items-center pb-3 mb-4" style="border-bottom: 1px solid var(--border-color);">
|
||||
<div class="d-flex align-items-center">
|
||||
<a href="home.php" class="text-dark me-2 d-flex align-items-center justify-content-center" style="width: 32px; height: 32px; background: rgba(0,0,0,0.05); border-radius: 50%;">
|
||||
<i class='bx bx-arrow-back fs-4'></i>
|
||||
</a>
|
||||
<h2 class="fw-bold mb-0" style="font-size: 1.25rem;">Mój Grafik</h2>
|
||||
</div>
|
||||
<!-- Zmieniona ikona na widok listy -->
|
||||
<a href="schedule.php" class="text-primary-custom d-flex align-items-center justify-content-center" style="width: 36px; height: 36px; background: rgba(105, 108, 255, 0.1); border-radius: 8px;">
|
||||
<i class='bx bx-list-ul fs-4'></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container pb-4">
|
||||
<!-- Przełącznik miesiąca -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<button class="btn btn-sm btn-link text-dark text-decoration-none"><i class='bx bx-chevron-left fs-4'></i></button>
|
||||
<span class="fw-bold">Sierpień 2026</span>
|
||||
<button class="btn btn-sm btn-link text-dark text-decoration-none"><i class='bx bx-chevron-right fs-4'></i></button>
|
||||
</div>
|
||||
|
||||
<!-- Siatka Kalendarza -->
|
||||
<div class="calendar-grid">
|
||||
<div class="calendar-header-day">Pn</div>
|
||||
<div class="calendar-header-day">Wt</div>
|
||||
<div class="calendar-header-day">Śr</div>
|
||||
<div class="calendar-header-day">Cz</div>
|
||||
<div class="calendar-header-day">Pt</div>
|
||||
<div class="calendar-header-day">So</div>
|
||||
<div class="calendar-header-day">Nd</div>
|
||||
|
||||
<!-- Tydzień 1 (zaczynamy od pustych dni) -->
|
||||
<div class="calendar-day empty"></div>
|
||||
<div class="calendar-day empty"></div>
|
||||
<div class="calendar-day empty"></div>
|
||||
<div class="calendar-day empty"></div>
|
||||
<div class="calendar-day empty"></div>
|
||||
<div class="calendar-day text-muted" style="opacity: 0.5;">1</div>
|
||||
<div class="calendar-day text-muted" style="opacity: 0.5;">2</div>
|
||||
|
||||
<!-- Tydzień 2 -->
|
||||
<div class="calendar-day">3<div class="shift-dot blue"></div></div>
|
||||
<div class="calendar-day">4<div class="shift-dot green"></div></div>
|
||||
<div class="calendar-day">5</div>
|
||||
<div class="calendar-day">6<div class="shift-dot blue"></div></div>
|
||||
<div class="calendar-day">7<div class="shift-dot blue"></div></div>
|
||||
<div class="calendar-day text-muted" style="opacity: 0.5;">8</div>
|
||||
<div class="calendar-day text-muted" style="opacity: 0.5;">9</div>
|
||||
|
||||
<!-- Tydzień 3 (Aktywny tydzień) -->
|
||||
<div class="calendar-day">10</div>
|
||||
<div class="calendar-day">11<div class="shift-dot green"></div></div>
|
||||
<div class="calendar-day active shadow-md">12<div class="shift-dot blue"></div></div>
|
||||
<div class="calendar-day">13<div class="shift-dot green"></div></div>
|
||||
<div class="calendar-day">14</div>
|
||||
<div class="calendar-day text-muted" style="opacity: 0.5;">15</div>
|
||||
<div class="calendar-day text-muted" style="opacity: 0.5;">16</div>
|
||||
|
||||
<!-- Tydzień 4 -->
|
||||
<div class="calendar-day">17<div class="shift-dot blue"></div></div>
|
||||
<div class="calendar-day">18<div class="shift-dot blue"></div></div>
|
||||
<div class="calendar-day">19<div class="shift-dot green"></div></div>
|
||||
<div class="calendar-day">20</div>
|
||||
<div class="calendar-day">21<div class="shift-dot blue"></div></div>
|
||||
<div class="calendar-day text-muted" style="opacity: 0.5;">22</div>
|
||||
<div class="calendar-day text-muted" style="opacity: 0.5;">23</div>
|
||||
|
||||
<!-- Tydzień 5 -->
|
||||
<div class="calendar-day">24</div>
|
||||
<div class="calendar-day">25<div class="shift-dot blue"></div></div>
|
||||
<div class="calendar-day">26<div class="shift-dot green"></div></div>
|
||||
<div class="calendar-day">27<div class="shift-dot green"></div></div>
|
||||
<div class="calendar-day">28</div>
|
||||
<div class="calendar-day text-muted" style="opacity: 0.5;">29</div>
|
||||
<div class="calendar-day text-muted" style="opacity: 0.5;">30</div>
|
||||
|
||||
<!-- Ostatni dzień -->
|
||||
<div class="calendar-day">31<div class="shift-dot blue"></div></div>
|
||||
<div class="calendar-day empty"></div>
|
||||
<div class="calendar-day empty"></div>
|
||||
<div class="calendar-day empty"></div>
|
||||
<div class="calendar-day empty"></div>
|
||||
<div class="calendar-day empty"></div>
|
||||
<div class="calendar-day empty"></div>
|
||||
</div>
|
||||
|
||||
<!-- Szczegóły dla wybranego dnia -->
|
||||
<div class="mt-4">
|
||||
<h6 class="fw-bold mb-3 text-muted text-uppercase small">Wybrany dzień: 12 Sierpnia</h6>
|
||||
|
||||
<div class="card-custom shift-card" style="border-left: 4px solid var(--primary-color);">
|
||||
<div class="p-2 px-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div>
|
||||
<h5 class="fw-bold mb-0" style="font-size: 1.1rem;">08:00 - 16:00</h5>
|
||||
<span class="text-primary-custom fw-semibold" style="font-size: 0.75rem;">Specjalista ds. Obsługi</span>
|
||||
</div>
|
||||
<span class="badge bg-label-primary">8h</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
@@ -0,0 +1,299 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<!-- Główny Topbar (taki sam jak w home.php) -->
|
||||
<div class="app-topbar">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="d-flex justify-content-center align-items-center bg-primary-custom rounded-circle me-2" style="width: 32px; height: 32px;">
|
||||
<i class='bx bx-cube-alt text-white'></i>
|
||||
</div>
|
||||
<h1 class="app-title">Acme Corp</h1>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<!-- Powiadomienia -->
|
||||
<div class="dropdown">
|
||||
<div class="position-relative" style="cursor: pointer;" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class='bx bx-bell fs-4 text-muted'></i>
|
||||
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger border border-white" style="font-size: 0.65rem; padding: 0.3em 0.45em;">
|
||||
1
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-end p-0 border-0 mt-2" style="border-radius: 16px; width: 340px; box-shadow: 0 0.5rem 1.5rem rgba(105, 108, 255, 0.15); overflow: hidden; z-index: 1050; margin-right: -40px;">
|
||||
<div class="p-3 border-bottom d-flex justify-content-between align-items-center" style="background-color: #fcfcfd;">
|
||||
<h6 class="mb-0 fw-bold" style="color: #566A7F;">Powiadomienia</h6>
|
||||
<span class="badge bg-label-danger rounded-pill">1 nowe</span>
|
||||
</div>
|
||||
|
||||
<div class="list-group list-group-flush" style="max-height: 350px; overflow-y: auto;">
|
||||
|
||||
<!-- Nieprzeczytane -->
|
||||
<a href="vacations.php" class="list-group-item list-group-item-action d-flex align-items-start p-3 border-0" style="background-color: rgba(105, 108, 255, 0.03); border-bottom: 1px solid rgba(0,0,0,0.03) !important;">
|
||||
<div class="d-flex justify-content-center align-items-center rounded-circle flex-shrink-0 me-3" style="width: 40px; height: 40px; background-color: rgba(255, 62, 29, 0.1); color: #ff3e1d;">
|
||||
<i class='bx bx-x-circle fs-5'></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-1 fw-bold" style="font-size: 0.9rem; color: #566A7F;">Wniosek odrzucony</h6>
|
||||
<p class="mb-1 small text-muted" style="line-height: 1.4;">Kierownik odrzucił Twój wniosek o urlop na żądanie (05 Wrz).</p>
|
||||
<small class="text-primary-custom fw-semibold" style="font-size: 0.75rem;">10 min temu</small>
|
||||
</div>
|
||||
<div class="flex-shrink-0 ms-2 mt-2">
|
||||
<div style="width: 8px; height: 8px; background-color: #696cff; border-radius: 50%;"></div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<!-- Przeczytane -->
|
||||
<a href="vacations.php" class="list-group-item list-group-item-action d-flex align-items-start p-3 border-0">
|
||||
<div class="d-flex justify-content-center align-items-center rounded-circle flex-shrink-0 me-3" style="width: 40px; height: 40px; background-color: rgba(113, 221, 55, 0.1); color: #71dd37;">
|
||||
<i class='bx bx-check-circle fs-5'></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-1 fw-semibold" style="font-size: 0.9rem; color: #566A7F;">Wniosek zaakceptowany</h6>
|
||||
<p class="mb-1 small text-muted" style="line-height: 1.4;">Twój urlop wypoczynkowy (12-16 Sie) został pomyślnie zatwierdzony.</p>
|
||||
<small class="text-muted" style="font-size: 0.75rem;">Wczoraj</small>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="p-3 border-top text-center" style="background-color: #fcfcfd;">
|
||||
<button class="btn btn-primary btn-sm w-100 fw-bold" style="border-radius: 8px;">Zobacz wszystkie</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="user-avatar shadow-sm">
|
||||
<i class='bx bx-user'></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pasek tytułowy sekcji -->
|
||||
<div class="container pt-4">
|
||||
<div class="d-flex justify-content-between align-items-center pb-3" style="border-bottom: 1px solid var(--border-color);">
|
||||
<div class="d-flex align-items-center">
|
||||
<a href="home.php" class="text-dark me-2 d-flex align-items-center justify-content-center" style="width: 32px; height: 32px; background: rgba(0,0,0,0.05); border-radius: 50%;">
|
||||
<i class='bx bx-arrow-back fs-4'></i>
|
||||
</a>
|
||||
<h2 class="fw-bold mb-0" style="font-size: 1.25rem;">Mój Grafik</h2>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<!-- Ikona filtrowania (Toggle zaawansowanych) -->
|
||||
<button class="btn btn-sm text-primary-custom d-flex align-items-center justify-content-center p-0" style="width: 36px; height: 36px; background: rgba(105, 108, 255, 0.1); border-radius: 8px; border: none;" data-bs-toggle="collapse" data-bs-target="#advancedFilters" aria-expanded="false">
|
||||
<i class='bx bx-filter fs-4'></i>
|
||||
</button>
|
||||
<a href="schedule-calendar.php" class="text-primary-custom d-flex align-items-center justify-content-center p-0 text-decoration-none" style="width: 36px; height: 36px; background: rgba(105, 108, 255, 0.1); border-radius: 8px;">
|
||||
<i class='bx bx-calendar fs-4'></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Szybki wybór okresu pod spodem -->
|
||||
<div class="mb-4 pt-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0 text-muted small text-uppercase fw-bold">Wybierz okres</h6>
|
||||
<select class="form-select form-select-sm border-0 bg-white shadow-sm fw-semibold text-primary-custom" style="border-radius: 8px; cursor: pointer; width: auto; padding: 0.4rem 2rem 0.4rem 0.8rem; box-shadow: 0 0.125rem 0.25rem rgba(161, 172, 184, 0.15) !important;">
|
||||
<optgroup label="Tygodnie">
|
||||
<option value="week_current">Bieżący tydzień</option>
|
||||
<option value="week_next">Następny tydzień</option>
|
||||
<option value="week_prev">Poprzedni tydzień</option>
|
||||
</optgroup>
|
||||
<optgroup label="Miesiące">
|
||||
<option value="month_current" selected>Bieżący miesiąc</option>
|
||||
<option value="month_next">Następny miesiąc</option>
|
||||
<option value="month_prev">Poprzedni miesiąc</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Zaawansowane filtry (ukryte domyślnie, otwierane przez ikonkę filtra) -->
|
||||
<div class="collapse mb-4" id="advancedFilters">
|
||||
<div class="card border-0 shadow-sm" style="border-radius: 16px; border: 1px solid rgba(105, 108, 255, 0.2) !important;">
|
||||
<div class="card-body p-3 pb-1">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="mb-0 fw-bold d-flex align-items-center" style="color: #696cff; font-size: 0.95rem;">
|
||||
<i class='bx bx-slider-alt me-1 fs-5'></i> Własny zakres dat
|
||||
</h6>
|
||||
<button type="button" class="btn-close" style="font-size: 0.7rem; box-shadow: none;" data-bs-toggle="collapse" data-bs-target="#advancedFilters" aria-label="Zamknij"></button>
|
||||
</div>
|
||||
|
||||
<!-- Data od -->
|
||||
<label class="form-label fw-medium" style="font-size: 0.85rem; color: #566A7F;">Data od</label>
|
||||
<div class="input-group mb-3 dropdown">
|
||||
<span class="input-group-text bg-white text-muted" style="border-radius: 8px 0 0 8px; border-color: #d9dee3;"><i class='bx bx-calendar'></i></span>
|
||||
<input type="text" class="form-control bg-white dropdown-toggle" value="2024-08-12" readonly data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false" style="border-radius: 0 8px 8px 0; border-color: #d9dee3; cursor: pointer;">
|
||||
|
||||
<!-- SneatDatePicker Mock -->
|
||||
<div class="dropdown-menu p-3 shadow border-0" style="border-radius: 8px; min-width: 300px; z-index: 1050; margin-top: 4px !important;">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<a href="#" class="btn btn-sm btn-icon text-secondary" style="padding: 4px;"><i class='bx bx-chevron-left fs-4'></i></a>
|
||||
<div class="fw-bold d-flex align-items-center gap-1" style="color: #566A7F; font-size: 1rem;">
|
||||
Sierpień 2024
|
||||
<div class="d-flex flex-column ms-1">
|
||||
<i class='bx bx-chevron-up' style="font-size: 1rem; line-height: 0.5; cursor: pointer; color: #566a7f;"></i>
|
||||
<i class='bx bx-chevron-down' style="font-size: 1rem; line-height: 0.5; cursor: pointer; color: #566a7f;"></i>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" class="btn btn-sm btn-icon text-secondary" style="padding: 4px;"><i class='bx bx-chevron-right fs-4'></i></a>
|
||||
</div>
|
||||
<div class="d-flex mb-2">
|
||||
<div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Pn</div><div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Wt</div><div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Śr</div><div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Cz</div><div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Pt</div><div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Sb</div><div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Nd</div>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap" style="row-gap: 8px;">
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #c7cdd4; text-align: center;">29</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #c7cdd4; text-align: center;">30</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #c7cdd4; text-align: center;">31</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">1</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">2</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">3</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">4</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">5</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">6</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">7</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">8</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">9</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">10</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">11</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; background-color: #696cff; color: #fff; text-align: center; border-radius: 6px; font-weight: 600; box-shadow: 0 2px 4px rgba(105, 108, 255, 0.4);">12</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 30px; border: 1px solid #696cff; color: #566A7F; text-align: center; border-radius: 6px;">13</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">14</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">15</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">16</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">17</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">18</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Data do -->
|
||||
<label class="form-label fw-medium" style="font-size: 0.85rem; color: #566A7F;">Data do</label>
|
||||
<div class="input-group mb-4 dropdown">
|
||||
<span class="input-group-text bg-white text-muted" style="border-radius: 8px 0 0 8px; border-color: #d9dee3;"><i class='bx bx-calendar'></i></span>
|
||||
<input type="text" class="form-control bg-white dropdown-toggle" value="2024-08-18" readonly data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false" style="border-radius: 0 8px 8px 0; border-color: #d9dee3; cursor: pointer;">
|
||||
|
||||
<!-- SneatDatePicker Mock -->
|
||||
<div class="dropdown-menu p-3 shadow border-0" style="border-radius: 8px; min-width: 300px; z-index: 1050; margin-top: 4px !important;">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<a href="#" class="btn btn-sm btn-icon text-secondary" style="padding: 4px;"><i class='bx bx-chevron-left fs-4'></i></a>
|
||||
<div class="fw-bold d-flex align-items-center gap-1" style="color: #566A7F; font-size: 1rem;">
|
||||
Sierpień 2024
|
||||
<div class="d-flex flex-column ms-1">
|
||||
<i class='bx bx-chevron-up' style="font-size: 1rem; line-height: 0.5; cursor: pointer; color: #566a7f;"></i>
|
||||
<i class='bx bx-chevron-down' style="font-size: 1rem; line-height: 0.5; cursor: pointer; color: #566a7f;"></i>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" class="btn btn-sm btn-icon text-secondary" style="padding: 4px;"><i class='bx bx-chevron-right fs-4'></i></a>
|
||||
</div>
|
||||
<div class="d-flex mb-2">
|
||||
<div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Pn</div><div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Wt</div><div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Śr</div><div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Cz</div><div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Pt</div><div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Sb</div><div class="text-center fw-semibold" style="width: 14.28%; font-size: 0.75rem; color: #a1acb8;">Nd</div>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap" style="row-gap: 8px;">
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #c7cdd4; text-align: center;">29</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #c7cdd4; text-align: center;">30</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #c7cdd4; text-align: center;">31</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">1</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">2</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">3</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">4</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">5</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">6</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">7</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">8</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">9</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">10</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">11</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">12</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 30px; border: 1px solid #696cff; color: #566A7F; text-align: center; border-radius: 6px;">13</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">14</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">15</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">16</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; color: #566A7F; text-align: center;">17</div></div>
|
||||
<div class="d-flex justify-content-center align-items-center" style="width: 14.28%;"><div style="width: 32px; height: 32px; line-height: 32px; background-color: #696cff; color: #fff; text-align: center; border-radius: 6px; font-weight: 600; box-shadow: 0 2px 4px rgba(105, 108, 255, 0.4);">18</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn w-100 mb-3 fw-medium text-white" style="border-radius: 8px; background-color: #696cff; border-color: #696cff;">Szukaj</button>
|
||||
|
||||
<div class="text-start">
|
||||
<a href="#" class="text-decoration-none fw-medium" style="font-size: 0.85rem; color: #566A7F;">Wyczyść filtry i daty</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container pb-4">
|
||||
<!-- Dzień: Poniedziałek -->
|
||||
<div class="schedule-day mb-3">
|
||||
<h6 class="fw-bold mb-2 text-muted text-uppercase" style="font-size: 0.7rem;">Poniedziałek, 12 Sierpnia</h6>
|
||||
|
||||
<div class="card-custom shift-card" style="border-left: 4px solid var(--primary-color);">
|
||||
<div class="p-2 px-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5 class="fw-bold mb-0" style="font-size: 1.1rem;">08:00 - 16:00</h5>
|
||||
<span class="text-primary-custom fw-semibold" style="font-size: 0.75rem;">Specjalista ds. Obsługi</span>
|
||||
</div>
|
||||
<span class="badge bg-label-primary">8h</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dzień: Wtorek -->
|
||||
<div class="schedule-day mb-3">
|
||||
<h6 class="fw-bold mb-2 text-muted text-uppercase" style="font-size: 0.7rem;">Wtorek, 13 Sierpnia</h6>
|
||||
|
||||
<div class="card-custom shift-card" style="border-left: 4px solid #71dd37;">
|
||||
<div class="p-2 px-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div>
|
||||
<h5 class="fw-bold mb-0" style="font-size: 1.1rem;">10:00 - 18:00</h5>
|
||||
<span class="text-success fw-semibold" style="font-size: 0.75rem;">Zastępca Kierownika</span>
|
||||
</div>
|
||||
<span class="badge bg-label-success" style="background: rgba(113, 221, 55, 0.1); color: #71dd37;">8h</span>
|
||||
</div>
|
||||
|
||||
<!-- Notatka od managera (bardzo kompaktowa) -->
|
||||
<div class="bg-light p-2 rounded text-muted d-flex align-items-start gap-2" style="font-size: 0.75rem;">
|
||||
<i class='bx bx-message-square-detail text-warning mt-1'></i>
|
||||
<div>
|
||||
<strong class="text-dark">Anna M.:</strong> Pamiętaj o odbiorze dużej dostawy przed południem.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dzień: Środa -->
|
||||
<div class="schedule-day mb-3">
|
||||
<h6 class="fw-bold mb-2 text-muted text-uppercase" style="font-size: 0.7rem;">Środa, 14 Sierpnia</h6>
|
||||
|
||||
<div class="card-custom bg-transparent border border-dashed border-secondary opacity-50" style="box-shadow: none;">
|
||||
<div class="p-2 text-center">
|
||||
<span class="text-muted fw-semibold" style="font-size: 0.8rem;">Dzień wolny</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dzień: Czwartek -->
|
||||
<div class="schedule-day mb-3">
|
||||
<h6 class="fw-bold mb-2 text-muted text-uppercase" style="font-size: 0.7rem;">Czwartek, 15 Sierpnia</h6>
|
||||
|
||||
<div class="card-custom shift-card" style="border-left: 4px solid var(--primary-color);">
|
||||
<div class="p-2 px-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5 class="fw-bold mb-0" style="font-size: 1.1rem;">08:00 - 16:00</h5>
|
||||
<span class="text-primary-custom fw-semibold" style="font-size: 0.75rem;">Specjalista ds. Obsługi</span>
|
||||
</div>
|
||||
<span class="badge bg-label-primary">8h</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="app-topbar">
|
||||
<div class="d-flex align-items-center">
|
||||
<a href="index.php" class="text-dark me-2"><i class='bx bx-arrow-back fs-4'></i></a>
|
||||
<h1 class="app-title">Wybierz firmę</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container py-4">
|
||||
<p class="text-muted mb-4">Wybierz firmę, dla której chcesz dzisiaj pracować, aby kontynuować logowanie.</p>
|
||||
|
||||
<div class="card-custom overflow-hidden">
|
||||
<a href="home.php" class="company-list-item">
|
||||
<div class="company-logo-placeholder bg-primary-custom">
|
||||
A
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-0 fw-bold">Acme Corp Sp. z o.o.</h6>
|
||||
<small class="text-muted">NIP: 1234567890</small>
|
||||
</div>
|
||||
<div>
|
||||
<i class='bx bx-chevron-right fs-3 text-muted'></i>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="home.php" class="company-list-item">
|
||||
<div class="company-logo-placeholder bg-dark text-white">
|
||||
G
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-0 fw-bold">Global Industries</h6>
|
||||
<small class="text-muted">NIP: 9876543210</small>
|
||||
</div>
|
||||
<div>
|
||||
<i class='bx bx-chevron-right fs-3 text-muted'></i>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="home.php" class="company-list-item">
|
||||
<div class="company-logo-placeholder bg-info text-white">
|
||||
M
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-0 fw-bold">Magico Sp. z o.o.</h6>
|
||||
<small class="text-muted">Oddział Główny</small>
|
||||
</div>
|
||||
<div>
|
||||
<i class='bx bx-chevron-right fs-3 text-muted'></i>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
@@ -0,0 +1,379 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<!-- Główny Topbar -->
|
||||
<div class="app-topbar">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="d-flex justify-content-center align-items-center bg-primary-custom rounded-circle me-2" style="width: 32px; height: 32px;">
|
||||
<i class='bx bx-cube-alt text-white'></i>
|
||||
</div>
|
||||
<h1 class="app-title">Acme Corp</h1>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<!-- Powiadomienia -->
|
||||
<div class="dropdown">
|
||||
<div class="position-relative" style="cursor: pointer;" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class='bx bx-bell fs-4 text-muted'></i>
|
||||
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger border border-white" style="font-size: 0.65rem; padding: 0.3em 0.45em;">
|
||||
1
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-end p-0 border-0 mt-2" style="border-radius: 16px; width: 340px; box-shadow: 0 0.5rem 1.5rem rgba(105, 108, 255, 0.15); overflow: hidden; z-index: 1050; margin-right: -40px;">
|
||||
<div class="p-3 border-bottom d-flex justify-content-between align-items-center" style="background-color: #fcfcfd;">
|
||||
<h6 class="mb-0 fw-bold" style="color: #566A7F;">Powiadomienia</h6>
|
||||
<span class="badge bg-label-danger rounded-pill">1 nowe</span>
|
||||
</div>
|
||||
|
||||
<div class="list-group list-group-flush" style="max-height: 350px; overflow-y: auto;">
|
||||
|
||||
<!-- Nieprzeczytane -->
|
||||
<a href="vacations.php" class="list-group-item list-group-item-action d-flex align-items-start p-3 border-0" style="background-color: rgba(105, 108, 255, 0.03); border-bottom: 1px solid rgba(0,0,0,0.03) !important;">
|
||||
<div class="d-flex justify-content-center align-items-center rounded-circle flex-shrink-0 me-3" style="width: 40px; height: 40px; background-color: rgba(255, 62, 29, 0.1); color: #ff3e1d;">
|
||||
<i class='bx bx-x-circle fs-5'></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-1 fw-bold" style="font-size: 0.9rem; color: #566A7F;">Wniosek odrzucony</h6>
|
||||
<p class="mb-1 small text-muted" style="line-height: 1.4;">Kierownik odrzucił Twój wniosek o urlop na żądanie (05 Wrz).</p>
|
||||
<small class="text-primary-custom fw-semibold" style="font-size: 0.75rem;">10 min temu</small>
|
||||
</div>
|
||||
<div class="flex-shrink-0 ms-2 mt-2">
|
||||
<div style="width: 8px; height: 8px; background-color: #696cff; border-radius: 50%;"></div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<!-- Przeczytane -->
|
||||
<a href="vacations.php" class="list-group-item list-group-item-action d-flex align-items-start p-3 border-0">
|
||||
<div class="d-flex justify-content-center align-items-center rounded-circle flex-shrink-0 me-3" style="width: 40px; height: 40px; background-color: rgba(113, 221, 55, 0.1); color: #71dd37;">
|
||||
<i class='bx bx-check-circle fs-5'></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-1 fw-semibold" style="font-size: 0.9rem; color: #566A7F;">Wniosek zaakceptowany</h6>
|
||||
<p class="mb-1 small text-muted" style="line-height: 1.4;">Twój urlop wypoczynkowy (12-16 Sie) został pomyślnie zatwierdzony.</p>
|
||||
<small class="text-muted" style="font-size: 0.75rem;">Wczoraj</small>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="p-3 border-top text-center" style="background-color: #fcfcfd;">
|
||||
<button class="btn btn-primary btn-sm w-100 fw-bold" style="border-radius: 8px;">Zobacz wszystkie</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="user-avatar shadow-sm">
|
||||
<i class='bx bx-user'></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pasek tytułowy sekcji -->
|
||||
<div class="container pt-4">
|
||||
<div class="d-flex justify-content-between align-items-center pb-3 mb-4" style="border-bottom: 1px solid var(--border-color);">
|
||||
<div class="d-flex align-items-center">
|
||||
<a href="home.php" class="text-dark me-2 d-flex align-items-center justify-content-center" style="width: 32px; height: 32px; background: rgba(0,0,0,0.05); border-radius: 50%;">
|
||||
<i class='bx bx-arrow-back fs-4'></i>
|
||||
</a>
|
||||
<h2 class="fw-bold mb-0" style="font-size: 1.25rem;">Moje Urlopy</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container pb-4">
|
||||
|
||||
<!-- Statystyki Urlopowe (Dashboard Widget) -->
|
||||
<div class="card border-0 shadow-sm mb-4" style="border-radius: 16px;">
|
||||
<div class="card-body p-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="d-flex justify-content-center align-items-center me-2" style="width: 32px; height: 32px; border-radius: 8px; background-color: rgba(105, 108, 255, 0.1);">
|
||||
<i class='bx bx-pie-chart-alt-2' style="color: #696cff; font-size: 1.2rem;"></i>
|
||||
</div>
|
||||
<h6 class="fw-bold mb-0" style="color: #566A7F; font-size: 1.05rem;">Pula urlopowa</h6>
|
||||
</div>
|
||||
<select class="form-select form-select-sm border-0 bg-light text-muted fw-semibold" style="width: auto; border-radius: 8px; cursor: pointer;">
|
||||
<option value="2024" selected>Rok: 2024</option>
|
||||
<option value="2023">Rok: 2023</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-end mb-2 mt-1">
|
||||
<div>
|
||||
<span class="text-muted small d-block mb-1">Dostępne do wykorzystania</span>
|
||||
<h3 class="mb-0 fw-bold" style="color: #696cff;">14 <span class="fs-6 fw-semibold text-muted">z 26 dni</span></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<div class="progress mb-2 mt-3" style="height: 10px; border-radius: 10px; background-color: rgba(105, 108, 255, 0.15);">
|
||||
<div class="progress-bar" role="progressbar" style="width: 46%; background-color: #696cff; border-radius: 10px;" aria-valuenow="12" aria-valuemin="0" aria-valuemax="26"></div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between text-muted" style="font-size: 0.8rem;">
|
||||
<span class="fw-medium">Wykorzystano: 12 dni</span>
|
||||
<span class="fw-medium">Zaległy: 0 dni</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Tabs: Nadchodzące / Historia -->
|
||||
<ul class="nav nav-pills nav-justified mb-4" id="vacationTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active fw-bold shadow-sm" id="upcoming-tab" data-bs-toggle="pill" data-bs-target="#upcoming" type="button" role="tab" aria-controls="upcoming" aria-selected="true" style="border-radius: 8px;">Nadchodzące</button>
|
||||
</li>
|
||||
<li class="nav-item ms-2" role="presentation">
|
||||
<button class="nav-link fw-bold shadow-sm" id="history-tab" data-bs-toggle="pill" data-bs-target="#history" type="button" role="tab" aria-controls="history" aria-selected="false" style="border-radius: 8px;">Historia</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content p-0 m-0 border-0" id="vacationTabsContent">
|
||||
<!-- ZAKŁADKA 1: Nadchodzące -->
|
||||
<div class="tab-pane fade show active" id="upcoming" role="tabpanel" aria-labelledby="upcoming-tab">
|
||||
|
||||
<!-- Wniosek 1: Zaakceptowany -->
|
||||
<div class="card-custom mb-3 border-0 shadow-sm" style="border-left: 4px solid #71dd37 !important;">
|
||||
<div class="p-3">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div>
|
||||
<span class="badge bg-label-success mb-1">Zaakceptowany</span>
|
||||
<h5 class="fw-bold mb-0" style="font-size: 1.1rem;">Urlop wypoczynkowy</h5>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="fw-bold text-dark fs-5">5</span>
|
||||
<span class="text-muted small d-block" style="margin-top: -5px;">dni</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center text-muted small mt-3">
|
||||
<i class='bx bx-calendar me-2 fs-5 text-primary-custom'></i>
|
||||
<span class="fw-semibold">12 Sierpnia 2026 - 16 Sierpnia 2026</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wniosek 2: Oczekujący -->
|
||||
<div class="card-custom mb-3 border-0 shadow-sm" style="border-left: 4px solid #8592a3 !important;">
|
||||
<div class="p-3">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div>
|
||||
<span class="badge bg-label-secondary mb-1">Oczekuje na akceptację</span>
|
||||
<h5 class="fw-bold mb-0" style="font-size: 1.1rem;">Urlop wypoczynkowy</h5>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="fw-bold text-dark fs-5">2</span>
|
||||
<span class="text-muted small d-block" style="margin-top: -5px;">dni</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center text-muted small mt-3">
|
||||
<i class='bx bx-calendar me-2 fs-5 text-primary-custom'></i>
|
||||
<span class="fw-semibold">27 Sierpnia 2026 - 28 Sierpnia 2026</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wniosek 3: Odrzucony -->
|
||||
<div class="card-custom mb-3 border-0 shadow-sm" style="border-left: 4px solid #ff3e1d !important;">
|
||||
<div class="p-3">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div>
|
||||
<span class="badge bg-label-danger mb-1">Odrzucony</span>
|
||||
<h5 class="fw-bold mb-0" style="font-size: 1.1rem;">Urlop na żądanie</h5>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="fw-bold text-dark fs-5">1</span>
|
||||
<span class="text-muted small d-block" style="margin-top: -5px;">dzień</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center text-muted small mt-3 mb-2">
|
||||
<i class='bx bx-calendar me-2 fs-5 text-primary-custom'></i>
|
||||
<span class="fw-semibold">05 Września 2026</span>
|
||||
</div>
|
||||
|
||||
<div class="bg-light p-2 rounded text-muted small d-flex align-items-start gap-2 mt-2">
|
||||
<i class='bx bx-info-circle text-danger mt-1'></i>
|
||||
<div>
|
||||
<strong class="text-dark">Kierownik:</strong> Niestety w tym terminie mamy inwentaryzację, brak możliwości udzielenia urlopu.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ZAKŁADKA 2: Historia -->
|
||||
<div class="tab-pane fade" id="history" role="tabpanel" aria-labelledby="history-tab">
|
||||
|
||||
<!-- FILTRY HISTORII -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 gap-2">
|
||||
<select class="form-select bg-white shadow-sm border-0" style="border-radius: 8px; color: #566A7F; font-weight: 500;">
|
||||
<option value="2024" selected>Rok: 2024</option>
|
||||
<option value="2023">Rok: 2023</option>
|
||||
<option value="2022">Rok: 2022</option>
|
||||
</select>
|
||||
<select class="form-select bg-white shadow-sm border-0" style="border-radius: 8px; color: #566A7F; font-weight: 500;">
|
||||
<option value="all" selected>Status: Wszystkie</option>
|
||||
<option value="accepted">Wykorzystane</option>
|
||||
<option value="rejected">Odrzucone</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Historia: Wykorzystany -->
|
||||
<div class="card-custom mb-3 border-0 shadow-sm" style="border-left: 4px solid #d9dee3 !important;">
|
||||
<div class="p-3">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div>
|
||||
<span class="badge bg-label-secondary mb-1">Wykorzystany</span>
|
||||
<h5 class="fw-bold mb-0 text-muted" style="font-size: 1.1rem;">Urlop wypoczynkowy</h5>
|
||||
</div>
|
||||
<div class="text-end text-muted">
|
||||
<span class="fw-bold fs-5">14</span>
|
||||
<span class="small d-block" style="margin-top: -5px;">dni</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center text-muted small mt-3">
|
||||
<i class='bx bx-calendar me-2 fs-5'></i>
|
||||
<span>01 Lipca 2024 - 14 Lipca 2024</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Historia: Odrzucony -->
|
||||
<div class="card-custom mb-3 border-0 shadow-sm" style="border-left: 4px solid #d9dee3 !important; opacity: 0.8;">
|
||||
<div class="p-3">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div>
|
||||
<span class="badge bg-label-danger mb-1 opacity-75">Odrzucony</span>
|
||||
<h5 class="fw-bold mb-0 text-muted" style="font-size: 1.1rem;">Urlop na żądanie</h5>
|
||||
</div>
|
||||
<div class="text-end text-muted">
|
||||
<span class="fw-bold fs-5">1</span>
|
||||
<span class="small d-block" style="margin-top: -5px;">dzień</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center text-muted small mt-3">
|
||||
<i class='bx bx-calendar me-2 fs-5'></i>
|
||||
<span>15 Maja 2024</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Modal: Nowy Wniosek -->
|
||||
<div class="modal fade" id="vacationModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
|
||||
<div class="modal-content border-0 shadow" style="border-radius: var(--radius-lg);">
|
||||
<div class="modal-header border-bottom-0 pb-0">
|
||||
<h5 class="modal-title fw-bold">Nowy wniosek o urlop</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="vacation-form">
|
||||
<div class="mb-3 text-start">
|
||||
<label class="form-label fw-semibold">Początek urlopu</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span class="input-group-text"><i class="bx bx-calendar"></i></span>
|
||||
<input type="text" id="date-start" class="form-control datepicker" placeholder="Wybierz datę początkową">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 text-start">
|
||||
<label class="form-label fw-semibold">Koniec urlopu</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span class="input-group-text"><i class="bx bx-calendar"></i></span>
|
||||
<input type="text" id="date-end" class="form-control datepicker" placeholder="Wybierz datę końcową">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 text-start">
|
||||
<label class="form-label fw-semibold">Powód</label>
|
||||
<select class="form-select">
|
||||
<option value="2">Urlop wypoczynkowy</option>
|
||||
<option value="4">Zwolnienie - opieka</option>
|
||||
<option value="3">Zwolnienie lekarskie</option>
|
||||
<option value="9">Służba wojskowa</option>
|
||||
<option value="8" selected>Urlop bezpłatny</option>
|
||||
<option value="5">Urlop macierzyński</option>
|
||||
<option value="7">Urlop rehabilitacyjny</option>
|
||||
<option value="6">Urlop wychowawczy</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-4 text-start">
|
||||
<label class="form-label fw-semibold">Dodatkowe informacje</label>
|
||||
<textarea class="form-control" rows="3" placeholder="Opcjonalna uwaga do przełożonego..."></textarea>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-primary w-100 py-3 fw-bold fs-6" id="btn-submit-vacation">
|
||||
Wyślij wniosek
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Flatpickr CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
|
||||
<!-- Flatpickr Theme (optional, fits well) -->
|
||||
<link rel="stylesheet" type="text/css" href="https://npmcdn.com/flatpickr/dist/themes/airbnb.css">
|
||||
|
||||
<!-- Floating Action Button (FAB) dla nowego wniosku -->
|
||||
<div class="fab-wrapper">
|
||||
<button class="btn btn-primary fab-btn" data-bs-toggle="modal" data-bs-target="#vacationModal" aria-label="Nowy wniosek o urlop">
|
||||
<i class='bx bx-plus'></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
|
||||
<!-- Flatpickr JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
|
||||
<script src="https://npmcdn.com/flatpickr/dist/l10n/pl.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Inicjalizacja datepickera (Flatpickr) z polskim językiem
|
||||
flatpickr(".datepicker", {
|
||||
locale: "pl",
|
||||
dateFormat: "Y-m-d",
|
||||
disableMobile: "true" // Pozwala na ładny natywny wygląd flatpickr na mobilkach
|
||||
});
|
||||
|
||||
// Symulacja wysyłania
|
||||
const btnSubmit = document.getElementById('btn-submit-vacation');
|
||||
if (btnSubmit) {
|
||||
btnSubmit.addEventListener('click', function() {
|
||||
const originalText = this.innerHTML;
|
||||
this.innerHTML = "<i class='bx bx-loader-alt bx-spin me-2'></i> Wysyłanie...";
|
||||
this.disabled = true;
|
||||
|
||||
setTimeout(() => {
|
||||
this.innerHTML = "<i class='bx bx-check me-2'></i> Wysłano pomyślnie!";
|
||||
this.classList.replace('btn-primary', 'btn-success');
|
||||
|
||||
setTimeout(() => {
|
||||
const modalEl = document.getElementById('vacationModal');
|
||||
const modal = bootstrap.Modal.getInstance(modalEl);
|
||||
if (modal) modal.hide();
|
||||
|
||||
// Reset buttona
|
||||
this.innerHTML = originalText;
|
||||
this.classList.replace('btn-success', 'btn-primary');
|
||||
this.disabled = false;
|
||||
}, 1500);
|
||||
}, 1500);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -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ę.
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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'; ?>
|
||||
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
$pageTitle = 'TravelLeader - Kalendarz Dostępności';
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="app-content">
|
||||
|
||||
<!-- Nagłówek widoku dostępności -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<h5 class="fw-bold mb-0" style="color: var(--dark-navy);">Dostępność Pilota</h5>
|
||||
<small class="text-muted" style="font-size: 0.78rem;">Zarządzaj terminami dla biur podróży</small>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-primary-custom py-1 px-3" style="width: auto; font-size: 0.8rem;" data-bs-toggle="modal" data-bs-target="#addUnavailableModal">
|
||||
<i class='bx bx-plus'></i> Dodaj blokadę
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Główna karta kalendarza -->
|
||||
<div class="section-card">
|
||||
<div class="calendar-month-header">
|
||||
<button class="btn btn-sm btn-light rounded-circle p-2 d-flex align-items-center"><i class='bx bx-chevron-left fs-5'></i></button>
|
||||
<h6 class="fw-bold mb-0" style="color: var(--dark-navy); font-size: 1rem;">Wrzesień 2027</h6>
|
||||
<button class="btn btn-sm btn-light rounded-circle p-2 d-flex align-items-center"><i class='bx bx-chevron-right fs-5'></i></button>
|
||||
</div>
|
||||
|
||||
<!-- Siatka kalendarza -->
|
||||
<div class="calendar-grid mb-2">
|
||||
<div class="calendar-day-header">Pn</div>
|
||||
<div class="calendar-day-header">Wt</div>
|
||||
<div class="calendar-day-header">Śr</div>
|
||||
<div class="calendar-day-header">Cz</div>
|
||||
<div class="calendar-day-header">Pt</div>
|
||||
<div class="calendar-day-header">Sb</div>
|
||||
<div class="calendar-day-header">Nd</div>
|
||||
|
||||
<!-- Dni poprzedniego miesiąca -->
|
||||
<div class="calendar-day-cell other-month">30</div>
|
||||
<div class="calendar-day-cell other-month">31</div>
|
||||
|
||||
<!-- Dni września -->
|
||||
<div class="calendar-day-cell blocked-day" title="Urlop prywatny">1</div>
|
||||
<div class="calendar-day-cell blocked-day" title="Urlop prywatny">2</div>
|
||||
<div class="calendar-day-cell blocked-day" title="Urlop prywatny">3</div>
|
||||
<div class="calendar-day-cell blocked-day" title="Urlop prywatny">4</div>
|
||||
<div class="calendar-day-cell free-day">5 <span class="calendar-day-dot bg-success"></span></div>
|
||||
|
||||
<div class="calendar-day-cell free-day">6 <span class="calendar-day-dot bg-success"></span></div>
|
||||
<div class="calendar-day-cell free-day">7 <span class="calendar-day-dot bg-success"></span></div>
|
||||
<div class="calendar-day-cell free-day">8 <span class="calendar-day-dot bg-success"></span></div>
|
||||
<div class="calendar-day-cell free-day">9 <span class="calendar-day-dot bg-success"></span></div>
|
||||
<div class="calendar-day-cell free-day">10 <span class="calendar-day-dot bg-success"></span></div>
|
||||
<div class="calendar-day-cell free-day">11 <span class="calendar-day-dot bg-success"></span></div>
|
||||
<div class="calendar-day-cell free-day">12 <span class="calendar-day-dot bg-success"></span></div>
|
||||
|
||||
<div class="calendar-day-cell free-day">13 <span class="calendar-day-dot bg-success"></span></div>
|
||||
<div class="calendar-day-cell free-day">14 <span class="calendar-day-dot bg-success"></span></div>
|
||||
<div class="calendar-day-cell free-day">15 <span class="calendar-day-dot bg-success"></span></div>
|
||||
|
||||
<!-- Wycieczka z systemu: 16-24.09.2027 -->
|
||||
<div class="calendar-day-cell trip-day" title="Wycieczka: 12 oskar prod">16 <span class="calendar-day-dot bg-primary"></span></div>
|
||||
<div class="calendar-day-cell trip-day" title="Wycieczka: 12 oskar prod">17 <span class="calendar-day-dot bg-primary"></span></div>
|
||||
<div class="calendar-day-cell trip-day" title="Wycieczka: 12 oskar prod">18 <span class="calendar-day-dot bg-primary"></span></div>
|
||||
<div class="calendar-day-cell trip-day" title="Wycieczka: 12 oskar prod">19 <span class="calendar-day-dot bg-primary"></span></div>
|
||||
|
||||
<div class="calendar-day-cell trip-day" title="Wycieczka: 12 oskar prod">20 <span class="calendar-day-dot bg-primary"></span></div>
|
||||
<div class="calendar-day-cell trip-day" title="Wycieczka: 12 oskar prod">21 <span class="calendar-day-dot bg-primary"></span></div>
|
||||
<div class="calendar-day-cell trip-day" title="Wycieczka: 12 oskar prod">22 <span class="calendar-day-dot bg-primary"></span></div>
|
||||
<div class="calendar-day-cell trip-day" title="Wycieczka: 12 oskar prod">23 <span class="calendar-day-dot bg-primary"></span></div>
|
||||
<div class="calendar-day-cell trip-day" title="Wycieczka: 12 oskar prod">24 <span class="calendar-day-dot bg-primary"></span></div>
|
||||
|
||||
<div class="calendar-day-cell free-day">25 <span class="calendar-day-dot bg-success"></span></div>
|
||||
<div class="calendar-day-cell free-day">26 <span class="calendar-day-dot bg-success"></span></div>
|
||||
|
||||
<div class="calendar-day-cell free-day">27 <span class="calendar-day-dot bg-success"></span></div>
|
||||
<div class="calendar-day-cell free-day">28 <span class="calendar-day-dot bg-success"></span></div>
|
||||
<div class="calendar-day-cell free-day">29 <span class="calendar-day-dot bg-success"></span></div>
|
||||
<div class="calendar-day-cell free-day">30 <span class="calendar-day-dot bg-success"></span></div>
|
||||
<div class="calendar-day-cell other-month">1</div>
|
||||
<div class="calendar-day-cell other-month">2</div>
|
||||
<div class="calendar-day-cell other-month">3</div>
|
||||
</div>
|
||||
|
||||
<!-- Legenda -->
|
||||
<div class="calendar-legend">
|
||||
<div class="legend-item">
|
||||
<div class="legend-color-box" style="background-color: var(--primary);"></div>
|
||||
<span>Wycieczka potwierdzona</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color-box" style="background-color: #ef4444;"></div>
|
||||
<span>Niedostępny / Urlop</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<div class="legend-color-box" style="background-color: #22c55e;"></div>
|
||||
<span>Dostępny dla biur</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lista wpisów w wybranym miesiącu -->
|
||||
<h6 class="fw-bold mb-2 mt-3" style="color: var(--dark-navy); font-size: 0.9rem;">Wydarzenia we Wrześniu 2027</h6>
|
||||
|
||||
<!-- Wycieczka potwierdzona -->
|
||||
<div class="card border-0 rounded-4 p-3 mb-2 shadow-sm" style="background: #ffffff; border-left: 4px solid var(--primary) !important;">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<span class="badge mb-1" style="background: #eff0fe; color: var(--primary); font-size: 0.72rem;">Wycieczka przypisana</span>
|
||||
<h6 class="fw-bold mb-1" style="font-size: 0.92rem; color: var(--dark-navy);">12 oskar prod (Włochy)</h6>
|
||||
<small class="text-muted d-block" style="font-size: 0.76rem;"><i class='bx bx-time-five'></i> 16 - 24 Września 2027 (9 dni)</small>
|
||||
</div>
|
||||
<a href="index.php" class="btn btn-sm btn-outline-primary py-1 px-2" style="font-size: 0.75rem;">Wycieczka ></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Prywatna niedostępność -->
|
||||
<div class="card border-0 rounded-4 p-3 mb-3 shadow-sm" style="background: #ffffff; border-left: 4px solid #ef4444 !important;">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<span class="badge mb-1" style="background: #fee2e2; color: #ef4444; font-size: 0.72rem;">Niedostępność pilota</span>
|
||||
<h6 class="fw-bold mb-1" style="font-size: 0.92rem; color: var(--dark-navy);">Urlop prywatny / sprawy rodzinne</h6>
|
||||
<small class="text-muted d-block" style="font-size: 0.76rem;"><i class='bx bx-time-five'></i> 01 - 04 Września 2027 (4 dni)</small>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-danger py-1 px-2" style="font-size: 0.75rem;" onclick="confirm('Czy chcesz usunąć tę blokadę terminu?')"><i class='bx bx-trash'></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Informacja o integracji z bazą biur -->
|
||||
<div class="p-3 rounded-4" style="background-color: #f1f3fa;">
|
||||
<div class="d-flex align-items-center gap-2 mb-1">
|
||||
<i class='bx bx-info-circle text-primary fs-5'></i>
|
||||
<span class="fw-bold" style="font-size: 0.82rem; color: var(--dark-navy);">Jak działa to dla biur podróży?</span>
|
||||
</div>
|
||||
<p class="text-muted mb-0" style="font-size: 0.75rem; line-height: 1.4;">
|
||||
Wyszukiwarka w aplikacji webowej pozwala pracownikowi biura wybrać np. „Termin: 05-15 Wrz 2027 + Kraj: Włochy”. Twój profil pojawi się na szczycie wyników, ponieważ zaznaczyłeś te dni jako wolne!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div><!-- /.app-content -->
|
||||
|
||||
<!-- Modal dodawania niedostępności -->
|
||||
<div class="modal fade" id="addUnavailableModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content rounded-4 border-0">
|
||||
<div class="modal-header border-bottom">
|
||||
<h6 class="modal-title fw-bold" style="color: var(--dark-navy);">Zgłoś niedostępność / Urlop</h6>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zamknij"></button>
|
||||
</div>
|
||||
<div class="modal-body p-3">
|
||||
<div class="mb-3">
|
||||
<label class="form-label-custom">Powód niedostępności</label>
|
||||
<select class="form-select form-control-custom">
|
||||
<option selected>Urlop prywatny</option>
|
||||
<option>Zlecenie poza systemem</option>
|
||||
<option>Szkolenie / Egzamin</option>
|
||||
<option>Sprawy zdrowotne</option>
|
||||
<option>Inny powód</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-6">
|
||||
<label class="form-label-custom">Data od</label>
|
||||
<input type="date" class="form-control-custom" value="2027-10-01">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="form-label-custom">Data do</label>
|
||||
<input type="date" class="form-control-custom" value="2027-10-08">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label-custom">Notatka prywatna (opcjonalnie)</label>
|
||||
<textarea class="form-control-custom" rows="2" placeholder="Widoczna tylko dla Ciebie..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer border-top p-2">
|
||||
<button type="button" class="btn btn-secondary-custom py-2 flex-grow-1" data-bs-dismiss="modal">Anuluj</button>
|
||||
<button type="button" class="btn-primary-custom py-2 flex-grow-1" data-bs-dismiss="modal" onclick="alert('Pomyślnie zablokowano termin w kalendarzu. Biura nie będą wysyłać zapytań na ten okres.');">
|
||||
Zapisz termin
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
$pageTitle = 'TravelLeader - Szkolenia i Certyfikaty';
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="app-content">
|
||||
|
||||
<!-- Nagłówek szkoleń (Czysty, minimalistyczny, bez zbędnych odznak) -->
|
||||
<div class="mb-3">
|
||||
<h5 class="fw-bold mb-0" style="color: var(--dark-navy);">Szkolenia & Certyfikaty</h5>
|
||||
<small class="text-muted" style="font-size: 0.76rem;">Kursy podnoszące rangę profilu w bazie biur podróży</small>
|
||||
</div>
|
||||
|
||||
<!-- Przełącznik zakładek (Segmented control) -->
|
||||
<div class="d-flex p-1 rounded-3 mb-3" style="background: #eef1f8;">
|
||||
<button type="button" id="tabCoursesBtn" class="btn btn-sm flex-fill fw-bold rounded-2 py-1" style="font-size: 0.78rem; background: white; color: var(--primary); box-shadow: 0 1px 4px rgba(0,0,0,0.06);" onclick="switchCourseTab('courses');">
|
||||
Dostępne kursy (3)
|
||||
</button>
|
||||
<button type="button" id="tabCertsBtn" class="btn btn-sm flex-fill fw-bold rounded-2 py-1 text-muted" style="font-size: 0.78rem;" onclick="switchCourseTab('certs');">
|
||||
Moje certyfikaty (2)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- SEKCJA 1: Dostępne kursy (Czyste karty, bez wielopoziomowych pięter) -->
|
||||
<div id="sectionCourses">
|
||||
|
||||
<!-- Kurs 1 -->
|
||||
<div class="section-card mb-2 p-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<span class="badge" style="background: #eff0fe; color: var(--primary); font-size: 0.68rem; font-weight: 700;">
|
||||
WŁOCHY
|
||||
</span>
|
||||
<span class="fw-bold text-dark" style="font-size: 0.82rem;">199 PLN</span>
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold mb-1" style="font-size: 0.9rem; color: var(--dark-navy);">
|
||||
Włochy Północne & Dolomity 2027
|
||||
</h6>
|
||||
<p class="text-muted mb-2" style="font-size: 0.75rem; line-height: 1.35;">
|
||||
Prowadzący: Janek. Warsztaty z logistyki autokarowej, stref ZTL i nowych szlaków.
|
||||
</p>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center pt-2 border-top">
|
||||
<small class="text-muted" style="font-size: 0.72rem;">
|
||||
<i class='bx bx-calendar'></i> 24.10 • 4h online
|
||||
</small>
|
||||
<button class="btn btn-sm btn-primary-custom py-1 px-3" style="width: auto; font-size: 0.74rem;" onclick="alert('Zapis na szkolenie przez TravelPay.');">
|
||||
Zapisz się >
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Kurs 2 -->
|
||||
<div class="section-card mb-2 p-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<span class="badge bg-warning-subtle text-warning-emphasis" style="font-size: 0.68rem; font-weight: 700;">
|
||||
RAINBOW TOURS
|
||||
</span>
|
||||
<span class="badge bg-success-subtle text-success fw-bold" style="font-size: 0.68rem;">BEZPŁATNE</span>
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold mb-1" style="font-size: 0.9rem; color: var(--dark-navy);">
|
||||
Standardy obsługi imprez objazdowych
|
||||
</h6>
|
||||
<p class="text-muted mb-2" style="font-size: 0.75rem; line-height: 1.35;">
|
||||
Szkolenie produktowe biura podróży dla pilotów szukających kontraktów na sezon.
|
||||
</p>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center pt-2 border-top">
|
||||
<small class="text-muted" style="font-size: 0.72rem;">
|
||||
<i class='bx bx-video'></i> VOD • 2h
|
||||
</small>
|
||||
<button class="btn btn-sm btn-secondary-custom py-1 px-3" style="width: auto; font-size: 0.74rem;" onclick="alert('Dostęp do materiałów VOD został odblokowany.');">
|
||||
Dołącz >
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Kurs 3 -->
|
||||
<div class="section-card mb-3 p-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<span class="badge bg-light text-muted" style="font-size: 0.68rem; font-weight: 700;">
|
||||
PRAWO & PROCEDURY
|
||||
</span>
|
||||
<span class="fw-bold text-dark" style="font-size: 0.82rem;">149 PLN</span>
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold mb-1" style="font-size: 0.9rem; color: var(--dark-navy);">
|
||||
Prawo w turystyce & reklamacje
|
||||
</h6>
|
||||
<p class="text-muted mb-2" style="font-size: 0.75rem; line-height: 1.35;">
|
||||
Procedury awaryjne, ubezpieczenia i rozwiązywanie trudnych sytuacji z turystami.
|
||||
</p>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center pt-2 border-top">
|
||||
<small class="text-muted" style="font-size: 0.72rem;">
|
||||
<i class='bx bx-calendar'></i> 12.11 • Webinar
|
||||
</small>
|
||||
<button class="btn btn-sm btn-primary-custom py-1 px-3" style="width: auto; font-size: 0.74rem;" onclick="alert('Zapis na webinar.');">
|
||||
Zapisz się >
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- SEKCJA 2: Moje certyfikaty (Ukryta domyślnie, przełączana zakładką) -->
|
||||
<div id="sectionCerts" class="d-none">
|
||||
|
||||
<div class="section-card mb-2 p-3">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="d-flex align-items-center justify-content-center rounded-circle bg-success-subtle text-success" style="width: 38px; height: 38px; flex-shrink: 0;">
|
||||
<i class='bx bx-badge-check fs-4'></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="fw-bold mb-0" style="font-size: 0.86rem; color: var(--dark-navy);">Superpilot: Europa Południowa</h6>
|
||||
<small class="text-muted" style="font-size: 0.72rem;">Weryfikacja TravelLeader • Dodano do profilu</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-card mb-3 p-3">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="d-flex align-items-center justify-content-center rounded-circle bg-primary-subtle text-primary" style="width: 38px; height: 38px; flex-shrink: 0;">
|
||||
<i class='bx bx-plus-medical fs-5'></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="fw-bold mb-0" style="font-size: 0.86rem; color: var(--dark-navy);">Pierwsza Pomoc (KPP)</h6>
|
||||
<small class="text-muted" style="font-size: 0.72rem;">Certyfikat ważny do: Kwiecień 2028</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-3 rounded-3 text-center" style="background: #f8f9fd;">
|
||||
<small class="text-muted" style="font-size: 0.74rem;">
|
||||
Wszystkie zdobyte certyfikaty są automatycznie widoczne dla biur podróży w Twojej wizytówce.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div><!-- /.app-content -->
|
||||
|
||||
<script>
|
||||
function switchCourseTab(tab) {
|
||||
const coursesSection = document.getElementById('sectionCourses');
|
||||
const certsSection = document.getElementById('sectionCerts');
|
||||
const coursesBtn = document.getElementById('tabCoursesBtn');
|
||||
const certsBtn = document.getElementById('tabCertsBtn');
|
||||
|
||||
if (tab === 'courses') {
|
||||
coursesSection.classList.remove('d-none');
|
||||
certsSection.classList.add('d-none');
|
||||
coursesBtn.style.background = 'white';
|
||||
coursesBtn.style.color = 'var(--primary)';
|
||||
coursesBtn.style.boxShadow = '0 1px 4px rgba(0,0,0,0.06)';
|
||||
certsBtn.style.background = 'transparent';
|
||||
certsBtn.style.color = 'var(--text-muted)';
|
||||
certsBtn.style.boxShadow = 'none';
|
||||
} else {
|
||||
coursesSection.classList.add('d-none');
|
||||
certsSection.classList.remove('d-none');
|
||||
certsBtn.style.background = 'white';
|
||||
certsBtn.style.color = 'var(--primary)';
|
||||
certsBtn.style.boxShadow = '0 1px 4px rgba(0,0,0,0.06)';
|
||||
coursesBtn.style.background = 'transparent';
|
||||
coursesBtn.style.color = 'var(--text-muted)';
|
||||
coursesBtn.style.boxShadow = 'none';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
$currentPage = basename($_SERVER['PHP_SELF'], '.php');
|
||||
?>
|
||||
<!-- Dolna belka nawigacyjna (Bottom Navigation Bar - zawsze widoczna na dole) -->
|
||||
<nav class="bottom-nav-bar">
|
||||
<a href="index.php" class="bottom-nav-item <?= in_array($currentPage, ['index', 'trip-details']) ? 'active' : '' ?>">
|
||||
<i class='bx <?= in_array($currentPage, ['index', 'trip-details']) ? 'bxs-compass' : 'bx-compass' ?>'></i>
|
||||
<span>Wycieczki</span>
|
||||
</a>
|
||||
|
||||
<a href="availability.php" class="bottom-nav-item <?= $currentPage === 'availability' ? 'active' : '' ?>">
|
||||
<i class='bx <?= $currentPage === 'availability' ? 'bxs-calendar' : 'bx-calendar' ?>'></i>
|
||||
<span>Dostępność</span>
|
||||
</a>
|
||||
|
||||
<a href="courses.php" class="bottom-nav-item <?= $currentPage === 'courses' ? 'active' : '' ?>">
|
||||
<i class='bx <?= $currentPage === 'courses' ? 'bxs-book-open' : 'bx-book-open' ?>'></i>
|
||||
<span>Szkolenia</span>
|
||||
</a>
|
||||
|
||||
<a href="profile.php" class="bottom-nav-item <?= in_array($currentPage, ['profile', 'profile-edit']) ? 'active' : '' ?>">
|
||||
<i class='bx <?= in_array($currentPage, ['profile', 'profile-edit']) ? 'bxs-user-pin' : 'bx-user' ?>'></i>
|
||||
<span>Mój Profil</span>
|
||||
<span class="nav-indicator" title="Profil wymaga uzupełnienia"></span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div><!-- /.phone-screen -->
|
||||
</div><!-- /.phone-viewport-wrapper -->
|
||||
|
||||
<!-- Lokalne skrypty Bootstrap (z projektu Magico, bez jsdelivr) -->
|
||||
<script src="../../assets/vendor/js/bootstrap.js"></script>
|
||||
|
||||
<!-- Magico Feedback JS -->
|
||||
<?php if (!empty($enablePrototypeComments)): ?>
|
||||
<script src="../../assets/js/comments.js"></script>
|
||||
<?php endif; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
$currentPage = basename($_SERVER['PHP_SELF'], '.php');
|
||||
if (!isset($pageTitle)) {
|
||||
$pageTitle = 'TravelLeader - Aplikacja Pilota';
|
||||
}
|
||||
$isAcceptedState = isset($_GET['accepted']) && $_GET['accepted'] == '15';
|
||||
$isRejectedState = isset($_GET['rejected']) && $_GET['rejected'] == '15';
|
||||
$headerNotifCount = ($isAcceptedState || $isRejectedState) ? 0 : 1;
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||
<title><?= htmlspecialchars($pageTitle) ?></title>
|
||||
|
||||
<!-- Google 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:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Lokalne assety (Boxicons + Core CSS z projektu Magico, bez jsdelivr) -->
|
||||
<link rel="stylesheet" href="../../assets/vendor/fonts/boxicons.css">
|
||||
<link rel="stylesheet" href="../../assets/vendor/css/core.css">
|
||||
|
||||
<!-- Stylistyka aplikacji mobilnej TravelLeader -->
|
||||
<link rel="stylesheet" href="assets/css/style.css">
|
||||
|
||||
<!-- Magico Comments -->
|
||||
<?php if (!empty($enablePrototypeComments)): ?>
|
||||
<link rel="stylesheet" href="../../assets/css/comments.css">
|
||||
<?php endif; ?>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="phone-viewport-wrapper">
|
||||
<div class="phone-screen">
|
||||
|
||||
<!-- Pasek stanu telefonu (Widoczny w ramce na desktopie, ukryty na mobile) -->
|
||||
<div class="phone-status-bar">
|
||||
<span>9:05</span>
|
||||
<div class="icons">
|
||||
<span class="badge bg-secondary" style="font-size: 0.6rem; padding: 1px 4px;">VPN</span>
|
||||
<i class='bx bx-wifi'></i>
|
||||
<i class='bx bx-signal-5'></i>
|
||||
<span style="font-size: 0.72rem; display: inline-flex; align-items: center; gap: 2px;">
|
||||
46 <i class='bx bxs-battery-charging' style="font-size: 1rem;"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nagłówek aplikacji TravelLeader -->
|
||||
<header class="app-header">
|
||||
<a href="index.php" class="brand-logo-area">
|
||||
<div class="brand-icon-box">
|
||||
<i class='bx bx-compass'></i>
|
||||
</div>
|
||||
<div class="brand-name">Travel<span style="color: #2b304f;">Leader</span></div>
|
||||
</a>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<!-- Dzwonek powiadomień o zaproszeniach -->
|
||||
<a href="index.php?tab=invitations" class="header-notif-btn" id="headerNotifBtn" title="Nowe zaproszenie na wycieczkę (<?= $headerNotifCount ?>)">
|
||||
<i class='bx bx-bell'></i>
|
||||
<?php if ($headerNotifCount > 0): ?>
|
||||
<span class="header-notif-badge" id="headerNotifBadge"><?= $headerNotifCount ?></span>
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
|
||||
<a href="profile.php" class="header-user-profile" title="Przejdź do profilu">
|
||||
<div class="header-user-info">
|
||||
<div class="header-user-email">oskar@magico.pl</div>
|
||||
<div class="header-user-role">Pilot Wycieczek</div>
|
||||
</div>
|
||||
<div class="header-avatar" style="overflow: hidden; padding: 0;">
|
||||
<img src="../../assets/img/pilots/oskar-avatar.jpg" alt="Oskar" style="width: 100%; height: 100%; object-fit: cover;">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
@@ -0,0 +1,588 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
$pageTitle = 'TravelLeader - Wycieczki i Zlecenia';
|
||||
|
||||
$isAccepted = isset($_GET['accepted']) && $_GET['accepted'] == '15';
|
||||
$isRejected = isset($_GET['rejected']) && $_GET['rejected'] == '15';
|
||||
$activeTab = $_GET['tab'] ?? ($isAccepted ? 'current' : 'current');
|
||||
|
||||
$currentCount = $isAccepted ? 2 : 1;
|
||||
$invitationsCount = ($isAccepted || $isRejected) ? 0 : 1;
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<!-- SYSTEMOWY STACK WSTĄŻEK (Pasy pod nagłówkiem na pełną szerokość - oddzielone kolorami i subtelnym borderem) -->
|
||||
<div class="system-ribbons-stack">
|
||||
|
||||
<!-- 1. WSTĄŻKA ZAPROSZENIA (Ciepły koral/bursztyn, 24h timer, priorytet biznesowy) -->
|
||||
<?php if (!$isAccepted && !$isRejected): ?>
|
||||
<div class="system-ribbon-bar ribbon-invitation" id="ribbonInvitationBar">
|
||||
<div class="d-flex align-items-center gap-2" style="min-width: 0;">
|
||||
<span class="ribbon-badge ribbon-badge-urgent">⏳ 18h</span>
|
||||
<div class="ribbon-text">
|
||||
<span class="fw-bold">Zaproszenie</span>
|
||||
<span class="ribbon-sub"> • Włochy (05–12.10) • 4 400 zł</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 flex-shrink-0">
|
||||
<a href="invitation-details.php?id=15" class="btn-ribbon-action btn-ribbon-invitation">
|
||||
Sprawdź >
|
||||
</a>
|
||||
<button type="button" class="btn-ribbon-close" onclick="this.closest('.ribbon-invitation').style.display='none';" title="Zamknij powiadomienie">
|
||||
<i class='bx bx-x'></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- 2. WSTĄŻKA PROFILU (Chabrowa, uzupełnienie danych) -->
|
||||
<div class="system-ribbon-bar ribbon-profile" id="ribbonProfileBar">
|
||||
<div class="d-flex align-items-center gap-2" style="min-width: 0;">
|
||||
<span class="ribbon-badge">45%</span>
|
||||
<div class="ribbon-text">
|
||||
<span class="fw-bold">Profil w bazie</span>
|
||||
<span class="ribbon-sub"> • Uzupełnij dane dla biur</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 flex-shrink-0">
|
||||
<a href="profile.php" class="btn-ribbon-action">
|
||||
Dokończ >
|
||||
</a>
|
||||
<button type="button" class="btn-ribbon-close" onclick="this.closest('.ribbon-profile').style.display='none';" title="Zamknij powiadomienie">
|
||||
<i class='bx bx-x'></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="app-content">
|
||||
|
||||
<?php if ($isAccepted): ?>
|
||||
<!-- Zwięzły alert sukcesu po akceptacji wycieczki -->
|
||||
<div class="p-2 px-3 rounded-2 mb-2 d-flex align-items-center justify-content-between" style="background: #ecfdf5; border: 1px solid #a7f3d0; font-size: 0.78rem;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class='bx bx-check-circle fs-5 text-success'></i>
|
||||
<span class="text-success fw-semibold">Wycieczka #15 została zaakceptowana i dodana do Twojego grafiku!</span>
|
||||
</div>
|
||||
<a href="index.php" class="btn-close" style="font-size: 0.65rem;" aria-label="Zamknij"></a>
|
||||
</div>
|
||||
<?php elseif ($isRejected): ?>
|
||||
<!-- Zwięzły alert informacyjny po odrzuceniu -->
|
||||
<div class="p-2 px-3 rounded-2 mb-2 d-flex align-items-center justify-content-between" style="background: #f8fafc; border: 1px solid #e2e8f0; font-size: 0.78rem;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class='bx bx-info-circle fs-5 text-muted'></i>
|
||||
<span class="text-muted">Zaproszenie #15 zostało odrzucone. Biuro podróży otrzymało powiadomienie.</span>
|
||||
</div>
|
||||
<a href="index.php" class="btn-close" style="font-size: 0.65rem;" aria-label="Zamknij"></a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Wyszukiwarka i filtr zakresu dat -->
|
||||
<div class="search-filter-card">
|
||||
<div class="search-filter-icon">
|
||||
<i class='bx bx-search'></i>
|
||||
</div>
|
||||
<div class="search-filter-text flex-grow-1">
|
||||
<h6>Wycieczki</h6>
|
||||
<small>
|
||||
<i class='bx bx-calendar'></i> 18 wrz 2026 → 18 wrz 2027
|
||||
</small>
|
||||
</div>
|
||||
<i class='bx bx-chevron-down text-muted fs-4'></i>
|
||||
</div>
|
||||
|
||||
<!-- Pasek zakładek i przełącznik widoku (Bieżące vs Zaproszenia vs Historia) -->
|
||||
<div class="trips-tabs-bar">
|
||||
<div class="nav-pills-custom">
|
||||
<a href="javascript:void(0);" class="nav-pill-item <?= $activeTab === 'current' ? 'active' : '' ?>" id="tabBtnCurrent" onclick="switchTripsTab('current');">
|
||||
<i class='bx bx-home-alt-2'></i>
|
||||
<span>Bieżące</span>
|
||||
<span class="badge-count" id="badgeCurrentCount"><?= $currentCount ?></span>
|
||||
</a>
|
||||
<a href="javascript:void(0);" class="nav-pill-item <?= $activeTab === 'invitations' ? 'active' : '' ?>" id="tabBtnInvitations" onclick="switchTripsTab('invitations');">
|
||||
<i class='bx bx-envelope'></i>
|
||||
<span>Zaproszenia</span>
|
||||
<span class="badge-count <?= $invitationsCount > 0 ? 'badge-alert' : '' ?>" id="badgeInvitationsCount"><?= $invitationsCount ?></span>
|
||||
</a>
|
||||
<a href="javascript:void(0);" class="nav-pill-item <?= $activeTab === 'history' ? 'active' : '' ?>" id="tabBtnHistory" onclick="switchTripsTab('history');">
|
||||
<i class='bx bx-history'></i>
|
||||
<span>Historia</span>
|
||||
<span class="badge-count">0</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Przełącznik Lista / Kalendarz w obrębie wycieczek -->
|
||||
<div class="view-toggle-group" id="tripsViewToggleGroup" style="<?= $activeTab === 'invitations' ? 'display: none !important;' : '' ?>">
|
||||
<button type="button" id="btnListView" class="view-toggle-btn active" title="Widok listy" onclick="switchTripsView('list');">
|
||||
<i class='bx bx-list-ul'></i>
|
||||
</button>
|
||||
<button type="button" id="btnCalendarView" class="view-toggle-btn" title="Widok kalendarza" onclick="switchTripsView('calendar');">
|
||||
<i class='bx bx-calendar'></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ==================== WIDOK 1: BIEŻĄCE WYCIECZKI (LISTA) ==================== -->
|
||||
<div id="tripsListView" class="<?= $activeTab !== 'current' ? 'd-none' : '' ?>">
|
||||
|
||||
<?php if ($isAccepted): ?>
|
||||
<!-- Nowo zaakceptowana wycieczka #15 -->
|
||||
<div class="trip-card border-success" style="border-left: 4px solid #10b981 !important;">
|
||||
<div class="trip-card-header">
|
||||
<div class="d-flex align-items-center flex-grow-1">
|
||||
<div class="trip-id-badge" style="background: linear-gradient(135deg, #10b981, #059669); color: white;">15</div>
|
||||
<div class="trip-title-area">
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
<span class="badge bg-success-subtle text-success py-0 px-1" style="font-size: 0.65rem;">Nowo przyjęta</span>
|
||||
<span class="text-muted small" style="font-size: 0.72rem;">Almatur</span>
|
||||
</div>
|
||||
<h5 class="trip-title mb-0">Magia Toskanii i Rzym</h5>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="trip-status-pill">
|
||||
<span class="badge-status bg-success-subtle text-success">Zaakceptowana</span>
|
||||
<div class="status-subtext">za 382 dni</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dane wycieczki -->
|
||||
<div class="trip-info-row">
|
||||
<i class='bx bx-calendar text-primary'></i>
|
||||
<span>05-12.10.2027 (8 dni)</span>
|
||||
</div>
|
||||
|
||||
<div class="trip-info-row">
|
||||
<i class='bx bx-group text-primary'></i>
|
||||
<span>38 osób • Autokar</span>
|
||||
</div>
|
||||
|
||||
<div class="trip-info-row">
|
||||
<i class='bx bx-map text-primary'></i>
|
||||
<span>🇮🇹 Włochy (Florencja, Rzym)</span>
|
||||
</div>
|
||||
|
||||
<!-- Stopka karty -->
|
||||
<div class="trip-card-footer">
|
||||
<div class="trip-code">
|
||||
<i class='bx bx-wallet'></i>
|
||||
<strong class="text-success">4 400 PLN netto</strong>
|
||||
</div>
|
||||
|
||||
<a href="invitation-details.php?id=15" class="btn-details">
|
||||
Szczegóły >
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Istniejąca wycieczka #12 -->
|
||||
<div class="trip-card">
|
||||
<div class="trip-card-header">
|
||||
<div class="d-flex align-items-center flex-grow-1">
|
||||
<div class="trip-id-badge">12</div>
|
||||
<div class="trip-title-area">
|
||||
<h5 class="trip-title">oskar prod</h5>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="trip-status-pill">
|
||||
<span class="badge-status">Planowana</span>
|
||||
<div class="status-subtext">za 363 dni</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dane wycieczki -->
|
||||
<div class="trip-info-row">
|
||||
<i class='bx bx-calendar'></i>
|
||||
<span>16-24.09.2027</span>
|
||||
</div>
|
||||
|
||||
<div class="trip-info-row">
|
||||
<i class='bx bx-group'></i>
|
||||
<span>24 / 0</span>
|
||||
</div>
|
||||
|
||||
<div class="trip-info-row">
|
||||
<i class='bx bx-map'></i>
|
||||
<span class="text-muted">Brak danych</span>
|
||||
</div>
|
||||
|
||||
<!-- Stopka karty -->
|
||||
<div class="trip-card-footer">
|
||||
<div class="trip-code">
|
||||
<i class='bx bx-briefcase-alt'></i>
|
||||
<span>123osk123</span>
|
||||
</div>
|
||||
|
||||
<a href="#" class="btn-details" onclick="openTripBottomSheet('16 WRZEŚNIA 2027'); return false;">
|
||||
Szczegóły >
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ==================== WIDOK 2: KALENDARZ WYCIECZEK ==================== -->
|
||||
<div id="tripsCalendarView" class="d-none">
|
||||
<div class="trips-calendar-container">
|
||||
|
||||
<!-- Nagłówek miesiąca -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 px-1">
|
||||
<button type="button" class="btn btn-sm btn-light rounded-circle p-1" onclick="alert('Przejdź do Sierpnia 2027');"><i class='bx bx-chevron-left fs-5'></i></button>
|
||||
<div class="month-title-header mb-0">WRZESIEŃ 2027</div>
|
||||
<button type="button" class="btn btn-sm btn-light rounded-circle p-1" onclick="alert('Przejdź do Października 2027');"><i class='bx bx-chevron-right fs-5'></i></button>
|
||||
</div>
|
||||
|
||||
<table class="calendar-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Pn</th>
|
||||
<th>Wt</th>
|
||||
<th>Śr</th>
|
||||
<th>Cz</th>
|
||||
<th>Pt</th>
|
||||
<th>So</th>
|
||||
<th>Nd</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Tydzień 1 -->
|
||||
<tr>
|
||||
<td class="other-month"><span class="day-number">30</span></td>
|
||||
<td class="other-month"><span class="day-number">31</span></td>
|
||||
<td><span class="day-number">1</span></td>
|
||||
<td><span class="day-number">2</span></td>
|
||||
<td><span class="day-number">3</span></td>
|
||||
<td><span class="day-number">4</span></td>
|
||||
<td><span class="day-number">5</span></td>
|
||||
</tr>
|
||||
<!-- Tydzień 2 -->
|
||||
<tr>
|
||||
<td><span class="day-number">6</span></td>
|
||||
<td><span class="day-number">7</span></td>
|
||||
<td><span class="day-number">8</span></td>
|
||||
<td><span class="day-number">9</span></td>
|
||||
<td><span class="day-number">10</span></td>
|
||||
<td><span class="day-number">11</span></td>
|
||||
<td><span class="day-number">12</span></td>
|
||||
</tr>
|
||||
<!-- Tydzień 3: Początek wycieczki 16-19 września -->
|
||||
<tr>
|
||||
<td><span class="day-number">13</span></td>
|
||||
<td><span class="day-number">14</span></td>
|
||||
<td><span class="day-number">15</span></td>
|
||||
<td onclick="openTripBottomSheet('16 WRZEŚNIA 2027');">
|
||||
<span class="day-number fw-bold text-primary">16</span>
|
||||
<span class="trip-ribbon-bar trip-ribbon-start">oskar prod</span>
|
||||
</td>
|
||||
<td onclick="openTripBottomSheet('17 WRZEŚNIA 2027');">
|
||||
<span class="day-number fw-bold text-primary">17</span>
|
||||
<span class="trip-ribbon-bar"> </span>
|
||||
</td>
|
||||
<td onclick="openTripBottomSheet('18 WRZEŚNIA 2027');">
|
||||
<span class="day-number fw-bold text-primary">18</span>
|
||||
<span class="trip-ribbon-bar"> </span>
|
||||
</td>
|
||||
<td onclick="openTripBottomSheet('19 WRZEŚNIA 2027');">
|
||||
<span class="day-number fw-bold text-primary">19</span>
|
||||
<span class="trip-ribbon-bar"> </span>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Tydzień 4: Ciąg dalszy wycieczki 20-24 września -->
|
||||
<tr>
|
||||
<td onclick="openTripBottomSheet('20 WRZEŚNIA 2027');">
|
||||
<span class="day-number fw-bold text-primary">20</span>
|
||||
<span class="trip-ribbon-bar">oskar prod</span>
|
||||
</td>
|
||||
<td onclick="openTripBottomSheet('21 WRZEŚNIA 2027');">
|
||||
<span class="day-number fw-bold text-primary">21</span>
|
||||
<span class="trip-ribbon-bar"> </span>
|
||||
</td>
|
||||
<td onclick="openTripBottomSheet('22 WRZEŚNIA 2027');">
|
||||
<span class="day-number fw-bold text-primary">22</span>
|
||||
<span class="trip-ribbon-bar"> </span>
|
||||
</td>
|
||||
<td onclick="openTripBottomSheet('23 WRZEŚNIA 2027');">
|
||||
<span class="day-number fw-bold text-primary">23</span>
|
||||
<span class="trip-ribbon-bar"> </span>
|
||||
</td>
|
||||
<td onclick="openTripBottomSheet('24 WRZEŚNIA 2027');">
|
||||
<span class="day-number fw-bold text-primary">24</span>
|
||||
<span class="trip-ribbon-bar trip-ribbon-end"> </span>
|
||||
</td>
|
||||
<td><span class="day-number">25</span></td>
|
||||
<td><span class="day-number">26</span></td>
|
||||
</tr>
|
||||
<!-- Tydzień 5 -->
|
||||
<tr>
|
||||
<td><span class="day-number">27</span></td>
|
||||
<td><span class="day-number">28</span></td>
|
||||
<td><span class="day-number">29</span></td>
|
||||
<td><span class="day-number">30</span></td>
|
||||
<td class="other-month"><span class="day-number">1</span></td>
|
||||
<td class="other-month"><span class="day-number">2</span></td>
|
||||
<td class="other-month"><span class="day-number">3</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="mt-3 pt-2 border-top d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted" style="font-size: 0.72rem;">
|
||||
<i class='bx bxs-circle text-primary' style="font-size: 0.6rem;"></i> Zaznaczony termin wycieczki (16-24 Wrz)
|
||||
</small>
|
||||
<a href="availability.php" class="text-primary fw-bold text-decoration-none" style="font-size: 0.72rem;">
|
||||
Kalendarz dostępności →
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ==================== WIDOK 3: ZAPROSZENIA I OFERTY BIUR (MINIMALISTYCZNA LISTA) ==================== -->
|
||||
<div id="tripsInvitationsView" class="<?= $activeTab !== 'invitations' ? 'd-none' : '' ?>">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="fw-bold mb-0 text-dark" style="font-size: 0.92rem;">Otrzymane zaproszenia na wycieczki</h6>
|
||||
<span class="badge bg-primary-subtle text-primary border border-primary-subtle px-2 py-1" style="font-size: 0.72rem;">
|
||||
<?= $invitationsCount ?> aktywne
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<?php if (!$isAccepted && !$isRejected): ?>
|
||||
<!-- Minimalistyczny wiersz zaproszenia #15 (Single-tap) -->
|
||||
<a href="invitation-details.php?id=15" class="invitation-compact-item is-pending" title="Kliknij, aby przejść do szczegółów i decyzji">
|
||||
<div class="inv-item-icon">
|
||||
<i class='bx bx-envelope'></i>
|
||||
<span class="inv-item-status-dot"></span>
|
||||
</div>
|
||||
<div class="inv-item-content">
|
||||
<div class="inv-item-top">
|
||||
<span class="inv-item-agency fw-bold" style="color: #ea580c;">Nowe zaproszenie</span>
|
||||
<span class="inv-item-timer">⏳ 18h na decyzję</span>
|
||||
</div>
|
||||
<div class="inv-item-title">#15: Magia Toskanii i Rzym</div>
|
||||
<div class="inv-item-meta">
|
||||
<span>05–12.10.2027 (8 dni)</span>
|
||||
<span>•</span>
|
||||
<span class="text-success fw-bold">4 400 PLN netto</span>
|
||||
<span>•</span>
|
||||
<span>Almatur</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inv-item-arrow">
|
||||
<i class='bx bx-chevron-right fs-4 text-muted'></i>
|
||||
</div>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($isAccepted): ?>
|
||||
<!-- Kompaktowy kafelek zaakceptowanego zlecenia -->
|
||||
<a href="invitation-details.php?id=15" class="invitation-compact-item is-accepted">
|
||||
<div class="inv-item-icon" style="background: #ecfdf5; color: #10b981;">
|
||||
<i class='bx bx-check'></i>
|
||||
</div>
|
||||
<div class="inv-item-content">
|
||||
<div class="inv-item-top">
|
||||
<span class="inv-item-agency">Almatur Warszawa</span>
|
||||
<span class="badge bg-success-subtle text-success py-0 px-1" style="font-size: 0.68rem;">Zaakceptowane</span>
|
||||
</div>
|
||||
<div class="inv-item-title">Wycieczka #15: Magia Toskanii i Rzym</div>
|
||||
<div class="inv-item-meta">
|
||||
<span>05–12.10.2027</span>
|
||||
<span>•</span>
|
||||
<span class="text-success fw-semibold">4 400 PLN</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inv-item-arrow">
|
||||
<i class='bx bx-chevron-right fs-4 text-muted'></i>
|
||||
</div>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($isRejected): ?>
|
||||
<!-- Kompaktowy kafelek odrzuconego zlecenia -->
|
||||
<div class="invitation-compact-item is-rejected">
|
||||
<div class="inv-item-icon" style="background: #fef2f2; color: #ef4444;">
|
||||
<i class='bx bx-x'></i>
|
||||
</div>
|
||||
<div class="inv-item-content">
|
||||
<div class="inv-item-top">
|
||||
<span class="inv-item-agency">Almatur Warszawa</span>
|
||||
<span class="badge bg-danger-subtle text-danger py-0 px-1" style="font-size: 0.68rem;">Odrzucone</span>
|
||||
</div>
|
||||
<div class="inv-item-title">Wycieczka #15: Magia Toskanii i Rzym</div>
|
||||
<div class="inv-item-meta">
|
||||
<span>05–12.10.2027</span>
|
||||
<span>•</span>
|
||||
<span>Kolizja terminów</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Zaproszenie zrealizowane z przeszłości -->
|
||||
<div class="invitation-compact-item opacity-75">
|
||||
<div class="inv-item-icon" style="background: #f1f5f9; color: #64748b;">
|
||||
<span>R</span>
|
||||
</div>
|
||||
<div class="inv-item-content">
|
||||
<div class="inv-item-top">
|
||||
<span class="inv-item-agency">Rainbow Tours</span>
|
||||
<span class="badge bg-light text-muted border py-0 px-1" style="font-size: 0.68rem;">Zrealizowana</span>
|
||||
</div>
|
||||
<div class="inv-item-title">Wycieczka #9: Klasyczna Grecja</div>
|
||||
<div class="inv-item-meta">
|
||||
<span>Czerwiec 2026</span>
|
||||
<span>•</span>
|
||||
<span>Ocena: ★ 5.0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ==================== WIDOK 4: HISTORIA WYCIECZEK ==================== -->
|
||||
<div id="tripsHistoryView" class="d-none">
|
||||
<div class="text-center py-5">
|
||||
<i class='bx bx-history text-muted' style="font-size: 3rem;"></i>
|
||||
<h6 class="fw-bold mt-2 text-dark">Brak zrealizowanych wycieczek w tym sezonie</h6>
|
||||
<p class="text-muted small">Wycieczki z poprzednich lat są archiwizowane w bazie zleceń.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /.app-content -->
|
||||
|
||||
<!-- BOTTOM SHEET DLA WYCIECZKI Z KALENDARZA -->
|
||||
<div id="tripSheetBackdrop" class="bottom-sheet-backdrop" onclick="closeTripBottomSheet();"></div>
|
||||
<div id="tripBottomSheet" class="bottom-sheet-content">
|
||||
<div class="sheet-drag-handle"></div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div>
|
||||
<div class="text-uppercase fw-bold text-muted" style="font-size: 0.68rem; letter-spacing: 0.5px;">WYBRANY DZIEŃ</div>
|
||||
<h5 class="fw-bold mb-0" id="sheetSelectedDate" style="color: var(--dark-navy); font-size: 1.15rem;">16 WRZEŚNIA 2027</h5>
|
||||
</div>
|
||||
<button type="button" class="btn-close" style="font-size: 0.75rem;" onclick="closeTripBottomSheet();" aria-label="Zamknij"></button>
|
||||
</div>
|
||||
|
||||
<!-- Karta wycieczki wewnątrz Bottom Sheet -->
|
||||
<div class="trip-card mb-0 shadow-none border mt-3">
|
||||
<div class="trip-card-header">
|
||||
<div class="d-flex align-items-center flex-grow-1">
|
||||
<div class="trip-id-badge">12</div>
|
||||
<div class="trip-title-area">
|
||||
<h5 class="trip-title">oskar prod</h5>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="trip-status-pill">
|
||||
<span class="badge-status">Planowana</span>
|
||||
<div class="status-subtext">za 363 dni</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="trip-info-row">
|
||||
<i class='bx bx-calendar'></i>
|
||||
<span>16-24.09.2027</span>
|
||||
</div>
|
||||
|
||||
<div class="trip-info-row">
|
||||
<i class='bx bx-group'></i>
|
||||
<span>24 / 0</span>
|
||||
</div>
|
||||
|
||||
<div class="trip-info-row">
|
||||
<i class='bx bx-map'></i>
|
||||
<span class="text-muted">Brak danych</span>
|
||||
</div>
|
||||
|
||||
<div class="trip-card-footer">
|
||||
<div class="trip-code">
|
||||
<i class='bx bx-briefcase-alt'></i>
|
||||
<span>123osk123</span>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn-details" onclick="alert('Otwieranie pełnych szczegółów wycieczki 12 oskar prod');">
|
||||
Szczegóły >
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function switchTripsTab(tab) {
|
||||
const tabCurrent = document.getElementById('tabBtnCurrent');
|
||||
const tabInvitations = document.getElementById('tabBtnInvitations');
|
||||
const tabHistory = document.getElementById('tabBtnHistory');
|
||||
|
||||
const listView = document.getElementById('tripsListView');
|
||||
const calView = document.getElementById('tripsCalendarView');
|
||||
const invView = document.getElementById('tripsInvitationsView');
|
||||
const histView = document.getElementById('tripsHistoryView');
|
||||
const toggleGroup = document.getElementById('tripsViewToggleGroup');
|
||||
|
||||
// Reset tabs
|
||||
[tabCurrent, tabInvitations, tabHistory].forEach(t => t.classList.remove('active'));
|
||||
[listView, calView, invView, histView].forEach(v => v.classList.add('d-none'));
|
||||
|
||||
if (tab === 'invitations') {
|
||||
tabInvitations.classList.add('active');
|
||||
invView.classList.remove('d-none');
|
||||
if (toggleGroup) toggleGroup.style.setProperty('display', 'none', 'important');
|
||||
} else if (tab === 'history') {
|
||||
tabHistory.classList.add('active');
|
||||
histView.classList.remove('d-none');
|
||||
if (toggleGroup) toggleGroup.style.setProperty('display', 'none', 'important');
|
||||
} else {
|
||||
tabCurrent.classList.add('active');
|
||||
listView.classList.remove('d-none');
|
||||
if (toggleGroup) toggleGroup.style.display = 'flex';
|
||||
// Reset sub toggle to list
|
||||
document.getElementById('btnListView').classList.add('active');
|
||||
document.getElementById('btnCalendarView').classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
function switchTripsView(view) {
|
||||
const listView = document.getElementById('tripsListView');
|
||||
const calView = document.getElementById('tripsCalendarView');
|
||||
const btnList = document.getElementById('btnListView');
|
||||
const btnCal = document.getElementById('btnCalendarView');
|
||||
|
||||
if (view === 'calendar') {
|
||||
listView.classList.add('d-none');
|
||||
calView.classList.remove('d-none');
|
||||
btnCal.classList.add('active');
|
||||
btnList.classList.remove('active');
|
||||
} else {
|
||||
listView.classList.remove('d-none');
|
||||
calView.classList.add('d-none');
|
||||
btnList.classList.add('active');
|
||||
btnCal.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
function openTripBottomSheet(dateTitle) {
|
||||
document.getElementById('sheetSelectedDate').innerText = dateTitle;
|
||||
document.getElementById('tripSheetBackdrop').classList.add('active');
|
||||
document.getElementById('tripBottomSheet').classList.add('active');
|
||||
}
|
||||
|
||||
function closeTripBottomSheet() {
|
||||
document.getElementById('tripSheetBackdrop').classList.remove('active');
|
||||
document.getElementById('tripBottomSheet').classList.remove('active');
|
||||
}
|
||||
|
||||
// Obsługa parametru URL przy starcie (np. kliknięcie dzwonka w headerze)
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const tabParam = urlParams.get('tab');
|
||||
if (tabParam === 'invitations') {
|
||||
switchTripsTab('invitations');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
$pageTitle = 'TravelLeader - Zaproszenie od biura podróży';
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="app-content" style="padding-bottom: 90px;">
|
||||
|
||||
<!-- Pasek powrotu i statusu zlecenia -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<a href="index.php" class="text-decoration-none fw-semibold d-inline-flex align-items-center gap-1" style="color: var(--dark-navy); font-size: 0.85rem;">
|
||||
<i class='bx bx-left-arrow-alt fs-4'></i> Wróć do wycieczek
|
||||
</a>
|
||||
<span class="badge bg-warning-subtle text-warning border border-warning-subtle py-1 px-2 rounded-pill d-inline-flex align-items-center gap-1" style="font-size: 0.74rem;">
|
||||
<i class='bx bx-time-five'></i> 18h na decyzję
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Karta biura podróży wysyłającego zaproszenie -->
|
||||
<div class="section-card mb-3 p-3">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="rounded-3 d-flex align-items-center justify-content-center text-white fw-bold shadow-sm" style="width: 44px; height: 44px; background: linear-gradient(135deg, #5c60f5, #777bfb); font-size: 1.25rem;">
|
||||
A
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-uppercase text-muted fw-bold" style="font-size: 0.68rem; letter-spacing: 0.5px;">ZAPRASZAJĄCE BIURO PODRÓŻY</span>
|
||||
<h5 class="fw-bold text-dark mb-0" style="font-size: 1rem;">Almatur Warszawa</h5>
|
||||
<small class="text-muted" style="font-size: 0.76rem;">Oddział Operacyjny • Koordynator: Michał Wiśniewski</small>
|
||||
</div>
|
||||
</div>
|
||||
<a href="tel:+48228261234" class="btn btn-sm btn-light border text-primary rounded-circle d-flex align-items-center justify-content-center" style="width: 36px; height: 36px;" title="Zadzwoń do biura">
|
||||
<i class='bx bx-phone-call fs-5'></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Karta zlecenia / wycieczki (Czysty, minimalistyczny układ bez zbędnych zdjęć) -->
|
||||
<div class="section-card mb-3 p-3">
|
||||
<div class="d-flex align-items-center gap-2 mb-2">
|
||||
<span class="badge bg-primary-subtle text-primary border border-primary-subtle px-2 py-1" style="font-size: 0.72rem;">
|
||||
Wycieczka #15
|
||||
</span>
|
||||
<span class="badge bg-light text-dark border px-2 py-1" style="font-size: 0.72rem;">
|
||||
🇮🇹 Włochy
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h4 class="fw-bold text-dark mb-1" style="font-size: 1.15rem; line-height: 1.3;">
|
||||
Magia Toskanii i Rzym
|
||||
</h4>
|
||||
<div class="text-muted small d-flex align-items-center gap-2 mb-3" style="font-size: 0.78rem;">
|
||||
<span><i class='bx bx-calendar text-primary me-1'></i>05.10.2027 – 12.10.2027</span>
|
||||
<span>•</span>
|
||||
<span>8 dni</span>
|
||||
</div>
|
||||
|
||||
<!-- Grid parametrów -->
|
||||
<div class="row g-2">
|
||||
<div class="col-6">
|
||||
<div class="p-2 rounded-2 bg-light border">
|
||||
<small class="text-muted d-block" style="font-size: 0.7rem;">Grupa & Transport</small>
|
||||
<strong class="text-dark" style="font-size: 0.82rem;"><i class='bx bx-group text-primary me-1'></i>38 osób (Autokar)</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="p-2 rounded-2 bg-light border">
|
||||
<small class="text-muted d-block" style="font-size: 0.7rem;">Charakter imprezy</small>
|
||||
<strong class="text-dark" style="font-size: 0.82rem;"><i class='bx bx-map-alt text-primary me-1'></i>Objazdowa (Dorośli)</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="p-2 rounded-2 bg-light border">
|
||||
<small class="text-muted d-block" style="font-size: 0.7rem;">Start i zakończenie</small>
|
||||
<strong class="text-dark" style="font-size: 0.82rem;"><i class='bx bx-navigation text-primary me-1'></i>Warszawa → Rzym</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="p-2 rounded-2 bg-light border">
|
||||
<small class="text-muted d-block" style="font-size: 0.7rem;">Główne punkty</small>
|
||||
<strong class="text-dark" style="font-size: 0.82rem;"><i class='bx bx-landmark text-primary me-1'></i>Florencja, Siena, Rzym</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Warunki finansowe i rozliczenie -->
|
||||
<div class="section-card mb-3 p-3">
|
||||
<h6 class="fw-bold text-dark mb-3 d-flex align-items-center gap-2" style="font-size: 0.9rem;">
|
||||
<i class='bx bx-wallet text-primary fs-5'></i> Warunki Finansowe
|
||||
</h6>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center p-3 rounded-3 mb-3" style="background: #f8fafd; border: 1px solid var(--border-color);">
|
||||
<div>
|
||||
<span class="text-muted small d-block" style="font-size: 0.72rem;">Stawka dzienna netto:</span>
|
||||
<span class="fs-5 fw-bold text-dark">550 PLN</span>
|
||||
<span class="text-muted small">/ dzień</span>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="text-muted small d-block" style="font-size: 0.72rem;">Łączne wynagrodzenie (8 dni):</span>
|
||||
<span class="fs-4 fw-extrabold text-success" style="font-weight: 800;">4 400 PLN</span>
|
||||
<span class="text-success small d-block" style="font-size: 0.72rem;">netto na fakturę / zlecenie</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-muted small mb-1" style="font-size: 0.74rem;">
|
||||
<i class='bx bx-info-circle me-1'></i> Wypłata wynagrodzenia realizowana w terminie do 7 dni roboczych od rozliczenia imprezy w Travel Managerze.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gwarantowane świadczenia od biura -->
|
||||
<div class="section-card mb-3 p-3">
|
||||
<h6 class="fw-bold text-dark mb-2 d-flex align-items-center gap-2" style="font-size: 0.9rem;">
|
||||
<i class='bx bx-check-circle text-success fs-5'></i> Zapewniane Świadczenia
|
||||
</h6>
|
||||
|
||||
<div class="d-flex flex-column gap-2 pt-1" style="font-size: 0.8rem;">
|
||||
<div class="d-flex align-items-center gap-2 text-dark">
|
||||
<i class='bx bx-check text-success fs-5'></i>
|
||||
<span><strong>Pokój 1-osobowy</strong> w hotelach (standard 3* / 4*)</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 text-dark">
|
||||
<i class='bx bx-check text-success fs-5'></i>
|
||||
<span><strong>Pełne wyżywienie pilota</strong> (śniadania w hotelach + obiadokolacje)</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 text-dark">
|
||||
<i class='bx bx-check text-success fs-5'></i>
|
||||
<span><strong>Ubezpieczenie KL / NNW</strong> na pełną sumę gwarancyjną</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 text-dark">
|
||||
<i class='bx bx-check text-success fs-5'></i>
|
||||
<span><strong>Zwrot kosztów transferu</strong> na lotnisko zbiórki</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wiadomość od biura podróży -->
|
||||
<div class="section-card mb-4 p-3" style="background: #fafbff;">
|
||||
<h6 class="fw-bold text-dark mb-2 d-flex align-items-center gap-2" style="font-size: 0.88rem;">
|
||||
<i class='bx bx-message-dots text-primary fs-5'></i> Wiadomość od Koordynatora
|
||||
</h6>
|
||||
<p class="text-muted mb-0" style="font-size: 0.8rem; line-height: 1.5; font-style: italic;">
|
||||
„Cześć Oskar! Szykujemy naszą flagową jesienną objazdówkę po Toskanii i Rzymie. Grupa dorosłych, bardzo kulturalna. Bardzo zależy nam na Twojej obecności ze względu na świetne referencje z Florencji. W razie pytań dzwoń śmiało na numer operacyjny!”
|
||||
</p>
|
||||
<div class="text-end mt-2">
|
||||
<span class="small fw-semibold text-dark" style="font-size: 0.74rem;">— Michał Wiśniewski, Almatur</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /.app-content -->
|
||||
|
||||
<!-- Pasek decyzji przypięty do dołu (Decision Sticky Bar) -->
|
||||
<div class="decision-sticky-footer" id="decisionFooter">
|
||||
<button type="button" class="btn-decision-decline" onclick="openRejectBottomSheet();">
|
||||
<i class='bx bx-x fs-5'></i>
|
||||
<span>Odrzuć</span>
|
||||
</button>
|
||||
<button type="button" class="btn-decision-accept" onclick="acceptInvitation();">
|
||||
<i class='bx bx-check-double fs-5'></i>
|
||||
<span>Przyjmij wycieczkę</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Modal potwierdzenia akceptacji z sukcesem -->
|
||||
<div class="modal fade" id="acceptSuccessModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static">
|
||||
<div class="modal-dialog modal-dialog-centered" style="max-width: 360px;">
|
||||
<div class="modal-content border-0 shadow-lg text-center p-4" style="border-radius: 24px;">
|
||||
<div class="mx-auto mb-3 rounded-circle d-flex align-items-center justify-content-center" style="width: 64px; height: 64px; background: #dcfce7; color: #16a34a; font-size: 2rem;">
|
||||
<i class='bx bx-check'></i>
|
||||
</div>
|
||||
<h4 class="fw-bold text-dark mb-1">Wycieczka Zaakceptowana!</h4>
|
||||
<p class="text-muted small mb-3" style="font-size: 0.82rem;">
|
||||
Gratulacje! Zlecenie <strong>#15 Magia Toskanii i Rzym</strong> zostało przypisane do Twojego grafiku. Biuro Almatur zostało natychmiast powiadomione.
|
||||
</p>
|
||||
<div class="p-2 px-3 bg-light rounded-2 text-start mb-3" style="font-size: 0.76rem;">
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<span class="text-muted">Termin:</span>
|
||||
<strong class="text-dark">05.10 – 12.10.2027</strong>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="text-muted">Wynagrodzenie:</span>
|
||||
<strong class="text-success">4 400 PLN netto</strong>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary w-100 py-2 fw-semibold" style="border-radius: 12px;" onclick="window.location.href='index.php?accepted=15';">
|
||||
Przejdź do moich wycieczek →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Arkusz wysuwany z wyborem powodu odrzucenia (Bottom Sheet) -->
|
||||
<div id="rejectSheetBackdrop" class="bottom-sheet-backdrop" onclick="closeRejectBottomSheet();"></div>
|
||||
<div id="rejectBottomSheet" class="bottom-sheet-content" style="z-index: 1060;">
|
||||
<div class="sheet-drag-handle"></div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<h5 class="fw-bold mb-0 text-dark" style="font-size: 1.05rem;">Odrzucenie Zaproszenia</h5>
|
||||
<small class="text-muted" style="font-size: 0.75rem;">Podaj powód, by biuro mogło dobrać innego pilota</small>
|
||||
</div>
|
||||
<button type="button" class="btn-close" style="font-size: 0.75rem;" onclick="closeRejectBottomSheet();" aria-label="Zamknij"></button>
|
||||
</div>
|
||||
|
||||
<form id="rejectReasonForm" onsubmit="confirmRejection(event);">
|
||||
<div class="mb-3">
|
||||
<div class="d-flex flex-column gap-2" style="font-size: 0.82rem;">
|
||||
<label class="p-2 px-3 rounded-2 border d-flex align-items-center gap-2 cursor-pointer" style="background: #fafbff;">
|
||||
<input type="radio" name="reject_reason" value="conflict" class="form-check-input mt-0" checked>
|
||||
<span>🗓️ Kolizja z planami prywatnymi / urlop</span>
|
||||
</label>
|
||||
<label class="p-2 px-3 rounded-2 border d-flex align-items-center gap-2 cursor-pointer" style="background: #fafbff;">
|
||||
<input type="radio" name="reject_reason" value="rate" class="form-check-input mt-0">
|
||||
<span>💰 Za niska stawka dzienna na ten termin</span>
|
||||
</label>
|
||||
<label class="p-2 px-3 rounded-2 border d-flex align-items-center gap-2 cursor-pointer" style="background: #fafbff;">
|
||||
<input type="radio" name="reject_reason" value="destination" class="form-check-input mt-0">
|
||||
<span>📍 Preferuję inne destynacje w tym okresie</span>
|
||||
</label>
|
||||
<label class="p-2 px-3 rounded-2 border d-flex align-items-center gap-2 cursor-pointer" style="background: #fafbff;">
|
||||
<input type="radio" name="reject_reason" value="other" class="form-check-input mt-0">
|
||||
<span>✏️ Inny powód</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-dark small fw-semibold mb-1" for="rejectMessage">Krótka notatka dla koordynatora (opcjonalnie):</label>
|
||||
<textarea class="form-control" id="rejectMessage" rows="2" style="font-size: 0.8rem;" placeholder="np. Chętnie pojadę z Wami w listopadzie..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-outline-secondary w-50 py-2" style="border-radius: 10px; font-size: 0.82rem;" onclick="closeRejectBottomSheet();">
|
||||
Anuluj
|
||||
</button>
|
||||
<button type="submit" class="btn btn-danger w-50 py-2 fw-semibold" style="border-radius: 10px; font-size: 0.82rem;">
|
||||
Odrzuć zlecenie
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openRejectBottomSheet() {
|
||||
document.getElementById('rejectSheetBackdrop').classList.add('active');
|
||||
document.getElementById('rejectBottomSheet').classList.add('active');
|
||||
}
|
||||
|
||||
function closeRejectBottomSheet() {
|
||||
document.getElementById('rejectSheetBackdrop').classList.remove('active');
|
||||
document.getElementById('rejectBottomSheet').classList.remove('active');
|
||||
}
|
||||
|
||||
function acceptInvitation() {
|
||||
// Pokaż modal sukcesu
|
||||
const modalEl = document.getElementById('acceptSuccessModal');
|
||||
const bsModal = new bootstrap.Modal(modalEl);
|
||||
bsModal.show();
|
||||
}
|
||||
|
||||
function confirmRejection(e) {
|
||||
e.preventDefault();
|
||||
closeRejectBottomSheet();
|
||||
alert('Zaproszenie zostało odrzucone. Biuro Almatur otrzymało powiadomienie.');
|
||||
window.location.href = 'index.php?rejected=15';
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
$pageTitle = 'TravelLeader - Edycja Profilu';
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="app-content">
|
||||
|
||||
<!-- Pasek powrotu -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<a href="profile.php" class="text-decoration-none d-flex align-items-center gap-1 text-muted" style="font-size: 0.85rem; font-weight: 600;">
|
||||
<i class='bx bx-arrow-back fs-5'></i> Powrót do profilu
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-primary-custom py-1 px-3" style="width: auto; font-size: 0.8rem;" onclick="saveProfile();">
|
||||
<i class='bx bx-check'></i> Zapisz
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h5 class="fw-bold mb-3" style="color: var(--dark-navy);">Edycja Profilu</h5>
|
||||
|
||||
<!-- Toast powiadomienie o zapisie -->
|
||||
<div id="saveToast" class="alert alert-success d-none rounded-3 shadow-sm py-2 px-3 mb-3 d-flex align-items-center justify-content-between" style="font-size: 0.82rem;">
|
||||
<span><i class='bx bx-check-circle me-1'></i> Profil zaktualizowany! Zmiany widoczne dla biur.</span>
|
||||
<a href="profile.php" class="fw-bold text-success text-decoration-underline ms-2">Zobacz</a>
|
||||
</div>
|
||||
|
||||
<!-- 0. Nowoczesny, kompaktowy przełącznik widoczności (Pill card) -->
|
||||
<div class="visibility-status-pill-card mb-3">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="status-indicator-dot active" id="visibilityDot"></span>
|
||||
<div>
|
||||
<span class="fw-bold" style="font-size: 0.84rem; color: var(--dark-navy);" id="visibilityTitle">
|
||||
Widoczny w bazie biur podróży
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-check form-switch m-0">
|
||||
<input class="form-check-input" type="checkbox" id="visibilitySwitch" checked style="cursor: pointer; transform: scale(1.15);" onchange="toggleVisibility(this);">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 1. Zdjęcie profilowe & Wideo-Wizytówka -->
|
||||
<div class="section-card mb-3">
|
||||
<div class="section-title">
|
||||
<span><i class='bx bx-image text-primary me-1'></i> Zdjęcie i Wideo</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-3 mb-3">
|
||||
<div class="profile-avatar-img d-flex align-items-center justify-content-center" style="width: 60px; height: 60px; background: linear-gradient(135deg, #4f46e5, #7c3aed); color: white; font-size: 1.4rem; font-weight: 800; flex-shrink: 0;">
|
||||
OK
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary rounded-pill py-1 px-3" style="font-size: 0.76rem;" onclick="alert('Wybierz zdjęcie z telefonu.');">
|
||||
<i class='bx bx-camera'></i> Zmień zdjęcie profilowe
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label-custom">Link do wideo-autoprezentacji</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-light border-end-0 py-1"><i class='bx bxl-youtube text-danger'></i></span>
|
||||
<input type="text" class="form-control form-control-custom" value="https://youtu.be/oskar-pilot-intro" placeholder="YouTube, Vimeo...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label-custom">Zdjęcia ze szlaków i wypraw</label>
|
||||
<div class="d-flex gap-2">
|
||||
<div class="media-thumbnail d-flex align-items-center justify-content-center" style="width: 50px; height: 50px; background: #eff0fe; color: var(--primary); cursor: pointer;" onclick="alert('Wybierz kolejne zdjęcia');">
|
||||
<i class='bx bx-plus fs-4'></i>
|
||||
</div>
|
||||
<div class="media-thumbnail d-flex align-items-center justify-content-center" style="width: 50px; height: 50px; background: #60a5fa; color: white;">
|
||||
<i class='bx bx-landscape fs-4'></i>
|
||||
</div>
|
||||
<div class="media-thumbnail d-flex align-items-center justify-content-center" style="width: 50px; height: 50px; background: #f59e0b; color: white;">
|
||||
<i class='bx bx-camera fs-4'></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. Dane podstawowe & Autoprezentacja -->
|
||||
<div class="section-card mb-3" id="bio">
|
||||
<div class="section-title">
|
||||
<span><i class='bx bx-user text-primary me-1'></i> Dane podstawowe & O mnie</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label-custom">Imię i Nazwisko</label>
|
||||
<input type="text" class="form-control-custom" value="Oskar Kaczmarek">
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label-custom">Tytuł zawodowy / Slogan</label>
|
||||
<input type="text" class="form-control-custom" value="Licencjonowany Pilot Wycieczek & Przewodnik">
|
||||
</div>
|
||||
|
||||
<div class="row g-2 mb-2">
|
||||
<div class="col-6">
|
||||
<label class="form-label-custom">Rok rozpoczęcia</label>
|
||||
<input type="number" id="startYearInput" class="form-control-custom" value="2019" min="1970" max="2026" oninput="updateExpYears();">
|
||||
<small class="text-primary fw-bold d-block mt-1" id="expCalculatedHint" style="font-size: 0.72rem;">
|
||||
<i class='bx bx-check'></i> Staż: 7 lat
|
||||
</small>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="form-label-custom">Miasto bazowe</label>
|
||||
<input type="text" class="form-control-custom" value="Warszawa / Rzym">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-1">
|
||||
<label class="form-label-custom">Opis autoprezentacji dla biur</label>
|
||||
<textarea class="form-control-custom" rows="3">Pasjonat kultury śródziemnomorskiej z wykształceniem historycznym. Od 2019 roku z sukcesami pilotuję wycieczki objazdowe po Włoszech, Hiszpanii i Bałkanach. Wyróżnia mnie opanowanie w sytuacjach kryzysowych, doskonały kontakt z grupą oraz płynna znajomość języka włoskiego i angielskiego.</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. Destynacje i Obsługiwane Kraje -->
|
||||
<div class="section-card mb-3" id="destynacje">
|
||||
<div class="section-title">
|
||||
<span><i class='bx bx-map-pin text-primary me-1'></i> Obsługiwane kraje</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap gap-1 mb-2" id="countryChips">
|
||||
<span class="chip-tag primary" onclick="this.classList.toggle('primary')">🇮🇹 Włochy</span>
|
||||
<span class="chip-tag primary" onclick="this.classList.toggle('primary')">🇬🇷 Grecja</span>
|
||||
<span class="chip-tag primary" onclick="this.classList.toggle('primary')">🇪🇸 Hiszpania</span>
|
||||
<span class="chip-tag primary" onclick="this.classList.toggle('primary')">🇭🇷 Chorwacja</span>
|
||||
<span class="chip-tag primary" onclick="this.classList.toggle('primary')">🇳🇴 Norwegia</span>
|
||||
<span class="chip-tag primary" onclick="this.classList.toggle('primary')">🇦🇹 Austria</span>
|
||||
<span class="chip-tag" onclick="this.classList.toggle('primary')">🇵🇹 Portugalia</span>
|
||||
<span class="chip-tag" onclick="this.classList.toggle('primary')">🇹🇷 Turcja</span>
|
||||
<span class="chip-tag" onclick="this.classList.toggle('primary')">🇫🇷 Francja</span>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" id="newCountryInput" class="form-control form-control-custom py-1" placeholder="Wpisz inny kraj...">
|
||||
<button class="btn btn-outline-primary py-1 px-3" type="button" onclick="addNewCountry();" style="border-radius: var(--radius-sm); margin-left: 6px; font-size: 0.78rem;">
|
||||
+ Dodaj
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4. Języki Obce -->
|
||||
<div class="section-card mb-3" id="jezyki">
|
||||
<div class="section-title">
|
||||
<span><i class='bx bx-chat text-primary me-1'></i> Języki</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column gap-2 mb-2">
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col-7">
|
||||
<input type="text" class="form-control-custom" value="Polski" readonly>
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<select class="form-select form-control-custom py-1">
|
||||
<option selected>Ojczysty</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col-7">
|
||||
<input type="text" class="form-control-custom" value="Angielski">
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<select class="form-select form-control-custom py-1">
|
||||
<option>B1 (Średni)</option>
|
||||
<option>B2 (Płynny)</option>
|
||||
<option selected>C1 (Biegły)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col-7">
|
||||
<input type="text" class="form-control-custom" value="Włoski">
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<select class="form-select form-control-custom py-1">
|
||||
<option>B1 (Średni)</option>
|
||||
<option selected>B2 (Płynny)</option>
|
||||
<option>C1 (Biegły)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-light w-100 rounded-3 py-1 text-muted" style="font-size: 0.76rem;" onclick="alert('Dodaj kolejny język');">
|
||||
+ Dodaj język
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 5. Tagi & Specjalizacje -->
|
||||
<div class="section-card mb-4" id="tagi">
|
||||
<div class="section-title">
|
||||
<span><i class='bx bx-purchase-tag text-primary me-1'></i> Tagi & Specjalizacje</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap gap-1">
|
||||
<span class="chip-tag primary" onclick="this.classList.toggle('primary')"><i class='bx bx-check'></i> Objazdówki</span>
|
||||
<span class="chip-tag primary" onclick="this.classList.toggle('primary')"><i class='bx bx-check'></i> Trekking & góry</span>
|
||||
<span class="chip-tag primary" onclick="this.classList.toggle('primary')"><i class='bx bx-check'></i> Przewodnik miejski</span>
|
||||
<span class="chip-tag primary" onclick="this.classList.toggle('primary')"><i class='bx bx-check'></i> Grupy szkolne</span>
|
||||
<span class="chip-tag primary" onclick="this.classList.toggle('primary')"><i class='bx bx-check'></i> VIP / Incentive</span>
|
||||
<span class="chip-tag" onclick="this.classList.toggle('primary')"><i class='bx bx-check'></i> Narty</span>
|
||||
<span class="chip-tag" onclick="this.classList.toggle('primary')"><i class='bx bx-check'></i> Rejsy</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Zapisz -->
|
||||
<div class="mb-4">
|
||||
<button type="button" class="btn-primary-custom" onclick="saveProfile();">
|
||||
<i class='bx bx-save'></i> Zapisz profil publiczny
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div><!-- /.app-content -->
|
||||
|
||||
<script>
|
||||
function updateExpYears() {
|
||||
const startYear = parseInt(document.getElementById('startYearInput').value, 10);
|
||||
const currentYear = new Date().getFullYear();
|
||||
const hint = document.getElementById('expCalculatedHint');
|
||||
if (!isNaN(startYear) && startYear > 1950 && startYear <= currentYear) {
|
||||
const years = currentYear - startYear;
|
||||
hint.innerHTML = `<i class='bx bx-check'></i> Staż: ${years} ${years === 1 ? 'rok' : (years < 5 ? 'lata' : 'lat')}`;
|
||||
hint.className = 'text-primary fw-bold d-block mt-1';
|
||||
} else {
|
||||
hint.innerHTML = `<i class='bx bx-info-circle'></i> Podaj prawidłowy rok`;
|
||||
hint.className = 'text-danger fw-bold d-block mt-1';
|
||||
}
|
||||
}
|
||||
|
||||
function toggleVisibility(checkbox) {
|
||||
const dot = document.getElementById('visibilityDot');
|
||||
const title = document.getElementById('visibilityTitle');
|
||||
if (checkbox.checked) {
|
||||
dot.className = 'status-indicator-dot active';
|
||||
title.innerHTML = 'Widoczny w bazie biur podróży';
|
||||
} else {
|
||||
dot.className = 'status-indicator-dot';
|
||||
title.innerHTML = 'Profil ukryty w bazie biur';
|
||||
}
|
||||
}
|
||||
|
||||
function saveProfile() {
|
||||
const toast = document.getElementById('saveToast');
|
||||
toast.classList.remove('d-none');
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
function addNewCountry() {
|
||||
const input = document.getElementById('newCountryInput');
|
||||
const val = input.value.trim();
|
||||
if (val) {
|
||||
const container = document.getElementById('countryChips');
|
||||
const span = document.createElement('span');
|
||||
span.className = 'chip-tag primary';
|
||||
span.innerHTML = '🌍 ' + val;
|
||||
span.onclick = function() { this.classList.toggle('primary'); };
|
||||
container.appendChild(span);
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
$pageTitle = 'TravelLeader - Opinie Biur Podróży';
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="app-content">
|
||||
|
||||
<!-- Pasek powrotu -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<a href="profile.php" class="text-decoration-none d-flex align-items-center gap-1 text-muted" style="font-size: 0.85rem; font-weight: 600;">
|
||||
<i class='bx bx-arrow-back fs-5'></i> Powrót do profilu
|
||||
</a>
|
||||
<span class="badge bg-light text-muted" style="font-size: 0.72rem;">Zweryfikowane przez Travel Manager</span>
|
||||
</div>
|
||||
|
||||
<!-- Podsumowanie ocen (Rating Summary Hero) -->
|
||||
<div class="section-card text-center mb-3">
|
||||
<div class="d-flex align-items-center justify-content-center gap-2 mb-1">
|
||||
<h2 class="fw-bold mb-0" style="color: var(--dark-navy); font-size: 2.2rem;">4.9</h2>
|
||||
<div class="text-start">
|
||||
<div class="text-warning" style="font-size: 1.1rem; line-height: 1;">
|
||||
<i class='bx bxs-star'></i><i class='bx bxs-star'></i><i class='bx bxs-star'></i><i class='bx bxs-star'></i><i class='bx bxs-star-half'></i>
|
||||
</div>
|
||||
<small class="text-muted" style="font-size: 0.72rem;">na podstawie 18 ocen</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-muted mb-3" style="font-size: 0.76rem;">100% biur poleca tego pilota na kolejne wyjazdy</div>
|
||||
|
||||
<!-- Rozbicie gwiazdek -->
|
||||
<div class="d-flex flex-column gap-1" style="max-width: 260px; margin: 0 auto; font-size: 0.72rem;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span style="width: 24px;">5★</span>
|
||||
<div class="progress flex-grow-1" style="height: 6px;">
|
||||
<div class="progress-bar bg-warning" style="width: 89%;"></div>
|
||||
</div>
|
||||
<span class="text-muted" style="width: 20px;">16</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span style="width: 24px;">4★</span>
|
||||
<div class="progress flex-grow-1" style="height: 6px;">
|
||||
<div class="progress-bar bg-warning" style="width: 11%;"></div>
|
||||
</div>
|
||||
<span class="text-muted" style="width: 20px;">2</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span style="width: 24px;">3★</span>
|
||||
<div class="progress flex-grow-1" style="height: 6px;">
|
||||
<div class="progress-bar bg-warning" style="width: 0%;"></div>
|
||||
</div>
|
||||
<span class="text-muted" style="width: 20px;">0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filtry opinii -->
|
||||
<div class="d-flex gap-2 overflow-x-auto pb-2 mb-3" style="white-space: nowrap;">
|
||||
<button class="nav-pill-item active" style="font-size: 0.78rem; padding: 5px 12px;">Wszystkie (18)</button>
|
||||
<button class="nav-pill-item" style="font-size: 0.78rem; padding: 5px 12px;">Włochy (12)</button>
|
||||
<button class="nav-pill-item" style="font-size: 0.78rem; padding: 5px 12px;">Hiszpania (4)</button>
|
||||
<button class="nav-pill-item" style="font-size: 0.78rem; padding: 5px 12px;">Grecja (2)</button>
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold mb-3" style="color: var(--dark-navy); font-size: 0.9rem;">Opinie od Biur Podróży</h6>
|
||||
|
||||
<!-- Opinia 1 (Z już dodaną odpowiedzią pilota) -->
|
||||
<div class="section-card mb-3">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center bg-primary-subtle text-primary fw-bold" style="width: 34px; height: 34px; font-size: 0.8rem;">
|
||||
AL
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="fw-bold mb-0" style="font-size: 0.86rem; color: var(--dark-navy);">Biuro Podróży Almatur</h6>
|
||||
<small class="text-muted" style="font-size: 0.7rem;">Wycieczka: Klasyczne Włochy • Maj 2026</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-warning-subtle text-warning fw-bold" style="font-size: 0.72rem;">
|
||||
<i class='bx bxs-star'></i> 5.0
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="mb-3 text-secondary" style="font-size: 0.82rem; line-height: 1.45;">
|
||||
"Pan Oskar prowadził 8-dniowy objazd po Włoszech. Klienci byli zachwyceni jego wiedzą i empatią. Brak jakichkolwiek problemów organizacyjnych. Bardzo chętnie zarezerwujemy kolejne terminy na jesień."
|
||||
</p>
|
||||
|
||||
<!-- Odpowiedź pilota (Już opublikowana) -->
|
||||
<div class="p-3 rounded-3" style="background-color: #f6f7fe; border-left: 3px solid var(--primary);">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
<i class='bx bx-reply text-primary'></i>
|
||||
<span class="fw-bold" style="font-size: 0.76rem; color: var(--primary);">Twoja odpowiedź jako pilot</span>
|
||||
</div>
|
||||
<small class="text-muted" style="font-size: 0.68rem;">3 dni temu</small>
|
||||
</div>
|
||||
<p class="mb-0 text-secondary" style="font-size: 0.78rem; line-height: 1.4;">
|
||||
Dziękuję serdecznie za zaufanie! Współpraca z koordynatorem trasy i grupą układała się wzorowo. Do zobaczenia na kolejnych wyjazdach!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Opinia 2 (Z możliwością dodania odpowiedzi przez formularz) -->
|
||||
<div class="section-card mb-3" id="reviewCard2">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center bg-info-subtle text-info fw-bold" style="width: 34px; height: 34px; font-size: 0.8rem;">
|
||||
LT
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="fw-bold mb-0" style="font-size: 0.86rem; color: var(--dark-navy);">Logos Tour Warszawa</h6>
|
||||
<small class="text-muted" style="font-size: 0.7rem;">Wycieczka: Andaluzja & Gibraltar • Kwiecień 2026</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-warning-subtle text-warning fw-bold" style="font-size: 0.72rem;">
|
||||
<i class='bx bxs-star'></i> 4.8
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="mb-2 text-secondary" style="font-size: 0.82rem; line-height: 1.45;">
|
||||
"Świetny kontakt telefoniczny i regularne raportowanie przebiegu trasy do centrali biura. Grupa bardzo zadowolona z realizacji programu mimo trudnych warunków pogodowych w górach."
|
||||
</p>
|
||||
|
||||
<!-- Przycisk / formularz odpowiedzi -->
|
||||
<div id="replyArea2">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary rounded-pill py-1 px-3 mt-1" style="font-size: 0.74rem;" onclick="toggleReplyBox(2);">
|
||||
<i class='bx bx-reply'></i> Odpowiedz na opinię
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="replyForm2" class="d-none mt-2 p-3 rounded-3" style="background-color: #f8f9fd; border: 1px solid var(--border-color);">
|
||||
<label class="form-label-custom mb-1" style="font-size: 0.76rem;">Napisz publiczną odpowiedź do biura:</label>
|
||||
<textarea id="replyText2" class="form-control-custom mb-2" rows="2" placeholder="Podziękuj za współpracę lub odnieś się do szczegółów wycieczki..."></textarea>
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn btn-sm btn-light rounded-pill py-1 px-3" style="font-size: 0.72rem;" onclick="toggleReplyBox(2);">Anuluj</button>
|
||||
<button type="button" class="btn btn-sm btn-primary rounded-pill py-1 px-3" style="font-size: 0.72rem;" onclick="publishReply(2);">Opublikuj odpowiedź</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Opinia 3 -->
|
||||
<div class="section-card mb-4" id="reviewCard3">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center bg-success-subtle text-success fw-bold" style="width: 34px; height: 34px; font-size: 0.8rem;">
|
||||
RT
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="fw-bold mb-0" style="font-size: 0.86rem; color: var(--dark-navy);">Rainbow Tours</h6>
|
||||
<small class="text-muted" style="font-size: 0.7rem;">Wycieczka: Sycylia w pigułce • Wrzesień 2025</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-warning-subtle text-warning fw-bold" style="font-size: 0.72rem;">
|
||||
<i class='bx bxs-star'></i> 5.0
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="mb-2 text-secondary" style="font-size: 0.82rem; line-height: 1.45;">
|
||||
"Wzorowy pilotaż i panowanie nad 45-osobową grupą. Zero opóźnień, kierowcy autokaru chwalili dyscyplinę i profesjonalizm pilota."
|
||||
</p>
|
||||
|
||||
<!-- Przycisk odpowiedzi -->
|
||||
<div id="replyArea3">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary rounded-pill py-1 px-3 mt-1" style="font-size: 0.74rem;" onclick="toggleReplyBox(3);">
|
||||
<i class='bx bx-reply'></i> Odpowiedz na opinię
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="replyForm3" class="d-none mt-2 p-3 rounded-3" style="background-color: #f8f9fd; border: 1px solid var(--border-color);">
|
||||
<label class="form-label-custom mb-1" style="font-size: 0.76rem;">Napisz publiczną odpowiedź do biura:</label>
|
||||
<textarea id="replyText3" class="form-control-custom mb-2" rows="2" placeholder="Podziękuj za współpracę..."></textarea>
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn btn-sm btn-light rounded-pill py-1 px-3" style="font-size: 0.72rem;" onclick="toggleReplyBox(3);">Anuluj</button>
|
||||
<button type="button" class="btn btn-sm btn-primary rounded-pill py-1 px-3" style="font-size: 0.72rem;" onclick="publishReply(3);">Opublikuj odpowiedź</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /.app-content -->
|
||||
|
||||
<script>
|
||||
function toggleReplyBox(id) {
|
||||
const form = document.getElementById('replyForm' + id);
|
||||
const area = document.getElementById('replyArea' + id);
|
||||
if (form.classList.contains('d-none')) {
|
||||
form.classList.remove('d-none');
|
||||
area.classList.add('d-none');
|
||||
} else {
|
||||
form.classList.add('d-none');
|
||||
area.classList.remove('d-none');
|
||||
}
|
||||
}
|
||||
|
||||
function publishReply(id) {
|
||||
const textInput = document.getElementById('replyText' + id);
|
||||
const val = textInput.value.trim();
|
||||
if (!val) {
|
||||
alert('Wpisz treść odpowiedzi przed publikacją.');
|
||||
return;
|
||||
}
|
||||
|
||||
const form = document.getElementById('replyForm' + id);
|
||||
form.classList.add('d-none');
|
||||
|
||||
const replyArea = document.getElementById('replyArea' + id);
|
||||
replyArea.innerHTML = `
|
||||
<div class="p-3 rounded-3 mt-2" style="background-color: #f6f7fe; border-left: 3px solid var(--primary);">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
<i class='bx bx-reply text-primary'></i>
|
||||
<span class="fw-bold" style="font-size: 0.76rem; color: var(--primary);">Twoja odpowiedź jako pilot</span>
|
||||
</div>
|
||||
<small class="text-muted" style="font-size: 0.68rem;">Przed chwilą</small>
|
||||
</div>
|
||||
<p class="mb-0 text-secondary" style="font-size: 0.78rem; line-height: 1.4;">${val}</p>
|
||||
</div>
|
||||
`;
|
||||
replyArea.classList.remove('d-none');
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
$enablePrototypeComments = true;
|
||||
$pageTitle = 'TravelLeader - Mój Profil Pilota';
|
||||
include 'header.php';
|
||||
|
||||
// Rok rozpoczęcia działalności jako pilot (z bazy danych / edycji)
|
||||
$startYear = 2019;
|
||||
$currentYear = (int)date('Y');
|
||||
$experienceYears = max(1, $currentYear - $startYear);
|
||||
?>
|
||||
|
||||
<div class="app-content">
|
||||
|
||||
<!-- Pasek nagłówka profilu z akcjami -->
|
||||
<!-- Pasek nagłówka profilu z akcjami i statusem bazy -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<div class="d-flex align-items-center gap-2 mb-1">
|
||||
<h5 class="fw-bold mb-0" style="color: var(--dark-navy);">Mój Profil</h5>
|
||||
<span class="d-inline-flex align-items-center gap-1 text-success fw-bold" style="font-size: 0.7rem; background: #ecfdf5; padding: 2px 8px; border-radius: 99px; border: 1px solid #a7f3d0;">
|
||||
<span style="width: 6px; height: 6px; background-color: #10b981; border-radius: 50%;"></span> Aktywny w bazie
|
||||
</span>
|
||||
</div>
|
||||
<small class="text-muted" style="font-size: 0.74rem;">Wizytówka widoczna dla biur podróży</small>
|
||||
</div>
|
||||
<a href="profile-edit.php" class="btn btn-sm btn-primary-custom py-2 px-3" style="width: auto; font-size: 0.78rem;">
|
||||
<i class='bx bx-edit-alt'></i> Edytuj
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Karta główna profilu (Hero - w 100% realne informacje pilota) -->
|
||||
<div class="profile-hero">
|
||||
<div class="profile-avatar-container">
|
||||
<div class="profile-avatar-img d-flex align-items-center justify-content-center" style="background: linear-gradient(135deg, #4f46e5, #7c3aed); color: white; font-size: 2.2rem; font-weight: 800;">
|
||||
OK
|
||||
</div>
|
||||
<div class="profile-verified-badge" title="Zweryfikowany pilot TravelLeader">
|
||||
<i class='bx bx-check'></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="profile-name">Oskar Kaczmarek</h4>
|
||||
<div class="profile-tagline">
|
||||
<i class='bx bx-compass text-primary'></i> Licencjonowany Pilot Wycieczek & Przewodnik
|
||||
</div>
|
||||
|
||||
<!-- Średnia ocen i liczba opinii od biur podróży -> przejście do nowego widoku profile-reviews.php -->
|
||||
<a href="profile-reviews.php" class="rating-badge-pill text-decoration-none" title="Zobacz opinie i referencje biur">
|
||||
<i class='bx bxs-star'></i>
|
||||
<span>4.9 / 5.0</span>
|
||||
<span class="text-muted fw-normal">(18 opinii od 6 biur)</span>
|
||||
<i class='bx bx-chevron-right text-muted'></i>
|
||||
</a>
|
||||
|
||||
<!-- Szybkie statystyki (staż wyliczany automatycznie z roku rozpoczęcia) -->
|
||||
<div class="row g-2 mt-2 pt-2 border-top">
|
||||
<div class="col-4">
|
||||
<div class="fw-bold" style="font-size: 1.05rem; color: var(--dark-navy);"><?= $experienceYears ?> lat</div>
|
||||
<div class="text-muted" style="font-size: 0.7rem;">Staż (od <?= $startYear ?>)</div>
|
||||
</div>
|
||||
<div class="col-4 border-start border-end">
|
||||
<div class="fw-bold" style="font-size: 1.05rem; color: var(--primary);">42</div>
|
||||
<div class="text-muted" style="font-size: 0.7rem;">Zrealizowanych</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div class="fw-bold" style="font-size: 1.05rem; color: #10b981;">100%</div>
|
||||
<div class="text-muted" style="font-size: 0.7rem;">Dyspozycyjność</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sekcja: Autoprezentacja / O mnie -->
|
||||
<div class="section-card">
|
||||
<div class="section-title">
|
||||
<span><i class='bx bx-user-voice text-primary me-1'></i> O mnie</span>
|
||||
<a href="profile-edit.php#bio" class="text-muted" style="font-size: 0.75rem;"><i class='bx bx-pencil'></i></a>
|
||||
</div>
|
||||
<p class="mb-0 text-secondary" style="font-size: 0.84rem; line-height: 1.5;">
|
||||
Pasjonat kultury śródziemnomorskiej z wykształceniem historycznym. Od <?= $startYear ?> roku z sukcesami pilotuję wycieczki objazdowe po Włoszech, Hiszpanii i Bałkanach. Wyróżnia mnie opanowanie w sytuacjach kryzysowych, doskonały kontakt z grupą oraz płynna znajomość języka włoskiego i angielskiego.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Sekcja: Wideo-Wizytówka & Multimedia -->
|
||||
<div class="section-card">
|
||||
<div class="section-title">
|
||||
<span><i class='bx bx-video text-primary me-1'></i> Wideo-Wizytówka i Zdjęcia</span>
|
||||
<span class="badge bg-label-primary rounded-pill" style="font-size: 0.7rem;">Wideo + 4 foto</span>
|
||||
</div>
|
||||
|
||||
<!-- Odtwarzacz wideo autoprezentacji -->
|
||||
<div class="video-card-preview" onclick="alert('Odtwarzanie wideo-autoprezentacji pilota (1:15 min). Biuro podróży może posłuchać głosu i prezencji pilota przed wysłaniem zlecenia.');">
|
||||
<div class="video-play-btn">
|
||||
<i class='bx bx-play' style="margin-left: 3px;"></i>
|
||||
</div>
|
||||
<div class="fw-bold" style="font-size: 0.85rem;">Autoprezentacja dla biur (1:15 min)</div>
|
||||
<small style="font-size: 0.72rem; opacity: 0.8;">Kliknij, aby odtworzyć próbkę</small>
|
||||
</div>
|
||||
|
||||
<!-- Siatka zdjęć z tras -->
|
||||
<div class="media-grid mt-2">
|
||||
<div class="media-thumbnail d-flex align-items-center justify-content-center" style="background: linear-gradient(135deg, #60a5fa, #3b82f6); color: white;">
|
||||
<i class='bx bx-landscape fs-2'></i>
|
||||
</div>
|
||||
<div class="media-thumbnail d-flex align-items-center justify-content-center" style="background: linear-gradient(135deg, #f59e0b, #d97706); color: white;">
|
||||
<i class='bx bx-camera fs-2'></i>
|
||||
</div>
|
||||
<div class="media-thumbnail d-flex align-items-center justify-content-center" style="background: linear-gradient(135deg, #10b981, #059669); color: white;">
|
||||
<i class='bx bx-group fs-2'></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sekcja: Obsługiwane Kraje i Destynacje -->
|
||||
<div class="section-card">
|
||||
<div class="section-title">
|
||||
<span><i class='bx bx-globe text-primary me-1'></i> Kraje & Destynacje</span>
|
||||
<a href="profile-edit.php#destynacje" class="text-muted" style="font-size: 0.75rem;"><i class='bx bx-pencil'></i></a>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap">
|
||||
<span class="chip-tag primary">🇮🇹 Włochy (Ekspert)</span>
|
||||
<span class="chip-tag primary">🇬🇷 Grecja & Cyklady</span>
|
||||
<span class="chip-tag primary">🇪🇸 Hiszpania & Andaluzja</span>
|
||||
<span class="chip-tag">🇭🇷 Chorwacja</span>
|
||||
<span class="chip-tag">🇳🇴 Norwegia (Fiordy)</span>
|
||||
<span class="chip-tag">🇦🇹 Austria</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sekcja: Języki Obce -->
|
||||
<div class="section-card">
|
||||
<div class="section-title">
|
||||
<span><i class='bx bx-message-dots text-primary me-1'></i> Języki</span>
|
||||
<a href="profile-edit.php#jezyki" class="text-muted" style="font-size: 0.75rem;"><i class='bx bx-pencil'></i></a>
|
||||
</div>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<div class="d-flex justify-content-between align-items-center p-2 rounded-3" style="background: #f8f9fd;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span style="font-size: 1.1rem;">🇵🇱</span>
|
||||
<span class="fw-bold" style="font-size: 0.84rem;">Polski</span>
|
||||
</div>
|
||||
<span class="badge bg-light text-dark" style="font-size: 0.72rem;">Ojczysty</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center p-2 rounded-3" style="background: #f8f9fd;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span style="font-size: 1.1rem;">🇬🇧</span>
|
||||
<span class="fw-bold" style="font-size: 0.84rem;">Angielski</span>
|
||||
</div>
|
||||
<span class="badge bg-primary-subtle text-primary" style="font-size: 0.72rem;">C1 (Biegły)</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center p-2 rounded-3" style="background: #f8f9fd;">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span style="font-size: 1.1rem;">🇮🇹</span>
|
||||
<span class="fw-bold" style="font-size: 0.84rem;">Włoski</span>
|
||||
</div>
|
||||
<span class="badge bg-primary-subtle text-primary" style="font-size: 0.72rem;">B2 (Płynny)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sekcja: Tagi & Specjalizacje -->
|
||||
<div class="section-card">
|
||||
<div class="section-title">
|
||||
<span><i class='bx bx-purchase-tag-alt text-primary me-1'></i> Specjalizacje & Tagi</span>
|
||||
<a href="profile-edit.php#tagi" class="text-muted" style="font-size: 0.75rem;"><i class='bx bx-pencil'></i></a>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap">
|
||||
<span class="chip-tag"><i class='bx bx-bus'></i> Wycieczki objazdowe</span>
|
||||
<span class="chip-tag"><i class='bx bx-walk'></i> Trekking & góry</span>
|
||||
<span class="chip-tag"><i class='bx bx-buildings'></i> Przewodnik miejski (Rzym, Florencja)</span>
|
||||
<span class="chip-tag"><i class='bx bx-book-open'></i> Grupy młodzieżowe / szkolne</span>
|
||||
<span class="chip-tag"><i class='bx bx-diamond'></i> Wyjazdy VIP / Incentive</span>
|
||||
<span class="chip-tag"><i class='bx bx-wine'></i> Enoturystyka & kulinaria</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sekcja: Kursy & Certyfikaty -->
|
||||
<div class="section-card">
|
||||
<div class="section-title">
|
||||
<span><i class='bx bx-award text-primary me-1'></i> Certyfikaty i Szkolenia</span>
|
||||
<a href="courses.php" class="text-primary fw-bold" style="font-size: 0.75rem;">Zobacz ofertę szkoleń →</a>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-3 p-2 rounded-3 mb-2" style="background: #f8f9fd;">
|
||||
<div class="d-flex align-items-center justify-content-center rounded-circle bg-success-subtle text-success" style="width: 36px; height: 36px;">
|
||||
<i class='bx bx-badge-check fs-4'></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="fw-bold" style="font-size: 0.84rem; color: var(--dark-navy);">Superpilot Academy 2026</div>
|
||||
<small class="text-muted" style="font-size: 0.72rem;">Certyfikat Pilotażu Europy Południowej</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-3 p-2 rounded-3" style="background: #f8f9fd;">
|
||||
<div class="d-flex align-items-center justify-content-center rounded-circle bg-primary-subtle text-primary" style="width: 36px; height: 36px;">
|
||||
<i class='bx bx-plus-medical fs-5'></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<div class="fw-bold" style="font-size: 0.84rem; color: var(--dark-navy);">Kwalifikowana Pierwsza Pomoc (KPP)</div>
|
||||
<small class="text-muted" style="font-size: 0.72rem;">Ważny do: 04.2028</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Przycisk edycji na dole -->
|
||||
<div class="mb-4">
|
||||
<a href="profile-edit.php" class="btn-primary-custom">
|
||||
<i class='bx bx-edit'></i> Przejdź do edycji danych profilu
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div><!-- /.app-content -->
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
@@ -109,15 +109,33 @@ include '../../header-travel.php';
|
||||
</div>
|
||||
<div class="card-body p-3">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small mb-1">Dostawca / NIP</label>
|
||||
<input type="text" class="form-control" placeholder="Szukaj...">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small mb-1">Numer faktury</label>
|
||||
<input type="text" class="form-control" placeholder="Wpisz numer">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small mb-1">Rodzaj faktury</label>
|
||||
<select class="form-select">
|
||||
<option value="">Wszystkie</option>
|
||||
<option value="podstawowa">Podstawowa</option>
|
||||
<option value="zaliczkowa">Zaliczkowa</option>
|
||||
<option value="rozliczeniowa">Rozliczeniowa</option>
|
||||
<option value="uproszczona">Uproszczona</option>
|
||||
<option value="korygujaca">Korygująca</option>
|
||||
<option value="korygujaca_zaliczkowa">Korygująca zaliczkową</option>
|
||||
<option value="korygujaca_rozliczeniowa">Korygująca rozliczeniową</option>
|
||||
<option value="podstawowa_pef">Podstawowa PEF</option>
|
||||
<option value="specjalizowana_pef">Specjalizowana PEF</option>
|
||||
<option value="korygujaca_pef">Korygująca PEF</option>
|
||||
<option value="pierwotna_rr">Pierwotna RR</option>
|
||||
<option value="korygujaca_rr">Korygująca RR</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small mb-1">Status powiązania</label>
|
||||
<select class="form-select">
|
||||
<option value="">Wszystkie</option>
|
||||
@@ -511,6 +529,18 @@ include '../../header-travel.php';
|
||||
<div class="tab-pane fade" id="navs-faktury" role="tabpanel">
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header border-bottom d-flex justify-content-between align-items-center py-3">
|
||||
<h5 class="card-title mb-0">Baza faktur</h5>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="bulkOperationsDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bx bx-check-double me-1"></i> Operacje masowe
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="bulkOperationsDropdown">
|
||||
<h6 class="dropdown-header text-uppercase ps-3">Finanse</h6>
|
||||
<li><a class="dropdown-item" href="javascript:void(0);" data-bs-toggle="modal" data-bs-target="#pekaoConnectModal"><i class="bx bx-transfer-alt me-2"></i> Prześlij do PeKaO Connect</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive text-nowrap">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-light">
|
||||
@@ -534,7 +564,7 @@ include '../../header-travel.php';
|
||||
<i class="bx bxs-file-pdf text-danger fs-4 me-2"></i>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="fw-semibold text-dark">FV/2026/05/1234</span>
|
||||
<small class="text-muted">Faktura VAT</small>
|
||||
<small class="text-muted">Podstawowa</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -558,7 +588,7 @@ include '../../header-travel.php';
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="tooltip"
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="modal" data-bs-target="#invoicePreviewModal" data-bs-toggle="tooltip"
|
||||
title="Podgląd dokumentu"><i class="bx bx-show"></i></button>
|
||||
<button class="btn btn-sm btn-primary ms-1" data-bs-toggle="modal"
|
||||
data-bs-target="#modalLinkExistingInvoice">Połącz ze zleceniem</button>
|
||||
@@ -572,7 +602,7 @@ include '../../header-travel.php';
|
||||
<i class="bx bx-receipt text-success fs-4 me-2"></i>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="fw-semibold text-dark">F-2026-999</span>
|
||||
<small class="text-muted">Faktura VAT z KSeF</small>
|
||||
<small class="text-muted">Podstawowa</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -599,7 +629,7 @@ include '../../header-travel.php';
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="tooltip"
|
||||
title="Szczegóły rozksięgowania"><i class="bx bx-list-ul"></i></button>
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary"><i
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="modal" data-bs-target="#invoicePreviewModal"><i
|
||||
class="bx bx-show"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -612,7 +642,7 @@ include '../../header-travel.php';
|
||||
title="Skan z aplikacji pilota"></i>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="fw-semibold text-dark">Paragon/FV 12</span>
|
||||
<small class="text-muted">Faktura zaliczkowa</small>
|
||||
<small class="text-muted">Zaliczkowa</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -639,7 +669,7 @@ include '../../header-travel.php';
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="tooltip"
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="modal" data-bs-target="#invoicePreviewModal" data-bs-toggle="tooltip"
|
||||
title="Podgląd skanu"><i class="bx bx-show"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -675,7 +705,7 @@ include '../../header-travel.php';
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="tooltip"
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="modal" data-bs-target="#invoicePreviewModal" data-bs-toggle="tooltip"
|
||||
title="Podgląd dokumentu"><i class="bx bx-show"></i></button>
|
||||
<button class="btn btn-sm btn-primary ms-1" data-bs-toggle="modal"
|
||||
data-bs-target="#modalLinkExistingInvoice">Połącz ze zleceniem</button>
|
||||
@@ -689,7 +719,7 @@ include '../../header-travel.php';
|
||||
<i class="bx bx-receipt text-primary fs-4 me-2"></i>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="fw-semibold text-dark">FVZ/2026/04/311</span>
|
||||
<small class="text-muted">Faktura zaliczkowa</small>
|
||||
<small class="text-muted">Zaliczkowa</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -713,7 +743,7 @@ include '../../header-travel.php';
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="tooltip"
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="modal" data-bs-target="#invoicePreviewModal" data-bs-toggle="tooltip"
|
||||
title="Podgląd dokumentu"><i class="bx bx-show"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -725,7 +755,7 @@ include '../../header-travel.php';
|
||||
<i class="bx bxs-file-pdf text-info fs-4 me-2"></i>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="fw-semibold text-dark">KSEF/2026/05/0042</span>
|
||||
<small class="text-muted">Faktura VAT z KSeF</small>
|
||||
<small class="text-muted">Podstawowa</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -752,7 +782,7 @@ include '../../header-travel.php';
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="tooltip"
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="modal" data-bs-target="#invoicePreviewModal" data-bs-toggle="tooltip"
|
||||
title="Podgląd dokumentu"><i class="bx bx-show"></i></button>
|
||||
<button class="btn btn-sm btn-primary ms-1" data-bs-toggle="modal"
|
||||
data-bs-target="#modalLinkExistingInvoice">Połącz ze zleceniem</button>
|
||||
@@ -793,7 +823,7 @@ include '../../header-travel.php';
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="tooltip"
|
||||
title="Szczegóły rozksięgowania"><i class="bx bx-list-ul"></i></button>
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary"><i
|
||||
<button class="btn btn-sm btn-icon btn-text-secondary" data-bs-toggle="modal" data-bs-target="#invoicePreviewModal"><i
|
||||
class="bx bx-show"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -811,6 +841,265 @@ include '../../header-travel.php';
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Modal: PeKaO Connect Bulk Transfer -->
|
||||
<div class="modal fade" id="pekaoConnectModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header border-bottom py-3">
|
||||
<h5 class="modal-title" id="pekaoConnectModalTitle"><i class="bx bx-transfer-alt text-primary me-2"></i> Przesyłanie do PeKaO Connect</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<div class="alert alert-info d-flex" role="alert">
|
||||
<span class="badge badge-center rounded-pill bg-info border-label-info p-3 me-3"><i class="bx bx-info-circle fs-5"></i></span>
|
||||
<div class="d-flex flex-column ps-1">
|
||||
<h6 class="alert-heading d-flex align-items-center mb-1">Wymagana autoryzacja w banku</h6>
|
||||
<span>Zaznaczone faktury zostaną przesłane i dodane do koszyka w systemie PeKaO Connect. Aby zrealizować płatności, musisz następnie zalogować się do bankowości elektronicznej i zautoryzować przygotowaną paczkę przelewów.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card bg-lighter shadow-none border border-dashed mb-0">
|
||||
<div class="card-body p-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="text-muted">Wybranych faktur:</span>
|
||||
<span class="fw-bold">4</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="text-muted">Łączna kwota paczki:</span>
|
||||
<span class="fw-bold fs-5 text-primary">12 450,00 PLN</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer border-top py-3">
|
||||
<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"><i class="bx bx-send me-1"></i> Utwórz paczkę</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Invoice Preview -->
|
||||
<div class="modal fade" id="invoicePreviewModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable" role="document">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header border-bottom py-3 d-flex justify-content-between align-items-center">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<h4 class="modal-title fw-bold mb-0">FV/2026/02/001</h4>
|
||||
<span class="badge bg-label-success fs-6">Opłacona</span>
|
||||
<span class="badge bg-label-secondary fs-6">Podstawowa</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
|
||||
<button type="button" class="btn btn-icon btn-label-secondary" data-bs-toggle="tooltip"
|
||||
title="Drukuj PDF">
|
||||
<i class="bx bx-printer"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-primary d-flex align-items-center">
|
||||
<i class="bx bx-link-external me-1"></i> Otwórz w invoice.travel
|
||||
</button>
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary" type="button" id="dropdownInvoiceActions"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Więcej akcji <i class="bx bx-chevron-down ms-1"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="dropdownInvoiceActions">
|
||||
<li><button class="dropdown-item"><i class="bx bx-edit-alt me-2"></i> Edytuj</button></li>
|
||||
<div class="dropdown-divider"></div>
|
||||
<li><button class="dropdown-item text-danger"><i class="bx bx-trash me-2"></i> Usuń fakturę
|
||||
</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn-close ms-2" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-body bg-lighter p-4">
|
||||
|
||||
<div class="row g-4 mb-4">
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card h-100 shadow-sm border-0">
|
||||
<div class="card-body p-4">
|
||||
<h6 class="card-title m-0 mb-3"><i class="bx bx-store me-2"></i>Sprzedawca</h6>
|
||||
<h6 class="mb-1">OVH Sp. z o.o.</h6>
|
||||
<p class="mb-1 mt-2">ul. Swobodna 1</p>
|
||||
<p class="mb-2">50-088 Wrocław, Polska</p>
|
||||
<span class="badge bg-label-secondary">NIP: 899 25 20 556</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card h-100 shadow-sm border-0">
|
||||
<div class="card-body p-4">
|
||||
<h6 class="card-title m-0 mb-3"><i class="bx bx-user me-2"></i>Nabywca</h6>
|
||||
<h6 class="mb-1">MARIUSZ BANACZYK "BURGER BAR"</h6>
|
||||
<p class="mb-1 text-muted small">KARCZMA BIESIADA, KULTOWA ZAPIEKANKA</p>
|
||||
<p class="mb-1 mt-2">ul. Ofiar Katynia 2</p>
|
||||
<p class="mb-2">37-450 Stalowa Wola, Polska</p>
|
||||
<span class="badge bg-label-secondary">NIP: 865 000 14 51</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card h-100 shadow-sm border-0 bg-white">
|
||||
<div class="card-body p-4">
|
||||
<h6 class="card-title m-0 mb-3"><i class="bx bx-info-circle me-2"></i>Szczegóły</h6>
|
||||
<table class="table table-borderless table-sm m-0 px-0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-muted ps-0 py-1">Rodzaj faktury:</td>
|
||||
<td class="fw-medium text-end pe-0 py-1">Podstawowa</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted ps-0 py-1">Data zakupu:</td>
|
||||
<td class="fw-medium text-end pe-0 py-1">2026-02-15</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted ps-0 py-1">Termin płatności:</td>
|
||||
<td class="fw-bold text-end pe-0 py-1">2026-02-22</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted ps-0 py-1">Kategoria kosztu:</td>
|
||||
<td class="fw-medium text-end pe-0 py-1">Usługi obce</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted ps-0 py-1">Metoda:</td>
|
||||
<td class="fw-medium text-end pe-0 py-1">Przelew</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted ps-0 py-1">Waluta / Język:</td>
|
||||
<td class="fw-medium text-end pe-0 py-1">PLN / Polski</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="table-responsive text-nowrap">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th style="width: 50px;">Lp</th>
|
||||
<th>Towar / Usługa</th>
|
||||
<th>J.m.</th>
|
||||
<th class="text-center">Ilość</th>
|
||||
<th class="text-end">Cena netto</th>
|
||||
<th class="text-end">Cena brutto</th>
|
||||
<th class="text-center">VAT</th>
|
||||
<th class="text-end">Wartość netto</th>
|
||||
<th class="text-end">Wartość brutto</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-muted">1</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="fw-medium">Hosting i domeny</span>
|
||||
<small class="text-muted">Kod: -</small>
|
||||
</div>
|
||||
</td>
|
||||
<td>szt.</td>
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-end">100,00 zł</td>
|
||||
<td class="text-end">123,00 zł</td>
|
||||
<td class="text-center"><span class="badge bg-label-secondary">23%</span></td>
|
||||
<td class="text-end fw-medium">100,00 zł</td>
|
||||
<td class="text-end fw-bold text-heading">123,00 zł</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4 g-4">
|
||||
<div class="col-md-7">
|
||||
<div class="card shadow-sm border-0 h-100 bg-white p-4">
|
||||
<h6 class="text-uppercase text-muted fw-bold mb-3 border-bottom pb-2">Dane do płatności</h6>
|
||||
<div class="mb-4">
|
||||
<p class="mb-1 text-body"><strong class="text-muted fw-medium me-2">Bank:</strong> mBank S.A.</p>
|
||||
<p class="mb-0 text-body d-flex align-items-center">
|
||||
<strong class="text-muted fw-medium me-2">Rachunek:</strong>
|
||||
<span class="font-monospace">34 1140 2004 0000 3002 0100 5005</span>
|
||||
<button class="btn btn-sm btn-icon text-secondary ms-2 p-0" data-bs-toggle="tooltip" title="Kopiuj rachunek"><i class="bx bx-copy"></i></button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h6 class="text-uppercase text-muted fw-bold mb-3 border-bottom pb-2">Opis zdarzenia</h6>
|
||||
<p class="mb-0 text-body">
|
||||
Opłata roczna za usługi hostingowe dla domeny
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-5">
|
||||
<div class="card shadow-sm border-0 h-100 bg-white p-4">
|
||||
<h6 class="text-uppercase text-muted fw-bold mb-3 border-bottom pb-2">Suma</h6>
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<span class="text-muted">Wartość netto:</span>
|
||||
<span class="fw-medium">100,00 zł</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-3 border-bottom pb-3">
|
||||
<span class="text-muted">Wartość VAT:</span>
|
||||
<span class="fw-medium">23,00 zł</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="fs-5 text-body">Wartość brutto:</span>
|
||||
<span class="fs-4 fw-bold text-primary">123,00 zł</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between text-success">
|
||||
<span>Zapłacono:</span>
|
||||
<span class="fw-bold">123,00 zł</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between text-muted mt-1 small">
|
||||
<span>Pozostało do zapłaty:</span>
|
||||
<span>0,00 zł</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-header bg-white border-bottom py-3">
|
||||
<h6 class="card-title m-0"><i class="bx bx-paperclip me-2"></i>Załączniki</h6>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<div class="d-flex flex-wrap gap-3">
|
||||
<div
|
||||
class="border rounded p-3 d-flex align-items-center justify-content-between w-100 mb-2">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bx bxs-file-pdf fs-2 text-danger me-3"></i>
|
||||
<div>
|
||||
<h6 class="mb-0">FZ2026_02_001.pdf</h6>
|
||||
<small class="text-muted">1.2 MB • Wgrano: 2026-02-15 10:23</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-icon btn-sm btn-label-secondary"><i
|
||||
class="bx bx-show"></i></button>
|
||||
<button class="btn btn-icon btn-sm btn-label-primary"><i
|
||||
class="bx bx-download"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="modalLinkedInvoicePreview" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content border-0 shadow-lg">
|
||||
@@ -824,7 +1113,7 @@ include '../../header-travel.php';
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<div class="small text-muted">Numer faktury</div>
|
||||
<div class="fw-semibold">FV/2026/05/1234</div>
|
||||
<div class="fw-semibold d-flex align-items-center gap-2">FV/2026/05/1234 <span class="badge bg-label-secondary" style="font-size: 0.7rem;">Podstawowa</span></div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="small text-muted">Dostawca</div>
|
||||
@@ -1101,13 +1390,30 @@ include '../../header-travel.php';
|
||||
</div>
|
||||
<div class="card-body pt-3 pb-2">
|
||||
<div class="row g-2">
|
||||
<div class="col-sm-6 mb-2">
|
||||
<div class="col-sm-4 mb-2">
|
||||
<label class="form-label small mb-1">Nr faktury <span
|
||||
class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control form-control-sm"
|
||||
value="FV/2026/05/123">
|
||||
</div>
|
||||
<div class="col-sm-6 mb-2">
|
||||
<div class="col-sm-4 mb-2">
|
||||
<label class="form-label small mb-1">Rodzaj faktury</label>
|
||||
<select class="form-select form-select-sm">
|
||||
<option value="podstawowa" selected>Podstawowa</option>
|
||||
<option value="zaliczkowa">Zaliczkowa</option>
|
||||
<option value="rozliczeniowa">Rozliczeniowa</option>
|
||||
<option value="uproszczona">Uproszczona</option>
|
||||
<option value="korygujaca">Korygująca</option>
|
||||
<option value="korygujaca_zaliczkowa">Koryg. zaliczkową</option>
|
||||
<option value="korygujaca_rozliczeniowa">Koryg. rozliczeniową</option>
|
||||
<option value="podstawowa_pef">Podstawowa PEF</option>
|
||||
<option value="specjalizowana_pef">Specjalizowana PEF</option>
|
||||
<option value="korygujaca_pef">Korygująca PEF</option>
|
||||
<option value="pierwotna_rr">Pierwotna RR</option>
|
||||
<option value="korygujaca_rr">Korygująca RR</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-2">
|
||||
<label class="form-label small mb-1">Data wystawienia <span
|
||||
class="text-danger">*</span></label>
|
||||
<input type="date" class="form-control form-control-sm"
|
||||
|
||||
@@ -31,6 +31,13 @@ $systemPages = ['app-dictionary.php', 'app-template.php', 'app-employee.php', 'a
|
||||
<div data-i18n="Dostawcy usług">Dostawcy usług</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<a href="../superpilot-web/index.php" class="menu-link text-primary fw-semibold">
|
||||
<i class="menu-icon tf-icons bx bx-compass text-primary"></i>
|
||||
<div data-i18n="Baza Pilotów">Baza Pilotów Superpilot</div>
|
||||
<span class="badge bg-label-primary rounded-pill ms-auto" style="font-size: 0.65rem;">NOWOŚĆ</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="menu-item <?php echo in_array($currentPage, $clientsPages, true) ? 'active open' : ''; ?>">
|
||||
<a href="javascript:void(0);" class="menu-link menu-toggle">
|
||||
|
||||