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

364 lines
12 KiB
PHP
Raw Normal View History

<?php
/**
* Calendar admin page.
*
* Displays availability calendar for rooms and buildings.
*
* @package Magdev\WpBnb\Admin
*/
declare( strict_types=1 );
namespace Magdev\WpBnb\Admin;
use Magdev\WpBnb\Booking\Availability;
use Magdev\WpBnb\PostTypes\Booking;
use Magdev\WpBnb\PostTypes\Building;
use Magdev\WpBnb\PostTypes\Room;
/**
* Calendar admin page class.
*/
final class Calendar {
/**
* Initialize the calendar page.
*
* @return void
*/
public static function init(): void {
add_action( 'admin_menu', array( self::class, 'register_menu' ) );
}
/**
* Register the admin menu item.
*
* @return void
*/
public static function register_menu(): void {
add_submenu_page(
'wp-bnb',
__( 'Calendar', 'wp-bnb' ),
__( 'Calendar', 'wp-bnb' ),
'edit_posts',
'wp-bnb-calendar',
array( self::class, 'render_page' )
);
}
/**
* Render the calendar page.
*
* @return void
*/
public static function render_page(): void {
// Get filter parameters.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page.
$building_id = isset( $_GET['building_id'] ) ? absint( $_GET['building_id'] ) : 0;
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page.
$room_id = isset( $_GET['room_id'] ) ? absint( $_GET['room_id'] ) : 0;
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page.
$year = isset( $_GET['year'] ) ? absint( $_GET['year'] ) : (int) gmdate( 'Y' );
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page.
$month = isset( $_GET['month'] ) ? absint( $_GET['month'] ) : (int) gmdate( 'n' );
// Validate month.
if ( $month < 1 || $month > 12 ) {
$month = (int) gmdate( 'n' );
}
// Get buildings and rooms for filters.
$buildings = get_posts(
array(
'post_type' => Building::POST_TYPE,
'post_status' => 'publish',
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC',
)
);
$rooms = get_posts(
array(
'post_type' => Room::POST_TYPE,
'post_status' => 'publish',
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC',
)
);
// If room is selected, get its building.
if ( $room_id && ! $building_id ) {
$building = Room::get_building( $room_id );
if ( $building ) {
$building_id = $building->ID;
}
}
// Get rooms to display.
$display_rooms = array();
if ( $room_id ) {
$room = get_post( $room_id );
if ( $room ) {
$display_rooms[] = $room;
}
} elseif ( $building_id ) {
$display_rooms = Room::get_rooms_for_building( $building_id );
} else {
$display_rooms = $rooms;
}
// Calculate navigation dates.
$prev_month = $month === 1 ? 12 : $month - 1;
$prev_year = $month === 1 ? $year - 1 : $year;
$next_month = $month === 12 ? 1 : $month + 1;
$next_year = $month === 12 ? $year + 1 : $year;
$month_name = gmdate( 'F', mktime( 0, 0, 0, $month, 1, $year ) );
?>
<div class="wrap">
<h1><?php esc_html_e( 'Availability Calendar', 'wp-bnb' ); ?></h1>
<div class="bnb-calendar-container">
<!-- Calendar Header -->
<div class="bnb-calendar-header">
<div class="bnb-calendar-nav">
<a href="<?php echo esc_url( self::get_calendar_url( $prev_year, $prev_month, $building_id, $room_id ) ); ?>"
class="button">
&laquo; <?php esc_html_e( 'Previous', 'wp-bnb' ); ?>
</a>
<a href="<?php echo esc_url( self::get_calendar_url( (int) gmdate( 'Y' ), (int) gmdate( 'n' ), $building_id, $room_id ) ); ?>"
class="button">
<?php esc_html_e( 'Today', 'wp-bnb' ); ?>
</a>
<a href="<?php echo esc_url( self::get_calendar_url( $next_year, $next_month, $building_id, $room_id ) ); ?>"
class="button">
<?php esc_html_e( 'Next', 'wp-bnb' ); ?> &raquo;
</a>
</div>
<h2><?php echo esc_html( $month_name . ' ' . $year ); ?></h2>
<div></div>
</div>
<!-- Filters -->
<div class="bnb-calendar-filters">
<form method="get" action="">
<input type="hidden" name="page" value="wp-bnb-calendar">
<input type="hidden" name="year" value="<?php echo esc_attr( $year ); ?>">
<input type="hidden" name="month" value="<?php echo esc_attr( $month ); ?>">
<label>
<?php esc_html_e( 'Building:', 'wp-bnb' ); ?>
<select name="building_id" onchange="this.form.submit()">
<option value=""><?php esc_html_e( 'All Buildings', 'wp-bnb' ); ?></option>
<?php foreach ( $buildings as $building ) : ?>
<option value="<?php echo esc_attr( $building->ID ); ?>" <?php selected( $building_id, $building->ID ); ?>>
<?php echo esc_html( $building->post_title ); ?>
</option>
<?php endforeach; ?>
</select>
</label>
<label>
<?php esc_html_e( 'Room:', 'wp-bnb' ); ?>
<select name="room_id" onchange="this.form.submit()">
<option value=""><?php esc_html_e( 'All Rooms', 'wp-bnb' ); ?></option>
<?php foreach ( $rooms as $room ) : ?>
<?php
$room_building = Room::get_building( $room->ID );
$room_label = $room->post_title;
if ( $room_building ) {
$room_label .= ' (' . $room_building->post_title . ')';
}
?>
<option value="<?php echo esc_attr( $room->ID ); ?>" <?php selected( $room_id, $room->ID ); ?>>
<?php echo esc_html( $room_label ); ?>
</option>
<?php endforeach; ?>
</select>
</label>
</form>
</div>
<!-- Calendar Grid -->
<div class="bnb-calendar-grid">
<?php if ( empty( $display_rooms ) ) : ?>
<div class="bnb-no-rooms">
<span class="dashicons dashicons-calendar-alt"></span>
<p><?php esc_html_e( 'No rooms found. Please add rooms first.', 'wp-bnb' ); ?></p>
<a href="<?php echo esc_url( admin_url( 'post-new.php?post_type=' . Room::POST_TYPE ) ); ?>" class="button button-primary">
<?php esc_html_e( 'Add Room', 'wp-bnb' ); ?>
</a>
</div>
<?php else : ?>
<?php self::render_calendar_table( $display_rooms, $year, $month, (bool) $room_id ); ?>
<?php endif; ?>
</div>
<!-- Legend -->
<div class="bnb-calendar-legend">
<div class="bnb-calendar-legend-item">
<span class="bnb-calendar-legend-color available"></span>
<?php esc_html_e( 'Available', 'wp-bnb' ); ?>
</div>
<div class="bnb-calendar-legend-item">
<span class="bnb-calendar-legend-color pending"></span>
<?php esc_html_e( 'Pending', 'wp-bnb' ); ?>
</div>
<div class="bnb-calendar-legend-item">
<span class="bnb-calendar-legend-color confirmed"></span>
<?php esc_html_e( 'Confirmed', 'wp-bnb' ); ?>
</div>
<div class="bnb-calendar-legend-item">
<span class="bnb-calendar-legend-color checked-in"></span>
<?php esc_html_e( 'Checked In', 'wp-bnb' ); ?>
</div>
</div>
</div>
</div>
<?php
}
/**
* Render the calendar table.
*
* @param array $rooms Rooms to display.
* @param int $year Year.
* @param int $month Month.
* @param bool $single_room Whether showing single room (more detail).
* @return void
*/
private static function render_calendar_table( array $rooms, int $year, int $month, bool $single_room = false ): void {
$days_in_month = (int) gmdate( 't', mktime( 0, 0, 0, $month, 1, $year ) );
$today = gmdate( 'Y-m-d' );
$class = $single_room ? 'bnb-calendar-table bnb-calendar-single-room' : 'bnb-calendar-table';
?>
<table class="<?php echo esc_attr( $class ); ?>">
<thead>
<tr>
<th class="room-header"><?php esc_html_e( 'Room', 'wp-bnb' ); ?></th>
<?php for ( $day = 1; $day <= $days_in_month; $day++ ) : ?>
<?php
$date_str = sprintf( '%04d-%02d-%02d', $year, $month, $day );
$day_of_week = gmdate( 'D', strtotime( $date_str ) );
$is_weekend = in_array( gmdate( 'N', strtotime( $date_str ) ), array( 6, 7 ), true );
?>
<th class="<?php echo $is_weekend ? 'weekend' : ''; ?>">
<?php echo esc_html( $day ); ?><br>
<small><?php echo esc_html( $day_of_week ); ?></small>
</th>
<?php endfor; ?>
</tr>
</thead>
<tbody>
<?php foreach ( $rooms as $room ) : ?>
<?php
$room_number = get_post_meta( $room->ID, '_bnb_room_room_number', true );
$room_building = Room::get_building( $room->ID );
$booked_dates = Availability::get_booked_dates( $room->ID, $year, $month );
?>
<tr>
<td class="bnb-calendar-room">
<a href="<?php echo esc_url( get_edit_post_link( $room->ID ) ); ?>">
<?php echo esc_html( $room->post_title ); ?>
<?php if ( $room_number ) : ?>
<span class="room-number">#<?php echo esc_html( $room_number ); ?></span>
<?php endif; ?>
</a>
<?php if ( $room_building ) : ?>
<span class="building-name"><?php echo esc_html( $room_building->post_title ); ?></span>
<?php endif; ?>
</td>
<?php for ( $day = 1; $day <= $days_in_month; $day++ ) : ?>
<?php
$date_str = sprintf( '%04d-%02d-%02d', $year, $month, $day );
$is_past = $date_str < $today;
$is_today = $date_str === $today;
$is_booked = isset( $booked_dates[ $date_str ] );
$classes = array( 'bnb-calendar-day' );
$title = '';
$booking_id = 0;
if ( $is_past ) {
$classes[] = 'past';
}
if ( $is_today ) {
$classes[] = 'today';
}
if ( $is_booked ) {
$booking_data = $booked_dates[ $date_str ];
$booking_id = $booking_data['booking_id'];
$classes[] = 'booked';
$classes[] = 'status-' . $booking_data['status'];
if ( $booking_data['is_start'] ) {
$classes[] = 'booked-start';
}
if ( $booking_data['is_end'] ) {
$classes[] = 'booked-end';
}
$title = sprintf(
/* translators: 1: Booking reference, 2: Guest name, 3: Check-in date, 4: Check-out date */
__( '%1$s - %2$s (%3$s to %4$s)', 'wp-bnb' ),
$booking_data['reference'],
$booking_data['guest'],
wp_date( get_option( 'date_format' ), strtotime( $booking_data['check_in'] ) ),
wp_date( get_option( 'date_format' ), strtotime( $booking_data['check_out'] ) )
);
} elseif ( ! $is_past ) {
$classes[] = 'available';
}
?>
<td class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>"
<?php if ( $booking_id ) : ?>
data-booking-id="<?php echo esc_attr( $booking_id ); ?>"
<?php endif; ?>
<?php if ( $title ) : ?>
title="<?php echo esc_attr( $title ); ?>"
<?php endif; ?>>
<?php if ( $single_room && $is_booked && $booking_data['is_start'] ) : ?>
<span class="guest-name"><?php echo esc_html( $booking_data['guest'] ); ?></span>
<?php endif; ?>
</td>
<?php endfor; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php
}
/**
* Get calendar URL with parameters.
*
* @param int $year Year.
* @param int $month Month.
* @param int $building_id Building ID (optional).
* @param int $room_id Room ID (optional).
* @return string URL.
*/
private static function get_calendar_url( int $year, int $month, int $building_id = 0, int $room_id = 0 ): string {
$args = array(
'page' => 'wp-bnb-calendar',
'year' => $year,
'month' => $month,
);
if ( $building_id ) {
$args['building_id'] = $building_id;
}
if ( $room_id ) {
$args['room_id'] = $room_id;
}
return add_query_arg( $args, admin_url( 'admin.php' ) );
}
}