array( 'label' => 'Nowe', 'color' => '#2F2482' ),
'przeczytane' => array( 'label' => 'Przeczytane', 'color' => '#f59e0b' ),
'zrealizowane' => array( 'label' => 'Zrealizowane', 'color' => '#22c55e' ),
);
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' ) );
}
// ─── Protection Packages (SOFT / PREMIUM) ────────────────────
public static function get_protection_packages_defaults() {
return array(
'soft' => array(
'name' => 'Ubezpieczenie SOFT',
'pricePerDay' => 0,
'active' => true,
'description' => '',
),
'premium' => array(
'name' => 'Ubezpieczenie PREMIUM',
'pricePerDay' => 0,
'active' => true,
'description' => '',
),
);
}
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'],
'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'],
);
}
return $out;
}
public function register_protection_packages_page() {
add_submenu_page(
'edit.php?post_type=' . self::POST_TYPE,
'Pakiety ochronne',
'Pakiety ochronne',
'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( 'Brak uprawnień.' );
}
$data = self::get_protection_packages();
$saved = isset( $_GET['carei_saved'] ) && $_GET['carei_saved'] === '1';
?>
Pakiety ochronne
Konfiguracja pakietów wyświetlanych w sekcji Pakiety ochronne formularza rezerwacji. Cena podawana jest za dobę — total = cena × liczba dób rezerwacji.
$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'];
$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'] ) ) : '';
$clean[ $key ] = array(
'name' => $name !== '' ? $name : $def['name'],
'pricePerDay' => $price,
'active' => $active,
'description' => $desc,
);
}
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',
'singular_name' => 'Rezerwacja',
'menu_name' => 'Rezerwacje',
'all_items' => 'Wszystkie rezerwacje',
'view_item' => 'Zobacz rezerwację',
'edit_item' => 'Szczegóły rezerwacji',
'search_items' => 'Szukaj rezerwacji',
'not_found' => 'Nie znaleziono rezerwacji',
'not_found_in_trash' => 'Brak rezerwacji w koszu',
),
'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' => ' ',
'reservation_no' => 'Nr rezerwacji',
'client' => 'Klient',
'segment' => 'Segment',
'dates' => 'Daty',
'branch' => 'Oddział',
'carei_status' => 'Status',
'date' => 'Data',
);
}
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(
'%s ',
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(
'%s ',
esc_attr( $info['color'] ),
esc_html( $info['label'] )
);
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 '';
echo 'Wszystkie statusy ';
foreach ( self::$statuses as $key => $info ) {
printf(
'%s ',
esc_attr( $key ),
selected( $current, $key, false ),
esc_html( $info['label'] )
);
}
echo ' ';
}
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',
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( '%s — %s zł/doba × %d = %s zł', $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'] ) ) : '—';
?>
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;
}
?>
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;
}
}