product-check: Features column with icon coverage
- new "Features" column: check with cards-and-icons count per product (e.g. "5·4i"), header carries the filled total like the other columns - detail pane lists every feature card with icon thumbnail (click to enlarge), title and text; cards without an icon show a red cross on the icon slot - the missing agency icon sets are visible at a glance Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6ff9c3e7a8
commit
f525ce3c27
1 file changed
+66
-1
@@ -89,6 +89,25 @@ foreach ($pdo->query(
|
||||
$solutions[(int)$r['product']][] = (string)$r['title'];
|
||||
}
|
||||
|
||||
// inline feature cards (Why choose ...?) + their icon references
|
||||
$features = [];
|
||||
foreach ($pdo->query(
|
||||
'SELECT uid, product, title, cardtext FROM tx_vitec_product_feature
|
||||
WHERE deleted = 0 AND hidden = 0 ORDER BY product, sorting, uid'
|
||||
)->fetchAll() as $r) {
|
||||
$features[(int)$r['product']][] = $r;
|
||||
}
|
||||
$featureIcons = [];
|
||||
foreach ($pdo->query(
|
||||
"SELECT r.uid_foreign, f.identifier, f.name
|
||||
FROM sys_file_reference r JOIN sys_file f ON f.uid = r.uid_local
|
||||
WHERE r.deleted = 0 AND r.hidden = 0 AND r.tablenames = 'tx_vitec_product_feature'
|
||||
AND r.fieldname = 'icon'
|
||||
ORDER BY r.uid_foreign, r.sorting_foreign"
|
||||
)->fetchAll() as $r) {
|
||||
$featureIcons[(int)$r['uid_foreign']] ??= ['url' => '/fileadmin' . $r['identifier'], 'name' => (string)$r['name']];
|
||||
}
|
||||
|
||||
// ---- helpers ------------------------------------------------------------------
|
||||
|
||||
function h(string $v): string
|
||||
@@ -133,6 +152,36 @@ function imageBlock(string $label, array $images): string
|
||||
return $out . '</div></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature cards with their icon thumbnails (click to enlarge).
|
||||
*
|
||||
* @param array<int,array<string,mixed>> $items
|
||||
* @param array<int,array{url:string,name:string}> $featureIcons
|
||||
*/
|
||||
function featuresBlock(array $items, array $featureIcons): string
|
||||
{
|
||||
if ($items === []) {
|
||||
return '';
|
||||
}
|
||||
$withIcon = 0;
|
||||
$cards = '';
|
||||
foreach ($items as $it) {
|
||||
$icon = $featureIcons[(int)$it['uid']] ?? null;
|
||||
if ($icon !== null) {
|
||||
$withIcon++;
|
||||
}
|
||||
$cards .= '<div class="feature">'
|
||||
. ($icon !== null
|
||||
? '<a href="' . h($icon['url']) . '" class="zoom ficon" title="' . h($icon['name']) . '">'
|
||||
. '<img loading="lazy" src="' . h($icon['url']) . '" alt="' . h($icon['name']) . '"></a>'
|
||||
: '<span class="ficon noicon" title="no icon">✗</span>')
|
||||
. '<div><div class="ftitle">' . h((string)$it['title']) . '</div>'
|
||||
. '<div class="ftext">' . h((string)$it['cardtext']) . '</div></div></div>';
|
||||
}
|
||||
return '<div class="fld"><div class="fldlabel">Features (' . count($items) . ' cards, ' . $withIcon . ' icons)</div>'
|
||||
. '<div class="features">' . $cards . '</div></div>';
|
||||
}
|
||||
|
||||
/** @param string[] $items */
|
||||
function listBlock(string $label, array $items): string
|
||||
{
|
||||
@@ -145,7 +194,7 @@ function listBlock(string $label, array $items): string
|
||||
|
||||
// ---- columns ------------------------------------------------------------------
|
||||
|
||||
$columns = ['Hero', 'Photos', 'Video', 'Teaser', 'Subtitle', 'Description', 'Descr. 2', 'Highlights', 'Capabilities', 'Applications', 'Resources', 'Datasheets', 'Solutions'];
|
||||
$columns = ['Hero', 'Photos', 'Video', 'Teaser', 'Subtitle', 'Description', 'Descr. 2', 'Highlights', 'Capabilities', 'Applications', 'Features', 'Resources', 'Datasheets', 'Solutions'];
|
||||
|
||||
$filledPerColumn = array_fill_keys($columns, 0);
|
||||
$rowsHtml = '';
|
||||
@@ -155,6 +204,8 @@ foreach ($products as $p) {
|
||||
$res = $resources[$uid] ?? [];
|
||||
$ds = $datasheets[$uid] ?? [];
|
||||
$sol = $solutions[$uid] ?? [];
|
||||
$feat = $features[$uid] ?? [];
|
||||
$featIconCount = count(array_filter($feat, static fn(array $it): bool => isset($featureIcons[(int)$it['uid']])));
|
||||
|
||||
$checks = [
|
||||
'Hero' => [count($f['heroimage'] ?? []), true],
|
||||
@@ -167,6 +218,7 @@ foreach ($products as $p) {
|
||||
'Highlights' => [filled($p['highlights']) ? 1 : 0, false],
|
||||
'Capabilities' => [filled($p['capabilities']) ? 1 : 0, false],
|
||||
'Applications' => [filled($p['applications']) ? 1 : 0, false],
|
||||
'Features' => [count($feat), true],
|
||||
'Resources' => [count($res), true],
|
||||
'Datasheets' => [count($ds), true],
|
||||
'Solutions' => [count($sol), true],
|
||||
@@ -176,6 +228,11 @@ foreach ($products as $p) {
|
||||
foreach ($columns as $column) {
|
||||
[$count, $showCount] = $checks[$column];
|
||||
$filledPerColumn[$column] += $count > 0 ? 1 : 0;
|
||||
if ($column === 'Features' && $count > 0) {
|
||||
// cards + how many of them carry an icon
|
||||
$cells .= '<td class="ok">✓<small>' . $count . '·' . $featIconCount . 'i</small></td>';
|
||||
continue;
|
||||
}
|
||||
$cells .= cell($count > 0, $showCount ? $count : 0);
|
||||
}
|
||||
|
||||
@@ -216,6 +273,7 @@ foreach ($products as $p) {
|
||||
. textBlock('Highlights', (string)$p['highlights'])
|
||||
. textBlock('Capabilities', (string)$p['capabilities'])
|
||||
. textBlock('Applications', (string)$p['applications'])
|
||||
. featuresBlock($feat, $featureIcons)
|
||||
. listBlock('Resources', $resourceLines)
|
||||
. listBlock('Datasheets', $ds)
|
||||
. listBlock('Solutions', $sol);
|
||||
@@ -280,6 +338,13 @@ tr.detail>td{background:#fafbfd;text-align:left;white-space:normal;padding:0}
|
||||
.fldval{background:#fff;border:1px solid var(--line);border-radius:6px;padding:8px 12px;overflow-wrap:anywhere}
|
||||
.fldval p:first-child{margin-top:0}.fldval p:last-child{margin-bottom:0}
|
||||
.fldlist{margin:0;background:#fff;border:1px solid var(--line);border-radius:6px;padding:8px 12px 8px 28px}
|
||||
.features{display:grid;gap:8px}
|
||||
.feature{display:flex;gap:10px;align-items:flex-start;background:#fff;border:1px solid var(--line);border-radius:6px;padding:8px 12px}
|
||||
.ficon{width:40px;height:40px;flex:0 0 40px;display:flex;align-items:center;justify-content:center;border:1px solid var(--line);border-radius:6px;background:#fff}
|
||||
.ficon img{max-width:30px;max-height:30px}
|
||||
.noicon{color:var(--miss);background:#fdf0ee;font-weight:700}
|
||||
.ftitle{font-weight:600}
|
||||
.ftext{color:#555;font-size:12.5px}
|
||||
.thumbs{display:flex;flex-wrap:wrap;gap:8px}
|
||||
.thumbs a{display:flex;width:140px;height:92px;align-items:center;justify-content:center;background:#fff;border:1px solid var(--line);border-radius:6px;overflow:hidden}
|
||||
.thumbs img{max-width:100%;max-height:100%;object-fit:contain}
|
||||
|
||||
Reference in new issue
Block a user