This commit is contained in:
2026-05-10 15:08:40 +02:00
parent 7278a422af
commit 8d75a95e69
14 changed files with 1181 additions and 706 deletions

View File

@@ -412,6 +412,10 @@ class Admin {
$gcal_alias = isset( $data['yacht_gcal_alias'] ) ? sanitize_text_field( wp_unslash( $data['yacht_gcal_alias'] ) ) : '';
Yacht::update_gcal_alias( $saved_id, $gcal_alias );
// Save admin-selected color for aggregated calendar.
$yacht_color = isset( $data['yacht_color'] ) ? sanitize_text_field( wp_unslash( $data['yacht_color'] ) ) : '';
Yacht::update_color( $saved_id, $yacht_color );
return $saved_id;
}

View File

@@ -13,7 +13,8 @@ if ( ! defined( 'ABSPATH' ) ) {
// Get yacht data
$title = $yacht ? $yacht->post_title : '';
$content = $yacht ? $yacht->post_content : '';
$gcal_alias = $yacht ? \YachtBooking\Yacht::get_gcal_alias( $yacht->ID ) : '';
$gcal_alias = $yacht ? \YachtBooking\Yacht::get_gcal_alias( $yacht->ID ) : '';
$yacht_color = $yacht ? \YachtBooking\Yacht::get_color( $yacht->ID ) : '';
$page_title = $yacht ? __( 'Edytuj Jacht', 'yacht-booking' ) : __( 'Dodaj Jacht', 'yacht-booking' );
?>
@@ -86,6 +87,28 @@ $page_title = $yacht ? __( 'Edytuj Jacht', 'yacht-booking' ) : __( 'Dodaj Jacht'
</td>
</tr>
<!-- Kolor jachtu w kalendarzu zbiorczym -->
<tr>
<th scope="row">
<label for="yacht_color">
<?php esc_html_e( 'Kolor w kalendarzu zbiorczym', 'yacht-booking' ); ?>
</label>
</th>
<td>
<input
type="text"
name="yacht_color"
id="yacht_color"
class="yacht-color-picker"
value="<?php echo esc_attr( $yacht_color ); ?>"
data-default-color=""
/>
<p class="description">
<?php esc_html_e( 'Kolor pasków rezerwacji tego jachtu w widgecie zbiorczym (/rezerwacja/). Pozostaw puste, aby użyć automatycznego koloru z palety.', 'yacht-booking' ); ?>
</p>
</td>
</tr>
<!-- Alias dla globalnej synchronizacji iCal -->
<tr>
<th scope="row">

View File

@@ -376,18 +376,40 @@ class Rest_Controller extends \WP_REST_Controller {
$is_global_mode = ( 'global' === Settings::get_ical_sync_mode() );
// Build yacht_id → color map (deterministic by ascending yacht_id).
$yacht_posts = get_posts(
// Build yacht_id → color map (admin-selected `_yacht_color` lub fallback z palety po ID).
$yacht_posts_full = 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 );
$yacht_ids = array();
foreach ( $yacht_posts_full as $yp ) {
$yacht_ids[] = (int) $yp->ID;
}
$color_map = self::get_yacht_color_palette( $yacht_ids );
// Build name/alias → yacht_id map (lowercase keys, sorted by length DESC for longest match).
$name_map = array();
foreach ( $yacht_posts_full as $yp ) {
$title = mb_strtolower( trim( (string) $yp->post_title ) );
if ( '' !== $title ) {
$name_map[ $title ] = (int) $yp->ID;
}
$alias = mb_strtolower( trim( (string) \YachtBooking\Yacht::get_gcal_alias( $yp->ID ) ) );
if ( '' !== $alias ) {
$name_map[ $alias ] = (int) $yp->ID;
}
}
uksort(
$name_map,
function( $a, $b ) {
return mb_strlen( $b ) - mb_strlen( $a );
}
);
// Query bookings overlapping [start, end] with status confirmed or pending.
$bookings = get_posts(
@@ -437,15 +459,6 @@ class Rest_Controller extends \WP_REST_Controller {
$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 );
// Color: zachowane z poprzedniej logiki (per-yacht paleta lub kolor global).
if ( $is_global_mode || $is_global_event ) {
$color = self::GLOBAL_EVENT_COLOR;
$y_id = 0;
} else {
$color = isset( $color_map[ $yacht_id ] ) ? $color_map[ $yacht_id ] : self::GLOBAL_EVENT_COLOR;
$y_id = $yacht_id;
}
// Title: raw SUMMARY z _booking_notes (iCal) lub customer_name (frontend).
// Klient świadomie cofa privacy z 09-04 — tytuły rezerwacji widoczne publicznie.
if ( in_array( $source, $ical_sources, true ) ) {
@@ -457,6 +470,26 @@ class Rest_Controller extends \WP_REST_Controller {
}
$title = sanitize_text_field( $title );
// Color resolution:
// - per-yacht event (yacht_id > 0): admin color or palette fallback
// - global event (yacht_id = 0): match yacht name/alias anywhere in title (longest wins)
if ( ! $is_global_event ) {
$color = isset( $color_map[ $yacht_id ] ) ? $color_map[ $yacht_id ] : self::GLOBAL_EVENT_COLOR;
$y_id = $yacht_id;
} else {
$color = self::GLOBAL_EVENT_COLOR;
$y_id = 0;
$title_lower = mb_strtolower( $title );
foreach ( $name_map as $needle => $matched_id ) {
if ( '' !== $needle && false !== mb_strpos( $title_lower, $needle ) ) {
if ( isset( $color_map[ $matched_id ] ) ) {
$color = $color_map[ $matched_id ];
}
break;
}
}
}
// Split na N eventów per dzień (allDay = każdy event mieści się w jednej komórce).
// Iteracja od start_date do end_date INCLUSIVE — pierwszy i ostatni dzień
// mają half-day visual (yacht odbierany / zwracany w południe).
@@ -562,7 +595,8 @@ class Rest_Controller extends \WP_REST_Controller {
$map = array();
foreach ( $ids as $i => $yacht_id ) {
$map[ $yacht_id ] = $palette[ $i % $count ];
$admin_color = \YachtBooking\Yacht::get_color( $yacht_id );
$map[ $yacht_id ] = '' !== $admin_color ? $admin_color : $palette[ $i % $count ];
}
return $map;

View File

@@ -79,11 +79,20 @@
/* Event styling — pełne wypełnienie kafelka kolorem jachtu, bez kropek/czasu */
.yacht-calendar-all .fc-event {
border: none !important;
padding: 2px 4px;
padding: 0 1px;
font-size: 12px;
font-weight: 500;
color: #fff;
cursor: default;
display: flex;
align-items: center;
}
.yacht-calendar-all .fc-event-main {
display: flex;
align-items: center;
width: 100%;
height: 100%;
}
.yacht-calendar-all .fc-daygrid-event-dot,
@@ -93,7 +102,7 @@
/* Custom kontener tytułu (renderowany przez eventContent w JS). */
.yacht-calendar-all .yc-event-title {
padding: 1px 6px;
padding: 0 2px;
font-size: 11px;
font-weight: 600;
color: #fff;
@@ -102,13 +111,15 @@
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.3;
text-align: center;
width: 100%;
}
/* Pasek eventu wyższy + gap między dziennymi segmentami (rezerwacja wielonocna
= N osobnych pasków zamiast jednej belki — patrz REST split per-day). */
.yacht-calendar-all .fc-daygrid-event {
min-height: 18px;
margin: 1px 2px !important;
margin: 1px 0 !important;
border-radius: 2px;
}
@@ -123,7 +134,7 @@
.yacht-calendar-all .yc-event-title {
font-size: 10px;
padding: 1px 4px;
padding: 0 1px;
}
.yacht-calendar-all .fc-toolbar.fc-header-toolbar {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -167,19 +167,18 @@ class Calendar_All_View {
<?php if ( $show_legend ) : ?>
<div class="yacht-calendar-legend yacht-calendar-all-legend" aria-label="<?php esc_attr_e( 'Legenda kalendarza', 'yacht-booking' ); ?>">
<?php foreach ( $yacht_posts as $yacht ) : ?>
<?php $color = isset( $color_map[ $yacht->ID ] ) ? $color_map[ $yacht->ID ] : $global_color; ?>
<span class="yacht-legend-item">
<span class="yacht-legend-swatch" style="background-color: <?php echo esc_attr( $color ); ?>;"></span>
<?php echo esc_html( $yacht->post_title ); ?>
</span>
<?php endforeach; ?>
<?php if ( 'global' === $sync_mode ) : ?>
<span class="yacht-legend-item">
<span class="yacht-legend-swatch" style="background-color: <?php echo esc_attr( $global_color ); ?>;"></span>
<?php esc_html_e( 'Rezerwacja', 'yacht-booking' ); ?>
<?php esc_html_e( 'Inne', 'yacht-booking' ); ?>
</span>
<?php else : ?>
<?php foreach ( $yacht_posts as $yacht ) : ?>
<?php $color = isset( $color_map[ $yacht->ID ] ) ? $color_map[ $yacht->ID ] : $global_color; ?>
<span class="yacht-legend-item">
<span class="yacht-legend-swatch" style="background-color: <?php echo esc_attr( $color ); ?>;"></span>
<?php echo esc_html( $yacht->post_title ); ?>
</span>
<?php endforeach; ?>
<?php endif; ?>
</div>
<?php endif; ?>

View File

@@ -218,6 +218,16 @@ class Yacht_Booking {
YACHT_BOOKING_VERSION
);
// WP color picker on yacht edit form.
if ( isset( $_GET['page'] ) && 'yacht-bookings-add-yacht' === $_GET['page'] ) {
wp_enqueue_style( 'wp-color-picker' );
wp_enqueue_script( 'wp-color-picker' );
wp_add_inline_script(
'wp-color-picker',
'jQuery(function($){ $(".yacht-color-picker").wpColorPicker(); });'
);
}
wp_enqueue_script(
'yacht-booking-admin',
YACHT_BOOKING_PLUGIN_URL . 'admin/assets/js/admin.js',

View File

@@ -152,4 +152,37 @@ class Yacht {
public static function update_features( $yacht_id, $features ) {
update_post_meta( $yacht_id, '_yacht_features', is_array( $features ) ? $features : array() );
}
/**
* Get admin-selected yacht color for the aggregated calendar.
*
* Returns sanitized hex (#rrggbb, lowercase) or '' when not set.
*
* @param int $yacht_id Yacht post ID.
* @return string
*/
public static function get_color( $yacht_id ) {
$value = (string) get_post_meta( $yacht_id, '_yacht_color', true );
if ( '' === $value ) {
return '';
}
return preg_match( '/^#[0-9a-f]{6}$/i', $value ) ? strtolower( $value ) : '';
}
/**
* Update yacht color. Empty value removes the meta (fallback to palette).
*
* @param int $yacht_id Yacht post ID.
* @param string $color Hex color (#rrggbb) or empty string.
*/
public static function update_color( $yacht_id, $color ) {
$color = trim( (string) $color );
if ( '' === $color ) {
delete_post_meta( $yacht_id, '_yacht_color' );
return;
}
if ( preg_match( '/^#[0-9a-f]{6}$/i', $color ) ) {
update_post_meta( $yacht_id, '_yacht_color', strtolower( $color ) );
}
}
}