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,138 @@
<?php
/**
* Multi Text Field.
*
* @package ReduxFramework/Fields
* @author Dovy Paukstys & Kevin Provance (kprovance)
* @version 4.0.0
*/
defined( 'ABSPATH' ) || exit;
// Don't duplicate me!
if ( ! class_exists( 'Redux_Multi_Text', false ) ) {
/**
* Main Redux_multi_text class
*
* @since 1.0.0
*/
class Redux_Multi_Text extends Redux_Field {
/**
* Set field defaults.
*/
public function set_defaults() {
$defaults = array(
'show_empty' => true,
'add_text' => esc_html__( 'Add More', 'redux-framework' ),
);
$this->field = wp_parse_args( $this->field, $defaults );
}
/**
* 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() {
echo '<ul id="' . esc_attr( $this->field['id'] ) . '-ul" class="redux-multi-text ' . esc_attr( $this->field['class'] ) . '">';
if ( isset( $this->value ) && is_array( $this->value ) ) {
foreach ( $this->value as $k => $value ) {
if ( '' !== $value || ( true === $this->field['show_empty'] ) ) {
echo '<li>';
echo '<input
type="text"
id="' . esc_attr( $this->field['id'] . '-' . $k ) . '"
name="' . esc_attr( $this->field['name'] . $this->field['name_suffix'] ) . '[]"
value="' . esc_attr( $value ) . '"
class="regular-text" /> ';
echo '<a
data-id="' . esc_attr( $this->field['id'] ) . '-ul"
href="javascript:void(0);"
class="deletion redux-multi-text-remove">' .
esc_html__( 'Remove', 'redux-framework' ) . '</a>';
echo '</li>';
}
}
} elseif ( true === $this->field['show_empty'] ) {
echo '<li>';
echo '<input
type="text"
id="' . esc_attr( $this->field['id'] . '-0' ) . '"
name="' . esc_attr( $this->field['name'] . $this->field['name_suffix'] ) . '[]"
value=""
class="regular-text" /> ';
echo '<a
data-id="' . esc_attr( $this->field['id'] ) . '-ul"
href="javascript:void(0);"
class="deletion redux-multi-text-remove">' .
esc_html__( 'Remove', 'redux-framework' ) . '</a>';
echo '</li>';
}
$the_name = '';
if ( isset( $this->value ) && empty( $this->value ) && false === $this->field['show_empty'] ) {
$the_name = $this->field['name'] . $this->field['name_suffix'];
}
echo '<li style="display:none;">';
echo '<input
type="text"
id="' . esc_attr( $this->field['id'] ) . '"
name="' . esc_attr( $the_name ) . '"
value=""
class="regular-text" /> ';
echo '<a
data-id="' . esc_attr( $this->field['id'] ) . '-ul"
href="javascript:void(0);"
class="deletion redux-multi-text-remove">' .
esc_html__( 'Remove', 'redux-framework' ) . '</a>';
echo '</li>';
echo '</ul>';
echo '<span style="clear:both;display:block;height:0;"></span>';
$this->field['add_number'] = ( isset( $this->field['add_number'] ) && is_numeric( $this->field['add_number'] ) ) ? $this->field['add_number'] : 1;
echo '<a href="javascript:void(0);" class="button button-primary redux-multi-text-add" data-add_number="' . esc_attr( $this->field['add_number'] ) . '" data-id="' . esc_attr( $this->field['id'] ) . '-ul" data-name="' . esc_attr( $this->field['name'] . $this->field['name_suffix'] ) . '">' . esc_html( $this->field['add_text'] ) . '</a><br/>';
}
/**
* 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() {
wp_enqueue_script(
'redux-field-multi-text',
Redux_Core::$url . 'inc/fields/multi_text/redux-multi-text' . Redux_Functions::is_min() . '.js',
array( 'jquery', 'redux-js' ),
$this->timestamp,
true
);
if ( $this->parent->args['dev_mode'] ) {
wp_enqueue_style(
'redux-field-multi-text',
Redux_Core::$url . 'inc/fields/multi_text/redux-multi-text.css',
array(),
$this->timestamp
);
}
}
}
}
class_alias( 'Redux_Multi_Text', 'ReduxFramework_Multi_Text' );

View File

@@ -0,0 +1,8 @@
<?php
/**
* Silence is golden.
*
* @package Redux Framework
*/
_deprecated_file( 'field_multi_text.php', '4.3', 'class-redux-multi-text.php', 'This file has been renamed and is no longer used in Redux 4. Please change any references to it as it will be removed in future versions of Redux.' );

View File

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

View File

@@ -0,0 +1,20 @@
.redux-container-multi_text ul.redux-multi-text { margin: 0; padding: 0; }
.redux-container-multi_text .redux-multi-text-add { clear: both; margin: 5px 0; }
.redux-container-multi_text a.redux-multi-text-remove.deletion { color: #f00; padding: 2px 4px; margin-left: 5px; }
.redux-container-multi_text a.redux-multi-text-remove.deletion:hover { background: #ff0; color: #fff; text-decoration: none; }
@media screen and (max-width: 782px) { .redux-container-multi_text input { clear: both; }
.redux-container-multi_text .redux-multi-text-remove { margin: 0; float: right; } }
.wp-customizer .redux-container-multi_text .button { float: right; }
.wp-customizer .redux-container-multi_text .redux-multi-text-remove { float: right; margin-bottom: 5px; }
.wp-customizer .redux-container-multi_text ul.redux-multi-text input { width: 100% !important; }
/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVkdXgtbXVsdGktdGV4dC5jc3MiLCJzb3VyY2VzIjpbInJlZHV4LW11bHRpLXRleHQuc2NzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxBQUNJLDJCQUR1QixDQUN2QixFQUFFLEFBQUEsaUJBQWlCLENBQUMsRUFDaEIsTUFBTSxFQUFFLENBQUMsRUFDVCxPQUFPLEVBQUUsQ0FBQyxHQUNiOztBQUpMLEFBTUksMkJBTnVCLENBTXZCLHFCQUFxQixDQUFDLEVBQ2xCLEtBQUssRUFBRSxJQUFJLEVBQ1gsTUFBTSxFQUFFLEtBQUssR0FDaEI7O0FBVEwsQUFZUSwyQkFabUIsQ0FXdkIsQ0FBQyxBQUFBLHdCQUF3QixBQUNwQixTQUFTLENBQUMsRUFDUCxLQUFLLEVBQUUsSUFBSSxFQUNYLE9BQU8sRUFBRSxPQUFPLEVBQ2hCLFdBQVcsRUFBRSxHQUFHLEdBT25COztBQXRCVCxBQWlCWSwyQkFqQmUsQ0FXdkIsQ0FBQyxBQUFBLHdCQUF3QixBQUNwQixTQUFTLEFBS0wsTUFBTSxDQUFDLEVBQ0osVUFBVSxFQUFFLElBQUksRUFDaEIsS0FBSyxFQUFFLElBQUksRUFDWCxlQUFlLEVBQUUsSUFBSSxHQUN4Qjs7QUFLYixNQUFNLENBQUMsTUFBTSxNQUFNLFNBQVMsRUFBRSxLQUFLLElBQy9CLEFBQ0ksMkJBRHVCLENBQ3ZCLEtBQUssQ0FBQyxFQUNGLEtBQUssRUFBRSxJQUFJLEdBQ2Q7Q0FITCxBQUtJLDJCQUx1QixDQUt2Qix3QkFBd0IsQ0FBQyxFQUNyQixNQUFNLEVBQUUsQ0FBQyxFQUNULEtBQUssRUFBRSxLQUFLLEdBQ2Y7O0FBSVQsQUFFUSxjQUZNLENBQ1YsMkJBQTJCLENBQ3ZCLE9BQU8sQ0FBQyxFQUNKLEtBQUssRUFBRSxLQUFLLEdBQ2Y7O0FBSlQsQUFLUSxjQUxNLENBQ1YsMkJBQTJCLENBSXZCLHdCQUF3QixDQUFDLEVBQ3JCLEtBQUssRUFBRSxLQUFLLEVBQ1osYUFBYSxFQUFFLEdBQUcsR0FDckI7O0FBUlQsQUFTUSxjQVRNLENBQ1YsMkJBQTJCLENBUXZCLEVBQUUsQUFBQSxpQkFBaUIsQ0FBQyxLQUFLLENBQUMsRUFDdEIsS0FBSyxFQUFFLGVBQWUsR0FDekIifQ== */
/*# sourceMappingURL=redux-multi-text.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["redux-multi-text.scss","redux-multi-text.css"],"names":[],"mappings":"AAAA,kDAAA,SAAA,ECGQ,UAAU,EAAA;;ADHlB,oDCOQ,WAAW,EDmBZ,aAAY,EAAA;;AA1BnB,iEAuCA,WACI,EC1BQ,gBAAgB,EDyB5B,gBACI,EAAA;;AAxCJ,uECkBgB,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EAAA;;AAMrC,uCACI,oCAEQ,WAAW,EAAA;CAFnB,uDAMQ,SAAS,EACT,YAAY,EAAA,EACf;;AAIT,qDAGY,YAAY,EAAA;;AAHxB,sEAMY,YAAY,EACZ,kBAAkB,EAAA;;AAP9B,uEAUY,sBAAsB,EAAA;;AAhClC,q1CAAq1C","file":"redux-multi-text.css","sourcesContent":[".redux-container-multi_text {\r\n ul.redux-multi-text {\r\n margin: 0;\r\n padding: 0;\r\n }\r\n\r\n .redux-multi-text-add {\r\n clear: both;\r\n margin: 5px 0;\r\n }\r\n\r\n a.redux-multi-text-remove {\r\n &.deletion {\r\n color: #f00;\r\n padding: 2px 4px;\r\n margin-left: 5px;\r\n\r\n &:hover {\r\n background: #ff0;\r\n color: #fff;\r\n text-decoration: none;\r\n }\r\n }\r\n }\r\n}\r\n\r\n@media screen and (max-width: 782px) {\r\n .redux-container-multi_text {\r\n input {\r\n clear: both;\r\n }\r\n\r\n .redux-multi-text-remove {\r\n margin: 0;\r\n float: right;\r\n }\r\n }\r\n}\r\n\r\n.wp-customizer {\r\n .redux-container-multi_text {\r\n .button {\r\n float: right;\r\n }\r\n .redux-multi-text-remove {\r\n float: right;\r\n margin-bottom: 5px;\r\n }\r\n ul.redux-multi-text input {\r\n width: 100% !important;\r\n }\r\n }\r\n}\r\n",".redux-container-multi_text ul.redux-multi-text { margin: 0; padding: 0; }\n\n.redux-container-multi_text .redux-multi-text-add { clear: both; margin: 5px 0; }\n\n.redux-container-multi_text a.redux-multi-text-remove.deletion { color: #f00; padding: 2px 4px; margin-left: 5px; }\n\n.redux-container-multi_text a.redux-multi-text-remove.deletion:hover { background: #ff0; color: #fff; text-decoration: none; }\n\n@media screen and (max-width: 782px) { .redux-container-multi_text input { clear: both; }\n\t.redux-container-multi_text .redux-multi-text-remove { margin: 0; float: right; } }\n\n.wp-customizer .redux-container-multi_text .button { float: right; }\n\n.wp-customizer .redux-container-multi_text .redux-multi-text-remove { float: right; margin-bottom: 5px; }\n\n.wp-customizer .redux-container-multi_text ul.redux-multi-text input { width: 100% !important; }\n\n/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVkdXgtbXVsdGktdGV4dC5jc3MiLCJzb3VyY2VzIjpbInJlZHV4LW11bHRpLXRleHQuc2NzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxBQUNJLDJCQUR1QixDQUN2QixFQUFFLEFBQUEsaUJBQWlCLENBQUMsRUFDaEIsTUFBTSxFQUFFLENBQUMsRUFDVCxPQUFPLEVBQUUsQ0FBQyxHQUNiOztBQUpMLEFBTUksMkJBTnVCLENBTXZCLHFCQUFxQixDQUFDLEVBQ2xCLEtBQUssRUFBRSxJQUFJLEVBQ1gsTUFBTSxFQUFFLEtBQUssR0FDaEI7O0FBVEwsQUFZUSwyQkFabUIsQ0FXdkIsQ0FBQyxBQUFBLHdCQUF3QixBQUNwQixTQUFTLENBQUMsRUFDUCxLQUFLLEVBQUUsSUFBSSxFQUNYLE9BQU8sRUFBRSxPQUFPLEVBQ2hCLFdBQVcsRUFBRSxHQUFHLEdBT25COztBQXRCVCxBQWlCWSwyQkFqQmUsQ0FXdkIsQ0FBQyxBQUFBLHdCQUF3QixBQUNwQixTQUFTLEFBS0wsTUFBTSxDQUFDLEVBQ0osVUFBVSxFQUFFLElBQUksRUFDaEIsS0FBSyxFQUFFLElBQUksRUFDWCxlQUFlLEVBQUUsSUFBSSxHQUN4Qjs7QUFLYixNQUFNLENBQUMsTUFBTSxNQUFNLFNBQVMsRUFBRSxLQUFLLElBQy9CLEFBQ0ksMkJBRHVCLENBQ3ZCLEtBQUssQ0FBQyxFQUNGLEtBQUssRUFBRSxJQUFJLEdBQ2Q7Q0FITCxBQUtJLDJCQUx1QixDQUt2Qix3QkFBd0IsQ0FBQyxFQUNyQixNQUFNLEVBQUUsQ0FBQyxFQUNULEtBQUssRUFBRSxLQUFLLEdBQ2Y7O0FBSVQsQUFFUSxjQUZNLENBQ1YsMkJBQTJCLENBQ3ZCLE9BQU8sQ0FBQyxFQUNKLEtBQUssRUFBRSxLQUFLLEdBQ2Y7O0FBSlQsQUFLUSxjQUxNLENBQ1YsMkJBQTJCLENBSXZCLHdCQUF3QixDQUFDLEVBQ3JCLEtBQUssRUFBRSxLQUFLLEVBQ1osYUFBYSxFQUFFLEdBQUcsR0FDckI7O0FBUlQsQUFTUSxjQVRNLENBQ1YsMkJBQTJCLENBUXZCLEVBQUUsQUFBQSxpQkFBaUIsQ0FBQyxLQUFLLENBQUMsRUFDdEIsS0FBSyxFQUFFLGVBQWUsR0FDekIifQ== */\n\n/*# sourceMappingURL=redux-multi-text.css.map */\n"]}

View File

@@ -0,0 +1,108 @@
/*global redux_change, redux*/
(function( $ ) {
'use strict';
redux.field_objects = redux.field_objects || {};
redux.field_objects.multi_text = redux.field_objects.multi_text || {};
redux.field_objects.multi_text.remove = function( el ) {
el.find( '.redux-multi-text-remove' ).on(
'click',
function() {
var id;
var lis;
var add;
var name;
redux_change( $( this ) );
$( this ).prev( 'input[type="text"]' ).val( '' );
id = $( this ).attr( 'data-id' );
$( this ).parent().slideUp(
'medium',
function() {
$( this ).remove();
lis = el.find( '#' + id + ' li' ).length;
if ( 1 === lis ) {
add = el.find( '.redux-multi-text-add' );
name = add.attr( 'data-name' );
el.find( '#' + id + ' li:last-child input[type="text"]' ).attr( 'name', name );
}
}
);
}
);
};
redux.field_objects.multi_text.init = function( selector ) {
selector = $.redux.getSelector( selector, 'multi_text' );
$( selector ).each(
function() {
var el = $( this );
var parent = el;
if ( ! el.hasClass( 'redux-field-container' ) ) {
parent = el.parents( '.redux-field-container:first' );
}
if ( parent.is( ':hidden' ) ) {
return;
}
if ( parent.hasClass( 'redux-field-init' ) ) {
parent.removeClass( 'redux-field-init' );
} else {
return;
}
redux.field_objects.multi_text.remove( el );
el.find( '.redux-multi-text-add' ).on(
'click',
function() {
var i;
var lis;
var css;
var input;
var new_input;
var number = parseInt( $( this ).attr( 'data-add_number' ) );
var id = $( this ).attr( 'data-id' );
var name = $( this ).attr( 'data-name' ) + '[]';
for ( i = 0; i < number; i += 1 ) {
new_input = $( '#' + id + ' li:last-child' ).clone();
el.find( '#' + id ).append( new_input );
el.find( '#' + id + ' li:last-child' ).removeAttr( 'style' );
el.find( '#' + id + ' li:last-child input[type="text"]' ).val( '' );
el.find( '#' + id + ' li:last-child input[type="text"]' ).attr( 'name', name );
}
lis = el.find( '#' + id + ' li' ).length;
if ( lis > 1 ) {
el.find( '#' + id + ' li' ).each(
function() {
css = $( this ).css( 'display' );
if ( 'none' === css ) {
input = $( this ).find( 'input[type="text"]' );
input.attr( 'name', '' );
}
}
);
}
redux.field_objects.multi_text.remove( el );
}
);
}
);
};
})( jQuery );

View File

@@ -0,0 +1 @@
!function(a){"use strict";redux.field_objects=redux.field_objects||{},redux.field_objects.multi_text=redux.field_objects.multi_text||{},redux.field_objects.multi_text.remove=function(i){i.find(".redux-multi-text-remove").on("click",function(){var t,e;redux_change(a(this)),a(this).prev('input[type="text"]').val(""),t=a(this).attr("data-id"),a(this).parent().slideUp("medium",function(){a(this).remove(),1===i.find("#"+t+" li").length&&(e=i.find(".redux-multi-text-add"),e=e.attr("data-name"),i.find("#"+t+' li:last-child input[type="text"]').attr("name",e))})})},redux.field_objects.multi_text.init=function(t){t=a.redux.getSelector(t,"multi_text"),a(t).each(function(){var l=a(this),t=l;(t=l.hasClass("redux-field-container")?t:l.parents(".redux-field-container:first")).is(":hidden")||t.hasClass("redux-field-init")&&(t.removeClass("redux-field-init"),redux.field_objects.multi_text.remove(l),l.find(".redux-multi-text-add").on("click",function(){for(var t,e=parseInt(a(this).attr("data-add_number")),i=a(this).attr("data-id"),d=a(this).attr("data-name")+"[]",n=0;n<e;n+=1)t=a("#"+i+" li:last-child").clone(),l.find("#"+i).append(t),l.find("#"+i+" li:last-child").removeAttr("style"),l.find("#"+i+' li:last-child input[type="text"]').val(""),l.find("#"+i+' li:last-child input[type="text"]').attr("name",d);1<l.find("#"+i+" li").length&&l.find("#"+i+" li").each(function(){"none"===a(this).css("display")&&a(this).find('input[type="text"]').attr("name","")}),redux.field_objects.multi_text.remove(l)}))})}}(jQuery);

View File

@@ -0,0 +1,53 @@
.redux-container-multi_text {
ul.redux-multi-text {
margin: 0;
padding: 0;
}
.redux-multi-text-add {
clear: both;
margin: 5px 0;
}
a.redux-multi-text-remove {
&.deletion {
color: #f00;
padding: 2px 4px;
margin-left: 5px;
&:hover {
background: #ff0;
color: #fff;
text-decoration: none;
}
}
}
}
@media screen and (max-width: 782px) {
.redux-container-multi_text {
input {
clear: both;
}
.redux-multi-text-remove {
margin: 0;
float: right;
}
}
}
.wp-customizer {
.redux-container-multi_text {
.button {
float: right;
}
.redux-multi-text-remove {
float: right;
margin-bottom: 5px;
}
ul.redux-multi-text input {
width: 100% !important;
}
}
}