This commit is contained in:
2026-05-07 14:57:59 +02:00
parent c4a485e530
commit 811069a25c
35 changed files with 2980 additions and 30 deletions

View File

@@ -24,6 +24,30 @@ class Rest_Controller extends \WP_REST_Controller {
*/
const NAMESPACE = 'yacht-booking/v1';
/**
* Deterministyczna paleta kolorów dla widgetu zbiorczego (wszystkie jachty).
*
* Mapowanie: yacht_id (sortowane rosnąco) → palette[index % count].
* Wspólna dla REST i frontendu (używana przez calendar-all.js do legendy).
*/
const YACHT_COLOR_PALETTE = array(
'#3498db',
'#e74c3c',
'#2ecc71',
'#f39c12',
'#9b59b6',
'#1abc9c',
'#34495e',
'#d35400',
);
/**
* Kolor dla wspólnych wydarzeń kalendarza (yacht_id=0, source=ical_global_calendar).
* Jasnoniebieski — dobry kontrast na ciemnogranatowym tle kalendarza, łagodniejszy
* dla oka niż akcent czerwony.
*/
const GLOBAL_EVENT_COLOR = '#7fb3d5';
/**
* Constructor
*/
@@ -97,6 +121,27 @@ class Rest_Controller extends \WP_REST_Controller {
)
);
// GET /yacht-booking/v1/availability/all
register_rest_route(
self::NAMESPACE,
'/availability/all',
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array( $this, 'get_all_availability' ),
'permission_callback' => '__return_true',
'args' => array(
'start' => array(
'required' => false,
'sanitize_callback' => 'sanitize_text_field',
),
'end' => array(
'required' => false,
'sanitize_callback' => 'sanitize_text_field',
),
),
)
);
// POST /yacht-booking/v1/bookings
register_rest_route(
self::NAMESPACE,
@@ -297,6 +342,136 @@ class Rest_Controller extends \WP_REST_Controller {
return rest_ensure_response( $events );
}
/**
* Get aggregated availability for all yachts + globalne wydarzenia kalendarza.
*
* Zwraca tablicę FullCalendar events (timed 12:00 → 12:00 dla efektu half-day).
* Eventy z yacht_id > 0: kolor z palety per-jacht.
* Eventy yacht_id = 0 (sync_mode=global): kolor `GLOBAL_EVENT_COLOR`.
*
* @param \WP_REST_Request $request Request object.
* @return \WP_REST_Response
*/
public function get_all_availability( $request ) {
$start = $request->get_param( 'start' );
$end = $request->get_param( 'end' );
if ( ! $start || ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $start ) ) {
$start = gmdate( 'Y-m-01' );
}
if ( ! $end || ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $end ) ) {
$end = gmdate( 'Y-m-d', strtotime( $start . ' +12 months' ) );
}
$is_global_mode = ( 'global' === Settings::get_ical_sync_mode() );
// Build yacht_id → color map (deterministic by ascending yacht_id).
$yacht_posts = get_posts(
array(
'post_type' => 'yacht',
'post_status' => 'publish',
'posts_per_page' => -1,
'orderby' => 'ID',
'order' => 'ASC',
'fields' => 'ids',
)
);
$color_map = self::get_yacht_color_palette( $yacht_posts );
// Query bookings overlapping [start, end] with status confirmed or pending.
$bookings = get_posts(
array(
'post_type' => 'yacht_booking',
'post_status' => 'publish',
'posts_per_page' => -1,
'meta_query' => array(
'relation' => 'AND',
array(
'key' => '_booking_status',
'value' => array( 'confirmed', 'pending' ),
'compare' => 'IN',
),
array(
'key' => '_booking_end_date',
'value' => $start,
'compare' => '>=',
'type' => 'DATE',
),
array(
'key' => '_booking_start_date',
'value' => $end,
'compare' => '<=',
'type' => 'DATE',
),
),
)
);
$events = array();
foreach ( $bookings as $booking ) {
$booking_id = $booking->ID;
$start_date = Booking::get_start_date( $booking_id );
$end_date = Booking::get_end_date( $booking_id );
if ( ! $start_date || ! $end_date ) {
continue;
}
$yacht_id = Booking::get_yacht_id( $booking_id );
$source = (string) get_post_meta( $booking_id, '_booking_source', true );
$is_global_event = ( 0 === $yacht_id || \YachtBooking\Integrations\ICal\ICal_Import::GLOBAL_CALENDAR_SOURCE === $source );
// W trybie global wszystko traktujemy jak wspólne wydarzenia: szary kolor,
// brak yacht_id, generyczny tytuł "Rezerwacja" — bez wycieku danych klientów.
if ( $is_global_mode || $is_global_event ) {
$color = self::GLOBAL_EVENT_COLOR;
$title = __( 'Rezerwacja', 'yacht-booking' );
$y_id = 0;
} else {
$color = isset( $color_map[ $yacht_id ] ) ? $color_map[ $yacht_id ] : self::GLOBAL_EVENT_COLOR;
$yacht = get_post( $yacht_id );
// Tryb per_yacht: pokazujemy tylko nazwę jachtu (bez nazwiska klienta — privacy).
$title = $yacht ? $yacht->post_title : __( 'Rezerwacja', 'yacht-booking' );
$y_id = $yacht_id;
}
$events[] = array(
'id' => $booking_id,
'title' => $title,
'start' => $start_date . 'T12:00:00',
'end' => $end_date . 'T12:00:00',
'color' => $color,
'yacht_id' => $y_id,
);
}
return rest_ensure_response( $events );
}
/**
* Buduje deterministyczną mapę yacht_id → kolor z palety.
*
* Sortuje yacht_ids rosnąco i indeksuje paletę modulo długość. Zapewnia stabilny
* kolor dla danego jachtu między requestami i wspólny przypisanie z frontendem.
*
* @param array $yacht_ids Lista ID jachtów.
* @return array<int,string> yacht_id => hex color.
*/
public static function get_yacht_color_palette( $yacht_ids ) {
$ids = array_map( 'intval', (array) $yacht_ids );
sort( $ids, SORT_NUMERIC );
$palette = self::YACHT_COLOR_PALETTE;
$count = count( $palette );
$map = array();
foreach ( $ids as $i => $yacht_id ) {
$map[ $yacht_id ] = $palette[ $i % $count ];
}
return $map;
}
/**
* Create new booking
*