Checkpoint: headless JSON architecture documented + cleanup (Stufe 1+2)

Restore point before Stufe 3 (central resolver-service refactoring).

Includes this session's work:
- Success Story (usecase) rebuild: new fields/tabs, UsecaseSerializer,
  thin List/Show renderers, MM relations, inline content elements.
- vitec_columns content block (two-column layout with per-item content)
  incl. unified header section; special-cased JSON resolution.
- Container per-column flex (items[].config align/justify) + gap on parent.
- Unified header section across CEs/plugins/containers; header fields in JSON.
- ISO-style architecture spec: Documentation/Headless-JSON-Architecture.md.
- Cleanup: removed local scratch + verified .bak backups.
- Fixes: B-6 (undefined thumbnail variable in Downloadcardcollection image
  resolver), B-7 (unsatisfiable legacy CType OR branch in Downloadcard/Datasheets).

Rollback: git reset --hard snapshot-2026-07-09-pre-stufe3

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
o-rascheandClaude Opus 4.8 committed 2026-07-09 10:01:56 +02:00
1 parent c6759186cb
commit bb7c03235d
237 files changed
+6698 -1749

No files matched your search

View File
File mode changed.
+67
View File
@@ -111,6 +111,73 @@
<trans-unit id="column.sidebar_33" resname="column.sidebar_33">
<source>Sidebar (33%)</source>
</trans-unit>
<trans-unit id="cards_carousel.title" resname="cards_carousel.title">
<source>VITEC · Cards Carousel</source>
</trans-unit>
<trans-unit id="cards_carousel.description" resname="cards_carousel.description">
<source>Horizontal carousel/slider. Place VITEC Card elements (or other content) inside; arrows, dots, autoplay and slides-per-view are configurable.</source>
</trans-unit>
<trans-unit id="cards_carousel.column" resname="cards_carousel.column">
<source>Slides</source>
</trans-unit>
<trans-unit id="cards_carousel.flexform.label" resname="cards_carousel.flexform.label">
<source>Carousel Settings</source>
</trans-unit>
<trans-unit id="flex.align.label" resname="flex.align.label">
<source>Align</source>
</trans-unit>
<trans-unit id="flex.align.flex_start" resname="flex.align.flex_start">
<source>flex-start</source>
</trans-unit>
<trans-unit id="flex.align.center" resname="flex.align.center">
<source>center</source>
</trans-unit>
<trans-unit id="flex.align.flex_end" resname="flex.align.flex_end">
<source>flex-end</source>
</trans-unit>
<trans-unit id="flex.align.stretch" resname="flex.align.stretch">
<source>stretch</source>
</trans-unit>
<trans-unit id="flex.justify.label" resname="flex.justify.label">
<source>Justify</source>
</trans-unit>
<trans-unit id="flex.justify.flex_start" resname="flex.justify.flex_start">
<source>flex-start</source>
</trans-unit>
<trans-unit id="flex.justify.center" resname="flex.justify.center">
<source>center</source>
</trans-unit>
<trans-unit id="flex.justify.space_between" resname="flex.justify.space_between">
<source>space-between</source>
</trans-unit>
<trans-unit id="flex.justify.space_around" resname="flex.justify.space_around">
<source>space-around</source>
</trans-unit>
<trans-unit id="flex.justify.space_evenly" resname="flex.justify.space_evenly">
<source>space-evenly</source>
</trans-unit>
<trans-unit id="palette.grid" resname="palette.grid">
<source>Grid</source>
</trans-unit>
<trans-unit id="gap.label" resname="gap.label">
<source>Gap</source>
</trans-unit>
<trans-unit id="gap.option.1" resname="gap.option.1">
<source>1</source>
</trans-unit>
<trans-unit id="gap.option.2" resname="gap.option.2">
<source>2</source>
</trans-unit>
<trans-unit id="gap.option.3" resname="gap.option.3">
<source>3</source>
</trans-unit>
<trans-unit id="gap.option.4" resname="gap.option.4">
<source>4</source>
</trans-unit>
<trans-unit id="gap.option.5" resname="gap.option.5">
<source>5</source>
</trans-unit>
</body>
</file>
</xliff>
View File
File mode changed.
+4 -4
View File
@@ -13,16 +13,16 @@
<f:if condition="{data.header}">
<f:then>{data.header}</f:then>
<f:else>
<em style="color:#888;">Download Card (kein Header)</em>
<em style="color:#888;">Download Card (no header)</em>
</f:else>
</f:if>
</h3>
<div class="vitec-preview__body-text">
<small>
Konfiguration im FlexForm:
<strong>Produkt</strong> oder <strong>Download</strong> wählen,
Layout-Variante setzen.
Configured via FlexForm:
Select a <strong>product</strong> or <strong>download</strong>,
set the layout variant.
</small>
</div>
</div>
@@ -13,15 +13,15 @@
<f:if condition="{data.header}">
<f:then>{data.header}</f:then>
<f:else>
<em style="color:#888;">Download Card Collection (kein Header)</em>
<em style="color:#888;">Download Card Collection (no header)</em>
</f:else>
</f:if>
</h3>
<div class="vitec-preview__body-text">
<small>
Multi-Download-Auswahl mit optionalem Header-Image.
Konfiguration im FlexForm.
Multi-download selection with optional header image.
Configured via FlexForm.
</small>
</div>
</div>
@@ -0,0 +1,22 @@
/* =============================================================
VITEC — container collapse/expand in the Page module
============================================================= */
/* Hide the container body (nested child columns) when collapsed. */
.t3-page-ce.vitec-ce-collapsed > .t3-page-ce-element > .t3-page-ce-body {
display: none;
}
/* Optional: dim/round the collapsed container header a touch. */
.t3-page-ce.vitec-ce-collapsed > .t3-page-ce-element > .t3-page-ce-header {
border-bottom: 1px dashed var(--typo3-component-border-color, #ccc);
}
/* Chevron rotates when collapsed. */
.vitec-collapse-toggle typo3-backend-icon {
display: inline-flex;
transition: transform 0.15s ease-in-out;
}
.t3-page-ce.vitec-ce-collapsed .vitec-collapse-toggle typo3-backend-icon {
transform: rotate(180deg);
}
View File
File mode changed.
@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
<rect x="1.5" y="2.5" width="13" height="11.5" rx="1.2" fill="none" stroke="#26358c" stroke-width="1.2"/>
<rect x="1.5" y="2.5" width="13" height="3" rx="1.2" fill="#26358c"/>
<rect x="4" y="1" width="1.6" height="3" rx="0.8" fill="#26358c"/>
<rect x="10.4" y="1" width="1.6" height="3" rx="0.8" fill="#26358c"/>
<rect x="3.5" y="7" width="2.4" height="2.2" rx="0.3" fill="#f47937"/>
<rect x="6.9" y="7" width="2.4" height="2.2" rx="0.3" fill="#26358c" opacity="0.35"/>
<rect x="10.3" y="7" width="2.4" height="2.2" rx="0.3" fill="#26358c" opacity="0.35"/>
<rect x="3.5" y="10.2" width="2.4" height="2.2" rx="0.3" fill="#26358c" opacity="0.35"/>
<rect x="6.9" y="10.2" width="2.4" height="2.2" rx="0.3" fill="#26358c" opacity="0.35"/>
</svg>

After

Width:  |  Height:  |  Size: 844 B

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
<polygon points="1,8 3,6.4 3,9.6" fill="#000" opacity="0.5"/>
<polygon points="15,8 13,6.4 13,9.6" fill="#000" opacity="0.5"/>
<rect x="3.8" y="3.5" width="3.4" height="8" rx="0.6" fill="none" stroke="#000" stroke-width="1" opacity="0.7"/>
<rect x="4.4" y="4.2" width="2.2" height="2.4" rx="0.3" fill="#000" opacity="0.3"/>
<rect x="4.4" y="9.6" width="2.2" height="1" rx="0.4" fill="#f47937"/>
<rect x="8.6" y="3.5" width="3.4" height="8" rx="0.6" fill="none" stroke="#000" stroke-width="1" opacity="0.7"/>
<rect x="9.2" y="4.2" width="2.2" height="2.4" rx="0.3" fill="#000" opacity="0.3"/>
<rect x="9.2" y="9.6" width="2.2" height="1" rx="0.4" fill="#f47937"/>
<circle cx="6.6" cy="13.4" r="0.7" fill="#000" opacity="0.8"/>
<circle cx="8.6" cy="13.4" r="0.7" fill="#000" opacity="0.3"/>
<circle cx="10.6" cy="13.4" r="0.7" fill="#000" opacity="0.3"/>
</svg>

After

Width:  |  Height:  |  Size: 966 B

View File
File mode changed.

Before

Width:  |  Height:  |  Size: 349 B

After

Width:  |  Height:  |  Size: 349 B

View File
File mode changed.

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 278 B

View File
File mode changed.

Before

Width:  |  Height:  |  Size: 201 B

After

Width:  |  Height:  |  Size: 201 B

View File
File mode changed.

Before

Width:  |  Height:  |  Size: 203 B

After

Width:  |  Height:  |  Size: 203 B

View File
File mode changed.

Before

Width:  |  Height:  |  Size: 202 B

After

Width:  |  Height:  |  Size: 202 B

View File
File mode changed.

Before

Width:  |  Height:  |  Size: 447 B

After

Width:  |  Height:  |  Size: 447 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
<rect x="3" y="1.5" width="10" height="13" rx="1" fill="none" stroke="#26358c" stroke-width="1.2"/>
<rect x="4.6" y="4" width="6.8" height="1.1" rx="0.3" fill="#26358c" opacity="0.5"/>
<rect x="4.6" y="6.2" width="6.8" height="1.1" rx="0.3" fill="#26358c" opacity="0.5"/>
<rect x="4.6" y="8.4" width="4.5" height="1.1" rx="0.3" fill="#26358c" opacity="0.5"/>
<rect x="4.6" y="11" width="4" height="1.6" rx="0.3" fill="#f47937"/>
</svg>

After

Width:  |  Height:  |  Size: 530 B

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
<rect x="1.5" y="2.5" width="13" height="11.5" rx="1.2" fill="none" stroke="#26358c" stroke-width="1.2"/>
<rect x="1.5" y="2.5" width="13" height="3" rx="1.2" fill="#26358c"/>
<rect x="4" y="1" width="1.6" height="3" rx="0.8" fill="#26358c"/>
<rect x="10.4" y="1" width="1.6" height="3" rx="0.8" fill="#26358c"/>
<rect x="3.5" y="7" width="9" height="1.4" rx="0.3" fill="#f47937"/>
<rect x="3.5" y="9.4" width="9" height="1.4" rx="0.3" fill="#26358c" opacity="0.4"/>
<rect x="3.5" y="11.8" width="6" height="1.4" rx="0.3" fill="#26358c" opacity="0.4"/>
</svg>

After

Width:  |  Height:  |  Size: 658 B

@@ -0,0 +1,131 @@
/**
* VITEC — collapse/expand toggle for container content elements in the
* TYPO3 v14 Page module.
*
* Adds a chevron button into the `.btn-toolbar` of every VITEC container
* (vitec_container, vitec_cols_*, vitec_cards_carousel — anything that
* renders a nested b13 grid). Clicking it hides/shows the container body
* (the nested child columns). State is persisted per element uid in
* localStorage, so it survives reloads.
*
* Pure DOM enhancement — no core or b13 template overrides.
*/
class VitecContainerCollapse {
constructor() {
this.storagePrefix = 'vitec-ce-collapsed-';
this.ready();
}
ready() {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => this.boot());
} else {
this.boot();
}
}
boot() {
this.processAll();
// Page module can re-render columns via AJAX; watch for new elements.
const root = document.querySelector('.t3js-page-columns-wrapper, .module, body');
if (root) {
const observer = new MutationObserver(() => {
window.clearTimeout(this._debounce);
this._debounce = window.setTimeout(() => this.processAll(), 150);
});
observer.observe(root, { childList: true, subtree: true });
}
}
processAll() {
document.querySelectorAll('.t3-page-ce[data-uid]').forEach((ce) => this.enhance(ce));
}
isCollapsed(uid) {
try {
return window.localStorage.getItem(this.storagePrefix + uid) === '1';
} catch (e) {
return false;
}
}
setCollapsed(uid, collapsed) {
try {
if (collapsed) {
window.localStorage.setItem(this.storagePrefix + uid, '1');
} else {
window.localStorage.removeItem(this.storagePrefix + uid);
}
} catch (e) {
/* storage unavailable — ignore */
}
}
enhance(ce) {
if (ce.dataset.vitecCollapseReady === '1') {
return;
}
// The direct body of THIS element (not a nested child's body).
const element = ce.querySelector(':scope > .t3-page-ce-element');
if (!element) {
return;
}
const body = element.querySelector(':scope > .t3-page-ce-body');
if (!body) {
return;
}
// A container is recognised by a nested b13 grid inside its body.
const hasNestedGrid = body.querySelector('.t3-grid-container') !== null;
if (!hasNestedGrid) {
return;
}
// Best-effort VITEC filter via the header icon identifier
// (b13 sets the icon identifier to the CType, e.g. "vitec_container").
const iconEl = ce.querySelector(
'.t3-page-ce-header-left typo3-backend-icon[identifier], .t3-page-ce-header-left [data-identifier]'
);
if (iconEl) {
const identifier =
iconEl.getAttribute('identifier') || iconEl.getAttribute('data-identifier') || '';
if (identifier && identifier.indexOf('vitec') === -1) {
return; // a non-VITEC container — leave it alone
}
}
const toolbar = element.querySelector(
':scope > .t3-page-ce-header .btn-toolbar .btn-group'
);
if (!toolbar) {
return;
}
ce.dataset.vitecCollapseReady = '1';
const uid = ce.dataset.uid;
const button = document.createElement('button');
button.type = 'button';
button.className = 'btn btn-sm btn-default btn-borderless vitec-collapse-toggle';
button.title = 'Collapse / expand container';
button.setAttribute('aria-label', 'Collapse or expand container');
button.innerHTML =
'<typo3-backend-icon identifier="actions-chevron-up" size="small"></typo3-backend-icon>';
toolbar.insertBefore(button, toolbar.firstChild);
if (this.isCollapsed(uid)) {
ce.classList.add('vitec-ce-collapsed');
}
button.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
const collapsed = ce.classList.toggle('vitec-ce-collapsed');
this.setCollapsed(uid, collapsed);
});
}
}
export default new VitecContainerCollapse();
File mode changed.