From 92ce677f7cc36e3661df1e2e7b5368a47e63cb76 Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Tue, 21 Oct 2025 00:17:58 +0200 Subject: [PATCH] =?UTF-8?q?Dodaj=20metod=C4=99=20get=5Froas=5Fbounds=20w?= =?UTF-8?q?=20klasie=20Products=20oraz=20zaktualizuj=20metod=C4=99=20get?= =?UTF-8?q?=5Fproducts,=20aby=20wy=C5=9Bwietla=C5=82a=20minimalny=20i=20ma?= =?UTF-8?q?ksymalny=20ROAS=20z=20paskiem=20wydajno=C5=9Bci.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/ftp-kr.sync.cache.json | 8 ++--- autoload/controls/class.Products.php | 50 ++++++++++++++++++++++++++-- autoload/factory/class.Products.php | 27 +++++++++++++++ 3 files changed, 79 insertions(+), 6 deletions(-) diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json index 73a5553..9d84bb1 100644 --- a/.vscode/ftp-kr.sync.cache.json +++ b/.vscode/ftp-kr.sync.cache.json @@ -35,8 +35,8 @@ }, "class.Cron.php": { "type": "-", - "size": 20896, - "lmtime": 1747343750756, + "size": 20969, + "lmtime": 1756237731311, "modified": false }, "class.Products.php": { @@ -73,8 +73,8 @@ }, "class.Products.php": { "type": "-", - "size": 4959, - "lmtime": 1755698070623, + "size": 5233, + "lmtime": 1756241352457, "modified": false }, "class.Users.php": { diff --git a/autoload/controls/class.Products.php b/autoload/controls/class.Products.php index 09d5d78..17bc0c3 100644 --- a/autoload/controls/class.Products.php +++ b/autoload/controls/class.Products.php @@ -51,6 +51,7 @@ class Products exit; } + static public function get_products() { $client_id = \S::get( 'client_id' ); @@ -61,6 +62,42 @@ class Products $order_name = $_POST['order'][0]['name'] ? $_POST['order'][0]['name'] : 'clicks'; $search = $_POST['search']['value']; + // ➊ MIN/MAX ROAS dla kontekstu klienta (opcjonalnie z filtrem search) + $bounds = \factory\Products::get_roas_bounds( $client_id, $search ); + $roas_min = (float)$bounds['min']; + $roas_max = (float)$bounds['max']; + // zabezpieczenie przed dzieleniem przez 0 + if ($roas_min === $roas_max) { + $roas_max = $roas_min + 0.000001; + } + + // ➋ Helper do paska performance (lokalna funkcja) + $renderPerfBar = function (float $value, float $min, float $max): string + { + // normalizacja 0..1 + $t = ($value - $min) / ($max - $min); + if ($t < 0) $t = 0; + if ($t > 1) $t = 1; + + // szerokości + $minPx = 20; // minimalna długość paska + $maxPx = 120; // szerokość „toru” + $fill = (int)round($minPx + $t * ($maxPx - $minPx)); + + // kolor od #E74C3C (czerwony) do #2ECC71 (zielony) + $from = [231, 76, 60]; + $to = [ 46,204,113]; + $r = (int)round($from[0] + ($to[0] - $from[0]) * $t); + $g = (int)round($from[1] + ($to[1] - $from[1]) * $t); + $b = (int)round($from[2] + ($to[2] - $from[2]) * $t); + $hex = sprintf('#%02X%02X%02X', $r, $g, $b); + + // prosty pasek (tor + wypełnienie) + return '
+
+
'; + }; + $db_results = \factory\Products::get_products( $client_id, $search, $limit, $start, $order_name, $order_dir ); $recordsTotal = \factory\Products::get_records_total_products( $client_id, $search ); @@ -97,6 +134,15 @@ class Products else if ( $custom_label_4 == 'paused' ) $custom_label_4_color = 'background-color:rgb(143, 143, 143); color: #FFF;'; + // ➌ ROAS – liczba + pasek performance + $roasValue = (float)$row['roas']; + $roasNumeric = ($roasValue <= (float)$row['min_roas']) + ? ''.($roasValue).'' + : $roasValue; + + $roasPerfBar = $renderPerfBar($roasValue, $roas_min, $roas_max); + $roasCellHtml = '
'.$roasNumeric.$roasPerfBar.'
'; + $data['data'][] = [ $row['product_id'], $row['offer_id'], @@ -110,14 +156,14 @@ class Products ', $row['impressions'], $row['impressions_30'], - '' . $row['clicks'] . '', + '' . $row['clicks'] . '', $row['clicks_30'], round( $row['ctr'], 2 ) . '%', \S::number_display( $row['cost'] ), \S::number_display( $row['cpc'] ), round( $row['conversions'], 2 ), \S::number_display( $row['conversions_value'] ), - $row['roas'] <= $row['min_roas'] ? '' . $row['roas'] . '' : $row['roas'], + $roasCellHtml, '', '', '' diff --git a/autoload/factory/class.Products.php b/autoload/factory/class.Products.php index 86e1707..79ed6b8 100644 --- a/autoload/factory/class.Products.php +++ b/autoload/factory/class.Products.php @@ -42,6 +42,33 @@ class Products return $mdb -> query( 'SELECT pt.*, p.offer_id, p.min_roas FROM products_temp AS pt INNER JOIN products AS p ON p.id = pt.product_id WHERE client_id = \'' . $client_id . '\' ORDER BY ' . $order_name . ' ' . $order_dir . ', id DESC LIMIT ' . $start . ', ' . $limit ) -> fetchAll(); } + + + // \factory\Products.php + public static function get_roas_bounds(int $client_id, ?string $search = null): array + { + global $mdb; + + $params = [':client_id' => $client_id]; + + $sql = 'SELECT MIN(p.min_roas) AS min_roas, MAX(pt.roas) AS max_roas + FROM products_temp AS pt + INNER JOIN products AS p ON p.id = pt.product_id + WHERE p.client_id = :client_id AND conversions > 10'; + + if ($search) { + $sql .= ' AND (pt.name LIKE :search OR p.offer_id LIKE :search)'; + $params[':search'] = '%' . $search . '%'; + } + + $row = $mdb->query($sql, $params)->fetch(\PDO::FETCH_ASSOC); + + return [ + 'min' => isset($row['min_roas']) ? (float)$row['min_roas'] : 0.0, + 'max' => isset($row['max_roas']) ? (float)$row['max_roas'] : 0.0, + ]; + } + static public function get_records_total_products( $client_id, $search ) { global $mdb;