> ## Documentation Index
> Fetch the complete documentation index at: https://docs-kit.lucdidion.lu/llms.txt
> Use this file to discover all available pages before exploring further.

# Feature Tools - Discovery & Comparaison

> Catalogue de 35+ outils numériques pour associations

## Vue d'ensemble

La feature **Tools** est le cœur de Kit'Asso : un catalogue d'outils numériques adaptés aux besoins des associations.

**Statistiques :**

* 35+ outils référencés
* 4 tiers de pricing (Gratuit, Freemium, Payant, Entreprise)
* Système de catégorisation par domaine
* Comparaison côte à côte (max 2 outils)
* Favoris avec localStorage

***

## Architecture technique

### Composants principaux

```
src/features/tools/
├── modals/
│   └── ToolModal/
│       ├── index.tsx           # Container principal
│       ├── ToolForm.tsx        # Formulaire d'édition
│       ├── ToolPreview.tsx     # Aperçu détaillé
│       ├── types.ts            # Types du modal
│       └── useToolModal.ts     # Logique métier
│
src/components/sections/
├── VirtualizedToolsGrid.tsx    # Grille virtualisée (react-window)
└── ToolCard.tsx                # Carte outil individuelle
```

### API Layer (GraphQL)

Toutes les opérations passent par `apiCall()` / `apiCallVoid()` + `nhost.graphql.request()` :

```typescript theme={null}
// src/api/tools.ts
export const toolsApi = {
  list: () => Promise<EnhancedTool[]>,
  getById: (id: string) => Promise<EnhancedTool>,
  create: (data: ToolInsert) => Promise<Tool>,
  update: (id: string, data: ToolUpdate) => Promise<Tool>,
  delete: (id: string) => Promise<void>,
  uploadLogo: (file: File) => Promise<string>
};
```

**Query GraphQL type :**

```graphql theme={null}
query GetTools {
  tools(order_by: { name: asc }) {
    id name description pricing_tier
    category_id logo_url website_url
    category { name }
    tool_features {
      filter { id value filter_type }
    }
  }
}
```

***

## Fonctionnalités détaillées

### 1. Recherche avancée

**Implémentation :** `src/hooks/useToolFilters.ts`

```typescript theme={null}
const {
  filteredTools,
  searchTerm,
  setSearchTerm,
  selectedPricing,
  setSelectedPricing,
  selectedCategory,
  setSelectedCategory
} = useToolFilters(tools);
```

**Caractéristiques :**

* Debounce de 300ms pour la recherche texte
* Filtrage multi-critères (pricing + catégorie)
* Combinaison ET des filtres
* Performance optimisée avec useMemo

***

### 2. Grille virtualisée

**Composant :** `VirtualizedToolsGrid.tsx`

Utilise `react-window` pour le rendu performant. Seuls les éléments visibles dans le viewport sont rendus.

**Configuration :**

```typescript theme={null}
<FixedSizeGrid
  columnCount={getColumnCount()} // 1-4 selon viewport
  rowCount={Math.ceil(tools.length / columnCount)}
  columnWidth={320}
  rowHeight={380}
  height={800}
  width={containerWidth}
>
  {({ columnIndex, rowIndex, style }) => (
    <ToolCard tool={tools[index]} style={style} />
  )}
</FixedSizeGrid>
```

***

### 3. Mode comparaison

**Route :** `/compare`

**Logique :** `src/hooks/useToolComparison.ts`

* Sélection max : 2 outils
* Affichage côte à côte des caractéristiques (pricing, catégorie, features, description, liens)

***

### 4. Système de favoris

**Hook :** `src/hooks/useFavorites.ts`

**Stockage :** LocalStorage (clé : `kitasso_favorites`)

```typescript theme={null}
const {
  favorites,      // Set<string> des IDs
  addFavorite,    // (id: string) => void
  removeFavorite, // (id: string) => void
  toggleFavorite, // (id: string) => void
  isFavorite,     // (id: string) => boolean
  count           // number
} = useFavorites();
```

***

### 5. Upload de logos

**Backend :** Nhost Storage

```typescript theme={null}
// Upload vers Nhost Storage
const { error, fileMetadata } = await nhost.storage.upload({
  file,
  bucketId: 'default',
});

// URL publique
const publicUrl = `https://${nhostSubdomain}.storage.${nhostRegion}.nhost.run/v1/files/${fileMetadata.id}`;
```

***

## Base de données

### Table `tools`

```sql theme={null}
CREATE TABLE tools (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT UNIQUE NOT NULL,
  description TEXT NOT NULL,
  pricing_tier TEXT CHECK (pricing_tier IN (
    'Gratuit', 'Freemium', 'Payant', 'Entreprise'
  )),
  category_id UUID REFERENCES categories(id) ON DELETE SET NULL,
  logo_url TEXT,
  website_url TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
```

### Relations via GraphQL

**Catégorie (Many-to-One) :**

```graphql theme={null}
query { tools { id name category { name } } }
```

**Features (Many-to-Many via `tool_features`) :**

```graphql theme={null}
query { tools { id name tool_features { filter { id value } } } }
```

### Permissions Hasura

* **public** : SELECT autorisé sur toutes les lignes
* **admin** : SELECT, INSERT, UPDATE, DELETE autorisés

***

## Types TypeScript

```typescript theme={null}
// src/api/types.ts
export interface Tool {
  id: string;
  name: string;
  description: string;
  pricing_tier: 'Gratuit' | 'Freemium' | 'Payant' | 'Entreprise';
  category_id: string;
  logo_url: string | null;
  website_url: string | null;
  created_at: string;
}

export interface EnhancedTool extends Tool {
  category_name: string;
  features: string[];
}

export interface ToolInsert {
  name: string;
  description: string;
  pricing_tier: Tool['pricing_tier'];
  category_id: string;
  logo_url?: string;
  website_url?: string;
}

export type ToolUpdate = Partial<ToolInsert>;
```

***

## Bonnes pratiques

### ✅ À faire

```typescript theme={null}
// Toujours passer par l'API layer
import { toolsApi } from '@/api';
const tools = await toolsApi.list();

// Gérer les erreurs
try {
  await toolsApi.create(data);
} catch (error) {
  if (error instanceof ApiError) {
    // Erreur typée avec message
  }
}
```

### ❌ À éviter

```typescript theme={null}
// NE PAS appeler nhost.graphql directement dans les composants
const result = await nhost.graphql.request({ query: '...' });

// NE PAS ignorer les erreurs
await toolsApi.create(data); // Sans try/catch
```

***

## Performance

**Métriques cibles :**

* Temps de chargement initial : \< 2s
* Temps de recherche : \< 100ms
* FPS scroll : 60
* Mémoire utilisée : \< 50MB

**Optimisations :**

* Virtualisation avec react-window
* Debounce sur recherche (300ms)
* Memoization avec React.memo
* Lazy loading des images
* Code splitting du modal admin

***

## Ressources

<CardGroup cols={2}>
  <Card title="Workflows" icon="route" href="/features/workflows">
    Parcours guidés étape par étape
  </Card>

  <Card title="Tool Packs" icon="box" href="/features/tool-packs">
    Collections curées d'outils
  </Card>

  <Card title="API Layer" icon="plug" href="/development/api-layer">
    Architecture API GraphQL
  </Card>

  <Card title="Testing" icon="flask" href="/development/testing">
    Écrire des tests pour vos features
  </Card>
</CardGroup>
