Files
carei.pagedev.pl/wp-content/plugins/carei-reservation/includes/class-admin-panel.php
2026-04-22 22:00:50 +02:00

753 lines
39 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class Carei_Admin_Panel {
const POST_TYPE = 'carei_reservation';
const META_PREFIX = '_carei_';
const PROTECTION_OPTION = 'carei_protection_packages';
const EXTRAS_SEEN_OPTION = 'carei_extras_seen';
const EXTRAS_TRANSLATIONS_OPTION = 'carei_extras_translations';
private static $statuses = array(
'nowe' => array( 'color' => '#2F2482' ),
'przeczytane' => array( 'color' => '#f59e0b' ),
'zrealizowane' => array( 'color' => '#22c55e' ),
);
private static function get_status_label( $key ) {
switch ( $key ) {
case 'nowe':
return __( 'Nowe', 'carei-reservation' );
case 'przeczytane':
return __( 'Przeczytane', 'carei-reservation' );
case 'zrealizowane':
return __( 'Zrealizowane', 'carei-reservation' );
}
return $key;
}
public function __construct() {
add_action( 'init', array( $this, 'register_post_type' ) );
add_filter( 'manage_' . self::POST_TYPE . '_posts_columns', array( $this, 'admin_columns' ) );
add_action( 'manage_' . self::POST_TYPE . '_posts_custom_column', array( $this, 'render_column' ), 10, 2 );
add_action( 'restrict_manage_posts', array( $this, 'status_filter_dropdown' ) );
add_action( 'pre_get_posts', array( $this, 'filter_by_status' ) );
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
add_action( 'save_post_' . self::POST_TYPE, array( $this, 'save_meta_box' ), 10, 2 );
add_action( 'edit_form_after_title', array( $this, 'auto_mark_read' ) );
add_action( 'admin_head', array( $this, 'admin_styles' ) );
add_action( 'admin_menu', array( $this, 'register_protection_packages_page' ) );
add_action( 'admin_post_carei_save_protection_packages', array( $this, 'handle_protection_packages_save' ) );
add_action( 'admin_menu', array( $this, 'register_extras_translations_page' ) );
add_action( 'admin_post_carei_save_extras_translations', array( $this, 'handle_extras_translations_save' ) );
}
// ─── Extras Translations (dynamic Softra pricelist item names) ────
public static function get_extras_seen() {
$seen = get_option( self::EXTRAS_SEEN_OPTION, array() );
if ( ! is_array( $seen ) ) return array();
$seen = array_values( array_unique( array_filter( array_map( 'strval', $seen ), 'strlen' ) ) );
sort( $seen, SORT_NATURAL | SORT_FLAG_CASE );
return $seen;
}
public static function get_extras_translations() {
$map = get_option( self::EXTRAS_TRANSLATIONS_OPTION, array() );
return is_array( $map ) ? $map : array();
}
public static function remember_extra_name( $pl_name ) {
$pl_name = trim( (string) $pl_name );
if ( $pl_name === '' ) return;
$seen = get_option( self::EXTRAS_SEEN_OPTION, array() );
if ( ! is_array( $seen ) ) $seen = array();
if ( ! in_array( $pl_name, $seen, true ) ) {
$seen[] = $pl_name;
update_option( self::EXTRAS_SEEN_OPTION, $seen, false );
}
}
public static function translate_extra_name( $pl_name, $locale = null ) {
if ( $locale === null ) {
$wp_locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale();
$locale = ( 0 === strpos( (string) $wp_locale, 'en' ) ) ? 'en' : 'pl';
}
if ( $locale !== 'en' ) return $pl_name;
$map = self::get_extras_translations();
if ( isset( $map[ $pl_name ] ) && $map[ $pl_name ] !== '' ) {
return $map[ $pl_name ];
}
return $pl_name;
}
public function register_extras_translations_page() {
add_submenu_page(
'edit.php?post_type=' . self::POST_TYPE,
__( 'Tłumaczenia extras', 'carei-reservation' ),
__( 'Tłumaczenia extras', 'carei-reservation' ),
'manage_options',
'carei-extras-translations',
array( $this, 'render_extras_translations_page' )
);
}
public function render_extras_translations_page() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Brak uprawnień.', 'carei-reservation' ) );
}
$seen = self::get_extras_seen();
$trans = self::get_extras_translations();
$saved = isset( $_GET['carei_saved'] ) && $_GET['carei_saved'] === '1';
?>
<div class="wrap">
<h1><?php esc_html_e( 'Tłumaczenia extras', 'carei-reservation' ); ?></h1>
<p><?php esc_html_e( 'Tłumaczenia nazw dodatkowych opcji (extras) zwracanych z API Softra. Puste pole = wersja polska jest używana także w wersji angielskiej (fallback).', 'carei-reservation' ); ?></p>
<?php if ( $saved ) : ?>
<div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Zapisano.', 'carei-reservation' ); ?></p></div>
<?php endif; ?>
<?php if ( empty( $seen ) ) : ?>
<div class="notice notice-info"><p><?php esc_html_e( 'Brak zebranych pozycji. Otwórz formularz rezerwacji i wybierz daty/oddział/klasę aby załadować pricelist — pozycje pojawią się tutaj automatycznie.', 'carei-reservation' ); ?></p></div>
<?php else : ?>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="carei-extras-form">
<input type="hidden" name="action" value="carei_save_extras_translations">
<?php wp_nonce_field( 'carei_extras_translations', 'carei_extras_nonce' ); ?>
<table class="form-table striped">
<thead>
<tr>
<th style="width: 40%;"><?php esc_html_e( 'Nazwa PL (z API Softra)', 'carei-reservation' ); ?></th>
<th><?php esc_html_e( 'Tłumaczenie EN', 'carei-reservation' ); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ( $seen as $pl_name ) :
$en = isset( $trans[ $pl_name ] ) ? (string) $trans[ $pl_name ] : '';
?>
<tr>
<td><strong><?php echo esc_html( $pl_name ); ?></strong></td>
<td>
<input type="text" name="translations[<?php echo esc_attr( $pl_name ); ?>]" value="<?php echo esc_attr( $en ); ?>" class="regular-text" placeholder="<?php echo esc_attr__( 'EN translation...', 'carei-reservation' ); ?>">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php submit_button( __( 'Zapisz tłumaczenia', 'carei-reservation' ) ); ?>
</form>
<?php endif; ?>
</div>
<?php
}
public function handle_extras_translations_save() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Brak uprawnień.', 'carei-reservation' ) );
}
if ( ! isset( $_POST['carei_extras_nonce'] ) || ! wp_verify_nonce( $_POST['carei_extras_nonce'], 'carei_extras_translations' ) ) {
wp_die( esc_html__( 'Nieprawidłowy token.', 'carei-reservation' ) );
}
$input = isset( $_POST['translations'] ) && is_array( $_POST['translations'] ) ? $_POST['translations'] : array();
$clean = array();
foreach ( $input as $pl_name => $en_value ) {
$pl = trim( (string) wp_unslash( $pl_name ) );
$en = sanitize_text_field( wp_unslash( (string) $en_value ) );
if ( $pl === '' ) continue;
$clean[ $pl ] = $en;
}
update_option( self::EXTRAS_TRANSLATIONS_OPTION, $clean, false );
$redirect = add_query_arg(
array(
'post_type' => self::POST_TYPE,
'page' => 'carei-extras-translations',
'carei_saved' => '1',
),
admin_url( 'edit.php' )
);
wp_safe_redirect( $redirect );
exit;
}
// ─── Protection Packages (SOFT / PREMIUM) ────────────────────
public static function get_protection_packages_defaults() {
return array(
'soft' => array(
'name' => __( 'Ubezpieczenie SOFT', 'carei-reservation' ),
'name_en' => 'SOFT Protection',
'pricePerDay' => 0,
'active' => true,
'description' => '',
'description_en' => '',
),
'premium' => array(
'name' => __( 'Ubezpieczenie PREMIUM', 'carei-reservation' ),
'name_en' => 'PREMIUM Protection',
'pricePerDay' => 0,
'active' => true,
'description' => '',
'description_en' => '',
),
);
}
public static function get_protection_packages() {
$defaults = self::get_protection_packages_defaults();
$stored = get_option( self::PROTECTION_OPTION, array() );
if ( ! is_array( $stored ) ) {
$stored = array();
}
$out = array();
foreach ( $defaults as $key => $def ) {
$item = isset( $stored[ $key ] ) && is_array( $stored[ $key ] ) ? $stored[ $key ] : array();
$out[ $key ] = array(
'name' => isset( $item['name'] ) && $item['name'] !== '' ? (string) $item['name'] : $def['name'],
'name_en' => isset( $item['name_en'] ) ? (string) $item['name_en'] : '',
'pricePerDay' => isset( $item['pricePerDay'] ) ? (float) $item['pricePerDay'] : (float) $def['pricePerDay'],
'active' => isset( $item['active'] ) ? (bool) $item['active'] : (bool) $def['active'],
'description' => isset( $item['description'] ) ? (string) $item['description'] : $def['description'],
'description_en' => isset( $item['description_en'] ) ? (string) $item['description_en'] : '',
);
}
return $out;
}
public function register_protection_packages_page() {
add_submenu_page(
'edit.php?post_type=' . self::POST_TYPE,
__( 'Pakiety ochronne', 'carei-reservation' ),
__( 'Pakiety ochronne', 'carei-reservation' ),
'manage_options',
'carei-protection-packages',
array( $this, 'render_protection_packages_page' )
);
}
public function render_protection_packages_page() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Brak uprawnień.', 'carei-reservation' ) );
}
$data = self::get_protection_packages();
$saved = isset( $_GET['carei_saved'] ) && $_GET['carei_saved'] === '1';
?>
<div class="wrap">
<h1><?php esc_html_e( 'Pakiety ochronne', 'carei-reservation' ); ?></h1>
<p><?php echo wp_kses( __( 'Konfiguracja pakietów wyświetlanych w sekcji <strong>Pakiety ochronne</strong> formularza rezerwacji. Cena podawana jest za dobę — total = cena × liczba dób rezerwacji.', 'carei-reservation' ), array( 'strong' => array() ) ); ?></p>
<?php if ( $saved ) : ?>
<div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Zapisano.', 'carei-reservation' ); ?></p></div>
<?php endif; ?>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="carei-protection-form">
<input type="hidden" name="action" value="carei_save_protection_packages">
<?php wp_nonce_field( 'carei_protection_packages', 'carei_protection_nonce' ); ?>
<?php foreach ( array( 'soft' => 'SOFT', 'premium' => 'PREMIUM' ) as $key => $label ) :
$pkg = $data[ $key ];
?>
<div class="carei-protection-card">
<h2><?php echo esc_html( sprintf( __( 'Pakiet %s', 'carei-reservation' ), $label ) ); ?></h2>
<table class="form-table">
<tr>
<th><label for="carei_<?php echo esc_attr( $key ); ?>_name"><?php esc_html_e( 'Nazwa wyświetlana (PL)', 'carei-reservation' ); ?></label></th>
<td><input type="text" id="carei_<?php echo esc_attr( $key ); ?>_name" name="packages[<?php echo esc_attr( $key ); ?>][name]" value="<?php echo esc_attr( $pkg['name'] ); ?>" class="regular-text" required></td>
</tr>
<tr>
<th><label for="carei_<?php echo esc_attr( $key ); ?>_name_en"><?php esc_html_e( 'Nazwa wyświetlana (EN)', 'carei-reservation' ); ?></label></th>
<td>
<input type="text" id="carei_<?php echo esc_attr( $key ); ?>_name_en" name="packages[<?php echo esc_attr( $key ); ?>][name_en]" value="<?php echo esc_attr( $pkg['name_en'] ); ?>" class="regular-text" placeholder="<?php echo esc_attr__( 'Np. SOFT Protection', 'carei-reservation' ); ?>">
<p class="description"><?php esc_html_e( 'Puste = fallback do wersji polskiej.', 'carei-reservation' ); ?></p>
</td>
</tr>
<tr>
<th><label for="carei_<?php echo esc_attr( $key ); ?>_price"><?php esc_html_e( 'Cena za dobę (zł)', 'carei-reservation' ); ?></label></th>
<td><input type="number" id="carei_<?php echo esc_attr( $key ); ?>_price" name="packages[<?php echo esc_attr( $key ); ?>][pricePerDay]" value="<?php echo esc_attr( $pkg['pricePerDay'] ); ?>" min="0" step="0.01" class="small-text" required></td>
</tr>
<tr>
<th><?php esc_html_e( 'Status', 'carei-reservation' ); ?></th>
<td><label><input type="checkbox" name="packages[<?php echo esc_attr( $key ); ?>][active]" value="1" <?php checked( $pkg['active'] ); ?>> <?php esc_html_e( 'Aktywny (widoczny w modalu)', 'carei-reservation' ); ?></label></td>
</tr>
<tr>
<th><label for="carei_<?php echo esc_attr( $key ); ?>_desc"><?php esc_html_e( 'Opis / zakres usług (PL)', 'carei-reservation' ); ?></label></th>
<td><textarea id="carei_<?php echo esc_attr( $key ); ?>_desc" name="packages[<?php echo esc_attr( $key ); ?>][description]" rows="3" cols="60" class="large-text"><?php echo esc_textarea( $pkg['description'] ); ?></textarea></td>
</tr>
<tr>
<th><label for="carei_<?php echo esc_attr( $key ); ?>_desc_en"><?php esc_html_e( 'Opis / zakres usług (EN)', 'carei-reservation' ); ?></label></th>
<td>
<textarea id="carei_<?php echo esc_attr( $key ); ?>_desc_en" name="packages[<?php echo esc_attr( $key ); ?>][description_en]" rows="3" cols="60" class="large-text" placeholder="<?php echo esc_attr__( 'Basic protection package...', 'carei-reservation' ); ?>"><?php echo esc_textarea( $pkg['description_en'] ); ?></textarea>
<p class="description"><?php esc_html_e( 'Puste = fallback do wersji polskiej.', 'carei-reservation' ); ?></p>
</td>
</tr>
</table>
</div>
<?php endforeach; ?>
<?php submit_button( __( 'Zapisz pakiety', 'carei-reservation' ) ); ?>
</form>
</div>
<style>
.carei-protection-card { background:#fff; border:1px solid #c3c4c7; border-left:4px solid #2F2482; padding:10px 20px; margin:20px 0; }
.carei-protection-card h2 { margin-top:10px; color:#2F2482; }
</style>
<?php
}
public function handle_protection_packages_save() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Brak uprawnień.', 'carei-reservation' ) );
}
if ( ! isset( $_POST['carei_protection_nonce'] ) || ! wp_verify_nonce( $_POST['carei_protection_nonce'], 'carei_protection_packages' ) ) {
wp_die( esc_html__( 'Nieprawidłowy token.', 'carei-reservation' ) );
}
$input = isset( $_POST['packages'] ) && is_array( $_POST['packages'] ) ? $_POST['packages'] : array();
$defaults = self::get_protection_packages_defaults();
$clean = array();
foreach ( $defaults as $key => $def ) {
$raw = isset( $input[ $key ] ) && is_array( $input[ $key ] ) ? $input[ $key ] : array();
$name = isset( $raw['name'] ) ? sanitize_text_field( wp_unslash( $raw['name'] ) ) : $def['name'];
$name_en = isset( $raw['name_en'] ) ? sanitize_text_field( wp_unslash( $raw['name_en'] ) ) : '';
$price = isset( $raw['pricePerDay'] ) ? (float) $raw['pricePerDay'] : 0;
if ( $price < 0 ) { $price = 0; }
$active = ! empty( $raw['active'] );
$desc = isset( $raw['description'] ) ? sanitize_textarea_field( wp_unslash( $raw['description'] ) ) : '';
$desc_en = isset( $raw['description_en'] ) ? sanitize_textarea_field( wp_unslash( $raw['description_en'] ) ) : '';
$clean[ $key ] = array(
'name' => $name !== '' ? $name : $def['name'],
'name_en' => $name_en,
'pricePerDay' => $price,
'active' => $active,
'description' => $desc,
'description_en' => $desc_en,
);
}
update_option( self::PROTECTION_OPTION, $clean );
$redirect = add_query_arg(
array(
'post_type' => self::POST_TYPE,
'page' => 'carei-protection-packages',
'carei_saved' => '1',
),
admin_url( 'edit.php' )
);
wp_safe_redirect( $redirect );
exit;
}
public function register_post_type() {
register_post_type( self::POST_TYPE, array(
'labels' => array(
'name' => __( 'Rezerwacje', 'carei-reservation' ),
'singular_name' => __( 'Rezerwacja', 'carei-reservation' ),
'menu_name' => __( 'Rezerwacje', 'carei-reservation' ),
'all_items' => __( 'Wszystkie rezerwacje', 'carei-reservation' ),
'view_item' => __( 'Zobacz rezerwację', 'carei-reservation' ),
'edit_item' => __( 'Szczegóły rezerwacji', 'carei-reservation' ),
'search_items' => __( 'Szukaj rezerwacji', 'carei-reservation' ),
'not_found' => __( 'Nie znaleziono rezerwacji', 'carei-reservation' ),
'not_found_in_trash' => __( 'Brak rezerwacji w koszu', 'carei-reservation' ),
),
'public' => false,
'show_ui' => true,
'show_in_menu' => true,
'menu_icon' => 'dashicons-car',
'menu_position' => 26,
'supports' => array( 'title' ),
'capability_type' => 'post',
'has_archive' => false,
'hierarchical' => false,
'show_in_rest' => false,
) );
}
// ─── Admin Columns ──────────────────────────────────────────
public function admin_columns( $columns ) {
return array(
'cb' => '<input type="checkbox" />',
'reservation_no' => __( 'Nr rezerwacji', 'carei-reservation' ),
'client' => __( 'Klient', 'carei-reservation' ),
'segment' => __( 'Segment', 'carei-reservation' ),
'dates' => __( 'Daty', 'carei-reservation' ),
'branch' => __( 'Oddział', 'carei-reservation' ),
'carei_status' => __( 'Status', 'carei-reservation' ),
'date' => __( 'Data', 'carei-reservation' ),
);
}
public function render_column( $column, $post_id ) {
switch ( $column ) {
case 'reservation_no':
$no = get_post_meta( $post_id, self::META_PREFIX . 'reservation_no', true ) ?: '—';
printf(
'<a class="row-title" href="%s"><strong>%s</strong></a>',
esc_url( get_edit_post_link( $post_id ) ),
esc_html( $no )
);
break;
case 'client':
$first = get_post_meta( $post_id, self::META_PREFIX . 'first_name', true );
$last = get_post_meta( $post_id, self::META_PREFIX . 'last_name', true );
echo esc_html( trim( $first . ' ' . $last ) ?: '—' );
break;
case 'segment':
echo esc_html( get_post_meta( $post_id, self::META_PREFIX . 'segment', true ) ?: '—' );
break;
case 'dates':
$from = get_post_meta( $post_id, self::META_PREFIX . 'date_from', true );
$to = get_post_meta( $post_id, self::META_PREFIX . 'date_to', true );
if ( $from && $to ) {
$from_fmt = date_i18n( 'd.m.Y H:i', strtotime( $from ) );
$to_fmt = date_i18n( 'd.m.Y H:i', strtotime( $to ) );
echo esc_html( $from_fmt . ' — ' . $to_fmt );
} else {
echo '—';
}
break;
case 'branch':
echo esc_html( get_post_meta( $post_id, self::META_PREFIX . 'pickup_branch', true ) ?: '—' );
break;
case 'carei_status':
$status = get_post_meta( $post_id, self::META_PREFIX . 'status', true ) ?: 'nowe';
$info = isset( self::$statuses[ $status ] ) ? self::$statuses[ $status ] : self::$statuses['nowe'];
printf(
'<span class="carei-status-badge" style="background:%s;">%s</span>',
esc_attr( $info['color'] ),
esc_html( self::get_status_label( $status ) )
);
break;
}
}
// ─── Status Filter ──────────────────────────────────────────
public function status_filter_dropdown( $post_type ) {
if ( $post_type !== self::POST_TYPE ) {
return;
}
$current = isset( $_GET['carei_status'] ) ? sanitize_text_field( $_GET['carei_status'] ) : '';
echo '<select name="carei_status">';
echo '<option value="">' . esc_html__( 'Wszystkie statusy', 'carei-reservation' ) . '</option>';
foreach ( self::$statuses as $key => $info ) {
printf(
'<option value="%s"%s>%s</option>',
esc_attr( $key ),
selected( $current, $key, false ),
esc_html( self::get_status_label( $key ) )
);
}
echo '</select>';
}
public function filter_by_status( $query ) {
if ( ! is_admin() || ! $query->is_main_query() ) {
return;
}
if ( ( $query->get( 'post_type' ) !== self::POST_TYPE ) ) {
return;
}
if ( ! empty( $_GET['carei_status'] ) ) {
$status = sanitize_text_field( $_GET['carei_status'] );
if ( isset( self::$statuses[ $status ] ) ) {
$query->set( 'meta_query', array(
array(
'key' => self::META_PREFIX . 'status',
'value' => $status,
),
) );
}
}
// Default sort by date desc
if ( ! $query->get( 'orderby' ) ) {
$query->set( 'orderby', 'date' );
$query->set( 'order', 'DESC' );
}
}
// ─── Meta Box ───────────────────────────────────────────────
public function add_meta_boxes() {
add_meta_box(
'carei_reservation_details',
__( 'Szczegóły rezerwacji', 'carei-reservation' ),
array( $this, 'render_meta_box' ),
self::POST_TYPE,
'normal',
'high'
);
}
public function render_meta_box( $post ) {
wp_nonce_field( 'carei_save_reservation', 'carei_reservation_nonce' );
$meta = array(
'reservation_no' => get_post_meta( $post->ID, self::META_PREFIX . 'reservation_no', true ),
'reservation_id' => get_post_meta( $post->ID, self::META_PREFIX . 'reservation_id', true ),
'customer_id' => get_post_meta( $post->ID, self::META_PREFIX . 'customer_id', true ),
'segment' => get_post_meta( $post->ID, self::META_PREFIX . 'segment', true ),
'date_from' => get_post_meta( $post->ID, self::META_PREFIX . 'date_from', true ),
'date_to' => get_post_meta( $post->ID, self::META_PREFIX . 'date_to', true ),
'pickup_branch' => get_post_meta( $post->ID, self::META_PREFIX . 'pickup_branch', true ),
'return_branch' => get_post_meta( $post->ID, self::META_PREFIX . 'return_branch', true ),
'first_name' => get_post_meta( $post->ID, self::META_PREFIX . 'first_name', true ),
'last_name' => get_post_meta( $post->ID, self::META_PREFIX . 'last_name', true ),
'email' => get_post_meta( $post->ID, self::META_PREFIX . 'email', true ),
'phone' => get_post_meta( $post->ID, self::META_PREFIX . 'phone', true ),
'pesel' => get_post_meta( $post->ID, self::META_PREFIX . 'pesel', true ),
'address' => get_post_meta( $post->ID, self::META_PREFIX . 'address', true ),
'extras' => get_post_meta( $post->ID, self::META_PREFIX . 'extras', true ),
'comments' => get_post_meta( $post->ID, self::META_PREFIX . 'comments', true ),
'status' => get_post_meta( $post->ID, self::META_PREFIX . 'status', true ) ?: 'nowe',
'protection' => get_post_meta( $post->ID, self::META_PREFIX . 'protection_package', true ),
);
$protection = $meta['protection'] ? json_decode( $meta['protection'], true ) : null;
$protection_str = '';
if ( is_array( $protection ) && ! empty( $protection['name'] ) ) {
$name = isset( $protection['name'] ) ? $protection['name'] : '';
$price = isset( $protection['pricePerDay'] ) ? (float) $protection['pricePerDay'] : 0;
$days = isset( $protection['days'] ) ? (int) $protection['days'] : 0;
$total = isset( $protection['total'] ) ? (float) $protection['total'] : ( $price * $days );
$protection_str = sprintf(
/* translators: 1: package name, 2: price per day, 3: number of days, 4: total */
__( '%1$s — %2$s zł/doba × %3$d = %4$s zł', 'carei-reservation' ),
$name,
number_format( $price, 2, ',', ' ' ),
$days,
number_format( $total, 2, ',', ' ' )
);
}
$address = $meta['address'] ? json_decode( $meta['address'], true ) : null;
$address_str = '';
if ( $address ) {
$parts = array_filter( array(
isset( $address['street'] ) ? $address['street'] : '',
isset( $address['zipCode'] ) ? $address['zipCode'] : '',
isset( $address['city'] ) ? $address['city'] : '',
) );
$address_str = implode( ', ', $parts );
}
$extras = $meta['extras'] ? json_decode( $meta['extras'], true ) : array();
$extras_str = '';
if ( is_array( $extras ) && ! empty( $extras ) ) {
$names = array_map( function ( $e ) {
return isset( $e['name'] ) ? $e['name'] : '';
}, $extras );
$extras_str = implode( ', ', array_filter( $names ) );
}
$from_fmt = $meta['date_from'] ? date_i18n( 'd.m.Y H:i', strtotime( $meta['date_from'] ) ) : '—';
$to_fmt = $meta['date_to'] ? date_i18n( 'd.m.Y H:i', strtotime( $meta['date_to'] ) ) : '—';
?>
<table class="carei-meta-table">
<tr><th><?php esc_html_e( 'Nr rezerwacji', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['reservation_no'] ?: '—' ); ?></td></tr>
<tr><th><?php esc_html_e( 'ID rezerwacji (Softra)', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['reservation_id'] ?: '—' ); ?></td></tr>
<tr><th><?php esc_html_e( 'ID klienta (Softra)', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['customer_id'] ?: '—' ); ?></td></tr>
<tr class="carei-meta-divider"><td colspan="2"><hr></td></tr>
<tr><th><?php esc_html_e( 'Segment', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['segment'] ?: '—' ); ?></td></tr>
<tr><th><?php esc_html_e( 'Data od', 'carei-reservation' ); ?></th><td><?php echo esc_html( $from_fmt ); ?></td></tr>
<tr><th><?php esc_html_e( 'Data do', 'carei-reservation' ); ?></th><td><?php echo esc_html( $to_fmt ); ?></td></tr>
<tr><th><?php esc_html_e( 'Oddział odbioru', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['pickup_branch'] ?: '—' ); ?></td></tr>
<tr><th><?php esc_html_e( 'Oddział zwrotu', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['return_branch'] ?: $meta['pickup_branch'] ?: '—' ); ?></td></tr>
<tr class="carei-meta-divider"><td colspan="2"><hr></td></tr>
<tr><th><?php esc_html_e( 'Imię', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['first_name'] ?: '—' ); ?></td></tr>
<tr><th><?php esc_html_e( 'Nazwisko', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['last_name'] ?: '—' ); ?></td></tr>
<tr><th><?php esc_html_e( 'Email', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['email'] ?: '—' ); ?></td></tr>
<tr><th><?php esc_html_e( 'Telefon', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['phone'] ?: '—' ); ?></td></tr>
<tr><th><?php esc_html_e( 'PESEL', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['pesel'] ?: '—' ); ?></td></tr>
<tr><th><?php esc_html_e( 'Adres', 'carei-reservation' ); ?></th><td><?php echo esc_html( $address_str ?: '—' ); ?></td></tr>
<tr class="carei-meta-divider"><td colspan="2"><hr></td></tr>
<tr><th><?php esc_html_e( 'Opcje dodatkowe', 'carei-reservation' ); ?></th><td><?php echo esc_html( $extras_str ?: __( 'Brak', 'carei-reservation' ) ); ?></td></tr>
<tr><th><?php esc_html_e( 'Pakiet ochronny', 'carei-reservation' ); ?></th><td><?php echo esc_html( $protection_str ?: __( 'Brak', 'carei-reservation' ) ); ?></td></tr>
<tr><th><?php esc_html_e( 'Wiadomość', 'carei-reservation' ); ?></th><td><?php echo esc_html( $meta['comments'] ?: '—' ); ?></td></tr>
<tr class="carei-meta-divider"><td colspan="2"><hr></td></tr>
<tr>
<th><?php esc_html_e( 'Status', 'carei-reservation' ); ?></th>
<td>
<select name="carei_status">
<?php foreach ( self::$statuses as $key => $info ) : ?>
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $meta['status'], $key ); ?>>
<?php echo esc_html( self::get_status_label( $key ) ); ?>
</option>
<?php endforeach; ?>
</select>
</td>
</tr>
</table>
<?php
}
public function save_meta_box( $post_id, $post ) {
if ( ! isset( $_POST['carei_reservation_nonce'] ) ) {
return;
}
if ( ! wp_verify_nonce( $_POST['carei_reservation_nonce'], 'carei_save_reservation' ) ) {
return;
}
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
if ( isset( $_POST['carei_status'] ) ) {
$status = sanitize_text_field( $_POST['carei_status'] );
if ( isset( self::$statuses[ $status ] ) ) {
update_post_meta( $post_id, self::META_PREFIX . 'status', $status );
}
}
}
// ─── Auto Mark Read ─────────────────────────────────────────
public function auto_mark_read( $post ) {
if ( $post->post_type !== self::POST_TYPE ) {
return;
}
$status = get_post_meta( $post->ID, self::META_PREFIX . 'status', true );
if ( $status === 'nowe' ) {
update_post_meta( $post->ID, self::META_PREFIX . 'status', 'przeczytane' );
}
}
// ─── Admin Styles ───────────────────────────────────────────
public function admin_styles() {
$screen = get_current_screen();
if ( ! $screen || $screen->post_type !== self::POST_TYPE ) {
return;
}
?>
<style>
.carei-status-badge {
display: inline-block;
padding: 4px 10px;
border-radius: 12px;
color: #fff;
font-size: 12px;
font-weight: 600;
line-height: 1.4;
white-space: nowrap;
}
.carei-meta-table {
width: 100%;
border-collapse: collapse;
}
.carei-meta-table th {
text-align: left;
padding: 8px 12px;
width: 180px;
color: #1d2327;
font-weight: 600;
vertical-align: top;
}
.carei-meta-table td {
padding: 8px 12px;
color: #50575e;
}
.carei-meta-table tr:nth-child(odd):not(.carei-meta-divider) {
background: #f9f9f9;
}
.carei-meta-divider td {
padding: 4px 0;
}
.carei-meta-divider hr {
border: none;
border-top: 1px solid #e0e0e0;
margin: 0;
}
.carei-meta-table select {
min-width: 180px;
}
/* Hide title input — auto-generated */
#post-body-content #titlediv { display: none; }
</style>
<?php
}
// ─── Save Reservation (called from REST proxy) ──────────────
public static function save_reservation( $booking_data, $api_result ) {
$reservation_no = isset( $api_result['reservationNo'] ) ? $api_result['reservationNo'] : '';
$reservation_id = isset( $api_result['reservationId'] ) ? $api_result['reservationId'] : '';
$driver = array();
if ( isset( $booking_data['drivers'] ) && is_array( $booking_data['drivers'] ) && ! empty( $booking_data['drivers'] ) ) {
$driver = $booking_data['drivers'][0];
}
$first_name = isset( $driver['firstName'] ) ? $driver['firstName'] : '';
$last_name = isset( $driver['lastName'] ) ? $driver['lastName'] : '';
$title = sprintf(
/* translators: 1: reservation number or ID, 2: first name, 3: last name */
__( 'Rezerwacja #%1$s — %2$s %3$s', 'carei-reservation' ),
$reservation_no ?: $reservation_id,
$first_name,
$last_name
);
$post_id = wp_insert_post( array(
'post_type' => self::POST_TYPE,
'post_title' => trim( $title ),
'post_status' => 'publish',
), true );
if ( is_wp_error( $post_id ) ) {
error_log( 'Carei: Failed to save reservation — ' . $post_id->get_error_message() );
return false;
}
$pickup_branch = '';
if ( isset( $booking_data['pickUpLocation']['branchName'] ) ) {
$pickup_branch = $booking_data['pickUpLocation']['branchName'];
}
$return_branch = '';
if ( isset( $booking_data['returnLocation']['branchName'] ) ) {
$return_branch = $booking_data['returnLocation']['branchName'];
}
$segment = '';
if ( isset( $booking_data['carParameters']['categoryName'] ) ) {
$segment = $booking_data['carParameters']['categoryName'];
}
$meta = array(
'reservation_no' => $reservation_no,
'reservation_id' => $reservation_id,
'customer_id' => isset( $booking_data['customerId'] ) ? $booking_data['customerId'] : '',
'segment' => $segment,
'date_from' => isset( $booking_data['dateFrom'] ) ? $booking_data['dateFrom'] : '',
'date_to' => isset( $booking_data['dateTo'] ) ? $booking_data['dateTo'] : '',
'pickup_branch' => $pickup_branch,
'return_branch' => $return_branch,
'first_name' => $first_name,
'last_name' => $last_name,
'email' => isset( $driver['email'] ) ? $driver['email'] : '',
'phone' => isset( $driver['phone'] ) ? $driver['phone'] : '',
'pesel' => isset( $driver['pesel'] ) ? $driver['pesel'] : '',
'address' => isset( $driver['address'] ) ? wp_json_encode( $driver['address'] ) : '',
'extras' => isset( $booking_data['priceItems'] ) ? wp_json_encode( $booking_data['priceItems'] ) : '',
'comments' => isset( $booking_data['comments'] ) ? $booking_data['comments'] : '',
'status' => 'nowe',
'raw_response' => wp_json_encode( $api_result ),
'protection_package' => ( isset( $booking_data['protectionPackage'] ) && is_array( $booking_data['protectionPackage'] ) )
? wp_json_encode( $booking_data['protectionPackage'] )
: '',
);
foreach ( $meta as $key => $value ) {
update_post_meta( $post_id, self::META_PREFIX . $key, $value );
}
return $post_id;
}
}