Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3aa2504f3c | |||
| c1a396f246 | |||
| a45ba1c8b3 | |||
| 7a944a73f7 | |||
| 71595cc8de | |||
| f89dbd0c23 | |||
| 9ba859e15b | |||
| 672e448e9a | |||
| 0749032fbc | |||
| d692614f70 | |||
| 1303cff9fd | |||
| 6b2bd0964b | |||
| 0d2cf13524 | |||
| f6ef9c39d2 |
@@ -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 11:59:10 GMT
|
||||||
<
|
<
|
||||||
* Connection #0 to host 93.43.5.102 left intact
|
* Connection #0 to host 93.43.5.102 left intact
|
||||||
|
|||||||
@@ -15,11 +15,11 @@
|
|||||||
* [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/CustomField(1083)?$expand=CustomFieldsValues]
|
||||||
* [HTTP/2] [1] [authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc1NTc3OTAyNSwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.bBV1z1uaxZeUuw-2YS2gLGaxTCQJAHTieM82KVJb5nw]
|
* [HTTP/2] [1] [authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc1NzMzOTk1MSwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.JywaxXVFGeXH44RCOZUPfDGuEr5dt0cHAWN_vmIiFaw]
|
||||||
* [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/CustomField(1083)?$expand=CustomFieldsValues HTTP/2
|
||||||
Host: 93.43.5.102
|
Host: 93.43.5.102
|
||||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc1NTc3OTAyNSwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.bBV1z1uaxZeUuw-2YS2gLGaxTCQJAHTieM82KVJb5nw
|
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc1NzMzOTk1MSwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.JywaxXVFGeXH44RCOZUPfDGuEr5dt0cHAWN_vmIiFaw
|
||||||
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 11:59: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
@@ -0,0 +1,23 @@
|
|||||||
|
# Swagger Codegen Ignore
|
||||||
|
# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen
|
||||||
|
|
||||||
|
# Use this file to prevent files from being overwritten by the generator.
|
||||||
|
# The patterns follow closely to .gitignore or .dockerignore.
|
||||||
|
|
||||||
|
# As an example, the C# client generator defines ApiClient.cs.
|
||||||
|
# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line:
|
||||||
|
#ApiClient.cs
|
||||||
|
|
||||||
|
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
||||||
|
#foo/*/qux
|
||||||
|
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
||||||
|
|
||||||
|
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
||||||
|
#foo/**/qux
|
||||||
|
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
||||||
|
|
||||||
|
# You can also negate patterns with an exclamation (!).
|
||||||
|
# For example, you can ignore all files in a docs folder with the file extension .md:
|
||||||
|
#docs/*.md
|
||||||
|
# Then explicitly reverse the ignore rule for a single file:
|
||||||
|
#!docs/README.md
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.0.34
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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
File diff suppressed because it is too large
Load Diff
+871
-69
@@ -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,722 @@ 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";
|
||||||
|
if (isCropping) {
|
||||||
|
console.log(
|
||||||
|
"Chiusura modale durante ritaglio, esco dalla modalità ritaglio",
|
||||||
|
);
|
||||||
|
exitCropMode();
|
||||||
|
}
|
||||||
|
if (isRemovingBackground) {
|
||||||
|
console.log(
|
||||||
|
"Chiusura modale durante rimozione sfondo, esco dalla modalità",
|
||||||
|
);
|
||||||
|
exitBackgroundRemovalMode();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inizializza canvas con Fabric.js
|
||||||
|
let canvas;
|
||||||
|
let cropRect = null;
|
||||||
|
let isCropping = false;
|
||||||
|
let croppedImage = null; // Memorizza l'immagine da ritagliare
|
||||||
|
let isApplyingCrop = false; // Flag per prevenire duplicazioni
|
||||||
|
let isRemovingBackground = false; // Flag per modalità rimozione sfondo
|
||||||
|
let backgroundRemovalImage = null; // Immagine in modalità rimozione sfondo
|
||||||
|
let history = []; // Pila per salvare gli stati del canvas
|
||||||
|
const maxHistory = 20; // Limite massimo di stati nella pila
|
||||||
|
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
// Imposta stile globale per i controlli
|
||||||
|
fabric.Object.prototype.set({
|
||||||
|
cornerColor: "black",
|
||||||
|
cornerStrokeColor: "black",
|
||||||
|
cornerSize: 12,
|
||||||
|
borderColor: "black",
|
||||||
|
transparentCorners: false,
|
||||||
|
});
|
||||||
|
// Abilita ridimensionamento e trascinamento
|
||||||
|
canvas.on("object:modified", () => {
|
||||||
|
console.log("Oggetto modificato nel canvas");
|
||||||
|
saveCanvasState();
|
||||||
|
canvas.renderAll();
|
||||||
|
});
|
||||||
|
// Gestisci selezione per abilitare/disabilitare pulsanti
|
||||||
|
canvas.on("selection:created", () => {
|
||||||
|
console.log("Evento selection:created triggerato");
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
canvas.on("selection:updated", () => {
|
||||||
|
console.log("Evento selection:updated triggerato");
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
canvas.on("selection:cleared", () => {
|
||||||
|
console.log("Evento selection:cleared triggerato");
|
||||||
|
if (!isCropping && !isRemovingBackground) {
|
||||||
|
updateButtons();
|
||||||
|
} else if (isCropping && cropRect) {
|
||||||
|
console.log(
|
||||||
|
"Ignoro selection:cleared perché in modalità ritaglio",
|
||||||
|
);
|
||||||
|
canvas.setActiveObject(cropRect); // Ripristina selezione del rettangolo
|
||||||
|
canvas.renderAll();
|
||||||
|
} else if (isRemovingBackground) {
|
||||||
|
console.log(
|
||||||
|
"Ignoro selection:cleared perché in modalità rimozione sfondo",
|
||||||
|
);
|
||||||
|
canvas.setActiveObject(backgroundRemovalImage); // Ripristina selezione dell'immagine
|
||||||
|
canvas.renderAll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Gestisci click sul canvas per la rimozione dello sfondo
|
||||||
|
canvas.on("mouse:down", (event) => {
|
||||||
|
if (isRemovingBackground && backgroundRemovalImage) {
|
||||||
|
console.log(
|
||||||
|
"Click sul canvas in modalità rimozione sfondo",
|
||||||
|
);
|
||||||
|
handleBackgroundColorSelection(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Salva lo stato iniziale del canvas
|
||||||
|
saveCanvasState();
|
||||||
|
// Forza un aggiornamento iniziale dei pulsanti
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salva lo stato del canvas nella pila
|
||||||
|
function saveCanvasState() {
|
||||||
|
if (isCropping || isRemovingBackground) {
|
||||||
|
console.log(
|
||||||
|
"Non salvo lo stato perché in modalità ritaglio o rimozione sfondo",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("Salvataggio stato del canvas");
|
||||||
|
const state = JSON.stringify(
|
||||||
|
canvas.toJSON([
|
||||||
|
"cornerColor",
|
||||||
|
"cornerStrokeColor",
|
||||||
|
"cornerSize",
|
||||||
|
"borderColor",
|
||||||
|
"transparentCorners",
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
history.push(state);
|
||||||
|
if (history.length > maxHistory) {
|
||||||
|
history.shift(); // Rimuovi lo stato più vecchio se superato il limite
|
||||||
|
}
|
||||||
|
console.log("Stato salvato, lunghezza pila:", history.length);
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ripristina l'ultimo stato del canvas
|
||||||
|
function undo() {
|
||||||
|
if (history.length <= 1) {
|
||||||
|
console.log("Nessuno stato da annullare");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("Annullamento ultima azione");
|
||||||
|
history.pop(); // Rimuovi lo stato corrente
|
||||||
|
const previousState = history[history.length - 1];
|
||||||
|
if (previousState) {
|
||||||
|
canvas.clear();
|
||||||
|
canvas.loadFromJSON(previousState, () => {
|
||||||
|
canvas.renderAll();
|
||||||
|
console.log("Stato ripristinato");
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn("Nessuno stato precedente disponibile");
|
||||||
|
canvas.clear();
|
||||||
|
canvas.setBackgroundColor("#fff");
|
||||||
|
canvas.renderAll();
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggiorna stato dei pulsanti
|
||||||
|
function updateButtons() {
|
||||||
|
const cropBtn = document.getElementById("cropImageBtn");
|
||||||
|
const applyCropBtn = document.getElementById("applyCropBtn");
|
||||||
|
const cancelCropBtn = document.getElementById("cancelCropBtn");
|
||||||
|
const removeBackgroundBtn = document.getElementById(
|
||||||
|
"removeBackgroundBtn",
|
||||||
|
);
|
||||||
|
const removeImageBtn = document.getElementById("removeImageBtn");
|
||||||
|
const undoBtn = document.getElementById("undoBtn");
|
||||||
|
const instruction = document.getElementById(
|
||||||
|
"backgroundRemovalInstruction",
|
||||||
|
);
|
||||||
|
const activeObject = canvas.getActiveObject();
|
||||||
|
console.log(
|
||||||
|
"updateButtons: activeObject =",
|
||||||
|
activeObject ? activeObject.type : null,
|
||||||
|
"isCropping =",
|
||||||
|
isCropping,
|
||||||
|
"isRemovingBackground =",
|
||||||
|
isRemovingBackground,
|
||||||
|
"history.length =",
|
||||||
|
history.length,
|
||||||
|
);
|
||||||
|
if (isCropping && cropRect) {
|
||||||
|
console.log(
|
||||||
|
"Modo ritaglio attivo, applyCropBtn e cancelCropBtn abilitati",
|
||||||
|
);
|
||||||
|
cropBtn.disabled = true;
|
||||||
|
applyCropBtn.disabled = false;
|
||||||
|
cancelCropBtn.disabled = false;
|
||||||
|
removeBackgroundBtn.disabled = true;
|
||||||
|
removeImageBtn.disabled = true;
|
||||||
|
undoBtn.disabled = true;
|
||||||
|
instruction.style.display = "none";
|
||||||
|
} else if (isRemovingBackground && backgroundRemovalImage) {
|
||||||
|
console.log(
|
||||||
|
"Modo rimozione sfondo attivo, removeBackgroundBtn disabilitato",
|
||||||
|
);
|
||||||
|
cropBtn.disabled = true;
|
||||||
|
applyCropBtn.disabled = true;
|
||||||
|
cancelCropBtn.disabled = true;
|
||||||
|
removeBackgroundBtn.disabled = true;
|
||||||
|
removeImageBtn.disabled = true;
|
||||||
|
undoBtn.disabled = true;
|
||||||
|
instruction.style.display = "block";
|
||||||
|
} else if (
|
||||||
|
activeObject &&
|
||||||
|
activeObject.type === "image" &&
|
||||||
|
!isCropping &&
|
||||||
|
!isRemovingBackground
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
"Abilitazione cropImageBtn, removeBackgroundBtn e removeImageBtn",
|
||||||
|
);
|
||||||
|
cropBtn.disabled = false;
|
||||||
|
applyCropBtn.disabled = true;
|
||||||
|
cancelCropBtn.disabled = true;
|
||||||
|
removeBackgroundBtn.disabled = false;
|
||||||
|
removeImageBtn.disabled = false;
|
||||||
|
undoBtn.disabled = history.length <= 1;
|
||||||
|
instruction.style.display = "none";
|
||||||
|
} else {
|
||||||
|
console.log("Disabilitazione tutti i pulsanti");
|
||||||
|
cropBtn.disabled = true;
|
||||||
|
applyCropBtn.disabled = true;
|
||||||
|
cancelCropBtn.disabled = true;
|
||||||
|
removeBackgroundBtn.disabled = true;
|
||||||
|
removeImageBtn.disabled = true;
|
||||||
|
undoBtn.disabled = history.length <= 1;
|
||||||
|
instruction.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entra in modalità ritaglio
|
||||||
|
function enterCropMode() {
|
||||||
|
const activeObject = canvas.getActiveObject();
|
||||||
|
if (!activeObject || activeObject.type !== "image") {
|
||||||
|
console.warn("Nessuna immagine selezionata per il ritaglio");
|
||||||
|
alert("Seleziona un'immagine prima di attivare il ritaglio!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(
|
||||||
|
"Entrata in modalità ritaglio per immagine:",
|
||||||
|
activeObject,
|
||||||
|
);
|
||||||
|
isCropping = true;
|
||||||
|
croppedImage = activeObject; // Memorizza l'immagine da ritagliare
|
||||||
|
canvas.discardActiveObject(); // Deseleziona l'immagine
|
||||||
|
// Crea un rettangolo di ritaglio
|
||||||
|
cropRect = new fabric.Rect({
|
||||||
|
left: activeObject.left,
|
||||||
|
top: activeObject.top,
|
||||||
|
width: activeObject.width * activeObject.scaleX * 0.5,
|
||||||
|
height: activeObject.height * activeObject.scaleY * 0.5,
|
||||||
|
fill: "rgba(0, 0, 0, 0.3)",
|
||||||
|
stroke: "red",
|
||||||
|
strokeWidth: 2,
|
||||||
|
hasBorders: true,
|
||||||
|
hasControls: true,
|
||||||
|
lockRotation: true,
|
||||||
|
selectable: true,
|
||||||
|
cornerColor: "black",
|
||||||
|
cornerStrokeColor: "black",
|
||||||
|
cornerSize: 12,
|
||||||
|
borderColor: "black",
|
||||||
|
transparentCorners: false,
|
||||||
|
});
|
||||||
|
canvas.add(cropRect);
|
||||||
|
canvas.setActiveObject(cropRect);
|
||||||
|
canvas.renderAll();
|
||||||
|
updateButtons();
|
||||||
|
console.log("Rettangolo di ritaglio creato e applicato");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Esci dalla modalità ritaglio
|
||||||
|
function exitCropMode() {
|
||||||
|
if (cropRect) {
|
||||||
|
console.log("Rimozione rettangolo di ritaglio");
|
||||||
|
canvas.remove(cropRect);
|
||||||
|
cropRect = null;
|
||||||
|
}
|
||||||
|
isCropping = false;
|
||||||
|
croppedImage = null;
|
||||||
|
isApplyingCrop = false;
|
||||||
|
canvas.discardActiveObject();
|
||||||
|
canvas.renderAll();
|
||||||
|
updateButtons();
|
||||||
|
console.log("Uscita dalla modalità ritaglio");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applica il ritaglio
|
||||||
|
function applyCrop() {
|
||||||
|
if (isApplyingCrop) {
|
||||||
|
console.log("applyCrop già in esecuzione, ignoro chiamata");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("applyCrop chiamato, stato:", {
|
||||||
|
isCropping,
|
||||||
|
cropRect: !!cropRect,
|
||||||
|
croppedImage: !!croppedImage,
|
||||||
|
});
|
||||||
|
if (!isCropping || !cropRect || !croppedImage) {
|
||||||
|
console.warn("Condizioni per il ritaglio non soddisfatte", {
|
||||||
|
isCropping,
|
||||||
|
cropRect: !!cropRect,
|
||||||
|
croppedImage: !!croppedImage,
|
||||||
|
});
|
||||||
|
alert(
|
||||||
|
"Nessun rettangolo di ritaglio attivo o immagine selezionata!",
|
||||||
|
);
|
||||||
|
exitCropMode();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isApplyingCrop = true;
|
||||||
|
console.log("Applicazione ritaglio all'immagine:", croppedImage);
|
||||||
|
const img = croppedImage;
|
||||||
|
const cropX = (cropRect.left - img.left) / img.scaleX;
|
||||||
|
const cropY = (cropRect.top - img.top) / img.scaleY;
|
||||||
|
const cropWidth = (cropRect.width * cropRect.scaleX) / img.scaleX;
|
||||||
|
const cropHeight = (cropRect.height * cropRect.scaleY) / img.scaleY;
|
||||||
|
console.log("Parametri di ritaglio:", {
|
||||||
|
cropX,
|
||||||
|
cropY,
|
||||||
|
cropWidth,
|
||||||
|
cropHeight,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Crea un'immagine ritagliata
|
||||||
|
fabric.Image.fromURL(
|
||||||
|
img.getSrc(),
|
||||||
|
(newImg) => {
|
||||||
|
newImg.set({
|
||||||
|
left: cropRect.left,
|
||||||
|
top: cropRect.top,
|
||||||
|
scaleX: img.scaleX,
|
||||||
|
scaleY: img.scaleY,
|
||||||
|
cropX: cropX,
|
||||||
|
cropY: cropY,
|
||||||
|
width: cropWidth,
|
||||||
|
height: cropHeight,
|
||||||
|
hasControls: true,
|
||||||
|
hasBorders: true,
|
||||||
|
cornerColor: "black",
|
||||||
|
cornerStrokeColor: "black",
|
||||||
|
cornerSize: 12,
|
||||||
|
borderColor: "black",
|
||||||
|
transparentCorners: false,
|
||||||
|
});
|
||||||
|
canvas.remove(img); // Rimuovi l'immagine originale
|
||||||
|
canvas.remove(cropRect); // Rimuovi il rettangolo di ritaglio
|
||||||
|
canvas.add(newImg); // Aggiungi l'immagine ritagliata
|
||||||
|
canvas.setActiveObject(newImg);
|
||||||
|
exitCropMode();
|
||||||
|
saveCanvasState(); // Salva lo stato dopo il ritaglio
|
||||||
|
canvas.renderAll();
|
||||||
|
console.log("Ritaglio applicato con successo");
|
||||||
|
},
|
||||||
|
{ crossOrigin: "anonymous" },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entra in modalità rimozione sfondo
|
||||||
|
function enterBackgroundRemovalMode() {
|
||||||
|
const activeObject = canvas.getActiveObject();
|
||||||
|
if (!activeObject || activeObject.type !== "image") {
|
||||||
|
console.warn(
|
||||||
|
"Nessuna immagine selezionata per la rimozione dello sfondo",
|
||||||
|
);
|
||||||
|
alert(
|
||||||
|
"Seleziona un'immagine prima di attivare la rimozione dello sfondo!",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(
|
||||||
|
"Entrata in modalità rimozione sfondo per immagine:",
|
||||||
|
activeObject,
|
||||||
|
);
|
||||||
|
isRemovingBackground = true;
|
||||||
|
backgroundRemovalImage = activeObject;
|
||||||
|
updateButtons();
|
||||||
|
console.log(
|
||||||
|
"Modalità rimozione sfondo attivata, clicca sull'immagine per selezionare il colore",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Esci dalla modalità rimozione sfondo
|
||||||
|
function exitBackgroundRemovalMode() {
|
||||||
|
isRemovingBackground = false;
|
||||||
|
backgroundRemovalImage = null;
|
||||||
|
canvas.discardActiveObject();
|
||||||
|
canvas.renderAll();
|
||||||
|
updateButtons();
|
||||||
|
console.log("Uscita dalla modalità rimozione sfondo");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestisci la selezione del colore dello sfondo
|
||||||
|
function handleBackgroundColorSelection(event) {
|
||||||
|
if (!isRemovingBackground || !backgroundRemovalImage) {
|
||||||
|
console.warn(
|
||||||
|
"Condizioni per la rimozione dello sfondo non soddisfatte",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const pointer = canvas.getPointer(event.e);
|
||||||
|
const img = backgroundRemovalImage;
|
||||||
|
|
||||||
|
// Crea una canvas temporanea per ottenere i dati dei pixel
|
||||||
|
const tempCanvas = document.createElement("canvas");
|
||||||
|
tempCanvas.width = img.width;
|
||||||
|
tempCanvas.height = img.height;
|
||||||
|
const ctx = tempCanvas.getContext("2d");
|
||||||
|
ctx.drawImage(img.getElement(), 0, 0, img.width, img.height);
|
||||||
|
|
||||||
|
// Calcola la posizione relativa del click rispetto all'immagine
|
||||||
|
const imgLeft = img.left;
|
||||||
|
const imgTop = img.top;
|
||||||
|
const scaleX = img.scaleX;
|
||||||
|
const scaleY = img.scaleY;
|
||||||
|
const x = (pointer.x - imgLeft) / scaleX;
|
||||||
|
const y = (pointer.y - imgTop) / scaleY;
|
||||||
|
|
||||||
|
// Ottieni il colore del pixel cliccato
|
||||||
|
const pixelData = ctx.getImageData(x, y, 1, 1).data;
|
||||||
|
const targetColor = {
|
||||||
|
r: pixelData[0],
|
||||||
|
g: pixelData[1],
|
||||||
|
b: pixelData[2],
|
||||||
|
};
|
||||||
|
console.log("Colore dello sfondo selezionato:", targetColor);
|
||||||
|
|
||||||
|
// Rimuovi il colore dello sfondo
|
||||||
|
removeBackground(img, targetColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rimuovi il colore dello sfondo
|
||||||
|
function removeBackground(img, targetColor) {
|
||||||
|
console.log("Inizio rimozione sfondo con colore:", targetColor);
|
||||||
|
const tempCanvas = document.createElement("canvas");
|
||||||
|
tempCanvas.width = img.width;
|
||||||
|
tempCanvas.height = img.height;
|
||||||
|
const ctx = tempCanvas.getContext("2d");
|
||||||
|
ctx.drawImage(img.getElement(), 0, 0, img.width, img.height);
|
||||||
|
|
||||||
|
const imageData = ctx.getImageData(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
tempCanvas.width,
|
||||||
|
tempCanvas.height,
|
||||||
|
);
|
||||||
|
const data = imageData.data;
|
||||||
|
const tolerance = 50; // Tolleranza per colori simili
|
||||||
|
|
||||||
|
// Scansiona i pixel e rendi trasparenti quelli che corrispondono al colore target
|
||||||
|
for (let i = 0; i < data.length; i += 4) {
|
||||||
|
const r = data[i];
|
||||||
|
const g = data[i + 1];
|
||||||
|
const b = data[i + 2];
|
||||||
|
// Verifica se il colore è simile al targetColor
|
||||||
|
if (
|
||||||
|
Math.abs(r - targetColor.r) <= tolerance &&
|
||||||
|
Math.abs(g - targetColor.g) <= tolerance &&
|
||||||
|
Math.abs(b - targetColor.b) <= tolerance
|
||||||
|
) {
|
||||||
|
data[i + 3] = 0; // Imposta l'alpha a 0 (trasparente)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.putImageData(imageData, 0, 0);
|
||||||
|
const newImageUrl = tempCanvas.toDataURL("image/png");
|
||||||
|
|
||||||
|
// Crea una nuova immagine con lo sfondo rimosso
|
||||||
|
fabric.Image.fromURL(
|
||||||
|
newImageUrl,
|
||||||
|
(newImg) => {
|
||||||
|
newImg.set({
|
||||||
|
left: img.left,
|
||||||
|
top: img.top,
|
||||||
|
scaleX: img.scaleX,
|
||||||
|
scaleY: img.scaleY,
|
||||||
|
hasControls: true,
|
||||||
|
hasBorders: true,
|
||||||
|
cornerColor: "black",
|
||||||
|
cornerStrokeColor: "black",
|
||||||
|
cornerSize: 12,
|
||||||
|
borderColor: "black",
|
||||||
|
transparentCorners: false,
|
||||||
|
});
|
||||||
|
canvas.remove(img); // Rimuovi l'immagine originale
|
||||||
|
canvas.add(newImg); // Aggiungi l'immagine con sfondo rimosso
|
||||||
|
canvas.setActiveObject(newImg);
|
||||||
|
exitBackgroundRemovalMode();
|
||||||
|
saveCanvasState(); // Salva lo stato dopo la rimozione dello sfondo
|
||||||
|
canvas.renderAll();
|
||||||
|
console.log("Sfondo rimosso con successo");
|
||||||
|
},
|
||||||
|
{ crossOrigin: "anonymous" },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rimuovi l'immagine selezionata
|
||||||
|
function removeImage() {
|
||||||
|
const activeObject = canvas.getActiveObject();
|
||||||
|
if (!activeObject || activeObject.type !== "image") {
|
||||||
|
console.warn("Nessuna immagine selezionata per la rimozione");
|
||||||
|
alert("Seleziona un'immagine da rimuovere!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("Rimozione immagine selezionata:", activeObject);
|
||||||
|
canvas.remove(activeObject);
|
||||||
|
canvas.discardActiveObject();
|
||||||
|
saveCanvasState(); // Salva lo stato dopo la rimozione
|
||||||
|
canvas.renderAll();
|
||||||
|
console.log("Immagine rimossa con successo");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
cornerColor: "black",
|
||||||
|
cornerStrokeColor: "black",
|
||||||
|
cornerSize: 12,
|
||||||
|
borderColor: "black",
|
||||||
|
transparentCorners: false,
|
||||||
|
});
|
||||||
|
canvas.add(img);
|
||||||
|
canvas.renderAll();
|
||||||
|
console.log(
|
||||||
|
"Immagine aggiunta al canvas:",
|
||||||
|
imgPath,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ crossOrigin: "anonymous" },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
// Deseleziona checkbox dopo aggiunta
|
||||||
|
checkboxes.forEach((cb) => (cb.checked = false));
|
||||||
|
saveCanvasState(); // Salva lo stato dopo l'aggiunta
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
console.log("Collage salvato e modale chiuso");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pulisci canvas
|
||||||
|
const clearCanvasBtn = document.getElementById("clearCanvasBtn");
|
||||||
|
if (clearCanvasBtn) {
|
||||||
|
clearCanvasBtn.addEventListener("click", () => {
|
||||||
|
canvas.clear();
|
||||||
|
canvas.setBackgroundColor("#fff");
|
||||||
|
history = []; // Resetta la pila di stati
|
||||||
|
saveCanvasState(); // Salva lo stato vuoto
|
||||||
|
canvas.renderAll();
|
||||||
|
console.log("Canvas pulito");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
saveCanvasState(); // Salva lo stato dopo il cambio di livello
|
||||||
|
console.log("Oggetto portato in primo piano");
|
||||||
|
} 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();
|
||||||
|
saveCanvasState(); // Salva lo stato dopo il cambio di livello
|
||||||
|
console.log("Oggetto mandato in fondo");
|
||||||
|
} 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();
|
||||||
|
saveCanvasState(); // Salva lo stato dopo il cambio di livello
|
||||||
|
console.log("Oggetto spostato avanti di un livello");
|
||||||
|
} 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();
|
||||||
|
saveCanvasState(); // Salva lo stato dopo il cambio di livello
|
||||||
|
console.log("Oggetto spostato indietro di un livello");
|
||||||
|
} else {
|
||||||
|
alert("Seleziona un'immagine sul canvas!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestione ritaglio immagini
|
||||||
|
const cropImageBtn = document.getElementById("cropImageBtn");
|
||||||
|
if (cropImageBtn) {
|
||||||
|
cropImageBtn.addEventListener("click", () => {
|
||||||
|
console.log("Pulsante Ritaglia cliccato");
|
||||||
|
enterCropMode();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const applyCropBtn = document.getElementById("applyCropBtn");
|
||||||
|
if (applyCropBtn) {
|
||||||
|
applyCropBtn.addEventListener("click", () => {
|
||||||
|
console.log("Pulsante Applica Ritaglio cliccato");
|
||||||
|
applyCrop();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelCropBtn = document.getElementById("cancelCropBtn");
|
||||||
|
if (cancelCropBtn) {
|
||||||
|
cancelCropBtn.addEventListener("click", () => {
|
||||||
|
console.log("Pulsante Annulla Ritaglio cliccato");
|
||||||
|
exitCropMode();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestione rimozione sfondo
|
||||||
|
const removeBackgroundBtn = document.getElementById(
|
||||||
|
"removeBackgroundBtn",
|
||||||
|
);
|
||||||
|
if (removeBackgroundBtn) {
|
||||||
|
removeBackgroundBtn.addEventListener("click", () => {
|
||||||
|
console.log("Pulsante Rimuovi Sfondo cliccato");
|
||||||
|
enterBackgroundRemovalMode();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestione rimozione immagine
|
||||||
|
const removeImageBtn = document.getElementById("removeImageBtn");
|
||||||
|
if (removeImageBtn) {
|
||||||
|
removeImageBtn.addEventListener("click", () => {
|
||||||
|
console.log("Pulsante Rimuovi Immagine cliccato");
|
||||||
|
removeImage();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestione undo
|
||||||
|
const undoBtn = document.getElementById("undoBtn");
|
||||||
|
if (undoBtn) {
|
||||||
|
undoBtn.addEventListener("click", () => {
|
||||||
|
console.log("Pulsante Annulla cliccato");
|
||||||
|
undo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 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 +1104,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");
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
// photos_popup.php
|
// photos_popup.php
|
||||||
include('include/headscript.php');
|
include('include/headscript.php');
|
||||||
|
// Includi Fabric.js solo per questa pagina
|
||||||
|
?>
|
||||||
|
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
|
||||||
|
|
||||||
|
<?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 +124,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 +162,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 +172,47 @@ $result->saveToFile($qrCodeFile);
|
|||||||
<span class="image-modal-close">×</span>
|
<span class="image-modal-close">×</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>
|
||||||
|
|
||||||
|
<!-- 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;">×</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; display: flex; flex-wrap: wrap; gap: 5px;">
|
||||||
|
<button id="saveCollageBtn" title="Salva il collage"><i class="fas fa-save"></i></button>
|
||||||
|
<!-- <button id="clearCanvasBtn" title="Pulisci il canvas"><i class="fas fa-trash"></i></button> -->
|
||||||
|
<button id="bringToFrontBtn" title="Porta in primo piano"><i class="fas fa-arrow-up"></i></button>
|
||||||
|
<button id="sendToBackBtn" title="Manda in fondo"><i class="fas fa-arrow-down"></i></button>
|
||||||
|
<button id="bringForwardBtn" title="Sposta avanti di un livello"><i class="fas fa-arrow-circle-up"></i></button>
|
||||||
|
<button id="sendBackwardBtn" title="Sposta indietro di un livello"><i class="fas fa-arrow-circle-down"></i></button>
|
||||||
|
<button id="cropImageBtn" title="Ritaglia immagine selezionata" disabled><i class="fas fa-crop"></i></button>
|
||||||
|
<button id="applyCropBtn" title="Applica ritaglio" disabled><i class="fas fa-crop"></i> Applica</button>
|
||||||
|
<button id="cancelCropBtn" title="Annulla ritaglio" disabled><i class="fas fa-crop"></i> Annulla</button>
|
||||||
|
<button id="removeBackgroundBtn" title="Rimuovi sfondo immagine selezionata" disabled><i class="fas fa-eraser"></i> Rimuovi Sfondo</button>
|
||||||
|
<button id="removeImageBtn" title="Rimuovi immagine selezionata" disabled><i class="fas fa-trash-alt"></i> Rimuovi</button>
|
||||||
|
<button id="undoBtn" title="Annulla ultima azione" disabled><i class="fas fa-undo"></i></button>
|
||||||
|
<p id="backgroundRemovalInstruction" style="display: none; color: #007bff;">Clicca sull'immagine per selezionare il colore dello sfondo da rimuovere</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -240,4 +292,56 @@ $result->saveToFile($qrCodeFile);
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stile per i pulsanti del modale collage */
|
||||||
|
#collageModal button {
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin: 5px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#saveCollageBtn {
|
||||||
|
background: #28a745;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clearCanvasBtn {
|
||||||
|
background: #dc3545;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bringToFrontBtn,
|
||||||
|
#sendToBackBtn,
|
||||||
|
#bringForwardBtn,
|
||||||
|
#sendBackwardBtn {
|
||||||
|
background: #007bff;
|
||||||
|
color: white;
|
||||||
|
padding: 8px;
|
||||||
|
/* Pulsanti solo icona più piccoli */
|
||||||
|
}
|
||||||
|
|
||||||
|
#cropImageBtn,
|
||||||
|
#applyCropBtn,
|
||||||
|
#cancelCropBtn,
|
||||||
|
#removeBackgroundBtn,
|
||||||
|
#removeImageBtn,
|
||||||
|
#undoBtn {
|
||||||
|
background: #ffc107;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#collageModal button:disabled {
|
||||||
|
background: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#collageModal button i {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -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()
|
||||||
|
]);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user