product.features: Why-choose cards with icons (contract 0036-FE)

- new inline table tx_vitec_product_feature (title, cardtext, FAL icon)
  + `features` field on the product record; ProductShowJsonRenderer
  emits product.features {heading, items[{title, text, icon}]} and null
  while a product has no cards, so the front end keeps its
  applications-HTML fallback
- new vitec:seed-product-features: seeds the cards from the
  applications <p><strong> pairs and matches the staged agency icons by
  normalised title (camelCase split, stop words, Levenshtein <= 3 typo
  tolerance); products with existing cards are never touched
- new vitec:build-why-vitec-bands: "Why VITEC?" intro + icon_card band
  on the 7 overview pages with a staged icon set (the contract's
  "centered" variant does not exist - documented in ticket 0039-BE);
  vision/visionav sub-group sets stay unassigned
- product-check page: sortable title column and an Open Product button
- new migration fix_colombia_quote.php: stray quote element #12037
  removed, quote_role set (ticket 0038-FE)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
orascheandClaude Fable 5 committed 2026-11-04 19:55:01 +01:00
1 parent 3ccb9b149e
commit 46b12d4a58
8 files changed
+799 -4

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
<?php
/**
* fix_colombia_quote.php (2026-11-04, Ticket 0038-FE Punkt 3)
*
* the-national-police-of-colombia: das lose Zitat-Element #12037 traegt nur
* die ROLLE ("Director of the Engineering Department of Cosmos Sistemas
* Integrado"), waehrend der Usecase-Record quote_person gefuellt, quote_role
* aber leer hat. Fix: Element soft-loeschen (+ Inline-Zaehler), quote_role
* am Record setzen.
*
* php migrations/fix_colombia_quote.php # INSPECT (default)
* php migrations/fix_colombia_quote.php --apply # schreibt
*/
declare(strict_types=1);
$apply = in_array('--apply', $argv, true);
$settings = require __DIR__ . '/../config/system/settings.php';
$db = $settings['DB']['Connections']['Default'];
$pdo = new PDO(
sprintf('mysql:host=%s;dbname=%s;charset=utf8mb4', $db['host'], $db['dbname']),
$db['user'],
$db['password'],
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
$role = 'Director of the Engineering Department of Cosmos Sistemas Integrado';
echo $apply ? "APPLY\n\n" : "INSPECT - nichts wird geschrieben (--apply zum Schreiben)\n\n";
$story = $pdo->query(
"SELECT uid, title, quote_person, quote_role, content_elements FROM tx_vitec_domain_model_usecase
WHERE deleted = 0 AND slug = 'the-national-police-of-colombia'"
)->fetch(PDO::FETCH_ASSOC);
if (!$story) {
echo "ABBRUCH: Story the-national-police-of-colombia nicht gefunden.\n";
exit(1);
}
printf(
"Story #%d \"%s\": quote_person=\"%s\" quote_role=\"%s\" content_elements=%d\n",
$story['uid'], $story['title'], $story['quote_person'], $story['quote_role'], $story['content_elements']
);
$element = $pdo->query(
'SELECT uid, pid, CType, deleted, tx_vitec_usecase_content, bodytext, header FROM tt_content WHERE uid = 12037'
)->fetch(PDO::FETCH_ASSOC);
if (!$element) {
echo "Element #12037 existiert nicht.\n";
exit(1);
}
printf(
"Element #12037: CType=%s deleted=%d usecase=%d text=\"%s\"\n",
$element['CType'], $element['deleted'], $element['tx_vitec_usecase_content'],
mb_substr(trim(strip_tags((string)$element['bodytext'] . ' ' . (string)$element['header'])), 0, 90)
);
if ((int)$element['tx_vitec_usecase_content'] !== (int)$story['uid']) {
echo "ABBRUCH: Element #12037 haengt nicht an dieser Story.\n";
exit(1);
}
if ((int)$element['deleted'] === 1) {
echo "Element ist bereits geloescht.\n";
}
if (!$apply) {
echo "\nWuerde: Element #12037 loeschen, content_elements neu zaehlen, quote_role setzen auf:\n \"$role\"\n";
exit(0);
}
if ((int)$element['deleted'] === 0) {
$pdo->prepare('UPDATE tt_content SET deleted = 1, tstamp = ? WHERE uid = 12037')->execute([time()]);
echo "Element #12037 geloescht.\n";
}
$count = (int)$pdo->query(
'SELECT COUNT(*) FROM tt_content WHERE deleted = 0 AND tx_vitec_usecase_content = ' . (int)$story['uid']
)->fetchColumn();
$pdo->prepare('UPDATE tx_vitec_domain_model_usecase SET content_elements = ?, quote_role = ?, tstamp = ? WHERE uid = ?')
->execute([$count, $role, time(), (int)$story['uid']]);
printf("Record aktualisiert: quote_role gesetzt, content_elements = %d.\n", $count);
echo "Danach: vendor/bin/typo3 cache:flush\n";