feat: add core implementation files for phases 4-6
Add missing implementation files and planning docs: - Phase 04: Shortcode handler and date helpers for form rendering - Phase 05: Planning documentation for volume calculations - Phase 06: Email generator for legacy HTML table format These complete the form rendering, calculation, and email system. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
90
includes/class-date-helpers.php
Normal file
90
includes/class-date-helpers.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* Date Helper Functions
|
||||
*
|
||||
* Provides date dropdown selectors for the moving date field
|
||||
*
|
||||
* @package Umzugsliste
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Date helpers class
|
||||
*/
|
||||
class Umzugsliste_Date_Helpers {
|
||||
|
||||
/**
|
||||
* Render day dropdown
|
||||
*
|
||||
* @param int $selected Selected day (1-31)
|
||||
* @return string HTML for day dropdown
|
||||
*/
|
||||
public static function render_day_select( $selected = null ) {
|
||||
if ( null === $selected ) {
|
||||
$selected = (int) current_time( 'j' );
|
||||
}
|
||||
|
||||
$html = '<div class="small-4 columns"><label>Tag</label><select name="day" class="Stil2">';
|
||||
|
||||
for ( $i = 1; $i <= 31; $i++ ) {
|
||||
$sel = ( $i === $selected ) ? ' selected' : '';
|
||||
$html .= '<option value="' . $i . '"' . $sel . '>' . $i . '</option>';
|
||||
}
|
||||
|
||||
$html .= '</select></div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render month dropdown
|
||||
*
|
||||
* @param int $selected Selected month (1-12)
|
||||
* @return string HTML for month dropdown
|
||||
*/
|
||||
public static function render_month_select( $selected = null ) {
|
||||
if ( null === $selected ) {
|
||||
$selected = (int) current_time( 'n' );
|
||||
}
|
||||
|
||||
$html = '<div class="small-4 columns"><label>Monat</label><select name="month" class="Stil2">';
|
||||
|
||||
for ( $i = 1; $i <= 12; $i++ ) {
|
||||
$sel = ( $i === $selected ) ? ' selected' : '';
|
||||
$html .= '<option value="' . $i . '"' . $sel . '>' . $i . '</option>';
|
||||
}
|
||||
|
||||
$html .= '</select></div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render year dropdown
|
||||
*
|
||||
* @param int $selected Selected year
|
||||
* @return string HTML for year dropdown
|
||||
*/
|
||||
public static function render_year_select( $selected = null ) {
|
||||
if ( null === $selected ) {
|
||||
$selected = (int) current_time( 'Y' );
|
||||
}
|
||||
|
||||
$html = '<div class="small-4 columns"><label>Jahr</label><select name="year" class="Stil2">';
|
||||
|
||||
// Show current year plus 15 years (matching legacy)
|
||||
$current_year = (int) current_time( 'Y' );
|
||||
for ( $i = 0; $i <= 15; $i++ ) {
|
||||
$year = $current_year + $i;
|
||||
$sel = ( $year === $selected ) ? ' selected' : '';
|
||||
$html .= '<option value="' . $year . '"' . $sel . '>' . $year . '</option>';
|
||||
}
|
||||
|
||||
$html .= '</select></div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
313
includes/class-email-generator.php
Normal file
313
includes/class-email-generator.php
Normal file
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
/**
|
||||
* Email Generator
|
||||
*
|
||||
* Generates HTML email matching legacy format exactly
|
||||
*
|
||||
* @package Umzugsliste
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Email generator class
|
||||
*/
|
||||
class Umzugsliste_Email_Generator {
|
||||
|
||||
/**
|
||||
* Generate complete email HTML
|
||||
*
|
||||
* @param array $data Form submission data
|
||||
* @return string Complete HTML email
|
||||
*/
|
||||
public static function generate( $data ) {
|
||||
$content = '';
|
||||
|
||||
// Moving date
|
||||
$content .= self::generate_date_section(
|
||||
$data['day'] ?? '',
|
||||
$data['month'] ?? '',
|
||||
$data['year'] ?? ''
|
||||
);
|
||||
|
||||
// Customer info
|
||||
$content .= self::generate_customer_info_section( $data );
|
||||
|
||||
// All rooms
|
||||
$content .= self::generate_all_rooms( $data );
|
||||
|
||||
// Grand totals
|
||||
$content .= self::generate_grand_totals( $data );
|
||||
|
||||
// Wrap in HTML document
|
||||
return self::wrap_html( $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate moving date section
|
||||
*
|
||||
* @param string $day Day
|
||||
* @param string $month Month
|
||||
* @param string $year Year
|
||||
* @return string HTML
|
||||
*/
|
||||
private static function generate_date_section( $day, $month, $year ) {
|
||||
return "<div class='row'>
|
||||
<div class='large-6 columns'>
|
||||
<fieldset>
|
||||
<legend>Voraussichtlicher Umzugstermin</legend>
|
||||
<p>" . esc_html( $day ) . "." . esc_html( $month ) . "." . esc_html( $year ) . "</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate customer info section
|
||||
*
|
||||
* @param array $data Form data
|
||||
* @return string HTML
|
||||
*/
|
||||
private static function generate_customer_info_section( $data ) {
|
||||
$info = $data['info'] ?? array();
|
||||
|
||||
// Build customer info array matching legacy structure
|
||||
$info_array = array(
|
||||
'bName' => $data['bName'] ?? '',
|
||||
'eName' => $data['eName'] ?? '',
|
||||
'bStraße' => $data['bStrasse'] ?? '',
|
||||
'eStraße' => $data['eStrasse'] ?? '',
|
||||
'bPLZ/Ort' => $data['bort'] ?? '',
|
||||
'ePLZ/Ort' => $data['eort'] ?? '',
|
||||
'bGeschoss' => $info['bGeschoss'] ?? '',
|
||||
'eGeschoss' => $info['eGeschoss'] ?? '',
|
||||
'bLift' => $info['bLift'] ?? 'nein',
|
||||
'eLift' => $info['eLift'] ?? 'nein',
|
||||
'bTelefon' => $data['bTelefon'] ?? '',
|
||||
'eTelefon' => $data['eTelefon'] ?? '',
|
||||
'bTelefax' => $info['bTelefax'] ?? '',
|
||||
'eTelefax' => $info['eTelefax'] ?? '',
|
||||
'bMobil' => $info['bMobil'] ?? '',
|
||||
'eMobil' => $info['eMobil'] ?? '',
|
||||
'eE-Mail' => $info['eE-Mail'] ?? '',
|
||||
);
|
||||
|
||||
$html = "<div class='row'>
|
||||
<div class='large-12 columns' style='margin: 10px 0px; overflow-x: auto;'>
|
||||
<table width='100%'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th align='left' bgcolor='#CCCCCC' colspan='2'>Beladeadresse</th>
|
||||
<th align='left' bgcolor='#CCCCCC' colspan='2'>Entladeadresse</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>";
|
||||
|
||||
// Alternate between Belade and Entlade columns
|
||||
$zumbruch = 'Nein';
|
||||
foreach ( $info_array as $key => $value ) {
|
||||
// Remove prefix (b or e) for label
|
||||
$label = substr( $key, 1 );
|
||||
$html .= '<td>' . $label . '</td>';
|
||||
$html .= '<td>' . esc_html( $value ) . '</td>';
|
||||
|
||||
if ( 'Ja' === $zumbruch ) {
|
||||
$html .= '</tr><tr>';
|
||||
$zumbruch = 'Nein';
|
||||
} else {
|
||||
$zumbruch = 'Ja';
|
||||
}
|
||||
}
|
||||
|
||||
$html .= '</tr></tbody></table></div></div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate all room sections
|
||||
*
|
||||
* @param array $data Form data
|
||||
* @return string HTML
|
||||
*/
|
||||
private static function generate_all_rooms( $data ) {
|
||||
$html = '';
|
||||
$rooms = Umzugsliste_Furniture_Data::get_rooms();
|
||||
|
||||
foreach ( $rooms as $room_key => $room_label ) {
|
||||
// Get post array name for this room
|
||||
$post_array_name = ucfirst( $room_key );
|
||||
if ( 'kueche_esszimmer' === $room_key ) {
|
||||
$post_array_name = 'Kueche_Esszimmer';
|
||||
}
|
||||
|
||||
// Get room data from submission
|
||||
$room_data = $data[ $post_array_name ] ?? array();
|
||||
|
||||
// Only include room if it has items with quantities
|
||||
if ( self::has_items_with_quantities( $room_data ) ) {
|
||||
$html .= self::generate_room_section( $room_key, $room_label, $room_data );
|
||||
}
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if room has any items with quantities
|
||||
*
|
||||
* @param array $room_data Room submission data
|
||||
* @return bool True if has items
|
||||
*/
|
||||
private static function has_items_with_quantities( $room_data ) {
|
||||
foreach ( $room_data as $key => $value ) {
|
||||
if ( substr( $key, 0, 1 ) === 'v' && ! empty( $value ) && floatval( $value ) > 0 ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate single room section
|
||||
*
|
||||
* @param string $room_key Room key
|
||||
* @param string $room_label Room label
|
||||
* @param array $room_data Room submission data
|
||||
* @return string HTML
|
||||
*/
|
||||
private static function generate_room_section( $room_key, $room_label, $room_data ) {
|
||||
$html = "<div class='row'>
|
||||
<div class='large-12 columns' style='margin: 10px 0px; overflow-x: auto;'>
|
||||
<table width='100%'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th align='left' bgcolor='#CCCCCC' width='54'>Anzahl</th>
|
||||
<th align='left' bgcolor='#CCCCCC'>Bezeichnung</th>
|
||||
<th align='right' bgcolor='#CCCCCC'>qbm</th>
|
||||
<th align='right' bgcolor='#CCCCCC'>Gesamt</th>
|
||||
<th align='left' bgcolor='#CCCCCC'>Montage?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><strong>" . esc_html( $room_label ) . "</strong></td>
|
||||
<td> </td>
|
||||
<td> </td>
|
||||
<td> </td>
|
||||
</tr>";
|
||||
|
||||
// Generate rows for each furniture item
|
||||
$room_total_quantity = 0;
|
||||
$room_total_cbm = 0;
|
||||
|
||||
// Process items in groups of v, q, m
|
||||
$processed_items = array();
|
||||
|
||||
foreach ( $room_data as $key => $value ) {
|
||||
if ( substr( $key, 0, 1 ) === 'v' ) {
|
||||
$item_name = substr( $key, 1 );
|
||||
if ( ! empty( $value ) && floatval( $value ) > 0 ) {
|
||||
$quantity = floatval( str_replace( ',', '.', trim( $value ) ) );
|
||||
$cbm = isset( $room_data[ 'q' . $item_name ] ) ? floatval( $room_data[ 'q' . $item_name ] ) : 0;
|
||||
$montage = isset( $room_data[ 'm' . $item_name ] ) ? $room_data[ 'm' . $item_name ] : 'nein';
|
||||
|
||||
$item_total = $quantity * $cbm;
|
||||
$room_total_quantity += $quantity;
|
||||
$room_total_cbm += $item_total;
|
||||
|
||||
// Format for display
|
||||
$cbm_display = str_replace( '.', ',', number_format( $cbm, 2, '.', '' ) );
|
||||
$total_display = str_replace( '.', ',', number_format( $item_total, 2, '.', '' ) );
|
||||
|
||||
$html .= '<tr>';
|
||||
$html .= '<td>' . esc_html( $value ) . '</td>';
|
||||
$html .= '<td>' . esc_html( $item_name ) . '</td>';
|
||||
$html .= "<td align='right'>" . esc_html( $cbm_display ) . '</td>';
|
||||
$html .= "<td align='right'>" . esc_html( $total_display ) . '</td>';
|
||||
$html .= '<td> ' . esc_html( $montage ) . '</td>';
|
||||
$html .= '</tr>';
|
||||
|
||||
$processed_items[] = $item_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Room totals
|
||||
$room_total_display = str_replace( '.', ',', number_format( $room_total_cbm, 2, '.', '' ) );
|
||||
|
||||
$html .= "<tr>
|
||||
<th bgcolor='CCCCCC' align='right'>" . $room_total_quantity . "</th>
|
||||
<th bgcolor='CCCCCC' align='left'>Summe " . esc_html( $room_label ) . "</th>
|
||||
<th bgcolor='CCCCCC' colspan='2' align='right'>" . esc_html( $room_total_display ) . "</th>
|
||||
<th bgcolor='CCCCCC'> </th>
|
||||
</tr>";
|
||||
|
||||
$html .= '</tbody></table></div></div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate grand totals section
|
||||
*
|
||||
* @param array $data Form data
|
||||
* @return string HTML
|
||||
*/
|
||||
private static function generate_grand_totals( $data ) {
|
||||
$grand_total_quantity = 0;
|
||||
$grand_total_cbm = 0;
|
||||
|
||||
$rooms = Umzugsliste_Furniture_Data::get_rooms();
|
||||
|
||||
foreach ( $rooms as $room_key => $room_label ) {
|
||||
$post_array_name = ucfirst( $room_key );
|
||||
if ( 'kueche_esszimmer' === $room_key ) {
|
||||
$post_array_name = 'Kueche_Esszimmer';
|
||||
}
|
||||
|
||||
$room_data = $data[ $post_array_name ] ?? array();
|
||||
|
||||
foreach ( $room_data as $key => $value ) {
|
||||
if ( substr( $key, 0, 1 ) === 'v' && ! empty( $value ) && floatval( $value ) > 0 ) {
|
||||
$item_name = substr( $key, 1 );
|
||||
$quantity = floatval( str_replace( ',', '.', trim( $value ) ) );
|
||||
$cbm = isset( $room_data[ 'q' . $item_name ] ) ? floatval( $room_data[ 'q' . $item_name ] ) : 0;
|
||||
|
||||
$grand_total_quantity += $quantity;
|
||||
$grand_total_cbm += ( $quantity * $cbm );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$grand_total_display = str_replace( '.', ',', number_format( $grand_total_cbm, 2, '.', '' ) );
|
||||
|
||||
return "<tr><th> </th></tr>
|
||||
<tr>
|
||||
<th bgcolor='CCCCCC' align='right'>" . $grand_total_quantity . "</th>
|
||||
<th bgcolor='CCCCCC' align='left'>Gesamtsummen</th>
|
||||
<th bgcolor='CCCCCC' colspan='2' align='right'>" . esc_html( $grand_total_display ) . "</th>
|
||||
<th bgcolor='CCCCCC'> </th>
|
||||
</tr></tbody></table></div></div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap content in HTML document structure
|
||||
*
|
||||
* @param string $content Email content
|
||||
* @return string Complete HTML document
|
||||
*/
|
||||
private static function wrap_html( $content ) {
|
||||
return "<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.0 Transitional//EN'>
|
||||
<html>
|
||||
<head>
|
||||
<title>Siegel Umzüge - Internetanfrage</title>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
</head>
|
||||
<body>" . $content . "</body>
|
||||
</html>";
|
||||
}
|
||||
}
|
||||
84
includes/class-shortcode.php
Normal file
84
includes/class-shortcode.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* Shortcode Handler
|
||||
*
|
||||
* Registers and handles the [umzugsliste] shortcode
|
||||
*
|
||||
* @package Umzugsliste
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcode class
|
||||
*/
|
||||
class Umzugsliste_Shortcode {
|
||||
|
||||
/**
|
||||
* Instance
|
||||
*
|
||||
* @var Umzugsliste_Shortcode
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return Umzugsliste_Shortcode
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
add_shortcode( 'umzugsliste', array( $this, 'render_form' ) );
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render form shortcode
|
||||
*
|
||||
* @param array $atts Shortcode attributes
|
||||
* @return string Form HTML
|
||||
*/
|
||||
public function render_form( $atts ) {
|
||||
// Ensure assets are enqueued
|
||||
$this->enqueue_assets();
|
||||
|
||||
// Render the form
|
||||
return Umzugsliste_Form_Renderer::render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue CSS and JS assets
|
||||
*/
|
||||
public function enqueue_assets() {
|
||||
$plugin_url = plugin_dir_url( dirname( __FILE__ ) );
|
||||
$plugin_version = '1.0.0';
|
||||
|
||||
// Enqueue form CSS
|
||||
wp_enqueue_style(
|
||||
'umzugsliste-form',
|
||||
$plugin_url . 'assets/css/form.css',
|
||||
array(),
|
||||
$plugin_version
|
||||
);
|
||||
|
||||
// Enqueue form JS (placeholder for Phase 5)
|
||||
wp_enqueue_script(
|
||||
'umzugsliste-form',
|
||||
$plugin_url . 'assets/js/form.js',
|
||||
array( 'jquery' ),
|
||||
$plugin_version,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user