Files
on-motorrad-buchungs-plugin/on-motorrad-buchung.php

365 lines
17 KiB
PHP

<?php
/**
* Plugin Name: ON Motorrad Buchung
* Description: Ein Buchungssystem für Motorradwerkstätten mit Admin-Verwaltung und Frontend-Formular.
* Version: 1.0.0
* Author: Antigravity
* Text Domain: on-motorrad-buchung
*/
if (!defined('ABSPATH')) {
exit;
}
// Constants
define('ON_BOOKING_PATH', plugin_dir_path(__FILE__));
define('ON_BOOKING_URL', plugin_dir_url(__FILE__));
define('ON_BOOKING_VERSION', '1.0.0');
// Include Classes
require_once ON_BOOKING_PATH . 'includes/class-on-booking-admin.php';
require_once ON_BOOKING_PATH . 'includes/class-on-booking-ajax.php';
require_once ON_BOOKING_PATH . 'includes/class-on-service-manager.php';
require_once ON_BOOKING_PATH . 'includes/class-on-email-manager.php'; // Email Manager
class ON_Booking_Plugin
{
public function __construct()
{
add_action('init', array($this, 'register_post_types'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_assets'));
// Initialize Sub-Classes
new ON_Booking_Admin();
new ON_Booking_Ajax();
ON_Email_Manager::init(); // Initialize Async Email Handling
// Shortcode
add_shortcode('on_booking_form', array($this, 'render_booking_form'));
}
public function register_post_types()
{
$labels = array(
'name' => 'Buchungen',
'singular_name' => 'Buchung',
'menu_name' => 'Buchungen',
'name_admin_bar' => 'Buchung',
'add_new' => 'Hinzufügen',
'add_new_item' => 'Neue Buchung',
'new_item' => 'Neue Buchung',
'edit_item' => 'Buchung bearbeiten',
'view_item' => 'Buchung ansehen',
'all_items' => 'Alle Buchungen',
'search_items' => 'Buchungen suchen',
'not_found' => 'Keine Buchungen gefunden',
'not_found_in_trash' => 'Keine Buchungen im Papierkorb',
);
$args = array(
'labels' => $labels,
'public' => false,
'publicly_queryable' => false,
'show_ui' => false, // Hidden from standard UI
'show_in_menu' => false, // Hidden from Admin Menu
'query_var' => true,
'rewrite' => array('slug' => 'on-booking'),
'capability_type' => 'post',
'has_archive' => false,
'hierarchical' => false,
'menu_position' => 50,
'menu_icon' => 'dashicons-calendar-alt',
'supports' => array('title', 'editor', 'custom-fields'),
);
register_post_type('on_booking', $args);
}
public function enqueue_assets()
{
wp_enqueue_style('on-booking-style', ON_BOOKING_URL . 'assets/css/style.css', array(), time());
// Google Fonts from example.html
wp_enqueue_style('on-booking-fonts', 'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap', array(), null);
wp_enqueue_script('on-booking-script', ON_BOOKING_URL . 'assets/js/script.js', array('jquery'), time(), true);
wp_localize_script('on-booking-script', 'onBookingData', array(
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('on_booking_nonce'),
'simpleMode' => (bool) get_option('on_booking_simple_mode', 0),
));
}
public function render_booking_form()
{
// Ensure modals are rendered in footer (once)
add_action('wp_footer', array($this, 'render_modals_once'));
ob_start();
?>
<div class="on-booking-system">
<button class="on-trigger-btn js-open-booking">
<span>Termin Buchen</span>
</button>
<button class="on-trigger-btn on-status-btn js-open-status" style="margin-left: 10px; background: #333;">
<span>Status Abfragen</span>
</button>
</div>
<?php
return ob_get_clean();
}
public function render_modals_once()
{
// Static var to prevent double rendering if hook is called multiple times (unlikely on footer but good practice)
static $rendered = false;
if ($rendered)
return;
$rendered = true;
?>
<!-- Structure copied and adapted from example.html -->
<style>
/* CRITICAL INLINE STYLES - Define CSS variables for moved modal */
#onModalOverlay,
#onStatusModalOverlay,
#onSuccessModalOverlay {
/* CSS Variables - replicate from .on-booking-system */
--on-blue: #0061ff;
--on-dark: #111111;
--on-gray: #1f1f1f;
--on-text: #ffffff;
--on-muted: #888888;
--on-border: #333333;
display: none;
position: fixed;
z-index: 2147483647;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.85);
/* Flexbox properties for when open */
align-items: center;
justify-content: center;
padding: 20px;
/* No overflow on overlay - content will scroll */
overflow: hidden;
}
#onModalOverlay.is-open,
#onStatusModalOverlay.is-open,
#onSuccessModalOverlay.is-open {
display: flex !important;
}
/* Content Box - THIS scrolls, not the overlay */
#onModalOverlay .on-modal-content,
#onStatusModalOverlay .on-modal-content,
#onSuccessModalOverlay .on-modal-content {
width: 100%;
max-width: 500px;
max-height: 90vh;
background: var(--on-gray);
border-left: 5px solid var(--on-blue);
color: var(--on-text);
padding: 20px;
position: relative;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
/* FORCE SCROLL - Very aggressive */
overflow-y: auto !important;
overflow-x: hidden !important;
-webkit-overflow-scrolling: touch !important;
overscroll-behavior: contain !important;
/* Ensure content is actually scrollable */
display: block !important;
/* Remove any potential transform issues */
will-change: scroll-position;
}
/* Lock body/html scroll */
html.on-modal-open,
body.on-modal-open {
overflow: hidden !important;
}
</style>
<!-- Status Modal -->
<div class="on-modal-overlay" id="onStatusModalOverlay">
<div class="on-modal-content">
<button class="on-close-btn" id="onCloseStatusModal">&times;</button>
<h3>Status <span>Prüfen</span></h3>
<div class="on-form-group">
<label>Deine Auftragsnummer</label>
<input type="text" class="on-input" id="onTrackingIdInput" placeholder="z.B. A1B2C3D4">
</div>
<button class="on-submit-btn" id="onCheckStatusBtn">Prüfen</button>
<div id="statusResult" style="margin-top: 20px; font-weight: bold; text-align: center;"></div>
</div>
</div>
<!-- Success Popup Modal -->
<div class="on-modal-overlay" id="onSuccessModalOverlay">
<div class="on-modal-content" style="max-width: 400px; text-align: center;">
<button class="on-close-btn" id="onCloseSuccessModal">&times;</button>
<div style="font-size: 4rem; color: var(--on-blue); margin-bottom: 15px;">✓</div>
<h3 style="margin-bottom: 10px;">Buchung <span>Erfolgreich!</span></h3>
<p style="color: #888; margin-bottom: 20px;">Deine Terminanfrage wurde erfolgreich übermittelt.</p>
<div
style="background: rgba(0,97,255,0.1); border: 1px solid var(--on-blue); border-radius: 8px; padding: 15px; margin-bottom: 20px;">
<p style="color: #888; font-size: 0.85rem; margin-bottom: 5px;">Deine Auftragsnummer:</p>
<p id="successTrackingId"
style="font-size: 1.5rem; font-weight: bold; color: var(--on-blue); letter-spacing: 2px;"></p>
</div>
<p style="color: #666; font-size: 0.85rem; margin-bottom: 20px;">
Bitte notiere dir diese Nummer, um den Status deiner Buchung später abfragen zu können.
</p>
<button class="on-submit-btn" id="onSuccessCloseBtn" style="width: 100%;">Alles klar!</button>
</div>
</div>
<?php $simple_mode = get_option('on_booking_simple_mode', 0); ?>
<!-- Modal Overlay -->
<div class="on-modal-overlay" id="onModalOverlay">
<div class="on-modal-content">
<button class="on-close-btn" id="onCloseModal">&times;</button>
<h3><?php echo $simple_mode ? 'Anfrage <span>Senden</span>' : 'Termin <span>W&auml;hlen</span>'; ?></h3>
<form id="onBookingForm" enctype="multipart/form-data">
<?php if ($simple_mode): ?>
<input type="hidden" name="simple_mode" value="1">
<?php endif; ?>
<?php if (!$simple_mode): ?>
<!-- Step 1: Service Selection (FIRST) -->
<div class="on-form-group on-step" id="stepService">
<label>1. Service w&auml;hlen</label>
<select class="on-input" name="service_id" id="serviceSelect" required>
<option value="" disabled selected>Bitte Service w&auml;hlen...</option>
<?php
$services = ON_Service_Manager::get_active_services();
$show_duration = get_option('on_booking_show_duration', 0);
foreach ($services as $service) {
$duration_label = '';
if ($show_duration) {
$duration_label = $service['duration'] < 60
? ' (' . $service['duration'] . ' min)'
: ' (' . ($service['duration'] / 60) . ' Std)';
}
echo '<option value="' . esc_attr($service['id']) . '" data-duration="' . esc_attr($service['duration']) . '">';
echo esc_html($service['name']) . $duration_label;
echo '</option>';
}
?>
</select>
<input type="hidden" name="service_duration" id="serviceDuration">
</div>
<!-- Step 2: Calendar (hidden until service selected) -->
<div class="on-form-group on-step" id="stepCalendar" style="display: none;">
<label>2. Datum w&auml;hlen</label>
<div class="on-calendar-header">
<button type="button" class="on-cal-btn" id="prevMonth">&lt;</button>
<span class="on-month-label" id="monthYear"></span>
<button type="button" class="on-cal-btn" id="nextMonth">&gt;</button>
</div>
<div class="on-calendar-grid" id="calendarGrid"></div>
<input type="hidden" name="selected_date" id="selectedDate">
</div>
<!-- Step 3: Time Slots (hidden until date selected) -->
<div class="on-form-group on-time-section on-step" id="timeSection" style="display: none;">
<label>3. Uhrzeit w&auml;hlen</label>
<div class="on-time-grid" id="timeGrid"></div>
<input type="hidden" name="selected_time" id="selectedTime">
</div>
<?php endif; ?>
<!-- Step 4: Details (in simple mode: always visible) -->
<div class="on-details-section on-step" id="stepDetails" <?php echo $simple_mode ? '' : 'style="display: none;"'; ?>>
<?php if (!$simple_mode): ?>
<div class="on-section-divider" style="margin: 20px 0; border-top: 1px solid #333;"></div>
<?php endif; ?>
<?php if ($simple_mode): ?>
<!-- Free-text request field (simple mode) -->
<div class="on-form-group">
<label>Was soll gemacht werden?</label>
<textarea class="on-input" name="customer_request" rows="4"
placeholder="Beschreibe kurz, was du dir w&uuml;nschst oder was repariert werden soll..."
required></textarea>
</div>
<div class="on-section-divider" style="margin: 20px 0; border-top: 1px solid #333;"></div>
<?php endif; ?>
<!-- Bike Details -->
<div class="on-form-group">
<label>Motorrad Marke</label>
<input type="text" class="on-input" name="brand" placeholder="z.B. Honda, Yamaha" <?php echo $simple_mode ? '' : 'required'; ?>>
</div>
<div class="on-form-group">
<label>Baujahr</label>
<input type="number" class="on-input" name="year" placeholder="z.B. 2019" <?php echo $simple_mode ? '' : 'required'; ?>>
</div>
<?php if (!$simple_mode): ?>
<!-- Additional Notes for "Sonstiges" -->
<div class="on-form-group" id="otherServiceGroup" style="display: none;">
<label>Beschreibung des Problems</label>
<textarea class="on-input" name="other_service_desc" rows="3"
placeholder="Was muss gemacht werden?"></textarea>
</div>
<?php endif; ?>
<!-- Personal Info -->
<div class="on-section-divider" style="margin: 20px 0; border-top: 1px solid #333;"></div>
<div class="on-form-group">
<label>Dein Name</label>
<input type="text" class="on-input" name="user_name" placeholder="Vor- und Nachname" required>
</div>
<div class="on-form-group">
<label>Telefon</label>
<input type="tel" class="on-input" name="user_phone" placeholder="Nr. f&uuml;r R&uuml;ckfragen"
required>
</div>
<div class="on-form-group">
<label>E-Mail</label>
<input type="email" class="on-input" name="user_email" placeholder="Deine E-Mail Adresse" required>
</div>
<!-- File Upload -->
<div class="on-form-group">
<label>Fahrzeugschein (Optional)</label>
<input type="file" class="on-input" name="vehicle_doc"
accept="image/png, image/jpeg, image/jpg, application/pdf">
<small style="display:block; color:#666; font-size: 0.7rem; margin-top:5px;">Erlaubt: JPG,
PNG,
PDF (Max. 10MB)</small>
</div>
<button type="submit" class="on-submit-btn" id="submitBtn">
<span>Jetzt Anfragen</span>
</button>
<div id="formFeedback" style="display:none; margin-top:15px; text-align:center; font-weight:bold;">
</div>
</div><!-- End stepDetails -->
</form>
</div>
</div>
<?php
}
}
new ON_Booking_Plugin();