feat(07-01): add captcha widget and error display
- Render validation errors from transient at form top - Display error summary with red border - Integrate captcha widget in submit section - Position captcha above submit button - Delete transient after displaying errors Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
370
includes/class-form-renderer.php
Normal file
370
includes/class-form-renderer.php
Normal file
@@ -0,0 +1,370 @@
|
||||
<?php
|
||||
/**
|
||||
* Form Renderer
|
||||
*
|
||||
* Generates HTML for the umzugsliste form
|
||||
*
|
||||
* @package Umzugsliste
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form renderer class
|
||||
*/
|
||||
class Umzugsliste_Form_Renderer {
|
||||
|
||||
/**
|
||||
* Render complete form
|
||||
*
|
||||
* @return string Complete form HTML
|
||||
*/
|
||||
public static function render() {
|
||||
ob_start();
|
||||
?>
|
||||
<div class="umzugsliste-wrapper">
|
||||
<form id="umzugsliste-form" name="umzug" method="post" action="">
|
||||
<?php
|
||||
self::render_validation_errors();
|
||||
self::render_header();
|
||||
self::render_date_selector();
|
||||
self::render_customer_info();
|
||||
self::render_all_rooms();
|
||||
self::render_grand_totals();
|
||||
self::render_submit_section();
|
||||
?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render validation errors if any exist
|
||||
*/
|
||||
private static function render_validation_errors() {
|
||||
// Check for validation errors in transient
|
||||
$session_id = session_id();
|
||||
if ( empty( $session_id ) ) {
|
||||
$session_id = 'default';
|
||||
}
|
||||
|
||||
$errors = get_transient( 'umzugsliste_errors_' . $session_id );
|
||||
|
||||
if ( ! $errors || empty( $errors['messages'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete transient after displaying
|
||||
delete_transient( 'umzugsliste_errors_' . $session_id );
|
||||
?>
|
||||
<div class="validation-summary">
|
||||
<h3>Bitte korrigieren Sie folgende Fehler:</h3>
|
||||
<ul>
|
||||
<?php foreach ( $errors['messages'] as $message ) : ?>
|
||||
<li><?php echo esc_html( $message ); ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render form header with logo and company info
|
||||
*/
|
||||
private static function render_header() {
|
||||
$plugin_url = plugin_dir_url( dirname( __FILE__ ) );
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="medium-6 columns">
|
||||
<h1>Umzugsliste</h1>
|
||||
</div>
|
||||
<div class="medium-6 columns">
|
||||
<p><br>Willi-Werner-Straße 6 · 65199 Wiesbaden<br>
|
||||
E-Mail: <a href="mailto:info@siegel-umzug.de">info@siegel-umzug.de</a><br>
|
||||
Telefon (06 11) 2 20 20 · Fax (06 11) 2 10 10<br>
|
||||
Mainz: Telefon (0 61 31) 22 21 41
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render moving date selector
|
||||
*/
|
||||
private static function render_date_selector() {
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="large-6 columns">
|
||||
<fieldset>
|
||||
<legend>Voraussichtlicher Umzugstermin</legend>
|
||||
<?php
|
||||
echo Umzugsliste_Date_Helpers::render_day_select();
|
||||
echo Umzugsliste_Date_Helpers::render_month_select();
|
||||
echo Umzugsliste_Date_Helpers::render_year_select();
|
||||
?>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="large-6 columns">
|
||||
<p><br>In unserer <a href="http://siegel-umzug.de/datenschutz.html">Datenschutzerklärung</a> erfahren Sie, wie die Siegel Umzüge GmbH & Co. KG Ihre Daten erfasst und verwendet.</p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render customer info section (Beladeadresse and Entladeadresse)
|
||||
*/
|
||||
private static function render_customer_info() {
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="large-6 columns">
|
||||
<div class="panel">
|
||||
<h3>Beladeadresse</h3>
|
||||
</div>
|
||||
<div class="small-12">
|
||||
<?php self::render_address_field( 'Name*', 'bName', true ); ?>
|
||||
<?php self::render_address_field( 'Straße*', 'bStrasse', true ); ?>
|
||||
<?php self::render_address_field( 'PLZ/Ort*', 'bort', true ); ?>
|
||||
<?php self::render_address_field( 'Geschoss', 'info[bGeschoss]' ); ?>
|
||||
<?php self::render_lift_field( 'info[bLift]' ); ?>
|
||||
<?php self::render_address_field( 'Telefon*', 'bTelefon', true ); ?>
|
||||
<?php self::render_address_field( 'Telefax', 'info[bTelefax]' ); ?>
|
||||
<?php self::render_address_field( 'Mobil', 'info[bMobil]' ); ?>
|
||||
<?php self::render_address_field( 'E-Mail*', 'info[eE-Mail]', true ); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="large-6 columns">
|
||||
<div class="panel">
|
||||
<h3>Entladeadresse</h3>
|
||||
</div>
|
||||
<div class="small-12">
|
||||
<?php self::render_address_field( 'Name*', 'eName', true ); ?>
|
||||
<?php self::render_address_field( 'Straße*', 'eStrasse', true ); ?>
|
||||
<?php self::render_address_field( 'PLZ/Ort*', 'eort', true ); ?>
|
||||
<?php self::render_address_field( 'Geschoss', 'info[eGeschoss]' ); ?>
|
||||
<?php self::render_lift_field( 'info[eLift]' ); ?>
|
||||
<?php self::render_address_field( 'Telefon', 'eTelefon' ); ?>
|
||||
<?php self::render_address_field( 'Telefax', 'info[eTelefax]' ); ?>
|
||||
<?php self::render_address_field( 'Mobil', 'info[eMobil]' ); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="large-12 columns">
|
||||
<div class="row">
|
||||
<div class="small-11 columns">
|
||||
<p><span class="radius secondary label">*Pflichtfelder</span></p>
|
||||
</div>
|
||||
<div class="small-1 columns"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render single address field
|
||||
*
|
||||
* @param string $label Field label
|
||||
* @param string $name Field name
|
||||
* @param bool $required Whether field is required
|
||||
*/
|
||||
private static function render_address_field( $label, $name, $required = false ) {
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="small-3 columns">
|
||||
<label for="<?php echo esc_attr( $name ); ?>" class="left inline"><?php echo esc_html( $label ); ?></label>
|
||||
</div>
|
||||
<div class="small-9 columns">
|
||||
<input type="text" id="<?php echo esc_attr( $name ); ?>" name="<?php echo esc_attr( $name ); ?>" <?php echo $required ? 'required' : ''; ?>>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render lift radio field
|
||||
*
|
||||
* @param string $name Field name
|
||||
*/
|
||||
private static function render_lift_field( $name ) {
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="small-3 columns">
|
||||
<label class="left">Lift</label>
|
||||
</div>
|
||||
<div class="small-9 columns">
|
||||
<input type="radio" name="<?php echo esc_attr( $name ); ?>" value="nein" checked><label>Nein</label>
|
||||
<input type="radio" name="<?php echo esc_attr( $name ); ?>" value="ja"><label>Ja</label>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render all room sections
|
||||
*/
|
||||
private static function render_all_rooms() {
|
||||
$rooms = Umzugsliste_Furniture_Data::get_rooms();
|
||||
|
||||
foreach ( $rooms as $room_key => $room_label ) {
|
||||
self::render_room_section( $room_key, $room_label );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render single room section
|
||||
*
|
||||
* @param string $room_key Room key
|
||||
* @param string $room_label Room label
|
||||
*/
|
||||
private static function render_room_section( $room_key, $room_label ) {
|
||||
$items = Umzugsliste_Furniture_Data::get_furniture_items( $room_key );
|
||||
|
||||
// Navigation anchor based on room
|
||||
$anchor_map = array(
|
||||
'wohnzimmer' => 'wohn',
|
||||
'schlafzimmer' => 'schlaf',
|
||||
'arbeitszimmer' => 'arbeit',
|
||||
'bad' => 'bad',
|
||||
'kueche_esszimmer' => 'kueche',
|
||||
'kinderzimmer' => 'kinder',
|
||||
'keller' => 'keller',
|
||||
);
|
||||
$anchor = isset( $anchor_map[ $room_key ] ) ? $anchor_map[ $room_key ] : $room_key;
|
||||
|
||||
// Post array name (capitalize first letter for legacy compatibility)
|
||||
$post_array_name = ucfirst( $room_key );
|
||||
// Special case for Küche/Esszimmer
|
||||
if ( 'kueche_esszimmer' === $room_key ) {
|
||||
$post_array_name = 'Kueche_Esszimmer';
|
||||
}
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<div class="panel">
|
||||
<a name="<?php echo esc_attr( $anchor ); ?>"></a>
|
||||
<h3 data-magellan-destination="<?php echo esc_attr( $anchor ); ?>"><?php echo esc_html( $room_label ); ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns" style="margin: 10px 0px; overflow-x: auto;">
|
||||
<table width="100%" data-room="<?php echo esc_attr( $room_key ); ?>">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Anzahl</th>
|
||||
<th>Bezeichnung</th>
|
||||
<th>qbm</th>
|
||||
<th id="thsmall">Montage?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><strong><?php echo esc_html( $room_label ); ?></strong></td>
|
||||
<td> </td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<?php
|
||||
foreach ( $items as $item ) {
|
||||
self::render_furniture_row( $post_array_name, $room_key, $item );
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="room-totals">
|
||||
<th class="room-total-quantity" align="right">0</th>
|
||||
<th align="left">Summe <?php echo esc_html( $room_label ); ?></th>
|
||||
<th colspan="2" class="room-total-cbm" align="right">0,00</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render single furniture row
|
||||
*
|
||||
* @param string $room_name Room post array name
|
||||
* @param string $room_key Room key
|
||||
* @param array $item Furniture item data
|
||||
*/
|
||||
private static function render_furniture_row( $room_name, $room_key, $item ) {
|
||||
$item_name = $item['name'];
|
||||
$cbm = $item['cbm'];
|
||||
$has_montage = $item['montage'];
|
||||
|
||||
// Generate field names matching legacy format
|
||||
$quantity_name = $room_name . '[v' . $item_name . ']';
|
||||
$cbm_name = $room_name . '[q' . $item_name . ']';
|
||||
$montage_name = $room_name . '[m' . $item_name . ']';
|
||||
?>
|
||||
<tr class="furniture-row" data-room="<?php echo esc_attr( $room_key ); ?>" data-cbm="<?php echo esc_attr( $cbm ); ?>" data-item="<?php echo esc_attr( $item_name ); ?>">
|
||||
<td><input type="text" name="<?php echo esc_attr( $quantity_name ); ?>" class="quantity-input" size="2" maxlength="3"></td>
|
||||
<td><?php echo esc_html( $item_name ); ?></td>
|
||||
<td><?php echo esc_html( str_replace( '.', ',', (string) $cbm ) ); ?></td>
|
||||
<input type="hidden" name="<?php echo esc_attr( $cbm_name ); ?>" value="<?php echo esc_attr( $cbm ); ?>">
|
||||
<td>
|
||||
<?php if ( $has_montage ) : ?>
|
||||
<input type="radio" name="<?php echo esc_attr( $montage_name ); ?>" value="ja"><label>Ja</label>
|
||||
<input type="radio" name="<?php echo esc_attr( $montage_name ); ?>" value="nein" checked><label>Nein</label>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render grand totals section
|
||||
*/
|
||||
private static function render_grand_totals() {
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<div class="panel" id="grand-total-section">
|
||||
<h3>Gesamtsumme</h3>
|
||||
<table width="100%">
|
||||
<tr class="grand-totals">
|
||||
<th align="right" id="grand-total-quantity" style="width: 10%;">0</th>
|
||||
<th align="left" style="width: 40%;">Gesamtsumme aller Zimmer</th>
|
||||
<th colspan="2" align="right" id="grand-total-cbm" style="width: 40%;">0,00</th>
|
||||
<th style="width: 10%;"> </th>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render submit section
|
||||
*/
|
||||
private static function render_submit_section() {
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<?php
|
||||
// Render captcha widget if enabled
|
||||
$captcha = Umzugsliste_Captcha::get_instance();
|
||||
if ( $captcha->is_enabled() ) {
|
||||
echo $captcha->render_widget();
|
||||
echo '<div style="margin-bottom: 1rem;"></div>';
|
||||
}
|
||||
?>
|
||||
<?php wp_nonce_field( 'umzugsliste_submit', 'umzugsliste_nonce' ); ?>
|
||||
<input type="hidden" name="umzugsliste_submit" value="1">
|
||||
<button type="submit" class="button">Anfrage absenden</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user