YYayaw

Modèles de données CMS

Modèles de données typés, localisés et publiables, et variables inline pour le contenu CMS.

Vue d'ensemble

Yayaw sépare les données de contenu CMS des design tokens.

  • /dashboard/content/design-tokens reste l'éditeur de design tokens.
  • /dashboard/content/data-models gère la structure scopée des modèles de données CMS.
  • /dashboard/content/data remplit et publie les entrées de ces modèles.

Les modèles de données CMS peuvent être scopés :

  • global : données partagées au niveau du site, contrôlées par global-data:manage.
  • organization : données de l'organisation active, contrôlées par page:manage ou sections:manage sur cette organisation.

Les valeurs proches de médias doivent être stockées comme URL ou chaînes. La médiathèque d'organisation reste scopée séparément. Utilisez Médiathèque pour les ressources binaires possédées par l'organisation et ne stockez que les URL résultantes dans les entrées de données globales ou d'organisation.

Rôle CMS

Les données CMS sont la couche source structurée pour le contenu qui a besoin d'un contrat stable : variables de site, menus, faits produit, faits d'organisation, collections réutilisables et champs liés par référence depuis les pages ou sections. Les entrées de données doivent contenir des valeurs, pas de la présentation. Les sections et les pages décident comment ces valeurs sont rendues.

Utilisez les modèles de données quand les auteurs ont besoin de champs typés, de localisation, de validation et d'un cycle de publication. Utilisez les ressources média pour les fichiers, les sections pour la composition visuelle réutilisable et les design tokens pour les valeurs de thème visuel.

Contrats de document

Les données globales utilisent deux documents JSON versionnés :

  • GlobalDataModelDocumentV1
  • GlobalDataEntryDocumentV1

Les documents de modèle définissent :

  • cardinality : singleton ou collection
  • fields : champs typés inspirés des champs personnalisés Builder
  • localization : locale par défaut et locales prises en charge
  • presentation : headless, builtin_renderer ou component_recipe

La cardinalité contrôle combien d'entrées un modèle peut posséder :

  • singleton : une entrée globale, par exemple header-menu, footer-menu ou les paramètres SEO par défaut.
  • collection : plusieurs entrées, par exemple produits, auteurs, redirections ou annonces réutilisables.

La présentation contrôle comment les données sont consommées :

  • headless : données structurées uniquement, consommées par les pages, bindings, helpers ou code personnalisé.
  • builtin_renderer : données rendues par les composants runtime Yayaw, actuellement utilisées par les modèles internes comme l'en-tête et le pied de page.
  • component_recipe : données destinées à être associées à une recette UI réutilisable dans un workflow ultérieur.

Chaque champ prend en charge les réglages de style Builder attendus par les éditeurs :

  • type
  • localisation
  • valeur par défaut
  • texte d'aide
  • obligatoire/facultatif
  • valeurs enum pour les champs select
  • champs masqués qui restent hors de l'édition d'entrée tout en préservant les valeurs/défauts

Les champs masqués obligatoires doivent définir une valeur par défaut afin que les éditeurs d'entrées générés ne puissent pas créer de brouillons impossibles.

Les types de champs V1 pris en charge sont :

  • text, long_text, url, file
  • number, boolean, select, color
  • rich_text, html, date, timestamp
  • list, reference, map, javascript, code, tags, json

file stocke une URL/chaîne en V1 parce que la médiathèque est scopée par organisation alors que les données globales sont à l'échelle du site.

Les documents d'entrée stockent les valeurs comme :

  • shared : une valeur pour toutes les locales
  • localized : valeurs par locale avec fallback de la locale demandée vers la locale par défaut, puis vers la première locale disponible

Stockage

Les données CMS utilisent des révisions immuables et une ligne de publication par élément de registre.

Tables :

  • global_data_model_registry_items
  • global_data_model_revisions
  • global_data_model_publications
  • global_data_entry_registry_items
  • global_data_entry_revisions
  • global_data_entry_publications

Les révisions sont dédupliquées par hash et validées avant publication. Les publications peuvent être draft, published ou archived.

global_data_model_registry_items stocke scope et organization_id. Les lignes existantes sont migrées vers scope = "global". Les slugs sont uniques par scope global ou par scope d'organisation.

Édition dans le dashboard

Le dashboard fournit :

  • liste de modèles avec Yayaw Table
  • éditeur de champs de modèle pleine largeur
  • liste dédiée d'entrées sur /dashboard/content/data
  • éditeur d'entrée généré de style inspecteur, basé sur les champs du modèle sélectionné
  • actions d'enregistrement, publication et archivage
  • rendu lecture seule pour les utilisateurs sans permission de gestion

L'édition exige :

  • global-data:manage pour les modèles globaux.
  • page:manage ou sections:manage sur l'organisation active pour les modèles d'organisation.

L'accès lecture seule est disponible via :

  • global-data:read ou global-data:list pour les modèles globaux.
  • page:read, page:list, sections:read ou sections:list pour les modèles d'organisation.

API runtime

Les helpers serveur vivent dans :

  • src/lib/server/services/global-data

Helpers clés :

  • getPublishedGlobalDataEntry
  • getPublishedGlobalDataValue
  • listPublishedGlobalDataEntries
  • listPublishedGlobalDataFieldReferences

La plupart des helpers acceptent scope et organizationId quand des données scopées par organisation sont nécessaires. Les bindings Page Builder existants global_data_field et global_data_query continuent de résoudre les données globales.

Le composant de slot runtime partagé vit dans :

  • src/blocks/shared/global-data-slot.tsx

Il résout une entrée singleton publiée et retombe sur l'UI statique existante quand aucune donnée publiée n'est disponible.

En-tête et pied de page

L'en-tête et le pied de page ne sont plus des entrées de catalogue de sections initialisées ni des props de page éditables sur chaque page.

Le script de seed crée deux modèles singleton à moteur de rendu intégré :

  • header-menu
  • footer-menu

Les deux reçoivent une entrée main par défaut.

Les en-têtes et pieds de page générés lient ces entrées singleton avec des props global_data_field. Les pages partagent donc une source de vérité localisée pour les libellés de marque, URL de marque propres aux locales, éléments de menu, tagline de pied de page et texte de copyright.

Le champ items_json est édité avec un éditeur visuel de menu dans le formulaire d'entrée de données globales sur /dashboard/content/data. Il prend en charge les liens de premier niveau, les groupes avec un niveau de liens imbriqués, les bascules de thème, les bascules de langue, les actions de compte, les contrôles de réordonnancement et un panneau de récupération JSON brut. Les éléments d'en-tête peuvent cibler les zones gauche, centre ou droite ; l'entrée par défaut utilise le lien de marque à gauche, la navigation au centre et les actions thème/langue/compte à droite. Les entrées par défaut header-menu/main et footer-menu/main organisent la navigation publique en deux groupes de premier niveau :

  • Yayaw : /[locale]/codebase, /[locale]/pricing et /[locale]/docs
  • Yayaw Table : /[locale]/table, /[locale]/table/example, /[locale]/docs/table et /[locale]/docs/table/installation

Gardez les liens de documentation développeur Yayaw Table sous le sous-projet Fumadocs /[locale]/docs/table/*. Les pages CMS /[locale]/table/* restent les surfaces publiques de produit et de démo. Les anciennes lignes de seed site-header, site-footer, builtin-header et builtin-footer sont supprimées par migration. Les documents de page hérités qui référencent encore les anciennes sections intégrées sont normalisés en noeuds d'en-tête/pied de page générés.

Bindings Page Builder

Les bindings de props de page prennent en charge les données globales en plus des littéraux et des ressources média.

Types de bindings :

  • global_data_field : lie un champ d'une entrée publiée
  • global_data_query : résout une collection publiée et extrait optionnellement un champ

L'inspecteur Puck expose un contrôle "Bind data" quand des références de données globales publiées sont disponibles. La V1 lie des champs complets.

Les variables CMS inline peuvent aussi être insérées dans les valeurs texte depuis le catalogue d'en-tête du dashboard sur les routes /dashboard/content/*. Les tokens sont namespacés pour éviter les collisions :

  • {site.name}
  • {organization.logo}
  • {data.global.header-menu.main.brand_label}
  • {data.organization.<modelSlug>.<entrySlug>.<fieldKey>}
  • {billing.product.pro_monthly.name}
  • {billing.price.pro_monthly.id}

Les tokens inconnus restent inchangés au runtime. Les tokens non namespacés comme {name} sont ignorés en V1.

Les variables de site sont éditées via l'entrée CMS Data du modèle singleton intégré site-variables/main. Ses champs par défaut sont verrouillés afin que la surface d'édition puisse changer les valeurs sans supprimer le contrat canonique site.*. Chaque champ conserve néanmoins son propre réglage de localisation : les valeurs techniques comme base_url, domain et les URL de logo restent partagées, tandis que les valeurs éditoriales comme description et title peuvent être localisées et se résoudre selon la locale demandée. Les valeurs system_settings existantes sous cms.site-variables.v1 restent des données de migration et de fallback.

Les variables de produits et prix de facturation sont lues depuis le catalogue runtime des produits de facturation. Les métadonnées produit viennent de billing_products quand elles sont configurées, tandis que les identifiants de prix Stripe sont stockés en interne après la synchronisation du catalogue admin ou MCP. Les variables d'organisation sont lues depuis la ligne Better Auth organization ; l'édition continue de vivre dans les flux de paramètres d'organisation.

La collection globale intégrée billing-product-content complète le catalogue de facturation avec une entrée CMS par clé produit. Son champ verrouillé catalog_product_key lie l'entrée CMS à Products & Services; les champs éditoriaux comme badge, résumé, bullets de fonctionnalités, label CTA et état de mise en avant peuvent être localisés. Ne stockez pas les noms de produits, prix, intervalles, IDs Stripe ou disponibilité checkout dans ce modèle. Résolvez ces faits depuis les namespaces de variables billing.product.* et billing.price.*.

Le seed porte aussi le snapshot de données globales exporté depuis Supabase pour les variables de site production, les menus header/footer et le singleton sales-offer/main. Ces entrées sont publiées à chaque run de seed afin que les bases production ou preview fraîches résolvent les mêmes variables CMS que celles authorées dans Supabase. Les entrées billing-product-content ne sont créées que lorsqu'elles manquent, afin de ne pas écraser ensuite le contenu éditorial produit par une baseline vide.

Invalidation du cache

Les données globales publiées sont mises en cache avec des tags :

  • global-data
  • global-data:{modelSlug}

Les actions de publication et d'archivage revalident les tags de données globales, les routes de données du dashboard, le chemin racine public et le cache runtime des pages publiées.

Validation

Vérifications utiles après des changements de modèles de données CMS :

bun test src/lib/shared/global-data/global-data.test.ts
bun run check
bunx tsc --noEmit