Trackily Docs

Categories

TL;DR : un produit a un champ category (texte libre). La page /catalog agrège la liste distincte des catégories et en fait des filter pills. Pas de table dédiée — choix volontaire pour rester léger sur les petits catalogues.

Page catalog publique

Concept

Trackily n'a pas de table categories séparée. À la place, native_products.category est un simple TEXT ajouté en v64 du schéma. La liste distincte des catégories est calculée à la demande, et les filter pills de /catalog sont construites à partir de cette liste.

Pourquoi pas de table relationnelle ? Parce que la majorité des opérateurs de commerce native font tourner des petits catalogues (5 à 30 produits). Maintenir une table de catégories + une table de jointure pour 12 produits, c'est du sur-engineering. Taper le nom de la catégorie dans le champ texte du produit est plus rapide, plus visible, et tu peux toujours migrer vers un schéma relationnel le jour où ton catalogue passe à 500 produits (la valeur étant déjà normalisée par LOWER() dans l'index, la migration est triviale).

Implémentation

ALTER TABLE native_products
  ADD COLUMN IF NOT EXISTS category TEXT DEFAULT '';

CREATE INDEX idx_native_products_category
  ON native_products(LOWER(category))
  WHERE category <> '';

L'index partiel sur LOWER(category) rend les requêtes "filter par catégorie" instantanées même sur des catalogues plus gros, en case-insensitive.

Compatibilité avec les collections Shopify

L'écosystème Shopify dispose en plus des add_product_to_collection / create_collection / remove_product_from_collection MCP qui ciblent les vraies collections Shopify. Pour le natif, ces outils ne sont pas applicables — utilise le champ category à la place. Si tu fais tourner les deux mondes en parallèle, la doctrine est :

Native Shopify
Regroupement native_products.category (TEXT) Collections Shopify (table dédiée)
Filter front /catalog?category=... Page collection Shopify
MCP update_product patch category add_product_to_collection, remove_product_from_collection
Multi-catégorie ? Non (1 produit = 1 catégorie) Oui (N-N via collections)

Comment faire (UI + MCP)

Via l'admin UI

  1. Fiche produit → champ Category.
  2. Tape le nom (par exemple "Suppléments cognitifs"). Auto-complete propose les catégories déjà utilisées sur d'autres produits (tu peux donc rester cohérent sans contrainte stricte).
  3. Save.
  4. Refresh /catalog — la pill apparaît si au moins un produit status='active' est dans cette catégorie.

Via MCP

{
  "name": "update_product",
  "arguments": {
    "platform": "native",
    "product_id": 12,
    "patch": { "category": "Suppléments cognitifs" }
  }
}

Pour Shopify, le pattern collections :

{
  "name": "create_collection",
  "arguments": {
    "store_id": 3,
    "title": "Best sellers Été 2026",
    "body_html": "<p>Nos produits les plus vendus en saison estivale.</p>",
    "sort_order": "best-selling"
  }
}
{
  "name": "add_product_to_collection",
  "arguments": {
    "store_id": 3,
    "product_id": 8721234567,
    "collection_id": 41212345678
  }
}
{
  "name": "remove_product_from_collection",
  "arguments": {
    "store_id": 3,
    "product_id": 8721234567,
    "collection_id": 41212345678
  }
}

Ces 3 outils sont Tier-2 (preview + confirm_token). Les collections Shopify sont visibles côté admin du store Shopify, pas côté admin Trackily.

La page /catalog en détail

/catalog est la vitrine publique de tes produits natifs. Trois utilisations principales :

  1. Vendre directement sans landing dédiée. Tu pousses ton trafic vers /catalog, le visiteur browse, clique sur une carte, atterrit sur /p/<slug>, achète.
  2. Servir de fallback sur un domaine sans root_behavior spécifique configuré. Les domaines avec root_behavior='catalog' rendent ce template sur /.
  3. Mini-store par catégorie : /catalog?category=Boissons filtre la grille en client-side ET côté serveur (le ?category= est utilisé pour le rendu initial SSR).

Structure du rendu

+----------------------------------------+
|   Brand label    |   Shop all    |    |   ← Header (depuis settings ou domain branding)
+----------------------------------------+
|                                        |
|   [Toutes 12]  [Boissons 4]            |   ← Filter pills (catégories distinctes)
|   [Snacks 3]   [Compléments 5]         |
|                                        |
+----------------------------------------+
|  ┌───────┐  ┌───────┐  ┌───────┐       |
|  │ image │  │ image │  │ image │       |   ← Grille produits (cards)
|  │ Nom   │  │ Nom   │  │ Nom   │       |
|  │ 29 €  │  │ 49 €  │  │ 19 €  │       |
|  └───────┘  └───────┘  └───────┘       |
+----------------------------------------+

Chaque pill affiche le count entre parenthèses (produits actifs de cette catégorie). La pill "All" est par défaut active. Le click change l'URL en ?category=Boissons et re-render la grille.

Stratégie de naming

Quelques recommandations pratiques :

  • Garde les noms courts — les pills ont un max-width, un nom trop long passe en ellipsis (frustrant).
  • Pluriel ou singulier — choisis et reste cohérent. "Suppléments" partout, pas "Supplément" pour 1 et "Suppléments" pour les autres (Trackily est case-insensitive mais pas pluriel-insensitive).
  • Une seule langue par catalog. Si tu vends en multi-langue, fais un produit par langue avec une catégorie par langue. Pas de routing automatique sur la pill.
  • Évite les emojis dans la catégorie — ça mange de la place, ça casse l'URL ?category=. Garde-les pour le nom du produit si tu veux du visuel.

Exemples concrets

1. Mini-store de 12 produits, 3 catégories

Boissons (4 produits)
  - Smoothie Énergie Matin
  - Smoothie Détox Soir
  - Boost Café Cordyceps
  - Tisane Sommeil Profond

Snacks (3 produits)
  - Barres Protéines Cacao
  - Barres Protéines Beurre de Cacahuète
  - Crackers Graines Bio

Compléments (5 produits)
  - Memo Mind (3 variantes)
  - Sleep Mind (2 variantes)
  - Joint Care Pro
  - Vitamine D3 Lipo
  - Omega-3 Cure

Côté MCP, tu peux scripter l'assignation :

{
  "name": "update_product",
  "arguments": { "platform": "native", "product_id": 12, "patch": { "category": "Compléments" } }
}

…sur les 12 produits en boucle (ton agent MCP peut chaîner les 12 appels Tier-2 successifs).

2. Re-catégoriser à la volée

Tu décides que "Suppléments cognitifs" devient "Brain boosters" pour la version anglaise du site. Plus simple : tu mets à jour les 5 produits via 5 patches MCP, refresh /catalog, la pill change. Aucune migration SQL.

3. Catégorie vide masquée

Si tu marques tous les produits de la catégorie "Boissons" en status='draft', la pill "Boissons" disparaît automatiquement de /catalog. La requête qui calcule les catégories ne ramène que celles ayant au moins un produit actif :

SELECT category, COUNT(*) AS n
  FROM native_products
 WHERE deleted_at IS NULL
   AND status = 'active'
   AND category <> ''
 GROUP BY LOWER(category), category
 ORDER BY n DESC;

Pas de pill fantôme.

Erreurs courantes

  • "Ma pill n'apparaît pas" — le produit est draft, ou deleted_at est setté, ou category est vide string. La pill apparaît uniquement quand un produit actif y est rangé.
  • "Deux pills pour la même catégorie" — un produit a "Boissons", un autre "boissons". L'index est case-insensitive mais le GROUP BY garde la casse d'origine du premier produit. Édite-en un pour aligner la casse (un seul autocomplete dans l'UI propose la version existante — tu n'as pas dû passer par l'auto-complete).
  • "Le filter ?category=X ne marche pas" — l'URL est encodée. "Suppléments cognitifs" devient ?category=Supplements%20cognitifs. C'est fait automatiquement par l'UI. Si tu testes à la main, n'oublie pas l'encodage.
  • "Je veux multi-catégoriser" — pas possible nativement. Trois options : (a) duppliquer le produit (déconseillé — duplique les stats), (b) basculer sur un product Shopify et utiliser les collections, (c) hacker via un champ metadata.categories (JSONB) mais l'UI catalog ne le lira pas.
  • "L'auto-complete ne propose rien" — l'auto-complete est alimenté par le distinct des catégories existantes. Sur un catalogue vide, tu pars de zéro.

Voir aussi

  • products.md — la fiche produit où vit le champ category.
  • Landings → AI builder — la catégorie alimente le contexte AI quand tu génères une landing.
  • Admin UI → Domainsroot_behavior='catalog' pour servir /catalog à la racine d'un domaine.
  • MCP list_collections, get_collection_detail — pour la partie Shopify.