| Server IP : 188.114.96.3 / Your IP : 216.73.216.143 Web Server : Apache System : Linux sv058.home.net.pl 6.8.2 husak_dirty_pages_fix #1 SMP PREEMPT_DYNAMIC Tue Apr 2 22:06:51 CEST 2024 x86_64 User : ( 3194769) PHP Version : 8.5.12 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /polana/ |
Upload File : |
<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/functions.php';
ensure_reservations_schema();
start_session();
$title = 'Polana Smaków – Restauracja Andrzeja Polana';
$description = 'Zarezerwuj stolik w restauracji ' . setting('site_name', APP_NAME) . '. Wyślij prośbę o rezerwację online.';
$errors = [];
$successCode = trim((string)($_GET['ok'] ?? ''));
$alreadyBooked = !empty($_GET['already']);
// Zapis i przekierowanie muszą nastąpić PRZED renderowaniem HTML.
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST' && reservations_enabled()) {
verify_csrf();
if (trim((string)($_POST['company_website'] ?? '')) !== '') {
redirect(polana_local_path('/rezerwacja') . '?ok=wyslano');
}
$validated = reservation_validate_public_input($_POST);
$errors = $validated['errors'];
if (!$errors) {
try {
$result = reservation_create_once($validated['data']);
if (!$result['created']) {
redirect(polana_local_path('/rezerwacja') . '?already=1');
}
$reservation = $result['reservation'];
$_SESSION['reservation_last_submit'] = time();
// Zgoda na newsletter jest niezależna i obsługiwana po udanym zapisie.
if (function_exists('newsletter_handle_reservation_optin')) {
newsletter_handle_reservation_optin();
}
if (session_status() === PHP_SESSION_ACTIVE) {
session_write_close();
}
try {
reservation_send_after_create($reservation);
} catch (Throwable $mailError) {
// Zapis w bazie już się powiódł. Problem SMTP nie może
// wywołać ponownego wysyłania formularza i duplikatów.
error_log('[POLANA RESERVATION EMAIL] ' . get_class($mailError) . ': ' . $mailError->getMessage());
}
// Powiadomienie ntfy działa niezależnie od konfiguracji SMTP.
try {
require_once __DIR__ . '/includes/reservation_integrations.php';
polana_ntfy_reservation($reservation, 'new');
} catch (Throwable $notifyError) {
error_log('[POLANA NTFY] ' . $notifyError->getMessage());
}
redirect(polana_local_path('/rezerwacja') . '?ok=' . rawurlencode((string)$reservation['reservation_code']));
} catch (Throwable $e) {
error_log('[POLANA RESERVATION POST] ' . get_class($e) . ': ' . $e->getMessage());
$errors[] = 'Nie udało się teraz wysłać rezerwacji. Spróbuj ponownie za chwilę lub skontaktuj się z restauracją.';
}
}
}
$minGuests = max(1, (int)reservation_setting('reservation_min_guests'));
$maxGuests = max($minGuests, (int)reservation_setting('reservation_max_guests'));
$today = new DateTimeImmutable('today');
$maxDate = $today->modify('+' . max(1, (int)reservation_setting('reservation_days_ahead')) . ' days');
render_public_head($title, $description, '/rezerwacja');
render_public_header('reservation');
?>
<main class="reservation-page">
<section class="reservation-hero">
<div class="container reservation-hero-grid">
<div class="reservation-art">
<?php render_showcase_visual('home', 'reservation-visual', 'Moja Polana Smaków'); ?>
</div>
<div class="reservation-copy">
<span class="eyebrow">Polana Smaków · Restauracja Andrzeja Polana</span>
<h1>Wpadnij do nas na coś dobrego.</h1>
<p>
<?= e(polana_lang() === 'en' ? 'We look forward to welcoming you. Please send us your preferred date and time.' : reservation_setting('reservation_intro')) ?>
</p>
</div>
</div>
</section>
<section class="reservation-form-section">
<div class="container reservation-form-wrap">
<?php if ($successCode !== ''): ?>
<div class="reservation-success">
<span class="eyebrow">Dziękujemy</span>
<h2>Prośba o rezerwację została wysłana.</h2>
<p><?= e(polana_lang() === 'en' ? 'Thank you. Our team will contact you to confirm your reservation.' : reservation_setting('reservation_success_message')) ?></p>
<?php if ($successCode !== 'wyslano'): ?>
<div class="reservation-code">
Numer rezerwacji: <strong><?= e(reservation_display_code($successCode)) ?></strong>
</div>
<?php endif; ?>
<a class="btn-public btn-dark" href="/">Strona główna</a>
</div>
<?php elseif ($alreadyBooked): ?>
<div class="reservation-success">
<span class="eyebrow">Rezerwacja</span>
<h2>Ta rezerwacja została już zgłoszona.</h2>
<p>Na wskazany termin mamy już zgłoszenie powiązane z tym numerem telefonu lub adresem e-mail. Nie wysyłamy drugiego zgłoszenia ani kolejnego e-maila. Jeśli chcesz coś zmienić, skontaktuj się z restauracją.</p>
<a class="btn-public btn-dark" href="/kontakt">Kontakt</a>
</div>
<?php elseif (!reservations_enabled()): ?>
<div class="reservation-success">
<span class="eyebrow">Rezerwacje</span>
<h2>Rezerwacje online są chwilowo wyłączone.</h2>
<p>Skontaktuj się z nami telefonicznie lub przez Instagram.</p>
<a class="btn-public btn-dark" href="/kontakt">Kontakt</a>
</div>
<?php else: ?>
<?php if ($errors): ?>
<div class="reservation-errors">
<strong>Sprawdź formularz:</strong>
<ul>
<?php foreach ($errors as $error): ?>
<li><?= e($error) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<form class="reservation-form" method="post" autocomplete="on" data-reservation-form>
<?= csrf_field() ?>
<div class="reservation-form-head">
<h2>Zarezerwuj stolik</h2>
<p>Po wysłaniu prośby otrzymasz potwierdzenie od obsługi.</p>
</div>
<div class="reservation-grid">
<label>
Imię i nazwisko
<input
name="guest_name"
value="<?= e($_POST['guest_name'] ?? '') ?>"
autocomplete="name"
required
>
</label>
<label>
Telefon
<input
name="phone"
value="<?= e($_POST['phone'] ?? '') ?>"
autocomplete="tel"
inputmode="tel"
required
>
</label>
<label>
E-mail
<input
type="email"
name="email"
value="<?= e($_POST['email'] ?? '') ?>"
autocomplete="email"
required
>
</label>
<label>
Liczba osób
<input
type="number"
name="guests"
min="<?= $minGuests ?>"
max="<?= $maxGuests ?>"
value="<?= e($_POST['guests'] ?? '2') ?>"
required
>
</label>
<label>
Data
<input
type="date"
name="reservation_date"
min="<?= e($today->format('Y-m-d')) ?>"
max="<?= e($maxDate->format('Y-m-d')) ?>"
value="<?= e($_POST['reservation_date'] ?? '') ?>"
required
>
</label>
<!-- POLANA_V175_PUBLIC_HOURS -->
<?php if (pv175_enabled()): ?>
<label>
Godzina
<select name="reservation_time" id="pv175-time" required aria-describedby="pv175-hint">
<option value="">Najpierw wybierz datę</option>
</select>
<small id="pv175-hint" style="display:block;color:#666;margin-top:5px">Wybierz datę, aby zobaczyć dostępne godziny.</small>
</label>
<?php else: ?>
<label>
Godzina
<input
type="time"
name="reservation_time"
step="900"
value="<?= e($_POST['reservation_time'] ?? '') ?>"
required
>
</label>
<?php endif; ?>
<label class="full">
Uwagi – opcjonalnie
<textarea
name="notes"
rows="4"
placeholder="np. wózek dziecięcy, alergia, urodziny"
><?= e($_POST['notes'] ?? '') ?></textarea>
</label>
<label class="reservation-consent full">
<input type="checkbox" name="consent" value="1" required <?= !empty($_POST['consent']) ? 'checked' : '' ?>>
<span>
Zapoznałem się z <a href="/polityka-prywatnosci" target="_blank" rel="noopener">Polityką prywatności</a>.
Dane podaję w celu obsługi rezerwacji.
</span>
</label>
<div class="reservation-hp" aria-hidden="true">
<label>
Strona firmy
<input name="company_website" tabindex="-1" autocomplete="off">
</label>
</div>
</div>
<!-- POLANA_NEWSLETTER_RESERVATION_BOX_V1_1 -->
<div class="reservation-newsletter">
<strong>Chcesz wiedzieć, co nowego na Polanie?</strong>
<label class="newsletter-consent">
<input type="checkbox" name="newsletter_optin" value="1" <?= !empty($_POST['newsletter_optin']) ? 'checked' : '' ?>>
<span>Dołącz do newslettera Mojej Polany Smaków. Chcę otrzymywać informacje o nowym menu, sezonowych smakach, wydarzeniach i nowościach. Zgoda jest dobrowolna i mogę ją wycofać w każdej chwili.</span>
</label>
</div>
<button class="btn-public btn-dark reservation-submit" type="submit" data-reservation-submit>
Wyślij prośbę o rezerwację
</button>
<p class="reservation-disclaimer">
Wysłanie formularza nie oznacza automatycznego potwierdzenia stolika.
</p>
</form>
<?php endif; ?>
</div>
</section>
</main>
<!-- POLANA_V175_PUBLIC_HOURS_SCRIPT -->
<?php if (pv175_enabled()): ?>
<script>
(function(){
const dateInput=document.querySelector('input[name="reservation_date"]');
const timeSelect=document.getElementById('pv175-time');
const hint=document.getElementById('pv175-hint');
if(!dateInput||!timeSelect||!hint)return;
const selectedOnSubmit=<?= json_encode((string)($_POST['reservation_time']??''), JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT) ?>;
let requestNo=0;
async function refresh(){
const id=++requestNo;
timeSelect.replaceChildren(new Option('Sprawdzam dostępne godziny…',''));
timeSelect.disabled=true;
if(!dateInput.value){timeSelect.replaceChildren(new Option('Najpierw wybierz datę',''));hint.textContent='Wybierz datę.';return;}
try{
const response=await fetch('/polana-rezerwacje-godziny.php?date='+encodeURIComponent(dateInput.value),{credentials:'same-origin',cache:'no-store'});
if(!response.ok)throw new Error('Błąd sprawdzania godzin');
const data=await response.json();
if(id!==requestNo)return;
timeSelect.replaceChildren(new Option(data.slots?.length?'Wybierz godzinę':'Brak dostępnych godzin',''));
for(const slot of (data.slots||[]))timeSelect.add(new Option(slot,slot));
const oldValue=(id===1?selectedOnSubmit:'');
if(oldValue&&data.slots.includes(oldValue))timeSelect.value=oldValue;
timeSelect.disabled=!data.slots?.length;
hint.textContent=data.message||'';
hint.style.color=data.slots?.length?'#566a56':'#9a442f';
}catch(err){
if(id!==requestNo)return;
timeSelect.replaceChildren(new Option('Nie można sprawdzić godzin',''));
timeSelect.disabled=true;
hint.textContent='Nie udało się pobrać godzin. Odśwież stronę i spróbuj ponownie.';
hint.style.color='#9a442f';
}
}
dateInput.addEventListener('change',refresh);
refresh();
})();
</script>
<?php endif; ?>
<script>
// Klient blokuje ponowne kliknięcie; serwer niezależnie blokuje powtórny zapis.
(function () {
const form = document.querySelector('[data-reservation-form]');
if (!form) return;
let submitted = false;
form.addEventListener('submit', function (event) {
if (submitted) { event.preventDefault(); return; }
if (!form.checkValidity()) return;
submitted = true;
const button = form.querySelector('[data-reservation-submit]');
if (button) {
button.disabled = true;
button.textContent = 'Wysyłanie…';
button.setAttribute('aria-busy', 'true');
}
});
window.addEventListener('pageshow', function () {
submitted = false;
const button = form.querySelector('[data-reservation-submit]');
if (button) {
button.disabled = false;
button.textContent = 'Wyślij prośbę o rezerwację';
button.removeAttribute('aria-busy');
}
});
})();
</script>
<?php render_public_footer(); ?>