Compare commits

...

11 Commits

Author SHA1 Message Date
solocla 7a944a73f7 get_commessaweb 2025-09-08 08:42:05 +02:00
solocla 71595cc8de added speech functions 2025-09-06 18:48:38 +02:00
solocla f89dbd0c23 added save all 2025-09-06 12:29:29 +02:00
solocla 9ba859e15b Merge remote-tracking branch 'origin/bugfix/warning-text-and-logic-change' 2025-09-05 21:44:38 +02:00
solocla 672e448e9a fixed renumerate parts 2025-09-04 15:16:19 +02:00
solocla 0749032fbc added collage, fixed parts marker color, added ri number 2025-09-04 15:01:03 +02:00
kapsona777 d692614f70 warning fix 2025-09-01 19:56:08 +04:00
solocla 1303cff9fd added export to lims button 2025-08-29 15:36:55 +02:00
solocla 6b2bd0964b fixed dropdown selection 2025-08-28 10:25:12 +02:00
solocla 0d2cf13524 update historical trf 2025-08-28 09:36:16 +02:00
solocla f6ef9c39d2 added multi webcam functionality 2025-08-27 20:21:38 +02:00
13 changed files with 3362 additions and 550 deletions
+1 -1
View File
@@ -31,6 +31,6 @@ Content-Length: 51
< strict-transport-security: max-age=2592000 < strict-transport-security: max-age=2592000
< x-powered-by: ASP.NET < x-powered-by: ASP.NET
< x-content-type-options: nosniff < x-content-type-options: nosniff
< date: Thu, 21 Aug 2025 10:23:44 GMT < date: Mon, 08 Sep 2025 06:40:14 GMT
< <
* Connection #0 to host 93.43.5.102 left intact * Connection #0 to host 93.43.5.102 left intact
+6 -6
View File
@@ -10,16 +10,16 @@
* issuer: C=US; O=Corporation Service Company; CN=Corporation Service Company RSA OV SSL CA * issuer: C=US; O=Corporation Service Company; CN=Corporation Service Company RSA OV SSL CA
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway. * SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2 * using HTTP/2
* [HTTP/2] [1] OPENED stream for https://93.43.5.102/limsapi/api/odata/CustomField(1083)?$expand=CustomFieldsValues * [HTTP/2] [1] OPENED stream for https://93.43.5.102/limsapi/api/odata/CommessaWeb(529435)
* [HTTP/2] [1] [:method: GET] * [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https] * [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: 93.43.5.102] * [HTTP/2] [1] [:authority: 93.43.5.102]
* [HTTP/2] [1] [:path: /limsapi/api/odata/CustomField(1083)?$expand=CustomFieldsValues] * [HTTP/2] [1] [:path: /limsapi/api/odata/CommessaWeb(529435)]
* [HTTP/2] [1] [authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc1NTc3OTAyNSwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.bBV1z1uaxZeUuw-2YS2gLGaxTCQJAHTieM82KVJb5nw] * [HTTP/2] [1] [authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc1NzMyMDgxNCwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.enjh2WJVy0NQ8zEDX5S09glaAOamuzcG33TM_fTgTMo]
* [HTTP/2] [1] [accept: application/json] * [HTTP/2] [1] [accept: application/json]
> GET /limsapi/api/odata/CustomField(1083)?$expand=CustomFieldsValues HTTP/2 > GET /limsapi/api/odata/CommessaWeb(529435) HTTP/2
Host: 93.43.5.102 Host: 93.43.5.102
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc1NTc3OTAyNSwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.bBV1z1uaxZeUuw-2YS2gLGaxTCQJAHTieM82KVJb5nw Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc1NzMyMDgxNCwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.enjh2WJVy0NQ8zEDX5S09glaAOamuzcG33TM_fTgTMo
Accept: application/json Accept: application/json
< HTTP/2 200 < HTTP/2 200
@@ -30,6 +30,6 @@ Accept: application/json
< odata-version: 4.0 < odata-version: 4.0
< x-powered-by: ASP.NET < x-powered-by: ASP.NET
< x-content-type-options: nosniff < x-content-type-options: nosniff
< date: Thu, 21 Aug 2025 10:23:44 GMT < date: Mon, 08 Sep 2025 06:40:14 GMT
< <
* Connection #0 to host 93.43.5.102 left intact * Connection #0 to host 93.43.5.102 left intact
File diff suppressed because one or more lines are too long
+1
View File
@@ -11,3 +11,4 @@
2025-08-26 16:48:44 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper 2025-08-26 16:48:44 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
2025-08-26 16:49:24 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper 2025-08-26 16:49:24 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
2025-08-26 16:50:23 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper 2025-08-26 16:50:23 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
2025-09-08 08:39:17 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
+39
View File
@@ -0,0 +1,39 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$api = VisualLimsApiClient::getInstance();
// ID della CommessaWeb specifica (cambialo di volta in volta)
$id = 529435; // TODO: Cambia questo valore con l'ID desiderato
// Endpoint per recuperare la CommessaWeb specifica con espansione dello schema custom
$endpoint = "CommessaWeb({$id})";
// Opzioni per l'espansione: includi OrderCustomFields per ottenere i campi custom dello schema assegnato all'ordine
$options = ['$expand' => 'OrderCustomFields'];
// Debug: salva URL usato
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
$query = http_build_query($options);
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
// Chiamata API
$data = $api->get($endpoint, $options);
// Salva il JSON in locale
file_put_contents(__DIR__ . '/commessaweb_schema_response.json', json_encode($data, JSON_PRETTY_PRINT));
echo json_encode($data);
} catch (Exception $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
+295 -148
View File
@@ -499,6 +499,22 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
.proceed-btn { .proceed-btn {
margin-top: 20px; margin-top: 20px;
} }
.grid-cell.button-cell,
.grid-header.button-header {
min-width: 210px !important;
flex: 0 0 210px !important;
}
.action-btn {
padding: 6px 8px;
margin-right: 5px;
border: none;
border-radius: 5px;
cursor: pointer;
width: 50px;
box-sizing: border-box;
}
</style> </style>
<title>Dati Storici - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title> <title>Dati Storici - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
</head> </head>
@@ -610,142 +626,190 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
</div> </div>
<div id="noResults" class="text-danger" style="display:none;">Nessun risultato trovato</div> <div id="noResults" class="text-danger" style="display:none;">Nessun risultato trovato</div>
<div class="scrollbar-container"></div> <div class="scrollbar-container"></div>
<form id="editForm"> <form id="editForm">
<div class="grid-container"> <div class="grid-container">
<?php $fixedColumns = ['filename_import', 'status', 'importdate']; ?> <div class="grid-top">
<?php if ($status === 'i'): ?> <div class="grid-cell actions-cell" style="flex: 0 0 200px;"></div>
<div class="grid-top"> <?php
<div class="grid-cell" style="flex: 0 0 100px;"></div> // Campo con main_field = 1
<div class="grid-cell" style="flex: 0 0 100px;"></div> if ($mainFieldMapping) {
<div class="grid-cell" style="flex: 0 0 100px;"></div> $inputClass = $mainFieldMapping['is_manual'] ? 'manual-input' : 'auto-input';
<div class="grid-cell" style="flex: 0 0 150px;"></div> if ($mainFieldMapping['is_required']) $inputClass .= ' required-input';
<?php $index = $mainFieldMapping['is_manual'] ? "manual_0" : "auto_0";
foreach ($fixedColumns as $col) { echo "<div class='grid-cell' style='flex: 0 0 150px;'>";
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>"; if ($mainFieldMapping['data_type'] === 'SceltaMultipla') {
} $fieldValue = $mainFieldMapping['manual_default'] ?? '';
$autoIndex = 0; echo "<select class='custom-field dropdown-select $inputClass' data-column='$index' data-field-id='{$mainFieldMapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
foreach ($allMappings as $mapping) { echo "<option value=''>Seleziona...</option>";
if (!$mapping['is_manual']) { echo "</select>";
$inputClass = 'auto-input'; echo "<button type='button' class='propagate-btn' data-column='$index' " . ($is_readonly ? 'disabled' : '') . "><i class='fas fa-arrow-down'></i></button>";
if ($mapping['is_required']) $inputClass .= ' required-input'; } else {
if ($mapping['data_type'] === 'SceltaMultipla') { $fieldValue = $mainFieldMapping['manual_default'] ?? '';
echo "<div class='grid-cell' style='flex: 0 0 150px;'>"; if ($mainFieldMapping['data_type'] === 'DATE' && $mainFieldMapping['manual_default'] === 'today') {
echo "<select class='custom-field dropdown-select $inputClass' data-column='auto_$autoIndex' data-field-id='{$mapping['field_id']}' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">"; $fieldValue = date('Y-m-d');
echo "<option value=''>Seleziona...</option>";
echo "</select>";
echo "<button type='button' class='propagate-btn' data-column='auto_$autoIndex' " . ($is_readonly ? 'disabled' : '') . "><i class='fas fa-arrow-down'></i></button>";
echo "</div>";
} else {
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
}
$autoIndex++;
} }
if ($mainFieldMapping['data_type'] === 'DATE') {
echo "<input type='date' class='custom-field $inputClass' data-column='$index' value='" . htmlspecialchars($fieldValue) . "' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
} elseif ($mainFieldMapping['data_type'] === 'INT') {
echo "<input type='number' class='custom-field $inputClass' data-column='$index' value='" . htmlspecialchars($fieldValue) . "' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
} else {
echo "<input type='text' class='custom-field $inputClass' data-column='$index' value='" . htmlspecialchars($fieldValue) . "' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
}
echo "<button type='button' class='propagate-btn' data-column='$index' " . ($is_readonly ? 'disabled' : '') . "><i class='fas fa-arrow-down'></i></button>";
} }
$manualIndex = 0; echo "</div>";
foreach ($allMappings as $mapping) { } else {
if ($mapping['is_manual']) { echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
}
// Campi automatici (escluso main_field)
$autoIndex = ($mainFieldMapping && !$mainFieldMapping['is_manual']) ? 1 : 0;
foreach ($allMappings as $mapping) {
if (!$mapping['is_manual'] && $mapping['main_field'] != 1) {
$inputClass = 'auto-input';
if ($mapping['is_required']) $inputClass .= ' required-input';
echo "<div class='grid-cell' style='flex: 0 0 150px;'>";
if ($mapping['data_type'] === 'SceltaMultipla') {
$fieldValue = $mapping['manual_default'] ?? ''; $fieldValue = $mapping['manual_default'] ?? '';
if ($mapping['data_type'] === 'DATE' && $mapping['manual_default'] === 'today') { echo "<select class='custom-field dropdown-select $inputClass' data-column='auto_$autoIndex' data-field-id='{$mapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
$fieldValue = date('Y-m-d'); echo "<option value=''>Seleziona...</option>";
} echo "</select>";
$inputClass = 'manual-input'; echo "<button type='button' class='propagate-btn' data-column='auto_$autoIndex' " . ($is_readonly ? 'disabled' : '') . "><i class='fas fa-arrow-down'></i></button>";
if ($mapping['is_required']) $inputClass .= ' required-input'; } else {
echo "<div class='grid-cell' style='flex: 0 0 150px;'>"; echo "<div style='height: 34px;'></div>";
if ($mapping['data_type'] === 'SceltaMultipla') {
echo "<select class='custom-field dropdown-select $inputClass' data-column='manual_$manualIndex' data-field-id='{$mapping['field_id']}' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
echo "<option value=''>Seleziona...</option>";
echo "</select>";
echo "<button type='button' class='propagate-btn' data-column='manual_$manualIndex' " . ($is_readonly ? 'disabled' : '') . "><i class='fas fa-arrow-down'></i></button>";
} elseif ($mapping['data_type'] === 'DATE') {
echo "<input type='date' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
} elseif ($mapping['data_type'] === 'INT') {
echo "<input type='number' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
} else {
echo "<input type='text' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
}
echo "<button type='button' class='propagate-btn' data-column='manual_$manualIndex' " . ($is_readonly ? 'disabled' : '') . "><i class='fas fa-arrow-down'></i></button>";
echo "</div>";
$manualIndex++;
} }
echo "</div>";
$autoIndex++;
} }
echo "<div class='grid-cell' style='flex: 0 0 200px;'></div>"; }
echo "<div class='grid-cell' style='flex: 0 0 250px;'></div>"; // Campi manuali (escluso main_field)
?> $manualIndex = ($mainFieldMapping && $mainFieldMapping['is_manual']) ? 1 : 0;
</div> foreach ($allMappings as $mapping) {
<?php endif; ?> if ($mapping['is_manual'] && $mapping['main_field'] != 1) {
$fieldValue = $mapping['manual_default'] ?? '';
if ($mapping['data_type'] === 'DATE' && $mapping['manual_default'] === 'today') {
$fieldValue = date('Y-m-d');
}
$inputClass = 'manual-input';
if ($mapping['is_required']) $inputClass .= ' required-input';
echo "<div class='grid-cell' style='flex: 0 0 150px;'>";
if ($mapping['data_type'] === 'SceltaMultipla') {
echo "<select class='custom-field dropdown-select $inputClass' data-column='manual_$manualIndex' data-field-id='{$mapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
echo "<option value=''>Seleziona...</option>";
echo "</select>";
echo "<button type='button' class='propagate-btn' data-column='manual_$manualIndex' " . ($is_readonly ? 'disabled' : '') . "><i class='fas fa-arrow-down'></i></button>";
} elseif ($mapping['data_type'] === 'DATE') {
echo "<input type='date' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
echo "<button type='button' class='propagate-btn' data-column='manual_$manualIndex' " . ($is_readonly ? 'disabled' : '') . "><i class='fas fa-arrow-down'></i></button>";
} elseif ($mapping['data_type'] === 'INT') {
echo "<input type='number' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
echo "<button type='button' class='propagate-btn' data-column='manual_$manualIndex' " . ($is_readonly ? 'disabled' : '') . "><i class='fas fa-arrow-down'></i></button>";
} else {
echo "<input type='text' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
echo "<button type='button' class='propagate-btn' data-column='manual_$manualIndex' " . ($is_readonly ? 'disabled' : '') . "><i class='fas fa-arrow-down'></i></button>";
}
echo "</div>";
$manualIndex++;
}
}
// Colonne status, Import Reference Code, filename_import
$fixedColumnsReduced = ['status'];
foreach ($fixedColumnsReduced as $col) {
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
}
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>"; // Import Reference Code
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>"; // filename_import
// AWB Number e Tracking Info
echo "<div class='grid-cell' style='flex: 0 0 200px;'></div>";
echo "<div class='grid-cell' style='flex: 0 0 250px;'></div>";
?>
</div>
<div class="grid-row"> <div class="grid-row">
<div class="grid-header" style="flex: 0 0 100px;">Save</div> <div class="grid-header actions-cell" style="flex: 0 0 200px; position: relative;">Azioni<div class="resizer"></div>
<div class="grid-header" style="flex: 0 0 100px;">Photos</div>
<div class="grid-header" style="flex: 0 0 100px;">Parts</div>
<div class="grid-header" data-index="3" style="flex: 0 0 150px; position: relative;">Import Reference Code<div class="resizer"></div>
</div> </div>
<?php <?php
$headerIndex = 4; // Header per il campo main_field = 1
foreach ($fixedColumns as $col) { $headerIndex = 1;
if ($mainFieldMapping) {
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>" . htmlspecialchars($mainFieldMapping['field_label']) . "<div class='resizer'></div></div>";
$headerIndex++;
}
// Header per campi automatici (escluso main_field)
foreach ($allMappings as $mapping) {
if (!$mapping['is_manual'] && $mapping['main_field'] != 1) {
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>" . htmlspecialchars($mapping['field_label']) . "<div class='resizer'></div></div>";
$headerIndex++;
}
}
// Header per campi manuali (escluso main_field)
foreach ($allMappings as $mapping) {
if ($mapping['is_manual'] && $mapping['main_field'] != 1) {
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>" . htmlspecialchars($mapping['field_label']) . "<div class='resizer'></div></div>";
$headerIndex++;
}
}
// Header per status, Import Reference Code, filename_import
foreach ($fixedColumnsReduced as $col) {
$displayName = $slugMapping[$col] ?? $col; $displayName = $slugMapping[$col] ?? $col;
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>$displayName<div class='resizer'></div></div>"; echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>$displayName<div class='resizer'></div></div>";
$headerIndex++; $headerIndex++;
} }
foreach ($allMappings as $mapping) { echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>Import Reference Code<div class='resizer'></div></div>";
if (!$mapping['is_manual']) { $headerIndex++;
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>" . htmlspecialchars($mapping['field_label']) . "<div class='resizer'></div></div>"; echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>File<div class='resizer'></div></div>";
$headerIndex++; $headerIndex++;
} // Header per AWB Number e Tracking Info
}
foreach ($allMappings as $mapping) {
if ($mapping['is_manual']) {
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>" . htmlspecialchars($mapping['field_label']) . "<div class='resizer'></div></div>";
$headerIndex++;
}
}
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 200px; position: relative;'>AWB Number<div class='resizer'></div></div>"; echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 200px; position: relative;'>AWB Number<div class='resizer'></div></div>";
echo "<div class='grid-header' data-index='" . ($headerIndex + 1) . "' style='flex: 0 0 250px; position: relative;'>Tracking Info<div class='resizer'></div></div>"; $headerIndex++;
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 250px; position: relative;'>Tracking Info<div class='resizer'></div></div>";
?> ?>
</div> </div>
<?php foreach ($importedData as $index => $row): ?> <?php foreach ($importedData as $index => $row): ?>
<div class="grid-row" data-id="<?= $row['iddatadb'] ?>"> <div class="grid-row" data-id="<?= $row['iddatadb'] ?>">
<div class="grid-cell" style="flex: 0 0 100px; position: relative;"> <div class="grid-cell actions-cell" style="flex: 0 0 200px; position: relative;">
<?php if (!$is_readonly): ?> <div style="display: flex; gap: 5px; justify-content: center;">
<button type="button" class="save-btn action-btn" data-row="<?= $index ?>" style="background: #28a745; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; width: 100%; box-sizing: border-box;"><i class="fas fa-save"></i></button> <?php if (!$is_readonly): ?>
<?php else: ?> <button type="button" class="save-btn action-btn" data-row="<?= $index ?>" style="background: #28a745; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; flex: 1;"><i class="fas fa-save"></i></button>
<button type="button" class="save-btn action-btn" data-row="<?= $index ?>" style="background: #ccc; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: not-allowed; width: 100%; box-sizing: border-box;" disabled><i class="fas fa-save"></i></button> <button type="button" class="photos-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" style="background: #007bff; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; flex: 1;"><i class="fas fa-camera"></i></button>
<?php endif; ?> <button type="button" class="parts-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" style="background: #ffc107; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; flex: 1;"><i class="fas fa-puzzle-piece"></i></button>
</div> <?php else: ?>
<div class="grid-cell" style="flex: 0 0 100px; position: relative;"> <button type="button" class="save-btn action-btn" data-row="<?= $index ?>" style="background: #ccc; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: not-allowed; flex: 1;" disabled><i class="fas fa-save"></i></button>
<button type="button" class="photos-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" style="background: #007bff; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; width: 100%; box-sizing: border-box;"><i class="fas fa-camera"></i></button> <button type="button" class="photos-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" style="background: #ccc; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: not-allowed; flex: 1;" disabled><i class="fas fa-camera"></i></button>
</div> <button type="button" class="parts-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" style="background: #ccc; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: not-allowed; flex: 1;" disabled><i class="fas fa-puzzle-piece"></i></button>
<div class="grid-cell" style="flex: 0 0 100px; position: relative;"> <?php endif; ?>
<button type="button" class="parts-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" style="background: #ffc107; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; width: 100%; box-sizing: border-box;"><i class="fas fa-puzzle-piece"></i></button> </div>
</div> </div>
<?php <?php
$cellIndex = 3; $cellIndex = 1;
echo "<div class='grid-cell' data-col='importreferencecode' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>"; $rowDetails = array_filter($manualDetails, fn($d) => $d['datadb_id'] == $row['iddatadb']);
echo "<span>" . htmlspecialchars($row['importreferencecode']) . "</span>"; // Campo con main_field = 1
echo "<input type='hidden' name='rows[$index][importreferencecode]' value='" . htmlspecialchars($row['importreferencecode']) . "'>"; if ($mainFieldMapping) {
echo "</div>"; $detail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mainFieldMapping['id']);
$cellIndex++; $detail = reset($detail) ?: ['field_value' => $mainFieldMapping['manual_default']];
foreach ($fixedColumns as $col) { $fieldValue = $detail['field_value'] ?? $mainFieldMapping['manual_default'] ?? '';
$value = $row[$col] ?? ''; $requiredClass = ($mainFieldMapping['is_required'] && (is_null($fieldValue) || $fieldValue === '')) ? 'missing-required' : '';
echo "<div class='grid-cell editable-cell' data-col='$col' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>"; $inputClass = $mainFieldMapping['is_manual'] ? 'manual-input' : 'auto-input';
if ($col === 'importdate') { if ($mainFieldMapping['is_required']) $inputClass .= ' required-input';
echo "<span>" . htmlspecialchars($value) . "</span>"; $indexField = $mainFieldMapping['is_manual'] ? "manual_0" : "auto_0";
echo "<input type='hidden' name='rows[$index][$col]' value='" . htmlspecialchars($value) . "'>"; echo "<div class='grid-cell editable-cell $requiredClass' data-col='$indexField' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
} elseif ($col === 'filename_import') { if ($mainFieldMapping['data_type'] === 'SceltaMultipla') {
echo "<a href='imported_trf/" . htmlspecialchars($value) . "' target='_blank'>File</a>"; echo "<select name='rows[$index][details][{$mainFieldMapping['id']}][field_value]' class='cell-input dropdown-select $inputClass' data-mapping-id='{$mainFieldMapping['id']}' data-field-id='{$mainFieldMapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
echo "<input type='hidden' name='rows[$index][$col]' value='" . htmlspecialchars($value) . "'>"; echo "<option value=''>Seleziona...</option>";
} elseif ($col === 'status') { echo "</select>";
$badgeClass = $value === 'i' ? 'status-i' : ($value === 'P' ? 'status-P' : 'status-l'); } elseif ($mainFieldMapping['data_type'] === 'DATE') {
$badgeText = $value === 'i' ? 'Imported' : ($value === 'P' ? 'Progress' : 'LIMS'); echo "<input type='date' name='rows[$index][details][{$mainFieldMapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
echo "<span class='status-badge $badgeClass'>" . htmlspecialchars($badgeText) . "</span>"; } elseif ($mainFieldMapping['data_type'] === 'INT') {
echo "<input type='hidden' name='rows[$index][$col]' value='" . htmlspecialchars($value ?? 'i') . "'>"; echo "<input type='number' name='rows[$index][details][{$mainFieldMapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
} else {
echo "<input type='text' name='rows[$index][details][{$mainFieldMapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
} }
echo "</div>"; echo "</div>";
$cellIndex++; $cellIndex++;
} }
$rowDetails = array_filter($manualDetails, fn($d) => $d['datadb_id'] == $row['iddatadb']); // Campi automatici (escluso main_field)
$autoIndex = 0; $autoIndex = ($mainFieldMapping && !$mainFieldMapping['is_manual']) ? 1 : 0;
foreach ($allMappings as $mapping) { foreach ($allMappings as $mapping) {
if (!$mapping['is_manual']) { if (!$mapping['is_manual'] && $mapping['main_field'] != 1) {
$detail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mapping['id']); $detail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mapping['id']);
$detail = reset($detail) ?: ['field_value' => $mapping['manual_default']]; $detail = reset($detail) ?: ['field_value' => $mapping['manual_default']];
$fieldValue = $detail['field_value'] ?? $mapping['manual_default'] ?? ''; $fieldValue = $detail['field_value'] ?? $mapping['manual_default'] ?? '';
@@ -754,7 +818,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
if ($mapping['is_required']) $inputClass .= ' required-input'; if ($mapping['is_required']) $inputClass .= ' required-input';
echo "<div class='grid-cell editable-cell $requiredClass' data-col='auto_$autoIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>"; echo "<div class='grid-cell editable-cell $requiredClass' data-col='auto_$autoIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
if ($mapping['data_type'] === 'SceltaMultipla') { if ($mapping['data_type'] === 'SceltaMultipla') {
echo "<select name='rows[$index][details][{$mapping['id']}][field_value]' class='cell-input dropdown-select $inputClass' data-mapping-id='{$mapping['id']}' data-field-id='{$mapping['field_id']}' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">"; echo "<select name='rows[$index][details][{$mapping['id']}][field_value]' class='cell-input dropdown-select $inputClass' data-mapping-id='{$mapping['id']}' data-field-id='{$mapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
echo "<option value=''>Seleziona...</option>"; echo "<option value=''>Seleziona...</option>";
echo "</select>"; echo "</select>";
} elseif ($mapping['data_type'] === 'DATE') { } elseif ($mapping['data_type'] === 'DATE') {
@@ -769,9 +833,10 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$autoIndex++; $autoIndex++;
} }
} }
$manualIndex = 0; // Campi manuali (escluso main_field)
$manualIndex = ($mainFieldMapping && $mainFieldMapping['is_manual']) ? 1 : 0;
foreach ($allMappings as $mapping) { foreach ($allMappings as $mapping) {
if ($mapping['is_manual']) { if ($mapping['is_manual'] && $mapping['main_field'] != 1) {
$detail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mapping['id']); $detail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mapping['id']);
$detail = reset($detail) ?: ['field_value' => $mapping['manual_default']]; $detail = reset($detail) ?: ['field_value' => $mapping['manual_default']];
$fieldValue = $detail['field_value'] ?? $mapping['manual_default'] ?? ''; $fieldValue = $detail['field_value'] ?? $mapping['manual_default'] ?? '';
@@ -783,7 +848,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
if ($mapping['is_required']) $inputClass .= ' required-input'; if ($mapping['is_required']) $inputClass .= ' required-input';
echo "<div class='grid-cell editable-cell $requiredClass' data-col='manual_$manualIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>"; echo "<div class='grid-cell editable-cell $requiredClass' data-col='manual_$manualIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
if ($mapping['data_type'] === 'SceltaMultipla') { if ($mapping['data_type'] === 'SceltaMultipla') {
echo "<select name='rows[$index][details][{$mapping['id']}][field_value]' class='cell-input dropdown-select $inputClass' data-mapping-id='{$mapping['id']}' data-field-id='{$mapping['field_id']}' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">"; echo "<select name='rows[$index][details][{$mapping['id']}][field_value]' class='cell-input dropdown-select $inputClass' data-mapping-id='{$mapping['id']}' data-field-id='{$mapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ($is_readonly ? ' readonly' : '') . ">";
echo "<option value=''>Seleziona...</option>"; echo "<option value=''>Seleziona...</option>";
echo "</select>"; echo "</select>";
} elseif ($mapping['data_type'] === 'DATE') { } elseif ($mapping['data_type'] === 'DATE') {
@@ -798,6 +863,32 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$manualIndex++; $manualIndex++;
} }
} }
// Colonna status
$fixedColumnsReduced = ['status'];
foreach ($fixedColumnsReduced as $col) {
$value = $row[$col] ?? '';
echo "<div class='grid-cell editable-cell' data-col='$col' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
if ($col === 'status') {
$badgeClass = $value === 'i' ? 'status-i' : ($value === 'P' ? 'status-P' : 'status-l');
$badgeText = $value === 'i' ? 'Imported' : ($value === 'P' ? 'Progress' : 'LIMS');
echo "<span class='status-badge $badgeClass'>" . htmlspecialchars($badgeText) . "</span>";
echo "<input type='hidden' name='rows[$index][$col]' value='" . htmlspecialchars($value ?? 'i') . "'>";
}
echo "</div>";
$cellIndex++;
}
// Colonne Import Reference Code e filename_import
echo "<div class='grid-cell' data-col='importreferencecode' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
echo "<span>" . htmlspecialchars($row['importreferencecode']) . "</span>";
echo "<input type='hidden' name='rows[$index][importreferencecode]' value='" . htmlspecialchars($row['importreferencecode']) . "'>";
echo "</div>";
$cellIndex++;
echo "<div class='grid-cell' data-col='filename_import' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
echo "<a href='imported_trf/" . htmlspecialchars($row['filename_import']) . "' target='_blank'>File</a>";
echo "<input type='hidden' name='rows[$index][filename_import]' value='" . htmlspecialchars($row['filename_import']) . "'>";
echo "</div>";
$cellIndex++;
// Colonne AWB Number e Tracking Info
?> ?>
<div class="grid-cell" style="flex: 0 0 200px;"> <div class="grid-cell" style="flex: 0 0 200px;">
<select name="rows[<?= $index ?>][carrier]" class="carrier-select" <?= $is_readonly ? 'disabled' : '' ?>> <select name="rows[<?= $index ?>][carrier]" class="carrier-select" <?= $is_readonly ? 'disabled' : '' ?>>
@@ -833,6 +924,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
</ul> </ul>
</nav> </nav>
</form> </form>
<?php <?php
if (file_exists('modal_parts.php')) { if (file_exists('modal_parts.php')) {
include 'modal_parts.php'; include 'modal_parts.php';
@@ -856,6 +948,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
</div> </div>
<?php include('jsinclude.php'); ?> <?php include('jsinclude.php'); ?>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
<script src="photos.js"></script> <script src="photos.js"></script>
<script src="parts.js"></script> <script src="parts.js"></script>
<script src="tracking.js"></script> <script src="tracking.js"></script>
@@ -890,7 +983,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
const cells = row.querySelectorAll('td'); const cells = row.querySelectorAll('td');
let match = false; let match = false;
cells.forEach((cell, index) => { cells.forEach((cell, index) => {
if (index >= 1 && index <= 3) { if (index >= 1 && index <= 4) { // Cerca in ID, main_field, importreferencecode, filename
const text = cell.textContent.toLowerCase(); const text = cell.textContent.toLowerCase();
if (text.includes(filter)) { if (text.includes(filter)) {
match = true; match = true;
@@ -934,6 +1027,18 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
} }
}); });
}); });
// Gestione eliminazione multipla
const bulkActionForm = document.getElementById('bulkActionForm');
bulkActionForm.addEventListener('submit', function(e) {
const selectedCheckboxes = document.querySelectorAll('input[name="selected_ids[]"]:checked');
if (selectedCheckboxes.length === 0) {
e.preventDefault();
alert('Seleziona almeno un record da eliminare.');
} else if (!confirm('Sicuro di voler eliminare i record selezionati?')) {
e.preventDefault();
}
});
<?php else: ?> <?php else: ?>
// Gestione input e celle espandibili // Gestione input e celle espandibili
const inputs = document.querySelectorAll('.cell-input'); const inputs = document.querySelectorAll('.cell-input');
@@ -961,8 +1066,13 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
inputs.forEach(input => { inputs.forEach(input => {
const name = input.name.replace(`rows[${rowIndex}]`, '').replace(/\[|\]/g, ''); const name = input.name.replace(`rows[${rowIndex}]`, '').replace(/\[|\]/g, '');
formData.append(name, input.value); formData.append(name, input.value);
// Aggiorna data-selected-value per i dropdown
if (input.tagName === 'SELECT' && input.classList.contains('dropdown-select')) {
input.setAttribute('data-selected-value', input.value);
}
}); });
formData.append('iddatadb', iddatadb); formData.append('iddatadb', iddatadb);
formData.append('mapping', JSON.stringify(<?= json_encode($allMappings) ?>));
fetch('save_edited_row.php', { fetch('save_edited_row.php', {
method: 'POST', method: 'POST',
body: formData body: formData
@@ -977,6 +1087,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
cell.classList.add('flash-success'); cell.classList.add('flash-success');
}); });
setTimeout(() => cells.forEach(cell => cell.classList.remove('flash-success')), 500); setTimeout(() => cells.forEach(cell => cell.classList.remove('flash-success')), 500);
alert('Salvataggio avvenuto con successo!');
} else { } else {
alert('Errore durante il salvataggio: ' + data.message); alert('Errore durante il salvataggio: ' + data.message);
} }
@@ -990,12 +1101,12 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
propagateButtons.forEach(button => { propagateButtons.forEach(button => {
button.addEventListener('click', function() { button.addEventListener('click', function() {
if (this.hasAttribute('disabled')) return; if (this.hasAttribute('disabled')) return;
const columnIndex = this.getAttribute('data-column').replace('manual_', ''); const column = this.getAttribute('data-column');
const input = this.previousElementSibling; const input = this.previousElementSibling;
const value = input.value; const value = input.value;
const gridTopCells = document.querySelector('.grid-top').querySelectorAll('.grid-cell'); const gridTopCells = document.querySelector('.grid-top').querySelectorAll('.grid-cell');
const targetTopIndex = Array.from(gridTopCells).findIndex(cell => const targetTopIndex = Array.from(gridTopCells).findIndex(cell =>
cell.querySelector('.propagate-btn') === button cell.querySelector('.propagate-btn[data-column="' + column + '"]')
); );
if (targetTopIndex !== -1) { if (targetTopIndex !== -1) {
const rows = document.querySelectorAll('.grid-row'); const rows = document.querySelectorAll('.grid-row');
@@ -1004,9 +1115,12 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
if (cells.length > targetTopIndex) { if (cells.length > targetTopIndex) {
const targetInput = cells[targetTopIndex].querySelector('input, select'); const targetInput = cells[targetTopIndex].querySelector('input, select');
if (targetInput && !targetInput.hasAttribute('readonly')) { if (targetInput && !targetInput.hasAttribute('readonly')) {
if (targetInput.type === 'date') targetInput.value = value; targetInput.value = value;
else if (targetInput.tagName === 'SELECT') targetInput.value = value; if (targetInput.tagName === 'SELECT') {
else targetInput.value = value; targetInput.setAttribute('data-selected-value', value);
const event = new Event('change');
targetInput.dispatchEvent(event);
}
} }
} }
}); });
@@ -1123,47 +1237,80 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
async function populateDropdowns() { async function populateDropdowns() {
if (<?= json_encode($is_readonly) ?>) return; if (<?= json_encode($is_readonly) ?>) return;
const dropdowns = document.querySelectorAll('.dropdown-select'); const dropdowns = document.querySelectorAll('.dropdown-select');
if (dropdowns.length === 0) return; console.log('Dropdown trovati:', dropdowns.length);
const uniqueFieldIds = [...new Set(Array.from(dropdowns).map(d => d.getAttribute('data-field-id')))].filter(fieldId => fieldId); if (dropdowns.length === 0) {
for (const fieldId of uniqueFieldIds) { console.warn('Nessun dropdown trovato con classe .dropdown-select');
if (!dropdownData[fieldId]) { return;
try {
const response = await fetch(`get_customfield_values.php?field_id=${fieldId}`);
const data = await response.json();
if (data.error) {
console.error('Errore per field_id', fieldId, ':', data.error);
continue;
}
dropdownData[fieldId] = data.CustomFieldsValues || [];
} catch (error) {
console.error('Errore nel fetch per field_id', fieldId, ':', error);
}
}
} }
dropdowns.forEach(dropdown => {
const fieldId = dropdown.getAttribute('data-field-id'); const uniqueFieldIds = [...new Set(Array.from(dropdowns).map(d => d.getAttribute('data-field-id')))].filter(fieldId => fieldId);
if (!fieldId || !dropdownData[fieldId]) { console.log('Field IDs unici:', uniqueFieldIds);
dropdown.innerHTML = '<option value="">Errore nel caricamento</option>'; const missingFieldIds = uniqueFieldIds.filter(fieldId => !dropdownData[fieldId]);
if (missingFieldIds.length > 0) {
try {
const response = await fetch(`get_customfield_values.php?field_ids=${missingFieldIds.join(',')}`);
if (!response.ok) {
throw new Error(`Errore HTTP: ${response.status} ${response.statusText}`);
}
const data = await response.json();
console.log('Risposta da get_customfield_values.php:', data);
if (data.error) {
console.error('Errore dal server:', data.error);
dropdowns.forEach(dropdown => {
dropdown.innerHTML = '<option value="">Errore server</option>';
});
return;
}
for (const fieldId of Object.keys(data)) {
dropdownData[fieldId] = data[fieldId] || [];
}
} catch (error) {
console.error('Errore nel fetch dei valori dei dropdown:', error);
dropdowns.forEach(dropdown => {
dropdown.innerHTML = '<option value="">Errore caricamento</option>';
});
return; return;
} }
}
dropdowns.forEach(dropdown => {
const fieldId = dropdown.getAttribute('data-field-id');
const mappingId = dropdown.getAttribute('data-mapping-id');
const selectedValue = dropdown.getAttribute('data-selected-value') || '';
const currentValue = dropdown.value || '';
console.log(`Popolamento dropdown field_id=${fieldId}, mapping_id=${mappingId}, valore corrente=${currentValue}, valore selezionato=${selectedValue}`);
if (!fieldId || !dropdownData[fieldId]) {
dropdown.innerHTML = '<option value="">Nessun dato</option>';
return;
}
// Preserva il valore corrente o usa quello salvato
dropdown.innerHTML = '<option value="">Seleziona...</option>'; dropdown.innerHTML = '<option value="">Seleziona...</option>';
dropdownData[fieldId].forEach(value => { dropdownData[fieldId].forEach(value => {
const option = document.createElement('option'); const option = document.createElement('option');
option.value = value.IdCustomFieldsValue; option.value = value.IdCustomFieldsValue;
option.textContent = value.Valore; option.textContent = value.Valore;
if (dropdown.value === option.value) option.selected = true; // Dai priorità a data-selected-value per il caricamento iniziale
if (selectedValue === String(value.IdCustomFieldsValue)) {
option.selected = true;
} else if (currentValue === String(value.IdCustomFieldsValue)) {
option.selected = true;
}
dropdown.appendChild(option); dropdown.appendChild(option);
}); });
// Verifica se il valore selezionato esiste tra le opzioni
if ((selectedValue || currentValue) && dropdown.value !== (selectedValue || currentValue)) {
dropdown.value = '';
console.warn(`Valore ${selectedValue || currentValue} non trovato per fieldId ${fieldId}`);
}
}); });
} }
populateDropdowns(); populateDropdowns();
saveButtons.forEach(btn => {
btn.addEventListener('click', function() {
if (!this.hasAttribute('disabled')) {
setTimeout(populateDropdowns, 100);
}
});
});
<?php endif; ?> <?php endif; ?>
}); });
</script> </script>
File diff suppressed because one or more lines are too long
+255 -144
View File
@@ -21,8 +21,8 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['template_id']) || !i
$template_id = intval($_POST['template_id']) ?? $_SESSION['template_id']; $template_id = intval($_POST['template_id']) ?? $_SESSION['template_id'];
$selected_rows = $_POST['selected_rows'] ?? $_SESSION['selected_rows']; $selected_rows = $_POST['selected_rows'] ?? $_SESSION['selected_rows'];
$columns = json_decode($_POST['columns'], true) ?? $_SESSION['columns']; // Header dell'XLS $columns = json_decode($_POST['columns'], true) ?? $_SESSION['columns'];
$rows = json_decode($_POST['rows'], true) ?? $_SESSION['rows']; // Dati dell'XLS $rows = json_decode($_POST['rows'], true) ?? $_SESSION['rows'];
$newFilename = htmlspecialchars($_POST['filename']) ?? $_SESSION['filename']; $newFilename = htmlspecialchars($_POST['filename']) ?? $_SESSION['filename'];
// Log dei dati ricevuti // Log dei dati ricevuti
@@ -31,7 +31,7 @@ error_log("Columns: " . json_encode($columns));
error_log("Rows: " . json_encode($rows)); error_log("Rows: " . json_encode($rows));
// Recupera l'ID dell'utente loggato // Recupera l'ID dell'utente loggato
$user_id = $iduserlogin ?? 1; // Default a 1 se non definito $user_id = $iduserlogin ?? 1;
$db = DBHandlerSelect::getInstance(); $db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection(); $pdo = $db->getConnection();
@@ -98,26 +98,26 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
<?php include('cssinclude.php'); ?> <?php include('cssinclude.php'); ?>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2Lw==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2Lw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<style> <style>
/* Colori pastello per input/select */ .cell-changed {
background-color: #fff3b0 !important;
transition: background-color 0.3s ease;
}
input.auto-input, input.auto-input,
select.auto-input { select.auto-input {
background-color: #d4edda; background-color: #d4edda;
/* Verde pastello */
} }
input.manual-input, input.manual-input,
select.manual-input { select.manual-input {
background-color: #fff3cd; background-color: #fff3cd;
/* Giallino pastello */
} }
input.required-input, input.required-input,
select.required-input { select.required-input {
background-color: #f8d7da; background-color: #f8d7da;
/* Rossino chiaro */
} }
/* Stili base per input/select */
input, input,
select { select {
width: 100%; width: 100%;
@@ -128,14 +128,11 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
font-size: 14px; font-size: 14px;
} }
/* Mantieni leggibilità del testo */
input, input,
select { select {
color: #333; color: #333;
/* Colore scuro per contrasto */
} }
/* Stili per i badge di stato */
.status-badge { .status-badge {
display: inline-block; display: inline-block;
padding: 2px 8px; padding: 2px 8px;
@@ -161,7 +158,6 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
color: white; color: white;
} }
/* Stili esistenti rimangono invariati */
.grid-container { .grid-container {
overflow-x: auto; overflow-x: auto;
max-width: 100%; max-width: 100%;
@@ -254,6 +250,11 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
align-items: center; align-items: center;
} }
.grid-top .save-all-cell {
flex: 0 0 210px;
align-items: flex-start;
}
.propagate-btn { .propagate-btn {
background: none; background: none;
border: none; border: none;
@@ -370,7 +371,6 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5); box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
} }
/* Stile per l'header dei pulsanti combinati */
.grid-cell.button-cell, .grid-cell.button-cell,
.grid-header.button-header { .grid-header.button-header {
min-width: 210px !important; min-width: 210px !important;
@@ -383,9 +383,28 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
border: none; border: none;
border-radius: 5px; border-radius: 5px;
cursor: pointer; cursor: pointer;
width: 50px; width: 35px;
box-sizing: border-box; box-sizing: border-box;
} }
.flash-success {
background-color: #d4edda !important;
transition: background-color 0.3s ease;
}
.save-all-btn {
background-color: #28a745;
color: white;
border: none;
border-radius: 5px;
padding: 8px 16px;
cursor: pointer;
font-size: 14px;
}
.save-all-btn:hover {
background-color: #218838;
}
</style> </style>
<title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title> <title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
</head> </head>
@@ -408,6 +427,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
<h6 class="mb-0">Modifica Dati Importati</h6> <h6 class="mb-0">Modifica Dati Importati</h6>
<div id="unsavedChanges" style="display:none; color: red; font-weight: bold; margin:10px 0;"> <div id="unsavedChanges" style="display:none; color: red; font-weight: bold; margin:10px 0;">
⚠️ Unsaved changes detected! Please save before leaving this page. ⚠️ Unsaved changes detected! Please save before leaving this page.
<ul id="changedFields" style="margin-top:5px; font-weight:normal; color:darkred;"></ul>
</div> </div>
</div> </div>
</div> </div>
@@ -415,9 +435,10 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
<div class="card-body"> <div class="card-body">
<form id="editForm"> <form id="editForm">
<div class="grid-container"> <div class="grid-container">
<!-- Riga superiore per gli input dei campi manuali -->
<div class="grid-top"> <div class="grid-top">
<div class="grid-cell button-cell" style="flex: 0 0 210px;"></div> <!-- Actions --> <div class="grid-cell save-all-cell">
<button type="button" class="save-all-btn" title="Save All Rows"><i class="fas fa-save"></i> Save All</button>
</div>
<?php if ($mainFieldMapping): ?> <?php if ($mainFieldMapping): ?>
<div class="grid-cell" style="flex: 0 0 150px;"> <div class="grid-cell" style="flex: 0 0 150px;">
<?php <?php
@@ -428,7 +449,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$inputClass = $mainFieldMapping['is_manual'] ? 'manual-input' : 'auto-input'; $inputClass = $mainFieldMapping['is_manual'] ? 'manual-input' : 'auto-input';
if ($mainFieldMapping['is_required']) $inputClass .= ' required-input'; if ($mainFieldMapping['is_required']) $inputClass .= ' required-input';
if ($mainFieldMapping['data_type'] === 'SceltaMultipla') { if ($mainFieldMapping['data_type'] === 'SceltaMultipla') {
echo "<select class='custom-field dropdown-select $inputClass' data-column='main_field' data-field-id='{$mainFieldMapping['field_id']}' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ">"; echo "<select class='custom-field dropdown-select $inputClass' data-column='main_field' data-field-id='{$mainFieldMapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ">";
echo "<option value=''>Seleziona...</option>"; echo "<option value=''>Seleziona...</option>";
echo "</select>"; echo "</select>";
echo "<button type='button' class='propagate-btn' data-column='main_field'><i class='fas fa-arrow-down'></i></button>"; echo "<button type='button' class='propagate-btn' data-column='main_field'><i class='fas fa-arrow-down'></i></button>";
@@ -445,9 +466,8 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
?> ?>
</div> </div>
<?php endif; ?> <?php endif; ?>
<div class="grid-cell" style="flex: 0 0 150px;"></div> <!-- status --> <div class="grid-cell" style="flex: 0 0 150px;"></div>
<?php <?php
// Campi automatici (is_manual = 0) escluso main_field
$autoIndex = 0; $autoIndex = 0;
foreach ($allMappings as $mapping) { foreach ($allMappings as $mapping) {
if (!$mapping['is_manual'] && $mapping['main_field'] != 1) { if (!$mapping['is_manual'] && $mapping['main_field'] != 1) {
@@ -455,7 +475,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
if ($mapping['is_required']) $inputClass .= ' required-input'; if ($mapping['is_required']) $inputClass .= ' required-input';
if ($mapping['data_type'] === 'SceltaMultipla') { if ($mapping['data_type'] === 'SceltaMultipla') {
echo "<div class='grid-cell' style='flex: 0 0 150px;'>"; echo "<div class='grid-cell' style='flex: 0 0 150px;'>";
echo "<select class='custom-field dropdown-select $inputClass' data-column='auto_$autoIndex' data-field-id='{$mapping['field_id']}' " . ($mapping['is_required'] ? 'required' : '') . ">"; echo "<select class='custom-field dropdown-select $inputClass' data-column='auto_$autoIndex' data-field-id='{$mapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
echo "<option value=''>Seleziona...</option>"; echo "<option value=''>Seleziona...</option>";
echo "</select>"; echo "</select>";
echo "<button type='button' class='propagate-btn' data-column='auto_$autoIndex'><i class='fas fa-arrow-down'></i></button>"; echo "<button type='button' class='propagate-btn' data-column='auto_$autoIndex'><i class='fas fa-arrow-down'></i></button>";
@@ -466,7 +486,6 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$autoIndex++; $autoIndex++;
} }
} }
// Campi manuali (is_manual = 1) escluso main_field
$manualIndex = 0; $manualIndex = 0;
foreach ($allMappings as $mapping) { foreach ($allMappings as $mapping) {
if ($mapping['is_manual'] && $mapping['main_field'] != 1) { if ($mapping['is_manual'] && $mapping['main_field'] != 1) {
@@ -478,7 +497,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
if ($mapping['is_required']) $inputClass .= ' required-input'; if ($mapping['is_required']) $inputClass .= ' required-input';
echo "<div class='grid-cell' style='flex: 0 0 150px;'>"; echo "<div class='grid-cell' style='flex: 0 0 150px;'>";
if ($mapping['data_type'] === 'SceltaMultipla') { if ($mapping['data_type'] === 'SceltaMultipla') {
echo "<select class='custom-field dropdown-select $inputClass' data-column='manual_$manualIndex' data-field-id='{$mapping['field_id']}' " . ($mapping['is_required'] ? 'required' : '') . ">"; echo "<select class='custom-field dropdown-select $inputClass' data-column='manual_$manualIndex' data-field-id='{$mapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mainFieldMapping['is_required'] ? 'required' : '') . ">";
echo "<option value=''>Seleziona...</option>"; echo "<option value=''>Seleziona...</option>";
echo "</select>"; echo "</select>";
} elseif ($mapping['data_type'] === 'DATE') { } elseif ($mapping['data_type'] === 'DATE') {
@@ -493,15 +512,14 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$manualIndex++; $manualIndex++;
} }
} }
echo "<div class='grid-cell' style='flex: 0 0 200px;'></div>"; // AWB echo "<div class='grid-cell' style='flex: 0 0 200px;'></div>";
echo "<div class='grid-cell' style='flex: 0 0 250px;'></div>"; // Tracking Info echo "<div class='grid-cell' style='flex: 0 0 250px;'></div>";
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>"; // importreferencecode echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>"; // filename_import echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>"; // importdate echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
?> ?>
</div> </div>
<!-- Header della tabella -->
<div class="grid-row"> <div class="grid-row">
<div class="grid-header button-header" style="flex: 0 0 210px;">Actions</div> <div class="grid-header button-header" style="flex: 0 0 210px;">Actions</div>
<?php if ($mainFieldMapping): ?> <?php if ($mainFieldMapping): ?>
@@ -538,10 +556,10 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
?> ?>
</div> </div>
<!-- Righe della tabella -->
<?php foreach ($importedData as $index => $row): ?> <?php foreach ($importedData as $index => $row): ?>
<div class="grid-row" data-id="<?= $row['iddatadb'] ?>"> <div class="grid-row" data-id="<?= $row['iddatadb'] ?>">
<div class="grid-cell button-cell" style="flex: 0 0 210px; position: relative;"> <div class="grid-cell button-cell" style="flex: 0 0 210px; position: relative;">
<button type="button" class="export-lims-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" title="Export to LIMS" style="background: #eb0b0bff; color: white; border: none; border-radius: 5px; cursor: pointer;"><i class="fas fa-upload"></i></button>
<button type="button" class="save-btn action-btn" data-row="<?= $index ?>" title="Save" style="background: #28a745; color: white; border: none; border-radius: 5px; cursor: pointer;"><i class="fas fa-save"></i></button> <button type="button" class="save-btn action-btn" data-row="<?= $index ?>" title="Save" style="background: #28a745; color: white; border: none; border-radius: 5px; cursor: pointer;"><i class="fas fa-save"></i></button>
<button type="button" class="photos-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" title="Photos" style="background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer;"><i class="fas fa-camera"></i></button> <button type="button" class="photos-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" title="Photos" style="background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer;"><i class="fas fa-camera"></i></button>
<button type="button" class="parts-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" title="Parts" style="background: #ffc107; color: white; border: none; border-radius: 5px; cursor: pointer;"><i class="fas fa-puzzle-piece"></i></button> <button type="button" class="parts-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" title="Parts" style="background: #ffc107; color: white; border: none; border-radius: 5px; cursor: pointer;"><i class="fas fa-puzzle-piece"></i></button>
@@ -560,7 +578,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
?> ?>
<div class="grid-cell editable-cell <?= $requiredClass ?>" data-col="main_field" data-row="<?= $index ?>" data-index="1" style="flex: 0 0 150px;"> <div class="grid-cell editable-cell <?= $requiredClass ?>" data-col="main_field" data-row="<?= $index ?>" data-index="1" style="flex: 0 0 150px;">
<?php if ($mainFieldMapping['data_type'] === 'SceltaMultipla'): ?> <?php if ($mainFieldMapping['data_type'] === 'SceltaMultipla'): ?>
<select name="rows[<?= $index ?>][details][<?= $mainFieldMapping['id'] ?>][field_value]" class="cell-input dropdown-select <?= $inputClass ?>" data-mapping-id="<?= $mainFieldMapping['id'] ?>" data-field-id="<?= $mainFieldMapping['field_id'] ?>" <?= $mainFieldMapping['is_required'] ? 'required' : '' ?>> <select name="rows[<?= $index ?>][details][<?= $mainFieldMapping['id'] ?>][field_value]" class="cell-input dropdown-select <?= $inputClass ?>" data-mapping-id="<?= $mainFieldMapping['id'] ?>" data-field-id="<?= $mainFieldMapping['field_id'] ?>" data-selected-value="<?= htmlspecialchars($fieldValue) ?>" <?= $mainFieldMapping['is_required'] ? 'required' : '' ?>>
<option value="">Seleziona...</option> <option value="">Seleziona...</option>
</select> </select>
<?php elseif ($mainFieldMapping['data_type'] === 'DATE'): ?> <?php elseif ($mainFieldMapping['data_type'] === 'DATE'): ?>
@@ -592,7 +610,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
if ($mapping['is_required']) $inputClass .= ' required-input'; if ($mapping['is_required']) $inputClass .= ' required-input';
echo "<div class='grid-cell editable-cell $requiredClass' data-col='auto_$autoIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>"; echo "<div class='grid-cell editable-cell $requiredClass' data-col='auto_$autoIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
if ($mapping['data_type'] === 'SceltaMultipla') { if ($mapping['data_type'] === 'SceltaMultipla') {
echo "<select name='rows[$index][details][{$mapping['id']}][field_value]' class='cell-input dropdown-select $inputClass' data-mapping-id='{$mapping['id']}' data-field-id='{$mapping['field_id']}' " . ($mapping['is_required'] ? 'required' : '') . ">"; echo "<select name='rows[$index][details][{$mapping['id']}][field_value]' class='cell-input dropdown-select $inputClass' data-mapping-id='{$mapping['id']}' data-field-id='{$mapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
echo "<option value=''>Seleziona...</option>"; echo "<option value=''>Seleziona...</option>";
echo "</select>"; echo "</select>";
} elseif ($mapping['data_type'] === 'DATE') { } elseif ($mapping['data_type'] === 'DATE') {
@@ -621,7 +639,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
if ($mapping['is_required']) $inputClass .= ' required-input'; if ($mapping['is_required']) $inputClass .= ' required-input';
echo "<div class='grid-cell editable-cell $requiredClass' data-col='manual_$manualIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>"; echo "<div class='grid-cell editable-cell $requiredClass' data-col='manual_$manualIndex' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
if ($mapping['data_type'] === 'SceltaMultipla') { if ($mapping['data_type'] === 'SceltaMultipla') {
echo "<select name='rows[$index][details][{$mapping['id']}][field_value]' class='cell-input dropdown-select $inputClass' data-mapping-id='{$mapping['id']}' data-field-id='{$mapping['field_id']}' " . ($mapping['is_required'] ? 'required' : '') . ">"; echo "<select name='rows[$index][details][{$mapping['id']}][field_value]' class='cell-input dropdown-select $inputClass' data-mapping-id='{$mapping['id']}' data-field-id='{$mapping['field_id']}' data-selected-value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
echo "<option value=''>Seleziona...</option>"; echo "<option value=''>Seleziona...</option>";
echo "</select>"; echo "</select>";
} elseif ($mapping['data_type'] === 'DATE') { } elseif ($mapping['data_type'] === 'DATE') {
@@ -686,101 +704,95 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
</div> </div>
<?php include('jsinclude.php'); ?> <?php include('jsinclude.php'); ?>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
<script src="photos.js"></script> <script src="photos.js"></script>
<script src="parts.js"></script> <script src="parts.js"></script>
<script src="tracking.js"></script> <script src="tracking.js"></script>
<script> <script>
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {
console.log("Page loaded, initializing event listeners");
const inputs = document.querySelectorAll(".cell-input, .dropdown-select, .carrier-select, .awb-input"); const inputs = document.querySelectorAll(".cell-input, .dropdown-select, .carrier-select, .awb-input");
const unsavedDiv = document.getElementById("unsavedChanges"); const unsavedDiv = document.getElementById("unsavedChanges");
const changedList = document.getElementById("changedFields");
let hasChanges = false; let hasChanges = false;
let changedFields = {};
function renderChangedList() {
console.log("Rendering changed fields list:", changedFields);
changedList.innerHTML = "";
Object.keys(changedFields).forEach(rowIndex => {
const fields = changedFields[rowIndex];
if (fields.length > 0) {
const li = document.createElement("li");
li.textContent = `Row ${parseInt(rowIndex) + 1}: ${fields.join(", ")}`;
changedList.appendChild(li);
}
});
unsavedDiv.style.display = Object.keys(changedFields).length > 0 ? "block" : "none";
}
inputs.forEach(el => { inputs.forEach(el => {
el.addEventListener("change", () => { el.addEventListener("change", () => {
console.log("Input changed:", el.name);
hasChanges = true; hasChanges = true;
unsavedDiv.style.display = "block"; const gridCell = el.closest(".grid-cell");
const colIndex = gridCell?.dataset.index;
const rowIndex = gridCell?.dataset.row;
let label = "Unknown field";
if (colIndex) {
const header = document.querySelector(`.grid-header[data-index="${colIndex}"]`);
if (header) {
label = header.textContent.replace(":", "").trim();
}
}
if (rowIndex !== undefined) {
if (!changedFields[rowIndex]) {
changedFields[rowIndex] = [];
}
if (!changedFields[rowIndex].includes(label)) {
changedFields[rowIndex].push(label);
}
gridCell.classList.add("cell-changed");
}
renderChangedList();
}); });
}); });
document.querySelectorAll(".save-btn").forEach(btn => { document.querySelectorAll(".save-btn").forEach(btn => {
btn.addEventListener("click", () => { btn.addEventListener("click", () => {
hasChanges = false; const rowIndex = btn.dataset.row;
unsavedDiv.style.display = "none"; console.log(`Saving row ${rowIndex}`);
}); const row = btn.closest('.grid-row');
});
window.addEventListener("beforeunload", function(e) {
if (hasChanges) {
e.preventDefault();
e.returnValue = "";
}
});
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
const inputs = document.querySelectorAll('.cell-input');
inputs.forEach(input => {
input.addEventListener('focus', function() {
this.closest('.grid-cell').classList.add('expanded');
});
input.addEventListener('blur', function() {
this.closest('.grid-cell').classList.remove('expanded');
});
});
const saveButtons = document.querySelectorAll('.save-btn');
saveButtons.forEach(button => {
btn.addEventListener('click', function() {
hasChanges = false;
unsavedDiv.style.display = "none";
});
});
window.addEventListener("beforeunload", function(e) {
if (hasChanges) {
e.preventDefault();
e.returnValue = "";
}
});
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
const inputs = document.querySelectorAll('.cell-input');
inputs.forEach(input => {
input.addEventListener('focus', function() {
this.closest('.grid-cell').classList.add('expanded');
});
input.addEventListener('blur', function() {
this.closest('.grid-cell').classList.remove('expanded');
});
});
const saveButtons = document.querySelectorAll('.save-btn');
saveButtons.forEach(button => {
button.addEventListener('click', function() {
const rowIndex = this.getAttribute('data-row');
const row = this.closest('.grid-row');
const iddatadb = row.getAttribute('data-id'); const iddatadb = row.getAttribute('data-id');
const formData = new FormData(); const formData = new FormData();
const inputs = row.querySelectorAll(`input[name^="rows[${rowIndex}]"], select[name^="rows[${rowIndex}]"]`); const inputs = row.querySelectorAll(`input[name^="rows[${rowIndex}][details]"], select[name^="rows[${rowIndex}][details]"]`);
inputs.forEach(input => { inputs.forEach(input => {
const name = input.name.replace(`rows[${rowIndex}]`, '').replace(/\[|\]/g, ''); const matches = input.name.match(/rows\[\d+\]\[details\]\[(\d+)\]\[field_value\]/);
formData.append(name, input.value); if (matches) {
const mappingId = matches[1];
formData.append(`details${mappingId}field_value`, input.value);
if (input.tagName === 'SELECT' && input.classList.contains('dropdown-select')) {
input.setAttribute('data-selected-value', input.value);
}
}
}); });
formData.append('iddatadb', iddatadb); formData.append('iddatadb', iddatadb);
formData.append('mapping', JSON.stringify(<?= json_encode($allMappings) ?>));
fetch('save_edited_row.php', { fetch('save_edited_row.php', {
method: 'POST', method: 'POST',
body: formData body: formData
}) })
.then(response => response.json()) .then(response => {
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.json();
})
.then(data => { .then(data => {
console.log("Save response:", data);
if (data.success) { if (data.success) {
const cells = row.querySelectorAll('.grid-cell'); const cells = row.querySelectorAll('.grid-cell');
cells.forEach(cell => { cells.forEach(cell => {
@@ -789,18 +801,130 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
cell.classList.add('flash-success'); cell.classList.add('flash-success');
}); });
setTimeout(() => cells.forEach(cell => cell.classList.remove('flash-success')), 500); setTimeout(() => cells.forEach(cell => cell.classList.remove('flash-success')), 500);
alert('Salvataggio avvenuto con successo!');
if (changedFields[rowIndex]) {
delete changedFields[rowIndex];
document.querySelectorAll(`.grid-cell[data-row="${rowIndex}"]`)
.forEach(cell => cell.classList.remove("cell-changed"));
renderChangedList();
hasChanges = Object.keys(changedFields).length > 0;
}
alert('Salvataggio riga avvenuto con successo!');
} else { } else {
alert('Errore durante il salvataggio: ' + data.message); alert('Errore durante il salvataggio: ' + data.message);
} }
}) })
.catch(error => alert('Errore durante il salvataggio: ' + error.message)); .catch(error => {
console.error("Save error:", error);
alert('Errore durante il salvataggio: ' + error.message);
});
});
});
document.querySelector('.save-all-btn').addEventListener('click', async () => {
console.log("Saving all rows");
const rows = document.querySelectorAll('.grid-row');
let successCount = 0;
let errorMessages = [];
for (const row of rows) {
const saveBtn = row.querySelector('.save-btn');
if (!saveBtn) {
console.warn(`No save button found in row with data-id: ${row.getAttribute('data-id')}`);
continue;
}
const rowIndex = saveBtn.dataset.row;
const iddatadb = row.getAttribute('data-id');
if (!rowIndex || !iddatadb) {
console.warn(`Missing rowIndex or iddatadb in row:`, row);
continue;
}
console.log(`Processing row ${rowIndex} with iddatadb ${iddatadb}`);
const formData = new FormData();
const inputs = row.querySelectorAll(`input[name^="rows[${rowIndex}][details]"], select[name^="rows[${rowIndex}][details]"]`);
inputs.forEach(input => {
const matches = input.name.match(/rows\[\d+\]\[details\]\[(\d+)\]\[field_value\]/);
if (matches) {
const mappingId = matches[1];
formData.append(`details${mappingId}field_value`, input.value);
if (input.tagName === 'SELECT' && input.classList.contains('dropdown-select')) {
input.setAttribute('data-selected-value', input.value);
}
}
});
formData.append('iddatadb', iddatadb);
try {
const response = await fetch('save_edited_row.php', {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const data = await response.json();
console.log(`Row ${rowIndex} save response:`, data);
if (data.success) {
successCount++;
const cells = row.querySelectorAll('.grid-cell');
cells.forEach(cell => {
cell.classList.remove('flash-success');
void cell.offsetWidth;
cell.classList.add('flash-success');
});
setTimeout(() => cells.forEach(cell => cell.classList.remove('flash-success')), 500);
if (changedFields[rowIndex]) {
delete changedFields[rowIndex];
document.querySelectorAll(`.grid-cell[data-row="${rowIndex}"]`)
.forEach(cell => cell.classList.remove("cell-changed"));
}
} else {
errorMessages.push(`Riga ${parseInt(rowIndex) + 1}: ${data.message}`);
}
} catch (error) {
console.error(`Row ${rowIndex} save error:`, error);
errorMessages.push(`Riga ${parseInt(rowIndex) + 1}: ${error.message}`);
}
}
renderChangedList();
hasChanges = Object.keys(changedFields).length > 0;
console.log(`Save all completed: ${successCount} successes, ${errorMessages.length} errors`);
if (errorMessages.length === 0) {
alert(`Tutte le ${successCount} righe salvate con successo!`);
} else {
alert(`Salvate ${successCount} righe con successo.\nErrori:\n${errorMessages.join('\n')}`);
}
});
window.addEventListener("beforeunload", function(e) {
if (hasChanges) {
e.preventDefault();
e.returnValue = "";
}
});
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
console.log("Initializing cell expansion and propagation");
const inputs = document.querySelectorAll('.cell-input');
inputs.forEach(input => {
input.addEventListener('focus', function() {
this.closest('.grid-cell').classList.add('expanded');
});
input.addEventListener('blur', function() {
this.closest('.grid-cell').classList.remove('expanded');
}); });
}); });
const propagateButtons = document.querySelectorAll('.propagate-btn'); const propagateButtons = document.querySelectorAll('.propagate-btn');
propagateButtons.forEach(button => { propagateButtons.forEach(button => {
button.addEventListener('click', function() { button.addEventListener('click', function() {
console.log("Propagating value for column:", this.getAttribute('data-column'));
const column = this.getAttribute('data-column'); const column = this.getAttribute('data-column');
const input = this.previousElementSibling; const input = this.previousElementSibling;
const value = input.value; const value = input.value;
@@ -817,9 +941,12 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
if (cells.length > targetTopIndex) { if (cells.length > targetTopIndex) {
const targetInput = cells[targetTopIndex].querySelector('input, select'); const targetInput = cells[targetTopIndex].querySelector('input, select');
if (targetInput) { if (targetInput) {
if (targetInput.type === 'date') targetInput.value = value; targetInput.value = value;
else if (targetInput.tagName === 'SELECT') targetInput.value = value; if (targetInput.tagName === 'SELECT') {
else targetInput.value = value; targetInput.setAttribute('data-selected-value', value);
const event = new Event('change');
targetInput.dispatchEvent(event);
}
} }
} }
}); });
@@ -834,6 +961,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
let columnIndex = 0; let columnIndex = 0;
resizers.forEach(resizer => { resizers.forEach(resizer => {
resizer.addEventListener('mousedown', function(e) { resizer.addEventListener('mousedown', function(e) {
console.log("Starting column resize");
currentResizer = resizer; currentResizer = resizer;
const header = resizer.parentElement; const header = resizer.parentElement;
columnIndex = header.getAttribute('data-index'); columnIndex = header.getAttribute('data-index');
@@ -856,6 +984,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
function stopResize() { function stopResize() {
if (currentResizer) { if (currentResizer) {
console.log("Stopping column resize");
document.removeEventListener('mousemove', resize); document.removeEventListener('mousemove', resize);
document.removeEventListener('mouseup', stopResize); document.removeEventListener('mouseup', stopResize);
currentResizer = null; currentResizer = null;
@@ -866,11 +995,15 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
</script> </script>
<script> <script>
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {
console.log("Initializing dropdown population");
const dropdownData = {}; const dropdownData = {};
async function populateDropdowns() { async function populateDropdowns() {
const dropdowns = document.querySelectorAll('.dropdown-select'); const dropdowns = document.querySelectorAll('.dropdown-select');
if (dropdowns.length === 0) return; if (dropdowns.length === 0) {
console.log("No dropdowns found");
return;
}
const uniqueFieldIds = [ const uniqueFieldIds = [
...new Set(Array.from(dropdowns).map(d => d.getAttribute('data-field-id'))) ...new Set(Array.from(dropdowns).map(d => d.getAttribute('data-field-id')))
@@ -879,28 +1012,34 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
const missingFieldIds = uniqueFieldIds.filter(fieldId => !dropdownData[fieldId]); const missingFieldIds = uniqueFieldIds.filter(fieldId => !dropdownData[fieldId]);
if (missingFieldIds.length > 0) { if (missingFieldIds.length > 0) {
console.log("Fetching dropdown data for field IDs:", missingFieldIds);
try { try {
const response = await fetch( const response = await fetch(
`get_customfield_values.php?field_ids=${missingFieldIds.join(",")}` `get_customfield_values.php?field_ids=${missingFieldIds.join(",")}`
); );
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const data = await response.json(); const data = await response.json();
if (data.error) { if (data.error) {
console.error("Errore fetch multiplo:", data.error); console.error("Fetch error:", data.error);
} else { } else {
for (const fieldId of Object.keys(data)) { for (const fieldId of Object.keys(data)) {
dropdownData[fieldId] = data[fieldId] || []; dropdownData[fieldId] = data[fieldId] || [];
} }
} }
} catch (error) { } catch (error) {
console.error("Errore generale nel fetch multiplo:", error); console.error("Fetch error:", error);
} }
} }
dropdowns.forEach(dropdown => { dropdowns.forEach(dropdown => {
const fieldId = dropdown.getAttribute('data-field-id'); const fieldId = dropdown.getAttribute('data-field-id');
const selectedValue = dropdown.getAttribute('data-selected-value') || '';
const currentValue = dropdown.value;
if (!fieldId || !dropdownData[fieldId]) { if (!fieldId || !dropdownData[fieldId]) {
dropdown.innerHTML = '<option value="">Errore nel caricamento</option>'; dropdown.innerHTML = '<option value="">Errore nel caricamento</option>';
console.warn(`No data for fieldId ${fieldId}`);
return; return;
} }
@@ -909,50 +1048,22 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
const option = document.createElement('option'); const option = document.createElement('option');
option.value = value.IdCustomFieldsValue; option.value = value.IdCustomFieldsValue;
option.textContent = value.Valore; option.textContent = value.Valore;
if (dropdown.value === option.value) option.selected = true; if (currentValue && currentValue === String(value.IdCustomFieldsValue)) {
option.selected = true;
} else if (!currentValue && selectedValue === String(value.IdCustomFieldsValue)) {
option.selected = true;
}
dropdown.appendChild(option); dropdown.appendChild(option);
}); });
if ((currentValue || selectedValue) && dropdown.value !== (currentValue || selectedValue)) {
dropdown.value = '';
console.warn(`Value ${currentValue || selectedValue} not found for fieldId ${fieldId}`);
}
}); });
} }
populateDropdowns(); populateDropdowns();
document.querySelectorAll('.save-btn').forEach(btn => {
btn.addEventListener('click', function() {
setTimeout(populateDropdowns, 100);
});
});
const propagateButtons = document.querySelectorAll('.propagate-btn');
propagateButtons.forEach(button => {
button.addEventListener('click', function() {
const column = this.getAttribute('data-column');
const input = this.previousElementSibling;
const value = input.value;
const gridTopCells = document.querySelector('.grid-top').querySelectorAll('.grid-cell');
const targetTopIndex = Array.from(gridTopCells).findIndex(cell =>
cell.querySelector('.propagate-btn[data-column="' + column + '"]')
);
if (targetTopIndex !== -1) {
const rows = document.querySelectorAll('.grid-row');
rows.forEach(row => {
const cells = row.querySelectorAll('.grid-cell');
if (cells.length > targetTopIndex) {
const targetInput = cells[targetTopIndex].querySelector('select.dropdown-select, input');
if (targetInput) {
targetInput.value = value;
if (targetInput.tagName === 'SELECT') {
const event = new Event('change');
targetInput.dispatchEvent(event);
}
}
}
});
}
});
});
}); });
</script> </script>
</body> </body>
+53 -28
View File
@@ -1,4 +1,4 @@
<!-- Modal --> <!-- Modal modificato con pulsante per riconoscimento vocale -->
<div class="modal fade" id="partsModal" tabindex="-1" aria-labelledby="partsModalLabel" aria-hidden="true"> <div class="modal fade" id="partsModal" tabindex="-1" aria-labelledby="partsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl" style="max-width: 80% !important; width: 80% !important;"> <div class="modal-dialog modal-xl" style="max-width: 80% !important; width: 80% !important;">
<div class="modal-content"> <div class="modal-content">
@@ -9,28 +9,36 @@
<div class="modal-body"> <div class="modal-body">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<h6>Elenco Parti</h6> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
<h6 style="margin: 0; white-space: nowrap;">Elenco Parti</h6>
<div style="display: flex; align-items: center;">
<input type="checkbox" id="showMixParts" name="showMixParts" style="margin-right: 5px;">
<label for="showMixParts" style="font-size: 0.9rem; margin-right: 10px;">Mix</label>
<button type="button" class="btn btn-info btn-sm" id="renumberPartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rinumera Parti</button>
<button type="button" class="btn btn-secondary btn-sm ms-2" id="toggleVoiceBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-microphone"></i> Voce</button>
</div>
</div>
<ul id="partsList" class="list-group"></ul> <ul id="partsList" class="list-group"></ul>
<table class="table table-striped table-sm mt-3" id="partsTable"> <table class="table table-striped table-sm mt-3" id="partsTable">
<thead> <thead>
<tr> <tr>
<th>Num. Parte</th> <th>Num. Parte</th>
<th>Descrizione Parte</th> <th>Descrizione Parte</th>
<th>Azioni</th> <th>Azioni</th>
</tr> </tr>
</thead> </thead>
<tbody id="partsTableBody"> <tbody id="partsTableBody">
<tr data-part-id="new"> <tr data-part-id="new">
<td><input type="number" class="form-control form-control-sm part-number" value="1" style="width: 80px;"></td> <td><input type="number" class="form-control form-control-sm part-number" value="1" style="width: 80px;"></td>
<td><input type="text" class="form-control form-control-sm part-description" placeholder="Inserisci descrizione" style="width: 100%;"></td> <td><input type="text" class="form-control form-control-sm part-description" placeholder="Inserisci descrizione" style="width: 100%;"></td>
<td> <td>
<button type="button" class="btn btn-success btn-sm add-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-plus fa-xs"></i></button> <button type="button" class="btn btn-success btn-sm add-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-plus fa-xs"></i></button>
<button type="button" class="btn btn-primary btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M</button> <button type="button" class="btn btn-primary btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M</button>
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash fa-xs"></i></button> <button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash fa-xs"></i></button>
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span> <span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span>
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span> <span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -50,7 +58,8 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-primary btn-sm" id="addDescriptionsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Aggiungi Lista Descrizioni</button> <button type="button" class="btn btn-primary btn-sm" id="addDescriptionsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Aggiungi Lista Descrizioni</button>
<button type="button" class="btn btn-danger btn-sm" id="removeAnnotationsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rimuovi Annotazioni</button> <button type="button" class="btn btn-danger btn-sm" id="removeAnnotationsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rimuovi Descrizioni</button>
<button type="button" class="btn btn-warning btn-sm" id="undoMarkerBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Undo Marker</button>
<button type="button" class="btn btn-success btn-sm" id="savePhotoBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Salva Foto con Nome</button> <button type="button" class="btn btn-success btn-sm" id="savePhotoBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Salva Foto con Nome</button>
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Chiudi</button> <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Chiudi</button>
</div> </div>
@@ -99,12 +108,21 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 5px 10px;
} }
#partsList .list-group-item:hover { #partsList .list-group-item:hover {
background-color: #f5f5f5; background-color: #f5f5f5;
} }
#partsList input[type="color"] {
width: 30px;
height: 24px;
padding: 0;
margin-left: 5px;
cursor: pointer;
}
.draggable-description { .draggable-description {
position: absolute; position: absolute;
background: rgba(255, 255, 255, 0.8); background: rgba(255, 255, 255, 0.8);
@@ -121,8 +139,6 @@
position: absolute; position: absolute;
width: 24px; width: 24px;
height: 24px; height: 24px;
background: rgba(255, 0, 0, 0.5);
border: 1px solid #ff0000;
border-radius: 50%; border-radius: 50%;
color: #ffffff; color: #ffffff;
text-align: center; text-align: center;
@@ -146,23 +162,32 @@
font-size: 0.8rem; font-size: 0.8rem;
} }
/* ნორმალური Save ღილაკი */ /* Normale Save button */
#savePhotoBtn { #savePhotoBtn {
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
} }
/* დაუმახსოვრებელი ცვლილებები */ /* Unsaved changes */
#savePhotoBtn.unsaved { #savePhotoBtn.unsaved {
background-color: #dc3545 !important; /* წითელი */ background-color: #dc3545 !important;
/* Rosso */
border-color: #dc3545 !important; border-color: #dc3545 !important;
color: white !important; color: white !important;
animation: pulse 1.2s infinite; animation: pulse 1.2s infinite;
} }
/* ლამაზი პულსაცია */ /* Animazione pulsante */
@keyframes pulse { @keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7); } 0% {
70% { box-shadow: 0 0 10px 15px rgba(220, 53, 69, 0); } box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7);
100% { box-shadow: 0 0 0 0 rgba(220, 53, 69, 0); } }
70% {
box-shadow: 0 0 10px 15px rgba(220, 53, 69, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0);
}
} }
</style> </style>
+556 -152
View File
File diff suppressed because it is too large Load Diff
+311 -69
View File
@@ -28,6 +28,7 @@ document.addEventListener("DOMContentLoaded", function () {
const webcamVideo = document.getElementById("webcamVideo"); const webcamVideo = document.getElementById("webcamVideo");
const captureBtn = document.getElementById("captureBtn"); const captureBtn = document.getElementById("captureBtn");
const closeWebcamBtn = document.getElementById("closeWebcamBtn"); const closeWebcamBtn = document.getElementById("closeWebcamBtn");
const webcamSelect = document.getElementById("webcamSelect");
let stream = null; let stream = null;
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
@@ -41,25 +42,116 @@ document.addEventListener("DOMContentLoaded", function () {
!webcamArea || !webcamArea ||
!webcamVideo || !webcamVideo ||
!captureBtn || !captureBtn ||
!closeWebcamBtn !closeWebcamBtn ||
!webcamSelect
) { ) {
console.error("Elementi webcam mancanti"); console.error("Elementi webcam mancanti", {
openWebcamBtn,
webcamArea,
webcamVideo,
captureBtn,
closeWebcamBtn,
webcamSelect,
});
return; return;
} }
// Apri la webcam // Funzione per avviare la webcam con un deviceId specifico
openWebcamBtn.addEventListener("click", async () => { async function startWebcam(deviceId = null) {
try { try {
stream = await navigator.mediaDevices.getUserMedia({ // Ferma il flusso video esistente, se presente
video: true, if (stream) {
}); stream.getTracks().forEach((track) => track.stop());
stream = null;
webcamVideo.srcObject = null;
}
// Configura i vincoli per getUserMedia
const constraints = {
video: deviceId ? { deviceId: { exact: deviceId } } : true,
};
// Avvia il flusso video
stream = await navigator.mediaDevices.getUserMedia(constraints);
webcamVideo.srcObject = stream; webcamVideo.srcObject = stream;
webcamArea.style.display = "block"; webcamArea.style.display = "block";
openWebcamBtn.style.display = "none"; openWebcamBtn.style.display = "none";
dropArea.style.display = "none"; dropArea.style.display = "none";
} catch (error) { } catch (error) {
console.error("Errore nell'accesso alla webcam:", error);
alert("Errore nell'accesso alla webcam: " + error.message); alert("Errore nell'accesso alla webcam: " + error.message);
webcamArea.style.display = "none";
openWebcamBtn.style.display = "block";
dropArea.style.display = "block";
} }
}
// Funzione per popolare il dropdown delle webcam
async function populateWebcamSelect() {
try {
// Richiedi i permessi per accedere ai dispositivi
await navigator.mediaDevices.getUserMedia({ video: true });
const devices = await navigator.mediaDevices.enumerateDevices();
const videoDevices = devices.filter(
(device) => device.kind === "videoinput",
);
// Svuota il dropdown
webcamSelect.innerHTML =
'<option value="">Select a webcam</option>';
// Popola il dropdown con le webcam disponibili
videoDevices.forEach((device) => {
const option = document.createElement("option");
option.value = device.deviceId;
option.text =
device.label || `Webcam ${webcamSelect.options.length}`;
webcamSelect.appendChild(option);
});
// Mostra il dropdown solo se ci sono più webcam
webcamSelect.style.display =
videoDevices.length > 1 ? "block" : "none";
// Avvia la webcam predefinita se ce n'è almeno una
if (videoDevices.length > 0) {
await startWebcam(videoDevices[0].deviceId);
} else {
alert("Nessuna webcam rilevata.");
webcamArea.style.display = "none";
openWebcamBtn.style.display = "block";
dropArea.style.display = "block";
}
} catch (error) {
console.error("Errore nel recupero dei dispositivi:", error);
alert("Errore nel recupero dei dispositivi: " + error.message);
webcamSelect.style.display = "none";
}
}
// Apri la webcam e popola il dropdown
openWebcamBtn.addEventListener("click", async () => {
await populateWebcamSelect();
});
// Gestisci il cambio della webcam selezionata
webcamSelect.addEventListener("change", async (e) => {
const deviceId = e.target.value;
if (deviceId) {
await startWebcam(deviceId);
}
});
// Chiudi la webcam
closeWebcamBtn.addEventListener("click", () => {
if (stream) {
stream.getTracks().forEach((track) => track.stop());
stream = null;
webcamVideo.srcObject = null;
}
webcamArea.style.display = "none";
openWebcamBtn.style.display = "block";
dropArea.style.display = "block";
}); });
// Cattura la foto // Cattura la foto
@@ -71,7 +163,6 @@ document.addEventListener("DOMContentLoaded", function () {
.getContext("2d") .getContext("2d")
.drawImage(webcamVideo, 0, 0, canvas.width, canvas.height); .drawImage(webcamVideo, 0, 0, canvas.width, canvas.height);
// Converti l'immagine in un file
canvas.toBlob(async (blob) => { canvas.toBlob(async (blob) => {
const file = new File( const file = new File(
[blob], [blob],
@@ -84,22 +175,62 @@ document.addEventListener("DOMContentLoaded", function () {
loader.style.display = "flex"; loader.style.display = "flex";
} }
await handleFiles([file], iddatadb); await handleFiles([file], iddatadb);
closeWebcam(); if (stream) {
}, "image/jpeg"); stream.getTracks().forEach((track) => track.stop());
}); stream = null;
webcamVideo.srcObject = null;
// Chiudi la webcam }
closeWebcamBtn.addEventListener("click", () => {
closeWebcam();
});
function closeWebcam() {
if (stream) {
stream.getTracks().forEach((track) => track.stop());
webcamVideo.srcObject = null;
webcamArea.style.display = "none"; webcamArea.style.display = "none";
openWebcamBtn.style.display = "block"; openWebcamBtn.style.display = "block";
dropArea.style.display = "block"; dropArea.style.display = "block";
}, "image/jpeg");
});
}
// Funzione per gestire il caricamento dei file
async function handleFiles(files, iddatadb) {
const loader = document.getElementById("loader");
if (!loader) {
console.error("Elemento loader non trovato");
return;
}
if (!files || files.length === 0) {
console.warn("Nessun file da caricare");
return;
}
for (const file of files) {
if (!file.type.startsWith("image/")) {
alert("Per favore, carica solo immagini!");
continue;
}
console.log("Inizio upload del file:", file.name);
loader.style.display = "flex";
const formData = new FormData();
formData.append("photo", file);
formData.append("iddatadb", iddatadb);
try {
const response = await fetch("upload_photo.php", {
method: "POST",
body: formData,
});
const result = await response.json();
if (result.success) {
console.log(
"Upload completato con successo, ricarico popup",
);
loadPopupContent(iddatadb);
} else {
alert("Errore durante il caricamento: " + result.message);
}
} catch (error) {
alert("Errore durante il caricamento: " + error.message);
} finally {
console.log("Nascondo loader dopo upload");
loader.style.display = "none";
} }
} }
} }
@@ -198,6 +329,9 @@ document.addEventListener("DOMContentLoaded", function () {
}); });
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
console.log(
"Foto eliminata con successo, ricarico popup",
);
loadPopupContent(iddatadb); loadPopupContent(iddatadb);
} else { } else {
alert( alert(
@@ -246,6 +380,162 @@ document.addEventListener("DOMContentLoaded", function () {
// Inizializza la gestione della webcam // Inizializza la gestione della webcam
setupWebcam(iddatadb); setupWebcam(iddatadb);
// Gestione bottone Crea Collage
const createCollageBtn = document.getElementById("createCollageBtn");
if (createCollageBtn) {
createCollageBtn.addEventListener("click", () => {
console.log("Apertura modale collage");
document.getElementById("collageModal").style.display = "block";
initCollageCanvas();
});
}
// Chiusura modale collage
const closeCollageBtn = document.querySelector(".close-collage");
if (closeCollageBtn) {
closeCollageBtn.addEventListener("click", () => {
console.log("Chiusura modale collage");
document.getElementById("collageModal").style.display = "none";
});
}
// Inizializza canvas con Fabric.js
let canvas;
function initCollageCanvas() {
if (typeof fabric === "undefined") {
console.error("Fabric.js non è caricato!");
alert(
"Errore: Fabric.js non è disponibile. Controlla la connessione al CDN.",
);
return;
}
canvas = new fabric.Canvas("collageCanvas", {
backgroundColor: "#fff",
selection: true,
});
// Abilita ridimensionamento e trascinamento
canvas.on("object:modified", () => canvas.renderAll());
}
// Aggiungi foto selezionate al canvas
const addToCanvasBtn = document.getElementById("addToCanvasBtn");
if (addToCanvasBtn) {
addToCanvasBtn.addEventListener("click", () => {
const checkboxes = document.querySelectorAll(
".photo-checkbox:checked",
);
if (checkboxes.length === 0) {
alert("Seleziona almeno una foto!");
return;
}
checkboxes.forEach((cb) => {
const imgPath = cb.getAttribute("data-path");
fabric.Image.fromURL(imgPath, (img) => {
img.set({
left: Math.random() * 600, // Posizione random iniziale
top: Math.random() * 400,
scaleX: 0.5, // Scala iniziale
scaleY: 0.5,
hasControls: true, // Abilita resize/rotate
hasBorders: true,
});
canvas.add(img);
canvas.renderAll();
});
});
// Deseleziona checkbox dopo aggiunta
checkboxes.forEach((cb) => (cb.checked = false));
});
}
// Salva collage
const saveCollageBtn = document.getElementById("saveCollageBtn");
if (saveCollageBtn) {
saveCollageBtn.addEventListener("click", async () => {
if (canvas.getObjects().length === 0) {
alert("Il canvas è vuoto! Aggiungi almeno una foto.");
return;
}
const dataURL = canvas.toDataURL({
format: "jpeg",
quality: 0.8,
});
const blob = await (await fetch(dataURL)).blob();
const file = new File([blob], `collage_${Date.now()}.jpg`, {
type: "image/jpeg",
});
// Upload come nuova foto
await handleFiles([file], iddatadb);
// Chiudi modale e ricarica popup
document.getElementById("collageModal").style.display = "none";
loadPopupContent(iddatadb);
});
}
// Pulisci canvas
const clearCanvasBtn = document.getElementById("clearCanvasBtn");
if (clearCanvasBtn) {
clearCanvasBtn.addEventListener("click", () => {
canvas.clear();
canvas.setBackgroundColor("#fff");
canvas.renderAll();
});
}
// Gestione livelli delle immagini
const bringToFrontBtn = document.getElementById("bringToFrontBtn");
if (bringToFrontBtn) {
bringToFrontBtn.addEventListener("click", () => {
const activeObject = canvas.getActiveObject();
if (activeObject) {
canvas.bringToFront(activeObject);
canvas.renderAll();
} else {
alert("Seleziona un'immagine sul canvas!");
}
});
}
const sendToBackBtn = document.getElementById("sendToBackBtn");
if (sendToBackBtn) {
sendToBackBtn.addEventListener("click", () => {
const activeObject = canvas.getActiveObject();
if (activeObject) {
canvas.sendToBack(activeObject);
canvas.renderAll();
} else {
alert("Seleziona un'immagine sul canvas!");
}
});
}
const bringForwardBtn = document.getElementById("bringForwardBtn");
if (bringForwardBtn) {
bringForwardBtn.addEventListener("click", () => {
const activeObject = canvas.getActiveObject();
if (activeObject) {
canvas.bringForward(activeObject);
canvas.renderAll();
} else {
alert("Seleziona un'immagine sul canvas!");
}
});
}
const sendBackwardBtn = document.getElementById("sendBackwardBtn");
if (sendBackwardBtn) {
sendBackwardBtn.addEventListener("click", () => {
const activeObject = canvas.getActiveObject();
if (activeObject) {
canvas.sendBackwards(activeObject);
canvas.renderAll();
} else {
alert("Seleziona un'immagine sul canvas!");
}
});
}
// Assicurati che il loader sia nascosto all'apertura del popup // Assicurati che il loader sia nascosto all'apertura del popup
const loader = document.getElementById("loader"); const loader = document.getElementById("loader");
if (loader) { if (loader) {
@@ -254,54 +544,6 @@ document.addEventListener("DOMContentLoaded", function () {
} }
} }
// Funzione per gestire il caricamento dei file
async function handleFiles(files, iddatadb) {
const loader = document.getElementById("loader");
if (!loader) {
console.error("Elemento loader non trovato");
return;
}
if (!files || files.length === 0) {
console.warn("Nessun file da caricare");
return;
}
for (const file of files) {
if (!file.type.startsWith("image/")) {
alert("Per favore, carica solo immagini!");
continue;
}
console.log("Inizio upload del file:", file.name);
loader.style.display = "flex";
const formData = new FormData();
formData.append("photo", file);
formData.append("iddatadb", iddatadb);
try {
const response = await fetch("upload_photo.php", {
method: "POST",
body: formData,
});
const result = await response.json();
if (result.success) {
console.log(
"Upload completato con successo, ricarico popup",
);
loadPopupContent(iddatadb);
} else {
alert("Errore durante il caricamento: " + result.message);
}
} catch (error) {
alert("Errore durante il caricamento: " + error.message);
} finally {
console.log("Nascondo loader dopo upload");
loader.style.display = "none";
}
}
}
// Gestione del pulsante Photos // Gestione del pulsante Photos
const photosButtons = document.querySelectorAll(".photos-btn"); const photosButtons = document.querySelectorAll(".photos-btn");
const photosModal = document.getElementById("photosModal"); const photosModal = document.getElementById("photosModal");
+43
View File
@@ -1,6 +1,10 @@
<?php <?php
// photos_popup.php // photos_popup.php
include('include/headscript.php'); include('include/headscript.php');
// Includi Fabric.js solo per questa pagina
?>
<?php
// Includi l'autoloader di Composer // Includi l'autoloader di Composer
require_once __DIR__ . '/../../vendor/autoload.php'; require_once __DIR__ . '/../../vendor/autoload.php';
@@ -118,6 +122,9 @@ $result->saveToFile($qrCodeFile);
<!-- Area per la webcam --> <!-- Area per la webcam -->
<div id="webcamArea" style="display: none; text-align: center; margin-bottom: 20px;"> <div id="webcamArea" style="display: none; text-align: center; margin-bottom: 20px;">
<p>Webcam Preview</p> <p>Webcam Preview</p>
<select id="webcamSelect" style="margin-bottom: 10px; padding: 5px;">
<option value="">Select a webcam</option>
</select>
<video id="webcamVideo" autoplay playsinline style="max-width: 100%; max-height: 300px;"></video> <video id="webcamVideo" autoplay playsinline style="max-width: 100%; max-height: 300px;"></video>
<div style="margin-top: 10px;"> <div style="margin-top: 10px;">
<button id="captureBtn" style="padding: 10px 20px; background: #28a745; color: white; border: none; cursor: pointer;">Capture Photo</button> <button id="captureBtn" style="padding: 10px 20px; background: #28a745; color: white; border: none; cursor: pointer;">Capture Photo</button>
@@ -153,6 +160,8 @@ $result->saveToFile($qrCodeFile);
</button> </button>
</div> </div>
<?php endforeach; ?> <?php endforeach; ?>
<!-- Bottone Crea Collage -->
<button id="createCollageBtn" style="padding: 10px 20px; background: #ffc107; color: white; border: none; cursor: pointer; margin-top: 20px;">Crea Collage</button>
<?php endif; ?> <?php endif; ?>
</div> </div>
@@ -161,6 +170,40 @@ $result->saveToFile($qrCodeFile);
<span class="image-modal-close">&times;</span> <span class="image-modal-close">&times;</span>
<img id="enlargedImage" class="image-modal-content" src="" alt="Immagine ingrandita"> <img id="enlargedImage" class="image-modal-content" src="" alt="Immagine ingrandita">
</div> </div>
<!-- Nuovo modale per collage -->
<div id="collageModal" class="modal" style="display: none; position: fixed; z-index: 1002; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.8);">
<div class="modal-content" style="background: white; margin: 5% auto; padding: 20px; width: 80%; max-width: 1200px; position: relative;">
<span class="close-collage" style="position: absolute; top: 10px; right: 20px; font-size: 30px; cursor: pointer;">&times;</span>
<h3>Crea Collage</h3>
<!-- Lista foto selezionabili -->
<div id="collagePhotoList" style="max-height: 200px; overflow-y: auto; margin-bottom: 20px;">
<?php foreach ($photos as $photo): ?>
<div style="display: inline-block; margin: 10px;">
<input type="checkbox" class="photo-checkbox" data-path="../photostrf/<?= htmlspecialchars($photo['file_path']) ?>">
<img src="../photostrf/<?= htmlspecialchars($photo['file_path']) ?>" alt="" style="width: 80px; height: 80px;">
</div>
<?php endforeach; ?>
</div>
<!-- Bottone per aggiungere selezionate al canvas -->
<button id="addToCanvasBtn">Aggiungi Selezionate al Canvas</button>
<!-- Canvas per editing -->
<canvas id="collageCanvas" width="800" height="600" style="border: 1px solid #ccc; margin-top: 20px;"></canvas>
<!-- Bottoni azioni -->
<div style="margin-top: 20px;">
<button id="saveCollageBtn">Salva Collage</button>
<button id="clearCanvasBtn">Pulisci Canvas</button>
<button id="bringToFrontBtn" title="Porta in primo piano">In Alto</button>
<button id="sendToBackBtn" title="Manda in fondo">In Fondo</button>
<button id="bringForwardBtn" title="Sposta avanti di un livello">Avanti</button>
<button id="sendBackwardBtn" title="Sposta indietro di un livello">Indietro</button>
</div>
</div>
</div>
</div> </div>
<style> <style>
+63
View File
@@ -0,0 +1,63 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$data = json_decode(file_get_contents('php://input'), true);
$iddatadb = $data['iddatadb'] ?? null;
$parts = $data['parts'] ?? [];
if (!$iddatadb || empty($parts)) {
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
exit;
}
try {
$pdo->beginTransaction();
// Elimina tutte le parti esistenti per l'iddatadb per evitare conflitti di unicità
$stmt = $pdo->prepare("DELETE FROM identification_parts WHERE iddatadb = :iddatadb");
$stmt->execute([':iddatadb' => $iddatadb]);
// Prepara l'inserimento delle nuove parti
$stmt = $pdo->prepare("
INSERT INTO identification_parts
(iddatadb, part_number, part_description, mix, created_at, updated_at)
VALUES (:iddatadb, :part_number, :part_description, :mix, NOW(), NOW())
");
$part_ids = [];
foreach ($parts as $part) {
$partNumber = $part['part_number'] ?? null;
$partDescription = $part['part_description'] ?? '';
$mix = $part['mix'] ?? 'N';
if (!$partNumber || !$partDescription) {
throw new PDOException("Numero parte o descrizione mancante per parte: " . json_encode($part));
}
$stmt->execute([
':iddatadb' => $iddatadb,
':part_number' => $partNumber,
':part_description' => $partDescription,
':mix' => $mix
]);
$part_ids[] = $pdo->lastInsertId();
}
$pdo->commit();
echo json_encode([
'success' => true,
'part_ids' => $part_ids,
'message' => 'Parti rinumerate con successo'
]);
} catch (PDOException $e) {
$pdo->rollBack();
echo json_encode([
'success' => false,
'message' => 'Errore nel salvataggio: ' . $e->getMessage()
]);
}