Author SHA1 Message Date
oskarn d17cba2bbb poprawki dla bazy pilotów web, modul zaproszen w apce mobile 2026-09-21 16:17:27 +02:00
53 changed files with 2291 additions and 3560 deletions
-17
View File
@@ -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
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

@@ -461,6 +461,14 @@ body {
position: relative;
}
.pilot-offer-avatar .pilot-avatar-img {
width: 100%;
height: 100%;
border-radius: 14px;
object-fit: cover;
display: block;
}
.pilot-offer-avatar .verified-badge-mini {
position: absolute;
bottom: -3px;
@@ -475,6 +483,36 @@ body {
justify-content: center;
font-size: 0.7rem;
border: 2px solid #ffffff;
z-index: 2;
}
.pilot-card-gallery-preview {
display: flex;
align-items: center;
gap: 6px;
margin-top: 8px;
}
.pilot-card-gallery-preview img {
width: 60px;
height: 40px;
border-radius: 6px;
object-fit: cover;
border: 1px solid var(--border-color);
transition: transform 0.15s ease, box-shadow 0.15s ease;
cursor: pointer;
}
.pilot-card-gallery-preview img:hover {
transform: scale(1.08);
box-shadow: var(--shadow-subtle);
}
.pilot-card-gallery-preview .gallery-more-badge {
font-size: 0.7rem;
color: var(--text-muted);
font-weight: 600;
margin-left: 2px;
}
.pilot-offer-content {
@@ -771,6 +809,14 @@ body {
box-shadow: 0 4px 12px rgba(92, 96, 245, 0.2);
}
.profile-avatar-large .profile-avatar-img {
width: 100%;
height: 100%;
border-radius: 18px;
object-fit: cover;
display: block;
}
.profile-avatar-large .large-verified-badge {
position: absolute;
bottom: -3px;
@@ -852,11 +898,11 @@ body {
}
.profile-stats-strip {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.85rem;
padding-top: 1.25rem;
margin-top: 1.25rem;
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
padding-top: 1rem;
margin-top: 1rem;
border-top: 1px solid var(--border-subtle);
}
@@ -864,22 +910,23 @@ body {
background: #f8f9fd;
border: 1px solid var(--border-color);
border-radius: var(--radius-sm);
padding: 0.75rem 1rem;
padding: 0.5rem 0.9rem;
display: flex;
flex-direction: column;
align-items: baseline;
gap: 0.55rem;
}
.profile-stat-box .stat-val {
font-size: 1.15rem;
font-size: 0.92rem;
font-weight: 700;
color: var(--dark-navy);
line-height: 1.2;
white-space: nowrap;
}
.profile-stat-box .stat-lbl {
font-size: 0.74rem;
font-size: 0.75rem;
color: var(--text-muted);
margin-top: 2px;
}
/* Karty sekcji profilu */
@@ -934,10 +981,10 @@ body {
/* Wideo i Zdjęcia */
.profile-video-box {
position: relative;
border-radius: var(--radius-md);
border-radius: var(--radius-sm);
overflow: hidden;
height: 200px;
background: linear-gradient(135deg, #1e2244 0%, #0f172a 100%);
height: 160px;
background: linear-gradient(135deg, #1e2244 0%, #111827 100%);
cursor: pointer;
display: flex;
align-items: center;
@@ -952,15 +999,15 @@ body {
}
.profile-video-play-btn {
width: 52px;
height: 52px;
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--primary);
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.6rem;
font-size: 1.4rem;
box-shadow: 0 4px 14px rgba(92, 96, 245, 0.4);
transition: transform 0.15s ease;
}
@@ -969,15 +1016,56 @@ body {
transform: scale(1.08);
}
.profile-photos-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
margin-top: 1rem;
.pilot-gallery-filters {
display: flex;
align-items: center;
gap: 5px;
flex-wrap: wrap;
}
.gallery-filter-btn {
background: #f1f3f9;
color: var(--text-body);
border: 1px solid var(--border-color);
font-size: 0.74rem;
font-weight: 600;
padding: 3px 10px;
border-radius: var(--radius-full);
transition: all 0.15s ease;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 4px;
line-height: 1.4;
}
.gallery-filter-btn:hover {
background: #e6e9f5;
color: var(--dark-navy);
}
.gallery-filter-btn.active {
background: var(--primary);
color: #ffffff;
border-color: var(--primary);
box-shadow: 0 2px 6px rgba(92, 96, 245, 0.25);
}
.gallery-filter-btn .badge-count {
font-size: 0.68rem;
opacity: 0.85;
}
.gallery-photo-item {
transition: transform 0.2s ease, opacity 0.2s ease;
}
.gallery-photo-item.is-hidden {
display: none !important;
}
.profile-photo-thumb {
height: 85px;
height: 94px;
border-radius: var(--radius-sm);
overflow: hidden;
cursor: pointer;
@@ -985,24 +1073,201 @@ body {
border: 1px solid var(--border-color);
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-weight: 600;
font-size: 0.78rem;
flex-direction: column;
justify-content: flex-end;
padding: 8px 10px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
}
.profile-photo-thumb:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-hover);
border-color: var(--primary-border);
}
.profile-photo-thumb .photo-tag-title {
font-size: 0.76rem;
font-weight: 700;
color: #ffffff;
line-height: 1.2;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
.profile-photo-thumb .photo-tag-sub {
font-size: 0.66rem;
color: rgba(255, 255, 255, 0.85);
line-height: 1.2;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
.profile-photo-thumb .photo-zoom-hint {
position: absolute;
top: 8px;
right: 8px;
width: 26px;
height: 26px;
border-radius: 50%;
background: rgba(15, 23, 42, 0.6);
backdrop-filter: blur(4px);
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.82rem;
opacity: 0;
transition: opacity 0.2s ease, transform 0.2s ease;
}
.profile-photo-thumb:hover .photo-zoom-hint {
opacity: 1;
transform: scale(1.05);
}
/* Modal Zaawansowanej Galerii (Lightbox Pro) */
.gallery-modal-content {
background: #0f172a;
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 16px;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.6);
overflow: hidden;
}
.modal-gallery-filters {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.modal-filter-btn {
background: rgba(255, 255, 255, 0.08);
color: #94a3b8;
border: 1px solid rgba(255, 255, 255, 0.12);
font-size: 0.76rem;
font-weight: 600;
padding: 4px 12px;
border-radius: 50px;
transition: all 0.2s ease;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 4px;
}
.modal-filter-btn:hover {
background: rgba(255, 255, 255, 0.16);
color: #ffffff;
}
.modal-filter-btn.active {
background: var(--primary);
color: #ffffff;
border-color: var(--primary);
box-shadow: 0 2px 8px rgba(92, 96, 245, 0.4);
}
.modal-filter-btn .count {
font-size: 0.7rem;
opacity: 0.8;
}
.gallery-stage {
position: relative;
display: flex;
align-items: center;
justify-content: center;
min-height: 460px;
max-height: 68vh;
background: #070b14;
border-radius: 12px;
overflow: hidden;
}
.gallery-stage img {
max-height: 68vh;
max-width: 100%;
object-fit: contain;
transition: opacity 0.2s ease-in-out;
}
.gallery-nav-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 40px;
border-radius: 50%;
background: rgba(15, 23, 42, 0.7);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #ffffff;
font-size: 1.35rem;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
z-index: 10;
}
.gallery-nav-btn:hover {
background: var(--primary);
color: #ffffff;
border-color: var(--primary);
transform: translateY(-50%) scale(1.08);
}
.gallery-nav-btn.prev {
left: 12px;
}
.gallery-nav-btn.next {
right: 12px;
}
.modal-gallery-thumbs {
display: flex;
gap: 8px;
overflow-x: auto;
padding: 6px 2px 2px;
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
}
.modal-gallery-thumb-item {
width: 68px;
height: 46px;
border-radius: 6px;
flex-shrink: 0;
cursor: pointer;
overflow: hidden;
position: relative;
border: 2px solid transparent;
opacity: 0.45;
transition: all 0.2s ease;
background-size: cover;
background-position: center;
}
.modal-gallery-thumb-item:hover {
opacity: 0.85;
transform: translateY(-2px);
}
.modal-gallery-thumb-item.active {
opacity: 1;
border-color: var(--primary);
box-shadow: 0 0 10px rgba(92, 96, 245, 0.55);
transform: scale(1.04);
}
/* Recenzje i opinie */
.rating-breakdown-card {
background: #f8f9fd;
border: 1px solid var(--border-color);
border-radius: var(--radius-sm);
padding: 1.25rem;
border-radius: var(--radius-md);
padding: 1.25rem 1.5rem;
}
.review-card-item {
+163
View File
@@ -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>
@@ -83,21 +83,49 @@
</div>
</div>
<!-- Modal Podglądu Zdjęć ze Szlaków (Lightbox) -->
<!-- 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-lg">
<div class="modal-content border-0 shadow-lg bg-dark text-white" style="border-radius: var(--sp-radius-md); overflow: hidden;">
<div class="modal-header border-0 pb-0">
<h6 class="modal-title fw-bold text-white mb-0" id="lightboxTitle">Zdjęcie z trasy</h6>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Zamknij"></button>
<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>
<div class="modal-body p-4 text-center">
<div class="rounded-3 d-flex align-items-center justify-content-center p-5 mb-2" id="lightboxPlaceholder" style="min-height: 380px; background: linear-gradient(135deg, #1e293b, #334155); color: white;">
<div class="text-center">
<i class='bx bx-landscape' style="font-size: 4rem; opacity: 0.8;"></i>
<h5 class="fw-bold mt-2" id="lightboxSubtext">Florencja & Toskania — Grupa objazdowa Almatur</h5>
<p class="text-white-50 small">Fotografia wykonana podczas realizacji programu autokarowego • Wrzesień 2026</p>
<!-- 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>
@@ -388,7 +388,7 @@ include __DIR__ . '/header.php';
data-dates-free="05-15-sep 25-30-sep październik-2027">
<div class="pilot-offer-avatar">
OK
<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>
@@ -443,6 +443,14 @@ include __DIR__ . '/header.php';
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">
@@ -476,9 +484,9 @@ include __DIR__ . '/header.php';
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, #059669 0%, #34d399 100%);">
AL
<div class="verified-badge-mini">
<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>
@@ -527,6 +535,14 @@ include __DIR__ . '/header.php';
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">
@@ -86,63 +86,61 @@ include __DIR__ . '/header.php';
</div>
</div>
<div class="table-responsive text-nowrap">
<table class="table table-hover align-middle mb-0">
<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 18px;">Wycieczka & Termin</th>
<th>Pilot</th>
<th>Stawka dzienna</th>
<th>Status w aplikacji pilota</th>
<th>Data wysłania</th>
<th class="text-end" style="padding: 12px 18px;">Akcje</th>
<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: 14px 18px;">
<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.8rem; flex-shrink: 0;">
<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">Wycieczka #15: Magia Toskanii i Rzym</div>
<small class="text-muted"><i class='bx bx-calendar'></i> 05.10.2027 – 12.10.2027 (8 dni)</small>
<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>
<td style="padding: 12px 14px;">
<div class="d-flex align-items-center gap-2">
<div class="rounded-circle d-flex align-items-center justify-content-center text-white fw-bold" style="width: 32px; height: 32px; background: linear-gradient(135deg, var(--primary) 0%, #767bf7 100%); font-size: 0.78rem; flex-shrink: 0;">
OK
</div>
<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">Oskar Kaczmarek</a>
<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>
<strong class="text-navy">550 PLN</strong> <small class="text-muted">/ dzień netto</small>
<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>
<span class="badge bg-warning-subtle text-warning fw-bold px-3 py-1 rounded-pill d-inline-flex align-items-center gap-1" style="font-size: 0.74rem;">
<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;">Powiadomienie PUSH doręczone (18h na odpowiedź)</small>
<small class="text-muted d-block mt-1" style="font-size: 0.7rem;">18h na odpowiedź w mobile</small>
</td>
<td>
<td style="padding: 12px 14px;">
<span class="text-muted small">Dziś, 09:15</span>
</td>
<td class="text-end" style="padding: 14px 18px;">
<div class="d-flex align-items-center justify-content-end gap-1">
<button type="button" class="btn btn-sm btn-outline-primary" style="font-size: 0.76rem;" onclick="showToastNotification('Przypomnienie PUSH wysłane!', 'Wysłano ponowny alert na telefon pilota Oskara Kaczmarka.');">
<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.76rem;" title="Profil pilota">
<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>
@@ -151,47 +149,45 @@ include __DIR__ . '/header.php';
<!-- Pozycja 2: Wycieczka #12 (Oskar Kaczmarek - potwierdzona) -->
<tr class="inquiry-row" data-status="confirmed">
<td style="padding: 14px 18px;">
<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.8rem; flex-shrink: 0;">
<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">Wycieczka #12: Klasyczne Włochy (oskar prod)</div>
<small class="text-muted"><i class='bx bx-calendar'></i> 16.09.2027 – 24.09.2027 (9 dni)</small>
<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>
<td style="padding: 12px 14px;">
<div class="d-flex align-items-center gap-2">
<div class="rounded-circle d-flex align-items-center justify-content-center text-white fw-bold" style="width: 32px; height: 32px; background: linear-gradient(135deg, var(--primary) 0%, #767bf7 100%); font-size: 0.78rem; flex-shrink: 0;">
OK
</div>
<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">Oskar Kaczmarek</a>
<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>
<strong class="text-navy">550 PLN</strong> <small class="text-muted">/ dzień netto</small>
<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>
<span class="badge bg-success-subtle text-success fw-bold px-3 py-1 rounded-pill d-inline-flex align-items-center gap-1" style="font-size: 0.74rem;">
<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 TravelLeader Mobile</small>
<small class="text-muted d-block mt-1" style="font-size: 0.7rem;">Potwierdzone w mobile</small>
</td>
<td>
<td style="padding: 12px 14px;">
<span class="text-muted small">Wczoraj, 14:22</span>
</td>
<td class="text-end" style="padding: 14px 18px;">
<div class="d-flex align-items-center justify-content-end gap-1">
<button type="button" class="btn btn-sm btn-light border text-muted" style="font-size: 0.76rem;" onclick="alert('Generowanie umowy zlecenia dla pilota Oskara Kaczmarka (Klasyczne Włochy)...');">
<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.76rem;">
<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>
@@ -200,47 +196,45 @@ include __DIR__ . '/header.php';
<!-- Pozycja 3: Norwegia (Anna Lewandowska - potwierdzona) -->
<tr class="inquiry-row" data-status="confirmed">
<td style="padding: 14px 18px;">
<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.8rem; flex-shrink: 0;">
<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">Wycieczka #18: Fiordy Norwegii & Bergen</div>
<small class="text-muted"><i class='bx bx-calendar'></i> 12.08.2027 – 19.08.2027 (8 dni)</small>
<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>
<td style="padding: 12px 14px;">
<div class="d-flex align-items-center gap-2">
<div class="rounded-circle d-flex align-items-center justify-content-center text-white fw-bold" style="width: 32px; height: 32px; background: linear-gradient(135deg, #059669, #10b981); font-size: 0.78rem; flex-shrink: 0;">
AL
</div>
<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">Anna Lewandowska</span>
<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>
<strong class="text-navy">650 PLN</strong> <small class="text-muted">/ dzień netto</small>
<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>
<span class="badge bg-success-subtle text-success fw-bold px-3 py-1 rounded-pill d-inline-flex align-items-center gap-1" style="font-size: 0.74rem;">
<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>
<td style="padding: 12px 14px;">
<span class="text-muted small">3 dni temu</span>
</td>
<td class="text-end" style="padding: 14px 18px;">
<div class="d-flex align-items-center justify-content-end gap-1">
<button type="button" class="btn btn-sm btn-light border text-muted" style="font-size: 0.76rem;" onclick="alert('Generowanie umowy zlecenia dla pilotki Anny Lewandowskiej...');">
<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.76rem;">
<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>
@@ -249,45 +243,45 @@ include __DIR__ . '/header.php';
<!-- Pozycja 4: Wycieczka zrealizowana (Do oceny / Zrecenzowana) -->
<tr class="inquiry-row" data-status="completed">
<td style="padding: 14px 18px;">
<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.8rem; flex-shrink: 0;">
<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">Wycieczka #8: Klasyczne Włochy (Maj 2026)</div>
<small class="text-muted"><i class='bx bx-calendar'></i> Zrealizowana pomyślnie</small>
<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>
<td style="padding: 12px 14px;">
<div class="d-flex align-items-center gap-2">
<div class="rounded-circle d-flex align-items-center justify-content-center text-white fw-bold" style="width: 32px; height: 32px; background: linear-gradient(135deg, var(--primary) 0%, #767bf7 100%); font-size: 0.78rem; flex-shrink: 0;">
OK
</div>
<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">Oskar Kaczmarek</a>
<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>
<strong class="text-navy">500 PLN</strong> <small class="text-muted">/ dzień netto</small>
<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>
<span class="badge bg-primary-subtle text-primary fw-bold px-3 py-1 rounded-pill d-inline-flex align-items-center gap-1" style="font-size: 0.74rem;">
<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>
<td style="padding: 12px 14px;">
<span class="text-muted small">Maj 2026</span>
</td>
<td class="text-end" style="padding: 14px 18px;">
<a href="pilot-profile.php#opinie" class="btn btn-sm btn-outline-primary" style="font-size: 0.76rem;">
Zobacz opinię &rarr;
</a>
<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ę &rarr;
</a>
</div>
</td>
</tr>
@@ -25,7 +25,7 @@ $experienceYears = max(1, $currentYear - $startYear);
<!-- Lewa strona Hero: Awatar, dane, ranga -->
<div class="d-flex align-items-start gap-4">
<div class="profile-avatar-large">
OK
<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>
@@ -33,14 +33,7 @@ $experienceYears = max(1, $currentYear - $startYear);
<div>
<div class="d-flex align-items-center gap-2 mb-1 flex-wrap">
<h2 class="profile-name">Oskar Kaczmarek</h2>
<span class="profile-badge-available">
<span class="status-dot"></span>
Dostępny dla biur
</span>
<span class="pilot-academy-badge">
<i class='bx bx-check-shield'></i> Superpilot Academy 2026
</span>
<h2 class="profile-name mb-0">Oskar Kaczmarek</h2>
</div>
<div class="text-muted mb-2" style="font-size: 0.88rem;">
@@ -68,39 +61,22 @@ $experienceYears = max(1, $currentYear - $startYear);
</div>
</div>
<!-- Prawa strona Hero: Główne akcje rezerwacji i kontaktu -->
<div class="d-flex flex-column gap-2 text-end">
<button type="button" class="btn btn-primary px-4 py-2 fw-semibold" style="font-size: 0.88rem; border-radius: var(--radius-sm);" onclick="openBookingModal('1', 'Oskar Kaczmarek', '16.09.2027 - 24.09.2027')">
<i class='bx bx-calendar-plus me-1'></i> Zarezerwuj termin / Wyślij zapytanie
<!-- 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>
<div class="d-flex gap-2 justify-content-end">
<a href="tel:+48600123456" class="btn btn-outline-secondary btn-sm" style="font-size: 0.78rem;">
<i class='bx bx-phone me-1'></i> +48 600 123 456
</a>
<button type="button" class="btn btn-outline-secondary btn-sm" style="font-size: 0.78rem;" onclick="this.classList.toggle('text-warning'); this.innerHTML = (this.classList.contains('text-warning') ? '<i class=\'bx bxs-bookmark me-1\'></i> Zapisano' : '<i class=\'bx bx-bookmark me-1\'></i> Zapisz');">
<i class='bx bx-bookmark me-1'></i> Zapisz
</button>
</div>
<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>
<!-- Pasek 3 spokojnych wskaźników jakości (Zgodnie z wytycznymi: bez zbędnego licznika zrealizowanych) -->
<div class="profile-stats-strip">
<div class="profile-stat-box">
<div class="stat-val"><?= $experienceYears ?> lat doświadczenia</div>
<div class="stat-lbl">Ciągła praktyka pilotażu tras objazdowych od <?= $startYear ?> r.</div>
</div>
<div class="profile-stat-box">
<div class="stat-val text-success">100% dyspozycyjności</div>
<div class="stat-lbl">Wszystkie potwierdzone zlecenia zrealizowane bez odwołań</div>
</div>
<div class="profile-stat-box">
<div class="stat-val" style="color: var(--primary);">100% poleceń biur</div>
<div class="stat-lbl">Każde biuro podróży deklaruje chęć ponownej współpracy</div>
</div>
</div>
</div>
<!-- Główny układ 2-kolumnowy (2/3 Zawartość merytoryczna + 1/3 Sticky Panel Rezerwacji i Kalendarza) -->
@@ -121,15 +97,9 @@ $experienceYears = max(1, $currentYear - $startYear);
<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-3" style="font-size: 0.9rem; line-height: 1.65;">
<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 class="d-flex flex-wrap gap-2 pt-2 border-top">
<span class="tech-tag"><i class='bx bx-check text-success me-1'></i> Wykształcenie historyczne (UW)</span>
<span class="tech-tag"><i class='bx bx-check text-success me-1'></i> Doświadczenie w grupach autokarowych i lotniczych</span>
<span class="tech-tag"><i class='bx bx-check text-success me-1'></i> Standardy rozliczeń dewizowych Travel Manager</span>
</div>
</div>
</div>
@@ -219,66 +189,93 @@ $experienceYears = max(1, $currentYear - $startYear);
</div>
</div>
<!-- Karta 3: Autoprezentacja & Fotorelacje ze szlaków -->
<!-- Karta 3: Autoprezentacja Wideo & Fotorelacje z Tras -->
<div class="profile-section-card">
<div class="profile-section-header">
<h5 class="profile-section-title">
<i class='bx bx-video'></i> Autoprezentacja Wideo & Fotorelacje
</h5>
<span class="badge bg-light text-muted border">Wideo 1:15 min</span>
<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">
<p class="text-secondary small mb-3">
Krótkie nagranie wideo przygotowane dla koordynatorów biur podróży: dykcja, kultura osobista oraz wypowiedzi w języku włoskim i angielskim.
</p>
<!-- Wideo-wizytówka -->
<div class="profile-video-box" 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.95rem;">Oskar Kaczmarek — Autoprezentacja warsztatu pilota</div>
<div class="text-white-50 small">Nagrano: Florencja / Rzym • Trasa objazdowa • 1:15 min</div>
</div>
</div>
<!-- Fotorelacje 4 miniatury ze szlaków -->
<div class="d-flex justify-content-between align-items-center mt-3 mb-1">
<div class="small fw-bold text-navy">Fotorelacje z tras i pracy z grupą:</div>
<small class="text-muted">4 zdjęcia w galerii</small>
</div>
<div class="profile-photos-grid">
<div class="profile-photo-thumb" style="background: linear-gradient(135deg, #60a5fa, #3b82f6);" onclick="openPhotoLightbox('Florencja & Toskania — Grupa objazdowa Almatur')">
<div class="text-center p-1">
<i class='bx bx-camera fs-4'></i>
<div class="small">Toskania</div>
<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>
<div class="profile-photo-thumb" style="background: linear-gradient(135deg, #f59e0b, #d97706);" onclick="openPhotoLightbox('Rzym — Koloseum & Forum Romanum')">
<div class="text-center p-1">
<i class='bx bx-buildings fs-4'></i>
<div class="small">Rzym</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>
<div class="profile-photo-thumb" style="background: linear-gradient(135deg, #10b981, #059669);" onclick="openPhotoLightbox('Cyklady & Ateny — Rejs z grupą')">
<div class="text-center p-1">
<i class='bx bx-ship fs-4'></i>
<div class="small">Grecja</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>
<div class="profile-photo-thumb" style="background: linear-gradient(135deg, #8b5cf6, #6d28d9);" onclick="openPhotoLightbox('Andaluzja — Alhambra & Granada')">
<div class="text-center p-1">
<i class='bx bx-group fs-4'></i>
<div class="small">Andaluzja</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>
@@ -302,58 +299,61 @@ $experienceYears = max(1, $currentYear - $startYear);
<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">
<div class="col-md-4 text-center border-end">
<div class="display-4 fw-bold text-navy mb-1" style="font-size: 2.75rem; line-height: 1;">4.9</div>
<div class="text-warning fs-5 mb-1">
<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">na podstawie 18 ocen od 6 biur</div>
<div class="badge bg-success-subtle text-success mt-2 rounded-pill px-3 py-1" style="font-size: 0.72rem;">
100% poleceń na kolejne wyjazdy
<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>
<div class="col-md-8 ps-md-4 mt-3 mt-md-0">
<!-- 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 align-items-center gap-2">
<span style="width: 30px;" class="fw-semibold">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: 45px;">16 (89%)</span>
<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 align-items-center gap-2">
<span style="width: 30px;" class="fw-semibold">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: 45px;">2 (11%)</span>
<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 align-items-center gap-2">
<span style="width: 30px;" class="fw-semibold">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: 45px;">0</span>
</div>
</div>
<div class="row g-2 mt-3 pt-3 border-top text-center" style="font-size: 0.75rem;">
<div class="col-4">
<strong class="text-navy d-block">5.0 / 5.0</strong>
<span class="text-muted">Punktualność</span>
</div>
<div class="col-4">
<strong class="text-navy d-block">4.9 / 5.0</strong>
<span class="text-muted">Kontakt z grupą</span>
</div>
<div class="col-4">
<strong class="text-navy d-block">4.9 / 5.0</strong>
<span class="text-muted">Język w trasie</span>
<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>
@@ -456,13 +456,8 @@ $experienceYears = max(1, $currentYear - $startYear);
<!-- 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">
<div>
<h6 class="fw-bold mb-0 text-navy" style="font-size: 0.92rem;">Kalendarz Dostępności</h6>
<small class="text-muted" style="font-size: 0.72rem;">Stan na żywo z aplikacji pilota</small>
</div>
<span class="badge bg-success-subtle text-success border border-success-subtle rounded-pill px-2 py-1" style="font-size: 0.68rem;">
Zsynchronizowany
</span>
<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 -->
@@ -528,29 +523,24 @@ $experienceYears = max(1, $currentYear - $startYear);
<!-- 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 termin
<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> Wycieczka potwierdzona
<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 class="mt-3 p-2 rounded-2 bg-light text-muted small" style="font-size: 0.75rem;">
<i class='bx bx-pointer me-1 text-primary'></i> Wybór: <strong id="calSelectedRange" class="text-dark">Kliknij zielony dzień, aby wybrać termin</strong>
<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-3 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 / Wyślij zapytanie
<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 class="text-center mt-2 text-muted" style="font-size: 0.72rem;">
Powiadomienie PUSH trafi bezpośrednio do aplikacji pilota
</div>
</div>
</div>
@@ -597,26 +587,29 @@ $experienceYears = max(1, $currentYear - $startYear);
</select>
</div>
<!-- Oceny cząstkowe -->
<!-- Oceny cząstkowe z perspektywy biura -->
<div class="col-12">
<label class="form-label fw-bold small text-muted">Oceny cząstkowe:</label>
<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">Punktualność</div>
<div class="text-warning fs-5">★★★★★</div>
<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">Kontakt z grupą</div>
<div class="text-warning fs-5">★★★★★</div>
<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">Radzenie w kryzysie</div>
<div class="text-warning fs-5">★★★★★</div>
<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>
@@ -665,19 +658,205 @@ function openVideoModal(pilotName, duration) {
}
}
function openPhotoLightbox(caption) {
// 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) {
if (document.getElementById('lightboxTitle')) document.getElementById('lightboxTitle').innerText = caption;
if (document.getElementById('lightboxSubtext')) document.getElementById('lightboxSubtext').innerText = caption;
const modal = new bootstrap.Modal(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');
@@ -690,7 +869,7 @@ document.addEventListener('DOMContentLoaded', function() {
const dayFormatted = (dayNum < 10 ? '0' + dayNum : dayNum) + '.09.2027';
window.selectedProfileDates = dayFormatted;
if (selectedRangeSpan) {
selectedRangeSpan.innerText = dayFormatted + ' (Termin wolny - gotowy do zapytania)';
selectedRangeSpan.innerHTML = '<span class="badge bg-success-subtle text-success border border-success-subtle py-1 px-2">Wybrany termin: <strong>' + dayFormatted + '</strong></span>';
}
});
});
@@ -112,30 +112,27 @@ include __DIR__ . '/header.php';
</div>
</div>
<!-- 4 kluczowe wymiary oceny -->
<!-- Kluczowe wymiary oceny biura podróży -->
<div class="row g-3 pt-3 border-top">
<div class="col-md-3 col-6">
<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>
<small class="text-muted d-block mt-1">Punktualność</small>
<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-3 col-6">
<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>
<small class="text-muted d-block mt-1">Kontakt z grupą</small>
<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-3 col-6">
<div class="p-3 rounded-3 bg-light text-center">
<div class="text-warning fs-5 fw-bold">4.9 ★</div>
<small class="text-muted d-block mt-1">Zarządzanie kryzysem</small>
</div>
</div>
<div class="col-md-3 col-6">
<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>
<small class="text-muted d-block mt-1">Języki w hotelach</small>
<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>
@@ -1,497 +0,0 @@
<?php
$enablePrototypeComments = true;
include '../../header-invoice.php';
?>
<div class="container-fluid flex-grow-1 container-p-y">
<!-- Header & Form Buttons -->
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center mb-4 gap-3">
<div class="d-flex flex-column justify-content-center">
<h4 class="mb-1 text-body"><i class="bx bx-repeat me-2 text-primary"></i>Nowy Szablon Faktury Cyklicznej</h4>
<p class="text-muted mb-0">Skonfiguruj reguły automatycznego generowania i wystawiania dokumentów abonamentowych</p>
</div>
<div class="d-flex align-content-center flex-wrap gap-2">
<a href="app-invoice-recurring-list.php" class="btn btn-label-secondary">Anuluj</a>
<button type="submit" form="recurringForm" class="btn btn-primary"><i class="bx bx-save me-1"></i> Zapisz szablon cyklu</button>
</div>
</div>
<form id="recurringForm" onsubmit="return false">
<!-- Top Section: Status & Cycle Rules -->
<div class="row mb-4">
<!-- Card 1: Harmonogram i Reguły Cyklu -->
<div class="col-lg-6 mb-4 mb-lg-0">
<div class="card mb-4">
<div class="card-header border-bottom py-3 d-flex justify-content-between align-items-center bg-label-primary">
<h6 class="card-title m-0 text-primary"><i class="bx bx-calendar-repeat me-2"></i>Harmonogram i Reguły Cyklu</h6>
<!-- Global Master Switch ON/OFF -->
<div class="d-flex align-items-center gap-2 me-4">
<span class="fw-bold small text-body">Status cyklu:</span>
<div class="form-check form-switch m-0 d-flex align-items-center">
<input class="form-check-input mt-0 me-3" type="checkbox" id="masterStatusToggle" checked style="width: 2.5em; height: 1.3em; cursor: pointer;">
<label class="form-check-label mb-0 ms-1" for="masterStatusToggle" style="cursor: pointer;">
<span class="badge bg-label-success" id="statusLabelText">AKTYWNY (ON)</span>
</label>
</div>
</div>
</div>
<div class="card-body pt-3">
<div class="mb-3">
<label class="form-label fw-bold">Nazwa własna cyklu / Szablonu</label>
<input type="text" class="form-control" placeholder="np. Abonament IT - Usługi Chmurowe (ABC Logistics)" value="Abonament IT i Wsparcie Techniczne">
<small class="text-muted">Nazwa widoczna w rejestrze cykli dla ułatwienia identyfikacji.</small>
</div>
<!-- Przebudowany Konfigurator Cyklu -->
<div class="card shadow-none border bg-lighter mb-3">
<div class="card-body p-3">
<h6 class="fw-bold mb-3 text-primary"><i class="bx bx-time-five me-1"></i>Konfiguracja Powtarzania</h6>
<!-- Data rozpoczęcia & Cykl rozliczeń -->
<div class="row g-2 mb-3">
<div class="col-md-6">
<label class="form-label fw-bold">Data rozpoczęcia</label>
<input type="text" class="form-control date-picker" id="startDateInput" value="2026-08-01">
<small class="text-muted">Dzień wystawienia pierwszej faktury.</small>
</div>
<div class="col-md-6">
<label class="form-label fw-bold">Cykl rozliczeń</label>
<select class="form-select" id="cycleUnitSelect">
<option value="miesiac" selected>Miesiąc</option>
<option value="rok">Rok</option>
<option value="dzien">Dzień</option>
</select>
<small class="text-muted">Jednostka czasu dla powtarzania.</small>
</div>
</div>
<!-- Interwał & Obowiązuje do -->
<div class="row g-2 mb-3">
<div class="col-md-6">
<label class="form-label fw-bold">Interwał (Co ile cykli)</label>
<div class="input-group">
<span class="input-group-text">Co</span>
<input type="number" class="form-control" value="1" min="1" id="intervalInput">
<span class="input-group-text" id="intervalUnitText">miesiąc</span>
</div>
<small class="text-muted">np. "1" = co miesiąc, "3" = kwartalnie, "12" = co rok.</small>
</div>
<div class="col-md-6">
<label class="form-label fw-bold">Obowiązuje do</label>
<input type="text" class="form-control date-picker" id="endDateInput" placeholder="RRRR-MM-DD (Bezterminowo)">
<small class="text-muted">Data, po której system automatycznie wyłączy cykl.</small>
</div>
</div>
<!-- Informacyjna ramka dynamicznego opisu -->
<div class="alert alert-primary mb-0 p-2 d-flex align-items-center" role="alert">
<i class="bx bx-info-circle fs-4 me-2"></i>
<span class="small" id="summaryDescription">
Faktura będzie generowana <strong>co 1 miesiąc</strong>, rozpoczynając od dnia <strong>01.08.2026 r.</strong> bezterminowo.
</span>
</div>
</div>
</div>
<hr class="my-3">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="emailAutoSend" checked>
<label class="form-check-label fw-bold" for="emailAutoSend">Automatyczna wysyłka e-mail do kontrahenta</label>
<div class="text-muted small mb-3">System będzie wysyłał wiadomości e-mail oraz powiadomienia zgodnie z wybranym profilem powiadomień cyklicznych.</div>
<div class="ps-1">
<label class="form-label small text-uppercase text-muted fw-bold">Wysyłka faktur e-mail</label>
<div class="mb-1">
<label class="form-check-label small">Automatyczna wysyłka wg profilu:</label>
</div>
<select class="form-select form-select-sm w-100">
<option value="1">Profil: Standardowy (Termin 30 dni)</option>
<option value="2">Profil: Szybki (Termin 3 dni)</option>
<option value="3">Profil: Tylko przypomnienia po terminie</option>
</select>
</div>
</div>
</div>
</div>
</div>
<!-- Card 2: Kontrahent - Nabywca i Odbiorca -->
<div class="col-lg-6">
<div class="card mb-4">
<div class="card-header border-bottom py-3 d-flex justify-content-between align-items-center">
<h6 class="card-title m-0"><i class="bx bx-user-check me-2 text-primary"></i>Kontrahent (Nabywca i Odbiorca)</h6>
</div>
<div class="card-body pt-3">
<!-- Nabywca -->
<div class="mb-3">
<label class="form-label fw-bold">Nabywca (Główny kontrahent / Płatnik)</label>
<div class="card shadow-none border bg-lighter">
<div class="card-body p-3 d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1 text-primary fw-bold">ABC Logistics Sp. z o.o.</h6>
<p class="mb-0 small text-muted">NIP: PL 865 223 11 90 • ul. Warszawska 45, 00-001 Warszawa</p>
<p class="mb-0 small text-muted">E-mail do faktur: ksiegowosc@abclogistics.pl</p>
</div>
<button type="button" class="btn btn-outline-primary btn-sm"><i class="bx bx-refresh me-1"></i> Zmień</button>
</div>
</div>
</div>
<!-- Switches for Recipient and Extra -->
<div class="d-flex flex-column gap-2 mb-3">
<label class="form-check form-switch m-0">
<input class="form-check-input" type="checkbox" id="recipientToggle">
<span class="form-check-label fw-bold">Wskaż innego Odbiorcę / Miejsce dostawy</span>
</label>
</div>
<!-- Odbiorca Container (Domyślnie ukryty) -->
<div class="card shadow-none border mb-3 bg-white d-none" id="recipientBox">
<div class="card-header py-2 bg-label-secondary d-flex justify-content-between align-items-center">
<span class="small fw-bold"><i class="bx bx-buildings me-1"></i>Dane Odbiorcy (np. Szkoła, Oddział, Filia)</span>
<button type="button" class="btn btn-xs btn-outline-secondary">Wybierz z listy</button>
</div>
<div class="card-body p-3">
<div class="mb-2">
<label class="form-label small text-muted mb-1">Nazwa Odbiorcy</label>
<input type="text" class="form-control form-control-sm" value="ABC Logistics - Oddział Magazynowy Stalowa Wola">
</div>
<div class="row g-2">
<div class="col-8">
<label class="form-label small text-muted mb-1">Adres Odbiorcy</label>
<input type="text" class="form-control form-control-sm" value="ul. Przemysłowa 12, 37-450 Stalowa Wola">
</div>
<div class="col-4">
<label class="form-label small text-muted mb-1">NIP Odbiorcy (opcja)</label>
<input type="text" class="form-control form-control-sm" placeholder="Opcjonalny NIP">
</div>
</div>
</div>
</div>
<div class="row g-2">
<div class="col-6">
<label class="form-label small text-uppercase fw-bold text-muted">Miejsce wystawienia</label>
<input type="text" class="form-control" value="Stalowa Wola">
</div>
<div class="col-6">
<label class="form-label small text-uppercase fw-bold text-muted">Osoba wystawiająca</label>
<input type="text" class="form-control" value="Bartłomiej Banaczyk">
</div>
</div>
</div>
</div>
<!-- Kontakt i powiadomienia -->
<div class="card mb-4">
<div class="card-body">
<h6 class="card-title text-primary mb-3">Kontakt i powiadomienia</h6>
<div class="mb-3">
<label class="form-label small text-uppercase text-muted fw-bold">Email kontaktowy</label>
<input type="email" class="form-control" value="ksiegowosc@abclogistics.pl">
</div>
<div class="mb-3">
<label class="form-label small text-uppercase text-muted fw-bold">Numer SMS</label>
<input type="text" class="form-control" placeholder="+48 ...">
</div>
<div class="mb-0">
<label class="form-label small text-uppercase text-muted fw-bold">Powiadomienia SMS</label>
<select class="form-select">
<option value="" selected>Wybierz opcję</option>
<option value="1">Wyślij powiadomienie SMS po wygenerowaniu</option>
<option value="0">Brak powiadomień</option>
</select>
</div>
</div>
</div>
<!-- Ostatni dokument -->
<div class="card mb-4 mb-lg-0">
<div class="card-body">
<h6 class="card-title text-primary mb-3">Ostatni dokument</h6>
<p class="text-muted mb-0 small">Brak dokumentów</p>
</div>
</div>
</div>
</div>
<!-- Section 2: Document Settings & Variables -->
<div class="card mb-4">
<div class="card-header border-bottom py-3">
<h6 class="card-title m-0"><i class="bx bx-slider-alt me-2 text-primary"></i>Ustawienia Dokumentów Generowanych</h6>
</div>
<div class="card-body pt-3">
<div class="row g-3">
<div class="col-md-3">
<label class="form-label fw-bold">Rodzaj dokumentu</label>
<select class="form-select border-primary text-primary fw-bold">
<option value="faktura" selected>Faktura VAT</option>
<option value="proforma">Proforma</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label fw-bold">Waluta & Język</label>
<div class="input-group">
<select class="form-select">
<option value="PLN" selected>PLN (zł)</option>
<option value="EUR">EUR (€)</option>
<option value="USD">USD ($)</option>
</select>
<select class="form-select">
<option value="PL" selected>PL</option>
<option value="EN">EN</option>
</select>
</div>
</div>
<div class="col-md-3">
<label class="form-label fw-bold">Forma płatności</label>
<select class="form-select">
<option value="przelew" selected>Przelew bankowy</option>
<option value="gotowka">Gotówka</option>
<option value="split">Split Payment (MPP)</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label fw-bold">Termin płatności</label>
<div class="input-group">
<input type="number" class="form-control" value="14">
<span class="input-group-text">dni</span>
</div>
</div>
</div>
<div class="row g-3 mt-1">
<div class="col-md-3">
<label class="form-label fw-bold">Konto bankowe</label>
<select class="form-select">
<option value="1">Main PLN: 89 1050 ... 4491</option>
<option value="2">EUR IBAN: PL 12 1050 ... 9901</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label fw-bold">Data sprzedaży</label>
<select class="form-select">
<option value="issue_date" selected>Zgodna z datą wystawienia</option>
<option value="last_day_prev_month">Ostatni dzień poprzedniego miesiąca</option>
<option value="first_day_curr_month">Pierwszy dzień bieżącego miesiąca</option>
</select>
</div>
<div class="col-md-5">
<label class="form-label fw-bold">Komentarz / Uwagi na fakturze (dla klienta)</label>
<input type="text" class="form-control" value="Opłata abonamentowa za miesiąc {MIESIAC} {ROK}. Dziękujemy za współpracę!">
<div class="mt-1">
<span class="badge bg-label-secondary me-1" style="cursor:pointer;">+ {MIESIAC}</span>
<span class="badge bg-label-secondary me-1" style="cursor:pointer;">+ {ROK}</span>
<span class="badge bg-label-secondary me-1" style="cursor:pointer;">+ {POPRZEDNI_MIESIAC}</span>
<span class="badge bg-label-secondary" style="cursor:pointer;">+ {KWARTAL}</span>
</div>
</div>
</div>
</div>
</div>
<!-- Section 3: Items Configurator with Dynamic Tags -->
<div class="card mb-4">
<div class="card-header bg-label-primary py-2 d-flex justify-content-between align-items-center">
<span class="fw-bold text-primary"><i class="bx bx-list-plus me-2"></i>Pozycje na fakturze cyklicznej</span>
<div class="d-flex align-items-center gap-3">
<div class="d-flex align-items-center">
<label class="text-muted small me-2 text-nowrap">Licz od:</label>
<select class="form-select form-select-sm">
<option value="netto" selected>Cen netto</option>
<option value="brutto">Cen brutto</option>
</select>
</div>
<button type="button" class="btn btn-primary btn-sm"><i class="bx bx-plus me-1"></i> Dodaj pozycję</button>
</div>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-bordered m-0">
<thead class="table-light">
<tr>
<th style="width: 35%;">Nazwa towaru / usługi (oraz zmienne okresu)</th>
<th style="width: 10%;">Ilość</th>
<th style="width: 10%;">J.m.</th>
<th style="width: 12%;">Cena netto</th>
<th style="width: 10%;">VAT</th>
<th style="width: 13%;">Wartość brutto</th>
<th style="width: 5%;" class="text-center">Usuń</th>
</tr>
</thead>
<tbody>
<!-- Item row 1 -->
<tr>
<td>
<input type="text" class="form-control mb-1 fw-medium" value="Abonament za opiekę serwerową IT - okres {MIESIAC} {ROK}">
</td>
<td><input type="number" class="form-control" value="1"></td>
<td>
<select class="form-select">
<option value="usluga" selected>usł.</option>
<option value="szt">szt.</option>
<option value="msc">m-c</option>
</select>
</td>
<td><input type="text" class="form-control" value="1 200,00"></td>
<td>
<select class="form-select">
<option value="23" selected>23%</option>
<option value="8">8%</option>
<option value="0">0%</option>
<option value="zw">zw.</option>
</select>
</td>
<td><input type="text" class="form-control fw-bold" value="1 476,00" readonly></td>
<td class="text-center align-middle">
<button type="button" class="btn btn-icon btn-sm btn-label-danger"><i class="bx bx-trash"></i></button>
</td>
</tr>
<!-- Item row 2 -->
<tr>
<td>
<input type="text" class="form-control mb-1 fw-medium" value="Hosting Chmurowy Magico Cloud (Kwartalnie)">
</td>
<td><input type="number" class="form-control" value="1"></td>
<td>
<select class="form-select">
<option value="usluga" selected>usł.</option>
<option value="szt">szt.</option>
</select>
</td>
<td><input type="text" class="form-control" value="300,00"></td>
<td>
<select class="form-select">
<option value="23" selected>23%</option>
</select>
</td>
<td><input type="text" class="form-control fw-bold" value="369,00" readonly></td>
<td class="text-center align-middle">
<button type="button" class="btn btn-icon btn-sm btn-label-danger"><i class="bx bx-trash"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Dynamic Tag Guide below table -->
<div class="p-3 border-top bg-lighter">
<div class="d-flex align-items-center gap-2">
<small class="text-muted fw-bold">Dostępne zmienne dla pozycji (kliknij aby skopiować):</small>
<span class="badge bg-label-primary px-2 py-1" style="cursor:pointer;">{MIESIAC}</span>
<span class="badge bg-label-primary px-2 py-1" style="cursor:pointer;">{ROK}</span>
<span class="badge bg-label-primary px-2 py-1" style="cursor:pointer;">{POPRZEDNI_MIESIAC}</span>
<span class="badge bg-label-primary px-2 py-1" style="cursor:pointer;">{KWARTAL}</span>
</div>
</div>
</div>
<div class="card-footer bg-lighter d-flex justify-content-end py-3 border-top">
<div class="d-flex flex-column align-items-end me-3">
<span class="text-muted small">Suma netto: <strong>1 500,00 PLN</strong></span>
<span class="text-muted small">Suma VAT (23%): <strong>345,00 PLN</strong></span>
<h5 class="mb-0 text-primary mt-1">Razem brutto: <strong>1 845,00 PLN</strong></h5>
</div>
</div>
</div>
<!-- Submit Bottom Bar -->
<div class="d-flex justify-content-end gap-2 mb-4">
<a href="app-invoice-recurring-list.php" class="btn btn-label-secondary">Anuluj</a>
<button type="submit" class="btn btn-primary"><i class="bx bx-save me-1"></i> Zapisz szablon cyklu</button>
</div>
</form>
</div>
<?php include '../../footer.php'; ?>
<script>
document.addEventListener('DOMContentLoaded', function () {
const toggle = document.getElementById('masterStatusToggle');
const label = document.getElementById('statusLabelText');
const unitSelect = document.getElementById('cycleUnitSelect');
const intervalText = document.getElementById('intervalUnitText');
if (toggle && label) {
toggle.addEventListener('change', function () {
if (this.checked) {
label.textContent = 'AKTYWNY (ON)';
label.className = 'badge bg-label-success';
} else {
label.textContent = 'WYŁĄCZONY (OFF)';
label.className = 'badge bg-label-danger';
}
});
}
const intervalInput = document.getElementById('intervalInput');
const summaryDesc = document.getElementById('summaryDescription');
const startDateInput = document.getElementById('startDateInput');
const endDateInput = document.getElementById('endDateInput');
function updateUnitsAndSummary() {
if (!unitSelect || !intervalInput || !intervalText) return;
const val = unitSelect.value;
const num = parseInt(intervalInput.value) || 1;
let unitLabel = '';
let unitGenitive = '';
if (val === 'miesiac') {
if (num === 1) unitLabel = 'miesiąc';
else if (num >= 2 && num <= 4) unitLabel = 'miesiące';
else unitLabel = 'miesięcy';
unitGenitive = unitLabel;
} else if (val === 'rok') {
if (num === 1) unitLabel = 'rok';
else if (num >= 2 && num <= 4) unitLabel = 'lata';
else unitLabel = 'lat';
unitGenitive = unitLabel;
} else if (val === 'dzien') {
if (num === 1) unitLabel = 'dzień';
else unitLabel = 'dni';
unitGenitive = unitLabel;
}
intervalText.textContent = unitLabel;
if (summaryDesc) {
const startDate = startDateInput ? startDateInput.value || 'dzisiaj' : 'dzisiaj';
const endDate = endDateInput && endDateInput.value ? ` do dnia <strong>${endDateInput.value}</strong>` : ' bezterminowo';
summaryDesc.innerHTML = `Faktura będzie generowana <strong>co ${num} ${unitGenitive}</strong>, rozpoczynając od dnia <strong>${startDate}</strong>${endDate}.`;
}
}
if (unitSelect) unitSelect.addEventListener('change', updateUnitsAndSummary);
if (intervalInput) intervalInput.addEventListener('input', updateUnitsAndSummary);
if (startDateInput) startDateInput.addEventListener('change', updateUnitsAndSummary);
if (endDateInput) endDateInput.addEventListener('change', updateUnitsAndSummary);
const recipientToggle = document.getElementById('recipientToggle');
const recipientBox = document.getElementById('recipientBox');
if (recipientToggle && recipientBox) {
recipientToggle.addEventListener('change', function () {
if (this.checked) {
recipientBox.classList.remove('d-none');
} else {
recipientBox.classList.add('d-none');
}
});
}
});
</script>
</body>
</html>
@@ -1,510 +0,0 @@
<?php
$enablePrototypeComments = true;
include '../../header-invoice.php';
?>
<div class="container-fluid flex-grow-1 container-p-y">
<h4 class="py-3 mb-4">
<span class="text-muted fw-light">Sprzedaż /</span> Faktury cykliczne
</h4>
<!-- Summary KPI Cards -->
<div class="row g-4 mb-4">
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span class="text-heading">Aktywne cykle</span>
<div class="d-flex align-items-center my-1">
<h4 class="mb-0 me-2">18</h4>
<span class="text-success small fw-medium">nowe w tym roku</span>
</div>
<small class="text-muted">Szablonów zlecających wysyłkę</small>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-primary">
<i class="bx bx-repeat bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span class="text-heading">Szacowany MRR</span>
<div class="d-flex align-items-center my-1">
<h4 class="mb-0 me-2">42 850,00 zł</h4>
</div>
<small class="text-muted">Przychód powtarzalny / m-c</small>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-success">
<i class="bx bx-trending-up bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span class="text-heading">Szacowany ARR</span>
<div class="d-flex align-items-center my-1">
<h4 class="mb-0 me-2">514 200,00 zł</h4>
</div>
<small class="text-muted">Przychód powtarzalny / rok</small>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-info">
<i class="bx bx-calendar-event bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span class="text-heading">Wyłączone (Nieaktywne)</span>
<div class="d-flex align-items-center my-1">
<h4 class="mb-0 me-2">3</h4>
</div>
<small class="text-muted">Cykle obecnie wyłączone</small>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-warning">
<i class="bx bx-pause-circle bx-sm"></i>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Main Grid: Table Left (9 cols), Filter Panel Right (3 cols) -->
<div class="row">
<!-- Table Column -->
<div class="col-lg-9" id="invoice-list-col">
<div class="card">
<div
class="card-header border-bottom d-flex justify-content-between align-items-center flex-wrap gap-2">
<div class="me-2">
<div class="input-group input-group-merge">
<span class="input-group-text" id="basic-addon-search31"><i class="bx bx-search"></i></span>
<input type="text" class="form-control" placeholder="Wpisz aby wyszukać cykl..."
aria-label="Search..." aria-describedby="basic-addon-search31">
</div>
</div>
<div class="d-flex align-items-center gap-2">
<div class="dropdown">
<button type="button" class="btn btn-label-secondary p-2" data-bs-toggle="dropdown"
aria-expanded="false">
<i class="bx bx-cog"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<h6 class="dropdown-header">Widoczność kolumn</h6>
</li>
<li>
<div class="dropdown-item">
<div class="form-check"><input class="form-check-input" type="checkbox" checked>
<label class="form-check-label">Nazwa cyklu</label>
</div>
</div>
</li>
<li>
<div class="dropdown-item">
<div class="form-check"><input class="form-check-input" type="checkbox" checked>
<label class="form-check-label">Kontrahent</label>
</div>
</div>
</li>
<li>
<div class="dropdown-item">
<div class="form-check"><input class="form-check-input" type="checkbox" checked>
<label class="form-check-label">Wartość brutto</label>
</div>
</div>
</li>
</ul>
</div>
<button type="button" class="btn btn-label-secondary d-flex align-items-center gap-2"
id="filter-toggle-btn">
<i class="bx bx-filter-alt"></i> Filtry
</button>
<select class="form-select" style="width: 80px;">
<option value="40">40</option>
<option value="60">60</option>
<option value="100">100</option>
</select>
<a href="app-invoice-recurring-add.php" class="btn btn-primary">
<i class="bx bx-plus me-1"></i> <span class="d-none d-sm-inline-block">Utwórz fakturę
cykliczną</span>
</a>
</div>
</div>
<div class="table-responsive text-nowrap" style="min-height: 400px;">
<table class="table table-hover table-striped">
<thead class="table-light">
<tr>
<th style="width: 50px;">#</th>
<th>Nazwa cyklu i rodzaj</th>
<th>Nabywca / Odbiorca</th>
<th>Wartość netto</th>
<th>Wartość brutto</th>
<th>Harmonogram</th>
<th>Najbliższa emisja</th>
<th>Status</th>
<th class="text-center">Akcje</th>
</tr>
</thead>
<tbody class="table-border-bottom-0">
<!-- Item 1 -->
<tr>
<td>1</td>
<td>
<div class="d-flex flex-column">
<a href="app-invoice-recurring-add.php" class="fw-bold text-body">Abonament
Serwis IT i Hosting</a>
<small class="text-muted">Faktura VAT</small>
</div>
</td>
<td>
<div class="d-flex flex-column">
<span class="fw-semibold">ABC Logistics Sp. z o.o.</span>
<small class="text-muted">NIP: 865-223-11-90</small>
</div>
</td>
<td>
<span class="text-heading">1 500,00 PLN</span>
</td>
<td>
<span class="fw-bold text-heading">1 845,00 PLN</span>
</td>
<td>
<span class="badge bg-label-info"><i class="bx bx-calendar me-1"></i> Co 1 miesiąc</span>
<br><small class="text-muted">Bezterminowo</small>
</td>
<td>
<span class="fw-medium text-primary">01.08.2026</span>
<br><small class="text-muted">Ostatnio: 01.07.2026</small>
</td>
<td>
<div class="form-check form-switch m-0" data-bs-toggle="tooltip" title="Włącz / Wyłącz cykl">
<input class="form-check-input" type="checkbox" checked style="cursor: pointer;">
<span class="badge bg-label-success ms-1"><i class="bx bx-power-off me-1"></i> ON</span>
</div>
</td>
<td class="text-center">
<div class="d-flex justify-content-center align-items-center gap-1">
<a href="app-invoice-recurring-add.php"
class="btn btn-icon btn-sm text-secondary" data-bs-toggle="tooltip"
title="Edytuj szablon">
<i class="bx bx-edit-alt fs-5"></i>
</a>
<div class="dropdown">
<button type="button" class="btn p-0 dropdown-toggle hide-arrow"
data-bs-toggle="dropdown">
<i class="bx bx-dots-vertical-rounded fs-5"></i>
</button>
<div class="dropdown-menu dropdown-menu-end">
<a class="dropdown-item" href="javascript:void(0);"><i
class="bx bx-power-off me-1"></i> Wyłącz cykl</a>
<a class="dropdown-item" href="app-invoice-list.php"><i
class="bx bx-history me-1"></i> Historia wygenerowanych
faktur</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger" href="javascript:void(0);"><i
class="bx bx-archive-in me-1"></i> Archiwizuj cykl</a>
</div>
</div>
</div>
</td>
</tr>
<!-- Item 2 -->
<tr>
<td>2</td>
<td>
<div class="d-flex flex-column">
<a href="app-invoice-recurring-add.php" class="fw-bold text-body">Obsługa
Księgowa i Kadrowa</a>
<small class="text-muted">Faktura VAT</small>
</div>
</td>
<td>
<div class="d-flex flex-column">
<span class="fw-semibold">Bud-Spec Budownictwo S.A.</span>
<small class="text-muted">NIP: 111-222-33-44 • Odbiorca: Oddział Warszawa</small>
</div>
</td>
<td>
<span class="text-heading">3 500,00 PLN</span>
</td>
<td>
<span class="fw-bold text-heading">4 305,00 PLN</span>
</td>
<td>
<span class="badge bg-label-info"><i class="bx bx-calendar me-1"></i> Co 1 miesiąc</span>
<br><small class="text-muted">Do: 31.12.2026</small>
</td>
<td>
<span class="fw-medium text-primary">10.08.2026</span>
<br><small class="text-muted">Ostatnio: 10.07.2026</small>
</td>
<td>
<div class="form-check form-switch m-0" data-bs-toggle="tooltip" title="Włącz / Wyłącz cykl">
<input class="form-check-input" type="checkbox" checked style="cursor: pointer;">
<span class="badge bg-label-success ms-1"><i class="bx bx-power-off me-1"></i> ON</span>
</div>
</td>
<td class="text-center">
<div class="d-flex justify-content-center align-items-center gap-1">
<a href="app-invoice-recurring-add.php"
class="btn btn-icon btn-sm text-secondary" data-bs-toggle="tooltip"
title="Edytuj szablon">
<i class="bx bx-edit-alt fs-5"></i>
</a>
<div class="dropdown">
<button type="button" class="btn p-0 dropdown-toggle hide-arrow"
data-bs-toggle="dropdown">
<i class="bx bx-dots-vertical-rounded fs-5"></i>
</button>
<div class="dropdown-menu dropdown-menu-end">
<a class="dropdown-item" href="javascript:void(0);"><i
class="bx bx-power-off me-1"></i> Wyłącz cykl</a>
<a class="dropdown-item" href="app-invoice-list.php"><i
class="bx bx-history me-1"></i> Historia wygenerowanych
faktur</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger" href="javascript:void(0);"><i
class="bx bx-archive-in me-1"></i> Archiwizuj cykl</a>
</div>
</div>
</div>
</td>
</tr>
<!-- Item 3 -->
<tr>
<td>3</td>
<td>
<div class="d-flex flex-column">
<a href="app-invoice-recurring-add.php" class="fw-bold text-body">Licencja
Oprogramowania Magico ERP</a>
<small class="text-muted">Proforma</small>
</div>
</td>
<td>
<div class="d-flex flex-column">
<span class="fw-semibold">Klinika Stomatologiczna DentaMed</span>
<small class="text-muted">NIP: 554-190-88-21</small>
</div>
</td>
<td>
<span class="text-heading">12 000,00 PLN</span>
</td>
<td>
<span class="fw-bold text-heading">14 760,00 PLN</span>
</td>
<td>
<span class="badge bg-label-warning"><i class="bx bx-calendar me-1"></i> Co 1 rok</span>
<br><small class="text-muted">Bezterminowo</small>
</td>
<td>
<span class="fw-medium text-muted">15.01.2027</span>
<br><small class="text-muted">Ostatnio: 15.01.2026</small>
</td>
<td>
<div class="form-check form-switch m-0" data-bs-toggle="tooltip" title="Włącz / Wyłącz cykl">
<input class="form-check-input" type="checkbox" style="cursor: pointer;">
<span class="badge bg-label-danger ms-1"><i class="bx bx-power-off me-1"></i> OFF</span>
</div>
</td>
<td class="text-center">
<div class="d-flex justify-content-center align-items-center gap-1">
<a href="app-invoice-recurring-add.php"
class="btn btn-icon btn-sm text-secondary" data-bs-toggle="tooltip"
title="Edytuj szablon">
<i class="bx bx-edit-alt fs-5"></i>
</a>
<div class="dropdown">
<button type="button" class="btn p-0 dropdown-toggle hide-arrow"
data-bs-toggle="dropdown">
<i class="bx bx-dots-vertical-rounded fs-5"></i>
</button>
<div class="dropdown-menu dropdown-menu-end">
<a class="dropdown-item text-success" href="javascript:void(0);"><i
class="bx bx-power-off me-1"></i> Włącz cykl</a>
<a class="dropdown-item" href="app-invoice-list.php"><i
class="bx bx-history me-1"></i> Historia wygenerowanych
faktur</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger" href="javascript:void(0);"><i
class="bx bx-archive-in me-1"></i> Archiwizuj cykl</a>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer d-flex justify-content-between align-items-center">
<span class="text-muted small">Wyświetlono 1-3 z 21 szablonów</span>
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm mb-0">
<li class="page-item disabled"><a class="page-link" href="#"><i
class="bx bx-chevron-left"></i></a></li>
<li class="page-item active"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item"><a class="page-link" href="#"><i class="bx bx-chevron-right"></i></a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<!-- Filter Column Right (Expandable / Collapsible) -->
<div class="col-lg-3" id="invoice-filters-col">
<div class="card sticky-top" style="top: 20px;">
<div class="card-header border-bottom d-flex justify-content-between align-items-center py-3">
<h5 class="card-title mb-0">Filtry cykli</h5>
<button type="button" class="btn-close text-reset small" aria-label="Close" id="close-filter-btn"
style="display:none;"></button>
</div>
<div class="card-body pt-4">
<div class="mb-3">
<label class="form-label">Kontrahent (Nabywca)</label>
<select class="select2 form-select" data-allow-clear="true">
<option value="">Wszyscy kontrahenci</option>
<option value="1">ABC Logistics Sp. z o.o.</option>
<option value="2">Bud-Spec Budownictwo S.A.</option>
<option value="3">Klinika Stomatologiczna DentaMed</option>
<option value="4">MCreo Mateusz Łada</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Status cyklu</label>
<div class="d-flex flex-column gap-2">
<div class="form-check">
<input name="status-radio" class="form-check-input" type="radio" value="" id="statusAll"
checked>
<label class="form-check-label" for="statusAll"> Wszystkie </label>
</div>
<div class="form-check">
<input name="status-radio" class="form-check-input" type="radio" value=""
id="statusActive">
<label class="form-check-label" for="statusActive"> tylko Aktywne </label>
</div>
<div class="form-check">
<input name="status-radio" class="form-check-input" type="radio" value=""
id="statusPaused">
<label class="form-check-label" for="statusPaused"> Wstrzymane (Pauza) </label>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Częstotliwość wystawiania</label>
<select class="form-select">
<option value="">Wszystkie częstotliwości</option>
<option value="1M">Co 1 miesiąc</option>
<option value="3M">Co 3 miesiące (Kwartalnie)</option>
<option value="12M">Co 1 rok (Rocznie)</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Zakres dat najbliższej emisji</label>
<input type="text" class="form-control flatpickr-range"
placeholder="RRRR-MM-DD do RRRR-MM-DD" />
</div>
<div class="mb-3">
<label class="form-label">Tryb automatyzacji</label>
<select class="form-select">
<option value="">Wszystkie tryby</option>
<option value="auto">Automatyczne wystawianie i wysyłka</option>
<option value="draft">Tworzenie jako Szkic</option>
</select>
</div>
<div class="d-flex justify-content-end gap-2 pt-2">
<button class="btn btn-outline-secondary btn-sm">Wyczyść filtry</button>
<button class="btn btn-primary btn-sm">Filtruj</button>
</div>
</div>
</div>
</div>
</div>
</div>
<?php include '../../footer.php'; ?>
<script>
document.addEventListener('DOMContentLoaded', function () {
// Obsługa przełącznika panelu filtrów po prawej stronie
const filterBtn = document.getElementById('filter-toggle-btn');
const filtersCol = document.getElementById('invoice-filters-col');
const listCol = document.getElementById('invoice-list-col');
if (filterBtn && filtersCol && listCol) {
filterBtn.addEventListener('click', function () {
filtersCol.classList.toggle('d-none');
if (filtersCol.classList.contains('d-none')) {
listCol.classList.remove('col-lg-9');
listCol.classList.add('col-lg-12');
} else {
listCol.classList.remove('col-lg-12');
listCol.classList.add('col-lg-9');
}
});
}
// Flatpickr range jeśli obecny
if (typeof flatpickr !== 'undefined') {
document.querySelectorAll('.flatpickr-range').forEach(function (element) {
flatpickr(element, {
mode: 'range',
dateFormat: 'Y-m-d'
});
});
}
});
</script>
</body>
</html>
+2 -78
View File
@@ -62,23 +62,7 @@ include '../../header-invoice.php';
</div>
</div>
<!-- Internal Notes & Tags Card -->
<div class="card mb-3">
<div class="card-header border-bottom py-3">
<h6 class="card-title m-0"><i class="bx bx-purchase-tag-alt me-2"></i>Notatki wewnętrzne i Tagi</h6>
</div>
<div class="card-body pt-3">
<div class="mb-3">
<label class="form-label fw-bold">Komentarz wewnętrzny</label>
<textarea class="form-control" rows="3" placeholder="Notatka widoczna tylko dla zespołu..."></textarea>
</div>
<div>
<label class="form-label fw-bold">Tagi</label>
<input id="tagify-purchase" class="form-control" name="tagify-purchase" value="koszty biurowe, sprzęt IT" placeholder="Wpisz tag i wciśnij enter">
<small class="text-muted d-block mt-1">Ułatwiają wyszukiwanie i grupowanie faktur w systemie.</small>
</div>
</div>
</div>
</div>
@@ -99,26 +83,6 @@ include '../../header-invoice.php';
<span class="text-muted fw-bold">Przeciągnij fakturę tutaj lub kliknij</span>
<span class="small text-muted">(PDF, JPG, PNG)</span>
</div>
<!-- Wgrany plik / Podgląd (Początkowo ukryty, pokazywany w symulacji OCR) -->
<div class="d-flex align-items-center p-3 mb-3 border rounded bg-lighter d-none" id="ocr-preview-container">
<div class="me-3 cursor-pointer" data-bs-toggle="modal" data-bs-target="#invoicePreviewModal" title="Kliknij, aby powiększyć">
<img src="https://via.placeholder.com/150x200.png?text=Faktura+OCR" alt="Faktura" class="rounded border shadow-sm" style="width: 50px; height: 70px; object-fit: cover; opacity: 0.9; transition: 0.3s;" onmouseover="this.style.opacity='1'; this.style.transform='scale(1.05)';" onmouseout="this.style.opacity='0.9'; this.style.transform='scale(1)';">
</div>
<div class="flex-grow-1">
<h6 class="mb-1 text-truncate" style="max-width: 180px;">skan_faktury_02_2026.png</h6>
<small class="text-muted">Odczytano dane z pliku</small>
</div>
<div>
<button type="button" class="btn btn-sm btn-icon btn-outline-primary me-1" data-bs-toggle="modal" data-bs-target="#invoicePreviewModal" title="Pełny podgląd">
<i class="bx bx-fullscreen"></i>
</button>
<button type="button" class="btn btn-sm btn-icon btn-outline-danger" title="Usuń plik" onclick="document.getElementById('ocr-preview-container').classList.add('d-none');">
<i class="bx bx-trash"></i>
</button>
</div>
</div>
<button type="button" class="btn btn-label-primary w-100 mb-4" id="btn-ocr-read-page">
<i class="bx bx-scan me-1"></i> Odczytaj dane z pliku (OCR)
</button>
@@ -222,6 +186,7 @@ include '../../header-invoice.php';
</div>
</div>
</div>
</div>
<div class="card mb-4">
<div
class="card-header bg-label-primary py-2 d-flex flex-wrap justify-content-between align-items-center gap-3">
@@ -336,20 +301,6 @@ include '../../header-invoice.php';
<textarea class="form-control bg-lighter" rows="3"
placeholder="Wpisz uwagi widoczne tylko dla nas..."></textarea>
<div class="mt-3 mb-2">
<label class="form-label d-flex align-items-center fw-bold">
Numer konta bankowego (IBAN)
<i class="bx bx-info-circle ms-1 fs-6 text-muted" data-bs-toggle="tooltip"
data-bs-placement="top"
title="Numer konta sprzedawcy, na który zostanie wykonany przelew.">
</i>
</label>
<div class="input-group input-group-merge">
<span class="input-group-text"><i class="bx bx-credit-card"></i></span>
<input type="text" class="form-control" placeholder="PL 00 0000 0000 0000 0000 0000 0000">
</div>
</div>
<div class="row g-2 mb-3 mt-3">
<div class="col-md-4">
<label class="form-label d-flex align-items-center">
@@ -570,12 +521,6 @@ include '../../header-invoice.php';
const totalEl = document.getElementById('finalTotal');
if (totalEl) totalEl.innerText = '553,50 zł';
// Pokazanie podglądu pliku z OCR
const previewContainer = document.getElementById('ocr-preview-container');
if (previewContainer) {
previewContainer.classList.remove('d-none');
}
// 4. Restore Button
btn.disabled = false;
btn.innerHTML = '<i class="bx bx-check me-1"></i> Dane odczytane poprawnie';
@@ -602,27 +547,6 @@ include '../../header-invoice.php';
});
</script>
<!-- Modal: Podgląd Faktury -->
<div class="modal fade" id="invoicePreviewModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header border-bottom">
<h5 class="modal-title d-flex align-items-center">
<i class="bx bx-image me-2"></i> Podgląd dokumentu: skan_faktury_02_2026.png
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zamknij"></button>
</div>
<div class="modal-body p-0 bg-lighter d-flex justify-content-center align-items-center" style="min-height: 70vh;">
<!-- Placeholder obrazka, docelowo pełen skan lub PDF w iframe -->
<img src="https://via.placeholder.com/800x1131.png?text=Faktura+Powi%C4%99kszona" alt="Podgląd Faktury" class="img-fluid" style="max-height: 80vh; object-fit: contain; box-shadow: 0 0 10px rgba(0,0,0,0.1);">
</div>
<div class="modal-footer border-top">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Zamknij</button>
</div>
</div>
</div>
</div>
</body>
</html>
+2 -49
View File
@@ -395,15 +395,8 @@ include '../../header-invoice.php';
</td>
<td>1</td>
<td>
<span class="fw-medium d-block mb-1">
FZ/2026/02/001
<i class="bx bx-message-square-detail text-primary ms-1 cursor-pointer" data-bs-toggle="tooltip" title="Sprawdzić czy na pewno to jest roczna opłata"></i>
</span>
<small class="text-muted d-block mb-1">Podstawowa | Hosting i domeny</small>
<div>
<span class="badge bg-label-info" style="font-size: 0.7em;">IT</span>
<span class="badge bg-label-info" style="font-size: 0.7em;">Koszty stałe</span>
</div>
<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>
@@ -440,7 +433,6 @@ include '../../header-invoice.php';
class="bx bx-money me-1"></i> Rozlicz fakturę</a>
<div class="dropdown-divider"></div>
<h6 class="dropdown-header text-uppercase ps-3">System</h6>
<a class="dropdown-item" href="javascript:void(0);" data-bs-toggle="modal" data-bs-target="#invoiceMetaModal"><i class="bx bx-purchase-tag-alt me-1"></i> Tagi i Komentarz</a>
<a class="dropdown-item" href="javascript:void(0);"><i
class="bx bx-duplicate me-1"></i> Duplikuj jako nową</a>
<a class="dropdown-item text-danger" href="javascript:void(0);"><i
@@ -501,7 +493,6 @@ include '../../header-invoice.php';
class="bx bx-money me-1"></i> Rozlicz fakturę</a>
<div class="dropdown-divider"></div>
<h6 class="dropdown-header text-uppercase ps-3">System</h6>
<a class="dropdown-item" href="javascript:void(0);" data-bs-toggle="modal" data-bs-target="#invoiceMetaModal"><i class="bx bx-purchase-tag-alt me-1"></i> Tagi i Komentarz</a>
<a class="dropdown-item" href="javascript:void(0);"><i
class="bx bx-duplicate me-1"></i> Duplikuj jako nową</a>
<a class="dropdown-item text-danger" href="javascript:void(0);"><i
@@ -564,7 +555,6 @@ include '../../header-invoice.php';
class="bx bx-money me-1"></i> Rozlicz fakturę</a>
<div class="dropdown-divider"></div>
<h6 class="dropdown-header text-uppercase ps-3">System</h6>
<a class="dropdown-item" href="javascript:void(0);" data-bs-toggle="modal" data-bs-target="#invoiceMetaModal"><i class="bx bx-purchase-tag-alt me-1"></i> Tagi i Komentarz</a>
<a class="dropdown-item" href="javascript:void(0);"><i
class="bx bx-duplicate me-1"></i> Duplikuj jako nową</a>
<a class="dropdown-item text-danger" href="javascript:void(0);"><i
@@ -625,7 +615,6 @@ include '../../header-invoice.php';
class="bx bx-money me-1"></i> Rozlicz fakturę</a>
<div class="dropdown-divider"></div>
<h6 class="dropdown-header text-uppercase ps-3">System</h6>
<a class="dropdown-item" href="javascript:void(0);" data-bs-toggle="modal" data-bs-target="#invoiceMetaModal"><i class="bx bx-purchase-tag-alt me-1"></i> Tagi i Komentarz</a>
<a class="dropdown-item" href="javascript:void(0);"><i
class="bx bx-duplicate me-1"></i> Duplikuj jako nową</a>
<a class="dropdown-item text-danger" href="javascript:void(0);"><i
@@ -927,7 +916,6 @@ include '../../header-invoice.php';
<div class="dropdown-divider"></div>
<h6 class="dropdown-header text-uppercase ps-3">System</h6>
<li><button class="dropdown-item" data-bs-toggle="modal" data-bs-target="#invoiceMetaModal"><i class="bx bx-purchase-tag-alt me-2"></i> Tagi i Komentarz</button></li>
<li><button class="dropdown-item"><i class="bx bx-duplicate me-2"></i> Duplikuj jako nową
</button></li>
<li><button class="dropdown-item text-danger"><i class="bx bx-trash me-2"></i> Usuń fakturę
@@ -1056,15 +1044,6 @@ include '../../header-invoice.php';
</p>
</div>
<h6 class="text-uppercase text-muted fw-bold mb-3 border-bottom pb-2">Komentarz wewnętrzny i Tagi</h6>
<p class="mb-2 text-body small">
<strong>Notatka:</strong> Sprawdzić czy na pewno to jest roczna opłata.
</p>
<div class="mb-4">
<span class="badge bg-label-info">IT</span>
<span class="badge bg-label-info">Koszty stałe</span>
</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
@@ -1205,32 +1184,6 @@ include '../../header-invoice.php';
</div>
</div>
<!-- Modal: Tagi i Komentarz -->
<div class="modal fade" id="invoiceMetaModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="bx bx-purchase-tag-alt text-primary me-2"></i> Tagi i Komentarz Wewnętrzny</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 fw-bold">Tagi</label>
<input id="tagify-modal" class="form-control" name="tagify-modal" value="IT, Koszty stałe" placeholder="Wpisz tag i wciśnij enter">
<small class="text-muted d-block mt-1">Tagi ułatwiają grupowanie i wyszukiwanie faktur.</small>
</div>
<div class="mb-3">
<label class="form-label fw-bold">Komentarz wewnętrzny</label>
<textarea class="form-control" rows="4" placeholder="Notatka systemowa widoczna tylko dla zespołu...">Sprawdzić czy na pewno to jest roczna opłata.</textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-label-secondary" data-bs-dismiss="modal">Anuluj</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Zapisz</button>
</div>
</div>
</div>
</div>
<?php include '../../footer.php'; ?>
@@ -1,338 +0,0 @@
<?php
$enablePrototypeComments = true;
include '../../header-invoice.php';
?>
<div class="container-fluid flex-grow-1 container-p-y">
<h4 class="py-3 mb-4">
<span class="text-muted fw-light">Sprzedaż / Ustawienia /</span> Powiadomienia e-mail (Faktury cykliczne)
</h4>
<div class="row">
<!-- Lewa Kolumna: Konfigurator -->
<div class="col-lg-5 mb-4 mb-lg-0">
<!-- Wybór Profilu -->
<div class="card mb-4">
<div class="card-header border-bottom d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">Wybierz Profil Powiadomień</h5>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-label-danger"><i class="bx bx-trash me-1"></i> Usuń profil</button>
<button class="btn btn-sm btn-primary"><i class="bx bx-plus me-1"></i> Nowy profil</button>
</div>
</div>
<div class="card-body pt-4">
<div class="mb-3">
<label class="form-label">Aktualny profil</label>
<select class="form-select select2" id="profile-select">
<option value="1">Standardowy (Termin 30 dni)</option>
<option value="2">Szybki (Termin 3 dni)</option>
<option value="3">Tylko przypomnienia po terminie</option>
</select>
</div>
<div class="alert alert-info d-flex mb-0" role="alert">
<span class="badge badge-center rounded-pill bg-info border-label-info p-3 me-2"><i class="bx bx-info-circle fs-6"></i></span>
<div class="d-flex flex-column ps-1">
<h6 class="alert-heading d-flex align-items-center mb-1">Czym są profile wysyłki?</h6>
<span class="mb-0 fs-6">Profile pozwalają na zgrupowanie reguł wysyłki e-mail. Możesz stworzyć profil "Długi termin" z wieloma przypomnieniami i przypisać go do wybranych faktur cyklicznych, a dla innych użyć profilu "Szybki" tylko z jednym mailem po terminie.</span>
</div>
</div>
</div>
</div>
<!-- Harmonogram wysyłki -->
<div class="card mb-4">
<div class="card-header border-bottom d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">Harmonogram wysyłki</h5>
<button class="btn btn-sm btn-label-primary" data-bs-toggle="modal" data-bs-target="#modalRule"><i class="bx bx-plus me-1"></i> Dodaj regułę</button>
</div>
<div class="card-body pt-4">
<ul class="list-group list-group-flush mb-0">
<li class="list-group-item d-flex justify-content-between align-items-center px-0 pt-0 pb-3" style="cursor: pointer;">
<div class="d-flex align-items-center gap-2">
<span class="badge bg-label-success p-2"><i class="bx bx-check"></i></span>
<div>
<h6 class="mb-0">Wystawienie faktury</h6>
<small class="text-muted">Natychmiast (W dniu dokumentu)</small>
</div>
</div>
<button class="btn btn-icon btn-sm text-secondary" data-bs-toggle="modal" data-bs-target="#modalRule"><i class="bx bx-edit"></i></button>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center px-3 py-3 rounded border border-primary bg-label-primary mb-2 mt-2" style="cursor: pointer;">
<div class="d-flex align-items-center gap-2">
<span class="badge bg-primary p-2 text-white"><i class="bx bx-time"></i></span>
<div>
<h6 class="mb-0 text-primary fw-bold">Przypomnienie przed terminem</h6>
<small class="text-primary">14 dni przed upływem terminu</small>
</div>
</div>
<button class="btn btn-icon btn-sm text-primary" data-bs-toggle="modal" data-bs-target="#modalRule"><i class="bx bx-edit"></i></button>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center px-0 py-3" style="cursor: pointer;">
<div class="d-flex align-items-center gap-2">
<span class="badge bg-label-warning p-2"><i class="bx bx-error"></i></span>
<div>
<h6 class="mb-0">Przypomnienie przed terminem</h6>
<small class="text-muted">3 dni przed upływem terminu</small>
</div>
</div>
<button class="btn btn-icon btn-sm text-secondary" data-bs-toggle="modal" data-bs-target="#modalRule"><i class="bx bx-edit"></i></button>
</li>
</ul>
</div>
</div>
<!-- Treść wiadomości -->
<div class="card border border-primary">
<div class="card-header border-bottom bg-label-primary">
<h5 class="card-title mb-0 text-primary">Treść wiadomości <span class="fw-normal fs-6 d-block mt-1">Edytujesz: <strong>Przypomnienie przed terminem (14 dni)</strong></span></h5>
</div>
<div class="card-body pt-4">
<div class="mb-3">
<label class="form-label">Temat e-maila</label>
<input type="text" id="email-subject" class="form-control" value="Przypomnienie: Zbliża się termin płatności faktury {NUMER_FAKTURY}">
<div class="form-text">Zmienne: {NUMER_FAKTURY}, {NAZWA_KLIENTA}</div>
</div>
<div class="mb-3">
<label class="form-label">Nagłówek powitania</label>
<input type="text" id="email-heading" class="form-control" value="Cześć {IMIE_NABYWCY},">
</div>
<div class="mb-3">
<label class="form-label">Główna treść wiadomości</label>
<textarea id="email-body" class="form-control" rows="4">Przypominamy, że za 14 dni upływa termin płatności za Twoją ostatnią fakturę. W załączniku przesyłamy plik PDF z dokumentem. Będziemy wdzięczni za terminową wpłatę.</textarea>
</div>
<div class="mb-0">
<label class="form-label">Podpis / Stopka</label>
<input type="text" id="email-footer" class="form-control" value="Pozdrawiamy serdecznie,\nZespół TwojaFirma">
</div>
<!-- Zmienne do kliknięcia -->
<div class="mt-4 pt-3 border-top">
<small class="text-muted text-uppercase fw-bold d-block mb-2">Dostępne zmienne (kliknij, aby wstawić)</small>
<div class="d-flex flex-wrap gap-2" id="variable-badges">
<span class="badge bg-label-primary" style="cursor: pointer;" data-var="{NUMER_FAKTURY}">+ {NUMER_FAKTURY}</span>
<span class="badge bg-label-primary" style="cursor: pointer;" data-var="{NAZWA_KLIENTA}">+ {NAZWA_KLIENTA}</span>
<span class="badge bg-label-primary" style="cursor: pointer;" data-var="{IMIE_NABYWCY}">+ {IMIE_NABYWCY}</span>
<span class="badge bg-label-primary" style="cursor: pointer;" data-var="{KWOTA_BRUTTO}">+ {KWOTA_BRUTTO}</span>
<span class="badge bg-label-primary" style="cursor: pointer;" data-var="{TERMIN_PLATNOSCI}">+ {TERMIN_PLATNOSCI}</span>
<span class="badge bg-label-primary" style="cursor: pointer;" data-var="{POZOSTALO_DNI_DO_TERMINU}">+ {POZOSTALO_DNI_DO_TERMINU}</span>
<span class="badge bg-label-primary" style="cursor: pointer;" data-var="{LINK_DO_OPLATY}">+ {LINK_DO_OPLATY}</span>
</div>
<div class="form-text mt-2">Tag zostanie wstawiony w polu, w którym ostatnio pisałeś.</div>
</div>
</div>
</div>
</div>
<!-- Prawa Kolumna: Podgląd Live -->
<div class="col-lg-7">
<div class="card h-100" style="background-color: #f8f9fa;">
<div class="card-header border-bottom bg-white d-flex justify-content-between align-items-center">
<div>
<small class="text-muted d-block mb-1">Podgląd na żywo</small>
<h6 class="mb-0">Temat: <span id="preview-subject" class="fw-normal">Przypomnienie: Zbliża się termin płatności faktury FV/2026/08/12</span></h6>
</div>
<span class="badge bg-label-secondary">Wersja Desktop</span>
</div>
<!-- Email Canvas -->
<div class="card-body d-flex justify-content-center align-items-start pt-5 pb-5 overflow-auto" style="min-height: 600px;">
<div class="email-container bg-white rounded shadow-sm" style="width: 100%; max-width: 600px; overflow: hidden; font-family: Arial, sans-serif;">
<!-- Email Header Logo (Image based on screenshot) -->
<div class="text-center pt-5 pb-3 bg-white">
<div style="background-color: #8ed689; width: 120px; height: 75px; border-radius: 8px; margin: 0 auto; position: relative; display: flex; align-items: center; justify-content: center;">
<!-- Simple illustration placeholder for the green money/face -->
<div style="width: 10px; height: 10px; background-color: #333; border-radius: 50%; position: absolute; left: 35px;"></div>
<div style="width: 10px; height: 10px; background-color: #333; border-radius: 50%; position: absolute; right: 35px;"></div>
<div style="width: 16px; height: 8px; border-bottom: 3px solid #333; border-radius: 50%; position: absolute; top: 40px;"></div>
</div>
</div>
<!-- Email Content Container -->
<div class="p-4 p-md-5 pt-0 bg-white">
<h3 id="preview-heading" class="fw-light mb-4" style="color: #2b5c8f; font-size: 26px;">Szanowni Państwo</h3>
<p id="preview-body" style="color: #4a5568; line-height: 1.6; margin-bottom: 40px; font-size: 15px;">
Dziękujemy za skorzystanie z naszych usług.<br><br>
Uprzejmie informujemy, że w dniu 22 lipca 2026 została wystawiona dla Państwa Faktura nr FV/31/07/2026 na kwotę 160,00 zł. Plik PDF znajduje się w załączniku.
</p>
<!-- ZABLOKOWANY MODUŁ Z FAKTURĄ (LOCKED SUMMARY TABLE) -->
<div class="invoice-summary border-0 rounded bg-lighter p-4 mb-4 text-center" style="border-left: 4px solid #2b5c8f !important;">
<div class="d-flex justify-content-between align-items-center mb-2 text-start">
<span class="text-muted">Typ faktury:</span>
<span class="text-dark">Faktura</span>
</div>
<div class="d-flex justify-content-between align-items-center mb-2 text-start">
<span class="text-muted">Termin płatności:</span>
<span class="text-dark fw-medium">29 lipca 2026</span>
</div>
<div class="d-flex justify-content-between align-items-center mb-3 text-start">
<span class="text-muted">Kwota do zapłaty:</span>
<span class="fw-bold text-dark">160,00 PLN</span>
</div>
<a href="#" class="btn btn-primary w-100 mt-2" style="background-color: #2b5c8f; border-color: #2b5c8f; padding: 10px 20px; text-decoration: none; display: inline-block;">
Zobacz dokument i opłać online
</a>
</div>
<!-- END LOCKED MODULE -->
<p id="preview-footer" style="color: #777; margin-top: 30px; font-size: 0.95em;">
Pozdrawiamy serdecznie,<br>Zespół TwojaFirma
</p>
</div>
<!-- Email Footer App Info -->
<div class="bg-light text-center py-4 px-3 border-top">
<small class="text-muted d-block mb-1" style="font-size: 11px;">Wiadomość wygenerowana automatycznie. Prosimy nie odpowiadać.</small>
<small class="text-muted d-block mb-3" style="font-size: 11px;">Wiadomość wysłana przez firmę TwojaFirma, z siedzibą w Stalowej Woli, al. Jana Pawła II 60F,<br>NIP: 865 256 70 62, REGON: 368428365.</small>
<small class="text-dark fw-bold d-block" style="font-size: 12px;">Magico Software</small>
<small class="text-muted d-block" style="font-size: 11px;">support@magico.pl | +48 665 408 808</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal: Dodaj Regułę -->
<div class="modal fade" id="modalRule" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalRuleTitle">Konfiguracja reguły wysyłki</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
<div class="col mb-3">
<label class="form-label">Wydarzenie początkowe (Wyzwalacz)</label>
<select class="form-select">
<option value="issue">Wystawienie nowej faktury</option>
<option value="due">Termin płatności faktury</option>
</select>
</div>
</div>
<div class="row g-2">
<div class="col mb-0">
<label class="form-label">Kiedy wysłać e-mail?</label>
<select class="form-select">
<option value="0">Dokładnie w tym dniu</option>
<option value="before">Przed wydarzeniem</option>
<option value="after">Po wydarzeniu</option>
</select>
</div>
<div class="col mb-0">
<label class="form-label">Ile dni?</label>
<input type="number" class="form-control" value="0">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-label-secondary" data-bs-dismiss="modal">Anuluj</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Zapisz regułę</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Inputs
const inputSubject = document.getElementById('email-subject');
const inputHeading = document.getElementById('email-heading');
const inputBody = document.getElementById('email-body');
const inputFooter = document.getElementById('email-footer');
// Previews
const previewSubject = document.getElementById('preview-subject');
const previewHeading = document.getElementById('preview-heading');
const previewBody = document.getElementById('preview-body');
const previewFooter = document.getElementById('preview-footer');
// Mocks for variables
const mockVars = {
'{NUMER_FAKTURY}': 'FV/2026/08/12',
'{NAZWA_KLIENTA}': 'ABC Logistics',
'{IMIE_NABYWCY}': 'Jan',
'{KWOTA_BRUTTO}': '1 845,00 PLN',
'{TERMIN_PLATNOSCI}': '15.08.2026',
'{POZOSTALO_DNI_DO_TERMINU}': '14',
'{LINK_DO_OPLATY}': 'https://magico.pl/pay/123'
};
function replaceVariables(text) {
let result = text;
for (const [key, value] of Object.entries(mockVars)) {
result = result.replace(new RegExp(key, 'g'), value);
}
return result;
}
function updatePreview() {
previewSubject.innerText = replaceVariables(inputSubject.value);
previewHeading.innerText = replaceVariables(inputHeading.value);
// Preserve line breaks in body
const bodyText = replaceVariables(inputBody.value);
previewBody.innerHTML = bodyText.replace(/\n/g, '<br>');
// Preserve line breaks in footer
const footerText = replaceVariables(inputFooter.value);
previewFooter.innerHTML = footerText.replace(/\n/g, '<br>');
}
// Event Listeners
inputSubject.addEventListener('input', updatePreview);
inputHeading.addEventListener('input', updatePreview);
inputBody.addEventListener('input', updatePreview);
inputFooter.addEventListener('input', updatePreview);
// Initial render
updatePreview();
// Variable insertion logic
let lastFocusedInput = inputBody; // default
const allInputs = [inputSubject, inputHeading, inputBody, inputFooter];
allInputs.forEach(input => {
input.addEventListener('focus', function() {
lastFocusedInput = this;
});
});
document.querySelectorAll('#variable-badges .badge').forEach(badge => {
badge.addEventListener('click', function() {
const varText = this.getAttribute('data-var');
if (lastFocusedInput) {
const start = lastFocusedInput.selectionStart;
const end = lastFocusedInput.selectionEnd;
const text = lastFocusedInput.value;
lastFocusedInput.value = text.substring(0, start) + varText + text.substring(end);
lastFocusedInput.selectionStart = lastFocusedInput.selectionEnd = start + varText.length;
lastFocusedInput.focus();
updatePreview();
}
});
});
});
</script>
<?php include '../../footer.php'; ?>
+2 -12
View File
@@ -108,7 +108,7 @@ $currentPage = basename($_SERVER['PHP_SELF']);
</li>
<li
class="menu-item <?php echo ($currentPage == 'app-invoice-list.php' || $currentPage == 'app-invoice-recurring-list.php' || $currentPage == 'app-invoice-recurring-add.php' || $currentPage == 'app-purchase-list.php' || $currentPage == 'app-purchase-calendar.php' || $currentPage == 'app-products.php') ? 'active open' : ''; ?>">
class="menu-item <?php echo ($currentPage == 'app-invoice-list.php' || $currentPage == 'app-purchase-list.php' || $currentPage == 'app-purchase-calendar.php' || $currentPage == 'app-products.php') ? 'active open' : ''; ?>">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon tf-icons bx bx-cart-alt"></i>
<div class="text-truncate" data-i18n="Faktury i sprzedaż">Faktury i sprzedaż</div>
@@ -119,11 +119,6 @@ $currentPage = basename($_SERVER['PHP_SELF']);
<div class="text-truncate" data-i18n="Faktury sprzedaż">Faktury sprzedaż</div>
</a>
</li>
<li class="menu-item <?php echo ($currentPage == 'app-invoice-recurring-list.php' || $currentPage == 'app-invoice-recurring-add.php') ? 'active' : ''; ?>">
<a href="app-invoice-recurring-list.php" class="menu-link">
<div class="text-truncate" data-i18n="Faktury cykliczne">Faktury cykliczne</div>
</a>
</li>
<li class="menu-item <?php echo ($currentPage == 'app-purchase-list.php') ? 'active' : ''; ?>">
<a href="app-purchase-list.php" class="menu-link">
<div class="text-truncate" data-i18n="Faktury zakupy">Faktury zakupy</div>
@@ -156,7 +151,7 @@ $currentPage = basename($_SERVER['PHP_SELF']);
</li>
<li
class="menu-item <?php echo ($currentPage == 'sys-export.php' || $currentPage == 'sys-dictionaries.php' || $currentPage == 'sys-settings.php' || $currentPage == 'app-settings-notifications.php' || $currentPage == 'sys-logs.php') ? 'active open' : ''; ?>">
class="menu-item <?php echo ($currentPage == 'sys-export.php' || $currentPage == 'sys-dictionaries.php' || $currentPage == 'sys-settings.php' || $currentPage == 'sys-logs.php') ? 'active open' : ''; ?>">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon tf-icons bx bx-cog"></i>
<div class="text-truncate" data-i18n="System">System</div>
@@ -177,11 +172,6 @@ $currentPage = basename($_SERVER['PHP_SELF']);
<div class="text-truncate" data-i18n="Ustawienia">Ustawienia</div>
</a>
</li>
<li class="menu-item <?php echo ($currentPage == 'app-settings-notifications.php') ? 'active' : ''; ?>">
<a href="app-settings-notifications.php" class="menu-link">
<div class="text-truncate" data-i18n="Powiadomienia (Cykliczne)">Powiadomienia (Cykliczne)</div>
</a>
</li>
<li class="menu-item <?php echo ($currentPage == 'sys-logs.php') ? 'active' : ''; ?>">
<a href="sys-logs.php" class="menu-link">
<div class="text-truncate" data-i18n="Logi zdarzeń">Logi zdarzeń</div>
+12 -87
View File
@@ -24,10 +24,8 @@ include '../../header-invoice.php';
<a class="nav-link w-100 text-start text-body" href="javascript:void(0);">Dokumenty</a>
</li>
<li class="nav-item mt-1">
<a class="nav-link w-100 text-start text-body" href="sys-settings-categories.php">Kategorie kosztów</a>
</li>
<li class="nav-item mt-1">
<a class="nav-link w-100 text-start text-body" href="#kontaBankowe">Rachunki bankowe</a>
<a class="nav-link w-100 text-start text-body" href="sys-settings-categories.php">Kategorie
kosztów</a>
</li>
<li class="nav-item mt-1">
<a class="nav-link w-100 text-start text-body" href="javascript:void(0);">Wysyłka e-mail</a>
@@ -59,6 +57,16 @@ include '../../header-invoice.php';
<div class="input-group"><input type="text" class="form-control" id="input446"
value="524220457"><!----><!----><!----></div>
</div>
<div class="mb-3"><label for="input446" class="form-label d-flex align-items-center">Nazwa
banku</label>
<div class="input-group"><input type="text" class="form-control" id="input446"
value="mBank (SWIFT BREXPLPWMBK)"><!----><!----><!----></div>
</div>
<div class="mb-3"><label for="input446" class="form-label d-flex align-items-center">Numer
konta</label>
<div class="input-group"><input type="text" class="form-control" id="input446"
value="24 1140 2004 0000 3502 8330 0117"><!----><!----><!----></div>
</div>
</div>
<div class="px-3">
<div class="mb-3"><label for="input446"
@@ -138,46 +146,6 @@ include '../../header-invoice.php';
</div>
</div>
<!-- Konta Bankowe Module -->
<div class="card mb-4" id="kontaBankowe">
<div class="card-header border-bottom d-flex justify-content-between align-items-center p-3">
<h5 class="card-title mb-0">Rachunki bankowe</h5>
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#modalBank"><i class="bx bx-plus me-1"></i> Dodaj rachunek</button>
</div>
<div class="card-body pt-3">
<div class="alert alert-primary d-flex align-items-center" role="alert">
<i class="bx bx-info-circle me-2"></i>
<span>Przy zmianie waluty na fakturze, system automatycznie podstawi Twój domyślny rachunek dla tej waluty.</span>
</div>
<div class="table-responsive text-nowrap">
<table class="table table-hover border-top">
<thead>
<tr>
<th>Numer IBAN (Rachunek)</th>
<th>SWIFT</th>
<th>Waluta</th>
<th>Domyślny</th>
<th>Akcje</th>
</tr>
</thead>
<tbody class="table-border-bottom-0">
<tr>
<td><span class="fw-medium">24 1140 2004 0000 3502 8330 0117</span></td>
<td>BREXPLPWMBK</td>
<td><span class="badge bg-label-secondary">PLN</span></td>
<td><span class="badge bg-label-success"><i class="bx bx-check"></i> Tak</span></td>
<td>
<button class="btn btn-sm btn-icon text-secondary" data-bs-toggle="modal" data-bs-target="#modalBank"><i class="bx bx-edit"></i></button>
<button class="btn btn-sm btn-icon text-danger"><i class="bx bx-trash"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="card">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">Logo na dokumentach</h5>
@@ -212,49 +180,6 @@ include '../../header-invoice.php';
</div>
<div class="content-backdrop fade"></div>
<!-- Modal: Dodaj Rachunek -->
<div class="modal fade" id="modalBank" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Dodaj rachunek bankowy</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">Numer IBAN / Rachunku</label>
<input type="text" class="form-control" placeholder="PL...">
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Kod SWIFT</label>
<input type="text" class="form-control" placeholder="Np. BREXPLPW...">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Waluta</label>
<select class="form-select">
<option value="PLN" selected>PLN</option>
<option value="EUR">EUR</option>
<option value="USD">USD</option>
<option value="GBP">GBP</option>
</select>
</div>
</div>
<div class="form-check mt-2">
<input class="form-check-input" type="checkbox" id="defaultBankCheck" checked>
<label class="form-check-label fw-bold" for="defaultBankCheck">
Ustaw jako domyślny rachunek dla tej waluty
</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-label-secondary" data-bs-dismiss="modal">Anuluj</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Zapisz rachunek</button>
</div>
</div>
</div>
</div>
<?php include '../../footer.php'; ?>
</body>
-165
View File
@@ -1,165 +0,0 @@
<!-- Modal Rezerwacji / Zapytania Ofertowego (Standard UX/UI 2026) -->
<div class="modal fade" id="bookingModal" tabindex="-1" aria-labelledby="bookingModalLabel" 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); overflow: hidden;">
<!-- Spokojny, czytelny nagłówek modala -->
<div class="modal-header bg-white border-bottom py-3 px-4">
<div class="d-flex align-items-center gap-2">
<div class="rounded-circle d-flex align-items-center justify-content-center" style="width: 36px; height: 36px; background: var(--primary-light); color: var(--primary); font-size: 1.2rem;">
<i class='bx bx-calendar-check'></i>
</div>
<div>
<h5 class="modal-title fw-bold text-navy mb-0" id="bookingModalLabel" style="font-size: 1.05rem;">
Zapytanie o Dostępność & Rezerwacja Terminu
</h5>
<small class="text-muted" style="font-size: 0.76rem;">
Zlecenie trafi bezpośrednio do aplikacji mobilnej pilota z możliwością natychmiastowej akceptacji
</small>
</div>
</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 (Spokojna karta) -->
<div class="d-flex align-items-center justify-content-between p-3 rounded-3 mb-4" style="background: #f8f9fd; border: 1px solid var(--border-color);">
<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, var(--primary) 0%, #767bf7 100%); font-size: 1.1rem; flex-shrink: 0;">
OK
</div>
<div>
<div class="d-flex align-items-center gap-2">
<span class="fw-bold text-dark" id="bookingPilotNameDisplay" style="font-size: 0.95rem;">Oskar Kaczmarek</span>
<span class="pilot-academy-badge" style="font-size: 0.68rem; padding: 2px 8px;">
<i class='bx bx-check-shield'></i> Superpilot Academy
</span>
</div>
<div class="text-muted small d-flex align-items-center gap-2 mt-1" style="font-size: 0.76rem;">
<span><i class='bx bx-compass text-primary'></i> Pilot Wycieczek & Przewodnik</span>
<span>•</span>
<span><i class='bx bx-map-pin text-muted'></i> Baza: Warszawa</span>
</div>
</div>
</div>
<div class="text-end">
<span class="profile-meta-rating" style="font-size: 0.75rem;">
<i class='bx bxs-star text-warning'></i> 4.9 (18 opinii)
</span>
</div>
</div>
<input type="hidden" id="bookingPilotId" value="1">
<input type="hidden" id="bookingPilotName" value="Oskar Kaczmarek">
<!-- Sekcja 1: Wybór wycieczki z Travel Managera -->
<div class="mb-3">
<label class="form-label fw-bold text-dark small mb-1">
Powiąż z imprezą w Travel Managerze:
</label>
<select class="form-select form-select-sm fw-semibold" id="bookingTourSelect" style="font-size: 0.84rem; padding: 0.5rem 0.75rem;">
<option value="12" selected>Wycieczka #12: Klasyczne Włochy (16.09 - 24.09.2027) • 44 uczestników</option>
<option value="15">Wycieczka #15: Magia Toskanii i Rzym (05.10 - 12.10.2027) • 38 uczestników</option>
<option value="21">Wycieczka #21: Andaluzja i Gibraltar (12.11 - 19.11.2027) • 40 uczestników</option>
<option value="new">+ Utwórz zapytanie niestandardowe / Inny termin...</option>
</select>
<div class="text-muted mt-1" style="font-size: 0.72rem;">
Wybranie imprezy automatycznie przeniesie ramowy program trasy do aplikacji pilota.
</div>
</div>
<!-- Sekcja 2: Termin i Trasa (2 kolumny) -->
<div class="row g-3 mb-3">
<div class="col-md-6">
<label class="form-label fw-bold text-dark small mb-1">Termin realizacji 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>
</div>
</div>
<div class="col-md-6">
<label class="form-label fw-bold text-dark small mb-1">Destynacja i 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>
</div>
</div>
</div>
<!-- Sekcja 3: Finanse i Świadczenia (2 kolumny) -->
<div class="row g-3 mb-3">
<div class="col-md-6">
<label class="form-label fw-bold text-dark small mb-1">Proponowana stawka dzienna netto:</label>
<div class="input-group input-group-sm mb-1">
<span class="input-group-text bg-light fw-bold text-dark">PLN</span>
<input type="number" class="form-control fw-bold text-dark" id="bookingRate" value="550" step="50">
<span class="input-group-text bg-light text-muted">/ dzień netto</span>
</div>
<div class="text-muted" style="font-size: 0.72rem;">
Szacowany koszt zlecenia (9 dni): <strong class="text-dark">4 950 PLN netto</strong>
</div>
</div>
<div class="col-md-6">
<label class="form-label fw-bold text-dark small mb-1">Świadczenia zapewniane przez biuro:</label>
<div class="p-2 border rounded-2 bg-light" style="font-size: 0.78rem;">
<div class="form-check mb-1">
<input class="form-check-input" type="checkbox" id="benefitHotel" checked>
<label class="form-check-label text-dark" for="benefitHotel">
Pokój 1-osobowy dla pilota w hotelach
</label>
</div>
<div class="form-check mb-1">
<input class="form-check-input" type="checkbox" id="benefitMeals" checked>
<label class="form-check-label text-dark" for="benefitMeals">
Wyżywienie (śniadania + obiadokolacje)
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="benefitInsurance" checked>
<label class="form-check-label text-dark" for="benefitInsurance">
Ubezpieczenie KL / NNW w trakcie trasy
</label>
</div>
</div>
</div>
</div>
<!-- Sekcja 4: Wiadomość dla pilota -->
<div class="mb-3">
<label class="form-label fw-bold text-dark small mb-1">Wiadomość / uwagi do zlecenia:</label>
<textarea class="form-control" rows="3" style="font-size: 0.82rem; line-height: 1.45;" placeholder="Dodatkowe informacje, specyfika grupy, zbiórka...">Dzień dobry Panie Oskarze, zapraszamy do poprowadzenia wycieczki objazdowej po Włoszech. Znając Pana referencje z poprzednich tras, liczymy na ponowną udaną współpracę!</textarea>
</div>
<!-- Dyskretny box o powiadomieniu PUSH -->
<div class="d-flex align-items-center gap-3 p-3 rounded-2" style="background: var(--primary-light); border: 1px solid var(--primary-border);">
<i class='bx bx-mobile-alt fs-3' style="color: var(--primary);"></i>
<div style="font-size: 0.78rem; line-height: 1.4; color: var(--dark-navy);">
<strong>Powiadomienie PUSH:</strong> Pilot otrzyma natychmiast zapytanie na telefonie w aplikacji <code>TravelLeader Mobile</code> i ma 24h na potwierdzenie dostępności.
</div>
</div>
</div>
<div class="modal-footer bg-light px-4 py-3 border-top d-flex justify-content-between align-items-center">
<span class="text-muted small" style="font-size: 0.74rem;">
<i class='bx bx-shield-check text-success me-1'></i> Bezpieczna rezerwacja w ekosystemie Travel Manager
</span>
<div class="d-flex align-items-center gap-2">
<button type="button" class="btn btn-outline-secondary btn-sm" 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 do pilota
</button>
</div>
</div>
</form>
</div>
</div>
</div>
-455
View File
@@ -1,455 +0,0 @@
<?php
$enablePrototypeComments = true;
include '../../header-telemetria.php';
?>
<link rel="stylesheet" href="<?= $basePath ?>/assets/vendor/libs/leaflet/leaflet.css" />
<style>
#installationsMap {
height: calc(100vh - 220px);
min-height: 480px;
width: 100%;
z-index: 1;
}
.map-sidebar-list {
max-height: calc(100vh - 420px);
min-height: 220px;
overflow-y: auto;
}
.map-marker-pin {
width: 32px;
height: 32px;
border-radius: 50% 50% 50% 0;
transform: rotate(-45deg);
border: 2px solid #fff;
box-shadow: 0 2px 6px rgba(67, 89, 113, 0.35);
display: flex;
align-items: center;
justify-content: center;
background-color: #696cff;
}
.map-marker-pin i {
transform: rotate(45deg);
color: #fff;
font-size: 14px;
line-height: 1;
}
.map-marker-pin.is-alert {
background-color: #ff3e1d;
}
.map-list-item {
cursor: pointer;
}
.map-list-item:hover,
.map-list-item.is-focused {
background-color: rgba(105, 108, 255, 0.08);
}
.map-list-item.is-hidden-on-map {
opacity: 0.55;
}
</style>
<div class="container-fluid flex-grow-1 container-p-y">
<h4 class="fw-bold py-3 mb-4">
Telemetria <span class="text-muted fw-light">/ Mapa instalacji</span>
</h4>
<div class="row g-4">
<div class="col-lg-8 col-xl-9">
<div class="card shadow-sm border-0 overflow-hidden">
<div id="installationsMap"></div>
</div>
</div>
<div class="col-lg-4 col-xl-3">
<div class="card shadow-sm border-0 h-100">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">Instalacje na mapie</h5>
<small class="text-muted">Wyszukaj, filtruj i zarządzaj widocznością</small>
</div>
<div class="card-body">
<div class="mb-3 mt-3">
<label class="form-label" for="mapSearchInput">Szukaj</label>
<div class="input-group input-group-merge">
<span class="input-group-text"><i class="bx bx-search"></i></span>
<input type="text" id="mapSearchInput" class="form-control" placeholder="Nazwa lub lokalizacja...">
</div>
</div>
<div class="mb-3">
<label class="form-label" for="mapTypeFilter">Typ</label>
<select id="mapTypeFilter" class="form-select">
<option value="">Wszystkie typy</option>
<option value="gas">Zbiornik Gazu</option>
<option value="farm">Kurnik / Farma</option>
</select>
</div>
<div class="mb-3">
<label class="form-label" for="mapStatusFilter">Status</label>
<select id="mapStatusFilter" class="form-select">
<option value="">Każdy status</option>
<option value="online">Online</option>
<option value="alert">Alert</option>
<option value="offline">Offline</option>
</select>
</div>
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="fw-medium">Widoczność</span>
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-primary" id="showAllInstallationsBtn">Wszystkie</button>
<button type="button" class="btn btn-outline-secondary" id="hideAllInstallationsBtn">Ukryj</button>
</div>
</div>
<div id="mapInstallationsList" class="map-sidebar-list border rounded"></div>
</div>
</div>
</div>
</div>
</div>
<?php include '../../footer.php'; ?>
<script src="<?= $basePath ?>/assets/vendor/libs/leaflet/leaflet.js"></script>
<script>
(function () {
const installations = [
{
id: 'inst-1',
name: 'Zbiornik LPG #1',
location: 'Stalowa Wola, ul. Przemysłowa 5',
type: 'gas',
typeLabel: 'Zbiornik Gazu',
status: 'online',
statusLabel: 'Online',
lat: 50.5826,
lng: 22.0534,
alert: null,
url: 'installation-live.php'
},
{
id: 'inst-2',
name: 'Kurnik B (Sektor 2)',
location: 'Farma Wola, Obiekt Główny',
type: 'farm',
typeLabel: 'Sterownik Kurnika',
status: 'online',
statusLabel: 'Online',
lat: 50.6012,
lng: 22.0918,
alert: null,
url: 'installation-live.php'
},
{
id: 'inst-3',
name: 'Zbiornik LPG #2',
location: 'Rzeszów, Baza Północ',
type: 'gas',
typeLabel: 'Zbiornik Gazu',
status: 'offline',
statusLabel: 'Offline',
lat: 50.0578,
lng: 22.0048,
alert: 'Brak łączności — brak sygnału od ponad 3 dni.',
url: 'installation-live.php'
},
{
id: 'inst-4',
name: 'Zbiornik LPG #3',
location: 'Lublin, ul. Metalurgiczna 12',
type: 'gas',
typeLabel: 'Zbiornik Gazu',
status: 'alert',
statusLabel: 'Alert',
lat: 51.2354,
lng: 22.5489,
alert: 'Niski poziom gazu — poziom poniżej 15%.',
url: 'installation-live.php'
},
{
id: 'inst-5',
name: 'Zbiornik LPG #4',
location: 'Tarnobrzeg, Stacja Przeładunkowa',
type: 'gas',
typeLabel: 'Zbiornik Gazu',
status: 'online',
statusLabel: 'Online',
lat: 50.5731,
lng: 21.6794,
alert: null,
url: 'installation-live.php'
},
{
id: 'inst-6',
name: 'Sterownik Farma Alpha',
location: 'Nisko, Sektor A',
type: 'farm',
typeLabel: 'Sterownik Kurnika',
status: 'online',
statusLabel: 'Online',
lat: 50.5198,
lng: 22.1396,
alert: null,
url: 'installation-live.php'
},
{
id: 'inst-7',
name: 'Zbiornik LPG #5',
location: 'Przemyśl, Baza Południe',
type: 'gas',
typeLabel: 'Zbiornik Gazu',
status: 'online',
statusLabel: 'Online',
lat: 49.7837,
lng: 22.7673,
alert: null,
url: 'installation-live.php'
}
];
const mapEl = document.getElementById('installationsMap');
if (!mapEl || typeof L === 'undefined') {
return;
}
const map = L.map(mapEl).setView([50.45, 22.15], 8);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
maxZoom: 18
}).addTo(map);
const markersById = {};
let closePopupTimer = null;
function createMarkerIcon(hasAlert) {
const pinClass = hasAlert ? ' is-alert' : '';
const iconHtml = hasAlert
? '<i class="bx bx-error"></i>'
: '<i class="bx bx-buildings"></i>';
return L.divIcon({
className: '',
html: '<div class="map-marker-pin' + pinClass + '">' + iconHtml + '</div>',
iconSize: [32, 32],
iconAnchor: [16, 32],
popupAnchor: [0, -28]
});
}
function buildPopupHtml(item) {
const alertBlock = item.alert
? '<div class="alert alert-danger py-2 px-3 mb-2 small">' +
'<i class="bx bx-error-circle me-1"></i>' + item.alert +
'</div>'
: '<p class="text-muted small mb-2">Brak aktywnych alertów</p>';
return (
'<div>' +
'<h6 class="mb-1 fw-bold">' + item.name + '</h6>' +
'<small class="text-muted d-block mb-2"><i class="bx bx-map me-1"></i>' + item.location + '</small>' +
alertBlock +
'<button type="button" class="btn btn-sm btn-primary w-100" onclick="window.location.href=\'' + item.url + '\'">' +
'<i class="bx bx-right-arrow-alt me-1"></i> Przejdź do instalacji' +
'</button>' +
'</div>'
);
}
function statusBadgeClass(status) {
if (status === 'online') return 'bg-success';
if (status === 'alert') return 'bg-danger';
return 'bg-secondary';
}
installations.forEach(function (item) {
const marker = L.marker([item.lat, item.lng], {
icon: createMarkerIcon(Boolean(item.alert)),
title: item.name
});
marker.bindPopup(buildPopupHtml(item));
marker.on('mouseover', function () {
if (closePopupTimer) {
clearTimeout(closePopupTimer);
closePopupTimer = null;
}
marker.openPopup();
});
marker.on('mouseout', function () {
closePopupTimer = setTimeout(function () {
marker.closePopup();
}, 280);
});
marker.on('popupopen', function () {
const popupEl = marker.getPopup().getElement();
if (!popupEl) return;
popupEl.addEventListener('mouseenter', function () {
if (closePopupTimer) {
clearTimeout(closePopupTimer);
closePopupTimer = null;
}
});
popupEl.addEventListener('mouseleave', function () {
closePopupTimer = setTimeout(function () {
marker.closePopup();
}, 200);
});
});
markersById[item.id] = {
item: item,
marker: marker,
visible: true,
matchesFilter: true
};
marker.addTo(map);
});
const listEl = document.getElementById('mapInstallationsList');
const searchInput = document.getElementById('mapSearchInput');
const typeFilter = document.getElementById('mapTypeFilter');
const statusFilter = document.getElementById('mapStatusFilter');
function renderList() {
const fragments = installations.map(function (item) {
const state = markersById[item.id];
if (!state.matchesFilter) {
return '';
}
const hiddenClass = state.visible ? '' : ' is-hidden-on-map';
const alertIcon = item.alert
? '<i class="bx bx-error-circle text-danger ms-1" title="Alert"></i>'
: '';
return (
'<div class="map-list-item border-bottom px-3 py-2' + hiddenClass + '" data-id="' + item.id + '">' +
'<div class="d-flex align-items-start gap-2">' +
'<div class="form-check form-switch mb-0 mt-1">' +
'<input class="form-check-input map-visibility-toggle" type="checkbox" role="switch" ' +
'data-id="' + item.id + '" ' + (state.visible ? 'checked' : '') + ' ' +
'aria-label="Widoczność ' + item.name + '">' +
'</div>' +
'<div class="flex-grow-1 min-w-0">' +
'<div class="d-flex justify-content-between align-items-start gap-2">' +
'<div class="min-w-0">' +
'<div class="fw-semibold text-truncate">' + item.name + alertIcon + '</div>' +
'<small class="text-muted text-truncate d-block">' + item.location + '</small>' +
'</div>' +
'<span class="badge ' + statusBadgeClass(item.status) + '">' + item.statusLabel + '</span>' +
'</div>' +
'<small class="text-muted">' + item.typeLabel + '</small>' +
'</div>' +
'</div>' +
'</div>'
);
}).join('');
listEl.innerHTML = fragments ||
'<div class="text-center text-muted py-4 px-3 small">Brak instalacji spełniających kryteria</div>';
}
function applyFilters() {
const query = (searchInput.value || '').trim().toLowerCase();
const type = typeFilter.value;
const status = statusFilter.value;
installations.forEach(function (item) {
const state = markersById[item.id];
const haystack = (item.name + ' ' + item.location).toLowerCase();
const matchesQuery = !query || haystack.indexOf(query) !== -1;
const matchesType = !type || item.type === type;
const matchesStatus = !status || item.status === status;
state.matchesFilter = matchesQuery && matchesType && matchesStatus;
const shouldShow = state.matchesFilter && state.visible;
if (shouldShow) {
if (!map.hasLayer(state.marker)) {
state.marker.addTo(map);
}
} else if (map.hasLayer(state.marker)) {
map.removeLayer(state.marker);
}
});
renderList();
}
function setAllVisibility(visible) {
installations.forEach(function (item) {
markersById[item.id].visible = visible;
});
applyFilters();
}
function focusInstallation(id) {
const state = markersById[id];
if (!state || !state.matchesFilter) return;
if (!state.visible) {
state.visible = true;
applyFilters();
}
map.flyTo([state.item.lat, state.item.lng], 12, { duration: 0.6 });
state.marker.openPopup();
listEl.querySelectorAll('.map-list-item').forEach(function (el) {
el.classList.toggle('is-focused', el.getAttribute('data-id') === id);
});
}
listEl.addEventListener('change', function (event) {
const toggle = event.target.closest('.map-visibility-toggle');
if (!toggle) return;
const id = toggle.getAttribute('data-id');
markersById[id].visible = toggle.checked;
applyFilters();
});
listEl.addEventListener('click', function (event) {
if (event.target.closest('.map-visibility-toggle')) return;
const row = event.target.closest('.map-list-item');
if (!row) return;
focusInstallation(row.getAttribute('data-id'));
});
searchInput.addEventListener('input', applyFilters);
typeFilter.addEventListener('change', applyFilters);
statusFilter.addEventListener('change', applyFilters);
document.getElementById('showAllInstallationsBtn').addEventListener('click', function () {
setAllVisibility(true);
});
document.getElementById('hideAllInstallationsBtn').addEventListener('click', function () {
setAllVisibility(false);
});
applyFilters();
setTimeout(function () {
map.invalidateSize();
}, 200);
})();
</script>
</body>
</html>
-61
View File
@@ -1,61 +0,0 @@
<?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">/ Logi systemowe</span>
</h4>
<div class="card shadow-sm border-0">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">Dziennik zdarzeń (Logi modułu)</h5>
</div>
<div class="table-responsive text-nowrap">
<table class="table table-hover">
<thead>
<tr>
<th>Data</th>
<th>Użytkownik</th>
<th>Operacja</th>
<th>Szczegóły</th>
</tr>
</thead>
<tbody class="table-border-bottom-0">
<tr>
<td>2026-06-19 16:30</td>
<td><span class="badge bg-label-primary"><i class="bx bx-user me-1"></i> Jan Kowalski</span></td>
<td><span class="fw-bold text-danger">Usunięcie urządzenia</span></td>
<td>ID: 1024</td>
</tr>
<tr>
<td>2026-06-19 16:20</td>
<td><span class="badge bg-label-secondary"><i class="bx bx-cog me-1"></i> System</span></td>
<td><span class="fw-bold text-warning">Zmiana statusu</span></td>
<td>ID: 1024, Status: Online → Offline (utrata łączności)</td>
</tr>
<tr>
<td>2026-06-19 16:15</td>
<td><span class="badge bg-label-primary"><i class="bx bx-user me-1"></i> Jan Kowalski</span></td>
<td><span class="fw-bold text-info">Edycja danych</span></td>
<td>ID: 1024, Edycja danych urządzenia.</td>
</tr>
<tr>
<td>2026-06-19 16:10</td>
<td><span class="badge bg-label-primary"><i class="bx bx-user me-1"></i> Jan Kowalski</span></td>
<td><span class="fw-bold text-success">Dodanie urządzenia</span></td>
<td>MAC: AA:BB:CC:DD:EE:FF, Nazwa: "Sterownik Klimatu Alpha"</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="content-backdrop fade"></div>
<?php include '../../footer.php'; ?>
</body>
</html>
+2 -32
View File
@@ -42,23 +42,11 @@ $currentPage = basename($_SERVER['PHP_SELF']);
</li>
</ul>
</li>
<li class="menu-item <?php echo in_array($currentPage, ['installations.php', 'installations-map.php'], true) ? 'active open' : ''; ?>">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<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>
<ul class="menu-sub">
<li class="menu-item <?php echo ($currentPage == 'installations.php') ? 'active' : ''; ?>">
<a href="installations.php" class="menu-link">
<div class="text-truncate" data-i18n="Lista">Lista instalacji</div>
</a>
</li>
<li class="menu-item <?php echo ($currentPage == 'installations-map.php') ? 'active' : ''; ?>">
<a href="installations-map.php" class="menu-link">
<div class="text-truncate" data-i18n="Mapa instalacji">Mapa instalacji</div>
</a>
</li>
</ul>
</li>
<li class="menu-item <?php echo ($currentPage == 'devices.php') ? 'active' : ''; ?>">
<a href="devices.php" class="menu-link">
@@ -72,23 +60,5 @@ $currentPage = basename($_SERVER['PHP_SELF']);
<div class="text-truncate" data-i18n="Raporty">Raporty</div>
</a>
</li>
<li class="menu-item <?php echo ($currentPage == 'settings.php' || $currentPage == 'logs.php') ? 'active open' : ''; ?>">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon tf-icons bx bx-server"></i>
<div class="text-truncate" data-i18n="System">System</div>
</a>
<ul class="menu-sub">
<li class="menu-item <?php echo ($currentPage == 'settings.php') ? 'active' : ''; ?>">
<a href="settings.php" class="menu-link">
<div class="text-truncate" data-i18n="Ustawienia">Ustawienia</div>
</a>
</li>
<li class="menu-item <?php echo ($currentPage == 'logs.php') ? 'active' : ''; ?>">
<a href="logs.php" class="menu-link">
<div class="text-truncate" data-i18n="Logi">Logi systemowe</div>
</a>
</li>
</ul>
</li>
</ul>
</aside>
-90
View File
@@ -1,90 +0,0 @@
<?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">/ Ustawienia</span>
</h4>
<div class="row">
<div class="col-md-6 col-12 mb-4">
<div class="card shadow-sm border-0">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">Powiadomienia i Alerty</h5>
</div>
<div class="card-body pt-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<h6 class="mb-1">Aktywny moduł alertów</h6>
<small class="text-muted">Pozwala na ustawianie zaawansowanych wyzwalaczy i powiadomień SMS/Email po przekroczeniu progów alarmowych w instalacjach.</small>
</div>
<div class="form-check form-switch form-switch-lg ms-3 mb-0">
<input class="form-check-input" type="checkbox" id="alertsModuleSwitch">
</div>
</div>
<button type="button" class="btn btn-primary mt-3" onclick="saveSettings()">Zapisz ustawienia</button>
</div>
</div>
</div>
</div>
</div>
<!-- Modal potwierdzenia włączenia -->
<div class="modal fade" id="confirmPaymentModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-warning border-2">
<div class="modal-header">
<h5 class="modal-title text-warning"><i class="bx bx-error-circle me-1"></i> Potwierdzenie subskrypcji</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="cancelSwitch()"></button>
</div>
<div class="modal-body">
<p>Uruchomienie modułu <strong>Powiadomienia i Alerty</strong> wiąże się z dodatkową opłatą abonamentową w wysokości <strong>50 zł / msc</strong> doliczaną do Twojej faktury.</p>
<p class="mb-0 text-dark fw-bold">Czy na pewno chcesz włączyć ten moduł?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal" onclick="cancelSwitch()">Anuluj</button>
<button type="button" class="btn btn-success" data-bs-dismiss="modal" onclick="confirmSave()">Tak, włączam moduł</button>
</div>
</div>
</div>
</div>
<script>
let isCurrentlyEnabled = false; // Zakładamy że na start jest wyłączone
function saveSettings() {
const switchEl = document.getElementById('alertsModuleSwitch');
// Jeśli chcemy włączyć, a wcześniej było wyłączone
if (switchEl.checked && !isCurrentlyEnabled) {
var paymentModal = new bootstrap.Modal(document.getElementById('confirmPaymentModal'));
paymentModal.show();
} else if (!switchEl.checked && isCurrentlyEnabled) {
alert('Moduł został pomyślnie wyłączony. Opłata nie będzie naliczana od następnego okresu rozliczeniowego.');
isCurrentlyEnabled = false;
} else {
alert('Ustawienia zapisane bez zmian.');
}
}
function confirmSave() {
isCurrentlyEnabled = true;
document.getElementById('alertsModuleSwitch').checked = true;
alert('Moduł został włączony! Doliczono 50 zł/msc do Twojego abonamentu. Możesz teraz konfigurować alerty w instalacjach.');
}
function cancelSwitch() {
// Jeśli anulowano modal, przywracamy przełącznik do stanu wylączonego
document.getElementById('alertsModuleSwitch').checked = false;
}
</script>
<div class="content-backdrop fade"></div>
<?php include '../../footer.php'; ?>
</body>
</html>
@@ -183,49 +183,81 @@ html, body {
padding: 14px 16px calc(var(--bottom-nav-height) + 16px);
}
/* System Ribbon Bar (Opcja 3: Wstążka systemowa pod nagłówkiem na pełną szerokość) */
/* System Ribbon Bar (Wstążki systemowe pod nagłówkiem na pełną szerokość) */
.system-ribbons-stack {
display: flex;
flex-direction: column;
flex-shrink: 0;
position: relative;
z-index: 15;
}
.system-ribbon-bar {
background: linear-gradient(90deg, #4f53e8 0%, #686cf7 100%);
color: white;
padding: 8px 18px;
padding: 7px 16px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
gap: 10px;
flex-shrink: 0;
box-shadow: 0 2px 8px rgba(79, 83, 232, 0.18);
box-shadow: 0 2px 8px rgba(79, 83, 232, 0.15);
position: relative;
z-index: 10;
}
/* Wstążka zaproszenia (Ciepły akcent pilności z 24h timerem) */
.system-ribbon-bar.ribbon-invitation {
background: linear-gradient(90deg, #d9480f 0%, #ea580c 100%);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 2px 8px rgba(217, 72, 15, 0.22);
}
.system-ribbon-bar.ribbon-invitation .btn-ribbon-action {
color: #c2410c;
}
.system-ribbon-bar.ribbon-invitation .btn-ribbon-action:hover {
color: #9a3412;
}
.ribbon-badge-urgent {
background: rgba(0, 0, 0, 0.22) !important;
border: 1px solid rgba(255, 255, 255, 0.4) !important;
color: #ffe0b2 !important;
}
.ribbon-badge {
background: rgba(255, 255, 255, 0.22);
color: white;
font-size: 0.72rem;
font-size: 0.7rem;
font-weight: 800;
padding: 2px 7px;
padding: 2px 6px;
border-radius: 99px;
border: 1px solid rgba(255, 255, 255, 0.35);
line-height: 1.2;
flex-shrink: 0;
}
.ribbon-text {
font-size: 0.78rem;
font-size: 0.77rem;
line-height: 1.25;
color: rgba(255, 255, 255, 0.95);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ribbon-sub {
color: rgba(255, 255, 255, 0.82);
color: rgba(255, 255, 255, 0.85);
font-size: 0.72rem;
}
.btn-ribbon-action {
background: rgba(255, 255, 255, 0.95);
color: #4f53e8;
padding: 4px 12px;
padding: 3px 10px;
border-radius: var(--radius-full);
font-size: 0.72rem;
font-size: 0.7rem;
font-weight: 700;
text-decoration: none;
white-space: nowrap;
@@ -234,11 +266,11 @@ html, body {
gap: 3px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
transition: all 0.2s ease;
flex-shrink: 0;
}
.btn-ribbon-action:hover {
background: white;
color: #383cc7;
transform: scale(1.03);
}
@@ -246,7 +278,7 @@ html, body {
background: transparent;
border: none;
color: rgba(255, 255, 255, 0.75);
font-size: 1.2rem;
font-size: 1.15rem;
padding: 0;
line-height: 1;
cursor: pointer;
@@ -254,6 +286,7 @@ html, body {
align-items: center;
justify-content: center;
transition: color 0.2s ease;
flex-shrink: 0;
}
.btn-ribbon-close:hover {
@@ -1061,3 +1094,400 @@ html, body {
background: #e4e7f3;
color: var(--dark-navy);
}
/* ==========================================================================
Moduł Zaproszeń i Powiadomień (TravelLeader Mobile 2026)
========================================================================== */
/* Dzwonek powiadomień w nagłówku */
.header-notif-btn {
position: relative;
width: 36px;
height: 36px;
border-radius: 50%;
background-color: #ffffff;
border: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: center;
color: var(--dark-navy);
font-size: 1.25rem;
text-decoration: none;
transition: all 0.2s ease;
box-shadow: 0 2px 6px rgba(31, 34, 68, 0.04);
}
.header-notif-btn:hover {
background-color: var(--primary-light);
color: var(--primary);
border-color: var(--primary-border);
}
.header-notif-badge {
position: absolute;
top: -2px;
right: -2px;
min-width: 17px;
height: 17px;
padding: 0 4px;
border-radius: 9999px;
background: #ff4757;
color: #ffffff;
font-size: 0.65rem;
font-weight: 800;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid #ffffff;
box-shadow: 0 2px 6px rgba(255, 71, 87, 0.4);
animation: notif-pulse 2s infinite ease-in-out;
}
@keyframes notif-pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.15);
}
}
/* Badge Alert w zakładce nawigacji */
.badge-count.badge-alert {
background: #ff4757 !important;
color: #ffffff !important;
font-weight: 800;
box-shadow: 0 2px 6px rgba(255, 71, 87, 0.35);
}
/* ==========================================================================
Moduł Zaproszeń i Powiadomień (TravelLeader Mobile 2026 - Wersja Minimalistyczna)
========================================================================== */
/* Dzwonek powiadomień w nagłówku */
.header-notif-btn {
position: relative;
width: 36px;
height: 36px;
border-radius: 50%;
background-color: #ffffff;
border: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: center;
color: var(--dark-navy);
font-size: 1.25rem;
text-decoration: none;
transition: all 0.2s ease;
box-shadow: 0 2px 6px rgba(31, 34, 68, 0.04);
}
.header-notif-btn:hover {
background-color: var(--primary-light);
color: var(--primary);
border-color: var(--primary-border);
}
.header-notif-badge {
position: absolute;
top: -2px;
right: -2px;
min-width: 17px;
height: 17px;
padding: 0 4px;
border-radius: 9999px;
background: #ff4757;
color: #ffffff;
font-size: 0.65rem;
font-weight: 800;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid #ffffff;
box-shadow: 0 2px 6px rgba(255, 71, 87, 0.4);
animation: notif-pulse 2s infinite ease-in-out;
}
@keyframes notif-pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.15);
}
}
/* Badge Alert w zakładce nawigacji */
.badge-count.badge-alert {
background: #ff4757 !important;
color: #ffffff !important;
font-weight: 800;
box-shadow: 0 2px 6px rgba(255, 71, 87, 0.35);
}
/* ==========================================================================
1. Ultrakompaktowy pasek na górze ekranu głównego (Zero drabinki, 1 linia)
========================================================================== */
.invitation-compact-bar {
display: flex;
align-items: center;
justify-content: space-between;
background: linear-gradient(135deg, #1e223d, #2b3056);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: var(--radius-sm);
padding: 10px 14px;
margin-bottom: 12px;
color: #ffffff;
text-decoration: none;
box-shadow: 0 4px 14px rgba(30, 34, 61, 0.15);
transition: all 0.2s ease;
}
.invitation-compact-bar:hover {
background: linear-gradient(135deg, #252a4a, #343a68);
color: #ffffff;
transform: translateY(-1px);
box-shadow: 0 6px 18px rgba(30, 34, 61, 0.22);
}
.inv-bar-left {
display: flex;
align-items: center;
gap: 7px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 0.8rem;
}
.inv-pulse-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #ff4757;
flex-shrink: 0;
box-shadow: 0 0 8px #ff4757;
animation: notif-pulse 1.8s infinite;
}
.inv-bar-agency {
font-weight: 800;
color: #ffffff;
}
.inv-bar-divider {
color: rgba(255, 255, 255, 0.35);
font-size: 0.75rem;
}
.inv-bar-title {
color: #e2e8f0;
font-weight: 500;
}
.inv-bar-rate {
color: #2ed573;
font-weight: 700;
margin-left: 2px;
}
.inv-bar-right {
display: flex;
align-items: center;
gap: 6px;
flex-shrink: 0;
}
.inv-bar-timer {
background: rgba(255, 209, 102, 0.16);
border: 1px solid rgba(255, 209, 102, 0.35);
color: #ffd166;
font-size: 0.68rem;
font-weight: 700;
padding: 2px 6px;
border-radius: 4px;
}
.inv-bar-right i {
font-size: 1.15rem;
color: rgba(255, 255, 255, 0.7);
}
/* ==========================================================================
2. Minimalistyczne kafelki w zakładce "Zaproszenia" (Single-Tap Row)
========================================================================== */
.invitation-compact-item {
display: flex;
align-items: center;
background: #ffffff;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
padding: 12px 14px;
margin-bottom: 10px;
text-decoration: none;
color: inherit;
box-shadow: var(--shadow-card);
transition: all 0.2s ease;
position: relative;
}
.invitation-compact-item:hover {
border-color: var(--primary-border);
transform: translateY(-1px);
box-shadow: 0 6px 16px rgba(92, 96, 245, 0.08);
}
.invitation-compact-item.is-pending {
border-left: 4px solid #f59e0b;
background: linear-gradient(to right, #fffdf8, #ffffff);
}
.invitation-compact-item.is-accepted {
border-left: 4px solid #10b981;
}
.invitation-compact-item.is-rejected {
border-left: 4px solid #ef4444;
opacity: 0.8;
}
.inv-item-icon {
position: relative;
width: 40px;
height: 40px;
border-radius: 10px;
background: #eff0fe;
color: var(--primary);
font-weight: 800;
font-size: 1.05rem;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-right: 12px;
}
.inv-item-status-dot {
position: absolute;
top: -2px;
right: -2px;
width: 10px;
height: 10px;
border-radius: 50%;
background: #ff4757;
border: 2px solid #ffffff;
}
.inv-item-content {
flex: 1;
min-width: 0;
}
.inv-item-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2px;
}
.inv-item-agency {
font-size: 0.72rem;
font-weight: 700;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.3px;
}
.inv-item-timer {
font-size: 0.68rem;
font-weight: 700;
color: #d97706;
background: #fef3c7;
padding: 1px 5px;
border-radius: 4px;
}
.inv-item-title {
font-size: 0.88rem;
font-weight: 700;
color: var(--dark-navy);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 3px;
}
.inv-item-meta {
font-size: 0.74rem;
color: var(--text-muted);
display: flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.inv-item-arrow {
margin-left: 8px;
flex-shrink: 0;
}
/* Pasek stałych decyzji na dole ekranu szczegółów */
.decision-sticky-footer {
position: sticky;
bottom: 0;
left: 0;
right: 0;
background: #ffffff;
border-top: 1px solid var(--border-color);
padding: 12px 16px 16px;
box-shadow: 0 -4px 20px rgba(31, 34, 68, 0.08);
z-index: 50;
display: grid;
grid-template-columns: 1fr 1.6fr;
gap: 12px;
}
.btn-decision-decline {
background: #fdf3f4;
border: 1px solid #fecdd3;
color: #e11d48;
font-size: 0.84rem;
font-weight: 700;
padding: 12px 14px;
border-radius: var(--radius-md);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
cursor: pointer;
transition: all 0.2s;
}
.btn-decision-decline:hover {
background: #ffe4e6;
color: #be123c;
}
.btn-decision-accept {
background: linear-gradient(135deg, #10b981, #059669);
border: none;
color: #ffffff;
font-size: 0.86rem;
font-weight: 700;
padding: 12px 16px;
border-radius: var(--radius-md);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
box-shadow: 0 4px 14px rgba(16, 185, 129, 0.35);
cursor: pointer;
transition: all 0.2s;
}
.btn-decision-accept:hover {
background: linear-gradient(135deg, #059669, #047857);
transform: translateY(-1px);
box-shadow: 0 6px 18px rgba(16, 185, 129, 0.45);
}
@@ -3,6 +3,9 @@ $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">
@@ -55,13 +58,23 @@ if (!isset($pageTitle)) {
<div class="brand-name">Travel<span style="color: #2b304f;">Leader</span></div>
</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">
<i class='bx bx-user'></i>
</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>
+588
View File
@@ -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ź &gt;
</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 &gt;
</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 &rarr; 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 &gt;
</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 &gt;
</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">&nbsp;</span>
</td>
<td onclick="openTripBottomSheet('18 WRZEŚNIA 2027');">
<span class="day-number fw-bold text-primary">18</span>
<span class="trip-ribbon-bar">&nbsp;</span>
</td>
<td onclick="openTripBottomSheet('19 WRZEŚNIA 2027');">
<span class="day-number fw-bold text-primary">19</span>
<span class="trip-ribbon-bar">&nbsp;</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">&nbsp;</span>
</td>
<td onclick="openTripBottomSheet('22 WRZEŚNIA 2027');">
<span class="day-number fw-bold text-primary">22</span>
<span class="trip-ribbon-bar">&nbsp;</span>
</td>
<td onclick="openTripBottomSheet('23 WRZEŚNIA 2027');">
<span class="day-number fw-bold text-primary">23</span>
<span class="trip-ribbon-bar">&nbsp;</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">&nbsp;</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 &rarr;
</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 &gt;
</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 &rarr; 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 &rarr;
</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'; ?>
-408
View File
@@ -1,408 +0,0 @@
<?php
$enablePrototypeComments = true;
include '../../header-travel.php';
?>
<div class="container-xxl flex-grow-1 container-p-y" style="padding-top: 1rem!important">
<h4 class="fw-bold py-3 mb-4">
Aplikacja dostawcy <span class="text-muted fw-light">/ Zlecenia</span>
</h4>
<!-- Wyszukiwarka i filtry -->
<div class="card mb-4">
<div class="card-body d-flex flex-column flex-lg-row justify-content-between align-items-center gap-3">
<div class="d-flex flex-column flex-md-row gap-3 w-100" style="max-width: 600px;">
<div class="input-group input-group-merge w-100">
<span class="input-group-text" id="search-addon"><i class="bx bx-search"></i></span>
<input type="text" class="form-control" placeholder="Szukaj wycieczki..." aria-label="Szukaj"
aria-describedby="search-addon">
</div>
<select class="form-select w-100" style="max-width: 250px;">
<option value="all" selected>Wszystkie</option>
<option value="1">Biuro Podróży "Słoneczko"</option>
<option value="2">Biuro Podróży "Adventure"</option>
</select>
</div>
<div class="btn-group" role="group" aria-label="Status wycieczek">
<input type="radio" class="btn-check" name="statusFilter" id="btnRadioAktualne" autocomplete="off"
checked>
<label class="btn btn-outline-primary" for="btnRadioAktualne">Aktualne</label>
<input type="radio" class="btn-check" name="statusFilter" id="btnRadioArchiwalne" autocomplete="off">
<label class="btn btn-outline-primary" for="btnRadioArchiwalne">Archiwalne</label>
</div>
</div>
</div>
<!-- Trip 1 -->
<div class="card mb-4">
<div class="card-header border-bottom d-flex justify-content-between align-items-center py-3">
<div>
<div class="d-flex align-items-center mb-1">
<div class="avatar avatar-xs me-2" style="cursor: default;">
<img src="assets/logo-sloneczko.png" alt="Logo" class="rounded-circle border">
</div>
<span class="fw-semibold text-secondary small">Biuro Podróży "Słoneczko"</span>
</div>
<div class="d-flex align-items-center flex-wrap gap-2">
<h5 class="mb-0 fw-bold">
<i class="bx bx-map-alt text-primary me-1"></i>Włochy Klasyczne (3 dni)
</h5>
<span class="badge bg-label-secondary d-inline-flex align-items-center"><i
class="bx bx-tag-alt me-1"></i>ID #1024</span>
<span class="badge bg-label-info d-inline-flex align-items-center"><i
class="bx bx-group me-1"></i>40+3</span>
</div>
</div>
<div>
<span class="badge bg-label-success fs-6">Aktywna</span>
</div>
</div>
<div class="table-responsive text-nowrap" style="overflow: visible;">
<table class="table table-hover">
<thead>
<tr>
<th>Zlecenie od</th>
<th>Zlecenie do</th>
<th>Status</th>
<th>Pliki</th>
<th>Akcje</th>
</tr>
</thead>
<tbody class="table-border-bottom-0">
<tr>
<td>2024-05-10</td>
<td>2024-05-12</td>
<td>
<div class="dropdown">
<button class="btn btn-sm btn-outline-primary dropdown-toggle" type="button"
data-bs-toggle="dropdown" aria-expanded="false">
Nowe
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="javascript:void(0);">Nowe</a></li>
<li><a class="dropdown-item" href="javascript:void(0);">W realizacji</a></li>
<li><a class="dropdown-item" href="javascript:void(0);">Zakończone</a></li>
</ul>
</div>
</td>
<td>
<span class="badge bg-label-secondary" title="3 załączniki (do pobrania w szczegółach)"><i
class="bx bx-paperclip me-1"></i>3</span>
</td>
<td>
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal"
data-bs-target="#orderDetailsModal">
<i class="bx bx-search-alt-2 me-1"></i> Szczegóły
</button>
</td>
</tr>
<tr>
<td>2024-05-15</td>
<td>2024-05-17</td>
<td>
<div class="dropdown">
<button class="btn btn-sm btn-outline-warning dropdown-toggle" type="button"
data-bs-toggle="dropdown" aria-expanded="false">
W realizacji
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="javascript:void(0);">Nowe</a></li>
<li><a class="dropdown-item" href="javascript:void(0);">W realizacji</a></li>
<li><a class="dropdown-item" href="javascript:void(0);">Zakończone</a></li>
</ul>
</div>
</td>
<td>
<span class="badge bg-label-secondary" title="2 załączniki (do pobrania w szczegółach)"><i
class="bx bx-paperclip me-1"></i>2</span>
</td>
<td>
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal"
data-bs-target="#orderDetailsModal">
<i class="bx bx-search-alt-2 me-1"></i> Szczegóły
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Trip 2 -->
<div class="card mb-4">
<div class="card-header border-bottom d-flex justify-content-between align-items-center py-3">
<div>
<div class="d-flex align-items-center mb-1">
<div class="avatar avatar-xs me-2" style="cursor: default;">
<img src="assets/logo-adventure.png" alt="Logo" class="rounded-circle border">
</div>
<span class="fw-semibold text-secondary small">Biuro Podróży "Adventure"</span>
</div>
<div class="d-flex align-items-center flex-wrap gap-2">
<h5 class="mb-0 fw-bold">
<i class="bx bx-map-alt text-primary me-1"></i>Złota Praga (3 dni)
</h5>
<span class="badge bg-label-secondary d-inline-flex align-items-center"><i
class="bx bx-tag-alt me-1"></i>ID #1080</span>
<span class="badge bg-label-info d-inline-flex align-items-center"><i
class="bx bx-group me-1"></i>30+2</span>
</div>
</div>
<div>
<span class="badge bg-label-primary fs-6">Zaplanowana</span>
</div>
</div>
<div class="table-responsive text-nowrap" style="overflow: visible;">
<table class="table table-hover">
<thead>
<tr>
<th>Zlecenie od</th>
<th>Zlecenie do</th>
<th>Status</th>
<th>Pliki</th>
<th>Akcje</th>
</tr>
</thead>
<tbody class="table-border-bottom-0">
<tr>
<td>2024-06-01</td>
<td>2024-06-03</td>
<td>
<div class="dropdown">
<button class="btn btn-sm btn-outline-success dropdown-toggle" type="button"
data-bs-toggle="dropdown" aria-expanded="false">
Zakończone
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="javascript:void(0);">Nowe</a></li>
<li><a class="dropdown-item" href="javascript:void(0);">W realizacji</a></li>
<li><a class="dropdown-item" href="javascript:void(0);">Zakończone</a></li>
</ul>
</div>
</td>
<td>
<span class="badge bg-label-secondary" title="1 załącznik (do pobrania w szczegółach)"><i
class="bx bx-paperclip me-1"></i>1</span>
</td>
<td>
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal"
data-bs-target="#orderDetailsModal">
<i class="bx bx-search-alt-2 me-1"></i> Szczegóły
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Modal Order Details -->
<div class="modal fade" id="orderDetailsModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="orderDetailsModalLabel">Szczegóły zlecenia | Wycieczka #1234</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zamknij"></button>
</div>
<div class="modal-body">
<div class="row mb-3">
<div class="col-12">
<h6 class="fw-bold mb-2">Szczegóły zlecenia</h6>
<div class="p-3 bg-lighter rounded">
<div class="row">
<div class="col-md-3 col-6 mb-3">
<small class="text-muted d-block">Zlecenie od</small>
<span class="fw-medium">2024-05-10</span>
</div>
<div class="col-md-3 col-6 mb-3">
<small class="text-muted d-block">Zlecenie do</small>
<span class="fw-medium">2024-05-12</span>
</div>
<div class="col-md-6 mb-3">
<small class="text-muted d-block">Wycieczka</small>
<span class="fw-medium">Włochy Klasyczne (3 dni)</span><br>
<span class="text-muted small">24.08.2024 - 26.08.2024</span>
</div>
<div class="col-md-6 mb-3">
<small class="text-muted d-block">Liczba osób</small>
<span>40+3</span>
</div>
<div class="col-md-6 mb-3">
<small class="text-muted d-block">Status</small>
<span class="badge bg-label-primary">Nowe</span>
</div>
<div class="col-md-6 mb-3">
<small class="text-muted d-block">Zleceniodawca</small>
<span>Biuro Podróży "Słoneczko"</span>
</div>
<div class="col-md-6 mb-3">
<small class="text-muted d-block">Organizacja</small>
<span>Szkoła Podstawowa nr 1 w Warszawie, Związek Turystyki Młodzieżowej</span>
</div>
<div class="col-md-6 mb-3">
<small class="text-muted d-block">Przedstawiciel zleceniodawcy</small>
<span class="fw-medium text-body">Bartłomiej Banaczyk</span>
<div class="small d-flex flex-wrap gap-3 mt-1">
<a href="tel:+48600100200"
class="text-decoration-none text-secondary d-inline-flex align-items-center">
<i class="bi bi-telephone text-primary me-1"></i>+48 600 100 200
</a>
<a href="mailto:b.banaczyk@sloneczko.pl"
class="text-decoration-none text-secondary d-inline-flex align-items-center">
<i class="bi bi-envelope text-primary me-1"></i>b.banaczyk@sloneczko.pl
</a>
</div>
</div>
<div class="col-md-6 mb-3">
<small class="text-muted d-block mb-1">Miejsce podstawienia</small>
<a href="https://maps.google.com/?q=Warszawa,+Plac+Defilad+1" target="_blank"
class="fw-medium text-primary text-decoration-none"
style="display:inline-flex; align-items:center;">
<i class="bx bx-map me-1"></i>Warszawa, Plac Defilad 1 (Parking pod PKiN) <i
class="bx bx-link-external ms-2"></i>
</a>
</div>
<div class="col-md-6 mb-3">
<small class="text-muted d-block mb-1">Data i godzina podstawienia</small>
<span class="fw-medium text-body d-inline-flex align-items-center">
<i class="bx bx-time-five text-primary me-1"></i>2024-05-10, godz. 07:30
</span>
</div>
<div class="col-12">
<small class="text-muted d-block mb-2"><i
class="bx bx-paperclip me-1"></i>Załączniki od organizatora (3)</small>
<div class="d-flex flex-wrap gap-2">
<a href="javascript:void(0);" class="btn btn-sm btn-outline-secondary">
<i class="bx bx-download me-1"></i> Karta wycieczki
</a>
<a href="javascript:void(0);" class="btn btn-sm btn-outline-secondary">
<i class="bx bx-download me-1"></i> Program wycieczki
</a>
<a href="javascript:void(0);" class="btn btn-sm btn-outline-secondary">
<i class="bx bx-download me-1"></i> Regulamin
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<hr>
<div class="row">
<div class="col-12">
<h6 class="fw-bold mb-3">Dane realizacyjne transportu</h6>
<p class="text-muted small mb-4">Prosimy o uzupełnienie danych wymaganych do realizacji zlecenia
oraz aktualizację statusu.</p>
</div>
<div class="col-md-12 mb-4">
<label class="form-label fw-bold text-primary d-block">Status zlecenia</label>
<div class="dropdown">
<button
class="btn btn-outline-primary dropdown-toggle w-100 text-start d-flex justify-content-between align-items-center"
type="button" data-bs-toggle="dropdown" aria-expanded="false">
<span><span class="badge bg-label-primary me-2">Nowe</span> Oczekuje na
realizację</span>
</button>
<ul class="dropdown-menu w-100">
<li><a class="dropdown-item" href="javascript:void(0);"><span
class="badge bg-label-primary me-2">Nowe</span> Oczekuje na realizację</a>
</li>
<li><a class="dropdown-item" href="javascript:void(0);"><span
class="badge bg-label-warning me-2">W realizacji</span> Przejazd w
trakcie</a></li>
<li><a class="dropdown-item" href="javascript:void(0);"><span
class="badge bg-label-success me-2">Zakończone</span> Przejazd
zrealizowany</a></li>
</ul>
</div>
</div>
<div class="col-md-6 mb-3">
<label for="busSeats" class="form-label">Liczba miejsc w autokarze</label>
<input type="number" id="busSeats" class="form-control" placeholder="Np. 50">
</div>
<div class="col-md-6 mb-3">
<label for="licensePlate" class="form-label">Numer rejestracyjny autokaru</label>
<input type="text" id="licensePlate" class="form-control" placeholder="Np. WA 12345">
</div>
<div class="col-md-6 mb-3">
<label for="driverName" class="form-label">Imię i nazwisko kierowcy</label>
<input type="text" id="driverName" class="form-control" placeholder="Wprowadź dane kierowcy">
</div>
<div class="col-md-6 mb-3">
<label for="driverPhone" class="form-label">Telefon do kierowcy</label>
<input type="text" id="driverPhone" class="form-control" placeholder="+48...">
</div>
<div class="col-12 mb-3">
<label class="form-label">Notatka do zlecenia</label>
<div class="p-3 bg-lighter rounded border">
<div class="d-flex align-items-start gap-2">
<i class="bx bx-message-square-detail text-primary fs-5 mt-1"></i>
<div>
<small class="text-muted d-block mb-1">Komunikat od zleceniodawcy:</small>
<div class="text-body">
Prosimy o podstawienie autokaru minimum 15 minut przed czasem. Sprawny mikrofon dla przewodnika oraz klimatyzacja są wymagane. Kierowca proszony o kontakt z pilotem po dotarciu na parking.
</div>
</div>
</div>
</div>
</div>
</div>
<hr>
<div class="row">
<div class="col-12">
<h6 class="fw-bold mb-3">Rozliczenie trasy</h6>
</div>
<div class="col-md-4 mb-3">
<label for="startOdometer" class="form-label">Początkowy odczyt licznika</label>
<div class="input-group">
<input type="number" id="startOdometer" class="form-control" placeholder="0">
<span class="input-group-text">km</span>
</div>
</div>
<div class="col-md-4 mb-3">
<label for="endOdometer" class="form-label">Końcowy odczyt licznika</label>
<div class="input-group">
<input type="number" id="endOdometer" class="form-control" placeholder="0">
<span class="input-group-text">km</span>
</div>
</div>
<div class="col-md-4 mb-3">
<label for="totalMileage" class="form-label">Przebieg</label>
<div class="input-group">
<input type="number" id="totalMileage" class="form-control" placeholder="0" readonly>
<span class="input-group-text bg-lighter">km</span>
</div>
<small class="text-muted">Wyliczane automatycznie</small>
</div>
<div class="col-md-12 mb-3">
<label for="parkingCosts" class="form-label text-warning fw-bold">Opłaty drogowe
(Edytowalne)</label>
<div class="input-group">
<input type="number" id="parkingCosts" class="form-control border-warning"
placeholder="0.00" step="0.01">
<span class="input-group-text border-warning">PLN</span>
</div>
</div>
</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">Zapisz zmiany</button>
</div>
</div>
</div>
</div>
<?php include '../../footer.php'; ?>
</body>
</html>
-90
View File
@@ -1,90 +0,0 @@
<?php
$enablePrototypeComments = true;
include '../../header-travel.php';
?>
<div class="container-xxl flex-grow-1 container-p-y" style="padding-top: 1rem!important">
<h4 class="fw-bold py-3 mb-4">
Aplikacja dostawcy <span class="text-muted fw-light">/ Biura podróży</span>
</h4>
<!-- Alert Informacyjny -->
<div class="alert alert-primary d-flex align-items-center mb-4" role="alert">
<span class="alert-icon text-primary me-3" style="font-size: 1.5rem;">
<i class="bx bx-info-circle"></i>
</span>
<div>
<strong>Poniżej znajduje się lista biur podróży, z którymi aktualnie współpracujesz.</strong><br>
Jeśli na liście brakuje biura, dla którego realizujesz zlecenia, poproś je o powiązanie Twojego konta dostawcy z ich Panelem Zarządzania Travel Manager.
</div>
</div>
<!-- Lista Biur Podróży -->
<div class="row g-4">
<!-- Biuro 1 -->
<div class="col-md-6 col-lg-4">
<div class="card h-100 text-center">
<div class="card-body">
<div class="mx-auto mb-3">
<img src="assets/logo-sloneczko.png" alt="Biuro Podróży Słoneczko" class="rounded-circle border" style="width: 100px; height: 100px; object-fit: cover;">
</div>
<h5 class="card-title mb-1">Biuro Podróży "Słoneczko"</h5>
<span class="badge bg-label-success mb-3">Aktywne powiązanie</span>
<ul class="list-unstyled text-start mb-0 px-2">
<li class="d-flex align-items-center mb-2">
<i class="bx bx-envelope text-muted me-2"></i> kontakt@sloneczko.pl
</li>
<li class="d-flex align-items-center mb-2">
<i class="bx bx-phone text-muted me-2"></i> +48 123 456 789
</li>
<li class="d-flex align-items-center">
<i class="bx bx-briefcase text-muted me-2"></i> 2 aktywne zlecenia
</li>
</ul>
</div>
<div class="card-footer bg-transparent border-top">
<a href="app-orders.php" class="btn btn-outline-primary w-100">Przejdź do zleceń</a>
</div>
</div>
</div>
<!-- Biuro 2 -->
<div class="col-md-6 col-lg-4">
<div class="card h-100 text-center">
<div class="card-body">
<div class="mx-auto mb-3">
<img src="assets/logo-adventure.png" alt="Biuro Podróży Adventure" class="rounded-circle border" style="width: 100px; height: 100px; object-fit: cover;">
</div>
<h5 class="card-title mb-1">Biuro Podróży "Adventure"</h5>
<span class="badge bg-label-success mb-3">Aktywne powiązanie</span>
<ul class="list-unstyled text-start mb-0 px-2">
<li class="d-flex align-items-center mb-2">
<i class="bx bx-envelope text-muted me-2"></i> biuro@adventure.com
</li>
<li class="d-flex align-items-center mb-2">
<i class="bx bx-phone text-muted me-2"></i> +48 987 654 321
</li>
<li class="d-flex align-items-center">
<i class="bx bx-briefcase text-muted me-2"></i> 1 aktywne zlecenie
</li>
</ul>
</div>
<div class="card-footer bg-transparent border-top">
<a href="app-orders.php" class="btn btn-outline-primary w-100">Przejdź do zleceń</a>
</div>
</div>
</div>
</div>
</div>
<?php include '../../footer.php'; ?>
</body>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

-20
View File
@@ -3,7 +3,6 @@ $currentPage = basename($_SERVER['PHP_SELF']);
$clientsPages = ['app-organization.php', 'app-client.php'];
$systemPages = ['app-dictionary.php', 'app-template.php', 'app-employee.php', 'app-settings.php'];
$supplierAppPages = ['app-orders.php', 'app-travel-agencies.php'];
?>
<aside id="layout-menu" class="layout-menu-horizontal menu-horizontal menu bg-menu-theme flex-grow-0" style="position: relative; top: 0px; padding-top: 0px;">
<div class="container-xxl d-flex h-100">
@@ -94,25 +93,6 @@ $supplierAppPages = ['app-orders.php', 'app-travel-agencies.php'];
</li>
</ul>
</li>
<li class="menu-item <?php echo in_array($currentPage, $supplierAppPages, true) ? 'active open' : ''; ?>">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon tf-icons bx bx-briefcase"></i>
<div data-i18n="Aplikacja dostawcy">Aplikacja dostawcy</div>
</a>
<ul class="menu-sub">
<li class="menu-item <?php echo ($currentPage == 'app-orders.php') ? 'active' : ''; ?>">
<a href="app-orders.php" class="menu-link">
<div data-i18n="Zlecenia">Zlecenia</div>
</a>
</li>
<li class="menu-item <?php echo ($currentPage == 'app-travel-agencies.php') ? 'active' : ''; ?>">
<a href="app-travel-agencies.php" class="menu-link">
<div data-i18n="Biura podróży">Biura podróży</div>
</a>
</li>
</ul>
</li>
</ul>
</div>
</aside>
-322
View File
@@ -1,322 +0,0 @@
<?php
$enablePrototypeComments = true;
$pageTitle = 'TravelLeader - Wycieczki i Zlecenia';
include 'header.php';
?>
<!-- Systemowa wstążka profilu (Edge-to-edge pod nagłówkiem, nie koliduje z wyszukiwarką) -->
<div class="system-ribbon-bar">
<div class="d-flex align-items-center gap-2">
<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">
<a href="profile.php" class="btn-ribbon-action">
Dokończ &gt;
</a>
<button type="button" class="btn-ribbon-close" onclick="this.closest('.system-ribbon-bar').style.display='none';" title="Zamknij powiadomienie">
<i class='bx bx-x'></i>
</button>
</div>
</div>
<div class="app-content">
<!-- Wyszukiwarka i filtr zakresu dat (odtworzone ze zrzutu ekranu) -->
<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 &rarr; 18 wrz 2027
</small>
</div>
<i class='bx bx-chevron-down text-muted fs-4'></i>
</div>
<!-- Pasek zakładek i przełącznik widoku (Lista vs Kalendarz wycieczek) -->
<div class="trips-tabs-bar">
<div class="nav-pills-custom">
<a href="#" class="nav-pill-item active">
<i class='bx bx-home-alt-2'></i>
<span>Bieżące</span>
<span class="badge-count">1</span>
</a>
<a href="#" class="nav-pill-item">
<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">
<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: LISTA WYCIECZEK -->
<div id="tripsListView">
<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 &gt;
</a>
</div>
</div>
</div>
<!-- WIDOK 2: KALENDARZ WYCIECZEK (Odtworzony 1:1 ze zrzutu ekranu) -->
<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">&nbsp;</span>
</td>
<td onclick="openTripBottomSheet('18 WRZEŚNIA 2027');">
<span class="day-number fw-bold text-primary">18</span>
<span class="trip-ribbon-bar">&nbsp;</span>
</td>
<td onclick="openTripBottomSheet('19 WRZEŚNIA 2027');">
<span class="day-number fw-bold text-primary">19</span>
<span class="trip-ribbon-bar">&nbsp;</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">&nbsp;</span>
</td>
<td onclick="openTripBottomSheet('22 WRZEŚNIA 2027');">
<span class="day-number fw-bold text-primary">22</span>
<span class="trip-ribbon-bar">&nbsp;</span>
</td>
<td onclick="openTripBottomSheet('23 WRZEŚNIA 2027');">
<span class="day-number fw-bold text-primary">23</span>
<span class="trip-ribbon-bar">&nbsp;</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">&nbsp;</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 &rarr;
</a>
</div>
</div>
</div>
</div><!-- /.app-content -->
<!-- BOTTOM SHEET MODAL (Odtworzony 1:1 ze zrzutu ekranu) -->
<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 &gt;
</button>
</div>
</div>
</div>
<script>
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');
}
</script>
<?php include 'footer.php'; ?>