Files
wp-bnb/src/Admin/Seasons.php

420 lines
14 KiB
PHP
Raw Normal View History

<?php
/**
* Seasons admin page.
*
* Handles the admin interface for managing seasonal pricing.
*
* @package Magdev\WpBnb\Admin
*/
declare( strict_types=1 );
namespace Magdev\WpBnb\Admin;
use Magdev\WpBnb\Pricing\Season;
/**
* Seasons admin page class.
*/
final class Seasons {
/**
* Initialize the admin page.
*
* @return void
*/
public static function init(): void {
add_action( 'admin_menu', array( self::class, 'register_menu' ) );
add_action( 'admin_init', array( self::class, 'handle_actions' ) );
}
/**
* Register the submenu page.
*
* @return void
*/
public static function register_menu(): void {
add_submenu_page(
'wp-bnb',
__( 'Seasons', 'wp-bnb' ),
__( 'Seasons', 'wp-bnb' ),
'manage_options',
'wp-bnb-seasons',
array( self::class, 'render_page' )
);
}
/**
* Handle form actions.
*
* @return void
*/
public static function handle_actions(): void {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Just checking page.
if ( ! isset( $_GET['page'] ) || 'wp-bnb-seasons' !== $_GET['page'] ) {
return;
}
// Handle delete action.
if ( isset( $_GET['action'], $_GET['season_id'], $_GET['_wpnonce'] )
&& 'delete' === $_GET['action']
&& wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'delete_season' )
) {
$season_id = sanitize_text_field( wp_unslash( $_GET['season_id'] ) );
Season::delete( $season_id );
wp_safe_redirect( admin_url( 'admin.php?page=wp-bnb-seasons&deleted=1' ) );
exit;
}
// Handle create default seasons.
if ( isset( $_GET['action'], $_GET['_wpnonce'] )
&& 'create_defaults' === $_GET['action']
&& wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'create_default_seasons' )
) {
Season::createDefaults();
wp_safe_redirect( admin_url( 'admin.php?page=wp-bnb-seasons&defaults_created=1' ) );
exit;
}
// Handle form submission.
if ( isset( $_POST['wp_bnb_season_nonce'] )
&& wp_verify_nonce( sanitize_key( $_POST['wp_bnb_season_nonce'] ), 'save_season' )
) {
self::save_season();
}
}
/**
* Save a season from form data.
*
* @return void
*/
private static function save_season(): void {
$data = array(
'id' => isset( $_POST['season_id'] ) && '' !== $_POST['season_id']
? sanitize_text_field( wp_unslash( $_POST['season_id'] ) )
: wp_generate_uuid4(),
'name' => isset( $_POST['season_name'] )
? sanitize_text_field( wp_unslash( $_POST['season_name'] ) )
: '',
'start_date' => isset( $_POST['season_start_date'] )
? sanitize_text_field( wp_unslash( $_POST['season_start_date'] ) )
: '',
'end_date' => isset( $_POST['season_end_date'] )
? sanitize_text_field( wp_unslash( $_POST['season_end_date'] ) )
: '',
'modifier' => isset( $_POST['season_modifier'] )
? floatval( $_POST['season_modifier'] )
: 1.0,
'priority' => isset( $_POST['season_priority'] )
? absint( $_POST['season_priority'] )
: 0,
'active' => isset( $_POST['season_active'] ),
);
$season = new Season( $data );
Season::save( $season );
$is_edit = isset( $_POST['season_id'] ) && '' !== $_POST['season_id'];
$message = $is_edit ? 'updated' : 'created';
wp_safe_redirect( admin_url( 'admin.php?page=wp-bnb-seasons&' . $message . '=1' ) );
exit;
}
/**
* Render the admin page.
*
* @return void
*/
public static function render_page(): void {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only.
$action = isset( $_GET['action'] ) ? sanitize_key( $_GET['action'] ) : 'list';
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only.
$season_id = isset( $_GET['season_id'] ) ? sanitize_text_field( wp_unslash( $_GET['season_id'] ) ) : '';
?>
<div class="wrap">
<h1 class="wp-heading-inline"><?php esc_html_e( 'Seasonal Pricing', 'wp-bnb' ); ?></h1>
<?php if ( 'list' === $action || '' === $action ) : ?>
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wp-bnb-seasons&action=add' ) ); ?>" class="page-title-action">
<?php esc_html_e( 'Add Season', 'wp-bnb' ); ?>
</a>
<?php endif; ?>
<hr class="wp-header-end">
<?php self::render_notices(); ?>
<?php
switch ( $action ) {
case 'add':
self::render_form();
break;
case 'edit':
self::render_form( $season_id );
break;
default:
self::render_list();
break;
}
?>
</div>
<?php
}
/**
* Render admin notices.
*
* @return void
*/
private static function render_notices(): void {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only.
if ( isset( $_GET['deleted'] ) ) {
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Season deleted successfully.', 'wp-bnb' ) . '</p></div>';
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only.
if ( isset( $_GET['created'] ) ) {
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Season created successfully.', 'wp-bnb' ) . '</p></div>';
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only.
if ( isset( $_GET['updated'] ) ) {
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Season updated successfully.', 'wp-bnb' ) . '</p></div>';
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only.
if ( isset( $_GET['defaults_created'] ) ) {
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Default seasons created successfully.', 'wp-bnb' ) . '</p></div>';
}
}
/**
* Render the seasons list.
*
* @return void
*/
private static function render_list(): void {
$seasons = Season::all();
?>
<div class="bnb-seasons-description">
<p><?php esc_html_e( 'Seasonal pricing allows you to automatically adjust room rates based on the time of year. Define seasons with date ranges and price modifiers.', 'wp-bnb' ); ?></p>
<p><?php esc_html_e( 'Seasons with higher priority take precedence when dates overlap (e.g., Winter Holidays overrides Low Season).', 'wp-bnb' ); ?></p>
</div>
<?php if ( empty( $seasons ) ) : ?>
<div class="bnb-no-seasons">
<p><?php esc_html_e( 'No seasons have been configured yet.', 'wp-bnb' ); ?></p>
<p>
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wp-bnb-seasons&action=add' ) ); ?>" class="button button-primary">
<?php esc_html_e( 'Add Your First Season', 'wp-bnb' ); ?>
</a>
<a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin.php?page=wp-bnb-seasons&action=create_defaults' ), 'create_default_seasons' ) ); ?>" class="button">
<?php esc_html_e( 'Create Default Seasons', 'wp-bnb' ); ?>
</a>
</p>
</div>
<?php else : ?>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th scope="col" class="column-name"><?php esc_html_e( 'Season Name', 'wp-bnb' ); ?></th>
<th scope="col" class="column-dates"><?php esc_html_e( 'Period', 'wp-bnb' ); ?></th>
<th scope="col" class="column-modifier"><?php esc_html_e( 'Price Modifier', 'wp-bnb' ); ?></th>
<th scope="col" class="column-priority"><?php esc_html_e( 'Priority', 'wp-bnb' ); ?></th>
<th scope="col" class="column-status"><?php esc_html_e( 'Status', 'wp-bnb' ); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ( $seasons as $season ) : ?>
<tr>
<td class="column-name">
<strong>
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wp-bnb-seasons&action=edit&season_id=' . $season->id ) ); ?>">
<?php echo esc_html( $season->name ); ?>
</a>
</strong>
<div class="row-actions">
<span class="edit">
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wp-bnb-seasons&action=edit&season_id=' . $season->id ) ); ?>">
<?php esc_html_e( 'Edit', 'wp-bnb' ); ?>
</a> |
</span>
<span class="delete">
<a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin.php?page=wp-bnb-seasons&action=delete&season_id=' . $season->id ), 'delete_season' ) ); ?>"
class="submitdelete"
onclick="return confirm('<?php esc_attr_e( 'Are you sure you want to delete this season?', 'wp-bnb' ); ?>');">
<?php esc_html_e( 'Delete', 'wp-bnb' ); ?>
</a>
</span>
</div>
</td>
<td class="column-dates">
<?php echo esc_html( self::format_date_range( $season->start_date, $season->end_date ) ); ?>
</td>
<td class="column-modifier">
<?php echo esc_html( $season->getModifierLabel() ); ?>
<?php if ( $season->modifier !== 1.0 ) : ?>
<span class="bnb-modifier-visual" style="color: <?php echo $season->modifier > 1 ? '#d63638' : '#00a32a'; ?>;">
(<?php echo esc_html( number_format( $season->modifier, 2 ) ); ?>x)
</span>
<?php endif; ?>
</td>
<td class="column-priority">
<?php echo esc_html( $season->priority ); ?>
</td>
<td class="column-status">
<?php if ( $season->active ) : ?>
<span class="bnb-status-active"><?php esc_html_e( 'Active', 'wp-bnb' ); ?></span>
<?php else : ?>
<span class="bnb-status-inactive"><?php esc_html_e( 'Inactive', 'wp-bnb' ); ?></span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php
}
/**
* Render the season form.
*
* @param string $season_id Season ID for editing, empty for new.
* @return void
*/
private static function render_form( string $season_id = '' ): void {
$season = $season_id ? Season::find( $season_id ) : null;
$is_edit = null !== $season;
if ( $season_id && ! $season ) {
echo '<div class="notice notice-error"><p>' . esc_html__( 'Season not found.', 'wp-bnb' ) . '</p></div>';
return;
}
?>
<form method="post" action="" class="bnb-season-form">
<?php wp_nonce_field( 'save_season', 'wp_bnb_season_nonce' ); ?>
<input type="hidden" name="season_id" value="<?php echo esc_attr( $season ? $season->id : '' ); ?>">
<table class="form-table" role="presentation">
<tr>
<th scope="row">
<label for="season_name"><?php esc_html_e( 'Season Name', 'wp-bnb' ); ?></label>
</th>
<td>
<input type="text" name="season_name" id="season_name"
value="<?php echo esc_attr( $season ? $season->name : '' ); ?>"
class="regular-text" required>
<p class="description"><?php esc_html_e( 'A descriptive name for this season (e.g., "High Season", "Winter Holidays").', 'wp-bnb' ); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="season_start_date"><?php esc_html_e( 'Start Date', 'wp-bnb' ); ?></label>
</th>
<td>
<input type="text" name="season_start_date" id="season_start_date"
value="<?php echo esc_attr( $season ? $season->start_date : '' ); ?>"
class="small-text" placeholder="MM-DD" pattern="\d{2}-\d{2}" required>
<p class="description"><?php esc_html_e( 'Format: MM-DD (e.g., 06-15 for June 15th). Seasons repeat annually.', 'wp-bnb' ); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="season_end_date"><?php esc_html_e( 'End Date', 'wp-bnb' ); ?></label>
</th>
<td>
<input type="text" name="season_end_date" id="season_end_date"
value="<?php echo esc_attr( $season ? $season->end_date : '' ); ?>"
class="small-text" placeholder="MM-DD" pattern="\d{2}-\d{2}" required>
<p class="description"><?php esc_html_e( 'Format: MM-DD. Seasons can span year boundaries (e.g., 12-20 to 01-06).', 'wp-bnb' ); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="season_modifier"><?php esc_html_e( 'Price Modifier', 'wp-bnb' ); ?></label>
</th>
<td>
<input type="number" name="season_modifier" id="season_modifier"
value="<?php echo esc_attr( $season ? $season->modifier : '1.00' ); ?>"
class="small-text" min="0.1" max="3" step="0.01" required>
<p class="description">
<?php esc_html_e( 'Multiplier for base prices. Examples:', 'wp-bnb' ); ?><br>
<?php esc_html_e( '1.00 = Normal price | 1.25 = 25% increase | 0.85 = 15% discount', 'wp-bnb' ); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="season_priority"><?php esc_html_e( 'Priority', 'wp-bnb' ); ?></label>
</th>
<td>
<input type="number" name="season_priority" id="season_priority"
value="<?php echo esc_attr( $season ? $season->priority : '10' ); ?>"
class="small-text" min="0" max="100">
<p class="description"><?php esc_html_e( 'Higher priority seasons take precedence when dates overlap.', 'wp-bnb' ); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Status', 'wp-bnb' ); ?></th>
<td>
<label>
<input type="checkbox" name="season_active" value="1"
<?php checked( $season ? $season->active : true ); ?>>
<?php esc_html_e( 'Active', 'wp-bnb' ); ?>
</label>
<p class="description"><?php esc_html_e( 'Inactive seasons are ignored in price calculations.', 'wp-bnb' ); ?></p>
</td>
</tr>
</table>
<p class="submit">
<input type="submit" name="submit" class="button button-primary"
value="<?php echo $is_edit ? esc_attr__( 'Update Season', 'wp-bnb' ) : esc_attr__( 'Add Season', 'wp-bnb' ); ?>">
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wp-bnb-seasons' ) ); ?>" class="button">
<?php esc_html_e( 'Cancel', 'wp-bnb' ); ?>
</a>
</p>
</form>
<?php
}
/**
* Format a date range for display.
*
* @param string $start Start date (MM-DD).
* @param string $end End date (MM-DD).
* @return string
*/
private static function format_date_range( string $start, string $end ): string {
$months = array(
'01' => __( 'Jan', 'wp-bnb' ),
'02' => __( 'Feb', 'wp-bnb' ),
'03' => __( 'Mar', 'wp-bnb' ),
'04' => __( 'Apr', 'wp-bnb' ),
'05' => __( 'May', 'wp-bnb' ),
'06' => __( 'Jun', 'wp-bnb' ),
'07' => __( 'Jul', 'wp-bnb' ),
'08' => __( 'Aug', 'wp-bnb' ),
'09' => __( 'Sep', 'wp-bnb' ),
'10' => __( 'Oct', 'wp-bnb' ),
'11' => __( 'Nov', 'wp-bnb' ),
'12' => __( 'Dec', 'wp-bnb' ),
);
$format_date = function ( string $date ) use ( $months ): string {
$parts = explode( '-', $date );
if ( count( $parts ) !== 2 ) {
return $date;
}
$month = $months[ $parts[0] ] ?? $parts[0];
$day = ltrim( $parts[1], '0' );
return $month . ' ' . $day;
};
return $format_date( $start ) . ' - ' . $format_date( $end );
}
}