366 lines
17 KiB
PHP
366 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: Keil&Schick
|
|
* Author URI: https://keil-schick.de
|
|
* 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">×</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">×</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">×</button>
|
|
|
|
<h3><?php echo $simple_mode ? 'Anfrage <span>Senden</span>' : 'Termin <span>Wä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ählen</label>
|
|
<select class="on-input" name="service_id" id="serviceSelect" required>
|
|
<option value="" disabled selected>Bitte Service wä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ählen</label>
|
|
<div class="on-calendar-header">
|
|
<button type="button" class="on-cal-btn" id="prevMonth"><</button>
|
|
<span class="on-month-label" id="monthYear"></span>
|
|
<button type="button" class="on-cal-btn" id="nextMonth">></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ä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ü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ür Rü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();
|