@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.
- package/README.md +169 -41
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
# @benjosivo/table-query
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
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
|
-
|
|
74
|
+
### Types de filtre disponibles (`paramFilter`)
|
|
54
75
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
|
66
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
80
|
-
filtre (voir la conversation précédente pour l'exemple complet).
|
|
126
|
+
#### Props de `<DataTable />`
|
|
81
127
|
|
|
82
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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.
|
|
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": "^
|
|
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": {
|