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:
Hotel Raxa Dev
2025-07-11 07:43:22 +02:00
commit 5b1e2453c7
9816 changed files with 2784509 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
<?php
/**
* Redux JS Button Extension Class
*
* @package Redux
* @author Kevin Provance <kevin.provance@gmail.com>
* @class Redux_Extension_Js_Button
* @version 4.3.16
*/
defined( 'ABSPATH' ) || exit;
if ( ! class_exists( 'Redux_Extension_Js_Button' ) ) {
/**
* Main Redux_Extension_Js_Button extension class
*
* @since 1.0.0
*/
class Redux_Extension_Js_Button extends Redux_Extension_Abstract {
/**
* Extension version.
*
* @var string
*/
public static $version = '4.3.16';
/**
* Extension friendly name.
*
* @var string
*/
public $extension_name = 'JS Button';
/**
* Class Constructor. Defines the args for the extensions class
*
* @since 1.0.0
* @access public
*
* @param ReduxFramework $redux Parent settings.
*
* @return void
*/
public function __construct( $redux ) {
parent::__construct( $redux, __FILE__ );
$this->add_field( 'js_button' );
}
}
}

View File

@@ -0,0 +1,8 @@
<?php
/**
* Silence is golden.
*
* @package Redux Framework
*/
echo null;

View File

@@ -0,0 +1,136 @@
<?php
/**
* Redux JS Button Field Class
*
* @package Redux Extentions
* @author Kevin Provance <kevin.provance@gmail.com>
* @class Redux_Js_Button
*/
defined( 'ABSPATH' ) || exit;
if ( ! class_exists( 'Redux_Js_Button' ) ) {
/**
* Main ReduxFramework_Js_Button class
*
* @since 1.0.0
*/
class Redux_Js_Button extends Redux_Field {
/**
* Field Render Function.
* Takes the vars and outputs the HTML for the field in the settings
*
* @since 1.0.0
* @access public
* @return void
*/
public function render() {
$field_id = $this->field['id'];
// primary container.
echo '<div
class="redux-js-button-container ' . esc_attr( $this->field['class'] ) . '"
id="' . esc_attr( $field_id ) . '_container"
data-id="' . esc_attr( $field_id ) . '"
style="width: 0px;"
>';
// Button render.
if ( isset( $this->field['buttons'] ) && is_array( $this->field['buttons'] ) ) {
echo '<div
class="redux-js-button-button-container"
id="redux-js-button-button-container"
style="display: inline-flex;"
>';
foreach ( $this->field['buttons'] as $idx => $arr ) {
$button_text = $arr['text'];
$button_class = $arr['class'];
$button_func = $arr['function'];
echo '<input
id="' . esc_attr( $field_id ) . '_input-' . intval( $idx ) . '"
class="hide-if-no-js button ' . esc_attr( $button_class ) . '"
type="button"
data-function="' . esc_attr( $button_func ) . '"
value="' . esc_attr( $button_text ) . '"
/>&nbsp;&nbsp;';
}
echo '</div>';
}
// Close container.
echo '</div>';
}
/**
* Do enqueue for every field instance.
*
* @return void
*/
public function always_enqueue() {
// Make sure script data exists first.
if ( isset( $this->field['script'] ) && ! empty( $this->field['script'] ) ) {
// URI location of script to enqueue.
$script_url = $this->field['script']['url'] ?? '';
// Get deps, if any.
$script_dep = $this->field['script']['dep'] ?? array();
// Get ver, if any.
$script_ver = $this->field['script']['ver'] ?? time();
// Script location in HTML.
$script_footer = $this->field['script']['in_footer'] ?? true;
// If a script exists, enqueue it.
if ( '' !== $script_url ) {
wp_enqueue_script(
'redux-js-button-' . $this->field['id'],
$script_url,
$script_dep,
$script_ver,
$script_footer
);
}
if ( isset( $this->field['enqueue_ajax'] ) && $this->field['enqueue_ajax'] ) {
wp_localize_script(
'redux-js-button-' . $this->field['id'],
'redux_ajax_script',
array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) )
);
}
}
}
/**
* Enqueue Function.
* If this field requires any scripts, or css define this function and register/enqueue the scripts/css
*
* @since 1.0.0
* @access public
* @return void
*/
public function enqueue() {
// Set up min files for dev_mode = false.
$min = Redux_Functions::isMin();
// Field dependent JS.
wp_enqueue_script(
'redux-field-js-button',
// phpcs:ignore WordPress.NamingConventions.ValidHookName
apply_filters( "redux/js_button/{$this->parent->args['opt_name']}/enqueue/redux-field-js-button-js", $this->url . 'redux-js-button' . $min . '.js' ),
array( 'jquery' ),
Redux_Extension_Js_Button::$version,
true
);
}
}
}

View File

@@ -0,0 +1,8 @@
<?php
/**
* Silence is golden.
*
* @package Redux Framework
*/
echo null;

View File

@@ -0,0 +1,92 @@
/* global redux, jQuery */
/**
* JS Button library
*
* @author Kevin Provance (kprovance)
*/
( function( $ ) {
'use strict';
redux.field_objects = redux.field_objects || {};
redux.field_objects.js_button = redux.field_objects.js_button || {};
redux.field_objects.js_button.mainID = '';
/*******************************************************************************
* Runs when library is loaded.
******************************************************************************/
redux.field_objects.js_button.init = function( selector ) {
// If no selector is passed, grab one from the HTML.
if ( ! selector ) {
selector = $( document ).find( '.redux-container-js_button' );
}
// Enum instances of our object.
$( selector ).each(
function() {
var button;
var el = $( this );
var parent = el;
if ( ! el.hasClass( 'redux-field-container' ) ) {
parent = el.parents( '.redux-field-container:first' );
}
if ( parent.hasClass( 'redux-field-init' ) ) {
parent.removeClass( 'redux-field-init' );
} else {
return;
}
// Do module level init.
redux.field_objects.js_button.modInit( el );
// Get the button handle.
button = $( el ).find( 'input' );
$.each(
button,
function( key, value ) {
key = null;
$( this ).on(
'click',
function( e ) {
var funcName = $( value ).data( 'function' );
// Not really needed, but just in case.
e.preventDefault();
if ( '' !== funcName ) {
// Ensure custom function exists.
if ( 'function' === typeof ( window[funcName] ) ) {
// Add it to the window object and execute.
window[funcName]();
} else {
// Let the dev know he fucked up someplace.
throw( 'JS Button Error. Function ' + funcName + ' does not exist.' );
}
}
}
);
}
);
}
);
};
/*******************************************************************************
* Module level init
******************************************************************************/
redux.field_objects.js_button.modInit = function( el ) {
// ID of the fieldset.
redux.field_objects.js_button.mainID = el.attr( 'data-id' );
};
} )( jQuery );

View File

@@ -0,0 +1 @@
!function(i){"use strict";redux.field_objects=redux.field_objects||{},redux.field_objects.js_button=redux.field_objects.js_button||{},redux.field_objects.js_button.mainID="",redux.field_objects.js_button.init=function(t){t=t||i(document).find(".redux-container-js_button"),i(t).each(function(){var t=i(this),e=t;(e=t.hasClass("redux-field-container")?e:t.parents(".redux-field-container:first")).hasClass("redux-field-init")&&(e.removeClass("redux-field-init"),redux.field_objects.js_button.modInit(t),e=i(t).find("input"),i.each(e,function(t,n){i(this).on("click",function(t){var e=i(n).data("function");if(t.preventDefault(),""!==e){if("function"!=typeof window[e])throw"JS Button Error. Function "+e+" does not exist.";window[e]()}})}))})},redux.field_objects.js_button.modInit=function(t){redux.field_objects.js_button.mainID=t.attr("data-id")}}(jQuery);