403Webshell
Server IP : 193.86.120.172  /  Your IP : 216.73.216.215
Web Server : Apache/2.4.63 (Unix)
System : Linux JServices 3.10.108 #86003 SMP Wed Oct 22 13:20:46 CST 2025 x86_64
User : kubec ( 1026)
PHP Version : 8.2.28
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /volume1/web/kadernictvi_jservices_cz/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /volume1/web/kadernictvi_jservices_cz/zmena-rezervace.php
<?php
require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/mailer.php';
require_once __DIR__ . '/includes/calendar.php';

$token = sanitize($_GET['token'] ?? '');
$error = '';
$rezervace = null;
$sluzby = [];
$uspech = false;

if (strlen($token) !== 64) { $error = 'Neplatný odkaz.'; }

if (!$error) {
    $stmt = db()->prepare("SELECT r.*, s.nazev AS sluzba_nazev, s.cena FROM rezervace r JOIN sluzby s ON s.id = r.sluzba_id WHERE r.token_zmena = ?");
    $stmt->execute([$token]);
    $rezervace = $stmt->fetch();
    if (!$rezervace) { $error = 'Rezervace nenalezena.'; }
    elseif ($rezervace['stav'] === 'zrusena') { $error = 'Tato rezervace byla již zrušena.'; }
}

if (!$error) {
    $stmt2 = db()->query("SELECT id, nazev, trvani_min, cena FROM sluzby WHERE aktivni = 1 ORDER BY poradi");
    $sluzby = $stmt2->fetchAll();
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && !$error) {
    $nova_sluzba_id = (int)($_POST['sluzba_id'] ?? 0);
    $novy_datum     = sanitize($_POST['datum'] ?? '');
    $novy_cas       = sanitize($_POST['cas'] ?? '');

    if (!$nova_sluzba_id || !valid_date($novy_datum) || !preg_match('/^\d{2}:\d{2}$/', $novy_cas)) {
        $error = 'Vyplňte všechna pole.';
    } elseif ($novy_datum < date('Y-m-d')) {
        $error = 'Nelze rezervovat v minulosti.';
    } else {
        $stmtS = db()->prepare("SELECT * FROM sluzby WHERE id = ? AND aktivni = 1");
        $stmtS->execute([$nova_sluzba_id]);
        $nova_sluzba = $stmtS->fetch();
        if (!$nova_sluzba) { $error = 'Neplatná služba.'; }
        else {
            $volne = volne_casy($novy_datum, $nova_sluzba['trvani_min']);
            if (!in_array($novy_cas, $volne)) { $error = 'Vybraný termín již není dostupný. Vyberte prosím jiný.'; }
            else {
                $cas_od = $novy_cas . ':00';
                $cas_do = date('H:i:s', strtotime($cas_od) + $nova_sluzba['trvani_min'] * 60);
                db()->prepare("UPDATE rezervace SET sluzba_id=?, datum=?, cas_od=?, cas_do=?, stav='ceka' WHERE token_zmena=?")
                    ->execute([$nova_sluzba_id, $novy_datum, $cas_od, $cas_do, $token]);

                // Notifikace admina i zákazníka
                $rez_mail = [
                    'jmeno'    => $rezervace['jmeno'],
                    'prijmeni' => $rezervace['prijmeni'],
                    'email'    => $rezervace['email'],
                    'telefon'  => $rezervace['telefon'] ?? '',
                    'poznamka' => $rezervace['poznamka'] ?? '',
                    'datum'    => $novy_datum,
                    'cas_od'   => $novy_cas,
                    'cena'     => number_format($nova_sluzba['cena'], 0, ',', ' '),
                    'token_zmena' => $token,
                ];
                // Aktualizuj nebo vytvoř událost v Microsoft Calendar
                $rez_cal = [
                    'jmeno'    => $rezervace['jmeno'], 'prijmeni' => $rezervace['prijmeni'],
                    'email'    => $rezervace['email'], 'telefon'  => $rezervace['telefon'] ?? '',
                    'poznamka' => $rezervace['poznamka'] ?? '',
                    'datum'    => $novy_datum,
                    'cas_od'   => $novy_cas,
                    'cas_do'   => substr($cas_do, 0, 5),
                    'cena'     => number_format($nova_sluzba['cena'], 0, ',', ' '),
                ];
                try {
                    if (!empty($rezervace['ms_event_id'])) {
                        graph_aktualizuj_udalost($rezervace['ms_event_id'], $rez_cal, $nova_sluzba['nazev']);
                    } else {
                        $new_event_id = graph_vytvor_udalost($rez_cal, $nova_sluzba['nazev']);
                        if ($new_event_id) {
                            db()->prepare("UPDATE rezervace SET ms_event_id = ? WHERE token_zmena = ?")
                               ->execute([$new_event_id, $token]);
                        }
                    }
                } catch (Exception $e) { error_log('[Zmena] Calendar error: ' . $e->getMessage()); }

                try { email_notifikace_admin($rez_mail, $nova_sluzba['nazev'], 'zmenena'); } catch (Exception $e) { error_log('[Zmena] Admin e-mail selhal: ' . $e->getMessage()); }

                $uspech = true;
            }
        }
    }
}

// Načti pracovní dobu per den z DB
$pracovni_dny_raw = [];
try {
    $stmt_pd = db()->query("SELECT den, od_min, do_min FROM pracovni_doba WHERE aktivni = 1 ORDER BY den");
    foreach ($stmt_pd->fetchAll() as $pd) {
        $pracovni_dny_raw[(int)$pd['den']] = true;
    }
} catch (Exception $e) {
    // Fallback
    foreach (explode(',', nastaveni('pracovni_dny', '1,2,3,4,5,6')) as $d) {
        $pracovni_dny_raw[(int)trim($d)] = true;
    }
}
$pracovni_dny = implode(',', array_keys($pracovni_dny_raw));
$salon_nazev  = nastaveni('salon_nazev', 'Kadeřnictví Denisa');
?>
<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Změna rezervace – <?= htmlspecialchars($salon_nazev) ?></title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:-apple-system,sans-serif;background:#f9fafb;color:#111827;min-height:100vh}
.page{max-width:520px;margin:0 auto;padding:2rem 1rem}
.logo{text-align:center;margin-bottom:1.5rem;font-size:20px;font-weight:700;color:#7c3aed}
.card{background:#fff;border:1px solid #e5e7eb;border-radius:16px;padding:1.5rem;margin-bottom:1rem}
h2{font-size:18px;font-weight:700;margin-bottom:6px}
.sub{font-size:13px;color:#6b7280;margin-bottom:1.25rem}
label{font-size:13px;color:#6b7280;display:block;margin-bottom:6px;margin-top:12px}
select{width:100%;border:2px solid #e9d5ff;border-radius:8px;padding:10px 12px;font-size:14px;color:#111827;outline:none;margin-bottom:4px;font-family:inherit;background:#fff}
select:focus{border-color:#a855f7}
.btn{width:100%;background:#9333ea;color:#fff;border:none;border-radius:999px;padding:13px;font-size:15px;font-weight:600;cursor:pointer;margin-top:16px}
.btn:hover:not(:disabled){background:#7c3aed}
.btn:disabled{background:#d8b4fe;cursor:default}
.alert{border-radius:10px;padding:12px 14px;font-size:13px;margin-bottom:1rem}
.alert.error{background:#fee2e2;color:#991b1b;border:1px solid #fca5a5}
.alert.success{background:#dcfce7;color:#166534;border:1px solid #86efac}
.info-row{display:flex;justify-content:space-between;font-size:13px;padding:6px 0;border-bottom:1px solid #f3e8ff}
.info-row:last-child{border-bottom:none;margin-bottom:1rem}
.info-label{color:#6b7280}
.info-val{font-weight:600}
.cal-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}
.cal-month{font-size:14px;font-weight:600}
.cal-nav{width:30px;height:30px;border:1px solid #e9d5ff;border-radius:8px;background:none;cursor:pointer;color:#9333ea;font-size:16px}
.cal-nav:hover{background:#faf5ff}
.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:3px}
.cal-day-name{text-align:center;font-size:11px;color:#9ca3af;padding:3px 0;font-weight:500}
.cal-day{text-align:center;padding:6px 2px;border-radius:7px;font-size:13px;cursor:pointer;border:2px solid transparent}
.cal-day:hover:not(.disabled):not(.empty){background:#f3e8ff;border-color:#e9d5ff}
.cal-day.today{border-color:#d8b4fe;color:#7c3aed;font-weight:600}
.cal-day.selected{background:#9333ea;color:#fff;border-color:#9333ea}
.cal-day.disabled{color:#d1d5db;cursor:default}
.cal-day.empty{cursor:default}
.times-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:7px;margin-top:10px}
.time-slot{border:2px solid #e9d5ff;border-radius:8px;padding:8px 4px;text-align:center;font-size:13px;cursor:pointer;transition:all .15s}
.time-slot:hover:not(.taken){background:#f3e8ff;border-color:#a855f7}
.time-slot.selected{background:#9333ea;color:#fff;border-color:#9333ea}
.time-slot.taken{color:#d1d5db;cursor:default;text-decoration:line-through;background:#f9fafb}
.loading{text-align:center;padding:1rem;color:#9ca3af;font-size:13px}
.note{font-size:12px;color:#9ca3af;margin-top:8px}
</style>
</head>
<body>
<div class="page">
<div class="logo">✂️ <?= htmlspecialchars($salon_nazev) ?></div>

<?php if ($uspech): ?>
<div class="card">
  <div class="alert success">✅ Rezervace byla úspěšně změněna. Potvrzení jsme vám zaslali e-mailem.</div>
  <a href="/" style="display:block;text-align:center;margin-top:1rem;color:#7c3aed;font-size:14px">← Zpět na rezervace</a>
</div>
<?php elseif ($error && empty($rezervace)): ?>
<div class="card">
  <div class="alert error"><?= htmlspecialchars($error) ?></div>
  <a href="/" style="display:block;text-align:center;margin-top:1rem;color:#7c3aed;font-size:14px">← Nová rezervace</a>
</div>
<?php else: ?>
<div class="card">
  <h2>Změnit rezervaci</h2>
  <div class="sub">Aktuální termín</div>
  <div class="info-row"><span class="info-label">Služba</span><span class="info-val"><?= htmlspecialchars($rezervace['sluzba_nazev']) ?></span></div>
  <div class="info-row"><span class="info-label">Datum</span><span class="info-val"><?= htmlspecialchars($rezervace['datum']) ?></span></div>
  <div class="info-row"><span class="info-label">Čas</span><span class="info-val"><?= substr($rezervace['cas_od'],0,5) ?></span></div>

  <?php if ($error): ?>
  <div class="alert error"><?= htmlspecialchars($error) ?></div>
  <?php endif; ?>

  <form method="post" id="zmenForm">
    <input type="hidden" name="datum" id="f-datum">
    <input type="hidden" name="cas" id="f-cas">

    <label>Nová služba</label>
    <select name="sluzba_id" id="f-sluzba" onchange="onSluzbaChange()">
      <?php foreach ($sluzby as $s): ?>
      <option value="<?= $s['id'] ?>" data-trvani="<?= $s['trvani_min'] ?>" <?= $s['id']==$rezervace['sluzba_id']?'selected':'' ?>>
        <?= htmlspecialchars($s['nazev']) ?> – <?= $s['trvani_min'] ?> min – <?= number_format($s['cena'],0,',',' ') ?> Kč
      </option>
      <?php endforeach; ?>
    </select>

    <label>Nové datum</label>
    <div class="cal-header">
      <button type="button" class="cal-nav" onclick="changeMonth(-1)">&#8249;</button>
      <div class="cal-month" id="calMonth"></div>
      <button type="button" class="cal-nav" onclick="changeMonth(1)">&#8250;</button>
    </div>
    <div class="cal-grid" id="calGrid"></div>

    <div id="timesSection" style="display:none;margin-top:14px">
      <label>Nový čas</label>
      <div id="timesLoading" class="loading" style="display:none">Načítám časy…</div>
      <div class="times-grid" id="timesGrid"></div>
      <p class="note">Přeškrtnuté časy jsou obsazeny.</p>
    </div>

    <button type="submit" class="btn" id="btnUlozit" disabled>Uložit změnu</button>
  </form>
</div>
<?php endif; ?>
</div>

<script>
var PRACOVNI_DNY = [<?= $pracovni_dny ?>];
var calDate = new Date(); calDate.setDate(1);
var MESICE = ['Leden','Únor','Březen','Duben','Květen','Červen','Červenec','Srpen','Září','Říjen','Listopad','Prosinec'];
var DNY_HL = ['Po','Út','St','Čt','Pá','So','Ne'];
var selectedDatum = null;

function getSluzbaTrvani() {
    var sel = document.getElementById('f-sluzba');
    return parseInt(sel.options[sel.selectedIndex].dataset.trvani);
}

function onSluzbaChange() {
    document.getElementById('f-datum').value = '';
    document.getElementById('f-cas').value = '';
    document.getElementById('timesSection').style.display = 'none';
    document.getElementById('btnUlozit').disabled = true;
    selectedDatum = null;
    renderCalendar();
}

function renderCalendar() {
    var now = new Date();
    var y = calDate.getFullYear(), m = calDate.getMonth();
    document.getElementById('calMonth').textContent = MESICE[m] + ' ' + y;
    var grid = document.getElementById('calGrid');
    grid.innerHTML = '';
    DNY_HL.forEach(function(d){ var el=document.createElement('div'); el.className='cal-day-name'; el.textContent=d; grid.appendChild(el); });
    var prvni = new Date(y, m, 1).getDay();
    var offset = prvni === 0 ? 6 : prvni - 1;
    for (var i = 0; i < offset; i++) { var e=document.createElement('div'); e.className='cal-day empty'; grid.appendChild(e); }
    var dniVM = new Date(y, m+1, 0).getDate();
    for (var d = 1; d <= dniVM; d++) {
        var el = document.createElement('div'); el.className = 'cal-day';
        el.textContent = d;
        var thisDate = new Date(y, m, d);
        var isPast = thisDate < new Date(now.getFullYear(), now.getMonth(), now.getDate());
        var dow = thisDate.getDay() === 0 ? 7 : thisDate.getDay();
        var isNepracovni = PRACOVNI_DNY.indexOf(dow) === -1;
        if (isPast || isNepracovni) {
            el.classList.add('disabled');
        } else {
            if (d === now.getDate() && m === now.getMonth() && y === now.getFullYear()) el.classList.add('today');
            if (selectedDatum && selectedDatum.getDate()===d && selectedDatum.getMonth()===m && selectedDatum.getFullYear()===y) el.classList.add('selected');
            (function(day, dateObj){ el.onclick = function(){ selectDatum(day, dateObj); }; })(d, thisDate);
        }
        grid.appendChild(el);
    }
}

async function selectDatum(d, dateObj) {
    selectedDatum = dateObj;
    document.getElementById('f-datum').value = formatDate(dateObj);
    document.getElementById('f-cas').value = '';
    document.getElementById('btnUlozit').disabled = true;
    renderCalendar();
    var ts = document.getElementById('timesSection');
    ts.style.display = 'block';
    document.getElementById('timesLoading').style.display = 'block';
    document.getElementById('timesGrid').innerHTML = '';
    var sluzbaId = document.getElementById('f-sluzba').value;
    try {
        var r = await fetch('/api/dostupnost?sluzba_id=' + sluzbaId + '&datum=' + formatDate(dateObj));
        var data = await r.json();
        document.getElementById('timesLoading').style.display = 'none';
        if (data.blokovano || !data.volne_casy || !data.volne_casy.length) {
            document.getElementById('timesGrid').innerHTML = '<p style="color:#9ca3af;font-size:13px;grid-column:1/-1">' + (data.blokovano ? 'Tento den je zablokován.' : 'Žádné volné termíny.') + '</p>';
            return;
        }
        var tg = document.getElementById('timesGrid');
        data.volne_casy.forEach(function(t) {
            var el = document.createElement('div'); el.className = 'time-slot'; el.textContent = t;
            el.onclick = function() {
                document.querySelectorAll('.time-slot').forEach(function(s){ s.classList.remove('selected'); });
                el.classList.add('selected');
                document.getElementById('f-cas').value = t;
                document.getElementById('btnUlozit').disabled = false;
            };
            tg.appendChild(el);
        });
    } catch(e) {
        document.getElementById('timesLoading').style.display = 'none';
        document.getElementById('timesGrid').innerHTML = '<p style="color:#991b1b;font-size:13px;grid-column:1/-1">Chyba načítání termínů.</p>';
    }
}

function changeMonth(dir) {
    var now = new Date();
    calDate.setMonth(calDate.getMonth() + dir);
    if (calDate < new Date(now.getFullYear(), now.getMonth(), 1)) {
        calDate = new Date(now.getFullYear(), now.getMonth(), 1);
    }
    renderCalendar();
}

function formatDate(d) {
    return d.getFullYear() + '-' + String(d.getMonth()+1).padStart(2,'0') + '-' + String(d.getDate()).padStart(2,'0');
}

renderCalendar();
</script>
</body>
</html>

Youez - 2016 - github.com/yon3zu
LinuXploit