Hotel Raxa - Advanced Booking System Implementation
🏨 Hotel Booking Enhancements: - Implemented Eagle Booking Advanced Pricing add-on - Added Booking.com-style rate management system - Created professional calendar interface for pricing - Integrated deals and discounts functionality 💰 Advanced Pricing Features: - Dynamic pricing models (per room, per person, per adult) - Base rates, adult rates, and child rates management - Length of stay discounts and early bird deals - Mobile rates and secret deals implementation - Seasonal promotions and flash sales 📅 Availability Management: - Real-time availability tracking - Stop sell and restriction controls - Closed to arrival/departure functionality - Minimum/maximum stay requirements - Automatic sold-out management 💳 Payment Integration: - Maintained Redsys payment gateway integration - Seamless integration with existing Eagle Booking - No modifications to core Eagle Booking plugin 🛠️ Technical Implementation: - Custom database tables for advanced pricing - WordPress hooks and filters integration - AJAX-powered admin interface - Data migration from existing Eagle Booking - Professional calendar view for revenue management 📊 Admin Interface: - Booking.com-style management dashboard - Visual rate and availability calendar - Bulk operations for date ranges - Statistics and analytics dashboard - Modal dialogs for quick editing 🔧 Code Quality: - WordPress coding standards compliance - Secure database operations with prepared statements - Proper input validation and sanitization - Error handling and logging - Responsive admin interface 🤖 Generated with Claude Code (https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Modules\AtomicWidgets\Base;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
abstract class Atomic_Control_Base implements JsonSerializable {
|
||||
private string $bind;
|
||||
private $label = null;
|
||||
private $description = null;
|
||||
|
||||
abstract public function get_type(): string;
|
||||
|
||||
abstract public function get_props(): array;
|
||||
|
||||
public static function bind_to( string $prop_name ) {
|
||||
return new static( $prop_name );
|
||||
}
|
||||
|
||||
protected function __construct( string $prop_name ) {
|
||||
$this->bind = $prop_name;
|
||||
}
|
||||
|
||||
public function get_bind() {
|
||||
return $this->bind;
|
||||
}
|
||||
|
||||
public function set_label( string $label ): self {
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function set_description( string $description ): self {
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array {
|
||||
return [
|
||||
'type' => 'control',
|
||||
'value' => [
|
||||
'type' => $this->get_type(),
|
||||
'bind' => $this->get_bind(),
|
||||
'label' => $this->label,
|
||||
'description' => $this->description,
|
||||
'props' => $this->get_props(),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
namespace Elementor\Modules\AtomicWidgets\Base;
|
||||
|
||||
use Elementor\Modules\AtomicWidgets\Controls\Section;
|
||||
use Elementor\Widget_Base;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
abstract class Atomic_Widget_Base extends Widget_Base {
|
||||
protected $version = '0.0';
|
||||
protected $styles = [];
|
||||
|
||||
public function __construct( $data = [], $args = null ) {
|
||||
parent::__construct( $data, $args );
|
||||
|
||||
$this->version = $data['version'] ?? '0.0';
|
||||
$this->styles = $data['styles'] ?? [];
|
||||
}
|
||||
|
||||
public function get_atomic_controls() {
|
||||
$controls = $this->define_atomic_controls();
|
||||
|
||||
return $this->get_valid_controls( $controls );
|
||||
}
|
||||
|
||||
private function get_valid_controls( array $controls ): array {
|
||||
$valid_controls = [];
|
||||
$schema = static::get_props_schema();
|
||||
|
||||
foreach ( $controls as $control ) {
|
||||
if ( $control instanceof Section ) {
|
||||
$cloned_section = clone $control;
|
||||
|
||||
$cloned_section->set_items(
|
||||
$this->get_valid_controls( $control->get_items() )
|
||||
);
|
||||
|
||||
$valid_controls[] = $cloned_section;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! ( $control instanceof Atomic_Control_Base ) ) {
|
||||
$this->safe_throw( 'Control must be an instance of `Atomic_Control_Base`.' );
|
||||
continue;
|
||||
}
|
||||
|
||||
$prop_name = $control->get_bind();
|
||||
|
||||
if ( ! $prop_name ) {
|
||||
$this->safe_throw( 'Control is missing a bound prop from the schema.' );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! array_key_exists( $prop_name, $schema ) ) {
|
||||
$this->safe_throw( "Prop `{$prop_name}` is not defined in the schema of `{$this->get_name()}`. Did you forget to define it?" );
|
||||
continue;
|
||||
}
|
||||
|
||||
$valid_controls[] = $control;
|
||||
}
|
||||
|
||||
return $valid_controls;
|
||||
}
|
||||
|
||||
private function safe_throw( string $message ) {
|
||||
if ( ! defined( 'ELEMENTOR_DEBUG' ) || ! ELEMENTOR_DEBUG ) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new \Exception( $message );
|
||||
}
|
||||
|
||||
abstract protected function define_atomic_controls(): array;
|
||||
|
||||
final public function get_controls( $control_id = null ) {
|
||||
if ( ! empty( $control_id ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
final public function get_initial_config() {
|
||||
$config = parent::get_initial_config();
|
||||
|
||||
$config['atomic_controls'] = $this->get_atomic_controls();
|
||||
$config['version'] = $this->version;
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
final public function get_data_for_save() {
|
||||
$data = parent::get_data_for_save();
|
||||
|
||||
$data['version'] = $this->version;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
final public function get_raw_data( $with_html_content = false ) {
|
||||
$raw_data = parent::get_raw_data( $with_html_content );
|
||||
|
||||
$raw_data['styles'] = $this->styles;
|
||||
|
||||
return $raw_data;
|
||||
}
|
||||
|
||||
final public function get_stack( $with_common_controls = true ) {
|
||||
return [
|
||||
'controls' => [],
|
||||
'tabs' => [],
|
||||
];
|
||||
}
|
||||
|
||||
final public function get_atomic_settings(): array {
|
||||
$schema = static::get_props_schema();
|
||||
$raw_settings = $this->get_settings();
|
||||
|
||||
$transformed_settings = [];
|
||||
|
||||
foreach ( $schema as $key => $prop ) {
|
||||
if ( array_key_exists( $key, $raw_settings ) ) {
|
||||
$transformed_settings[ $key ] = $raw_settings[ $key ];
|
||||
} else {
|
||||
$transformed_settings[ $key ] = $prop->get_default();
|
||||
}
|
||||
|
||||
$transformed_settings[ $key ] = $this->transform_setting( $transformed_settings[ $key ] );
|
||||
}
|
||||
|
||||
return $transformed_settings;
|
||||
}
|
||||
|
||||
public static function get_props_schema() {
|
||||
return static::define_props_schema();
|
||||
}
|
||||
|
||||
private function transform_setting( $setting ) {
|
||||
if ( ! $this->is_transformable( $setting ) ) {
|
||||
return $setting;
|
||||
}
|
||||
|
||||
switch ( $setting['$$type'] ) {
|
||||
case 'classes':
|
||||
return is_array( $setting['value'] )
|
||||
? join( ' ', $setting['value'] )
|
||||
: '';
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function is_transformable( $setting ): bool {
|
||||
return ! empty( $setting['$$type'] ) && 'string' === getType( $setting['$$type'] ) && isset( $setting['value'] );
|
||||
}
|
||||
|
||||
abstract protected static function define_props_schema(): array;
|
||||
}
|
||||
Reference in New Issue
Block a user