@benjosivo/table-query 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +169 -41
  2. package/package.json +4 -4
package/README.md CHANGED
@@ -1,14 +1,11 @@
1
1
  # @benjosivo/table-query
2
2
 
3
- Table triable/filtrable/paginée : hook + composants React d'un côté (`/react`), logique
4
- SQL de pagination/tri/filtres côté serveur de l'autre (`/server`). Un projet peut
5
- n'utiliser qu'un des deux côtés.
3
+ Tableau de données triable, filtrable et paginé, en deux parties indépendantes :
6
4
 
7
- ```
8
- src/
9
- react/ → DataTable, Table, Pagination, useDataTable, FilterModal, FilterPanel, Cell, utils, types
10
- server/ → createTableQueryModule (router Express + reqTableQuery)
11
- ```
5
+ - **`@benjosivo/table-query/server`** — logique de requêtage côté serveur (Express + MySQL) : pagination, tri, filtres, calcul des valeurs disponibles pour chaque colonne, mise en cache optionnelle.
6
+ - **`@benjosivo/table-query/react`** — composants et hook React pour afficher ce type de données : tableau, pagination, filtre rapide par colonne, panneau de filtres avancés (multiselect / plage numérique / plage de dates).
7
+
8
+ Les deux parties fonctionnent ensemble mais peuvent aussi être utilisées séparément (par exemple le composant React seul, avec ton propre backend).
12
9
 
13
10
  ## Installation
14
11
 
@@ -16,72 +13,203 @@ src/
16
13
  npm install @benjosivo/table-query
17
14
  ```
18
15
 
19
- Peer dependencies : `react` (si tu utilises `/react`), `express` et `@benjosivo/mysql`
20
- (si tu utilises `/server`) — ce sont des `peerDependencies` optionnelles, donc pas
21
- besoin des trois si tu n'utilises qu'un des deux côtés.
16
+ Selon la partie utilisée, il faut aussi avoir installé dans le projet :
17
+
18
+ | Partie utilisée | Dépendances nécessaires |
19
+ |---|---|
20
+ | `/server` | `express`, `@benjosivo/mysql` |
21
+ | `/react` | `react` (v18+) |
22
+
23
+ Ce sont des `peerDependencies` : le package ne les installe pas lui-même, il utilise celles déjà présentes dans ton projet.
24
+
25
+ ---
22
26
 
23
27
  ## Côté serveur
24
28
 
29
+ ### Créer le module
30
+
25
31
  ```ts
26
32
  import { createTableQueryModule } from '@benjosivo/table-query/server';
27
- import { convertToMySQLDateTime, wrapRouteHandler } from './functions.js';
28
- import { getSQLCache, setSQLCache, deleteSQLCache } from './redis.js';
29
33
 
30
34
  const { router, reqTableQuery } = createTableQueryModule({
31
- convertToMySQLDateTime,
32
- wrapRouteHandler,
33
- // Optionnel : à fournir seulement si tu veux pouvoir utiliser le cache quelque part.
34
- cache: { getSQLCache, setSQLCache, deleteSQLCache },
35
+ // Formate une date/heure au format attendu par MySQL
36
+ convertToMySQLDateTime: (date) => /* ta fonction */,
37
+
38
+ // Enveloppe un handler Express (gestion d'erreurs, etc.)
39
+ wrapRouteHandler: (fn) => /* ta fonction */,
40
+
41
+ // Optionnel : uniquement nécessaire si tu veux pouvoir utiliser le cache (voir plus bas)
42
+ cache: {
43
+ getSQLCache: (key) => /* lit une entrée de cache */,
44
+ setSQLCache: (key, value) => /* écrit une entrée de cache */,
45
+ deleteSQLCache: (key) => /* supprime une entrée de cache */,
46
+ },
35
47
  });
48
+ ```
49
+
50
+ `router` expose la route `GET /getFiltres`, utilisée en interne pour récupérer les filtres calculés en tâche de fond. Monte-le sur le chemin de ton choix :
36
51
 
52
+ ```ts
37
53
  app.use('/tableCreation/api', router);
54
+ ```
55
+
56
+ ### Répondre à une requête de table
38
57
 
58
+ ```ts
39
59
  app.post('/api/commandes', wrapRouteHandler(async (req, res) => {
40
60
  const result = await reqTableQuery({
41
61
  query: `SELECT c.*, COUNT(*) OVER() AS TotalCount FROM commandes c`,
42
62
  req,
63
+ // Un type de filtre par colonne renvoyée par la requête (sauf la 1ère, l'identifiant)
43
64
  paramFilter: ['MULTISELECT', 'HIDE', 'DATE', 'MULTISELECT'],
44
- useCache: true, // voir plus bas
45
65
  });
66
+
46
67
  if (result.error) return res.status(result.status ?? 500).send(result.error);
47
68
  res.json(result.data);
48
69
  }));
49
70
  ```
50
71
 
51
- ### Le paramètre `useCache`
72
+ `reqTableQuery` lit `limit`, `offset`, `sorting`, `filtre` et `setFilter` dans `req.body` (ou `req.query`) — c'est exactement ce que le composant React `DataTable`/`useDataTable` envoie, donc les deux côtés s'emboîtent directement.
52
73
 
53
- `reqTableQuery({ ..., useCache })` :
74
+ ### Types de filtre disponibles (`paramFilter`)
54
75
 
55
- - **`useCache: true`** (ou omis, si `cache` a été fourni à `createTableQueryModule`) —
56
- comportement d'origine : résultats de requête et filtres mis en cache Redis, filtres
57
- calculés en tâche de fond et récupérés via `/getFiltres` (polling).
58
- - **`useCache: false`** aucune lecture/écriture Redis, requête toujours fraîche, filtres
59
- calculés et renvoyés directement dans la même réponse (pas de round-trip `/getFiltres`).
60
- Utile pour un écran qui doit toujours montrer les données à l'instant T, ou pour un
61
- projet qui n'a pas (encore) de Redis configuré.
62
- - Si tu passes `useCache: true` sans avoir fourni `cache` à `createTableQueryModule`,
63
- une erreur explicite est levée au lieu d'échouer silencieusement.
76
+ | Type | Effet |
77
+ |---|---|
78
+ | `'MULTISELECT'` | Liste de valeurs distinctes de la colonne, sélection multiple |
79
+ | `'UNGROUP_MULTISELECT'` | Comme `MULTISELECT`, mais éclate les valeurs séparées par `, ` dans une même cellule |
80
+ | `'SLIDER'` | Plage numérique (min/max) |
81
+ | `'DATE'` / `'DATETIME'` | Plage de dates |
82
+ | `'JSON'` / `'FILE'` | Pas de filtre, juste un rendu spécifique côté React |
83
+ | `'HIDE'` | Colonne sans filtre |
84
+ | `null` | Colonne sans filtre particulier |
64
85
 
65
- Le cache est donc décidé **par appel** (`reqTableQuery`), pas globalement pour tout le
66
- module — tu peux avoir certains endpoints en cache et d'autres non avec le même module.
86
+ ### Le cache (`useCache`)
87
+
88
+ Le cache est activé **par appel**, pas globalement :
89
+
90
+ ```ts
91
+ reqTableQuery({ query, req, paramFilter, useCache: true }); // utilise le cache
92
+ reqTableQuery({ query, req, paramFilter, useCache: false }); // toujours une donnée fraîche
93
+ ```
94
+
95
+ - Si `cache` a été fourni à `createTableQueryModule`, `useCache` vaut `true` par défaut.
96
+ - Si `cache` n'a pas été fourni, `useCache` vaut `false` par défaut (aucun Redis requis).
97
+ - Demander `useCache: true` sans avoir fourni `cache` lève une erreur explicite.
98
+ - Avec `useCache: false`, les valeurs de filtre disponibles sont calculées et renvoyées directement dans la réponse. Avec `useCache: true`, elles sont calculées en tâche de fond et le composant React va les chercher via `/getFiltres` (polling automatique, rien à faire côté appelant).
99
+
100
+ ---
67
101
 
68
102
  ## Côté React
69
103
 
104
+ ### Utilisation simple (tout inclus)
105
+
70
106
  ```tsx
71
107
  import { DataTable } from '@benjosivo/table-query/react';
72
108
 
73
- <DataTable
74
- fetchData={(params) => fetch('/api/commandes', { method: 'POST', body: JSON.stringify(params) }).then((r) => r.ok ? r.json() : null)}
75
- advancedFilters
76
- />
109
+ function CommandesTable() {
110
+ return (
111
+ <DataTable
112
+ fetchData={(params) =>
113
+ fetch('/api/commandes', {
114
+ method: 'POST',
115
+ headers: { 'Content-Type': 'application/json' },
116
+ body: JSON.stringify(params),
117
+ }).then((res) => (res.ok ? res.json() : null))
118
+ }
119
+ onRowClick={(id, row) => console.log('ligne cliquée', id, row)}
120
+ advancedFilters
121
+ />
122
+ );
123
+ }
77
124
  ```
78
125
 
79
- Ou en composant seulement `useDataTable` + `Table` + `Pagination` avec ta propre UI de
80
- filtre (voir la conversation précédente pour l'exemple complet).
126
+ #### Props de `<DataTable />`
81
127
 
82
- ## Build & publish
128
+ | Prop | Type | Description |
129
+ |---|---|---|
130
+ | `fetchData` | `(params) => Promise<FetchResult \| null>` | **Obligatoire.** Appelée à chaque changement de page/tri/filtre. |
131
+ | `onRowClick` | `(id, row) => void` | Appelée au clic sur une ligne |
132
+ | `filterEnabled` | `boolean` (défaut `true`) | Affiche le bouton de filtre rapide par colonne |
133
+ | `sortingEnabled` | `boolean` (défaut `true`) | Active le tri au clic sur l'en-tête |
134
+ | `advancedFilters` | `boolean` (défaut `false`) | Affiche le panneau de filtres avancés (nécessite `setFilter`/`paramFilter` côté serveur) |
135
+ | `height` | `string` (défaut `'76vh'`) | Hauteur max de la zone de défilement |
136
+ | `rowsPerPageOptions` | `number[]` | Choix disponibles pour le nombre de lignes par page |
137
+ | `defaultRowsPerPage` | `number` | Valeur par défaut |
138
+ | `onImagePreview` | `(src) => void` | Appelée au clic sur une image (sinon ouverture dans un nouvel onglet) |
83
139
 
84
- ```bash
85
- npm run build # tsc → dist/react + dist/server
86
- npm publish # même config GitHub Packages que @benjosivo/mysql
140
+ ### Utilisation avec ta propre UI de filtre
141
+
142
+ Si le système de filtre par défaut ne convient pas, utilise le hook et les composants séparément :
143
+
144
+ ```tsx
145
+ import { useDataTable, Table, Pagination } from '@benjosivo/table-query/react';
146
+
147
+ function CommandesTable() {
148
+ const table = useDataTable({ fetchData: fetchCommandes });
149
+ const [recherche, setRecherche] = useState('');
150
+
151
+ const rechercher = () => {
152
+ table.replaceFilters({ reference: [`/*/${recherche}/*/`] });
153
+ // ou, filtre par filtre : table.setColumnFilter('statut', ['en_cours']);
154
+ };
155
+
156
+ return (
157
+ <>
158
+ <input value={recherche} onChange={(e) => setRecherche(e.target.value)} />
159
+ <button onClick={rechercher}>Rechercher</button>
160
+
161
+ <Table
162
+ items={table.items}
163
+ fieldsType={table.fieldsType}
164
+ sortColumn={table.sortColumn}
165
+ sortDirection={table.sortDirection}
166
+ onSort={table.toggleSort}
167
+ />
168
+
169
+ <Pagination
170
+ page={table.page}
171
+ totalPages={table.totalPages}
172
+ perPage={table.perPage}
173
+ count={table.count}
174
+ onChangePerPage={table.changePerPage}
175
+ onFirst={table.firstPage}
176
+ onPrevious={table.previousPage}
177
+ onNext={table.nextPage}
178
+ onLast={table.lastPage}
179
+ />
180
+ </>
181
+ );
182
+ }
87
183
  ```
184
+
185
+ `useDataTable` gère la pagination, le tri et l'appel réseau ; il n'impose aucune UI de filtre — `table.setColumnFilter(colonne, valeur)` et `table.replaceFilters(objet)` permettent de piloter le filtrage depuis n'importe quelle interface.
186
+
187
+ ### Format attendu par `fetchData`
188
+
189
+ ```ts
190
+ interface FetchParams {
191
+ limit: number;
192
+ offset: number;
193
+ sorting?: string;
194
+ filtre?: Record<string, unknown>;
195
+ setFilter?: boolean;
196
+ }
197
+
198
+ interface FetchResult<T> {
199
+ items: T[];
200
+ count: number;
201
+ fieldsType?: { fieldType: string }[]; // 'DATE' | 'DATETIME' | 'JSON' | 'FILE' | 'BLOB' | ...
202
+ filtre?: FilterConfig; // renvoyé par le serveur si des filtres avancés sont demandés
203
+ cleRecupFiltre?: string; // utilisé en interne pour le polling du cache
204
+ }
205
+ ```
206
+
207
+ C'est exactement la forme renvoyée par `reqTableQuery` côté serveur.
208
+
209
+ ---
210
+
211
+ ## Build
212
+
213
+ ```bash
214
+ npm run build # compile src/react et src/server dans dist/
215
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benjosivo/table-query",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Table triable/filtrable/paginée : hook + composants React d'un côté (`/react`), logique SQL de pagination/tri/filtres côté serveur de l'autre (`/server`). Un projet peut n'utiliser qu'un des deux côtés.",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/benjosivo/table-query#readme",
@@ -33,15 +33,15 @@
33
33
  "prepublishOnly": "npm run build"
34
34
  },
35
35
  "dependencies": {
36
- "@benjosivo/mysql": "^1.3.2"
36
+ "@benjosivo/mysql": "^1.3.2",
37
+ "express": "^5.2.1"
37
38
  },
38
39
  "devDependencies": {
39
- "@types/express": "^4.17.0",
40
+ "@types/express": "^5.0.6",
40
41
  "@types/react": "^18.0.0",
41
42
  "typescript": "^5.4.0"
42
43
  },
43
44
  "peerDependencies": {
44
- "express": "^4.0.0",
45
45
  "react": "^18.0.0"
46
46
  },
47
47
  "peerDependenciesMeta": {