Dodaj metodę get_roas_bounds w klasie Products oraz zaktualizuj metodę get_products, aby wyświetlała minimalny i maksymalny ROAS z paskiem wydajności.
This commit is contained in:
8
.vscode/ftp-kr.sync.cache.json
vendored
8
.vscode/ftp-kr.sync.cache.json
vendored
@@ -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": {
|
||||
|
||||
@@ -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 '<div class="roas-perf-wrap" title="Min: '.number_format($min,2).' | Max: '.number_format($max,2).'" style="margin-top:4px;width:'.$maxPx.'px;height:8px;background:#eee;border-radius:4px;overflow:hidden;">
|
||||
<div class="roas-perf-fill" style="height:100%;width:'.$fill.'px;background:'.$hex.';"></div>
|
||||
</div>';
|
||||
};
|
||||
|
||||
$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'])
|
||||
? '<span class="text-danger text-bold">'.($roasValue).'</span>'
|
||||
: $roasValue;
|
||||
|
||||
$roasPerfBar = $renderPerfBar($roasValue, $roas_min, $roas_max);
|
||||
$roasCellHtml = '<div class="roas-cell">'.$roasNumeric.$roasPerfBar.'</div>';
|
||||
|
||||
$data['data'][] = [
|
||||
$row['product_id'],
|
||||
$row['offer_id'],
|
||||
@@ -110,14 +156,14 @@ class Products
|
||||
</div>',
|
||||
$row['impressions'],
|
||||
$row['impressions_30'],
|
||||
'<span style="color: ' . ( $row['clicks'] > 200 ? '#57b951' : '' ) . '">' . $row['clicks'] . '</span>',
|
||||
'<span style="color: ' . ( $row['clicks'] > 200 ? ( $row['clicks'] > 400 ? '#0047ccff' : '#57b951' ) : '' ) . '">' . $row['clicks'] . '</span>',
|
||||
$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'] ? '<span class="text-danger text-bold">' . $row['roas'] . '</span>' : $row['roas'],
|
||||
$roasCellHtml,
|
||||
'<input type="text" class="form-control min_roas" product_id="' . $row['product_id'] . '" value="' . $row['min_roas'] . '" style="width: 100px;">',
|
||||
'',
|
||||
'<input type="text" class="form-control custom_label_4" product_id="' . $row['product_id'] . '" value="' . $custom_label_4 . '" style="' . $custom_label_4_color . '">'
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user