[Visualisation] Widget custom builder pour visualisation avancée

Comme c’est bientôt Noël, je vous propose un tutoriel pour apprendre à utiliser le Custom Widget Builder en buvant un petit chocolat chaud ! Si vous suivez toutes ces étapes vous pourrez créer le dashboard du Père Noël.

Si vous préférez voir directement le résultat (et que vous avez accès aux templates de la DINUM) c’est par là :santa:

Modèle de Noël

Et puisque tout ce qui compte à Noël ce sont les :gift: je vous partage le guide pour modifier le contenu et l’apparence de la fiche commande en bas de ce post.

:one: Création de la table

  1. Créez un nouveau document Grist
  2. Renommez la table en « Commandes_Noel »
  3. Créez les champs suivants :

id_commande
nom_enfant
type_commande (type Choix unique)
statut_commande (type Choix unique)
date_reception (type Date)
date_maj (type Date)
zone_livraison (type Choix unique)
delai_livraison
moyen_transport (type Choice)
equipe_lutins
lutin_responsable
lutin_assistant
renne_assigne (type Choix unique)
budget_matieres (type Numérique)
budget_lutins (type Numérique)
budget_total (type formule : budget_matieres +budget_lutins)
notes_budget
alerte_budget
niveau_sagesse (type Choix unique)
points_bonus (type Numérique)
priorite_livraison (type Choix unique)

  1. Pour chaque champ Choix unique copiez les options suivantes :

type_commande :

:gift: Cadeau simple
:video_game: Jouet électronique
:books: Livre/Culture
:teddy_bear: Peluche/Doudou
:bike: Jeu d’extérieur
:art: Activité créative

statut_commande :

:memo: Lettre reçue
:eyes: En cours d’étude
:factory: En fabrication
:sparkles: En test qualité
:gift: Prêt pour emballage
:sled: Prêt pour livraison
:christmas_tree: Livré

niveau_sagesse :

:innocent: Très sage
:blush: Sage
:neutral_face: Quelques bêtises
:sweat_smile: À surveiller

zone_livraison :

:mountain_snow: Pôle Nord
:earth_africa: Europe
:earth_americas: Amériques
:earth_asia: Asie-Pacifique

moyen_transport :

:sled: Traîneau standard
:airplane: Traîneau supersonique
:helicopter: Mini-traîneau héliporté
:deer: Renne solo

renne_assigne :

Oui
Non

priorite_livraison :

:star: Ultra prioritaire
:star2: Très prioritaire
:sparkles: Priorité normale
:dizzy: Non urgent

:two: Configuration du Widget

  1. Cliquez sur « Nouveau »
  2. Ajoutez une vue à la page : Personnalisée
  3. Liez le widget à la table « Commandes_Noel »
  4. Sélectionnez le custom widget builder
  5. Cliquez sur « Ouvrir la configuration »
  6. Dans l’onglet HTML, supprimez le code présent et collez celui-ci :
    `

  <!-- Ligne 2 : Référence + Type + État -->
  <div class="flex items-center gap-4 text-left mb-3">
    <div>
      <span class="text-yellow-200">🎅</span>
      <span id="id_commande" class="text-yellow-200 font-bold"></span>
    </div>
    <div>
      <span class="text-yellow-200"></span>
      <span id="type_commande" class="px-3 py-1 rounded-md text-sm font-medium"></span>
    </div>
    <div>
      <span class="text-yellow-200"></span>
      <span id="statut_commande" class="px-3 py-1 rounded-md text-sm font-medium"></span>
    </div>
  </div>

  <!-- Ligne 3 : Atelier + Zone + Délai -->
  <div class="mt-2 pt-2 border-t border-yellow-300">
    <div class="grid grid-cols-3 gap-4 mb-3">
      <div>
        <span class="text-yellow-200">🏭 Atelier</span>
        <div id="equipe_lutins" class="font-medium"></div>
      </div>
      <div>
        <span class="text-yellow-200">🌍 Zone de livraison</span>
        <div id="zone_livraison" class="font-medium"></div>
      </div>
      <div class="text-right">
        <span class="text-yellow-200">📅 Délai</span>
        <div id="delai_livraison" class="font-medium"></div>
      </div>
    </div>
  
    <!-- Dates en bas à droite -->
    <div class="flex justify-end text-xs text-yellow-200/80">
      <div class="text-right">
        <div>Lettre reçue le <span id="date_reception"></span></div>
        <div>Dernière mise à jour <span id="date_maj"></span></div>
      </div>
    </div>
  </div>
</div>

<!-- Section Budget -->
<div class="bg-[#F9F9F9] rounded-lg shadow p-4 mt-4 text-[#1B4D3E] border-2 border-[#B8860B]">
  <h2 class="text-lg font-semibold mb-4">✨ Budget Fabrication</h2>
  <div class="grid grid-cols-2 gap-6">
    <!-- Première colonne -->
    <div class="space-y-2">
      <div class="flex justify-between p-2 bg-green-50 rounded">
        <span>Matières premières</span>
        <span id="budget_matieres" class="font-extralight"></span>
      </div>
      <div class="flex justify-between p-2 bg-green-50 rounded">
        <span>Main d'œuvre lutins</span>
        <span id="budget_lutins" class="font-extralight"></span>
      </div>
    </div>
    
    <!-- Deuxième colonne -->
    <div class="space-y-3">
      <div class="p-2 bg-green-50 rounded">
        <div class="text-gray-600 mb-1">Notes spéciales :</div>
        <div id="notes_budget" class="font-medium"></div>
      </div>
    </div>
  </div>

  <!-- Total fabrication -->
  <div class="mt-2 pt-2 border-t border-[#1B4D3E]">
    <div class="grid grid-cols-12 gap-4 items-center px-2">
      <div class="col-span-3">
        <span class="text-xl font-semibold">Budget total fabrication</span>
      </div>
      <div class="col-span-3 text-left">
        <span id="budget_total" class="text-xl font-bold"></span>
      </div>
      <div class="col-span-6">
        <span id="alerte_budget" class="text-sm text-red-500"></span>
      </div>
    </div>
  </div>
</div>

<!-- Section Priorités -->
<div class="bg-[#F9F9F9] rounded-lg shadow p-4 mt-4 text-[#1B4D3E] border-2 border-[#B8860B]">
  <h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
    🎄 Priorités de livraison
  </h2>
  <div class="space-y-2">
    <div class="p-2 bg-green-50 rounded">
      <div class="text-sm text-gray-600 mb-1">Niveau priorité</div>
      <div id="priorite_livraison" class="font-medium"></div>
    </div>
    <div class="p-2 bg-green-50 rounded">
      <div class="text-sm text-gray-600 mb-1">Transport prévu</div>
      <div id="moyen_transport" class="font-medium"></div>
    </div>
  </div>
</div>

<!-- Section Évaluation -->
<div class="bg-[#F9F9F9] rounded-lg shadow p-4 mt-4 text-[#1B4D3E] border-2 border-[#B8860B]">
  <h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
    😇 Évaluation Sagesse
  </h2>
  <div class="grid grid-cols-2 gap-6">
    <div>
      <div class="flex justify-between p-2 bg-green-50 rounded">
        <span>Niveau de sagesse</span>
        <span id="niveau_sagesse" class="font-medium"></span>
      </div>
    </div>
    <div>
      <div class="flex justify-between p-2 bg-green-50 rounded">
        <span>Points bonus</span>
        <span id="points_bonus" class="font-medium"></span>
      </div>
    </div>
  </div>
</div>

<!-- Section Équipe Lutins -->
<div class="bg-[#F9F9F9] rounded-lg shadow p-4 mt-4 text-[#1B4D3E] border-2 border-[#B8860B]">
  <h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
    🧝‍♂️ Équipe Assignée
  </h2>
  <div class="grid grid-cols-2 gap-6">
    <!-- Colonne Contacts -->
    <div class="space-y-3">
      <div class="p-3 bg-green-50 rounded-lg">
        <div class="flex items-center gap-2">
          🎅 <span class="text-gray-600">Lutin responsable :</span>
          <span id="lutin_responsable" class="font-medium"></span>
        </div>
      </div>
      <div class="p-3 bg-green-50 rounded-lg">
        <div class="flex items-center gap-2">
          🧝‍♀️ <span class="text-gray-600">Lutin assistant :</span>
          <span id="lutin_assistant" class="font-medium"></span>
        </div>
      </div>
    </div>
    <!-- Colonne Renne -->
    <div>
      <div class="p-4 bg-red-50 rounded-lg">
        <div class="flex items-center justify-between">
          <span class="font-medium">Renne assigné</span>
          <span id="renne_assigne" class="font-medium"></span>
        </div>
      </div>
    </div>
  </div>
</div>

<!-- Section Documents -->
<div class="bg-[#F9F9F9] rounded-lg shadow p-4 mt-4 text-[#1B4D3E] border-2 border-[#B8860B]">
  <h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
    📜 Documents
  </h2>
  <div class="grid grid-cols-2 gap-6">
    <!-- Colonne 1 -->
    <div class="space-y-3">
      <div class="flex items-center gap-2 p-3 bg-green-50 rounded">
        <span class="text-[#8B0000]">✉️</span>
        <span>Lettre au Père Noël</span>
        <span id="lettre_enfant" class="ml-2 px-2 py-1 text-xs rounded-full"></span>
      </div>
      <div class="flex items-center gap-2 p-3 bg-green-50 rounded">
        <span class="text-[#8B0000]">🎨</span>
        <span>Dessin</span>
        <span id="dessin_enfant" class="ml-2 px-2 py-1 text-xs rounded-full"></span>
      </div>
      <div class="flex items-center gap-2 p-3 bg-green-50 rounded">
        <span class="text-[#8B0000]">📋</span>
        <span>Liste détaillée</span>
        <span id="liste_cadeaux" class="ml-2 px-2 py-1 text-xs rounded-full"></span>
      </div>
    </div>
    <!-- Colonne 2 -->
    <div class="space-y-3">
      <div class="flex items-center gap-2 p-3 bg-green-50 rounded">
        <span class="text-[#8B0000]">📝</span>
        <span>Notes des lutins</span>
        <span id="notes_lutins" class="ml-2 px-2 py-1 text-xs rounded-full"></span>
      </div>
      <div class="flex items-center gap-2 p-3 bg-green-50 rounded">
        <span class="text-[#8B0000]">⭐</span>
        <span>Certificat de sagesse</span>
        <span id="certificat_sagesse" class="ml-2 px-2 py-1 text-xs rounded-full"></span>
      </div>
      <div class="mt-6">
        <a id="lien_dossier" href="#" target="_blank" 
           class="inline-flex items-center gap-2 px-4 py-2 bg-red-50 hover:bg-red-100 rounded transition-colors text-[#8B0000]">
          🎄 <span class="underline">Voir dossier complet</span>
        </a>
      </div>
    </div>
  </div>
</div>
`
  1. Dans l’onglet JavaScript, supprimez le code existant et collez celui-ci :
function formatDate(date) {
  if (!date) return '';
  return new Date(date).toLocaleDateString('fr-FR', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  });
}

function formatMoney(amount) {
  if (!amount) return '0 🎄';
  return new Intl.NumberFormat('fr-FR', {
    style: 'currency',
    currency: 'EUR',
    minimumFractionDigits: 0
  }).format(amount).replace('€', '🎄');
}

function getStateColor(state) {
  const stateColors = {
    'Lettre reçue': 'bg-[#FFD700]',
    'En cours d\'étude': 'bg-[#1B4D3E]',
    'En fabrication': 'bg-[#8B0000]',
    'En test qualité': 'bg-[#B8860B]',
    'Prêt pour emballage': 'bg-[#228B22]',
    'Prêt pour livraison': 'bg-[#4169E1]',
    'Livré': 'bg-[#9B0000]'
  };
  return stateColors[state] || 'bg-[#FFD700]';
}

function getTypeColor(type) {
  const typeColors = {
    'Cadeau simple': 'bg-[#228B22]',
    'Jouet électronique': 'bg-[#4169E1]',
    'Livre/Culture': 'bg-[#8B4513]',
    'Peluche/Doudou': 'bg-[#FF69B4]',
    'Jeu d\'extérieur': 'bg-[#32CD32]',
    'Activité créative': 'bg-[#FF8C00]'
  };
  return typeColors[type] || 'bg-[#FFD700]';
}

grist.ready({
  requiredTables: ['Commandes_Noel']
});

grist.onRecord(function(record) {
  if (!record) return;
  
  // État de la commande
  const etatElement = document.getElementById('statut_commande');
  etatElement.textContent = record.statut_commande || '';
  etatElement.className = 'px-3 py-1 rounded-full text-sm font-medium text-white ' + getStateColor(record.statut_commande);

  // Type de commande
  const typeElement = document.getElementById('type_commande');
  typeElement.textContent = record.type_commande || '';
  typeElement.className = 'px-3 py-1 rounded-full text-sm font-medium text-white ' + getTypeColor(record.type_commande);

  // Bandeau
  document.getElementById('id_commande').textContent = record.id_commande || '';
  document.getElementById('nom_enfant').textContent = record.nom_enfant || '';
  document.getElementById('equipe_lutins').textContent = record.equipe_lutins || '';
  document.getElementById('zone_livraison').textContent = record.zone_livraison || '';
  document.getElementById('delai_livraison').textContent = record.delai_livraison || '';
  document.getElementById('date_reception').textContent = formatDate(record.date_reception);
  document.getElementById('date_maj').textContent = formatDate(record.date_maj);

  // Priorités
  document.getElementById('priorite_livraison').textContent = record.priorite_livraison || '';
  document.getElementById('moyen_transport').textContent = record.moyen_transport || '';

  // Budget
  document.getElementById('budget_matieres').textContent = formatMoney(record.budget_matieres);
  document.getElementById('budget_lutins').textContent = formatMoney(record.budget_lutins);
  document.getElementById('notes_budget').textContent = record.notes_budget || 'Aucune note';
  document.getElementById('budget_total').textContent = formatMoney(record.budget_total);
  document.getElementById('alerte_budget').textContent = record.alerte_budget || '';

  // Évaluation sagesse
  document.getElementById('niveau_sagesse').textContent = record.niveau_sagesse || '';
  document.getElementById('points_bonus').textContent = record.points_bonus || '';

  // Équipe
  const renneElement = document.getElementById('renne_assigne');
  const renneValue = record.renne_assigne;
  if (renneValue === 'Oui') {
    renneElement.textContent = '✅ Assigné';
    renneElement.className = 'font-medium text-green-600';
  } else {
    renneElement.textContent = '❌ Non assigné';
    renneElement.className = 'font-medium text-red-600';
  }
  
  document.getElementById('lutin_responsable').textContent = record.lutin_responsable || '';
  document.getElementById('lutin_assistant').textContent = record.lutin_assistant || '';

  // Documents
  ['lettre_enfant', 'dessin_enfant', 'liste_cadeaux', 'notes_lutins', 'certificat_sagesse'].forEach(id => {
    const element = document.getElementById(id);
    if (element) {
      if (record[id]) {
        element.textContent = 'Document présent';
        element.className = 'ml-2 px-2 py-1 text-xs rounded-full bg-green-100 text-green-800';
      } else {
        element.textContent = 'Manquant';
        element.className = 'ml-2 px-2 py-1 text-xs rounded-full bg-red-100 text-red-800';
      }
    }
  });

  // Lien dossier
  const lienElement = document.getElementById('lien_dossier');
  if (record.lien_dossier) {
    lienElement.href = record.lien_dossier || '#';
    lienElement.target = '_blank';
  }
});
  1. Cliquez sur « Preview » et enregistrez

:three: Test avec données d’exemple

Copiez ces 10 lignes dans votre table (disponible dans les commentaires ci-dessous).

1	Emma Dubois	🎮 Jouet électronique	📝 Lettre reçue	2023-12-01	2023-12-02	🌍 Europe	Urgent	🛷 Traîneau standard	Atelier high-tech	Pixel	Micro	Oui	250	150	400	Budget standard	Aucune	😇 Très sage	5	⭐ Ultra prioritaire
2	Lucas Martin	📚 Livre/Culture	🏭 En fabrication	2023-12-02	2023-12-03	🌎 Amériques	Normal	✈️ Traîneau supersonique	Atelier culture	Story	Read	Non	120	80	200	Budget réduit	Aucune	😊 Sage	3	💫 Non urgent
3	Sofia Patel	🚲 Jeu d'extérieur	✨ En test qualité	2023-12-01	2023-12-04	🌏 Asie-Pacifique	Urgent	🚁 Mini-traîneau héliporté	Atelier jouets bois	Sport	Active	Non	290	160	450	Matériaux spéciaux	Attention au délai	😐 Quelques bêtises	2	✨ Priorité normale
4	Amir Hassan	🎨 Activité créative	🛷 Prêt pour livraison	2023-12-02	2023-12-05	🌍 Europe	Normal	🦌 Renne solo	Atelier créatif	Create	Make	Oui	180	120	300	RAS	Aucune	😇 Très sage	4	🌟 Très prioritaire
5	Léa Chen	🎮 Jouet électronique	👀 En cours d'étude	2023-12-03	2023-12-04	🌏 Asie-Pacifique	Urgent	✈️ Traîneau supersonique	Atelier high-tech	Tech	Code	Non	200	150	350	Budget majoré	Aucune	😊 Sage	3	⭐ Ultra prioritaire
6	Mohamed Dubois	🧸 Peluche/Doudou	🏭 En fabrication	2023-12-01	2023-12-05	🌍 Europe	Normal	🛷 Traîneau standard	Atelier peluches	Soft	Cuddle	Oui	300	200	500	Matériaux rares	Attention budget	😐 Quelques bêtises	2	✨ Priorité normale
7	Julie N'Guyen	📚 Livre/Culture	📝 Lettre reçue	2023-12-02	2023-12-03	🌍 Afrique	Urgent	🚁 Mini-traîneau héliporté	Atelier culture	Book	Write	Non	280	180	460	RAS	Aucune	😇 Très sage	5	🌟 Très prioritaire
8	Thomas Lee	🚲 Jeu d'extérieur	✨ En test qualité	2023-12-03	2023-12-05	🌏 Asie-Pacifique	Normal	🦌 Renne solo	Atelier jouets bois	Build	Craft	Oui	150	100	250	Budget standard	Aucune	😊 Sage	3	💫 Non urgent
9	Maria Garcia	🎨 Activité créative	🛷 Prêt pour livraison	2023-12-01	2023-12-04	🌎 Amériques	Urgent	✈️ Traîneau supersonique	Atelier créatif	Art	Paint	Non	110	70	180	RAS	Aucune	😇 Très sage	4	⭐ Ultra prioritaire
10	Yuki Tanaka	🎮 Jouet électronique	👀 En cours d'étude	2023-12-02	2023-12-03	🌏 Asie-Pacifique	Normal	🛷 Traîneau standard	Atelier high-tech	Robot	Circuit	Oui	160	110	270	Budget réduit	Aucune	😐 Quelques bêtises	2	✨ Priorité normale

:tada: Si tout est bien configuré, vous devriez voir apparaître le dashboard de Noël ! Mais, bien sûr cet exemple n’est qu’un pretexte, vous pouvez adapter ce format d’affichage à vos besoins.

:art: Personnalisation

Modifier les couleurs

Fond principal

<body class="bg-[#C41E3A] min-h-screen">

Changez #C41E3A pour une autre couleur

Bandeau supérieur

<div class="bg-gradient-to-r from-[#7BBF6A] to-[#016936] rounded-lg">

Modifiez #7BBF6A et #016936 pour changer le dégradé

Sections

<div class="bg-[#F9F9F9] rounded-lg shadow p-4 mt-4 text-[#1B4D3E] border-2 border-[#B8860B]">
  • #F9F9F9 : fond des sections
  • #1B4D3E : couleur du texte
  • #B8860B : couleur des bordures

Ajouter une section

  1. Copiez ce template :
<div class="bg-[#F9F9F9] rounded-lg shadow p-4 mt-4 text-[#1B4D3E] border-2 border-[#B8860B]">
  <h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
    🎄 Titre Section
  </h2>
  <div class="grid grid-cols-2 gap-6">
    <div class="p-2 bg-green-50 rounded">
      <span>Label</span>
      <span id="nom_champ" class="font-medium"></span>
    </div>
  </div>
</div>
  1. Ajoutez dans le JavaScript :
document.getElementById('nom_champ').textContent = record.nom_champ || '';

Supprimer une section

  1. Identifiez la section dans le HTML (entre <div class="bg-[#F9F9F9]..."> et son </div> correspondant)
  2. Supprimez tout le bloc
  3. Supprimez les références correspondantes dans le JavaScript

Modifier les icônes

Cherchez les emojis (:christmas_tree:, :santa:, etc.) et remplacez-les par d’autres

Format des données

Les dates utilisent :

formatDate(record.date)

Les montants utilisent :

formatMoney(record.montant)

Statuts et couleurs

Modifiez les couleurs des statuts dans le JavaScript :

function getStateColor(state) {
  const stateColors = {
    'Statut 1': 'bg-[#COULEUR]',
    // etc.
  };
}

Personnalisations avancées

Modification des tableaux de données

<div class="grid grid-cols-2 gap-6">
  • grid-cols-2 : nombre de colonnes (1-12)
  • gap-6 : espacement entre éléments (1-12)

Styles de cartes

<div class="p-4 bg-green-50 rounded-lg">
  • p-4 : padding (1-12)
  • rounded-lg : arrondis (sm, md, lg, full)
  • bg-green-50 : intensité couleur (50-900)

Typographie

<span class="text-lg font-semibold text-gray-600">
  • text-lg : taille (sm, base, lg, xl, 2xl…)
  • font-semibold : graisse (light, normal, medium, bold…)
  • text-gray-600 : couleur texte

Effets visuels

<div class="shadow hover:shadow-lg transition-all">
  • shadow : ombre (sm, md, lg, xl)
  • hover: : effet au survol
  • transition-all : animation

Styles d’alertes

<div class="bg-red-100 text-red-800 p-2 rounded">
  • Rouge : erreur
  • Jaune : alerte
  • Vert : succès
  • Bleu : info

Badges et états

<span class="px-2 py-1 text-xs rounded-full">

Modifiez :

  • Couleurs : bg-[#COULEUR]
  • Taille : text-xs, text-sm
  • Padding : px-2, py-1

Espacements dynamiques

<div class="space-y-3">
  • space-y-3 : vertical (1-12)
  • space-x-3 : horizontal (1-12)

Breakpoints responsifs

Préfixes :

  • sm: : >640px
  • md: : >768px
  • lg: : >1024px
  • xl: : >1280px

Exemple :

<div class="md:grid-cols-2 lg:grid-cols-3">

Ajouter des animations

<div class="transition-all duration-300 hover:scale-105">
  • duration-300 : vitesse (100-1000)
  • scale-105 : zoom (95-125)
  • rotate-3 : rotation
  • translate-y-1 : déplacement

Amusez-vous bien :wink:

6 « J'aime »

Génial Céline merci !

Je pose aussi le template ici pour que tout le monde puisse y accéder :
Template Widget Visualisation (1).grist (596 Ko)

1 « J'aime »

Pardon vous devez utiliser ce html le précédent n’était pas complet :wink:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script src="https://docs.getgrist.com/grist-plugin-api.js"></script>
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-[#C41E3A] min-h-screen">
  <div class="w-full max-w-5xl mx-auto p-4 pt-8">
    <!-- Bandeau -->
    <div class="bg-gradient-to-r from-[#7BBF6A] to-[#016936] rounded-lg p-4 text-white sticky top-0 z-50 backdrop-filter backdrop-blur shadow-lg">
      <!-- Ligne 1 : Nom de l'enfant -->
      <div class="mb-5">
        <h1 id="nom_enfant" class="text-3xl font-bold"></h1>
      </div>
      
      <!-- Ligne 2 : Référence + Type + État -->
      <div class="flex items-center gap-4 text-left mb-3">
        <div>
          <span class="text-yellow-200">🎅</span>
          <span id="id_commande" class="text-yellow-200 font-bold"></span>
        </div>
        <div>
          <span class="text-yellow-200"></span>
          <span id="type_commande" class="px-3 py-1 rounded-md text-sm font-medium"></span>
        </div>
        <div>
          <span class="text-yellow-200"></span>
          <span id="statut_commande" class="px-3 py-1 rounded-md text-sm font-medium"></span>
        </div>
      </div>

      <!-- Ligne 3 : Atelier + Zone + Délai -->
      <div class="mt-2 pt-2 border-t border-yellow-300">
        <div class="grid grid-cols-3 gap-4 mb-3">
          <div>
            <span class="text-yellow-200">🏭 Atelier</span>
            <div id="equipe_lutins" class="font-medium"></div>
          </div>
          <div>
            <span class="text-yellow-200">🌍 Zone de livraison</span>
            <div id="zone_livraison" class="font-medium"></div>
          </div>
          <div class="text-right">
            <span class="text-yellow-200">📅 Délai</span>
            <div id="delai_livraison" class="font-medium"></div>
          </div>
        </div>
      
        <!-- Dates en bas à droite -->
        <div class="flex justify-end text-xs text-yellow-200/80">
          <div class="text-right">
            <div>Lettre reçue le <span id="date_reception"></span></div>
            <div>Dernière mise à jour <span id="date_maj"></span></div>
          </div>
        </div>
      </div>
    </div>

    <!-- Section Budget -->
    <div class="bg-[#F9F9F9] rounded-lg shadow p-4 mt-4 text-[#1B4D3E] border-2 border-[#B8860B]">
      <h2 class="text-lg font-semibold mb-4">✨ Budget Fabrication</h2>
      <div class="grid grid-cols-2 gap-6">
        <!-- Première colonne -->
        <div class="space-y-2">
          <div class="flex justify-between p-2 bg-green-50 rounded">
            <span>Matières premières</span>
            <span id="budget_matieres" class="font-extralight"></span>
          </div>
          <div class="flex justify-between p-2 bg-green-50 rounded">
            <span>Main d'œuvre lutins</span>
            <span id="budget_lutins" class="font-extralight"></span>
          </div>
        </div>
        
        <!-- Deuxième colonne -->
        <div class="space-y-3">
          <div class="p-2 bg-green-50 rounded">
            <div class="text-gray-600 mb-1">Notes spéciales :</div>
            <div id="notes_budget" class="font-medium"></div>
          </div>
        </div>
      </div>

      <!-- Total fabrication -->
      <div class="mt-2 pt-2 border-t border-[#1B4D3E]">
        <div class="grid grid-cols-12 gap-4 items-center px-2">
          <div class="col-span-3">
            <span class="text-xl font-semibold">Budget total fabrication</span>
          </div>
          <div class="col-span-3 text-left">
            <span id="budget_total" class="text-xl font-bold"></span>
          </div>
          <div class="col-span-6">
            <span id="alerte_budget" class="text-sm text-red-500"></span>
          </div>
        </div>
      </div>
    </div>

    <!-- Section Priorités -->
    <div class="bg-[#F9F9F9] rounded-lg shadow p-4 mt-4 text-[#1B4D3E] border-2 border-[#B8860B]">
      <h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
        🎄 Priorités de livraison
      </h2>
      <div class="space-y-2">
        <div class="p-2 bg-green-50 rounded">
          <div class="text-sm text-gray-600 mb-1">Niveau priorité</div>
          <div id="priorite_livraison" class="font-medium"></div>
        </div>
        <div class="p-2 bg-green-50 rounded">
          <div class="text-sm text-gray-600 mb-1">Transport prévu</div>
          <div id="moyen_transport" class="font-medium"></div>
        </div>
      </div>
    </div>

    <!-- Section Évaluation -->
    <div class="bg-[#F9F9F9] rounded-lg shadow p-4 mt-4 text-[#1B4D3E] border-2 border-[#B8860B]">
      <h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
        😇 Évaluation Sagesse
      </h2>
      <div class="grid grid-cols-2 gap-6">
        <div>
          <div class="flex justify-between p-2 bg-green-50 rounded">
            <span>Niveau de sagesse</span>
            <span id="niveau_sagesse" class="font-medium"></span>
          </div>
        </div>
        <div>
          <div class="flex justify-between p-2 bg-green-50 rounded">
            <span>Points bonus</span>
            <span id="points_bonus" class="font-medium"></span>
          </div>
        </div>
      </div>
    </div>

    <!-- Section Équipe Lutins -->
    <div class="bg-[#F9F9F9] rounded-lg shadow p-4 mt-4 text-[#1B4D3E] border-2 border-[#B8860B]">
      <h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
        🧝‍♂️ Équipe Assignée
      </h2>
      <div class="grid grid-cols-2 gap-6">
        <!-- Colonne Contacts -->
        <div class="space-y-3">
          <div class="p-3 bg-green-50 rounded-lg">
            <div class="flex items-center gap-2">
              🎅 <span class="text-gray-600">Lutin responsable :</span>
              <span id="lutin_responsable" class="font-medium"></span>
            </div>
          </div>
          <div class="p-3 bg-green-50 rounded-lg">
            <div class="flex items-center gap-2">
              🧝‍♀️ <span class="text-gray-600">Lutin assistant :</span>
              <span id="lutin_assistant" class="font-medium"></span>
            </div>
          </div>
        </div>
        <!-- Colonne Renne -->
        <div>
          <div class="p-4 bg-red-50 rounded-lg">
            <div class="flex items-center justify-between">
              <span class="font-medium">Renne assigné</span>
              <span id="renne_assigne" class="font-medium"></span>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- Section Documents -->
    <div class="bg-[#F9F9F9] rounded-lg shadow p-4 mt-4 text-[#1B4D3E] border-2 border-[#B8860B]">
      <h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
        📜 Documents
      </h2>
      <div class="grid grid-cols-2 gap-6">
        <!-- Colonne 1 -->
        <div class="space-y-3">
          <div class="flex items-center gap-2 p-3 bg-green-50 rounded">
            <span class="text-[#8B0000]">✉️</span>
            <span>Lettre au Père Noël</span>
            <span id="lettre_enfant" class="ml-2 px-2 py-1 text-xs rounded-full"></span>
          </div>
          <div class="flex items-center gap-2 p-3 bg-green-50 rounded">
            <span class="text-[#8B0000]">🎨</span>
            <span>Dessin</span>
            <span id="dessin_enfant" class="ml-2 px-2 py-1 text-xs rounded-full"></span>
          </div>
          <div class="flex items-center gap-2 p-3 bg-green-50 rounded">
            <span class="text-[#8B0000]">📋</span>
            <span>Liste détaillée</span>
            <span id="liste_cadeaux" class="ml-2 px-2 py-1 text-xs rounded-full"></span>
          </div>
        </div>
        <!-- Colonne 2 -->
        <div class="space-y-3">
          <div class="flex items-center gap-2 p-3 bg-green-50 rounded">
            <span class="text-[#8B0000]">📝</span>
            <span>Notes des lutins</span>
            <span id="notes_lutins" class="ml-2 px-2 py-1 text-xs rounded-full"></span>
          </div>
          <div class="flex items-center gap-2 p-3 bg-green-50 rounded">
            <span class="text-[#8B0000]">⭐</span>
            <span>Certificat de sagesse</span>
            <span id="certificat_sagesse" class="ml-2 px-2 py-1 text-xs rounded-full"></span>
          </div>
          <div class="mt-6">
            <a id="lien_dossier" href="#" target="_blank" 
               class="inline-flex items-center gap-2 px-4 py-2 bg-red-50 hover:bg-red-100 rounded transition-colors text-[#8B0000]">
              🎄 <span class="underline">Voir dossier complet</span>
            </a>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>
</html>'''

Vraiment très intéressant lorsque l’on débute avec Grist. J’ai quand même un problème avec ce template que je suis en train de modifier à ma sauce pour un suivi de produits. La fonction « équipe » semble ne pas fonctionner avec l’attribution « renne » la ligne

if (renneValue === ‹ Oui ›) {

ne semble pas être comprise par le script du coup on est toujours dans le cas d'un renne non assigné. J'ai testé sur ton portail d'essai et j'ai le meme comportement...Une solution ???

oui tu as raison, j’ai fait une erreur, je n’avais pas vu ;), le champ Renne assigné est de type booleen
donc il faut juste mettre if (renneValue) {

dis moi si ça marche mieux :wink:

C’est parfait. En plus j’apprends plein de tips avec ces exemples ! MERCI !

Bonjour à toutes et tous,

Meilleurs vœux pour cette nouvelle année.

J’ai profité des quelques jours de congés pour jouer avec ce bel outil qui nous a été présenté et ai essayé de l’adapté à mes besoins.

J’aurai quelques questions car je n’arrive pas avoir ce que je veux.

Je souhaite me servir de ce widget pour créer des fiches de synthèse pour le suivi de mes établissements. Pour cela je souhaitais requêter sur 3 tables (‹ ETAB ›, ‹ CONTACTS ›, ‹ FONCTIONS ›), est-ce possible ?
Si oui, comment faire ?

J’ai essayé de faire sans la première option (++ tables) en ne me servant que de ma table « Etabs » dans laquelle j’ai deux colonnes avec des lookuprecords (CONTACTS_Prenom_Nom et CONTACTS_Fonction) vers ma table « CONTACTS » pour récupérer les noms des personnes et leur fonction. J’arrive bien à récupérer tous les noms des contacts mais pas leurs fonctions, ou si j’arrive a récupéré la fonction, il ne récupère plus les noms.

Ci-dessous le code que j’ai écrit :
grist.ready({
requiredTables: [‹ ETAB ›, ‹ CONTACTS ›, ‹ FONCTIONS ›]
});

grist.onRecord(function (record) {
if (!record) return;

// EPLE
const etabElement = document.getElementById(‹ uai ›);
etabElement.textContent = record.UAI || ‹  ›;
etabElement.className = 'px-3 py-1 rounded-full text-sm font-medium text-white ';

// Bandeau
document.getElementById(‹ nom_etab ›).textContent = record.Nom_complet || ‹  ›;
document.getElementById(‹ email ›).textContent = record.email || ‹  ›;
document.getElementById(‹ ville ›).textContent = record.Ville || ‹  ›;
document.getElementById(‹ telephone ›).textContent = record.Telephone || ‹  ›;
document.getElementById(‹ edt ›).textContent = record.EDT || ‹  ›;
document.getElementById(‹ viesco ›).textContent = record.VIESCO || ‹  ›;
document.getElementById(‹ collectivite ›).textContent = record.Collectivite || ‹  ›;

// Contacts
// Obligée de rajouter le guillemet sinon les données ne sont pas lues
contacts = ‹ " › + record.CONTACTS_Prenom_Nom;
tabcontacts = contacts.split(« , »);
fonctions = record.CONTACTS_Fonction;
nb_contacts = tabcontacts.length;
// Obligée de retirer le guillemet ajouté plus haut
document.getElementById(‹ nom_contact0 ›).textContent = tabcontacts[0].substring(1);
document.getElementById(‹ fonction0 ›).textContent = ‹  ›;
// Pour rafraichir les champs quand il y a moins de contacts dans certains établissements
for (let j = 1; j < 8; j++) {
document.getElementById(‹ nom_contact › + j).textContent = ‹  ›;
//document.getElementById(‹ fonction › + j).textContent = ‹  ›;
}
for (let i = 1; i < nb_contacts; i++) {
contact = tabcontacts[i];
//fonction = tabfonctions[i];
document.getElementById(‹ nom_contact › + i).textContent = contact;
//document.getElementById(‹ fonction › + i).textContent = fonction;
}
document.getElementById(‹ nb_contact ›).textContent = nb_contacts;

});

Merci pour votre éclairage.

Bonjour @Vlacoume , non je crois qu’il n’est pas possible d’appeler plusieurs tables, MAIS tu peux afficher les datas des autres tables dans la table qui te sert pour ton widget, avec des « Champs rapportés » ou lookups, ainsi tu peux appeller ces champs lookups dans ton widget.

Bonjour

Merci pour la réponse, c’est ce que je pensais mais visiblement ça coince, j’ai torturé mes formules javascript et lookupRecords dans tous les sens, j’arrive à récupéré une valeur d’une colonne lookupRecords mais pas deux. Un bug ?

Pour le moment j’ai contourné le problème en concaténant mais 2 lookupRecords avec la formule suivante :
contacts = CONTACTS.lookupRecords(UAI=CONTAINS($id))
[contact.Prenom_Nom + " - " + contact.Fonction.Fonction for contact in contacts]

oui je pense que le widget bug, j’ai eu d’autres retours qui le confirment