@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.
@@ -1,136 +0,0 @@
1
- function addNewStepClickEvent() {
2
- createAndSelectEmptyCrop();
3
- loadSelectedStepToEditor(selectedStep);
4
- displayCropDetailView();
5
- refreshAllTables();
6
- }
7
-
8
- function createAndSelectEmptyCrop() {
9
- let crop = new StepModel({
10
- startDate: getRotationEndDate(),
11
- });
12
-
13
- crop.setDurationInMonths(2)
14
- crops.steps.push(crop.getStep());
15
-
16
- //select last created crop to be editable
17
- selectedStep = crop;
18
- }
19
-
20
- function loadSelectedStepToEditor(aStep) {
21
- setInputValue("cropName", aStep.getStep().name);
22
- setInputValue("cropColor", aStep.getStep().color);
23
- setInputValue("cropStartDate", aStep.getStep().startDate.toISOString().split('T')[0]);
24
- setInputValue("cropEndDate", aStep.getStep().endDate.toISOString().split('T')[0]);
25
- setInputValue("cropDescription", aStep.getStep().description);
26
- document.getElementById("cropSecondary").checked = aStep.getStep().secondary_crop || false;
27
- }
28
-
29
- function refreshStepsButtonList() {
30
- let cropsContainer = $("#cropsContainer");
31
- cropsContainer.html('');
32
-
33
- crops.steps.forEach((crop) => {
34
- const rowDiv = createCropRow(crop);
35
- cropsContainer.append(rowDiv);
36
- });
37
-
38
- cropsContainer.sortable("refresh");
39
- }
40
-
41
- function createCropRow(crop) {
42
- let step = new StepModel(crop); // in case crop is a plain object, convert to Crop instance
43
-
44
- let rowDiv = $('<div class="row mb-2 step-row editable-row position-relative" data-id="'+step.getStep().id +'"></div>');
45
-
46
- rowDiv.append($('<div class="col"></div>')
47
- .append($('<i class="fa fa-bars drag-handle" aria-hidden="true"></i>'))
48
- .append($('<strong>' + step.getStep().name + '</strong>')));
49
-
50
- addEditAndRemoveButtons(rowDiv,
51
- step.getStep().id,
52
- function () {
53
- console.log("Selected step:", step.getStep().name);
54
- SelectStep(step);
55
- },
56
- function(id) {
57
- crops.steps = crops.steps.filter(function (crop) { return crop.id != id })
58
-
59
- refreshAllTables();
60
- displayCropListView();
61
- },
62
- function(id) {
63
- duplicateStep(id);
64
- });
65
-
66
- rowDiv.click();
67
-
68
- return rowDiv;
69
- }
70
-
71
- function duplicateStep(stepId) {
72
- // Find the step to duplicate
73
- let originalStep = crops.steps.find(crop => crop.id === stepId);
74
- if (!originalStep) return;
75
-
76
- // Get the latest end date in the rotation
77
- let latestEndDate = getRotationEndDate();
78
-
79
- // Calculate the duration of the original step
80
- let originalStart = new Date(originalStep.startDate);
81
- let originalEnd = new Date(originalStep.endDate);
82
-
83
- // Calculate how many years to add to position after the latest step
84
- let yearsToAdd = 0;
85
- let newStartDate = new Date(originalStart);
86
- let newEndDate = new Date(originalEnd);
87
-
88
- // Keep adding years until the new start date is after the latest end date
89
- while (newStartDate < latestEndDate) {
90
- yearsToAdd++;
91
- newStartDate = new Date(originalStart);
92
- newStartDate.setFullYear(originalStart.getFullYear() + yearsToAdd);
93
- newEndDate.setFullYear(originalEnd.getFullYear() + yearsToAdd);
94
- }
95
-
96
- // Create the new step with all properties cloned
97
- let newStep = {
98
- name: originalStep.name,
99
- color: originalStep.color,
100
- startDate: newStartDate,
101
- endDate: newEndDate,
102
- description: originalStep.description,
103
- secondary_crop: originalStep.secondary_crop || false,
104
- useDefaultColor: originalStep.useDefaultColor,
105
- useDefaultStartDate: originalStep.useDefaultStartDate,
106
- useDefaultEndDate: originalStep.useDefaultEndDate,
107
- interventions: originalStep.interventions ? originalStep.interventions.map(i => ({
108
- day: i.day,
109
- name: i.name,
110
- type: i.type,
111
- description: i.description
112
- })) : [],
113
- attributes: originalStep.attributes ? originalStep.attributes.map(a => ({
114
- name: a.name,
115
- value: a.value
116
- })) : []
117
- };
118
-
119
- // Create a StepModel instance to ensure proper initialization
120
- let stepModel = new StepModel(newStep);
121
-
122
- // Add the duplicated step to the rotation
123
- crops.steps.push(stepModel.getStep());
124
-
125
- // Refresh the UI
126
- refreshAllTables();
127
- }
128
-
129
- function SelectStep(crop) {
130
- selectedStep = crop;
131
- loadSelectedStepToEditor(selectedStep);
132
- displayCropDetailView();
133
-
134
- refreshAttributesTable();
135
- refreshInterventionsTable();
136
- }
@@ -1,118 +0,0 @@
1
- function exportToJsonFile(data, fileName = 'export-itk.json') {
2
- const jsonData = JSON.stringify(data, null, 2);
3
- const blob = new Blob([jsonData], { type: 'application/json' });
4
- const url = URL.createObjectURL(blob);
5
-
6
- const a = document.createElement('a');
7
- a.href = url;
8
- a.download = fileName;
9
- a.click();
10
-
11
- URL.revokeObjectURL(url);
12
- }
13
-
14
- function importFromJsonFile() {
15
- showConfirmationModal(() => {
16
- openFileInput();
17
- });
18
- }
19
-
20
- function importFromTestJson() {
21
- showConfirmationModal(() => {
22
- importTestJSON();
23
- });
24
- }
25
-
26
- function importTestJSON() {
27
- fetch('test/test.json')
28
- .then(response => {
29
- if (!response.ok) {
30
- throw new Error("Erreur HTTP " + response.status);
31
- }
32
- return response.json();
33
- })
34
- .then(data => {
35
- console.log("Données JSON :", data);
36
- data.steps.forEach(step => {
37
- let sm = new StepModel(step)
38
- sm.setAsEdited();
39
- });
40
- reloadCropsFromJson(data);
41
- })
42
- .catch(error => {
43
- console.error("Impossible de charger le JSON :", error);
44
- });
45
- }
46
-
47
- function wipe() {
48
- showConfirmationModal(() => {
49
- let crops = {
50
- "title": DEFAULT_TITLE,
51
- "options": {
52
- "view": "horizontal",
53
- "show_transcript": true,
54
- "title_top_interventions": "Contrôle adventices",
55
- "title_bottom_interventions": "Autres interventions",
56
- "title_steps": "Étapes de la rotation dans la parcelle",
57
- },
58
- "steps": []
59
- };
60
-
61
- reloadCropsFromJson(crops);
62
- });
63
- }
64
-
65
- function openFileInput() {
66
- const input = document.createElement('input');
67
- input.type = 'file';
68
- input.accept = '.json';
69
-
70
- input.addEventListener('change', (event) => {
71
- const file = event.target.files[0];
72
- if (file) {
73
- const reader = new FileReader();
74
- reader.onload = () => {
75
- try {
76
- const jsonData = JSON.parse(reader.result);
77
- jsonData.steps.forEach(step => {
78
- let sm = new StepModel(step)
79
- sm.setAsEdited();
80
- });
81
- reloadCropsFromJson(jsonData);
82
- } catch (error) {
83
- console.error("Error parsing JSON file:", error);
84
- showJsonErrorModal(error.message);
85
- }
86
- };
87
- reader.readAsText(file);
88
- }
89
- });
90
-
91
- input.click();
92
- }
93
-
94
- function showJsonErrorModal(errorMessage) {
95
- const errorModal = new bootstrap.Modal(document.getElementById('jsonErrorModal'));
96
- document.getElementById('jsonErrorMessage').textContent = errorMessage;
97
- errorModal.show();
98
- }
99
-
100
- function showConfirmationModal(onConfirm) {
101
-
102
- if (crops?.steps.length === 0)
103
- return onConfirm();
104
-
105
- const confirmationModal = new bootstrap.Modal(document.getElementById('confirmationModal'));
106
- const confirmButton = document.getElementById('confirmImport');
107
-
108
- // Avoid multiple event listeners
109
- const newConfirmButton = confirmButton.cloneNode(true);
110
- confirmButton.replaceWith(newConfirmButton);
111
-
112
- newConfirmButton.addEventListener('click', () => {
113
- confirmationModal.hide();
114
- onConfirm();
115
- });
116
-
117
- confirmationModal.show();
118
- }
@@ -1,172 +0,0 @@
1
- # Quick Start - React/Next.js
2
-
3
- Guide rapide pour intégrer `@osfarm/itineraire-technique` dans votre projet React ou Next.js.
4
-
5
- ## Installation (5 minutes)
6
-
7
- ### Étape 1 : Installer le package
8
-
9
- ```bash
10
- npm install @osfarm/itineraire-technique
11
- # ou
12
- yarn add @osfarm/itineraire-technique
13
- # ou
14
- pnpm add @osfarm/itineraire-technique
15
- ```
16
-
17
- ### Étape 2 : Copier les fichiers statiques
18
-
19
- ```bash
20
- npm run setup:react
21
- ```
22
-
23
- Ce script copie automatiquement les fichiers JS, CSS et de test dans votre dossier `public/`.
24
-
25
- ### Étape 3 : Ajouter les dépendances CDN
26
-
27
- **Pour Next.js App Router** : Créez ou modifiez `app/layout.tsx` :
28
-
29
- ```tsx
30
- export default function RootLayout({ children }: { children: React.ReactNode }) {
31
- return (
32
- <html lang="fr">
33
- <head>
34
- <script src="https://cdn.jsdelivr.net/npm/echarts@6.0.0/dist/echarts.js"></script>
35
- <script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
36
- <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.7/underscore-umd-min.js"></script>
37
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" />
38
- <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet" />
39
-
40
- <script src="/js/chart-render.js"></script>
41
- <link href="/css/styles-rendering.css" rel="stylesheet" />
42
- </head>
43
- <body>{children}</body>
44
- </html>
45
- );
46
- }
47
- ```
48
-
49
- **Pour Next.js Pages Router** : Créez ou modifiez `pages/_document.tsx` (voir [exemple complet](../examples/nextjs-_document.tsx))
50
-
51
- ## Utilisation - Visualiseur Simple
52
-
53
- Créez un composant pour afficher un itinéraire :
54
-
55
- ```tsx
56
- 'use client'; // Pour Next.js App Router uniquement
57
-
58
- import { TikaRenderer } from '@osfarm/itineraire-technique/react';
59
- import { useEffect, useState } from 'react';
60
-
61
- export default function MyItineraire() {
62
- const [data, setData] = useState(null);
63
-
64
- useEffect(() => {
65
- // Charger les données de test
66
- fetch('/test/test.json')
67
- .then(res => res.json())
68
- .then(setData);
69
- }, []);
70
-
71
- if (!data) return <div>Chargement...</div>;
72
-
73
- return (
74
- <div className="container">
75
- <h1>{data.title}</h1>
76
- <TikaRenderer data={data} />
77
- </div>
78
- );
79
- }
80
- ```
81
-
82
- ## Utilisation - Éditeur Simple
83
-
84
- ```tsx
85
- 'use client';
86
-
87
- import { useItineraire } from '@osfarm/itineraire-technique/react';
88
- import { TikaRenderer } from '@osfarm/itineraire-technique/react';
89
-
90
- export default function MyEditor() {
91
- const { data, addStep, exportToJson } = useItineraire({
92
- title: "Ma rotation",
93
- options: { view: "horizontal", show_transcript: true },
94
- steps: []
95
- });
96
-
97
- const handleAddCrop = () => {
98
- addStep({
99
- id: crypto.randomUUID(),
100
- name: 'Nouvelle culture',
101
- startDate: new Date().toISOString(),
102
- endDate: new Date(Date.now() + 90*24*60*60*1000).toISOString(),
103
- color: '#4CAF50',
104
- interventions: []
105
- });
106
- };
107
-
108
- return (
109
- <div className="container">
110
- <button className="btn btn-primary" onClick={handleAddCrop}>
111
- Ajouter une culture
112
- </button>
113
- <button className="btn btn-secondary" onClick={() => console.log(exportToJson())}>
114
- Exporter JSON
115
- </button>
116
-
117
- {data && <TikaRenderer data={data} />}
118
- </div>
119
- );
120
- }
121
- ```
122
-
123
- ## TypeScript Support
124
-
125
- Types inclus ! Importez-les directement :
126
-
127
- ```tsx
128
- import type { ItineraireData, Step, Intervention } from '@osfarm/itineraire-technique/react';
129
-
130
- const myRotation: ItineraireData = {
131
- title: "Ma rotation bio",
132
- options: {
133
- view: "horizontal",
134
- show_transcript: true
135
- },
136
- steps: []
137
- };
138
- ```
139
-
140
- ## Exemples Complets
141
-
142
- Consultez le dossier [`examples/`](../examples/) pour :
143
- - ✅ Visualiseur Next.js App Router
144
- - ✅ Éditeur Next.js App Router
145
- - ✅ Configuration `_document.tsx`
146
- - ✅ API Routes pour sauvegarder/charger des données
147
-
148
- ## Troubleshooting
149
-
150
- ### "RotationRenderer is not defined"
151
-
152
- ➡️ Vérifiez que `/js/chart-render.js` est bien chargé dans votre HTML/layout
153
- ➡️ Utilisez le hook `useItineraireDependencies` pour vérifier le chargement
154
-
155
- ### Les styles ne s'appliquent pas
156
-
157
- ➡️ Assurez-vous d'avoir chargé `/css/styles-rendering.css`
158
- ➡️ Vérifiez que Bootstrap CSS est aussi chargé
159
-
160
- ### Erreur "use client" manquant
161
-
162
- ➡️ Ajoutez `'use client';` en haut de vos composants qui utilisent des hooks React
163
-
164
- ## Documentation Complète
165
-
166
- 📚 [Documentation React complète](README.md)
167
-
168
- ## Besoin d'aide ?
169
-
170
- - 🐛 [Signaler un bug](https://github.com/osfarm/itineraire-technique/issues)
171
- - 💬 [Questions et discussions](https://github.com/osfarm/itineraire-technique/discussions)
172
- - 📖 [Documentation Triple Performance](https://wiki.tripleperformance.fr/)