Compare commits

...

2 Commits

Author SHA1 Message Date
4de5479c41 ver. 0.309: ApiloLogger + cache-busting CSS/JS + poprawki UI
- ApiloLogger: logowanie operacji Apilo do pp_log z kontekstem JSON
- Cache-busting: ?ver=filemtime() dla CSS i JS w admin main-layout
- Fix: inicjalizacja $mdb przed SettingsRepository w admin/index.php
- Fix: rzutowanie (string) w ShopProductController::escapeHtml()
- UI: text-overflow ellipsis dla kategorii produktow + title tooltip
- JS: navigator.clipboard API w copyToClipboard() z fallbackiem
- CSS: uproszczenie .site-content, usuniecie .with-menu
- Migracja: pp_log + kolumny action, order_id, context

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 09:31:28 +01:00
f31630b69c build: update package v0.308
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:01:37 +01:00
18 changed files with 228 additions and 2042 deletions

View File

@@ -44,3 +44,7 @@ cron/temp/
# IDE
.vscode/
.serena/
# Cache testów
.phpunit.result.cache

View File

@@ -44,6 +44,15 @@ define( 'REDBEAN_MODEL_PREFIX', '' );
date_default_timezone_set( 'Europe/Warsaw' );
$mdb = new medoo( [
'database_type' => 'mysql',
'database_name' => $database['name'],
'server' => $database['host'],
'username' => $database['user'],
'password' => $database['password'],
'charset' => 'utf8'
] );
$settings = ( new \Domain\Settings\SettingsRepository( $mdb ) )->allSettings();
if ( file_exists( 'config.php' ) )
@@ -79,15 +88,6 @@ if ( !$lang = \Shared\Helpers\Helpers::get_session( 'lang-' . $lang_id ) )
\Shared\Helpers\Helpers::set_session( 'lang-' . $lang_id, $lang );
}
$mdb = new medoo( [
'database_type' => 'mysql',
'database_name' => $database['name'],
'server' => $database['host'],
'username' => $database['user'],
'password' => $database['password'],
'charset' => 'utf8'
] );
$user = \Shared\Helpers\Helpers::get_session( 'user', true );
\admin\App::update();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -455,23 +455,14 @@ body {
}
.site-content {
&.with-menu {
width: 100%;
@include respond-above(xs) {
width: calc(100% - 243px);
margin-left: 243px;
}
}
@include respond-below(md) {
margin-left: 0;
}
margin-left: 0;
background-color: #fff;
margin-left: 244px;
@include respond-above(xs) {
width: calc(100% - 243px);
margin-left: 243px;
}
.top-user {
text-align: right;
@@ -1750,33 +1741,16 @@ li.sort-collapsed.sort-hover div {
}
}
#table-products {
.product-categories {
display: block;
width: 100%;
text-wrap: wrap;
}
.product-categories {
display: block;
width: 100%;
text-wrap: wrap;
.product-name {
display: flex;
justify-content: space-between;
.duplicate-product {
margin-left: 15px;
}
}
.duplicate-product {
float: right;
font-size: 13px;
}
.btn-success {
color: #FFF !important;
&.btn-create-product {
margin-top: 5px;
}
&--cats {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 600px;
}
}
@@ -2104,6 +2078,10 @@ textarea.form-control {
}
.order-details {
.fa-copy {
cursor: pointer !important;
}
.paid-status {
margin-top: 10px;

View File

@@ -8,36 +8,36 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="www.project-pro.pl - internetowe rozwi&#261;zania dla biznesu">
<link rel='stylesheet' type="text/css" href='https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700'>
<link rel="stylesheet" type="text/css" href="/libraries/framework/skin/default_skin/css/theme.css">
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/magnific/magnific-popup.css">
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/datepicker/css/bootstrap-datetimepicker.css">
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.structure.min.css">
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.theme.min.css">
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/daterange/daterangepicker.css">
<link rel="stylesheet" type="text/css" href="/libraries/jquery-confirm/jquery-confirm.min.css">
<link rel="stylesheet" type="text/css" href="/libraries/easy-tabs/css/easy-responsive-tabs.css">
<link rel="stylesheet" type="text/css" href="/libraries/bootstrap-4.5.2-dist/css/bootstrap.css">
<link rel="stylesheet" type="text/css" href="/libraries/font-awesome-4.7.0/css/font-awesome.css">
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/minimal.css">
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/blue.css">
<script type="text/javascript" src="/libraries/framework/vendor/jquery/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.min.js"></script>
<script type="text/javascript" src="/libraries/framework/js/utility/utility.js"></script>
<script type="text/javascript" src="/libraries/framework/vendor/plugins/magnific/jquery.magnific-popup.js"></script>
<script type="text/javascript" src="/libraries/easy-tabs/js/easyResponsiveTabs.js"></script>
<script type="text/javascript" src="/libraries/framework/vendor/plugins/moment/moment.js"></script>
<script type="text/javascript" src="/libraries/framework/vendor/plugins/moment/pl.js"></script>
<script type="text/javascript" src="/libraries/framework/vendor/plugins/datepicker/js/bootstrap-datetimepicker.js"></script>
<script type="text/javascript" src="/libraries/framework/vendor/plugins/daterange/daterangepicker.js"></script>
<script type="text/javascript" src="/libraries/jquery-confirm/jquery-confirm.min.js"></script>
<script type="text/javascript" src="/libraries/bootstrap-4.5.2-dist/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/libraries/bootstrap-4.5.2-dist/js/bootstrap.bundle.min.js"></script>
<script type="text/javascript" src="/libraries/grid/plugins/icheck/icheck.js"></script>
<script type="text/javascript" src="/libraries/functions.js"></script>
<script type="text/javascript" src="/admin/js/functions.js"></script>
<link rel="stylesheet" href="/admin/layout/style-css/style.css" />
<link rel="stylesheet" href="/admin/layout/style-css/table-list.css" />
<link rel="stylesheet" href="/admin/layout/style-css/order-details-mobile.css" />
<link rel="stylesheet" type="text/css" href="/libraries/framework/skin/default_skin/css/theme.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/skin/default_skin/css/theme.css'); ?>">
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/magnific/magnific-popup.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/magnific/magnific-popup.css'); ?>">
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/datepicker/css/bootstrap-datetimepicker.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/datepicker/css/bootstrap-datetimepicker.css'); ?>">
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.structure.min.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.structure.min.css'); ?>">
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.theme.min.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.theme.min.css'); ?>">
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/daterange/daterangepicker.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/daterange/daterangepicker.css'); ?>">
<link rel="stylesheet" type="text/css" href="/libraries/jquery-confirm/jquery-confirm.min.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/jquery-confirm/jquery-confirm.min.css'); ?>">
<link rel="stylesheet" type="text/css" href="/libraries/easy-tabs/css/easy-responsive-tabs.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/easy-tabs/css/easy-responsive-tabs.css'); ?>">
<link rel="stylesheet" type="text/css" href="/libraries/bootstrap-4.5.2-dist/css/bootstrap.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/bootstrap-4.5.2-dist/css/bootstrap.css'); ?>">
<link rel="stylesheet" type="text/css" href="/libraries/font-awesome-4.7.0/css/font-awesome.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/font-awesome-4.7.0/css/font-awesome.css'); ?>">
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/minimal.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/grid/plugins/icheck/skins/minimal/minimal.css'); ?>">
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/blue.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/grid/plugins/icheck/skins/minimal/blue.css'); ?>">
<script type="text/javascript" src="/libraries/framework/vendor/jquery/jquery-1.11.1.min.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/jquery/jquery-1.11.1.min.js'); ?>"></script>
<script type="text/javascript" src="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.min.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.min.js'); ?>"></script>
<script type="text/javascript" src="/libraries/framework/js/utility/utility.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/js/utility/utility.js'); ?>"></script>
<script type="text/javascript" src="/libraries/framework/vendor/plugins/magnific/jquery.magnific-popup.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/magnific/jquery.magnific-popup.js'); ?>"></script>
<script type="text/javascript" src="/libraries/easy-tabs/js/easyResponsiveTabs.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/easy-tabs/js/easyResponsiveTabs.js'); ?>"></script>
<script type="text/javascript" src="/libraries/framework/vendor/plugins/moment/moment.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/moment/moment.js'); ?>"></script>
<script type="text/javascript" src="/libraries/framework/vendor/plugins/moment/pl.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/moment/pl.js'); ?>"></script>
<script type="text/javascript" src="/libraries/framework/vendor/plugins/datepicker/js/bootstrap-datetimepicker.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/datepicker/js/bootstrap-datetimepicker.js'); ?>"></script>
<script type="text/javascript" src="/libraries/framework/vendor/plugins/daterange/daterangepicker.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/daterange/daterangepicker.js'); ?>"></script>
<script type="text/javascript" src="/libraries/jquery-confirm/jquery-confirm.min.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/jquery-confirm/jquery-confirm.min.js'); ?>"></script>
<script type="text/javascript" src="/libraries/bootstrap-4.5.2-dist/js/bootstrap.min.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/bootstrap-4.5.2-dist/js/bootstrap.min.js'); ?>"></script>
<script type="text/javascript" src="/libraries/bootstrap-4.5.2-dist/js/bootstrap.bundle.min.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/bootstrap-4.5.2-dist/js/bootstrap.bundle.min.js'); ?>"></script>
<script type="text/javascript" src="/libraries/grid/plugins/icheck/icheck.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/grid/plugins/icheck/icheck.js'); ?>"></script>
<script type="text/javascript" src="/libraries/functions.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/functions.js'); ?>"></script>
<script type="text/javascript" src="/admin/js/functions.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/admin/js/functions.js'); ?>"></script>
<link rel="stylesheet" href="/admin/layout/style-css/style.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/admin/layout/style-css/style.css'); ?>" />
<link rel="stylesheet" href="/admin/layout/style-css/table-list.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/admin/layout/style-css/table-list.css'); ?>" />
<link rel="stylesheet" href="/admin/layout/style-css/order-details-mobile.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/admin/layout/style-css/order-details-mobile.css'); ?>" />
</head>
<body>
<div class="admin-page">

View File

@@ -0,0 +1,30 @@
<?php
namespace Domain\Integrations;
class ApiloLogger
{
/**
* @param \medoo $db
* @param string $action np. 'send_order', 'payment_sync', 'status_sync', 'status_poll'
* @param int|null $orderId
* @param string $message
* @param mixed $context dane do zapisania jako JSON (request/response)
*/
public static function log($db, string $action, ?int $orderId, string $message, $context = null): void
{
$contextJson = null;
if ($context !== null) {
$contextJson = is_string($context)
? $context
: json_encode($context, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
$db->insert('pp_log', [
'action' => $action,
'order_id' => $orderId,
'message' => $message,
'context' => $contextJson,
'date' => date('Y-m-d H:i:s'),
]);
}
}

View File

@@ -425,13 +425,29 @@ class OrderAdminService
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$apiloResultRaw = curl_exec($ch);
$http_code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
$apiloResult = json_decode((string)$apiloResultRaw, true);
if (!is_array($apiloResult) || (int)($apiloResult['updates'] ?? 0) !== 1) {
\Domain\Integrations\ApiloLogger::log(
$mdb,
'resend_order',
$orderId,
'Błąd ponownego wysyłania zamówienia do Apilo (HTTP: ' . $http_code . ')',
['apilo_order_id' => $order['apilo_order_id'], 'http_code' => $http_code, 'response' => $apiloResult]
);
curl_close($ch);
return false;
}
\Domain\Integrations\ApiloLogger::log(
$mdb,
'resend_order',
$orderId,
'Zamówienie ponownie wysłane do Apilo (apilo_order_id: ' . $order['apilo_order_id'] . ')',
['apilo_order_id' => $order['apilo_order_id'], 'http_code' => $http_code, 'response' => $apiloResult]
);
$query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'pp_shop_orders' AND COLUMN_NAME != 'id'";
$stmt = $mdb->query($query);
$columns = $stmt ? $stmt->fetchAll(\PDO::FETCH_COLUMN) : [];
@@ -685,6 +701,23 @@ class OrderAdminService
self::appendApiloLog("PAYMENT RESPONSE\nHTTP: " . $http_code . "\nCURL: " . $curl_error . "\n" . print_r($apilo_response, true));
}
$success = ($curl_error === '' && $http_code >= 200 && $http_code < 300);
\Domain\Integrations\ApiloLogger::log(
$db,
'payment_sync',
(int)$order['id'],
$success
? 'Płatność zsynchronizowana z Apilo (apilo_order_id: ' . $order['apilo_order_id'] . ')'
: 'Błąd synchronizacji płatności (HTTP: ' . $http_code . ($curl_error ? ', cURL: ' . $curl_error : '') . ')',
[
'apilo_order_id' => $order['apilo_order_id'],
'http_code' => $http_code,
'curl_error' => $curl_error,
'response' => json_decode((string)$apilo_response, true),
]
);
if ($curl_error !== '') return false;
if ($http_code < 200 || $http_code >= 300) return false;
@@ -729,6 +762,24 @@ class OrderAdminService
self::appendApiloLog("STATUS RESPONSE\nHTTP: " . $http_code . "\nCURL: " . $curl_error . "\n" . print_r($apilo_result, true));
}
$success = ($curl_error === '' && $http_code >= 200 && $http_code < 300);
\Domain\Integrations\ApiloLogger::log(
$db,
'status_sync',
(int)$order['id'],
$success
? 'Status zsynchronizowany z Apilo (apilo_order_id: ' . $order['apilo_order_id'] . ', status: ' . $status . ')'
: 'Błąd synchronizacji statusu (HTTP: ' . $http_code . ($curl_error ? ', cURL: ' . $curl_error : '') . ')',
[
'apilo_order_id' => $order['apilo_order_id'],
'status' => $status,
'http_code' => $http_code,
'curl_error' => $curl_error,
'response' => json_decode((string)$apilo_result, true),
]
);
if ($curl_error !== '') return false;
if ($http_code < 200 || $http_code >= 300) return false;

View File

@@ -95,7 +95,7 @@ class ShopProductController
. '<a href="/admin/shop_product/product_edit/id=' . $id . '">' . $name . '</a> '
. '<a href="#" class="text-muted duplicate-product" product-id="' . $id . '">duplikuj</a>'
. '</div>'
. '<small class="text-muted product-categories">' . $categories . '</small>'
. '<small class="text-muted product-categories product-categories--cats" title="' . $categories . '">' . $categories . '</small>'
. '<small class="text-muted product-categories">SKU: ' . $sku . ', EAN: ' . $ean . '</small>';
$priceHtml = '<input type="text" class="product-price form-control text-right" product-id="' . $id . '" value="' . htmlspecialchars( (string) $product['price_brutto'], ENT_QUOTES, 'UTF-8' ) . '" style="width: 75px;">';
@@ -688,7 +688,7 @@ class ShopProductController
foreach ( $products as $key => $val ) {
if ( (int) $key !== $productId ) {
$selected = ( is_array( $product['products_related'] ?? null ) && in_array( $key, $product['products_related'] ) ) ? ' selected' : '';
$html .= '<option value="' . (int) $key . '"' . $selected . '>' . $this->escapeHtml( $val ) . '</option>';
$html .= '<option value="' . (int) $key . '"' . $selected . '>' . $this->escapeHtml( (string) $val ) . '</option>';
}
}
$html .= '</select></div></div>';

View File

@@ -406,10 +406,13 @@ if ( $apilo_settings['enabled'] and $apilo_settings['sync_orders'] and $apilo_se
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
$response = curl_exec( $ch );
if (curl_errno( $ch ) ) {
echo 'Błąd cURL: ' . curl_error( $ch );
$curl_error_send = curl_error( $ch );
\Domain\Integrations\ApiloLogger::log( $mdb, 'send_order', (int)$order['id'], 'Błąd cURL przy wysyłaniu zamówienia: ' . $curl_error_send, [ 'curl_error' => $curl_error_send ] );
echo 'Błąd cURL: ' . $curl_error_send;
}
curl_close( $ch );
$http_code_send = (int)curl_getinfo( $ch, CURLINFO_HTTP_CODE );
$response = json_decode( $response, true );
if ( $config['debug']['apilo'] )
@@ -423,6 +426,7 @@ if ( $apilo_settings['enabled'] and $apilo_settings['sync_orders'] and $apilo_se
{
$apilo_order_id = str_replace( 'Order id: ', '', $response['description'] );
$mdb -> update( 'pp_shop_orders', [ 'apilo_order_id' => $apilo_order_id ], [ 'id' => $order['id'] ] );
\Domain\Integrations\ApiloLogger::log( $mdb, 'send_order', (int)$order['id'], 'Zamówienie już istnieje w Apilo (apilo_order_id: ' . $apilo_order_id . ')', [ 'http_code' => $http_code_send, 'response' => $response ] );
echo '<p>Zaktualizowałem id zamówienia na podstawie zamówienia apilo.com</p>';
}
elseif ( $response['message'] == 'Validation error' )
@@ -462,6 +466,7 @@ if ( $apilo_settings['enabled'] and $apilo_settings['sync_orders'] and $apilo_se
{
$apilo_order_id = $get_response_data['list'][0]['id'];
$mdb -> update( 'pp_shop_orders', [ 'apilo_order_id' => $apilo_order_id ], [ 'id' => $order['id'] ] );
\Domain\Integrations\ApiloLogger::log( $mdb, 'send_order', (int)$order['id'], 'Duplikat idExternal - pobrano apilo_order_id: ' . $apilo_order_id, [ 'http_code' => $http_code_send, 'response' => $response, 'get_response' => $get_response_data ] );
echo '<p>Zamówienie już istnieje w Apilo. Zaktualizowano ID zamówienia: ' . $apilo_order_id . '</p>';
}
else
@@ -471,6 +476,8 @@ if ( $apilo_settings['enabled'] and $apilo_settings['sync_orders'] and $apilo_se
echo print_r( $postData, true );
echo '</pre>';
\Domain\Integrations\ApiloLogger::log( $mdb, 'send_order', (int)$order['id'], 'Błąd: duplikat idExternal, ale nie znaleziono zamówienia w Apilo', [ 'http_code' => $http_code_send, 'response' => $response, 'get_response' => $get_response_data ] );
$email_data = print_r( $response, true );
$email_data .= print_r( $postData, true );
\Shared\Helpers\Helpers::send_email( 'biuro@project-pro.pl', 'Błąd wysyłania zamówienia do apilo.com - nie znaleziono zamówienia', $email_data );
@@ -483,6 +490,8 @@ if ( $apilo_settings['enabled'] and $apilo_settings['sync_orders'] and $apilo_se
echo print_r( $postData, true );
echo '</pre>';
\Domain\Integrations\ApiloLogger::log( $mdb, 'send_order', (int)$order['id'], 'Błąd walidacji wysyłania zamówienia do Apilo', [ 'http_code' => $http_code_send, 'response' => $response ] );
$email_data = print_r( $response, true );
$email_data .= print_r( $postData, true );
\Shared\Helpers\Helpers::send_email( 'biuro@project-pro.pl', 'Błąd wysyłania zamówienia do apilo.com', $email_data );
@@ -491,6 +500,7 @@ if ( $apilo_settings['enabled'] and $apilo_settings['sync_orders'] and $apilo_se
else
{
$mdb -> update( 'pp_shop_orders', [ 'apilo_order_id' => $response['id'] ], [ 'id' => $order['id'] ] );
\Domain\Integrations\ApiloLogger::log( $mdb, 'send_order', (int)$order['id'], 'Zamówienie wysłane do Apilo (apilo_order_id: ' . $response['id'] . ')', [ 'http_code' => $http_code_send, 'response' => $response ] );
echo '<p>Wysłałem zamówienie do apilo.com: ID: ' . $order['id'] . ' - ' . $response['id'] . '</p>';
}
}
@@ -515,6 +525,7 @@ if ( $apilo_settings['enabled'] and $apilo_settings['sync_orders'] and $apilo_se
] );
$response = curl_exec( $ch );
$http_code_poll = (int)curl_getinfo( $ch, CURLINFO_HTTP_CODE );
$responseData = json_decode( $response, true );
if ( $responseData['id'] and $responseData['status'] )
@@ -524,6 +535,8 @@ if ( $apilo_settings['enabled'] and $apilo_settings['sync_orders'] and $apilo_se
if ( $shop_status_id )
$orderAdminService->changeStatus( (int)$order['id'], $shop_status_id, false );
\Domain\Integrations\ApiloLogger::log( $mdb, 'status_poll', (int)$order['id'], 'Status pobrany z Apilo (apilo_status: ' . $responseData['status'] . ', shop_status: ' . ($shop_status_id ?: 'brak mapowania') . ')', [ 'apilo_order_id' => $order['apilo_order_id'], 'http_code' => $http_code_poll, 'response' => $responseData ] );
$orderRepo->updateApiloStatusDate( (int)$order['id'], date( 'Y-m-d H:i:s' ) );
echo '<p>Zaktualizowałem status zamówienia <b>' . $order['number'] . '</b></p>';
}

View File

@@ -4,6 +4,19 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
---
## ver. 0.309 (2026-02-23) - ApiloLogger + cache-busting CSS/JS + poprawki UI
- **NEW**: `ApiloLogger` — logowanie operacji Apilo do tabeli `pp_log` z kontekstem JSON (send_order, resend_order, payment_sync, status_sync, status_poll)
- **NEW**: Migracja `pp_log` — kolumny `action`, `order_id`, `context` + indeksy
- **NEW**: Cache-busting dla CSS i JS w admin panelu — `?ver=filemtime()` przy wszystkich lokalnych zasobach w `main-layout.php`
- **FIX**: Przeniesienie inicjalizacji `$mdb` przed `SettingsRepository` w `admin/index.php`
- **FIX**: Rzutowanie na `(string)` w `ShopProductController::escapeHtml()` — zapobiega warningom
- **ZMIANA**: Skrocone kategorie produktow na liscie — `text-overflow: ellipsis` z `title` tooltip
- **ZMIANA**: `copyToClipboard()` — uzywa `navigator.clipboard` API z fallbackiem na `<textarea>`
- **ZMIANA**: Uproszczenie CSS `.site-content` — usuniety zbedny `.with-menu`, menu widoczne zawsze na desktop
---
## ver. 0.308 (2026-02-22) - Kolory statusow zamowien + poprawki bezpieczenstwa
- **NEW**: Kolorowe badge statusow na liscie zamowien w admin panelu — kolory pobierane z `pp_shop_statuses.color`, kontrast tekstu obliczany automatycznie

0
docs/TODO.md Normal file
View File

View File

@@ -1,10 +1,20 @@
window.copyToClipboard = function( element )
{
var $temp = $( "<input>" );
$( "#" + element ).after( $temp );
$temp.val( $( '#' + element ).text() ).select();
document.execCommand( "copy" );
$temp.remove();
var text = $( '#' + element ).text().trim();
if ( navigator.clipboard && navigator.clipboard.writeText )
{
navigator.clipboard.writeText( text );
}
else
{
var $temp = $( "<textarea>" );
$temp.css({ position: 'fixed', left: '-9999px', top: '-9999px' });
$( "body" ).append( $temp );
$temp.val( text ).select();
document.execCommand( "copy" );
$temp.remove();
}
}
function checkForm(id)

5
migrations/0.309.sql Normal file
View File

@@ -0,0 +1,5 @@
ALTER TABLE pp_log ADD COLUMN `action` VARCHAR(100) NULL DEFAULT NULL AFTER `id`;
ALTER TABLE pp_log ADD COLUMN `order_id` INT NULL DEFAULT NULL AFTER `action`;
ALTER TABLE pp_log ADD COLUMN `context` TEXT NULL DEFAULT NULL AFTER `message`;
ALTER TABLE pp_log ADD INDEX `idx_action` (`action`);
ALTER TABLE pp_log ADD INDEX `idx_order_id` (`order_id`);

BIN
updates/0.30/ver_0.308.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,26 @@
{
"changelog": "NEW - kolorowe badge statusow zamowien, walidacja hex, sanityzacja HTML transport, optymalizacja SQL",
"version": "0.308",
"files": {
"added": [
],
"deleted": [
],
"modified": [
"admin/templates/components/table-list.php",
"autoload/Domain/Order/OrderAdminService.php",
"autoload/Domain/Order/OrderRepository.php",
"autoload/admin/Controllers/ShopOrderController.php"
]
},
"checksum_zip": "sha256:01bd6e18f737db848913f334a7af78aef729d37b741803f3cbacb7e379969d0e",
"sql": [
],
"date": "2026-02-22",
"directories_deleted": [
]
}

View File

@@ -1,3 +1,9 @@
<b>ver. 0.308 - 22.02.2026</b><br />
NEW - kolorowe badge statusow zamowien, walidacja hex, sanityzacja HTML transport, optymalizacja SQL
<hr>
<b>ver. 0.308 - 22.02.2026</b><br />
NEW - kolorowe badge statusow zamowien, walidacja hex, sanityzacja HTML transport, optymalizacja SQL
<hr>
<?php
// Auto-generated changelog from manifest files + legacy entries.
// Scans manifest JSON files, merges with changelog-legacy.json, sorts descending, outputs HTML.

View File

@@ -1,5 +1,5 @@
<?
$current_ver = 308;
$current_ver = 309;
for ($i = 1; $i <= $current_ver; $i++)
{