query('SELECT COUNT(*) FROM products')->fetchColumn() === 0) { $ins = $pdo->prepare('INSERT INTO products (code,name,default_minutes,price_cents,active) VALUES (?,?,?,?,1)'); foreach ($CFG['products'] as $p) { $ins->execute([$p['code'],$p['name'],$p['minutes'],$p['price_cents']]); } } $step = $_GET['step'] ?? 'product'; // Hent aktive produkter $products = $pdo->query('SELECT * FROM products WHERE active=1 ORDER BY id')->fetchAll(); function findProduct(array $prods, int $id): ?array { foreach ($prods as $p) if ((int)$p['id']===$id) return $p; return null; } ?> Værkstedsbooking

Booking af værkstedsbesøg

Vælg et produkt for at fortsætte:


min ·
Ugyldigt produkt.

'; exit; } $year = (int)($_GET['y'] ?? (int)date('Y')); $month = (int)($_GET['m'] ?? (int)date('n')); $first = new DateTimeImmutable(sprintf('%04d-%02d-01', $year, $month)); $dow = (int)$first->format('N'); // 1..7 $daysInMonth = (int)$first->format('t'); $prev = $first->modify('-1 month'); $next = $first->modify('+1 month'); ?>

· min ·

◀ Forrige format('F Y') ?> Næste ▶

'; for ($col=1; $col<=7; $col++) { if (($row===0 && $col < $dow) || $d > $daysInMonth) { echo ''; continue; } $ymd = $first->format('Y-m-') . sprintf('%02d',$d); $isPast = (new DateTimeImmutable($ymd . ' 23:59:59')) < (new DateTimeImmutable('now')); $green = !$isPast && $avail->dateIsGreen($ymd, (int)$product['default_minutes']); if ($green) { echo ''; } else { echo ''; } $d++; } echo ''; if ($d > $daysInMonth) break; } ?>
ManTirOnsTorFreLørSøn
'.$d.''.$d.'

Grøn = ledig (kapacitet), Rød = fuldt optaget eller lukket.

Ugyldig forespørgsel.

'; exit; } $slots = $avail->listSlots($date); ?>

· Dato:

Vælg afhentningstidspunkt:

Efter valg viser vi afleveringsfrist (24t før afhentning).

Ugyldig forespørgsel.

'; exit; } $pickup_at = $date . ' ' . $time . ':00'; $deadline = $avail->dropoffDeadline($pickup_at); // Kapacitets- og slot-tjek igen (server-side) if (!$avail->dateIsGreen($date, (int)$product['default_minutes'])) { echo '

Denne dato er desværre blevet optaget. Vælg venligst en anden dato.

'; exit; } $slotOk = false; foreach ($avail->listSlots($date) as $s) { if ($s['label']===$time && $s['free']) $slotOk = true; } if (!$slotOk) { echo '

Det valgte klokkeslæt er netop blevet optaget. Vælg et andet.

'; exit; } ?>

· Afhentning:
Aflever senest:

Når du sender, modtager du en e-mailbekræftelse.

prepare('SELECT * FROM products WHERE id=? AND active=1'); $product->execute([$product_id]); $product = $product->fetch(); if (!$product) { echo '

Ugyldigt produkt.

'; exit; } $pickup_at = $_POST['pickup_at'] ?? ''; $deadline = $_POST['deadline'] ?? ''; if (!preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/',$pickup_at) || !preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/',$deadline)) { echo '

Ugyldige tider.

'; exit; } // Final kapacitets/slot check $date = substr($pickup_at,0,10); $time = substr($pickup_at,11,5); if (!$avail->dateIsGreen($date, (int)$product['default_minutes'])) { echo '

Datoen blev optaget.

'; exit; } $slotOk = false; foreach ($avail->listSlots($date) as $s) { if ($s['label']===$time && $s['free']) $slotOk = true; } if (!$slotOk) { echo '

Klokkeslættet blev optaget.

'; exit; } // Valider felter $data = [ 'first_name' => trim((string)($_POST['first_name'] ?? '')), 'last_name' => trim((string)($_POST['last_name'] ?? '')), 'address' => trim((string)($_POST['address'] ?? '')), 'house_no' => trim((string)($_POST['house_no'] ?? '')), 'zip' => trim((string)($_POST['zip'] ?? '')), 'city' => trim((string)($_POST['city'] ?? '')), 'phone' => trim((string)($_POST['phone'] ?? '')), 'email' => trim((string)($_POST['email'] ?? '')), 'plate' => trim((string)($_POST['plate'] ?? '')), ]; foreach ($data as $k=>$v) if ($v==='') { echo '

Manglende felt: '.h($k).'

'; exit; } $cancel = bin2hex(random_bytes(16)); $stmt = $pdo->prepare('INSERT INTO bookings (product_id, status, pickup_at, dropoff_deadline, planned_minutes, first_name,last_name,address,house_no,zip,city,phone,email,plate,cancel_token) VALUES (?, "booked", ?, ?, ?, ?,?,?,?,?,?,?,?,?,?)'); $stmt->execute([ $product_id, $pickup_at, $deadline, (int)$product['default_minutes'], $data['first_name'],$data['last_name'],$data['address'],$data['house_no'], $data['zip'],$data['city'],$data['phone'],$data['email'],$data['plate'],$cancel ]); $bookingId = (int)$pdo->lastInsertId(); // Send e-mail $html = '

Tak for din booking.

' . '

'.h($product['name']).'
' . 'Pris: '.money_dkk((int)$product['price_cents']).'
' . 'Afhentning: '.h($pickup_at).'
' . 'Aflever senest: '.h($deadline).'
' . 'Nr.plade: '.h($data['plate']).'

' . '

Hvis du har spørgsmål, kontakt os gerne.

'; $mailer->send($data['email'], 'Bekræftelse på værkstedsbooking', $html); // (Valgfrit) log udsendelsen $pdo->prepare('INSERT INTO notifications_log (booking_id, channel, payload) VALUES (?,?,?)') ->execute([$bookingId,'email',$html]); // Bekræftelse echo '

Tak – din booking er modtaget

'; echo '

Du har valgt: '.h($product['name']).' – '.money_dkk((int)$product['price_cents']).'

'; echo '

Afhentning: '.h($pickup_at).'
Aflever senest: '.h($deadline).'

'; echo '

En bekræftelse er sendt til '.h($data['email']).'.

'; echo '

Ny booking

';

Ugyldigt step.