@osfarm/itineraire-technique 1.1.4 → 1.1.6

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.
@@ -35,14 +35,14 @@ class RotationRenderer {
35
35
  <div class="col-auto left-transcript"><div class="transcript"></div></div>
36
36
  <div class="col col-auto chart-div"><div class="charts"></div></div>
37
37
  <div class="col col-12 bottom-transcript"><div class="transcript"></div></div>
38
- </div>`);
38
+ </div>`);
39
39
  }
40
40
  } else {
41
41
  if (this.itk_container.find('.mainITKContainer').length == 0) {
42
42
  this.itk_container.append(`<div class="row mainITKContainer">
43
43
  <div class="col col-12 chart-div"><div class="charts"></div></div>
44
- </div>`);
45
- }
44
+ </div>`);
45
+ }
46
46
  }
47
47
  }
48
48
 
@@ -278,6 +278,8 @@ class RotationRenderer {
278
278
  let self = this;
279
279
  let data = [];
280
280
 
281
+ let bHasSecondaryCrops = steps.some(item => item.secondary_crop);
282
+
281
283
  steps.forEach((item, index) => {
282
284
  if (item.name == Number(item.name))
283
285
  item.name = "Etape " + item.name; // Force the item name to be a string
@@ -295,7 +297,9 @@ class RotationRenderer {
295
297
  item.startDate.valueOf(), // Date de début
296
298
  item.endDate.valueOf(), // Date de fin
297
299
  item.name, // Nom
298
- 'rotation_item' // Type
300
+ 'rotation_item', // Type
301
+ item.secondary_crop ? true : false,
302
+ bHasSecondaryCrops ? true : false
299
303
  ],
300
304
  itemStyle: {
301
305
  color: item.color
@@ -316,7 +320,7 @@ class RotationRenderer {
316
320
  intervention.type == 'intervention_top' ? 'intervention_top' : 'intervention_bottom' // Type
317
321
  ],
318
322
  divId: 'Intervention_' + index + '_' + interventionIndex,
319
- interventionDate: new Date(item.startDate.valueOf()),
323
+ interventionDate: new Date(item.startDate.valueOf() + intervention.day * 86400000),
320
324
  interventionDays: intervention.day,
321
325
  itemStyle: {
322
326
  color: item.color
@@ -327,6 +331,55 @@ class RotationRenderer {
327
331
  }
328
332
  });
329
333
 
334
+ function wrapText(echarts, text, maxWidth, maxHeight) {
335
+ const words = text.split(' ');
336
+ let line = '';
337
+ let lines = [];
338
+
339
+ for (let word of words) {
340
+ const testLine = line + word + ' ';
341
+ let testWidth = echarts.format.getTextRect(testLine).width;
342
+ if (testWidth > maxWidth && line !== '') {
343
+ lines.push(line);
344
+ line = word + ' ';
345
+ } else {
346
+ line = testLine;
347
+ }
348
+
349
+ // Avoid words that are too long
350
+ testWidth = echarts.format.getTextRect(line).width;
351
+ while (testWidth > maxWidth) {
352
+ line = line.slice(0, -1);
353
+ testWidth = echarts.format.getTextRect(line).width;
354
+ }
355
+ }
356
+
357
+ if (line.length > 0) {
358
+ lines.push(line);
359
+ }
360
+
361
+ let wrapped = '';
362
+ let wrappedText = false;
363
+ lines.forEach(l => {
364
+ if (wrappedText)
365
+ return;
366
+
367
+ let test = wrapped + l;
368
+ if (echarts.format.getTextRect(test).height > maxHeight)
369
+ {
370
+ wrappedText = true;
371
+ return;
372
+ }
373
+
374
+ wrapped += l + "\n";
375
+ });
376
+
377
+ if (wrappedText) {
378
+ return wrapped.trim() + '...';
379
+ }
380
+
381
+ return lines.join("\n");
382
+ }
330
383
 
331
384
  let maxXPositions = new Map();
332
385
 
@@ -337,6 +390,8 @@ class RotationRenderer {
337
390
  var end = api.coord([api.value(2), categoryIndex]);
338
391
  var name = api.value(3);
339
392
  var type = api.value(4);
393
+ let secondary_crop = api.value(5);
394
+ let bHasSecondaryCrops = api.value(6);
340
395
 
341
396
  const x = start[0];
342
397
  let y = start[1];
@@ -373,25 +428,45 @@ class RotationRenderer {
373
428
  // params.coordSys.width, // largeur du canva
374
429
  // params.coordSys.height // hauteur du canva
375
430
 
376
- const height = self.barHeight - 40; // 20 px margin top and bottom
431
+ let height = self.barHeight - 20; // 20 px margin top and bottom
432
+ let top = y - height / 2;
433
+ let textXMargin = 2;
434
+ let textYMargin = 10;
435
+
436
+ if (bHasSecondaryCrops) {
437
+ height = self.barHeight - 40; // 20 px margin top and bottom
438
+ top = y - height / 2 - 15;
439
+ textXMargin = 2;
440
+ textYMargin = 10;
441
+ }
442
+
443
+ if (secondary_crop) {
444
+ // Move secondary crops a bit down and reduce their size
445
+ top = top + height + 5;
446
+ height = height / 3;
447
+ textXMargin = 5;
448
+ textYMargin = 5;
449
+ }
450
+
377
451
  const arrowWidth = height / 3;
378
452
  const border = 3;
379
- const textMargin = 10;
380
453
 
381
454
  var points = [
382
- [x, y - height / 2],
383
- [end[0] - border, y - height / 2],
384
- [end[0] + arrowWidth - border, y],
385
- [end[0] - border, y + height / 2],
386
- [x, y + height / 2],
387
- [x + arrowWidth, y],
455
+ [x, top],
456
+ [end[0] - border, top],
457
+ [end[0] + arrowWidth - border, top + height / 2],
458
+ [end[0] - border, top + height],
459
+ [x, top + height],
460
+ [x + arrowWidth, top + height / 2],
388
461
  ];
389
462
 
390
- const itemLabelWidth = echarts.format.getTextRect(name).width + textMargin * 2;
463
+ //const itemLabelWidth = echarts.format.getTextRect(name).width + textMargin * 2;
391
464
  const itemWidth = end[0] - x;
392
465
 
393
- if (itemLabelWidth > itemWidth)
394
- name = ''; // Hide the label as we won't have the room to show it
466
+ // if (itemLabelWidth > itemWidth)
467
+ // name = ''; // Hide the label as we won't have the room to show it
468
+
469
+ name = wrapText(echarts, name, itemWidth - arrowWidth, height);
395
470
 
396
471
  // See this for clip regions : https://stackoverflow.com/questions/71735038/setting-border-and-label-in-custom-apache-echarts
397
472
  // https://stackoverflow.com/questions/73653691/how-to-draw-a-custom-triangle-in-renderitem-in-apache-echarts
@@ -413,7 +488,7 @@ class RotationRenderer {
413
488
  },
414
489
  },
415
490
  textConfig: {
416
- position: [arrowWidth + textMargin, height / 2 - 5]
491
+ position: [arrowWidth + textXMargin, textYMargin]
417
492
  },
418
493
  textContent: {
419
494
  style: {
@@ -430,7 +505,6 @@ class RotationRenderer {
430
505
  if (type == 'intervention_bottom' || type == 'intervention_top') {
431
506
 
432
507
  const height = 20;
433
- const border = 3;
434
508
  const margin = 10;
435
509
  const textMargin = 5;
436
510
 
@@ -478,8 +552,8 @@ class RotationRenderer {
478
552
 
479
553
  const arrowWidth = 3;
480
554
 
481
- let arrowTop = y + 60;
482
- let arrowBottom = y - 60;
555
+ let arrowTop = y + 55;
556
+ let arrowBottom = y - 55;
483
557
 
484
558
  y = margin + y + trackToUse * (height + margin) - (self.barHeight / 2);
485
559
 
@@ -542,28 +616,28 @@ class RotationRenderer {
542
616
  }
543
617
 
544
618
  return [
545
- {
546
- type: 'custom',
547
- renderItem: renderItem,
548
- clip: true,
549
- itemStyle: {
550
- opacity: 0.8
551
- },
552
- encode: {
553
- x: [1, 2],
554
- y: 0
555
- },
556
- data: data
557
- }
558
- ];
619
+ {
620
+ type: 'custom',
621
+ renderItem: renderItem,
622
+ clip: true,
623
+ itemStyle: {
624
+ opacity: 0.8
625
+ },
626
+ encode: {
627
+ x: [1, 2],
628
+ y: 0
629
+ },
630
+ data: data
631
+ }
632
+ ];
559
633
  }
560
634
 
561
635
  getCategoriesLabels() {
562
636
  let self = this;
563
637
 
564
638
  let categories = [self.chartOptions.title_bottom_interventions ?? '',
565
- self.chartOptions.title_steps ?? '',
566
- self.chartOptions.title_top_interventions ?? ''];
639
+ self.chartOptions.title_steps ?? '',
640
+ self.chartOptions.title_top_interventions ?? ''];
567
641
 
568
642
  // simulate some wrapping of the category labels
569
643
  categories = categories.map((item) => {
@@ -584,7 +658,7 @@ class RotationRenderer {
584
658
  if (item.interventions?.length > 0 || item.attributes?.length > 0)
585
659
  visibility = "visible";
586
660
 
587
- let collapseButton = '<div class="collapse-button '+visibility+ '"><i class="fa fa-chevron-down" aria-hidden="true"></i></div>';
661
+ let collapseButton = '<div class="collapse-button ' + visibility + '"><i class="fa fa-chevron-down" aria-hidden="true"></i></div>';
588
662
 
589
663
  let start = item.startDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: '2-digit' });
590
664
  let end = item.endDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: '2-digit' });
@@ -595,7 +669,7 @@ class RotationRenderer {
595
669
  + collapseButton
596
670
  + '<div class="step_dates">' + dates + '</div>'
597
671
  + '<h4 class="">' + item.name + '<i class="fa fa-pencil step-edit" aria-hidden="true"></i></h4>'
598
- + '</div><p class="step_description clearfix">' + (item.description ?? '') + '</p>'
672
+ + '</div><p class="step_description clearfix">' + self.getHTMLFormatedDescription(item.description) + '</p>'
599
673
  + '<div class="details">'
600
674
  + (item.attributes?.length > 0 ? item.attributes.map((attribute) => { return '<p><dt>' + attribute.name + '</dt><dd>' + attribute.value + '</dd></p>' }).join('') : '');
601
675
 
@@ -605,9 +679,9 @@ class RotationRenderer {
605
679
  let intDate = new Date(item.startDate.valueOf() + intervention.day * 86400000).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: 'numeric' });
606
680
  let days = intervention.day;
607
681
  if (days >= 0)
608
- intDate += ' (J+' + days + ')';
682
+ intDate += ' (J+' + (days == 0 ? '0' : days) + ')';
609
683
  else
610
- intDate += ' (J' + days + ')';
684
+ intDate += ' (J-' + days + ')';
611
685
 
612
686
  let title = intervention.name;
613
687
 
@@ -615,8 +689,8 @@ class RotationRenderer {
615
689
  title = title + '⚠️';
616
690
 
617
691
  html += '<div class="Intervention_' + index + '_' + interventionIndex + ' intervention"><span class="intervention_title">' + title + '</span>'
618
- + '<span class="intervention_date badge rounded-pill">' + intDate + '</span>'
619
- + '<div class="intervention_description">' + intervention.description + '</div></div>';
692
+ + '<span class="intervention_date badge rounded-pill">' + intDate + '</span>'
693
+ + '<div class="intervention_description">' + intervention.description + '</div></div>';
620
694
  });
621
695
  }
622
696
 
@@ -665,16 +739,16 @@ class RotationRenderer {
665
739
  },
666
740
  data: []
667
741
  };
668
-
742
+
669
743
  let lastDayOfPreviousStep = null;
670
744
 
671
745
  steps.forEach((item, index) => {
672
-
746
+
673
747
  if (lastDayOfPreviousStep) {
674
748
  // If there's a gap between the end of the previous step and the start of the new step, we add an dummy pie item:
675
749
  let days = Math.round((item.startDate - lastDayOfPreviousStep) / (1000 * 60 * 60 * 24));
676
750
  if (days > 0) {
677
-
751
+
678
752
  let pieItem = {
679
753
  'name': '',
680
754
  'value': days,
@@ -684,18 +758,18 @@ class RotationRenderer {
684
758
  'endDate': new Date(item.startDate.valueOf()), // Date de fin
685
759
  'duration': Math.round(days / 30),
686
760
  'description': '',
687
- 'emphasis': {'disabled': true},
688
- 'select': {'disabled': true},
689
- 'tooltip': {'show': false},
761
+ 'emphasis': { 'disabled': true },
762
+ 'select': { 'disabled': true },
763
+ 'tooltip': { 'show': false },
690
764
  'itemStyle': {
691
765
  'color': '#FFFFFF'
692
- }
766
+ }
693
767
  };
694
768
 
695
769
  crops.data.push(pieItem);
696
770
  }
697
771
  }
698
-
772
+
699
773
 
700
774
  let days = Math.round((item.endDate - item.startDate) / (1000 * 60 * 60 * 24));
701
775
  lastDayOfPreviousStep = new Date(item.endDate.valueOf());
@@ -762,18 +836,18 @@ class RotationRenderer {
762
836
  const monthName = startMonth.toLocaleDateString(undefined, { month: 'short' });
763
837
  const year = startMonth.getFullYear();
764
838
 
765
- months.data.push({
766
- 'name': monthName,
839
+ months.data.push({
840
+ 'name': monthName,
767
841
  'value': 1,
768
842
  'itemStyle': {
769
843
  'color': monthsColorScale[startMonth.getMonth()]
770
- }
844
+ }
771
845
  });
772
846
 
773
847
  let currentMonthsPerYear = monthsPerYear.get(year);
774
848
  if (currentMonthsPerYear == undefined)
775
849
  currentMonthsPerYear = 0;
776
- monthsPerYear.set(year, ++currentMonthsPerYear);
850
+ monthsPerYear.set(year, ++currentMonthsPerYear);
777
851
 
778
852
  // increment the current month
779
853
  startMonth.setMonth(startMonth.getMonth() + 1);
@@ -802,9 +876,9 @@ class RotationRenderer {
802
876
  };
803
877
 
804
878
  monthsPerYear.forEach((nbMonths, year) => {
805
- years.data.push({
806
- 'name': year,
807
- 'value': nbMonths
879
+ years.data.push({
880
+ 'name': year,
881
+ 'value': nbMonths
808
882
  });
809
883
  });
810
884
 
@@ -845,60 +919,60 @@ class RotationRenderer {
845
919
  }
846
920
 
847
921
  option.tooltip = {
848
- extraCssText: "text-wrap: wrap;",
849
- className: "rotation-tooltip",
850
- formatter: function (params) {
851
- if (params.data.type == 'rotation_item') {
852
- let start = params.data.startDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: '2-digit' });
853
- let end = params.data.endDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: '2-digit' });
854
-
855
- return params.marker + params.name + ' : ' + params.data.duration + ' mois (' + start + ' ➜ ' + end + ')<br>' + params.data.description;
856
- }
857
- else {
858
- let interventionDate = params.data.interventionDate;
859
- const days = params.data.interventionDays;
860
- let dateString = interventionDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' });
922
+ extraCssText: "text-wrap: wrap;",
923
+ className: "rotation-tooltip",
924
+ formatter: function (params) {
925
+ if (params.data.type == 'rotation_item') {
926
+ let start = params.data.startDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: '2-digit' });
927
+ let end = params.data.endDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: '2-digit' });
928
+
929
+ return params.marker + params.name + ' : ' + params.data.duration + ' mois (' + start + ' ➜ ' + end + ')<br>' + params.data.description;
930
+ }
931
+ else {
932
+ let interventionDate = params.data.interventionDate;
933
+ const days = params.data.interventionDays;
934
+ let dateString = interventionDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' });
861
935
 
862
- if (days >= 0)
863
- dateString += ' (J+' + days + ')';
864
- else
865
- dateString += ' (J' + days + ')';
936
+ if (days >= 0)
937
+ dateString += ' (J+' + (days == 0 ? '0' : days) + ')';
938
+ else
939
+ dateString += ' (J-' + days + ')';
866
940
 
867
- return params.marker + params.name + ' - ' + dateString + '<br>' + params.data.description;
868
- }
941
+ return params.marker + params.name + ' - ' + dateString + '<br>' + params.data.description;
869
942
  }
870
- };
943
+ }
944
+ };
871
945
 
872
946
  option.toolbox = {
873
- "itemSize": 25,
874
- "iconStyle": {
875
- "borderColor": "#AAA",
876
- "borderWidth": 1
947
+ "itemSize": 25,
948
+ "iconStyle": {
949
+ "borderColor": "#AAA",
950
+ "borderWidth": 1
951
+ },
952
+ "feature": {
953
+ "myTool1": {
954
+ "show": true,
955
+ "title": 'Rotation',
956
+ "icon": 'path://M18.15,12.99c-.06-.07-.14-.13-.23-.17l-.38-.15c.33-.93.51-1.92.51-2.96,0-3-1.49-5.65-3.77-7.26l-3.03,2.72c1.72.79,2.91,2.53,2.91,4.54,0,.54-.09,1.07-.25,1.56l-.44-.17c-.3-.12-.64.03-.76.33-.04.1-.05.21-.03.31l.79,4.82c.06.32.36.53.68.47.1-.02.19-.06.27-.13l3.66-3.1c.25-.21.28-.58.07-.82ZM6.94,5.24c.46-.23.97-.39,1.5-.47l.13.59c.07.32.38.52.69.45.1-.02.2-.07.28-.14l3.59-3.31c.23-.22.24-.59.02-.83-.07-.07-.16-.13-.26-.16L8.3.02c-.31-.09-.63.09-.73.39-.03.09-.03.19-.01.29l.06.27c-1.12.2-2.16.6-3.1,1.17l2.41,3.09ZM5.88,5.96l-2.39-3.06c-.98.82-1.79,1.84-2.34,3.01l3.62,1.44c.29-.53.66-1,1.11-1.39ZM4.17,9.72c0-.41.05-.81.14-1.19l-3.63-1.44c-.18.57-.3,1.16-.35,1.77l3.84,1.1c0-.08,0-.16,0-.24ZM9.17,14.72c-1.59,0-3-.74-3.92-1.9l.42-.38c.24-.22.26-.59.04-.83-.07-.08-.16-.14-.26-.17l-4.66-1.47c-.31-.09-.64.08-.73.39-.03.1-.03.2,0,.3l1.12,4.66c.07.31.39.51.7.43.09-.02.18-.07.25-.13l.23-.21c1.63,1.94,4.07,3.18,6.8,3.18,1.3,0,2.54-.28,3.66-.79l-.8-3.99c-.81.56-1.79.9-2.86.9Z',
957
+ onclick: function () {
958
+ self.initialLayout = 'donut';
959
+ self.renderChart();
960
+ }
877
961
  },
878
- "feature": {
879
- "myTool1": {
880
- "show": true,
881
- "title": 'Rotation',
882
- "icon": 'path://M18.15,12.99c-.06-.07-.14-.13-.23-.17l-.38-.15c.33-.93.51-1.92.51-2.96,0-3-1.49-5.65-3.77-7.26l-3.03,2.72c1.72.79,2.91,2.53,2.91,4.54,0,.54-.09,1.07-.25,1.56l-.44-.17c-.3-.12-.64.03-.76.33-.04.1-.05.21-.03.31l.79,4.82c.06.32.36.53.68.47.1-.02.19-.06.27-.13l3.66-3.1c.25-.21.28-.58.07-.82ZM6.94,5.24c.46-.23.97-.39,1.5-.47l.13.59c.07.32.38.52.69.45.1-.02.2-.07.28-.14l3.59-3.31c.23-.22.24-.59.02-.83-.07-.07-.16-.13-.26-.16L8.3.02c-.31-.09-.63.09-.73.39-.03.09-.03.19-.01.29l.06.27c-1.12.2-2.16.6-3.1,1.17l2.41,3.09ZM5.88,5.96l-2.39-3.06c-.98.82-1.79,1.84-2.34,3.01l3.62,1.44c.29-.53.66-1,1.11-1.39ZM4.17,9.72c0-.41.05-.81.14-1.19l-3.63-1.44c-.18.57-.3,1.16-.35,1.77l3.84,1.1c0-.08,0-.16,0-.24ZM9.17,14.72c-1.59,0-3-.74-3.92-1.9l.42-.38c.24-.22.26-.59.04-.83-.07-.08-.16-.14-.26-.17l-4.66-1.47c-.31-.09-.64.08-.73.39-.03.1-.03.2,0,.3l1.12,4.66c.07.31.39.51.7.43.09-.02.18-.07.25-.13l.23-.21c1.63,1.94,4.07,3.18,6.8,3.18,1.3,0,2.54-.28,3.66-.79l-.8-3.99c-.81.56-1.79.9-2.86.9Z',
883
- onclick: function (){
884
- self.initialLayout = 'donut';
885
- self.renderChart();
886
- }
887
- },
888
- "myTool2": {
889
- "show": true,
890
- "title": 'Frise',
891
- "icon": 'path://M4.63,0H0v10.01h4.97c.07,0,.13-.05.19-.14l2.61-4.14c.17-.28.23-.89.12-1.35-.03-.15-.08-.27-.13-.35L5.16.12c-.05-.08-.11-.12-.17-.12h-.37ZM11.9,4.38c-.03-.15-.08-.27-.13-.35L9.17.12c-.05-.08-.11-.12-.17-.12h-.37s-2.26,0-2.26,0v.03s.06.05.08.09l2.6,3.9c.06.08.1.21.13.35.1.47.05,1.07-.12,1.35l-2.61,4.14s-.05.07-.08.09v.04h2.6c.07,0,.13-.05.19-.14l2.61-4.14c.17-.28.23-.89.12-1.35ZM18.28,4.38c-.03-.15-.08-.27-.13-.35L15.55.12c-.05-.08-.11-.12-.17-.12h-.37s-4.63,0-4.63,0v.03s.06.05.08.09l2.6,3.9c.06.08.1.21.13.35.1.47.05,1.07-.12,1.35l-2.61,4.14s-.05.07-.08.09v.04h4.97c.07,0,.13-.05.19-.14l2.61-4.14c.17-.28.23-.89.12-1.35Z',
892
- onclick: function (){
893
- self.initialLayout = 'horizontal';
894
- self.renderChart();
895
- }
896
- },
897
- "saveAsImage": {
898
- 'excludeComponents': ["dataZoom", "toolbox"]
962
+ "myTool2": {
963
+ "show": true,
964
+ "title": 'Frise',
965
+ "icon": 'path://M4.63,0H0v10.01h4.97c.07,0,.13-.05.19-.14l2.61-4.14c.17-.28.23-.89.12-1.35-.03-.15-.08-.27-.13-.35L5.16.12c-.05-.08-.11-.12-.17-.12h-.37ZM11.9,4.38c-.03-.15-.08-.27-.13-.35L9.17.12c-.05-.08-.11-.12-.17-.12h-.37s-2.26,0-2.26,0v.03s.06.05.08.09l2.6,3.9c.06.08.1.21.13.35.1.47.05,1.07-.12,1.35l-2.61,4.14s-.05.07-.08.09v.04h2.6c.07,0,.13-.05.19-.14l2.61-4.14c.17-.28.23-.89.12-1.35ZM18.28,4.38c-.03-.15-.08-.27-.13-.35L15.55.12c-.05-.08-.11-.12-.17-.12h-.37s-4.63,0-4.63,0v.03s.06.05.08.09l2.6,3.9c.06.08.1.21.13.35.1.47.05,1.07-.12,1.35l-2.61,4.14s-.05.07-.08.09v.04h4.97c.07,0,.13-.05.19-.14l2.61-4.14c.17-.28.23-.89.12-1.35Z',
966
+ onclick: function () {
967
+ self.initialLayout = 'horizontal';
968
+ self.renderChart();
899
969
  }
970
+ },
971
+ "saveAsImage": {
972
+ 'excludeComponents': ["dataZoom", "toolbox"]
900
973
  }
901
- };
974
+ }
975
+ };
902
976
 
903
977
  return option;
904
978
  }
@@ -28,6 +28,7 @@ function loadSelectedStepToEditor(aStep) {
28
28
  setInputValue("cropStartDate", aStep.getStep().startDate.toISOString().split('T')[0]);
29
29
  setInputValue("cropEndDate", aStep.getStep().endDate.toISOString().split('T')[0]);
30
30
  setInputValue("cropDescription", aStep.getStep().description);
31
+ document.getElementById("cropSecondary").checked = aStep.getStep().secondary_crop || false;
31
32
  }
32
33
 
33
34
  function refreshStepsButtonList() {
@@ -71,6 +71,18 @@ function createInterventionForm(id, day, name, type, description, row) {
71
71
  type = type || "";
72
72
  description = description || "";
73
73
 
74
+ // Calculate absolute date from relative day
75
+ let absoluteDate = "";
76
+ if (day == "")
77
+ day = 0;
78
+
79
+ if (selectedStep && selectedStep.getStep().startDate) {
80
+ const stepStartDate = new Date(selectedStep.getStep().startDate);
81
+ const interventionDate = new Date(stepStartDate);
82
+ interventionDate.setDate(stepStartDate.getDate() + parseInt(day));
83
+ absoluteDate = interventionDate.toISOString().split('T')[0];
84
+ }
85
+
74
86
  const formContainer = document.createElement("div");
75
87
  formContainer.innerHTML =
76
88
  `<form id="interventionForm">
@@ -84,11 +96,15 @@ function createInterventionForm(id, day, name, type, description, row) {
84
96
  <textarea id="interventionDescription" class="form-control"
85
97
  placeholder="Ajouter une description">${description}</textarea>
86
98
  </div>
87
- <div class="col-12 mb-2">
88
- <label for="interventionDay" class="form-label">Quel jour après la plantation ?</label>
89
- <input type="number" id="interventionDay" class="form-control" placeholder="Jour"
99
+ <div class="col-4 mb-2">
100
+ <label for="interventionDay" class="form-label">Jour relatif</label>
101
+ <input type="number" id="interventionDay" class="form-control text-right" placeholder="Jour"
90
102
  value="${day}">
91
103
  </div>
104
+ <div class="col-8 mb-2">
105
+ <label for="interventionDate" class="form-label">Date absolue</label>
106
+ <input type="date" id="interventionDate" class="form-control" value="${absoluteDate}">
107
+ </div>
92
108
  <div class="col-12 mb-2">
93
109
  <select id="interventionType" class="form-select" aria-label="Type">
94
110
  <option value="intervention_top"
@@ -113,5 +129,49 @@ function createInterventionForm(id, day, name, type, description, row) {
113
129
  document.getElementById("newInterventionButton").classList.add("d-none");
114
130
  }
115
131
 
132
+ // Add event listeners for date synchronization
133
+ setupInterventionDateListeners();
134
+
116
135
  $("#interventionName").focus();
136
+ }
137
+
138
+ function setupInterventionDateListeners() {
139
+ // When relative day changes, update absolute date
140
+ $("#interventionDay").on("input change", function() {
141
+ updateAbsoluteDateFromRelative();
142
+ });
143
+
144
+ // When absolute date changes, update relative day
145
+ $("#interventionDate").on("change", function() {
146
+ updateRelativeDayFromAbsolute();
147
+ });
148
+ }
149
+
150
+ function updateAbsoluteDateFromRelative() {
151
+ const relativeDay = $("#interventionDay").val();
152
+ if (relativeDay == "")
153
+ relativeDay = 0;
154
+
155
+ if (selectedStep && selectedStep.getStep().startDate) {
156
+ const stepStartDate = new Date(selectedStep.getStep().startDate);
157
+ const interventionDate = new Date(stepStartDate);
158
+ interventionDate.setDate(stepStartDate.getDate() + parseInt(relativeDay));
159
+
160
+ const absoluteDateStr = interventionDate.toISOString().split('T')[0];
161
+ $("#interventionDate").val(absoluteDateStr);
162
+ }
163
+ }
164
+
165
+ function updateRelativeDayFromAbsolute() {
166
+ const absoluteDate = $("#interventionDate").val();
167
+ if (absoluteDate !== "" && selectedStep && selectedStep.getStep().startDate) {
168
+ const stepStartDate = new Date(selectedStep.getStep().startDate);
169
+ const interventionDate = new Date(absoluteDate);
170
+
171
+ // Calculate difference in days
172
+ const timeDiff = interventionDate.getTime() - stepStartDate.getTime();
173
+ const dayDiff = Math.round(timeDiff / (1000 * 60 * 60 * 24));
174
+
175
+ $("#interventionDay").val(dayDiff);
176
+ }
117
177
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osfarm/itineraire-technique",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "A visualisation tool to show agricultural technical itineraries based on Echarts",
5
5
  "main": "editor.html",
6
6
  "scripts": {
@@ -74,6 +74,10 @@ $header-height: 3rem;
74
74
  }
75
75
  }
76
76
 
77
+ .intervention-row {
78
+ background: #e0e0e0;
79
+ }
80
+
77
81
  .primary-button {
78
82
  color: $button-text-color;
79
83
  background-color: $button-color;
@@ -103,4 +107,10 @@ $header-height: 3rem;
103
107
  margin-right: 10px;
104
108
  vertical-align: middle;
105
109
  cursor: grab;
110
+ }
111
+
112
+ .form-control {
113
+ &.text-right {
114
+ text-align: right;
115
+ }
106
116
  }