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

@@ -23,7 +23,7 @@ return [
],
],
'types' => [
'1' => ['showitem' => 'title, subtitle, slug, teaser, description, description2, applications, highlights, contentelement, contentelementcta, portfolio, showdatapath, textrelatedproducts, relatedprodukt, related_texts, solutions,
'1' => ['showitem' => 'title, subtitle, slug, teaser, description, description2, applications, features, highlights, contentelement, contentelementcta, portfolio, showdatapath, textrelatedproducts, relatedprodukt, related_texts, solutions,
--div--;Resources, resources, resources_layout,
--div--;SEO, seotitle, urltitle, seometa, keywords, structureddata,
--div--;Images and Videos, heroimage, productimage, ogimage, video, videofile,
@@ -484,6 +484,27 @@ return [
],
],
],
// "Why choose <product>?" cards (contract 0036-FE, 2026-11-04): the
// structured twin of the `applications` RTE blob - one record per card
// (title, text, optional icon). The front end renders `features` when
// items exist and falls back to parsing `applications` otherwise, so
// editors may keep working in either place during the transition.
'features' => [
'exclude' => true,
'label' => 'Why-choose Features',
'description' => '"Why choose" cards of the detail page: title, text and icon per card. When at least one card exists, the front end shows these instead of parsing the Applications HTML.',
'config' => [
'type' => 'inline',
'foreign_table' => 'tx_vitec_product_feature',
'foreign_field' => 'product',
'foreign_sortby' => 'sorting',
'appearance' => [
'collapseAll' => true,
'levelLinksPosition' => 'top',
'useSortable' => true,
],
],
],
'resources' => [
'exclude' => true,
'label' => 'Resources',
@@ -0,0 +1,85 @@
<?php
/**
* "Why choose <product>?" cards of the product detail page (contract
* 0036-FE, 2026-11-04): title + short text + optional icon, rendered as the
* centered card band under the intro ("Find the right appliance for your
* application" in the Figma). Child table of tx_vitec_domain_model_product
* via the `features` inline field - edited only there (hideTable), no
* Extbase model on purpose: ProductShowJsonRenderer queries the rows
* directly. Seeded once from the `applications` HTML pairs plus the staged
* agency icons by vitec:seed-product-features; afterwards editors own the
* records (the seeder never touches a product that already has cards).
*/
return [
'ctrl' => [
'title' => 'Product Feature (Why choose)',
'label' => 'title',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'delete' => 'deleted',
'sortby' => 'sorting',
'hideTable' => true,
'enablecolumns' => [
'disabled' => 'hidden',
],
'iconfile' => 'EXT:vitec/Resources/Public/Icons/tx_vitec_domain_model_download.gif',
'security' => [
'ignorePageTypeRestriction' => true,
],
],
'types' => [
'1' => ['showitem' => 'title, cardtext, icon, hidden'],
],
'columns' => [
'hidden' => [
'exclude' => true,
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.visible',
'config' => [
'type' => 'check',
'renderType' => 'checkboxToggle',
'items' => [
[
'label' => '',
'invertStateDisplay' => true,
],
],
],
],
'product' => [
'config' => [
'type' => 'passthrough',
],
],
'title' => [
'exclude' => true,
'label' => 'Card title',
'config' => [
'type' => 'input',
'size' => 40,
'eval' => 'trim',
'default' => '',
],
],
'cardtext' => [
'exclude' => true,
'label' => 'Card text',
'config' => [
'type' => 'text',
'cols' => 40,
'rows' => 3,
'eval' => 'trim',
],
],
'icon' => [
'exclude' => true,
'label' => 'Icon',
'description' => 'Feature icon (SVG). The agency set lives under fileadmin/icons/delivered/products/.',
'config' => [
'type' => 'file',
'maxitems' => 1,
'allowed' => 'common-image-types,svg',
],
],
],
];