@osfarm/itineraire-technique 1.2.0 → 1.2.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/react/README.md DELETED
@@ -1,305 +0,0 @@
1
- # Utilisation avec React et Next.js
2
-
3
- Ce package peut être utilisé dans des applications React et Next.js. Voici comment l'intégrer.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install @osfarm/itineraire-technique
9
- ```
10
-
11
- ## Dépendances à inclure
12
-
13
- Les composants React dépendent de plusieurs bibliothèques externes qui doivent être chargées. Vous avez deux options :
14
-
15
- ### Option 1 : Via CDN (recommandé pour Next.js)
16
-
17
- Dans votre `_document.tsx` ou `_app.tsx` (Next.js) ou dans votre `index.html` (React) :
18
-
19
- ```tsx
20
- // pages/_document.tsx (Next.js)
21
- import { Html, Head, Main, NextScript } from 'next/document'
22
-
23
- export default function Document() {
24
- return (
25
- <Html>
26
- <Head>
27
- {/* ECharts */}
28
- <script src="https://cdn.jsdelivr.net/npm/echarts@6.0.0/dist/echarts.js"></script>
29
-
30
- {/* jQuery */}
31
- <script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
32
- <script src="https://cdn.jsdelivr.net/npm/jquery-ui@1.14.1/dist/jquery-ui.min.js"></script>
33
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-ui@1.14.1/themes/base/jquery-ui.css" />
34
-
35
- {/* Underscore */}
36
- <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.7/underscore-umd-min.js"></script>
37
-
38
- {/* Bootstrap (pour l'éditeur) */}
39
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
40
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" />
41
-
42
- {/* Font Awesome */}
43
- <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet" />
44
-
45
- {/* Scripts TIKA */}
46
- <script src="/chart-render.js"></script>
47
- <script src="/editor-attributes.js"></script>
48
- <script src="/editor-interventions.js"></script>
49
- <script src="/editor-crops.js"></script>
50
- <script src="/editor-export.js"></script>
51
- <script src="/editor-wiki-editor.js"></script>
52
-
53
- {/* Styles TIKA */}
54
- <link href="/styles-rendering.css" rel="stylesheet" />
55
- <link href="/styles-editor.css" rel="stylesheet" />
56
- </Head>
57
- <body>
58
- <Main />
59
- <NextScript />
60
- </body>
61
- </Html>
62
- )
63
- }
64
- ```
65
-
66
- ### Option 2 : Via NPM
67
-
68
- ```bash
69
- npm install echarts jquery jquery-ui underscore bootstrap
70
- ```
71
-
72
- Puis importez-les dans votre application.
73
-
74
- ## Copier les fichiers statiques
75
-
76
- Copiez les fichiers JS et CSS depuis `node_modules/@osfarm/itineraire-technique/` vers votre dossier `public/` :
77
-
78
- ```bash
79
- # Créer les dossiers
80
- mkdir -p public/js public/css
81
-
82
- # Copier les fichiers JS
83
- cp node_modules/@osfarm/itineraire-technique/js/*.js public/
84
-
85
- # Copier les fichiers CSS
86
- cp node_modules/@osfarm/itineraire-technique/css/*.css public/
87
- ```
88
-
89
- ## Utilisation du composant de visualisation
90
-
91
- ```tsx
92
- 'use client'; // Pour Next.js App Router
93
-
94
- import { TikaRenderer } from '@osfarm/itineraire-technique/react';
95
- import { useEffect, useState } from 'react';
96
-
97
- export default function ItinerairePage() {
98
- const [data, setData] = useState(null);
99
- const [loading, setLoading] = useState(true);
100
-
101
- useEffect(() => {
102
- // Charger vos données depuis une API ou un fichier
103
- fetch('/api/itineraire')
104
- .then(res => res.json())
105
- .then(data => {
106
- setData(data);
107
- setLoading(false);
108
- });
109
- }, []);
110
-
111
- if (loading) return <div>Chargement...</div>;
112
- if (!data) return <div>Aucune donnée</div>;
113
-
114
- return (
115
- <div className="container">
116
- <h1>Itinéraire Technique</h1>
117
- <TikaRenderer
118
- data={data}
119
- width="100%"
120
- height="600px"
121
- onItemClick={(itemId, event) => {
122
- console.log('Item clicked:', itemId);
123
- }}
124
- />
125
- </div>
126
- );
127
- }
128
- ```
129
-
130
- ## Utilisation du composant éditeur
131
-
132
- ```tsx
133
- 'use client';
134
-
135
- import { TikaEditor } from '@osfarm/itineraire-technique/react';
136
- import { useState } from 'react';
137
-
138
- export default function EditorPage() {
139
- const [data, setData] = useState({
140
- title: "Ma rotation",
141
- options: {
142
- view: "horizontal",
143
- show_transcript: true,
144
- title_top_interventions: "Cultures principales",
145
- title_bottom_interventions: "Couverts",
146
- title_steps: "Rotation",
147
- region: "France"
148
- },
149
- steps: []
150
- });
151
-
152
- const handleSave = (newData) => {
153
- console.log('Données sauvegardées:', newData);
154
- setData(newData);
155
-
156
- // Envoyer à votre API
157
- fetch('/api/itineraire', {
158
- method: 'POST',
159
- headers: { 'Content-Type': 'application/json' },
160
- body: JSON.stringify(newData)
161
- });
162
- };
163
-
164
- const handleExport = (data) => {
165
- // Télécharger le JSON
166
- const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
167
- const url = URL.createObjectURL(blob);
168
- const a = document.createElement('a');
169
- a.href = url;
170
- a.download = 'itineraire.json';
171
- a.click();
172
- };
173
-
174
- return (
175
- <div className="container">
176
- <h1>Éditeur d'Itinéraire Technique</h1>
177
- <TikaEditor
178
- initialData={data}
179
- onSave={handleSave}
180
- onExport={handleExport}
181
- enableAutoSave={true}
182
- autoSaveInterval={10000}
183
- />
184
- </div>
185
- );
186
- }
187
- ```
188
-
189
- ## Utilisation avec le hook personnalisé
190
-
191
- ```tsx
192
- 'use client';
193
-
194
- import { useItineraire } from '@osfarm/itineraire-technique/react';
195
- import { TikaRenderer } from '@osfarm/itineraire-technique/react';
196
-
197
- export default function MyComponent() {
198
- const {
199
- data,
200
- loading,
201
- error,
202
- addStep,
203
- updateStep,
204
- deleteStep,
205
- exportToJson
206
- } = useItineraire(initialData);
207
-
208
- const handleAddStep = () => {
209
- const newStep = {
210
- id: crypto.randomUUID(),
211
- name: 'Nouvelle culture',
212
- startDate: new Date().toISOString(),
213
- endDate: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString(),
214
- color: '#4CAF50',
215
- interventions: []
216
- };
217
- addStep(newStep);
218
- };
219
-
220
- if (loading) return <div>Chargement...</div>;
221
- if (error) return <div>Erreur: {error.message}</div>;
222
- if (!data) return <div>Aucune donnée</div>;
223
-
224
- return (
225
- <div>
226
- <button onClick={handleAddStep}>Ajouter une étape</button>
227
- <button onClick={() => console.log(exportToJson())}>Exporter JSON</button>
228
- <TikaRenderer data={data} />
229
- </div>
230
- );
231
- }
232
- ```
233
-
234
- ## TypeScript
235
-
236
- Les types TypeScript sont inclus. Importez-les depuis le package :
237
-
238
- ```tsx
239
- import type {
240
- ItineraireData,
241
- Step,
242
- Intervention
243
- } from '@osfarm/itineraire-technique/react';
244
-
245
- const myData: ItineraireData = {
246
- title: "Ma rotation",
247
- options: {
248
- view: "horizontal",
249
- show_transcript: true
250
- },
251
- steps: []
252
- };
253
- ```
254
-
255
- ## Configuration Next.js
256
-
257
- Si vous utilisez Next.js 13+ avec App Router, assurez-vous que vos composants sont marqués avec `'use client'` car ils utilisent des hooks React et accèdent au DOM.
258
-
259
- ### next.config.js
260
-
261
- ```javascript
262
- /** @type {import('next').NextConfig} */
263
- const nextConfig = {
264
- // Permettre les scripts externes
265
- experimental: {
266
- externalDir: true
267
- }
268
- }
269
-
270
- module.exports = nextConfig
271
- ```
272
-
273
- ## Exemples complets
274
-
275
- Consultez le dossier `examples/` pour des exemples complets d'intégration avec :
276
- - Next.js App Router
277
- - Next.js Pages Router
278
- - Create React App
279
- - Vite + React
280
-
281
- ## Troubleshooting
282
-
283
- ### "RotationRenderer is not defined"
284
-
285
- Assurez-vous que `chart-render.js` est chargé avant le rendu de votre composant. Utilisez `useItineraireDependencies` pour vérifier :
286
-
287
- ```tsx
288
- import { useItineraireDependencies } from '@osfarm/itineraire-technique/react';
289
-
290
- function MyComponent() {
291
- const { loaded, error } = useItineraireDependencies();
292
-
293
- if (!loaded) return <div>Chargement des dépendances...</div>;
294
- if (error) return <div>Erreur: {error.message}</div>;
295
-
296
- return <TikaRenderer data={data} />;
297
- }
298
- ```
299
-
300
- ### Problèmes de styles
301
-
302
- Assurez-vous que les fichiers CSS sont bien importés :
303
- - `styles-rendering.css` pour le visualiseur
304
- - `styles-editor.css` pour l'éditeur
305
- - Bootstrap pour l'interface de l'éditeur
@@ -1,212 +0,0 @@
1
- /**
2
- * TikaEditor - Composant React pour éditer un itinéraire technique
3
- *
4
- * @example
5
- * import TikaEditor from '@osfarm/itineraire-technique/react/TikaEditor';
6
- *
7
- * function MyComponent() {
8
- * const [data, setData] = useState(initialData);
9
- *
10
- * const handleSave = (newData) => {
11
- * console.log('Data saved:', newData);
12
- * setData(newData);
13
- * };
14
- *
15
- * return <TikaEditor initialData={data} onSave={handleSave} />;
16
- * }
17
- */
18
-
19
- import React, { useEffect, useRef, useState } from 'react';
20
-
21
- const TikaEditor = ({
22
- initialData = null,
23
- onSave = null,
24
- onExport = null,
25
- className = '',
26
- showWikiButtons = false,
27
- enableAutoSave = false,
28
- autoSaveInterval = 5000
29
- }) => {
30
- const containerRef = useRef(null);
31
- const editorInitialized = useRef(false);
32
- const autoSaveTimer = useRef(null);
33
- const [isLoading, setIsLoading] = useState(true);
34
- const [currentData, setCurrentData] = useState(initialData);
35
-
36
- // Fonction pour récupérer les données actuelles de l'éditeur
37
- const getCurrentData = () => {
38
- if (typeof window === 'undefined' || !window.rotation_data) return null;
39
- return JSON.parse(JSON.stringify(window.rotation_data));
40
- };
41
-
42
- // Fonction pour sauvegarder
43
- const handleSave = () => {
44
- const data = getCurrentData();
45
- if (data && onSave) {
46
- onSave(data);
47
- setCurrentData(data);
48
- }
49
- };
50
-
51
- // Fonction pour exporter
52
- const handleExport = () => {
53
- const data = getCurrentData();
54
- if (data && onExport) {
55
- onExport(data);
56
- } else if (data && window.exportToJsonFile) {
57
- // Utiliser la fonction d'export native si pas de callback personnalisé
58
- window.exportToJsonFile(data);
59
- }
60
- };
61
-
62
- // Auto-save
63
- useEffect(() => {
64
- if (enableAutoSave && onSave) {
65
- autoSaveTimer.current = setInterval(() => {
66
- handleSave();
67
- }, autoSaveInterval);
68
-
69
- return () => {
70
- if (autoSaveTimer.current) {
71
- clearInterval(autoSaveTimer.current);
72
- }
73
- };
74
- }
75
- }, [enableAutoSave, autoSaveInterval, onSave]);
76
-
77
- useEffect(() => {
78
- if (typeof window === 'undefined') return;
79
-
80
- // Vérifier les dépendances
81
- const checkDependencies = () => {
82
- const missing = [];
83
- if (!window.$) missing.push('jQuery');
84
- if (!window.echarts) missing.push('ECharts');
85
- if (!window._) missing.push('Underscore');
86
- if (!window.RotationRenderer) missing.push('chart-render.js');
87
-
88
- // Vérifier les fonctions de l'éditeur
89
- if (!window.refreshAttributesTable) missing.push('editor-attributes.js');
90
- if (!window.refreshStepsButtonList) missing.push('editor-crops.js');
91
- if (!window.exportToJsonFile) missing.push('editor-export.js');
92
- if (!window.refreshInterventionsList) missing.push('editor-interventions.js');
93
-
94
- return missing;
95
- };
96
-
97
- const missingDeps = checkDependencies();
98
- if (missingDeps.length > 0) {
99
- console.error('Missing dependencies for TikaEditor:', missingDeps.join(', '));
100
- setIsLoading(false);
101
- return;
102
- }
103
-
104
- // Initialiser l'éditeur seulement une fois
105
- if (!editorInitialized.current && containerRef.current) {
106
- editorInitialized.current = true;
107
-
108
- // Initialiser la structure globale de données si nécessaire
109
- if (!window.rotation_data) {
110
- window.rotation_data = initialData || {
111
- title: "Nouvel itinéraire",
112
- options: {
113
- view: "horizontal",
114
- show_transcript: true,
115
- title_top_interventions: "Cultures principales",
116
- title_bottom_interventions: "Couverts et CIVE",
117
- title_steps: "Rotation",
118
- region: "France",
119
- show_climate_diagram: false
120
- },
121
- steps: []
122
- };
123
- } else if (initialData) {
124
- window.rotation_data = initialData;
125
- }
126
-
127
- // Appeler les fonctions d'initialisation de l'éditeur
128
- try {
129
- if (window.refreshAttributesTable) window.refreshAttributesTable();
130
- if (window.refreshStepsButtonList) window.refreshStepsButtonList();
131
- if (window.refreshInterventionsList) window.refreshInterventionsList();
132
-
133
- // Rendre le graphique initial
134
- if (window.renderChart) {
135
- window.renderChart();
136
- }
137
-
138
- setIsLoading(false);
139
- } catch (error) {
140
- console.error('Error initializing editor:', error);
141
- setIsLoading(false);
142
- }
143
- }
144
-
145
- // Exposer les fonctions de save/export via window pour que le HTML interne puisse les appeler
146
- window.reactEditorSave = handleSave;
147
- window.reactEditorExport = handleExport;
148
-
149
- }, [initialData]);
150
-
151
- // Mettre à jour les données si initialData change
152
- useEffect(() => {
153
- if (initialData && window.rotation_data) {
154
- window.rotation_data = initialData;
155
- if (window.refreshAttributesTable) window.refreshAttributesTable();
156
- if (window.refreshStepsButtonList) window.refreshStepsButtonList();
157
- if (window.refreshInterventionsList) window.refreshInterventionsList();
158
- if (window.renderChart) window.renderChart();
159
- }
160
- }, [initialData]);
161
-
162
- if (isLoading) {
163
- return (
164
- <div className={`itineraire-editor-loading ${className}`}>
165
- <div className="spinner-border" role="status">
166
- <span className="visually-hidden">Chargement...</span>
167
- </div>
168
- </div>
169
- );
170
- }
171
-
172
- return (
173
- <div ref={containerRef} className={`itineraire-editor ${className}`}>
174
- {/* Boutons de contrôle React */}
175
- <div className="react-editor-controls mb-3">
176
- <div className="btn-group" role="group">
177
- {onSave && (
178
- <button
179
- type="button"
180
- className="btn btn-primary"
181
- onClick={handleSave}
182
- >
183
- <i className="fa fa-save"></i> Enregistrer
184
- </button>
185
- )}
186
- {onExport && (
187
- <button
188
- type="button"
189
- className="btn btn-outline-primary"
190
- onClick={handleExport}
191
- >
192
- <i className="fa fa-download"></i> Exporter JSON
193
- </button>
194
- )}
195
- </div>
196
- </div>
197
-
198
- {/* Le contenu HTML de l'éditeur sera injecté ici via un portail ou iframe */}
199
- {/* Pour l'instant, on délègue au HTML existant */}
200
- <div id="editor-content-container">
201
- {/* L'éditeur HTML existant sera chargé ici */}
202
- <iframe
203
- src="/editor.html"
204
- style={{ width: '100%', height: '800px', border: 'none' }}
205
- title="Éditeur d'itinéraire technique"
206
- />
207
- </div>
208
- </div>
209
- );
210
- };
211
-
212
- export default TikaEditor;
@@ -1,116 +0,0 @@
1
- /**
2
- * TikaRenderer - Composant React pour visualiser un itinéraire technique
3
- *
4
- * @example
5
- * import TikaRenderer from '@osfarm/itineraire-technique/react/TikaRenderer';
6
- *
7
- * function MyComponent() {
8
- * const data = { ... }; // Données JSON de l'itinéraire
9
- * return <TikaRenderer data={data} />;
10
- * }
11
- */
12
-
13
- import React, { useEffect, useRef } from 'react';
14
-
15
- const TikaRenderer = ({
16
- data,
17
- width = '100%',
18
- height = 'auto',
19
- className = '',
20
- onItemClick = null,
21
- onItemHover = null
22
- }) => {
23
- const containerRef = useRef(null);
24
- const rendererRef = useRef(null);
25
- const divId = useRef(`itk-${Math.random().toString(36).substr(2, 9)}`);
26
-
27
- useEffect(() => {
28
- // Vérifier que les dépendances sont chargées
29
- if (typeof window === 'undefined') return;
30
-
31
- if (!window.RotationRenderer) {
32
- console.error('RotationRenderer not loaded. Make sure chart-render.js is included.');
33
- return;
34
- }
35
-
36
- if (!window.echarts) {
37
- console.error('ECharts not loaded. Make sure ECharts is included.');
38
- return;
39
- }
40
-
41
- if (!window.$) {
42
- console.error('jQuery not loaded. Make sure jQuery is included.');
43
- return;
44
- }
45
-
46
- // Créer le conteneur si nécessaire
47
- if (containerRef.current && !containerRef.current.querySelector('.mainITKContainer')) {
48
- // Initialiser le renderer
49
- try {
50
- rendererRef.current = new window.RotationRenderer(divId.current, data);
51
- rendererRef.current.render();
52
-
53
- // Attacher les événements personnalisés si fournis
54
- if (onItemClick) {
55
- containerRef.current.addEventListener('click', (e) => {
56
- const item = e.target.closest('.rotation_item');
57
- if (item) {
58
- const itemId = item.dataset.id || item.id;
59
- onItemClick(itemId, e);
60
- }
61
- });
62
- }
63
-
64
- if (onItemHover) {
65
- containerRef.current.addEventListener('mouseover', (e) => {
66
- const item = e.target.closest('.rotation_item');
67
- if (item) {
68
- const itemId = item.dataset.id || item.id;
69
- onItemHover(itemId, e);
70
- }
71
- });
72
- }
73
- } catch (error) {
74
- console.error('Error initializing RotationRenderer:', error);
75
- }
76
- }
77
-
78
- // Cleanup
79
- return () => {
80
- if (rendererRef.current && rendererRef.current.chart) {
81
- try {
82
- rendererRef.current.chart.dispose();
83
- } catch (error) {
84
- console.error('Error disposing chart:', error);
85
- }
86
- }
87
- };
88
- }, [data, onItemClick, onItemHover]);
89
-
90
- // Réagir aux changements de données
91
- useEffect(() => {
92
- if (rendererRef.current && data) {
93
- try {
94
- // Recréer le renderer avec les nouvelles données
95
- if (rendererRef.current.chart) {
96
- rendererRef.current.chart.dispose();
97
- }
98
- rendererRef.current = new window.RotationRenderer(divId.current, data);
99
- rendererRef.current.render();
100
- } catch (error) {
101
- console.error('Error updating chart data:', error);
102
- }
103
- }
104
- }, [data]);
105
-
106
- return (
107
- <div
108
- ref={containerRef}
109
- id={divId.current}
110
- className={`itineraire-renderer ${className}`}
111
- style={{ width, height }}
112
- />
113
- );
114
- };
115
-
116
- export default TikaRenderer;