@osfarm/itineraire-technique 1.2.3 → 1.2.5
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/css/styles-editor.css +1 -1
- package/css/styles-editor.css.map +1 -1
- package/js/editor-loader-default.js +36 -9
- package/js/editor-loader-itinera.js +15 -13
- package/js/editor-loader-wiki.js +48 -45
- package/js/editor-main.js +115 -49
- package/package.json +1 -1
- package/scss/styles-editor.scss +14 -0
package/css/styles-editor.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.main-header{background-color:#6fa76f;color:#fff;height:3rem;display:flex;align-items:center}.main-header .btn.show,.main-header .btn:first-child:active,.main-header :not(.btn-check)+.btn:active{background-color:#026602}.editor-view{overflow-y:auto;height:calc(100vh - 4rem)}.rotation_item .step-edit{color:#878787;cursor:pointer;display:none;margin-left:10px;font-size:70%;vertical-align:super}.rotation_item:hover .step-edit{display:inline !important}.welcome-view{background-color:#6fa76f;color:#fff;padding:1rem;margin-top:1rem;border-radius:1rem}.welcome-view #cropsContainer{color:green}.card-white{background-color:#fff;padding:1rem;border-radius:1rem}.card-holder{background-color:#f5f5f5;padding:1rem;border-radius:1rem;margin:auto}.editable-row{background-color:#f7f7f7;min-height:3rem;display:flex;align-items:center;border-radius:.5rem}.editable-row .edit-buttons{visibility:hidden}.editable-row:hover>.edit-buttons{visibility:visible}.intervention-row{background:#e0e0e0}.primary-button{color:#fff;background-color:green;border:green}.primary-button:hover{background-color:#026602}.close-step-times{background:none;border:none;color:#878787;font-size:120%}.close-step-times:hover{color:#494949}#cropsContainer .drag-handle{color:#ccc;font-weight:normal;font-size:86%;margin-right:10px;vertical-align:middle;cursor:grab}#cropsContainer div.col{cursor:pointer}.form-control.text-right{text-align:right}.modal .form-label{font-weight:600}#code-snippet{font-family:monospace;font-size:13px;text-align:left;border:1px inset;background-color:#f1f1f1}/*# sourceMappingURL=styles-editor.css.map */
|
|
1
|
+
.main-header{background-color:#6fa76f;color:#fff;height:3rem;display:flex;align-items:center}.main-header .btn.show,.main-header .btn:first-child:active,.main-header :not(.btn-check)+.btn:active{background-color:#026602}.editor-view{overflow-y:auto;height:calc(100vh - 4rem)}.rotation_item .step-edit{color:#878787;cursor:pointer;display:none;margin-left:10px;font-size:70%;vertical-align:super}.rotation_item:hover .step-edit{display:inline !important}.welcome-view{background-color:#6fa76f;color:#fff;padding:1rem;margin-top:1rem;border-radius:1rem}.welcome-view #cropsContainer{color:green}.card-white{background-color:#fff;padding:1rem;border-radius:1rem}.card-holder{background-color:#f5f5f5;padding:1rem;border-radius:1rem;margin:auto}@keyframes step-highlight{0%{box-shadow:0 0 0 3px #ffc107}80%{box-shadow:0 0 0 3px #ffc107}100%{box-shadow:0 0 0 0px rgba(0,0,0,0)}}.editable-row{background-color:#f7f7f7;min-height:3rem;display:flex;align-items:center;border-radius:.5rem}.editable-row .edit-buttons{visibility:hidden}.editable-row:hover>.edit-buttons{visibility:visible}.editable-row.step-shifted{animation:step-highlight 1.4s ease-out forwards}.editable-row.step-shifted .edit-buttons{visibility:visible}.intervention-row{background:#e0e0e0}.primary-button{color:#fff;background-color:green;border:green}.primary-button:hover{background-color:#026602}.close-step-times{background:none;border:none;color:#878787;font-size:120%}.close-step-times:hover{color:#494949}#cropsContainer .drag-handle{color:#ccc;font-weight:normal;font-size:86%;margin-right:10px;vertical-align:middle;cursor:grab}#cropsContainer div.col{cursor:pointer}.form-control.text-right{text-align:right}.modal .form-label{font-weight:600}#code-snippet{font-family:monospace;font-size:13px;text-align:left;border:1px inset;background-color:#f1f1f1}/*# sourceMappingURL=styles-editor.css.map */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sourceRoot":"","sources":["../scss/styles-editor.scss"],"names":[],"mappings":"AAKA,aACI,yBACA,WACA,OALiB,KAOjB,aACA,mBAEA,sGAGI,yBAIR,aACI,gBAGA,0BAKA,0BACI,cACA,eACA,aACA,iBACA,cACA,qBAGJ,gCACI,0BAIR,cACI,yBACA,WACA,aACA,gBACA,mBAEA,8BACI,YAIR,YACI,sBACA,aACA,mBAGJ,aACI,yBACA,aACA,mBACA,YAGJ,cACI,yBACA,gBAEA,aACA,mBAEA,oBAEA,4BACI,kBAGJ,kCACI,
|
|
1
|
+
{"version":3,"sourceRoot":"","sources":["../scss/styles-editor.scss"],"names":[],"mappings":"AAKA,aACI,yBACA,WACA,OALiB,KAOjB,aACA,mBAEA,sGAGI,yBAIR,aACI,gBAGA,0BAKA,0BACI,cACA,eACA,aACA,iBACA,cACA,qBAGJ,gCACI,0BAIR,cACI,yBACA,WACA,aACA,gBACA,mBAEA,8BACI,YAIR,YACI,sBACA,aACA,mBAGJ,aACI,yBACA,aACA,mBACA,YAGJ,0BACI,gCACA,iCACA,yCAGJ,cACI,yBACA,gBAEA,aACA,mBAEA,oBAEA,4BACI,kBAGJ,kCACI,mBAGJ,2BACI,gDAEA,yCACI,mBAKZ,kBACI,mBAGJ,gBACI,MAvGiB,KAwGjB,iBA1GiB,MA2GjB,OA3GiB,MA6GjB,sBACI,iBA7Ga,QAiHrB,kBACI,gBACA,YACA,cACA,eAEA,wBACI,cAKJ,6BACI,WACA,mBACA,cACA,kBACA,sBACA,YAGJ,wBACI,eAKJ,yBACI,iBAKJ,mBACI,gBAIR,cACI,sBACA,eACA,gBACA,iBACA","file":"styles-editor.css"}
|
|
@@ -5,6 +5,31 @@
|
|
|
5
5
|
class DefaultLoader {
|
|
6
6
|
constructor(tikaeditorInstance = null) {
|
|
7
7
|
this.tikaeditorInstance = tikaeditorInstance;
|
|
8
|
+
this.hasUnsavedChanges = false;
|
|
9
|
+
|
|
10
|
+
// Warn the user before leaving the page if there are unsaved changes
|
|
11
|
+
const self = this;
|
|
12
|
+
window.addEventListener('beforeunload', function(e) {
|
|
13
|
+
if (self.hasUnsavedChanges) {
|
|
14
|
+
e.preventDefault();
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Mark dirty on any data mutation
|
|
19
|
+
if (tikaeditorInstance) {
|
|
20
|
+
const originalRefresh = tikaeditorInstance.refreshAllTables.bind(tikaeditorInstance);
|
|
21
|
+
tikaeditorInstance.refreshAllTables = function() {
|
|
22
|
+
self.hasUnsavedChanges = true;
|
|
23
|
+
return originalRefresh();
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Mark the editor as having no unsaved changes
|
|
30
|
+
*/
|
|
31
|
+
markClean() {
|
|
32
|
+
this.hasUnsavedChanges = false;
|
|
8
33
|
}
|
|
9
34
|
|
|
10
35
|
/**
|
|
@@ -15,7 +40,7 @@ class DefaultLoader {
|
|
|
15
40
|
setupButtons() {
|
|
16
41
|
const self = this;
|
|
17
42
|
const container = '#toolbar-buttons-container';
|
|
18
|
-
|
|
43
|
+
|
|
19
44
|
// Create the NonWikiButtons div structure for standalone mode
|
|
20
45
|
const nonWikiButtonsDiv = $(`
|
|
21
46
|
<div id="NonWikiButtons" class="">
|
|
@@ -35,26 +60,26 @@ class DefaultLoader {
|
|
|
35
60
|
</button>
|
|
36
61
|
</div>
|
|
37
62
|
`);
|
|
38
|
-
|
|
63
|
+
|
|
39
64
|
// Clear container and append the new buttons
|
|
40
65
|
$(container).empty().append(nonWikiButtonsDiv);
|
|
41
|
-
|
|
66
|
+
|
|
42
67
|
// Attach event handlers
|
|
43
68
|
nonWikiButtonsDiv.find('.btn-import-json').on('click', function(e) {
|
|
44
69
|
e.preventDefault();
|
|
45
70
|
self.importFromJsonFile();
|
|
46
71
|
});
|
|
47
|
-
|
|
72
|
+
|
|
48
73
|
nonWikiButtonsDiv.find('.btn-import-test').on('click', function(e) {
|
|
49
74
|
e.preventDefault();
|
|
50
75
|
self.importFromTestJson();
|
|
51
76
|
});
|
|
52
|
-
|
|
77
|
+
|
|
53
78
|
nonWikiButtonsDiv.find('.btn-export-json').on('click', function(e) {
|
|
54
79
|
e.preventDefault();
|
|
55
80
|
self.doExportToJsonFile();
|
|
56
81
|
});
|
|
57
|
-
|
|
82
|
+
|
|
58
83
|
// Add wipe button
|
|
59
84
|
this.addWipeButton();
|
|
60
85
|
}
|
|
@@ -65,15 +90,15 @@ class DefaultLoader {
|
|
|
65
90
|
addWipeButton() {
|
|
66
91
|
const self = this;
|
|
67
92
|
const container = '#toolbar-buttons-container';
|
|
68
|
-
|
|
93
|
+
|
|
69
94
|
const wipeButton = $(`
|
|
70
95
|
<button type="button" id="wipe-button" class="btn btn-outline-primary primary-button">
|
|
71
96
|
<i class="fa fa-trash" aria-hidden="true"></i> Tout effacer
|
|
72
97
|
</button>
|
|
73
98
|
`);
|
|
74
|
-
|
|
99
|
+
|
|
75
100
|
$(container).append(wipeButton);
|
|
76
|
-
|
|
101
|
+
|
|
77
102
|
wipeButton.on('click', function(e) {
|
|
78
103
|
e.preventDefault();
|
|
79
104
|
self.wipe();
|
|
@@ -86,6 +111,7 @@ class DefaultLoader {
|
|
|
86
111
|
doExportToJsonFile() {
|
|
87
112
|
let jsonName = this.tikaeditorInstance.system.title.replace(/\s+/g, '-').toLowerCase() + ".json";
|
|
88
113
|
this.exportToJsonFile(this.tikaeditorInstance.system, jsonName);
|
|
114
|
+
this.markClean();
|
|
89
115
|
}
|
|
90
116
|
|
|
91
117
|
/**
|
|
@@ -169,6 +195,7 @@ class DefaultLoader {
|
|
|
169
195
|
};
|
|
170
196
|
|
|
171
197
|
self.tikaeditorInstance.reloadCropsFromJson(crops);
|
|
198
|
+
self.markClean();
|
|
172
199
|
});
|
|
173
200
|
}
|
|
174
201
|
|
|
@@ -13,7 +13,7 @@ class ItineraLoader extends DefaultLoader {
|
|
|
13
13
|
setupButtons() {
|
|
14
14
|
const self = this;
|
|
15
15
|
const container = '#toolbar-buttons-container';
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
// Create the ItineraButtons div structure
|
|
18
18
|
const itineraButtonsDiv = $(`
|
|
19
19
|
<div id="ItineraButtons" class="">
|
|
@@ -22,25 +22,25 @@ class ItineraLoader extends DefaultLoader {
|
|
|
22
22
|
</button>
|
|
23
23
|
</div>
|
|
24
24
|
`);
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
// Clear container and append the new buttons
|
|
27
27
|
$(container).empty().append(itineraButtonsDiv);
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
// Attach event handler
|
|
30
30
|
itineraButtonsDiv.find('.btn-save-itinera').on('click', function(e) {
|
|
31
31
|
e.preventDefault();
|
|
32
32
|
self.saveToItinera();
|
|
33
33
|
});
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
// Don't add wipe button in Itinera mode
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* When the page loads, get the URL paremeter with the target page title we want to edit:
|
|
40
|
-
* @returns
|
|
40
|
+
* @returns
|
|
41
41
|
*/
|
|
42
42
|
loadPageFromURL() {
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
const self = this;
|
|
45
45
|
|
|
46
46
|
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -51,7 +51,7 @@ class ItineraLoader extends DefaultLoader {
|
|
|
51
51
|
return; // No page title provided, we are not in wiki edit mode
|
|
52
52
|
|
|
53
53
|
// Build API URL with UUID if provided
|
|
54
|
-
const apiUrl = uuid
|
|
54
|
+
const apiUrl = uuid
|
|
55
55
|
? `/api/systems/${encodeURIComponent(self.systemID)}?uuid=${encodeURIComponent(uuid)}`
|
|
56
56
|
: `/api/systems/${encodeURIComponent(self.systemID)}`;
|
|
57
57
|
|
|
@@ -69,7 +69,7 @@ class ItineraLoader extends DefaultLoader {
|
|
|
69
69
|
// Extract UUID from redirectTo path (format: /project/22/UUID)
|
|
70
70
|
const pathParts = data.redirectTo.split('/');
|
|
71
71
|
const redirectUuid = pathParts[pathParts.length - 1];
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
if (redirectUuid) {
|
|
74
74
|
// Retry with UUID parameter
|
|
75
75
|
return fetch(`/api/systems/${encodeURIComponent(self.systemID)}?uuid=${redirectUuid}`, {
|
|
@@ -87,15 +87,16 @@ class ItineraLoader extends DefaultLoader {
|
|
|
87
87
|
let sm = new StepModel(step)
|
|
88
88
|
sm.setAsEdited();
|
|
89
89
|
});
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
self.tikaeditorInstance.reloadCropsFromJson(content);
|
|
92
|
+
self.markClean();
|
|
92
93
|
|
|
93
94
|
let codeSnippet = `{{Graphique Triple Performance \n| title=${content.title} \n| json=${self.systemID} \n| type=Rotation }}`;
|
|
94
95
|
$('#code-snippet').val(codeSnippet).on('focus', function() {
|
|
95
96
|
$(this).select();
|
|
96
97
|
});
|
|
97
98
|
$('#codeSnippetDiv').show();
|
|
98
|
-
|
|
99
|
+
|
|
99
100
|
|
|
100
101
|
} catch (e) {
|
|
101
102
|
console.error("Erreur lors de l'analyse du JSON de la page :", e);
|
|
@@ -107,7 +108,7 @@ class ItineraLoader extends DefaultLoader {
|
|
|
107
108
|
self.wipe();
|
|
108
109
|
}
|
|
109
110
|
});
|
|
110
|
-
|
|
111
|
+
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
/**
|
|
@@ -120,7 +121,7 @@ class ItineraLoader extends DefaultLoader {
|
|
|
120
121
|
// Error ?
|
|
121
122
|
return;
|
|
122
123
|
}
|
|
123
|
-
|
|
124
|
+
|
|
124
125
|
// Proceed to save
|
|
125
126
|
const response = await fetch(`/api/systems/${encodeURIComponent(self.systemID)}`, {
|
|
126
127
|
method: 'PATCH',
|
|
@@ -142,12 +143,13 @@ class ItineraLoader extends DefaultLoader {
|
|
|
142
143
|
|
|
143
144
|
if (response.ok) {
|
|
144
145
|
// Successfully saved - show a toast
|
|
146
|
+
self.markClean();
|
|
145
147
|
|
|
146
148
|
toast.find('.toast-body').text('Sauvegardé dans Itinéra !');
|
|
147
149
|
|
|
148
150
|
const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toast);
|
|
149
151
|
toastBootstrap.show();
|
|
150
|
-
|
|
152
|
+
|
|
151
153
|
} else {
|
|
152
154
|
// Error saving
|
|
153
155
|
|
package/js/editor-loader-wiki.js
CHANGED
|
@@ -13,7 +13,7 @@ class WikiLoader extends DefaultLoader {
|
|
|
13
13
|
setupButtons() {
|
|
14
14
|
const self = this;
|
|
15
15
|
const container = '#toolbar-buttons-container';
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
// Create the WikiButtons div structure
|
|
18
18
|
const wikiButtonsDiv = $(`
|
|
19
19
|
<div id="WikiButtons" class="">
|
|
@@ -43,10 +43,10 @@ class WikiLoader extends DefaultLoader {
|
|
|
43
43
|
</div>
|
|
44
44
|
</div>
|
|
45
45
|
`);
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
// Clear container and append the new buttons
|
|
48
48
|
$(container).empty().append(wikiButtonsDiv);
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
// Attach event handlers
|
|
51
51
|
wikiButtonsDiv.find('.btn-load-wiki').on('click', function(e) {
|
|
52
52
|
e.preventDefault();
|
|
@@ -54,32 +54,32 @@ class WikiLoader extends DefaultLoader {
|
|
|
54
54
|
self.loadFromWiki();
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
wikiButtonsDiv.find('.btn-save-wiki').on('click', function(e) {
|
|
59
59
|
e.preventDefault();
|
|
60
60
|
self.saveToWiki();
|
|
61
61
|
});
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
wikiButtonsDiv.find('.btn-save-as').on('click', function(e) {
|
|
64
64
|
e.preventDefault();
|
|
65
65
|
self.showSaveAsModal();
|
|
66
66
|
});
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
wikiButtonsDiv.find('.btn-import-test').on('click', function(e) {
|
|
69
69
|
e.preventDefault();
|
|
70
70
|
self.importFromTestJson();
|
|
71
71
|
});
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
wikiButtonsDiv.find('.btn-import-json').on('click', function(e) {
|
|
74
74
|
e.preventDefault();
|
|
75
75
|
self.importFromJsonFile();
|
|
76
76
|
});
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
wikiButtonsDiv.find('.btn-export-json').on('click', function(e) {
|
|
79
79
|
e.preventDefault();
|
|
80
80
|
self.doExportToJsonFile();
|
|
81
81
|
});
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
// Add wipe button
|
|
84
84
|
this.addWipeButton();
|
|
85
85
|
|
|
@@ -95,10 +95,10 @@ class WikiLoader extends DefaultLoader {
|
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* When the page loads, get the URL paremeter with the target page title we want to edit:
|
|
98
|
-
* @returns
|
|
98
|
+
* @returns
|
|
99
99
|
*/
|
|
100
100
|
loadPageFromURL() {
|
|
101
|
-
|
|
101
|
+
|
|
102
102
|
const self = this;
|
|
103
103
|
|
|
104
104
|
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -124,6 +124,7 @@ class WikiLoader extends DefaultLoader {
|
|
|
124
124
|
sm.setAsEdited();
|
|
125
125
|
});
|
|
126
126
|
self.tikaeditorInstance.reloadCropsFromJson(content);
|
|
127
|
+
self.markClean();
|
|
127
128
|
|
|
128
129
|
let codeSnippet = `{{Graphique Triple Performance \n| title=${content.title} \n| json=${self.pageTitle} \n| type=Rotation }}`;
|
|
129
130
|
$('#code-snippet').val(codeSnippet).on('focus', function() {
|
|
@@ -151,7 +152,7 @@ class WikiLoader extends DefaultLoader {
|
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
154
|
});
|
|
154
|
-
|
|
155
|
+
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
/**
|
|
@@ -164,10 +165,11 @@ class WikiLoader extends DefaultLoader {
|
|
|
164
165
|
self.showSaveAsModal();
|
|
165
166
|
return;
|
|
166
167
|
}
|
|
167
|
-
|
|
168
|
+
|
|
168
169
|
// If a page title is provided, save to that page
|
|
169
170
|
self.savePageToWiki(self.pageTitle, JSON.stringify(self.tikaeditorInstance.system, null, 2))
|
|
170
171
|
.then(async () => {
|
|
172
|
+
self.markClean();
|
|
171
173
|
alert("Itinéraire technique enregistré avec succès !");
|
|
172
174
|
})
|
|
173
175
|
.catch(err => {
|
|
@@ -204,7 +206,7 @@ class WikiLoader extends DefaultLoader {
|
|
|
204
206
|
const editData = await editResp.json();
|
|
205
207
|
console.log(editData);
|
|
206
208
|
|
|
207
|
-
if (editData.edit && editData.edit.result === 'Success') {
|
|
209
|
+
if (editData.edit && editData.edit.result === 'Success') {
|
|
208
210
|
return Promise.resolve();
|
|
209
211
|
} else {
|
|
210
212
|
return Promise.reject(editData);
|
|
@@ -230,7 +232,7 @@ class WikiLoader extends DefaultLoader {
|
|
|
230
232
|
const encodedUsername = 'User:' + username;
|
|
231
233
|
const query = encodeURIComponent(`[[Page author::${encodedUsername}]][[~*.json]]`);
|
|
232
234
|
const url = `/api.php?action=ask&query=${query}|sort=Modification date|order=desc&format=json`;
|
|
233
|
-
|
|
235
|
+
|
|
234
236
|
const response = await fetch(url, {
|
|
235
237
|
credentials: 'include'
|
|
236
238
|
});
|
|
@@ -249,7 +251,7 @@ class WikiLoader extends DefaultLoader {
|
|
|
249
251
|
const encodedUsername = 'User:' + username;
|
|
250
252
|
const query = encodeURIComponent(`[[Page author::${encodedUsername}]]`);
|
|
251
253
|
const url = `/api.php?action=ask&query=${query}|sort=Modification date|order=desc&format=json`;
|
|
252
|
-
|
|
254
|
+
|
|
253
255
|
const response = await fetch(url, {
|
|
254
256
|
credentials: 'include'
|
|
255
257
|
});
|
|
@@ -265,40 +267,40 @@ class WikiLoader extends DefaultLoader {
|
|
|
265
267
|
// Show the modal
|
|
266
268
|
const modal = new bootstrap.Modal(document.getElementById('wikiFilesModal'));
|
|
267
269
|
modal.show();
|
|
268
|
-
|
|
270
|
+
|
|
269
271
|
// Reset the modal content
|
|
270
272
|
$('#wikiFilesStatus').html('<i class="fa fa-spinner fa-spin"></i> Chargement de vos fichiers...');
|
|
271
273
|
$('#wikiFilesList').empty();
|
|
272
|
-
|
|
274
|
+
|
|
273
275
|
try {
|
|
274
276
|
// Get user info
|
|
275
277
|
const userInfo = await this.getWikiUserInfo();
|
|
276
|
-
|
|
278
|
+
|
|
277
279
|
if (!userInfo.id || userInfo.id === 0) {
|
|
278
280
|
$('#wikiFilesStatus').html('<div class="alert alert-warning">Vous devez être connecté au wiki pour utiliser cette fonctionnalité.</div>');
|
|
279
281
|
return;
|
|
280
282
|
}
|
|
281
|
-
|
|
283
|
+
|
|
282
284
|
// Get user's JSON files
|
|
283
285
|
const filesData = await this.getWikiUserFiles(userInfo.name);
|
|
284
|
-
|
|
286
|
+
|
|
285
287
|
// Parse the results
|
|
286
288
|
const results = filesData.query?.results;
|
|
287
|
-
|
|
289
|
+
|
|
288
290
|
if (!results || Object.keys(results).length === 0) {
|
|
289
291
|
$('#wikiFilesStatus').html('<div class="alert alert-info">Aucun fichier JSON trouvé dans vos pages.</div>');
|
|
290
292
|
return;
|
|
291
293
|
}
|
|
292
|
-
|
|
294
|
+
|
|
293
295
|
// Display the files
|
|
294
296
|
$('#wikiFilesStatus').html(`<p class="text-muted">Connecté en tant que <strong>${userInfo.name}</strong></p>`);
|
|
295
|
-
|
|
297
|
+
|
|
296
298
|
const filesList = $('#wikiFilesList');
|
|
297
|
-
|
|
299
|
+
|
|
298
300
|
for (const [pageTitle, pageData] of Object.entries(results)) {
|
|
299
301
|
const displayTitle = pageData.displaytitle || pageTitle;
|
|
300
302
|
const fullUrl = pageData.fullurl || '';
|
|
301
|
-
|
|
303
|
+
|
|
302
304
|
// if displayTitle is a subpage (contains /), split in two spans:
|
|
303
305
|
let [parent, child] = displayTitle.includes('/') ? displayTitle.split('/') : [displayTitle];
|
|
304
306
|
|
|
@@ -321,10 +323,10 @@ class WikiLoader extends DefaultLoader {
|
|
|
321
323
|
</div>
|
|
322
324
|
</a>
|
|
323
325
|
`);
|
|
324
|
-
|
|
326
|
+
|
|
325
327
|
filesList.append(listItem);
|
|
326
328
|
}
|
|
327
|
-
|
|
329
|
+
|
|
328
330
|
} catch (error) {
|
|
329
331
|
$('#wikiFilesStatus').html(`<div class="alert alert-danger">Erreur lors du chargement des fichiers: ${error.message}</div>`);
|
|
330
332
|
}
|
|
@@ -335,11 +337,11 @@ class WikiLoader extends DefaultLoader {
|
|
|
335
337
|
*/
|
|
336
338
|
async showSaveAsModal() {
|
|
337
339
|
const self = this;
|
|
338
|
-
|
|
340
|
+
|
|
339
341
|
try {
|
|
340
342
|
// Get user info to check if logged in
|
|
341
343
|
const userInfo = await this.getWikiUserInfo();
|
|
342
|
-
|
|
344
|
+
|
|
343
345
|
if (!userInfo.id || userInfo.id === 0) {
|
|
344
346
|
alert('Vous devez être connecté au wiki pour utiliser cette fonctionnalité.');
|
|
345
347
|
return;
|
|
@@ -347,23 +349,23 @@ class WikiLoader extends DefaultLoader {
|
|
|
347
349
|
|
|
348
350
|
// Get user's existing pages
|
|
349
351
|
const pagesData = await this.getWikiUserPages(userInfo.name);
|
|
350
|
-
|
|
352
|
+
|
|
351
353
|
// Show the modal
|
|
352
354
|
const modal = new bootstrap.Modal(document.getElementById('saveAsModal'));
|
|
353
355
|
modal.show();
|
|
354
|
-
|
|
356
|
+
|
|
355
357
|
// Populate the select with user's pages
|
|
356
358
|
const pageSelect = $('#saveAsPageSelect');
|
|
357
359
|
pageSelect.empty();
|
|
358
360
|
pageSelect.append('<option value="">Sélectionner une page...</option>');
|
|
359
|
-
|
|
361
|
+
|
|
360
362
|
if (pagesData.query?.results) {
|
|
361
363
|
for (const [pageTitle, pageData] of Object.entries(pagesData.query.results)) {
|
|
362
364
|
const displayTitle = pageData.displaytitle || pageTitle;
|
|
363
365
|
pageSelect.append(`<option value="${pageTitle}">${displayTitle}</option>`);
|
|
364
366
|
}
|
|
365
367
|
}
|
|
366
|
-
|
|
368
|
+
|
|
367
369
|
// Set default filename from title if it's been changed
|
|
368
370
|
const filenameInput = $('#saveAsFilename');
|
|
369
371
|
const currentTitle = self.tikaeditorInstance.system.title || '';
|
|
@@ -372,7 +374,7 @@ class WikiLoader extends DefaultLoader {
|
|
|
372
374
|
} else {
|
|
373
375
|
filenameInput.val('');
|
|
374
376
|
}
|
|
375
|
-
|
|
377
|
+
|
|
376
378
|
} catch (error) {
|
|
377
379
|
console.error("Error showing save as modal:", error);
|
|
378
380
|
alert('Erreur lors du chargement des données utilisateur.');
|
|
@@ -387,20 +389,20 @@ class WikiLoader extends DefaultLoader {
|
|
|
387
389
|
const useExistingPage = $('#saveAsUseExistingPage').prop('checked');
|
|
388
390
|
const selectedPage = $('#saveAsPageSelect').val();
|
|
389
391
|
const filename = $('#saveAsFilename').val().trim();
|
|
390
|
-
|
|
392
|
+
|
|
391
393
|
if (!filename) {
|
|
392
394
|
alert('Veuillez saisir un nom de fichier.');
|
|
393
395
|
return null;
|
|
394
396
|
}
|
|
395
|
-
|
|
397
|
+
|
|
396
398
|
let subpageName;
|
|
397
|
-
|
|
399
|
+
|
|
398
400
|
if (useExistingPage && selectedPage) {
|
|
399
401
|
subpageName = selectedPage;
|
|
400
402
|
} else {
|
|
401
403
|
subpageName = 'Itinéraires techniques non classés';
|
|
402
404
|
}
|
|
403
|
-
|
|
405
|
+
|
|
404
406
|
// Build the final URL: subpagename/filename.json
|
|
405
407
|
const finalUrl = `${subpageName}/${filename}.json`;
|
|
406
408
|
|
|
@@ -416,11 +418,11 @@ class WikiLoader extends DefaultLoader {
|
|
|
416
418
|
*/
|
|
417
419
|
async saveAs() {
|
|
418
420
|
const url = this.buildSaveAsUrl();
|
|
419
|
-
|
|
421
|
+
|
|
420
422
|
if (!url) {
|
|
421
423
|
return; // Error already handled in buildSaveAsUrl
|
|
422
424
|
}
|
|
423
|
-
|
|
425
|
+
|
|
424
426
|
const self = this;
|
|
425
427
|
const oldPageTitle = self.pageTitle;
|
|
426
428
|
|
|
@@ -430,17 +432,18 @@ class WikiLoader extends DefaultLoader {
|
|
|
430
432
|
|
|
431
433
|
// Save to the wiki first
|
|
432
434
|
await self.savePageToWiki(self.pageTitle, JSON.stringify(self.tikaeditorInstance.system, null, 2));
|
|
433
|
-
|
|
435
|
+
self.markClean();
|
|
436
|
+
|
|
434
437
|
// Close the modal
|
|
435
438
|
const modal = bootstrap.Modal.getInstance(document.getElementById('saveAsModal'));
|
|
436
439
|
modal.hide();
|
|
437
|
-
|
|
440
|
+
|
|
438
441
|
alert(`Itinéraire technique enregistré avec succès sous "${url}"`);
|
|
439
|
-
|
|
442
|
+
|
|
440
443
|
// Only navigate after successful save
|
|
441
444
|
const newEditorUrl = `editor.html?wiki=${encodeURIComponent(self.pageTitle)}`;
|
|
442
445
|
window.location.href = newEditorUrl;
|
|
443
|
-
|
|
446
|
+
|
|
444
447
|
} catch (error) {
|
|
445
448
|
// Restore the old page title if save failed
|
|
446
449
|
self.pageTitle = oldPageTitle;
|
package/js/editor-main.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
class TikaEditor {
|
|
6
6
|
constructor() {
|
|
7
7
|
this.DEFAULT_TITLE = "Nouvel itinéraire technique";
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
// Initialize the crops data structure
|
|
10
10
|
this.system = {
|
|
11
11
|
"title": this.DEFAULT_TITLE,
|
|
@@ -48,7 +48,6 @@ class TikaEditor {
|
|
|
48
48
|
this.enableTitleEditing();
|
|
49
49
|
this.setupCloseStepButtons();
|
|
50
50
|
this.setupCropFormKeydown();
|
|
51
|
-
this.setupCropsSortable();
|
|
52
51
|
this.setupParamsModal();
|
|
53
52
|
}
|
|
54
53
|
|
|
@@ -101,7 +100,7 @@ class TikaEditor {
|
|
|
101
100
|
let movedStep = new StepModel(step);
|
|
102
101
|
let duration = movedStep.getDurationInDays();
|
|
103
102
|
|
|
104
|
-
if (lastStepEnd != null) {
|
|
103
|
+
if (lastStepEnd != null) {
|
|
105
104
|
lastStepEnd.setDate(lastStepEnd.getDate() + 1);
|
|
106
105
|
|
|
107
106
|
movedStep.setStartDate(lastStepEnd);
|
|
@@ -135,7 +134,7 @@ class TikaEditor {
|
|
|
135
134
|
*/
|
|
136
135
|
setupParamsModal() {
|
|
137
136
|
const self = this;
|
|
138
|
-
|
|
137
|
+
|
|
139
138
|
$('#modalParams').on('show.bs.modal', function (event) {
|
|
140
139
|
// Set the modal form inputs values from crops.options
|
|
141
140
|
$("#viewSelect").val(self.system.options.view);
|
|
@@ -147,16 +146,16 @@ class TikaEditor {
|
|
|
147
146
|
$("#addressInput").val(self.system.options.address ?? "");
|
|
148
147
|
$("#latitudeInput").val(self.system.options.latitude ?? "");
|
|
149
148
|
$("#longitudeInput").val(self.system.options.longitude ?? "");
|
|
150
|
-
|
|
149
|
+
|
|
151
150
|
// Load ombrothermic data from climate_data if present
|
|
152
|
-
let hasClimateData = self.system.options.climate_data &&
|
|
153
|
-
self.system.options.climate_data.temperatures &&
|
|
151
|
+
let hasClimateData = self.system.options.climate_data &&
|
|
152
|
+
self.system.options.climate_data.temperatures &&
|
|
154
153
|
self.system.options.climate_data.precipitations;
|
|
155
|
-
|
|
154
|
+
|
|
156
155
|
// Check the checkbox if show_climate_diagram is explicitly true OR if climate_data exists
|
|
157
156
|
let showDiagram = self.system.options.show_climate_diagram === true;
|
|
158
157
|
$("#ombroCheck").prop("checked", showDiagram);
|
|
159
|
-
|
|
158
|
+
|
|
160
159
|
if (hasClimateData) {
|
|
161
160
|
let tempLine = self.system.options.climate_data.temperatures.join(' ');
|
|
162
161
|
let precipLine = self.system.options.climate_data.precipitations.join(' ');
|
|
@@ -164,21 +163,21 @@ class TikaEditor {
|
|
|
164
163
|
} else {
|
|
165
164
|
$("#ombroData").val("");
|
|
166
165
|
}
|
|
167
|
-
|
|
166
|
+
|
|
168
167
|
// Enable/disable textarea based on checkbox state
|
|
169
168
|
$("#ombroData").prop("disabled", !showDiagram);
|
|
170
169
|
});
|
|
171
|
-
|
|
170
|
+
|
|
172
171
|
// Add event listener to toggle textarea when checkbox changes
|
|
173
172
|
$("#ombroCheck").on("change", function() {
|
|
174
173
|
$("#ombroData").prop("disabled", !this.checked);
|
|
175
174
|
});
|
|
176
|
-
|
|
175
|
+
|
|
177
176
|
// Update Google Maps link when coordinates change
|
|
178
177
|
function updateGoogleMapsLink() {
|
|
179
178
|
const lat = $("#latitudeInput").val().trim();
|
|
180
179
|
const lon = $("#longitudeInput").val().trim();
|
|
181
|
-
|
|
180
|
+
|
|
182
181
|
if (lat && lon && !isNaN(parseFloat(lat)) && !isNaN(parseFloat(lon))) {
|
|
183
182
|
const mapsUrl = `https://www.google.com/maps?q=${lat},${lon}`;
|
|
184
183
|
$("#googleMapsLink a").attr("href", mapsUrl);
|
|
@@ -187,28 +186,28 @@ class TikaEditor {
|
|
|
187
186
|
$("#googleMapsLink").hide();
|
|
188
187
|
}
|
|
189
188
|
}
|
|
190
|
-
|
|
189
|
+
|
|
191
190
|
// Attach listeners to lat/long inputs
|
|
192
191
|
$("#latitudeInput, #longitudeInput").on("input change", updateGoogleMapsLink);
|
|
193
|
-
|
|
192
|
+
|
|
194
193
|
// Update link when modal opens
|
|
195
194
|
$('#modalParams').on('shown.bs.modal', function() {
|
|
196
195
|
updateGoogleMapsLink();
|
|
197
196
|
});
|
|
198
|
-
|
|
197
|
+
|
|
199
198
|
// Location search button handler
|
|
200
199
|
$("#searchLocationBtn").on("click", function() {
|
|
201
200
|
const address = $("#addressInput").val().trim();
|
|
202
|
-
|
|
201
|
+
|
|
203
202
|
if (!address) {
|
|
204
203
|
$("#locationSearchStatus").html('<span class="text-warning">Veuillez entrer une adresse</span>');
|
|
205
204
|
return;
|
|
206
205
|
}
|
|
207
|
-
|
|
206
|
+
|
|
208
207
|
// Show loading state
|
|
209
208
|
$("#searchLocationBtn").prop("disabled", true);
|
|
210
209
|
$("#locationSearchStatus").html('<i class="fa fa-spinner fa-spin"></i> Recherche en cours...');
|
|
211
|
-
|
|
210
|
+
|
|
212
211
|
$.ajax({
|
|
213
212
|
url: "https://itk-info.tripleperformance.fr/api/location",
|
|
214
213
|
method: "POST",
|
|
@@ -216,14 +215,14 @@ class TikaEditor {
|
|
|
216
215
|
data: JSON.stringify({ address: address }),
|
|
217
216
|
success: function(data) {
|
|
218
217
|
console.log("Location data received:", data);
|
|
219
|
-
|
|
218
|
+
|
|
220
219
|
// Populate latitude and longitude
|
|
221
220
|
if (data.latitude && data.longitude) {
|
|
222
221
|
$("#latitudeInput").val(data.latitude);
|
|
223
222
|
$("#longitudeInput").val(data.longitude);
|
|
224
223
|
updateGoogleMapsLink();
|
|
225
224
|
}
|
|
226
|
-
|
|
225
|
+
|
|
227
226
|
// Populate climate data if available
|
|
228
227
|
if (data.monthly_temperatures && data.monthly_rainfall) {
|
|
229
228
|
let tempLine = data.monthly_temperatures.join(' ');
|
|
@@ -232,7 +231,7 @@ class TikaEditor {
|
|
|
232
231
|
$("#ombroCheck").prop("checked", true);
|
|
233
232
|
$("#ombroData").prop("disabled", false);
|
|
234
233
|
}
|
|
235
|
-
|
|
234
|
+
|
|
236
235
|
// Show success message
|
|
237
236
|
let message = '<span class="text-success">✓ Coordonnées trouvées';
|
|
238
237
|
if (data.source_explanation) {
|
|
@@ -261,20 +260,20 @@ class TikaEditor {
|
|
|
261
260
|
self.system.options.address = $("#addressInput").val().trim();
|
|
262
261
|
self.system.options.latitude = $("#latitudeInput").val().trim();
|
|
263
262
|
self.system.options.longitude = $("#longitudeInput").val().trim();
|
|
264
|
-
|
|
263
|
+
|
|
265
264
|
// Convert ombrothermic data to climate_data object
|
|
266
265
|
let ombroEnabled = $("#ombroCheck").prop("checked");
|
|
267
|
-
|
|
266
|
+
|
|
268
267
|
if (ombroEnabled) {
|
|
269
268
|
self.system.options.show_climate_diagram = true;
|
|
270
|
-
|
|
269
|
+
|
|
271
270
|
let ombroText = $("#ombroData").val().trim();
|
|
272
271
|
let lines = ombroText.split('\n');
|
|
273
|
-
|
|
272
|
+
|
|
274
273
|
if (lines.length >= 2) {
|
|
275
274
|
let temperatures = lines[0].trim().split(/\s+/).map(v => parseFloat(v)).filter(v => !isNaN(v));
|
|
276
275
|
let precipitations = lines[1].trim().split(/\s+/).map(v => parseFloat(v)).filter(v => !isNaN(v));
|
|
277
|
-
|
|
276
|
+
|
|
278
277
|
if (temperatures.length > 0 && precipitations.length > 0) {
|
|
279
278
|
self.system.options.climate_data = {
|
|
280
279
|
temperatures: temperatures,
|
|
@@ -312,18 +311,18 @@ class TikaEditor {
|
|
|
312
311
|
const self = this;
|
|
313
312
|
$(document).ready(function() {
|
|
314
313
|
// If we are in a wiki (the domain contains "tripleperformance.ag or tripleperformance.fr" then show the Wiki buttons
|
|
315
|
-
if (window.location.hostname.includes("
|
|
314
|
+
if ((window.location.hostname.includes("itinera") || window.location.hostname.includes("localhost"))
|
|
315
|
+
&& window.location.search.includes("itinera") ) {
|
|
316
316
|
|
|
317
|
-
//
|
|
318
|
-
self.editorLoader = new
|
|
317
|
+
// If we are in Itinera - the domain is *.itinera.ag or localhost with a itinera param
|
|
318
|
+
self.editorLoader = new ItineraLoader(self);
|
|
319
319
|
self.editorLoader.setupButtons();
|
|
320
320
|
self.editorLoader.loadPageFromURL();
|
|
321
321
|
|
|
322
|
-
} else if (window.location.hostname.includes("
|
|
323
|
-
(window.location.hostname.includes("localhost") && window.location.search.includes("itinera")) ) {
|
|
322
|
+
} else if (window.location.hostname.includes("tripleperformance.ag") || window.location.hostname.includes("tripleperformance.fr")) {
|
|
324
323
|
|
|
325
|
-
//
|
|
326
|
-
self.editorLoader = new
|
|
324
|
+
// Hide NonWikiButtons
|
|
325
|
+
self.editorLoader = new WikiLoader(self);
|
|
327
326
|
self.editorLoader.setupButtons();
|
|
328
327
|
self.editorLoader.loadPageFromURL();
|
|
329
328
|
|
|
@@ -367,6 +366,7 @@ class TikaEditor {
|
|
|
367
366
|
*/
|
|
368
367
|
updateSelectedStep() {
|
|
369
368
|
this.selectedStep.updateFromForm();
|
|
369
|
+
this.sortStepsByStartDate();
|
|
370
370
|
this.refreshAllTables();
|
|
371
371
|
}
|
|
372
372
|
|
|
@@ -377,10 +377,35 @@ class TikaEditor {
|
|
|
377
377
|
document.getElementById(elementId).value = value;
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
+
/**
|
|
381
|
+
* Briefly highlight a step row to signal it has been moved
|
|
382
|
+
*/
|
|
383
|
+
highlightStepRow(id) {
|
|
384
|
+
let row = $('#cropsContainer .step-row[data-id="' + id + '"]');
|
|
385
|
+
if (!row.length) return;
|
|
386
|
+
row.removeClass('step-shifted');
|
|
387
|
+
// Force reflow so the animation restarts on repeated clicks
|
|
388
|
+
void row[0].offsetWidth;
|
|
389
|
+
row.addClass('step-shifted');
|
|
390
|
+
row.one('animationend', function () {
|
|
391
|
+
$(this).removeClass('step-shifted');
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Sort steps array by start date (ascending)
|
|
397
|
+
*/
|
|
398
|
+
sortStepsByStartDate() {
|
|
399
|
+
this.system.steps.sort(function (a, b) {
|
|
400
|
+
return new Date(a.startDate) - new Date(b.startDate);
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
380
404
|
/**
|
|
381
405
|
* Refresh all tables and views
|
|
382
406
|
*/
|
|
383
407
|
refreshAllTables() {
|
|
408
|
+
this.sortStepsByStartDate();
|
|
384
409
|
this.refreshStepsButtonList();
|
|
385
410
|
this.renderChart();
|
|
386
411
|
}
|
|
@@ -400,7 +425,7 @@ class TikaEditor {
|
|
|
400
425
|
hideStepEditor() {
|
|
401
426
|
$('#cropListView').show();
|
|
402
427
|
$('#welcomeView').hide();
|
|
403
|
-
$('#cropEditorView').hide();
|
|
428
|
+
$('#cropEditorView').hide();
|
|
404
429
|
}
|
|
405
430
|
|
|
406
431
|
/**
|
|
@@ -414,7 +439,7 @@ class TikaEditor {
|
|
|
414
439
|
$('.step-edit').click(function (event) {
|
|
415
440
|
event.stopPropagation();
|
|
416
441
|
let stepId = renderer.getElementID(event.target.closest('.rotation_item'));
|
|
417
|
-
|
|
442
|
+
|
|
418
443
|
let index = stepId.split('_')[1];
|
|
419
444
|
self.selectedStep = new StepModel(self.system.steps[index]);
|
|
420
445
|
self.selectStep(self.selectedStep);
|
|
@@ -487,12 +512,12 @@ class TikaEditor {
|
|
|
487
512
|
}),
|
|
488
513
|
success: function(data) {
|
|
489
514
|
console.log("Réponse:", data);
|
|
490
|
-
|
|
515
|
+
|
|
491
516
|
if (data.color_hex && self.selectedStep.step.useDefaultColor) {
|
|
492
517
|
self.setInputValue("cropColor", data.color_hex);
|
|
493
518
|
self.updateSelectedStep();
|
|
494
519
|
}
|
|
495
|
-
|
|
520
|
+
|
|
496
521
|
let startDate = self.getRotationEndDate();
|
|
497
522
|
if (data.average_sowing_date && self.selectedStep.step.useDefaultStartDate) {
|
|
498
523
|
// data.average_sowing_date is in MM-DD format, we need to convert it to YYYY-MM-DD
|
|
@@ -520,7 +545,7 @@ class TikaEditor {
|
|
|
520
545
|
self.setInputValue("cropEndDate", endDate.toISOString().split('T')[0]);
|
|
521
546
|
self.updateSelectedStep();
|
|
522
547
|
}
|
|
523
|
-
|
|
548
|
+
|
|
524
549
|
if (data.source_explanation) {
|
|
525
550
|
$('#itk-api-comment').text(data.source_explanation);
|
|
526
551
|
} else {
|
|
@@ -530,7 +555,7 @@ class TikaEditor {
|
|
|
530
555
|
error: function(err) {
|
|
531
556
|
console.error("Erreur:", err);
|
|
532
557
|
}
|
|
533
|
-
});
|
|
558
|
+
});
|
|
534
559
|
}
|
|
535
560
|
|
|
536
561
|
/**
|
|
@@ -572,7 +597,7 @@ class TikaEditor {
|
|
|
572
597
|
|
|
573
598
|
this.InterventionTableManager.setupDiv();
|
|
574
599
|
this.InterventionTableManager.refreshInterventionsTable(this.selectedStep);
|
|
575
|
-
|
|
600
|
+
|
|
576
601
|
this.refreshAllTables();
|
|
577
602
|
}
|
|
578
603
|
|
|
@@ -614,17 +639,29 @@ class TikaEditor {
|
|
|
614
639
|
const rowDiv = this.createCropRow(crop);
|
|
615
640
|
cropsContainer.append(rowDiv);
|
|
616
641
|
});
|
|
617
|
-
|
|
618
|
-
cropsContainer.sortable("refresh");
|
|
619
642
|
}
|
|
620
643
|
|
|
621
|
-
addEditAndRemoveButtons(rowDiv, deleteId, editFunction, deleteFunction, duplicateFunction, style="btn-group") {
|
|
644
|
+
addEditAndRemoveButtons(rowDiv, deleteId, editFunction, deleteFunction, duplicateFunction, shiftYearUpFunction, shiftYearDownFunction, style="btn-group") {
|
|
622
645
|
rowDiv = $(rowDiv);
|
|
623
646
|
|
|
624
647
|
let actionContainer = $(`<div class="col-auto edit-buttons m-1 ${style}" role="group"></div>`);
|
|
625
648
|
|
|
626
649
|
rowDiv.append(actionContainer);
|
|
627
650
|
|
|
651
|
+
if (shiftYearUpFunction != null) {
|
|
652
|
+
actionContainer.append($('<button class="btn btn-outline-secondary p-2" title="Reculer d\'un an"><i class="fa fa-arrow-up"></i></button>').click(function (event) {
|
|
653
|
+
event.stopPropagation();
|
|
654
|
+
shiftYearUpFunction(deleteId);
|
|
655
|
+
}));
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (shiftYearDownFunction != null) {
|
|
659
|
+
actionContainer.append($('<button class="btn btn-outline-secondary p-2" title="Avancer d\'un an"><i class="fa fa-arrow-down"></i></button>').click(function (event) {
|
|
660
|
+
event.stopPropagation();
|
|
661
|
+
shiftYearDownFunction(deleteId);
|
|
662
|
+
}));
|
|
663
|
+
}
|
|
664
|
+
|
|
628
665
|
actionContainer.append($('<button class="edit-button btn btn-outline-primary p-2"><i class="fa fa-pencil"></i></button>').click(function(event) {
|
|
629
666
|
event.stopPropagation();
|
|
630
667
|
editFunction();
|
|
@@ -657,12 +694,17 @@ class TikaEditor {
|
|
|
657
694
|
|
|
658
695
|
let rowDiv = $('<div class="row mb-2 step-row editable-row position-relative" data-id="'+step.getStep().id +'"></div>');
|
|
659
696
|
|
|
697
|
+
let startDate = new Date(step.getStep().startDate);
|
|
698
|
+
let startDateStr = startDate.toLocaleDateString('fr-FR', { month: 'short', year: 'numeric' });
|
|
699
|
+
startDateStr = startDateStr.charAt(0).toUpperCase() + startDateStr.slice(1);
|
|
700
|
+
|
|
660
701
|
rowDiv.append($('<div class="col"></div>')
|
|
661
|
-
.append($('<
|
|
662
|
-
.append($('<
|
|
702
|
+
.append($('<strong>' + step.getStep().name + '</strong>'))
|
|
703
|
+
.append($('<br>'))
|
|
704
|
+
.append($('<small class="text-muted">' + startDateStr + '</small>')));
|
|
663
705
|
|
|
664
|
-
this.addEditAndRemoveButtons(rowDiv,
|
|
665
|
-
step.getStep().id,
|
|
706
|
+
this.addEditAndRemoveButtons(rowDiv,
|
|
707
|
+
step.getStep().id,
|
|
666
708
|
function () {
|
|
667
709
|
console.log("Selected step:", step.getStep().name);
|
|
668
710
|
self.selectStep(step);
|
|
@@ -674,6 +716,30 @@ class TikaEditor {
|
|
|
674
716
|
},
|
|
675
717
|
function(id) {
|
|
676
718
|
self.duplicateStep(id);
|
|
719
|
+
},
|
|
720
|
+
function(id) {
|
|
721
|
+
let s = self.system.steps.find(function (c) { return c.id == id; });
|
|
722
|
+
if (!s) return;
|
|
723
|
+
let start = new Date(s.startDate);
|
|
724
|
+
let end = new Date(s.endDate);
|
|
725
|
+
start.setFullYear(start.getFullYear() - 1);
|
|
726
|
+
end.setFullYear(end.getFullYear() - 1);
|
|
727
|
+
s.startDate = start;
|
|
728
|
+
s.endDate = end;
|
|
729
|
+
self.refreshAllTables();
|
|
730
|
+
self.highlightStepRow(id);
|
|
731
|
+
},
|
|
732
|
+
function(id) {
|
|
733
|
+
let s = self.system.steps.find(function (c) { return c.id == id; });
|
|
734
|
+
if (!s) return;
|
|
735
|
+
let start = new Date(s.startDate);
|
|
736
|
+
let end = new Date(s.endDate);
|
|
737
|
+
start.setFullYear(start.getFullYear() + 1);
|
|
738
|
+
end.setFullYear(end.getFullYear() + 1);
|
|
739
|
+
s.startDate = start;
|
|
740
|
+
s.endDate = end;
|
|
741
|
+
self.refreshAllTables();
|
|
742
|
+
self.highlightStepRow(id);
|
|
677
743
|
});
|
|
678
744
|
|
|
679
745
|
rowDiv.click();
|
|
@@ -730,7 +796,7 @@ class TikaEditor {
|
|
|
730
796
|
|
|
731
797
|
// Create a StepModel instance to ensure proper initialization
|
|
732
798
|
let stepModel = new StepModel(newStep);
|
|
733
|
-
|
|
799
|
+
|
|
734
800
|
// Add the duplicated step to the rotation
|
|
735
801
|
this.system.steps.push(stepModel.getStep());
|
|
736
802
|
|
package/package.json
CHANGED
package/scss/styles-editor.scss
CHANGED
|
@@ -66,6 +66,12 @@ $header-height : 3rem;
|
|
|
66
66
|
margin : auto
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
@keyframes step-highlight {
|
|
70
|
+
0% { box-shadow: 0 0 0 3px #ffc107; }
|
|
71
|
+
80% { box-shadow: 0 0 0 3px #ffc107; }
|
|
72
|
+
100% { box-shadow: 0 0 0 0px transparent; }
|
|
73
|
+
}
|
|
74
|
+
|
|
69
75
|
.editable-row {
|
|
70
76
|
background-color: #f7f7f7;
|
|
71
77
|
min-height : 3rem;
|
|
@@ -82,6 +88,14 @@ $header-height : 3rem;
|
|
|
82
88
|
&:hover>.edit-buttons {
|
|
83
89
|
visibility: visible;
|
|
84
90
|
}
|
|
91
|
+
|
|
92
|
+
&.step-shifted {
|
|
93
|
+
animation: step-highlight 1.4s ease-out forwards;
|
|
94
|
+
|
|
95
|
+
.edit-buttons {
|
|
96
|
+
visibility: visible;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
85
99
|
}
|
|
86
100
|
|
|
87
101
|
.intervention-row {
|