@igea/oac_frontend 1.0.95 → 1.0.96

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@igea/oac_frontend",
3
- "version": "1.0.95",
3
+ "version": "1.0.96",
4
4
  "description": "Frontend service for the OAC project",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -12,7 +12,8 @@ module.exports = function(serviceName) {
12
12
  root: serviceName,
13
13
  title: 'Investigation Creation',
14
14
  currentPath: req.baseUrl +req.path,
15
- uuid
15
+ uuid,
16
+ mode: 'edit'
16
17
  });
17
18
  res.render('investigation/form.twig', data.toJson());
18
19
  }
@@ -25,6 +26,23 @@ module.exports = function(serviceName) {
25
26
  renderForm(req, res, req.params.uuid);
26
27
  });
27
28
 
29
+ router.get('/view/:uuid', (req, res) => {
30
+ const { uuid } = req.params;
31
+
32
+ let data = new DataModel(req, {
33
+ root: serviceName,
34
+ title: 'Indagine viewer',
35
+ uuid,
36
+
37
+ // 👇 NASCONDE MENU E SIDEBAR
38
+ activeMenu: null,
39
+ activeSidebar: null,
40
+ activeSidebarItem: null
41
+ });
42
+
43
+ res.render('investigation/view.twig', data.toJson());
44
+ });
45
+
28
46
  return router
29
47
  }
30
48
 
package/src/index.js CHANGED
@@ -253,6 +253,7 @@ app.get('/frontend/v2/investigation/form', async (req, res) => {
253
253
  title: 'Indagine',
254
254
  activeMenu: 'investigation',
255
255
  activeSidebar: 'investigation',
256
+ activeSidebarItem: 'investigation',
256
257
  showForm: true
257
258
  });
258
259
 
@@ -36,7 +36,11 @@ const KEEP_LOCK_EVERY = 45*1000;
36
36
  document.addEventListener('DOMContentLoaded', () => {
37
37
 
38
38
  // App separata per l'autocomplete
39
- if (typeof __AUTO_COMPLETE_COMPONENT__ !== 'undefined') {
39
+
40
+ const elSearch = document.getElementById(searchId);
41
+
42
+ if (elSearch && typeof __AUTO_COMPLETE_COMPONENT__ !== 'undefined') {
43
+ //if (typeof __AUTO_COMPLETE_COMPONENT__ !== 'undefined') {
40
44
  try {
41
45
  const elSearch = document.getElementById(searchId);
42
46
  const searchApp = createApp({
@@ -131,9 +135,9 @@ document.addEventListener('DOMContentLoaded', () => {
131
135
  } catch (error) {
132
136
  console.error('Error mounting search app:', error);
133
137
  }
134
- } else {
138
+ } /*else {
135
139
  console.error('__AUTO_COMPLETE_COMPONENT__ not defined');
136
- }
140
+ }*/
137
141
 
138
142
  const el = document.getElementById(appId);
139
143
 
@@ -178,9 +182,16 @@ document.addEventListener('DOMContentLoaded', () => {
178
182
  }
179
183
  },
180
184
  mounted() {
181
- if (showForm) {
185
+ console.log("UUID da dataset:", this.uuid);
186
+ if (showForm && this.uuid == null) {
182
187
  this.resetShaclForm(null, true);
183
188
  }
189
+ else
190
+ if (showForm && this.uuid) {
191
+ this.isVisible = true;
192
+ this.inEditing = true;
193
+ this.resetShaclForm(this.uuid, true); // ❌ uuid non definito
194
+ }
184
195
 
185
196
  this.initShaclForm();
186
197
  setInterval(this.autoSave.bind(this), 30*1000);
@@ -303,19 +314,17 @@ document.addEventListener('DOMContentLoaded', () => {
303
314
  form.remove();
304
315
  },
305
316
  resetShaclForm(uuid, edit) {
306
- // Trova il form esistente
307
317
  const oldForm = document.querySelector("shacl-form");
308
318
  const parent = document.getElementById("shacl-container");
309
-
319
+
310
320
  if (oldForm) oldForm.remove();
311
- if(!parent){
312
- setTimeout((function(){
313
- this.resetShaclForm(uuid, edit);
314
- }).bind(this), 150);
321
+ if (!parent) {
322
+ setTimeout(() => {
323
+ this.resetShaclForm(uuid, edit);
324
+ }, 150);
315
325
  return;
316
326
  }
317
327
 
318
- // Crea un nuovo shacl-form con gli stessi attributi
319
328
  const newForm = document.createElement("shacl-form");
320
329
  newForm.id = "shacl-form";
321
330
  newForm.dataset.collapse = "open";
@@ -323,27 +332,27 @@ document.addEventListener('DOMContentLoaded', () => {
323
332
  newForm.dataset.shapesUrl = "/backend/ontology/schema/editing";
324
333
  newForm.dataset.generateNodeShapeReference = "";
325
334
 
326
- //newForm.dataset.editing = edit;
327
- if(!edit){
335
+ if (!edit) {
328
336
  newForm.dataset.view = "";
329
337
  }
330
-
331
- newForm.dataset.shapeSubject="http://example.org/shapes/E7Activity01Shape";
332
-
333
- if(uuid){
334
- var rnd = (new Date()).getTime();
335
- newForm.dataset.valuesUrl="/backend/ontology/form/" + uuid + "?rnd=" + rnd;
336
- newForm.dataset.valuesSubject="http://indagine/" + uuid;
338
+
339
+ newForm.dataset.shapeSubject =
340
+ "http://example.org/shapes/E7Activity01Shape";
341
+
342
+ // QUESTO È IL PEZZO MANCANTE
343
+ if (uuid) {
344
+ const rnd = Date.now();
345
+ newForm.dataset.valuesUrl =
346
+ "/backend/ontology/form/" + uuid + "?rnd=" + rnd;
347
+ newForm.dataset.valuesSubject =
348
+ "http://indagine/" + uuid;
337
349
  }
338
350
 
339
- // Inserisci il nuovo form
340
351
  parent.appendChild(newForm);
341
352
 
342
- // Resetta il riferimento e reinizializza
343
353
  this.form = null;
344
354
  this.initShaclForm(uuid);
345
-
346
- },
355
+ },
347
356
  initShaclForm(uuid) {
348
357
  var _this = this;
349
358
  setTimeout(async ()=>{
@@ -5,113 +5,309 @@
5
5
  // reference to the sparnatural webcomponent
6
6
  const sparnatural = document.querySelector("spar-natural");
7
7
 
8
- // display on the page the endpoint URL with which sparnatural is configured
9
- //document.querySelector("#displayEndpoint").setAttribute("href", sparnatural.getAttribute("endpoint"));
10
- //document.querySelector("#displayEndpoint").textContent = sparnatural.getAttribute("endpoint");
8
+ /* =========================
9
+ VIEW MODE (radio buttons)
10
+ ========================= */
11
+
12
+ let viewMode = "direct";
13
+
14
+ document.querySelectorAll('input[name="viewMode"]').forEach(radio => {
15
+ radio.addEventListener("change", e => {
16
+ viewMode = e.target.value;
17
+ sparnatural.enablePlayBtn();
18
+ console.log("View mode:", viewMode);
19
+ });
20
+ });
21
+
22
+ /* =========================
23
+ INDAGINE RESOLVERS MAP
24
+ ========================= */
25
+
26
+ const INDAGINE_RESOLVERS = {
27
+ "crm:E39_Actor": {
28
+ property: "crm:P14_carried_out_by"
29
+ },
30
+ "crm:E42_Identifier": {
31
+ property: "crm:P48_has_preferred_identifier"
32
+ },
33
+ "crm:E55_Type": {
34
+ property: "crm:P2_has_type"
35
+ },
36
+ "base:I12_Adopted_Belief": {
37
+ property: "crm:P17_was_motivated_by"
38
+ },
39
+ "j.0:S13_Sample": {
40
+ property: "crm:P16_used_specific_object"
41
+ }
42
+ };
43
+
44
+ /* =========================
45
+ YASQE INIT
46
+ ========================= */
11
47
 
12
- // init yasQE query editor
13
48
  const yasqe = new Yasqe(document.getElementById("yasqe"), {
14
- requestConfig: {
15
- // make sure the endpoint is the same as sparnatural
16
- endpoint: sparnatural.getAttribute("endpoint"),
17
- method: "GET",
18
- header: {}
19
- },
20
- copyEndpointOnNewTab: false
49
+ requestConfig: {
50
+ endpoint: sparnatural.getAttribute("endpoint"),
51
+ method: "GET",
52
+ header: {}
53
+ },
54
+ copyEndpointOnNewTab: false
21
55
  });
22
56
 
23
- // init yasR result display
24
- // register a specific plugin that is capable of displaying clikable label + URI
25
- Yasr.registerPlugin("TableX",SparnaturalYasguiPlugins.TableX);
26
- Yasr.registerPlugin("Grid",SparnaturalYasguiPlugins.GridPlugin);
27
- delete Yasr.plugins['table'];
57
+ /* =========================
58
+ YASR INIT
59
+ ========================= */
28
60
 
61
+ Yasr.registerPlugin("TableX", SparnaturalYasguiPlugins.TableX);
62
+ Yasr.registerPlugin("Grid", SparnaturalYasguiPlugins.GridPlugin);
63
+ delete Yasr.plugins['table'];
29
64
 
30
65
  const yasr = new Yasr(document.getElementById("yasr"), {
31
- pluginOrder: ["TableX", "Grid", "response"],
32
- defaultPlugin: "TableX"
66
+ pluginOrder: ["TableX", "Grid", "response"],
67
+ defaultPlugin: "TableX"
33
68
  });
34
69
 
35
- yasr.plugins["TableX"].config.uriHrefAdapter = function(uri) {
36
- if(uri.startsWith("http://diagnostica/") || uri.startsWith("http://indagine/")) {
37
- return "/frontend/v2/rdf/view?iri=" + encodeURIComponent("<" + uri + ">");
38
- } else {
39
- return uri;
40
- }
70
+ yasr.plugins["TableX"].config.uriHrefAdapter = function (uri) {
71
+
72
+ // modalità "indagine": link diretto al form SHACL
73
+ if (viewMode === "indagine" && uri.startsWith("http://indagine/")) {
74
+ const id = uri.split("/").pop();
75
+ return `/frontend/investigation/view/${id}`;
76
+ }
77
+
78
+ // modalità "direct": comportamento attuale
79
+ if (viewMode === "direct" && uri.startsWith("http://indagine/")) {
80
+ return "/frontend/v2/rdf/view?iri=" + encodeURIComponent("<" + uri + ">");
81
+ }
82
+
83
+ // fallback
84
+ return uri;
41
85
  };
42
86
 
43
- // link yasqe and yasr
44
- yasqe.on("queryResponse", function(_yasqe, response, duration) {
45
- yasr.setResponse(response, duration);
46
- // when response is received, enable the button
47
- sparnatural.enablePlayBtn();
87
+
88
+ /* =========================
89
+ QUERY RESPONSE HANDLER
90
+ ========================= */
91
+
92
+ yasqe.on("queryResponse", function (_yasqe, response, duration) {
93
+ yasr.setResponse(response, duration);
94
+
95
+ if (viewMode === "indagine") {
96
+ resolvedIndagini.clear();
97
+ const sparqlResponse = normalizeSparqlResponse(response);
98
+ if (sparqlResponse) {
99
+ resolveIndagini(sparqlResponse);
100
+ }
101
+ }
102
+
103
+
104
+ sparnatural.enablePlayBtn();
48
105
  });
49
106
 
107
+ /* =========================
108
+ RESOLVER PIPELINE
109
+ ========================= */
110
+
111
+ function resolveIndagini(response) {
112
+ const uris = extractUrisFromResponse(response);
113
+
114
+ console.log("URIs trovate:", uris);
115
+
116
+ uris.forEach(uri => {
117
+ resolveIndagineFromUri(uri);
118
+ });
119
+ }
120
+
121
+ function resolveIndagineFromUri(uri) {
122
+ const typeQuery = `
123
+ PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
124
+
125
+ SELECT DISTINCT ?type
126
+ WHERE {
127
+ <${uri}> rdf:type ?type .
128
+ }
129
+ LIMIT 10
130
+ `;
131
+
132
+ fetchSparql(typeQuery).then(data => {
133
+ const types = data.results.bindings
134
+ .map(b => normalizeRdfType(b.type.value))
135
+ .filter(Boolean);
136
+
137
+ console.log("Tipi normalizzati:", types);
138
+
139
+ types.forEach(t => {
140
+ const resolver = INDAGINE_RESOLVERS[t];
141
+ if (!resolver) return;
142
+
143
+ const q = buildResolverQuery(uri, resolver.property);
144
+ fetchIndagini(q);
145
+ });
146
+
147
+ });
148
+ }
149
+
150
+ let resolvedIndagini = new Set();
151
+
152
+ function fetchIndagini(query) {
153
+ fetchSparql(query).then(data => {
154
+ const bindings = data.results?.bindings || [];
155
+
156
+ bindings.forEach(b => {
157
+ if (b.indagine?.type === "uri") {
158
+ resolvedIndagini.add(b.indagine.value);
159
+ }
160
+ });
161
+
162
+ // quando finiamo di risolvere, aggiorniamo la UI
163
+ renderIndaginiResults();
164
+ });
165
+ }
166
+
167
+ function renderIndaginiResults() {
168
+ const bindings = Array.from(resolvedIndagini).map(uri => ({
169
+ indagine: {
170
+ type: "uri",
171
+ value: uri
172
+ }
173
+ }));
174
+
175
+ const fakeResponse = {
176
+ head: {
177
+ vars: ["indagine"]
178
+ },
179
+ results: {
180
+ bindings
181
+ }
182
+ };
183
+
184
+ console.log("SPARQL response (indagini):", fakeResponse);
185
+
186
+ yasr.setResponse(fakeResponse);
187
+ }
188
+
189
+
190
+ /* =========================
191
+ SPARQL HELPERS
192
+ ========================= */
193
+
194
+ function fetchSparql(query) {
195
+ const url =
196
+ sparnatural.getAttribute("endpoint") +
197
+ "?query=" + encodeURIComponent(query);
198
+
199
+ return fetch(url).then(r => r.json());
200
+ }
201
+
202
+ function extractUrisFromResponse(response) {
203
+ const vars = response.head.vars;
204
+ const results = response.results.bindings;
205
+ const uris = [];
206
+
207
+ results.forEach(row => {
208
+ vars.forEach(v => {
209
+ if (row[v]?.type === "uri") {
210
+ uris.push(row[v].value);
211
+ }
212
+ });
213
+ });
214
+
215
+ return [...new Set(uris)];
216
+ }
217
+
218
+
219
+ function normalizeSparqlResponse(response) {
220
+ if (response?.head && response?.results) {
221
+ return response; // già ok
222
+ }
223
+
224
+ if (typeof response?.text === "string") {
225
+ try {
226
+ return JSON.parse(response.text);
227
+ } catch (e) {
228
+ console.error("Errore parsing SPARQL JSON", response.text);
229
+ }
230
+ }
231
+
232
+ console.warn("Formato response SPARQL non riconosciuto", response);
233
+ return null;
234
+ }
235
+
236
+ function normalizeRdfType(uri) {
237
+ if (!uri) return null;
238
+
239
+ // CIDOC CRM
240
+ if (uri.startsWith("http://www.cidoc-crm.org/cidoc-crm/")) {
241
+ return "crm:" + uri.split("/").pop();
242
+ }
243
+
244
+ // CRMsci / j.0
245
+ if (uri.startsWith("http://www.cidoc-crm.org/extensions/crmsci/")) {
246
+ return "j.0:" + uri.split("/").pop();
247
+ }
248
+
249
+ // base CPM / CRMinf (adatta se necessario)
250
+ if (uri.startsWith("http://ontome.net/ns/cpm/")) {
251
+ return "base:" + uri.split("/").pop();
252
+ }
253
+
254
+ // fallback: ultimo frammento
255
+ return uri.split("/").pop();
256
+ }
257
+
258
+ /* =========================
259
+ SPARQL QUERY BUILDER
260
+ ========================= */
261
+
262
+ function buildResolverQuery(uri, property) {
263
+ return `
264
+ PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
265
+ PREFIX crm: <http://www.cidoc-crm.org/cidoc-crm/>
266
+
267
+ SELECT DISTINCT ?indagine
268
+ WHERE {
269
+ ?indagine rdf:type crm:E7_Activity ;
270
+ ${property} <${uri}> .
271
+ }
272
+ `;
273
+ }
274
+
275
+ /* =========================
276
+ SPARNATURAL EVENTS
277
+ ========================= */
50
278
 
51
279
  sparnatural.addEventListener("init", (event) => {
52
- // notify the specification to yasr plugins
53
- for (const plugin in yasr.plugins) {
54
- if (yasr.plugins[plugin].notifyConfiguration) {
55
- yasr.plugins[plugin].notifyConfiguration(
56
- event.detail.config
57
- );
58
- }
59
- }
280
+ for (const plugin in yasr.plugins) {
281
+ if (yasr.plugins[plugin].notifyConfiguration) {
282
+ yasr.plugins[plugin].notifyConfiguration(event.detail.config);
283
+ }
284
+ }
60
285
  });
61
286
 
62
- // listener when sparnatural updates the query
63
- // see http://docs.sparnatural.eu/Javascript-integration.html#sparnatural-events
64
287
  sparnatural.addEventListener("queryUpdated", (event) => {
65
- // get the SPARQL query string, and pass it to yasQE
66
- queryString = sparnatural.expandSparql(event.detail.queryString);
67
- yasqe.setValue(queryString);
68
- // display the JSON query on the console
69
- console.log("Sparnatural JSON query structure:");
70
- console.dir(event.detail.queryJson);
71
-
72
- // notify the query to yasr plugins
73
- for (const plugin in yasr.plugins) {
74
- if (yasr.plugins[plugin].notifyQuery) {
75
- yasr.plugins[plugin].notifyQuery(event.detail.queryJson);
76
- }
77
- }
78
- });
288
+ const queryString = sparnatural.expandSparql(event.detail.queryString);
289
+ yasqe.setValue(queryString);
79
290
 
80
- // listener when the sparnatural submit button is clicked
81
- // see http://docs.sparnatural.eu/Javascript-integration.html#sparnatural-events
82
- sparnatural.addEventListener("submit", (event) => {
83
- // enable loader on button
84
- sparnatural.disablePlayBtn() ;
85
- // trigger the query from YasQE
86
- yasqe.query();
291
+ console.log("Sparnatural JSON query:");
292
+ console.dir(event.detail.queryJson);
87
293
  });
88
294
 
89
- // listener when the sparnatural reset button is clicked
90
- // see http://docs.sparnatural.eu/Javascript-integration.html#sparnatural-events
91
- sparnatural.addEventListener("reset", (event) => {
92
- // empties the SPARQL query on yasQE
93
- yasqe.setValue("");
295
+ sparnatural.addEventListener("submit", () => {
296
+ sparnatural.disablePlayBtn();
297
+ yasqe.query();
94
298
  });
95
299
 
96
- // hide/show yasQE
97
- document.getElementById('sparql-toggle').onclick = function() {
98
- if(document.getElementById('yasqe').style.display == 'none') {
99
- document.getElementById('yasqe').style.display = 'block';
100
- yasqe.setValue(yasqe.getValue());
101
- yasqe.refresh();
102
- } else {
103
- document.getElementById('yasqe').style.display = 'none';
104
- }
105
- return false;
106
- } ;
107
-
108
- /*
109
- const treeValues = [
110
- { label: "Fruit", children: ["Apple", "Orange", "Banana"] },
111
- { label: "Vegetables", children: ["Carrot", "Lettuce"] }
112
- ];
113
-
114
- const widget = new TreeSelectWidget("myWidgetContainer", treeValues);
115
- */
300
+ sparnatural.addEventListener("reset", () => {
301
+ yasqe.setValue("");
302
+ });
116
303
 
304
+ /* =========================
305
+ TOGGLE SPARQL EDITOR
306
+ ========================= */
117
307
 
308
+ document.getElementById('sparql-toggle').onclick = function () {
309
+ const el = document.getElementById('yasqe');
310
+ el.style.display = el.style.display === 'none' ? 'block' : 'none';
311
+ yasqe.refresh();
312
+ return false;
313
+ };
@@ -100,7 +100,7 @@ body.layout-v2 {
100
100
  background-image: url('/frontend/v2/images/banner.png');
101
101
  background-repeat: no-repeat;
102
102
  background-size: contain;
103
- background-position: top center;
103
+ background-position: top left;
104
104
  background-color: #B1A891;
105
105
  }
106
106
 
@@ -607,5 +607,61 @@ body.layout-v2 {
607
607
  object-fit: contain;
608
608
  flex-shrink: 0;
609
609
  }
610
+ /* =====================================================
611
+ PARTECIPANTI – ACCORDION (SPAZIO BIANCO REALE)
612
+ ===================================================== */
613
+
614
+ .department-banner {
615
+ position: relative;
616
+ height: 90px;
617
+ padding: 20px;
618
+
619
+ display: flex;
620
+ align-items: center;
621
+
622
+ background-size: cover;
623
+ background-position: center;
624
+ border-radius: 4px;
625
+
626
+ filter: grayscale(100%);
627
+ }
628
+
629
+ .department-name {
630
+ font-size: 0.9rem;
631
+ font-weight: 500;
632
+ color: rgba(255,255,255,0.9);
633
+ }
610
634
 
611
635
 
636
+ .department-people {
637
+ font-size: 1.05rem;
638
+ font-weight: 600;
639
+
640
+ color: #ffffff; /* 🔑 bianco pieno */
641
+ letter-spacing: 0.2px;
642
+
643
+ text-shadow:
644
+ 0 1px 2px rgba(0,0,0,0.45); /* 🔑 aiuta molto */
645
+ }
646
+
647
+
648
+ .department-overlay {
649
+ position: relative;
650
+ z-index: 1;
651
+ }
652
+
653
+
654
+ .department-banner::before {
655
+ content: '';
656
+ position: absolute;
657
+ inset: 0;
658
+
659
+ background: linear-gradient(
660
+ to right,
661
+ rgba(0,0,0,0.35),
662
+ rgba(0,0,0,0.10)
663
+ );
664
+
665
+ border-radius: 4px;
666
+ z-index: 0;
667
+ }
Binary file
Binary file
Binary file
@@ -1,7 +1,7 @@
1
1
  document.addEventListener('DOMContentLoaded', () => {
2
2
 
3
3
  /* =========================
4
- * TOP MENU
4
+ * TOP MENU
5
5
  * ========================= */
6
6
  const topMenuEl = document.querySelector('#top-menu');
7
7
  if (topMenuEl) {
@@ -28,4 +28,42 @@ document.addEventListener('DOMContentLoaded', () => {
28
28
  }).mount(sidebarEl);
29
29
  }
30
30
 
31
+
32
+ /* =========================
33
+ * SIDEBAR PRESENTAZIONE
34
+ * ========================= */
35
+ const sidebarInd = document.querySelector('#sidebar-investigation');
36
+ if (sidebarInd) {
37
+ const activeItem = document.body.dataset.activeSidebarItem || '';
38
+
39
+ Vue.createApp({
40
+ data() {
41
+ return { activeItem };
42
+ }
43
+ }).mount(sidebarInd);
44
+ }
45
+ /* =========================
46
+ * PARTECIPANTI – ACCORDION
47
+ * ========================= */
48
+ const accordionHeaders = document.querySelectorAll('.accordion-trigger');
49
+ if (accordionHeaders.length) {
50
+
51
+ accordionHeaders.forEach(btn => {
52
+ btn.addEventListener('click', () => {
53
+ const item = btn.closest('.department-banner');
54
+ if (!item) return;
55
+
56
+ // Chiude gli altri accordion aperti
57
+ document.querySelectorAll('.department-banner.open')
58
+ .forEach(el => {
59
+ if (el !== item) el.classList.remove('open');
60
+ });
61
+
62
+ // Toggle dell’elemento corrente
63
+ item.classList.toggle('open');
64
+ });
65
+ });
66
+
67
+ }
68
+
31
69
  });
@@ -0,0 +1,155 @@
1
+ {% extends "/v2/layout.twig" %}
2
+
3
+ {# --------------------------------------------------
4
+ Forziamo modalità viewer:
5
+ - niente sidebar
6
+ - header minimale
7
+ -------------------------------------------------- #}
8
+ {% set activeMenu = null %}
9
+ {% set activeSidebar = null %}
10
+ {% set activeSidebarItem = null %}
11
+
12
+ {% block extendHead %}
13
+
14
+ {# SHACL FORM web component #}
15
+ <script src="/frontend/js/lib/shacl-form.bundle.js" type="module"></script>
16
+
17
+ {# Vue investigation (stessa usata per editing) #}
18
+ <script src="/frontend/js/app/vue-investigation.js"></script>
19
+
20
+ <style>
21
+ .viewer-container {
22
+ max-width: 1100px;
23
+ margin: 20px auto;
24
+ }
25
+
26
+ .viewer-header {
27
+ display: flex;
28
+ justify-content: space-between;
29
+ align-items: center;
30
+ margin-bottom: 10px;
31
+ }
32
+
33
+ .viewer-header h2 {
34
+ margin: 0;
35
+ }
36
+
37
+ .viewer-actions {
38
+ text-align: center;
39
+ margin-top: 20px;
40
+ }
41
+ </style>
42
+
43
+ {% endblock %}
44
+
45
+ {% block content %}
46
+
47
+ <div class="viewer-container">
48
+
49
+ <div class="viewer-header">
50
+ <h2>Indagine</h2>
51
+ </div>
52
+
53
+ {# --------------------------------------------------
54
+ Vue root (SENZA search)
55
+ UUID passato dal controller
56
+ -------------------------------------------------- #}
57
+ <div v-cloak
58
+ id="investigation-app"
59
+ data-cur_role="{{ user.role }}"
60
+ data-uuid="{{ uuid }}"
61
+ data-editing="{{ user.role != 3 }}"
62
+ data-show-form="true"
63
+ data-label_save_ok="{{ t('investigation.save_ok') }}"
64
+ data-label_save_err="{{ t('investigation.save_err') }}"
65
+ >
66
+
67
+
68
+ <template v-if="isVisible">
69
+
70
+ {# ---------------- BUTTONS ---------------- #}
71
+ {% if user.role != 3 %}
72
+ <div class="col-12 text-center mt-4">
73
+
74
+ <button class="btn btn-secondary mx-2"
75
+ @click="download('xml')">
76
+ RDF/XML
77
+ </button>
78
+
79
+ <button class="btn btn-secondary mx-2"
80
+ @click="download('ttl')">
81
+ TURTLE
82
+ </button>
83
+
84
+ <button v-if="inEditing && validForm"
85
+ :disabled="saving"
86
+ class="btn btn-primary mx-2"
87
+ @click="save(false)">
88
+ <i v-if="saving" class="fa-solid fa-spinner"></i>
89
+ <i v-else class="fa-solid fa-save"></i>
90
+ </button>
91
+
92
+ </div>
93
+ {% endif %}
94
+
95
+ {# ---------------- SHACL FORM ---------------- #}
96
+ <div id="shacl-container"
97
+ style="margin-top:15px;
98
+ border:dashed 2px gray;
99
+ padding:15px;
100
+ border-radius:6px;">
101
+
102
+ <shacl-form id="shacl-form"
103
+ data-collapse="open"
104
+ data-values-namespace="indagine:"
105
+ data-shapes-url="/backend/ontology/schema/editing"
106
+ data-generate-node-shape-reference=""
107
+ ></shacl-form>
108
+
109
+ </div>
110
+
111
+ {# ---------------- DEBUG OUTPUT (admin) ---------------- #}
112
+ {% if user.role in [0,1] %}
113
+ <fieldset v-if="inEditing"
114
+ style="margin-top:15px;
115
+ border: solid 1px gray;
116
+ border-radius: 6px;
117
+ padding: 10px;">
118
+ <legend>
119
+ Output SHACL
120
+ [{@ validForm ? 'valido' : 'non valido' @}]
121
+ </legend>
122
+ <pre :style="outputStyle">{@ serializedForm @}</pre>
123
+ </fieldset>
124
+ {% endif %}
125
+
126
+ </template>
127
+
128
+ </div>
129
+
130
+ {# ---------------- CLOSE TAB ---------------- #}
131
+ <div class="viewer-actions">
132
+ <button class="btn btn-outline-secondary"
133
+ onclick="tryCloseViewer()">
134
+ Chiudi
135
+ </button>
136
+
137
+ <div id="close-hint"
138
+ style="display:none; margin-top:6px; color:#666; font-size:0.9em;">
139
+ Puoi chiudere questa scheda dal browser.
140
+ </div>
141
+ </div>
142
+
143
+ </div>
144
+
145
+ <script>
146
+ function tryCloseViewer() {
147
+ window.close();
148
+ setTimeout(() => {
149
+ const hint = document.getElementById('close-hint');
150
+ if (hint) hint.style.display = 'block';
151
+ }, 200);
152
+ }
153
+ </script>
154
+
155
+ {% endblock %}
@@ -26,6 +26,7 @@
26
26
  </style>
27
27
  {% endblock %}
28
28
 
29
+
29
30
  {% block content %}
30
31
  {% include 'v2/investigation/form_content.twig' %}
31
- {% endblock %}
32
+ {% endblock %}
@@ -1,7 +1,7 @@
1
1
 
2
2
 
3
3
 
4
-
4
+ <div style="margin: 24px 32px;">
5
5
 
6
6
 
7
7
  <style>
@@ -72,8 +72,9 @@
72
72
  data-label_new="{{ t('investigation.new_item') }}"
73
73
  data-label_search="{{ t('investigation.search') }}"
74
74
  data-label_stop_edit="{{ t('investigation.stop_edit') }}"
75
+ data-label_edit_from_other_user="{{ t('investigation.edit_from_other_user') }}"
75
76
  ></div>
76
-
77
+
77
78
  <div v-cloak id="investigation-app"
78
79
  data-show-form="{{ showForm ? 'true' : 'false' }}"
79
80
  data-cur_role="{{ user.role }}"
@@ -167,3 +168,4 @@
167
168
 
168
169
  </div>
169
170
 
171
+ </div>
@@ -0,0 +1,28 @@
1
+ {% extends "/v2/layout.twig" %}
2
+
3
+ {% block content %}
4
+
5
+ <div class="container my-4">
6
+
7
+ <h2>Indagine</h2>
8
+
9
+ <div id="shacl-container"
10
+ style="margin-top:10px;
11
+ border: solid 1px #ccc;
12
+ padding:15px;
13
+ border-radius:6px;">
14
+
15
+ <shacl-form
16
+ id="shacl-form"
17
+ data-collapse="open"
18
+ data-readonly="true"
19
+ data-values-namespace="indagine:"
20
+ data-node-iri="indagine:{{ uuid }}"
21
+ data-shapes-url="/backend/ontology/schema/view"
22
+ ></shacl-form>
23
+
24
+ </div>
25
+
26
+ </div>
27
+
28
+ {% endblock %}
@@ -1,4 +1,4 @@
1
- {% set isRdfViewer = title == 'RDF viewer' %}
1
+ {% set isRdfViewer = 'viewer' in title|lower %}
2
2
 
3
3
  <header class="header-wrapper" id="top-menu">
4
4
 
@@ -11,7 +11,13 @@
11
11
  </header>
12
12
 
13
13
  <p class="intro-text">
14
- Contatti
14
+ <p>
15
+ Per informazioni e contatti, è possibile inviare un’email all’indirizzo
16
+ <a href="mailto:hdlsd.info@gmail.com?subject=Richiesta%20informazioni">
17
+ hdlsd.info@gmail.com
18
+ </a>
19
+
20
+ </p>
15
21
  </p>
16
22
 
17
23
 
@@ -2,18 +2,51 @@
2
2
 
3
3
  {% block content %}
4
4
 
5
- <section class="system-page">
5
+ <section class="models-section credits-section">
6
6
 
7
- {# TITOLO #}
8
- <header class="system-header">
7
+ <header class="models-header">
9
8
  <h1>Crediti</h1>
9
+ <p class="models-intro">
10
+ La realizzazione di HD-LSD è il risultato di un lavoro interdisciplinare che ha
11
+ coinvolto competenze scientifiche, metodologiche e informatiche.
12
+ </p>
10
13
  </header>
11
14
 
12
- <p class="intro-text">
13
- Crediti
14
- </p>
15
-
16
-
15
+ <!-- SVILUPPO MODELLI -->
16
+ <section class="credits-block">
17
+ <h2 class="credits-title">Analisi, progettazione e modellazione formale</h2>
18
+ <p class="credits-text">
19
+ Progettazione, definizione e validazione dei modelli concettuali e ontologici
20
+ per la rappresentazione dei dati diagnostici e del patrimonio culturale.
21
+ </p>
22
+ <ul class="credits-list">
23
+ <li>Donatella Fiorani</li>
24
+ <li>Chiara Porrovecchio</li>
25
+ <li>Alessia Vaccariello</li>
26
+ </ul>
27
+ </section>
28
+
29
+ <!-- SVILUPPO INFORMATICO -->
30
+ <section class="credits-block">
31
+ <h2 class="credits-title">Sviluppo del sistema informatico - IGeA Informatica Geografia e Ambiente</h2>
32
+ <p class="credits-text">
33
+ Progettazione e sviluppo dell’architettura software, delle interfacce web
34
+ e dei servizi di gestione e interrogazione dei dati.
35
+ </p>
36
+ <ul class="credits-list">
37
+ <li>Corrado Maddaluno</li>
38
+ <li>Armando De Filippo</li>
39
+ <li>Christian Picone</li>
40
+ </ul>
41
+ </section>
42
+
43
+ <!-- LOGHI -->
44
+ <section class="credits-block">
45
+ <h2 class="credits-title">Logo</h2>
46
+ <ul class="credits-list">
47
+ <li>Silvia Cutarelli</li>
48
+ </ul>
49
+ </section>
17
50
 
18
51
  </section>
19
52
 
@@ -15,7 +15,7 @@
15
15
  {# COLONNA 1 — Aree #}
16
16
  <div class="system-col system-col-areas">
17
17
  <ul class="system-areas system-areas--bulleted">
18
- <li>Decentrare</li>
18
+ <li>Documentare</li>
19
19
  <li>Conoscere</li>
20
20
  <li>Comparare</li>
21
21
  <li>Comprendere</li>
@@ -20,18 +20,51 @@
20
20
  </p>
21
21
 
22
22
  <ul class="department-banners">
23
- <li class="department-banner banner-sdara">
24
- <span>Dipartimento di Storia, Disegno e Restauro dell'Architettura</span>
25
- </li>
23
+
24
+ <li class="department-banner banner-sdara">
25
+ <div class="department-overlay">
26
+ <div class="department-name">
27
+ Dipartimento di Storia, Disegno e Restauro dell'Architettura
28
+ </div>
29
+ <div class="department-people">
30
+ Donatella Fiorani · Chiara Porrovecchio · Silvia Cigognetti
31
+ </div>
32
+ </div>
33
+ </li>
34
+
26
35
  <li class="department-banner banner-antichita">
27
- <span>Dipartimento di Scienze dell'Antichità</span>
28
- </li>
29
- <li class="department-banner banner-terra">
30
- <span>Dipartimento di Scienze della Terra</span>
31
- </li>
32
- <li class="department-banner banner-biologia">
33
- <span>Dipartimento di Biologia Ambientale</span>
34
- </li>
36
+ <div class="department-overlay">
37
+ <div class="department-name">
38
+ Dipartimento di Scienze dell'Antichità
39
+ </div>
40
+ <div class="department-people">
41
+ Cristina Lemorini · Giulia Previti
42
+ </div>
43
+ </div>
44
+ </li>
45
+
46
+ <li class="department-banner banner-terra">
47
+ <div class="department-overlay">
48
+ <div class="department-name">
49
+ Dipartimento di Scienze della Terra
50
+ </div>
51
+ <div class="department-people">
52
+ Laura Medeghini · Melania Di Fazio
53
+ </div>
54
+ </div>
55
+ </li>
56
+
57
+ <li class="department-banner banner-biologia">
58
+ <div class="department-overlay">
59
+ <div class="department-name">
60
+ Dipartimento di Biologia Ambientale
61
+ </div>
62
+ <div class="department-people">
63
+ Mauro Iberite · Laura Sadori · Alessia Masi · Eleonora Creafogli
64
+ </div>
65
+ </div>
66
+ </li>
67
+
35
68
  </ul>
36
69
 
37
70
 
@@ -44,6 +44,19 @@
44
44
  debug="true"
45
45
  ></spar-natural>
46
46
 
47
+ <!-- Modalità risultati -->
48
+ <div class="mt-3 mb-2">
49
+ <label class="me-3">
50
+ <input type="radio" name="viewMode" value="direct" checked>
51
+ Risultati diretti
52
+ </label>
53
+
54
+ <label>
55
+ <input type="radio" name="viewMode" value="indagine">
56
+ Indagini collegate
57
+ </label>
58
+ </div>
59
+
47
60
  <span class="btn btn-primary" id="sparql-toggle">Toggle SPARQL query</span>
48
61
 
49
62
  </div>
@@ -3,6 +3,10 @@
3
3
  {% block content %}
4
4
  <div class="system-page">
5
5
  <h1>Ricerca</h1>
6
- <p>Pagina ricerche caricata correttamente.</p>
6
+ <p>Funzioni di ricerca SPARQL:</p>
7
+ <ul>
8
+ <li><strong>Veloce</strong>: solo per specifici oggetti predefiniti</li>
9
+ <li><strong>Avanzata</strong>: per tutti gli oggetti</li>
10
+ </ul>
7
11
  </div>
8
12
  {% endblock %}
@@ -1,4 +1,11 @@
1
- <div class="sidebar-investigation">
2
- <span>Indagine</span>
3
- </div>
1
+ <aside class="sidebar"
2
+ id="sidebar-investigation">
4
3
 
4
+ <nav class="sidebar-nav">
5
+
6
+ <a
7
+ :class="{ active: activeItem === 'investigation' }">
8
+ Indagine
9
+ </a>
10
+ </nav>
11
+ </aside>
@@ -3,6 +3,6 @@
3
3
  {% block content %}
4
4
  <div class="system-page">
5
5
  <h1>Amministrazione</h1>
6
- <p>Pagina amministrazione caricata correttamente.</p>
6
+ <p> Funzioni di amministrazione per la gestione degli utenti ed aggiornamento dei vocabolari.</p>
7
7
  </div>
8
8
  {% endblock %}
@@ -7,7 +7,7 @@
7
7
 
8
8
  {% block content %}
9
9
 
10
- <div id="manage-users"
10
+ <div id="manage-users" style="margin: 30px;"
11
11
  data-root="frontend/v2"
12
12
  data-role_sudo="{{ t('users.role.sudo') }}"
13
13
  data-role_admin="{{ t('users.role.admin') }}"
@@ -8,6 +8,8 @@
8
8
  {% endblock %}
9
9
 
10
10
  {% block content %}
11
+ <div style="margin: 30px;">
12
+
11
13
  <div>
12
14
  <h2 style="display:flex; justify-content:space-between; align-items:center;">
13
15
  <span>{{ t('users.vocabolaries.title') }}:</span>
@@ -41,7 +43,7 @@
41
43
  </div>
42
44
  </div>
43
45
  </div>
44
-
46
+ </div>
45
47
  <script>
46
48
  dropFilesInit({
47
49
  labels: {