|
35 | 35 | * apps.<app>.navigation.<id>.label |
36 | 36 | * dashboards.<dash>.label / .description |
37 | 37 | * dashboards.<dash>.widgets.<w>.title / .description |
| 38 | + * pages.<page>.label / .description |
| 39 | + * pages.<page>.title / .subtitle (from the page's `page:header` component) |
38 | 40 | * metadataForms.<type>.label / .description |
39 | 41 | * metadataForms.<type>.sections.<section>.label / .description |
40 | 42 | * metadataForms.<type>.fields.<dotPath>.label / .helpText / .placeholder |
@@ -72,6 +74,7 @@ export interface ExpectedEntry { |
72 | 74 | | 'navigation' |
73 | 75 | | 'dashboard' |
74 | 76 | | 'widget' |
| 77 | + | 'page' |
75 | 78 | | 'metadataType' |
76 | 79 | | 'metadataFormSection' |
77 | 80 | | 'metadataFormField'; |
@@ -396,6 +399,36 @@ export function collectExpectedEntries(config: any): ExpectedEntry[] { |
396 | 399 | } |
397 | 400 | } |
398 | 401 |
|
| 402 | + // ── Pages + their `page:header` copy ────────────────────────────── |
| 403 | + const pages: any[] = Array.isArray(config?.pages) ? config.pages : []; |
| 404 | + for (const page of pages) { |
| 405 | + if (!page?.name) continue; |
| 406 | + const name = page.name as string; |
| 407 | + if (page.label) pushEntry(out, ['pages', name, 'label'], page.label, 'page'); |
| 408 | + if (page.description) { |
| 409 | + pushEntry(out, ['pages', name, 'description'], page.description, 'page'); |
| 410 | + } |
| 411 | + // Header copy is authored inside the page's `page:header` component but |
| 412 | + // is addressed by page name — `translatePage` overlays it back onto every |
| 413 | + // header in the page's regions. |
| 414 | + const regions: any[] = Array.isArray(page.regions) ? page.regions : []; |
| 415 | + for (const region of regions) { |
| 416 | + const components: any[] = Array.isArray(region?.components) ? region.components : []; |
| 417 | + for (const component of components) { |
| 418 | + if (component?.type !== 'page:header') continue; |
| 419 | + const props = component.properties ?? {}; |
| 420 | + // `title` duplicating `label` is the common case and resolves via the |
| 421 | + // label fallback — only emit it when the two genuinely differ. |
| 422 | + if (typeof props.title === 'string' && props.title && props.title !== page.label) { |
| 423 | + pushEntry(out, ['pages', name, 'title'], props.title, 'page'); |
| 424 | + } |
| 425 | + if (typeof props.subtitle === 'string' && props.subtitle) { |
| 426 | + pushEntry(out, ['pages', name, 'subtitle'], props.subtitle, 'page'); |
| 427 | + } |
| 428 | + } |
| 429 | + } |
| 430 | + } |
| 431 | + |
399 | 432 | // ── Metadata configuration forms (Studio admin UI) ──────────────── |
400 | 433 | // Registry-driven: always included, independent of stack config. These |
401 | 434 | // emit under `metadataForms.<type>.*` so the generic renderer can pick |
|
0 commit comments