This commit is contained in:
2026-03-31 00:18:53 +02:00
parent b6496d1e14
commit 6317bb3d20
19 changed files with 147 additions and 57 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -701,8 +701,8 @@
"widgets": {
"apartaments.php": {
"type": "-",
"size": 14014,
"lmtime": 1774271189698,
"size": 15744,
"lmtime": 1774522885487,
"modified": false
},
"parking-spots.php": {

BIN
wp-admin/.DS_Store vendored

Binary file not shown.

BIN
wp-content/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +1,4 @@
document.addEventListener('DOMContentLoaded', function () {
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.apartament-gallery-swiper').forEach(function (el) {
new Swiper(el, {
slidesPerView: 1,
@@ -21,6 +21,50 @@ document.addEventListener('DOMContentLoaded', function () {
// Historia cen
document.addEventListener('DOMContentLoaded', function () {
function formatPrice(value) {
if (value === null || value === undefined) {
return '';
}
var normalized = String(value)
.replace(/\u00a0/g, '')
.replace(/\s+/g, '')
.replace(/zł|zl/gi, '')
.replace(/[^0-9,.-]/g, '');
if (!normalized) {
return '';
}
var lastComma = normalized.lastIndexOf(',');
var lastDot = normalized.lastIndexOf('.');
if (lastComma !== -1 && lastDot !== -1) {
if (lastComma > lastDot) {
normalized = normalized.replace(/\./g, '').replace(',', '.');
} else {
normalized = normalized.replace(/,/g, '');
}
} else if (lastComma !== -1) {
normalized = normalized.replace(/\./g, '').replace(',', '.');
} else {
var dotParts = normalized.split('.');
if (dotParts.length > 2) {
normalized = dotParts.slice(0, -1).join('') + '.' + dotParts[dotParts.length - 1];
}
}
var amount = Number(normalized);
if (Number.isNaN(amount)) {
return String(value).trim();
}
return amount.toLocaleString('pl-PL', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
}
var overlay = document.getElementById('price-history-overlay');
var closeBtn = document.getElementById('price-history-close');
var elTitle = document.getElementById('price-history-title');
@@ -49,20 +93,16 @@ document.addEventListener('DOMContentLoaded', function () {
document.body.style.overflow = '';
}
// Zamknij przyciskiem X
closeBtn.addEventListener('click', closePopup);
// Zamknij klikając na overlay (poza modalem)
overlay.addEventListener('click', function (e) {
if (e.target === overlay) closePopup();
});
// Zamknij klawiszem Escape
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && overlay.classList.contains('is-open')) closePopup();
});
// Kliknięcie w przycisk Historia cen (apartamenty)
document.querySelectorAll('.btn-historia-cen').forEach(function (btn) {
btn.addEventListener('click', function () {
var postId = this.dataset.postId;
@@ -99,8 +139,8 @@ document.addEventListener('DOMContentLoaded', function () {
var d = json.data;
elTitle.textContent = d.title || '';
elPrice.textContent = d.price ? d.price + ' zł' : '—';
elPriceM2.textContent = d.price_m2 ? d.price_m2 + ' zł' : '—';
elPrice.textContent = d.price ? formatPrice(d.price) + ' zł' : '—';
elPriceM2.textContent = d.price_m2 ? formatPrice(d.price_m2) + ' zł' : '—';
if (!d.history || d.history.length === 0) {
elTbody.innerHTML = '<tr><td colspan="3">Brak historii cen</td></tr>';
@@ -110,8 +150,8 @@ document.addEventListener('DOMContentLoaded', function () {
elTbody.innerHTML = d.history.map(function (row) {
return '<tr>' +
'<td>' + (row.recorded_at || '') + '</td>' +
'<td>' + (row.price_m2 ? row.price_m2 + ' zł/m²' : '—') + '</td>' +
'<td>' + (row.price ? row.price + ' zł' : '—') + '</td>' +
'<td>' + (row.price_m2 ? formatPrice(row.price_m2) + ' zł/m²' : '—') + '</td>' +
'<td>' + (row.price ? formatPrice(row.price) + ' zł' : '—') + '</td>' +
'</tr>';
}).join('');
})
@@ -121,7 +161,6 @@ document.addEventListener('DOMContentLoaded', function () {
});
});
// Kliknięcie w przycisk Historia cen (miejsca postojowe)
document.querySelectorAll('.btn-parking-historia-cen').forEach(function (btn) {
btn.addEventListener('click', function () {
var parkingType = this.dataset.parkingType;
@@ -158,8 +197,8 @@ document.addEventListener('DOMContentLoaded', function () {
var d = json.data;
elTitle.textContent = d.title || '';
elPrice.textContent = d.price ? d.price + ' zł' : '—';
elPriceM2.textContent = d.price_m2 ? d.price_m2 + ' zł' : '—';
elPrice.textContent = d.price ? formatPrice(d.price) + ' zł' : '—';
elPriceM2.textContent = d.price_m2 ? formatPrice(d.price_m2) + ' zł' : '—';
if (!d.history || d.history.length === 0) {
elTbody.innerHTML = '<tr><td colspan="3">Brak historii cen</td></tr>';
@@ -169,8 +208,8 @@ document.addEventListener('DOMContentLoaded', function () {
elTbody.innerHTML = d.history.map(function (row) {
return '<tr>' +
'<td>' + (row.recorded_at || '') + '</td>' +
'<td>' + (row.price_m2 ? row.price_m2 + ' zł/m²' : '—') + '</td>' +
'<td>' + (row.price ? row.price + ' zł' : '—') + '</td>' +
'<td>' + (row.price_m2 ? formatPrice(row.price_m2) + ' zł/m²' : '—') + '</td>' +
'<td>' + (row.price ? formatPrice(row.price) + ' zł' : '—') + '</td>' +
'</tr>';
}).join('');
})

View File

@@ -16,6 +16,53 @@ if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Formatuje kwotÄ™ do postaci 1 000 000,00.
*
* @param mixed $price Surowa wartość ceny.
* @return string
*/
function elementor_addon_format_price( $price ) {
if ( '' === $price || null === $price ) {
return '';
}
$value = wp_strip_all_tags( (string) $price );
$value = str_replace( [ "\xc2\xa0", ' ', 'zł', 'zl' ], '', $value );
$value = preg_replace( '/[^\d,.\-]/u', '', $value );
if ( '' === $value || null === $value ) {
return '';
}
$last_comma = strrpos( $value, ',' );
$last_dot = strrpos( $value, '.' );
if ( false !== $last_comma && false !== $last_dot ) {
if ( $last_comma > $last_dot ) {
$value = str_replace( '.', '', $value );
$value = str_replace( ',', '.', $value );
} else {
$value = str_replace( ',', '', $value );
}
} elseif ( false !== $last_comma ) {
$value = str_replace( '.', '', $value );
$value = str_replace( ',', '.', $value );
} else {
$parts = explode( '.', $value );
if ( count( $parts ) > 2 ) {
$decimal = array_pop( $parts );
$value = implode( '', $parts ) . '.' . $decimal;
}
}
if ( ! is_numeric( $value ) ) {
return trim( (string) $price );
}
return number_format_i18n( (float) $value, 2 );
}
/**
* Register widget files
*/
@@ -88,7 +135,7 @@ function elementor_addon_register_assets() {
add_action( 'wp_enqueue_scripts', 'elementor_addon_register_assets' );
/**
* Przekaż dane do JS (ajaxUrl + nonce) gdy skrypt jest enqueue'owany przez widget.
* PrzekaĹĽ dane do JS (ajaxUrl + nonce) gdy skrypt jest enqueue'owany przez widget.
*/
function elementor_addon_localize_scripts() {
wp_localize_script( 'elementor-addon-main-js', 'apartamentsData', [
@@ -99,8 +146,8 @@ function elementor_addon_localize_scripts() {
add_action( 'wp_enqueue_scripts', 'elementor_addon_localize_scripts', 20 );
/**
* Włącza wsparcie "Atrybuty wpisu" (menu_order) dla CPT apartamenty,
* aby można było ręcznie ustawiać kolejność.
* Włącza wsparcie "Atrybuty wpisu" (menu_order) dla CPT apartamenty,
* aby można było ręcznie ustawiać kolejność.
*/
function elementor_addon_enable_apartamenty_menu_order( $args, $post_type ) {
if ( 'apartamenty' !== $post_type ) {
@@ -125,11 +172,11 @@ function elementor_addon_enable_apartamenty_menu_order( $args, $post_type ) {
add_filter( 'register_post_type_args', 'elementor_addon_enable_apartamenty_menu_order', 20, 2 );
// ===========================================================
// HISTORIA CEN TABELA DB
// HISTORIA CEN — TABELA DB
// ===========================================================
/**
* Tworzy tabelę wp_price_history jeśli nie istnieje lub wersja DB jest stara.
* Tworzy tabelę wp_price_history jeśli nie istnieje lub wersja DB jest stara.
*/
function elementor_addon_create_price_history_table() {
global $wpdb;
@@ -154,7 +201,7 @@ function elementor_addon_create_price_history_table() {
}
/**
* Tworzy tabelę wp_parking_price_history dla miejsc postojowych.
* Tworzy tabelÄ™ wp_parking_price_history dla miejsc postojowych.
*/
function elementor_addon_create_parking_price_history_table() {
global $wpdb;
@@ -188,7 +235,7 @@ function elementor_addon_create_tables() {
register_activation_hook( __FILE__, 'elementor_addon_create_tables' );
/**
* Sprawdza wersję DB przy każdym init i tworzy tabele jeśli brakuje.
* Sprawdza wersję DB przy każdym init i tworzy tabele jeśli brakuje.
*/
function elementor_addon_maybe_update_db() {
if ( get_option( 'elementor_addon_db_version' ) !== '1.1' ) {
@@ -198,11 +245,11 @@ function elementor_addon_maybe_update_db() {
add_action( 'init', 'elementor_addon_maybe_update_db' );
// ===========================================================
// HISTORIA CEN CRON DZIENNY
// HISTORIA CEN — CRON DZIENNY
// ===========================================================
/**
* Rejestruje WP Cron jeśli jeszcze nie zaplanowany.
* Rejestruje WP Cron jeśli jeszcze nie zaplanowany.
*/
function elementor_addon_schedule_cron() {
if ( ! wp_next_scheduled( 'apartamenty_record_prices' ) ) {
@@ -212,8 +259,8 @@ function elementor_addon_schedule_cron() {
add_action( 'wp', 'elementor_addon_schedule_cron' );
/**
* Zapisuje aktualne ceny wszystkich apartamentów do tabeli historii.
* Używa INSERT IGNORE jeden rekord na apartament na dzień.
* Zapisuje aktualne ceny wszystkich apartamentĂłw do tabeli historii.
* Używa INSERT IGNORE — jeden rekord na apartament na dzień.
*/
function elementor_addon_record_prices() {
global $wpdb;
@@ -252,7 +299,7 @@ function elementor_addon_record_prices() {
);
}
// Miejsca postojowe zapis cen z ACF options (pola w grupach)
// Miejsca postojowe - zapis cen z ACF options (pola w grupach)
$parking_table = $wpdb->prefix . 'parking_price_history';
$parking_groups = [
'zwykle' => 'miejsce_postojowe_zwykle',
@@ -281,7 +328,7 @@ function elementor_addon_record_prices() {
add_action( 'apartamenty_record_prices', 'elementor_addon_record_prices' );
// ===========================================================
// HISTORIA CEN AJAX ENDPOINT
// HISTORIA CEN - AJAX ENDPOINT
// ===========================================================
/**
@@ -381,11 +428,11 @@ add_action( 'wp_ajax_parking_get_price_history', 'elementor_addon_get_parking_pr
add_action( 'wp_ajax_nopriv_parking_get_price_history', 'elementor_addon_get_parking_price_history_ajax' );
// ===========================================================
// JAWNOŚĆ CEN — XML ENDPOINTS
// JAWNOŚĆ CEN — XML ENDPOINTS
// ===========================================================
/**
* Rejestruje reguły przepisywania dla endpointów XML.
* Rejestruje reguły przepisywania dla endpointów XML.
*/
function apartamenty_xml_rewrite_rules() {
add_rewrite_rule( '^ceny-mieszkan\.(xml|md5)$', 'index.php?apartamenty_xml=$matches[1]', 'top' );
@@ -394,7 +441,7 @@ function apartamenty_xml_rewrite_rules() {
add_action( 'init', 'apartamenty_xml_rewrite_rules', 10 );
/**
* Dodaje query vars dla endpointów XML.
* Dodaje query vars dla endpointĂłw XML.
*/
function apartamenty_xml_query_vars( $vars ) {
$vars[] = 'apartamenty_xml';
@@ -404,8 +451,8 @@ function apartamenty_xml_query_vars( $vars ) {
add_filter( 'query_vars', 'apartamenty_xml_query_vars' );
/**
* Generuje XML z cenami wszystkich apartamentów.
* Wynik cachowany w transiencie na 1 godzinę.
* Generuje XML z cenami wszystkich apartamentĂłw.
* Wynik cachowany w transiencie na 1 godzinÄ™.
*
* @return string XML jako string
*/
@@ -485,7 +532,7 @@ function apartamenty_generate_price_xml() {
$parking_table = $wpdb->prefix . 'parking_price_history';
$parking_configs = [
'zwykle' => [
'label' => 'Miejsce postojowe zwykłe',
'label' => 'Miejsce postojowe zwykłe',
'group_name' => 'miejsce_postojowe_zwykle',
],
'rodzinne' => [
@@ -593,7 +640,7 @@ function apartamenty_generate_datagov_xml() {
}
/**
* Obsługuje żądania do endpointów XML — wysyła odpowiedź i kończy.
* Obsługuje żądania do endpointów XML — wysyła odpowiedź i kończy.
*/
function apartamenty_xml_template_redirect() {
$xml_type = get_query_var( 'apartamenty_xml' );
@@ -626,16 +673,16 @@ function apartamenty_xml_template_redirect() {
add_action( 'template_redirect', 'apartamenty_xml_template_redirect', 1 );
// ===========================================================
// JAWNOŚĆ CEN — STRONA ADMINISTRACYJNA
// JAWNOŚĆ CEN — STRONA ADMINISTRACYJNA
// ===========================================================
/**
* Rejestruje stronę Jawność Cen w menu Narzędzia wp-admin.
* Rejestruje stronę Jawność Cen w menu Narzędzia wp-admin.
*/
function apartamenty_jawnosc_cen_menu() {
add_management_page(
'Jawność Cen',
'Jawność Cen',
'Jawność Cen',
'Jawność Cen',
'manage_options',
'jawnosc-cen',
'apartamenty_jawnosc_cen_page'
@@ -644,17 +691,17 @@ function apartamenty_jawnosc_cen_menu() {
add_action( 'admin_menu', 'apartamenty_jawnosc_cen_menu' );
/**
* Renderuje stronę administracyjną z URL-ami do zgłoszenia.
* Renderuje stronę administracyjną z URL-ami do zgłoszenia.
*/
function apartamenty_jawnosc_cen_page() {
$url_ceny = esc_url( home_url( '/ceny-mieszkan.xml' ) );
$url_datagov = esc_url( home_url( '/dane-gov-pl.xml' ) );
?>
<div class="wrap">
<h1>Jawność Cen — Wyszyńskiego 12</h1>
<h1>Jawność Cen — Wyszyńskiego 12</h1>
<div class="notice notice-info">
<p>Dane aktualizowane codziennie przez WP Cron. Zgłoś URL katalogu dane.gov.pl do administratora portalu: <strong>kontakt@dane.gov.pl</strong></p>
<p>Dane aktualizowane codziennie przez WP Cron. Zgłoś URL katalogu dane.gov.pl do administratora portalu: <strong>kontakt@dane.gov.pl</strong></p>
</div>
<table class="widefat" style="max-width:800px; margin-top:20px;">
@@ -671,15 +718,15 @@ function apartamenty_jawnosc_cen_page() {
<td><code><?php echo $url_ceny; ?></code></td>
<td>
<button class="button" onclick="copyUrl('<?php echo esc_js( $url_ceny ); ?>')">Kopiuj URL</button>
<a href="<?php echo $url_ceny; ?>" target="_blank" class="button">Otwórz XML</a>
<a href="<?php echo $url_ceny; ?>" target="_blank" class="button">OtwĂłrz XML</a>
</td>
</tr>
<tr>
<td><strong>Katalog dane.gov.pl</strong><br><em>(zgłoś Ministerstwu)</em></td>
<td><strong>Katalog dane.gov.pl</strong><br><em>(zgłoś Ministerstwu)</em></td>
<td><code><?php echo $url_datagov; ?></code></td>
<td>
<button class="button button-primary" onclick="copyUrl('<?php echo esc_js( $url_datagov ); ?>')">Kopiuj URL</button>
<a href="<?php echo $url_datagov; ?>" target="_blank" class="button">Otwórz XML</a>
<a href="<?php echo $url_datagov; ?>" target="_blank" class="button">OtwĂłrz XML</a>
</td>
</tr>
</tbody>

View File

@@ -1,4 +1,4 @@
<?php
<?php
if (!defined('ABSPATH')) {
exit;
}
@@ -57,7 +57,6 @@ class Elementor_Apartaments extends \Elementor\Widget_Base {
'title' => 'ASC',
],
]);
if ( ! $apartaments->have_posts() ) {
echo '<p>Brak apartamentów.</p>';
return;
@@ -70,6 +69,8 @@ class Elementor_Apartaments extends \Elementor\Widget_Base {
$gallery = get_field('gallery');
$information = get_field('information');
$documents = get_field('documents');
$formatted_price = elementor_addon_format_price($information['price'] ?? '');
$formatted_price_m2 = elementor_addon_format_price($information['price_m2'] ?? '');
?>
<div class="apartament-card">
@@ -154,16 +155,16 @@ class Elementor_Apartaments extends \Elementor\Widget_Base {
<td class="apartament-card__info_table-value"><?php echo esc_html(!empty($information['ogrodek']) ? $information['ogrodek'] : $information['garden']); ?> m2</td>
</tr>
<?php endif; ?>
<?php if (!empty($information['price'])) : ?>
<?php if (!empty($formatted_price)) : ?>
<tr>
<td class="apartament-card__info_table-title">Cena</td>
<td class="apartament-card__info_table-value"><?php echo esc_html($information['price']); ?> zł</td>
<td class="apartament-card__info_table-value"><?php echo esc_html($formatted_price); ?> zł</td>
</tr>
<?php endif; ?>
<?php if (!empty($information['price_m2'])) : ?>
<?php if (!empty($formatted_price_m2)) : ?>
<tr>
<td class="apartament-card__info_table-title">Cena m2</td>
<td class="apartament-card__info_table-value"><?php echo esc_html($information['price_m2']); ?> zł</td>
<td class="apartament-card__info_table-value"><?php echo esc_html($formatted_price_m2); ?> zł</td>
</tr>
<?php endif; ?>
@@ -236,7 +237,7 @@ class Elementor_Apartaments extends \Elementor\Widget_Base {
<?php wp_reset_postdata(); ?>
</div>
<?php // Popup historia cen jeden globalny, wypełniany przez JS ?>
<?php // Popup historia cen - jeden globalny, wypełniany przez JS ?>
<div class="price-history-overlay" id="price-history-overlay" aria-hidden="true">
<div class="price-history-modal" role="dialog" aria-modal="true">
<button class="price-history-modal__close" id="price-history-close" aria-label="Zamknij">
@@ -266,3 +267,4 @@ class Elementor_Apartaments extends \Elementor\Widget_Base {
<?php
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
@@ -65,13 +65,14 @@ class Elementor_Parking_Spots extends \Elementor\Widget_Base {
<div class="parking-spots">
<?php foreach ( $parking_types as $type => $data ) : ?>
<?php $formatted_price = elementor_addon_format_price( $data['price'] ); ?>
<div class="parking-spot-card">
<div class="parking-spot-card__header">
<h3 class="parking-spot-card__title"><?php echo esc_html( $data['label'] ); ?></h3>
</div>
<?php if ( ! empty( $data['price'] ) ) : ?>
<?php if ( ! empty( $formatted_price ) ) : ?>
<div class="parking-spot-card__price">
<?php echo esc_html( $data['price'] ); ?> zł
<?php echo esc_html( $formatted_price ); ?> zł
</div>
<?php endif; ?>
<div class="parking-spot-card__history-btn btn-parking-historia-cen"
@@ -94,7 +95,7 @@ class Elementor_Parking_Spots extends \Elementor\Widget_Base {
</div>
<?php
// Popup historia cen renderuj tylko jeśli nie ma go w DOM (apartamenty mogą go już mieć)
// Popup historia cen - renderuj tylko jeśli nie ma go w DOM (apartamenty mogą go już mieć)
?>
<div class="price-history-overlay price-history-overlay--parking-fallback" id="price-history-overlay-parking" aria-hidden="true" style="display:none;">
<div class="price-history-modal" role="dialog" aria-modal="true">
@@ -149,3 +150,4 @@ class Elementor_Parking_Spots extends \Elementor\Widget_Base {
<?php
}
}

Binary file not shown.

BIN
wp-includes/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.