Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d6302fa9c | |||
| 3da8ff81c9 | |||
| a36dd02771 | |||
| 0a6fb98476 | |||
| 412dce8941 | |||
| 586226ceaf | |||
| ac09d8d0eb | |||
| 33e3ae059d |
@@ -0,0 +1,47 @@
|
|||||||
|
APP_ENV=production
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_KEY=base64:C+sutHm6xP5sE4QXhoZFhYjArlVN11s2mDU1F8beUkM=
|
||||||
|
APP_URL=http://vanguard.test
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
|
||||||
|
DB_CONNECTION=mysql
|
||||||
|
DB_HOST="localhost"
|
||||||
|
DB_DATABASE="trfcertest"
|
||||||
|
DB_USERNAME="solocla"
|
||||||
|
DB_PASSWORD="!Massarosa2"
|
||||||
|
DB_PREFIX="auth_"
|
||||||
|
|
||||||
|
BROADCAST_DRIVER=log
|
||||||
|
CACHE_DRIVER=file
|
||||||
|
QUEUE_DRIVER=sync
|
||||||
|
SESSION_DRIVER=database
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PASSWORD=null
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
MAIL_MAILER=mail
|
||||||
|
MAIL_FROM_NAME=Vanguard
|
||||||
|
MAIL_FROM_ADDRESS=vanguard@test.dev
|
||||||
|
MAIL_HOST=smtp.mailtrap.io
|
||||||
|
MAIL_PORT=2525
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_ENCRYPTION=null
|
||||||
|
|
||||||
|
PUSHER_APP_ID=
|
||||||
|
PUSHER_APP_KEY=
|
||||||
|
PUSHER_APP_SECRET=
|
||||||
|
PUSHER_APP_CLUSTER=mt1
|
||||||
|
|
||||||
|
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||||
|
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||||
|
|
||||||
|
# Credenziali API VisualLims
|
||||||
|
API_BASE_URL=https://93.43.5.102/limsapi
|
||||||
|
API_USERNAME=WebApiUser
|
||||||
|
API_PASSWORD=webapiuser01
|
||||||
|
|
||||||
|
BASE_URL=http://localhost:8000/userarea/
|
||||||
@@ -32,4 +32,3 @@ $langdatatables = [
|
|||||||
"paginate_next" => "Next",
|
"paginate_next" => "Next",
|
||||||
"paginate_previous" => "Previous"
|
"paginate_previous" => "Previous"
|
||||||
];
|
];
|
||||||
$quotationstitle = "Quotations";
|
|
||||||
|
|||||||
@@ -120,4 +120,76 @@ class VisualLimsApiClient
|
|||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function post($endpoint, $payload)
|
||||||
|
{
|
||||||
|
$token = $this->getToken();
|
||||||
|
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
|
||||||
|
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer {$token}",
|
||||||
|
"Content-Type: application/json",
|
||||||
|
"Accept: application/json"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$curl_error = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($response === false) {
|
||||||
|
throw new Exception("Errore nella richiesta POST: {$curl_error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($http_code < 200 || $http_code >= 300) {
|
||||||
|
throw new Exception("POST fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_decode($response, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function patch($endpoint, $payload)
|
||||||
|
{
|
||||||
|
$token = $this->getToken();
|
||||||
|
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
|
||||||
|
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer {$token}",
|
||||||
|
"Content-Type: application/json",
|
||||||
|
"Accept: application/json"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$curl_error = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($response === false) {
|
||||||
|
throw new Exception("Errore nella richiesta PATCH: {$curl_error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($http_code < 200 || $http_code >= 300) {
|
||||||
|
throw new Exception("PATCH fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_decode($response, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBaseUrl()
|
||||||
|
{
|
||||||
|
return $this->baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
* Trying 93.43.5.102:443...
|
||||||
|
* Connected to 93.43.5.102 (93.43.5.102) port 443
|
||||||
|
* ALPN: curl offers h2,http/1.1
|
||||||
|
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
|
||||||
|
* ALPN: server accepted h2
|
||||||
|
* Server certificate:
|
||||||
|
* subject: C=FR; ST=Île-de-France; O=Bureau Veritas; CN=bvcpsitaly-elims.it
|
||||||
|
* start date: Feb 17 00:00:00 2025 GMT
|
||||||
|
* expire date: Feb 17 23:59:59 2026 GMT
|
||||||
|
* issuer: C=US; O=Corporation Service Company; CN=Corporation Service Company RSA OV SSL CA
|
||||||
|
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
|
||||||
|
* using HTTP/2
|
||||||
|
* [HTTP/2] [1] OPENED stream for https://93.43.5.102/limsapi/api/authentication/authenticate
|
||||||
|
* [HTTP/2] [1] [:method: POST]
|
||||||
|
* [HTTP/2] [1] [:scheme: https]
|
||||||
|
* [HTTP/2] [1] [:authority: 93.43.5.102]
|
||||||
|
* [HTTP/2] [1] [:path: /limsapi/api/authentication/authenticate]
|
||||||
|
* [HTTP/2] [1] [content-type: application/json]
|
||||||
|
* [HTTP/2] [1] [accept: application/json]
|
||||||
|
* [HTTP/2] [1] [content-length: 51]
|
||||||
|
> POST /limsapi/api/authentication/authenticate HTTP/2
|
||||||
|
Host: 93.43.5.102
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
Content-Length: 51
|
||||||
|
|
||||||
|
< HTTP/2 200
|
||||||
|
< cache-control: max-age=0
|
||||||
|
< content-type: application/json; charset=utf-8
|
||||||
|
< server: Microsoft-IIS/10.0
|
||||||
|
< strict-transport-security: max-age=2592000
|
||||||
|
< x-powered-by: ASP.NET
|
||||||
|
< x-content-type-options: nosniff
|
||||||
|
< date: Sat, 06 Sep 2025 10:24:00 GMT
|
||||||
|
<
|
||||||
|
* Connection #0 to host 93.43.5.102 left intact
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
* Trying 93.43.5.102:443...
|
||||||
|
* Connected to 93.43.5.102 (93.43.5.102) port 443
|
||||||
|
* ALPN: curl offers h2,http/1.1
|
||||||
|
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
|
||||||
|
* ALPN: server accepted h2
|
||||||
|
* Server certificate:
|
||||||
|
* subject: C=FR; ST=Île-de-France; O=Bureau Veritas; CN=bvcpsitaly-elims.it
|
||||||
|
* start date: Feb 17 00:00:00 2025 GMT
|
||||||
|
* expire date: Feb 17 23:59:59 2026 GMT
|
||||||
|
* issuer: C=US; O=Corporation Service Company; CN=Corporation Service Company RSA OV SSL CA
|
||||||
|
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
|
||||||
|
* using HTTP/2
|
||||||
|
* [HTTP/2] [1] OPENED stream for https://93.43.5.102/limsapi/api/odata/CustomField(1083)?$expand=CustomFieldsValues
|
||||||
|
* [HTTP/2] [1] [:method: GET]
|
||||||
|
* [HTTP/2] [1] [:scheme: https]
|
||||||
|
* [HTTP/2] [1] [:authority: 93.43.5.102]
|
||||||
|
* [HTTP/2] [1] [:path: /limsapi/api/odata/CustomField(1083)?$expand=CustomFieldsValues]
|
||||||
|
* [HTTP/2] [1] [authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc1NzE2MTQ0MCwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.UgVN5wzjtR8MRU2xLDFWRHIAYLJTmxF3x7kWeVU11YU]
|
||||||
|
* [HTTP/2] [1] [accept: application/json]
|
||||||
|
> GET /limsapi/api/odata/CustomField(1083)?$expand=CustomFieldsValues HTTP/2
|
||||||
|
Host: 93.43.5.102
|
||||||
|
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc1NzE2MTQ0MCwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.UgVN5wzjtR8MRU2xLDFWRHIAYLJTmxF3x7kWeVU11YU
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
< HTTP/2 200
|
||||||
|
< cache-control: max-age=0
|
||||||
|
< content-type: application/json; odata.metadata=minimal; odata.streaming=true; charset=utf-8
|
||||||
|
< server: Microsoft-IIS/10.0
|
||||||
|
< strict-transport-security: max-age=2592000
|
||||||
|
< odata-version: 4.0
|
||||||
|
< x-powered-by: ASP.NET
|
||||||
|
< x-content-type-options: nosniff
|
||||||
|
< date: Sat, 06 Sep 2025 10:24:02 GMT
|
||||||
|
<
|
||||||
|
* Connection #0 to host 93.43.5.102 left intact
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,28 +0,0 @@
|
|||||||
<?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);
|
|
||||||
|
|
||||||
$partId = $data['part_id'] ?? null;
|
|
||||||
|
|
||||||
if (!$partId) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'ID parte mancante']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM identification_parts WHERE id = :part_id");
|
|
||||||
$stmt->execute([':part_id' => $partId]);
|
|
||||||
$rowCount = $stmt->rowCount();
|
|
||||||
if ($rowCount > 0) {
|
|
||||||
echo json_encode(['success' => true, 'message' => 'Parte eliminata con successo']);
|
|
||||||
} else {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Nessuna parte trovata con ID ' . $partId]);
|
|
||||||
}
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore nell\'eliminazione: ' . $e->getMessage()]);
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
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,4 +11,3 @@
|
|||||||
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,172 @@
|
|||||||
|
<?php
|
||||||
|
require_once "class/VisualLimsApiClient.class.php";
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
$dbHandler = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $dbHandler->getConnection();
|
||||||
|
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$iddatadb = $_POST['iddatadb'] ?? null;
|
||||||
|
if (!$iddatadb) {
|
||||||
|
throw new Exception("Missing iddatadb");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔹 STEP 1+2: Fetch Cliente ID + Schema ID
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT et.idclient AS clienteId, et.idschema AS schemaId
|
||||||
|
FROM datadb as d
|
||||||
|
INNER JOIN excel_templates as et ON d.templateid = et.id
|
||||||
|
WHERE d.iddatadb = :iddatadb
|
||||||
|
LIMIT 1
|
||||||
|
");
|
||||||
|
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||||
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
throw new Exception("No Cliente/Schema found for iddatadb {$iddatadb}");
|
||||||
|
}
|
||||||
|
|
||||||
|
$clienteId = (int) $result['clienteId'];
|
||||||
|
$schemaId = (int) $result['schemaId'];
|
||||||
|
|
||||||
|
// 🔹 STEP 3: Fetch Parts (including idmatrice)
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT part_number, part_description, material, color, mix, idmatrice
|
||||||
|
FROM identification_parts
|
||||||
|
WHERE iddatadb = :iddatadb
|
||||||
|
");
|
||||||
|
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||||
|
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// 🔹 STEP 4: Fetch Field Values with Labels
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT
|
||||||
|
idd.field_value,
|
||||||
|
m.field_label,
|
||||||
|
m.schema_id,
|
||||||
|
m.field_id
|
||||||
|
FROM
|
||||||
|
import_data_details as idd
|
||||||
|
JOIN template_mapping m ON idd.mapping_id = m.id
|
||||||
|
WHERE idd.id = :iddatadb
|
||||||
|
");
|
||||||
|
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$fieldValues = [];
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$fieldValues[] = [
|
||||||
|
"IdCommesseCustomFields" => (int) $row['field_id'],
|
||||||
|
"Valore" => $row['field_value']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔹 Initialize API client
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// 🔹 STEP 5: Create CommessaWeb (NOT WebOrder)
|
||||||
|
$commessaWebPayload = [
|
||||||
|
"Cliente" => $clienteId,
|
||||||
|
"SchemaCustomField" => $schemaId,
|
||||||
|
"Richiedente" => "Test Web Import",
|
||||||
|
"Descrizione" => "TEST CommessaWeb",
|
||||||
|
];
|
||||||
|
$commessaWeb = $api->post("CommessaWeb", $commessaWebPayload);
|
||||||
|
|
||||||
|
$commessaId = $commessaWeb["IdCommessa"];
|
||||||
|
// Estraiamo il numero della commessa usando CodiceCommessa
|
||||||
|
$commessaWebCode = substr($commessaWeb["CodiceCommessa"] ?? "TEST CommessaWeb", 0, 30); // Limite a 30 caratteri
|
||||||
|
|
||||||
|
// 🔹 STEP 6: Create Campioni (Samples) for each part
|
||||||
|
$campioni = [];
|
||||||
|
foreach ($parts as $index => $part) {
|
||||||
|
$matriceId = (int) ($part["idmatrice"] ?? 0);
|
||||||
|
|
||||||
|
if ($matriceId <= 0) {
|
||||||
|
throw new Exception("Invalid or missing idmatrice for part: " . ($part["part_number"] ?? "Unknown"));
|
||||||
|
}
|
||||||
|
|
||||||
|
$campionePayload = [
|
||||||
|
"Commessa" => $commessaId,
|
||||||
|
"Matrice" => $matriceId,
|
||||||
|
"SottoMatrice" => null,
|
||||||
|
"SchemaCustomField" => $schemaId,
|
||||||
|
"NoteWeb" => $part["part_description"] ?? ""
|
||||||
|
];
|
||||||
|
|
||||||
|
$campione = $api->post("Campione", $campionePayload);
|
||||||
|
|
||||||
|
$campione["PartNumber"] = $part["part_number"] ?? "";
|
||||||
|
$campione["Material"] = $part["material"] ?? "";
|
||||||
|
$campione["Color"] = $part["color"] ?? "";
|
||||||
|
$campione["Mix"] = $part["mix"] ?? "";
|
||||||
|
|
||||||
|
$campioni[] = $campione;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔹 STEP 7: Update Custom Fields for CommessaWeb
|
||||||
|
if (!empty($fieldValues)) {
|
||||||
|
$commessaWithFields = $api->get("CommessaWeb(" . $commessaId . ")?\$expand=CommesseCustomFields");
|
||||||
|
$commessaCustomFields = [];
|
||||||
|
foreach ($commessaWithFields["CommesseCustomFields"] as $customField) {
|
||||||
|
foreach ($fieldValues as $fieldValue) {
|
||||||
|
if ($customField["IdCommesseCustomFields"] == $fieldValue["IdCommesseCustomFields"]) {
|
||||||
|
$commessaCustomFields[] = [
|
||||||
|
"IdCommesseCustomFields" => $customField["IdCommesseCustomFields"],
|
||||||
|
"Valore" => $fieldValue["Valore"]
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($commessaCustomFields)) {
|
||||||
|
$updatePayload = ["CommesseCustomFields" => $commessaCustomFields];
|
||||||
|
$api->patch("CommessaWeb({$commessaId})", $updatePayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔹 STEP 8: Update datadb with idcommessaweb, commessaweb, and status
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
UPDATE datadb
|
||||||
|
SET idcommessaweb = :idcommessaweb, commessaweb = :commessaweb, status = 'l'
|
||||||
|
WHERE iddatadb = :iddatadb
|
||||||
|
");
|
||||||
|
$stmt->execute([
|
||||||
|
'idcommessaweb' => $commessaId,
|
||||||
|
'commessaweb' => $commessaWebCode,
|
||||||
|
'iddatadb' => $iddatadb
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 🔹 STEP 9: Send CommessaWeb to laboratory
|
||||||
|
$sendResult = $api->post("CommessaWeb({$commessaId})/InviaCommessa", []);
|
||||||
|
|
||||||
|
// 🔹 STEP 10: Prepare final response
|
||||||
|
$finalCommessa = [
|
||||||
|
"Cliente" => $clienteId,
|
||||||
|
"SchemaCustomField" => $schemaId,
|
||||||
|
"Richiedente" => $commessaWeb["Richiedente"] ?? "Web Import",
|
||||||
|
"Descrizione" => $commessaWeb["Descrizione"] ?? "",
|
||||||
|
"CommesseCustomFields" => $fieldValues,
|
||||||
|
"Campioni" => $campioni,
|
||||||
|
"Inviata" => 1
|
||||||
|
];
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
"success" => true,
|
||||||
|
"commessaWeb" => $finalCommessa,
|
||||||
|
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
|
||||||
|
"totalCampioni" => count($campioni),
|
||||||
|
"totalCustomFields" => count($fieldValues),
|
||||||
|
"message" => "Export successful"
|
||||||
|
]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log("LIMS Export Error: " . $e->getMessage() . "\nTrace: " . $e->getTraceAsString());
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
"success" => false,
|
||||||
|
"message" => "Export failed: " . $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
<?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()]);
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -559,7 +559,11 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
<?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;">
|
||||||
|
<!-- commented only for admin roles -->
|
||||||
|
<?php if ((Auth::user()->hasRole('Admin'))) : ?>
|
||||||
<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="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>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<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>
|
||||||
@@ -709,6 +713,33 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
<script src="parts.js"></script>
|
<script src="parts.js"></script>
|
||||||
<script src="tracking.js"></script>
|
<script src="tracking.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
$(document).on("click", ".export-lims-btn", function() {
|
||||||
|
let rowId = $(this).data("row");
|
||||||
|
let idDataDb = $(this).data("iddatadb");
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "export_to_lims.php",
|
||||||
|
method: "POST",
|
||||||
|
data: {
|
||||||
|
iddatadb: idDataDb
|
||||||
|
},
|
||||||
|
dataType: "json",
|
||||||
|
beforeSend: function() {
|
||||||
|
alert("Export started in background for row " + rowId);
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
alert(response.message);
|
||||||
|
} else {
|
||||||
|
alert("❌ Error: " + response.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
alert("❌ AJAX error: " + error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
console.log("Page loaded, initializing event listeners");
|
console.log("Page loaded, initializing event listeners");
|
||||||
|
|
||||||
|
|||||||
@@ -41,21 +41,7 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>-->
|
||||||
<li>
|
|
||||||
<a href="javascript:;" class="has-arrow">
|
|
||||||
<div class="parent-icon"><i class="bx bx-category"></i>
|
|
||||||
</div>
|
|
||||||
<div class="menu-title">Other Functions</div>
|
|
||||||
</a>
|
|
||||||
<ul>
|
|
||||||
<li> <a href="quotations.php"><i class='bx bx-radio-circle'></i><?php echo $quotationstitle; ?></a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
include('include/headscript.php');
|
|
||||||
|
|
||||||
$dbHandler = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $dbHandler->getConnection();
|
|
||||||
|
|
||||||
$idquotations = $_GET['idquotations'] ?? null;
|
|
||||||
|
|
||||||
if (!$idquotations) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'ID quotations mancante']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$stmt = $pdo->prepare("SELECT id, idquotations, part_number, part_description FROM identification_parts WHERE idquotations = :idquotations ORDER BY part_number ASC");
|
|
||||||
$stmt->execute([':idquotations' => $idquotations]);
|
|
||||||
$parts = $stmt->fetchAll();
|
|
||||||
|
|
||||||
echo json_encode(['success' => true, 'parts' => $parts]);
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
// load_photo_quotation.php
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
include('include/headscript.php');
|
|
||||||
|
|
||||||
$dbHandler = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $dbHandler->getConnection();
|
|
||||||
|
|
||||||
$idquotations = isset($_GET['idquotations']) ? intval($_GET['idquotations']) : null;
|
|
||||||
|
|
||||||
if (!$idquotations) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'ID quotation mancante']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Seleziona le foto per il dato idquotations dalla tabella datadb_photos
|
|
||||||
$stmt = $pdo->prepare("SELECT id, file_path FROM datadb_photos WHERE idquotations = ?");
|
|
||||||
$stmt->execute([$idquotations]);
|
|
||||||
$photos = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if ($photos && count($photos) > 0) {
|
|
||||||
$photoPaths = array_map(function ($photo) {
|
|
||||||
return '../photostrf/' . $photo['file_path'];
|
|
||||||
}, $photos);
|
|
||||||
echo json_encode(['success' => true, 'photos' => $photoPaths]);
|
|
||||||
} else {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Nessuna foto trovata']);
|
|
||||||
}
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
|
|
||||||
}
|
|
||||||
@@ -21,7 +21,7 @@ $schemajson = $template['schemajson'] ? json_decode($template['schemajson'], tru
|
|||||||
$isSchemajsonEmpty = empty(trim($template['schemajson']));
|
$isSchemajsonEmpty = empty(trim($template['schemajson']));
|
||||||
|
|
||||||
// Recupera i campi dalla tabella template_mapping
|
// Recupera i campi dalla tabella template_mapping
|
||||||
$stmt = $pdo->prepare("SELECT id, field_id, excel_column, is_manual, manual_default, data_type, is_required, default_value, has_list, length, decimals, min_value, max_value, default_curr_date, tablename, field_label, main_field, is_visible_import FROM template_mapping WHERE template_id = ?");
|
$stmt = $pdo->prepare("SELECT id, field_id, excel_column, is_manual, manual_default, data_type, is_required, default_value, has_list, length, decimals, min_value, max_value, default_curr_date, tablename, field_label, main_field FROM template_mapping WHERE template_id = ?");
|
||||||
$stmt->execute([$id]);
|
$stmt->execute([$id]);
|
||||||
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
@@ -83,10 +83,8 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
}
|
}
|
||||||
|
|
||||||
#schemaFieldsTable th:first-child,
|
#schemaFieldsTable th:first-child,
|
||||||
#schemaFieldsTable td:first-child,
|
#schemaFieldsTable td:first-child {
|
||||||
#schemaFieldsTable th:nth-child(2),
|
width: 50px;
|
||||||
#schemaFieldsTable td:nth-child(2) {
|
|
||||||
width: 100px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -139,8 +137,8 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Main</th>
|
<th>Main</th>
|
||||||
<th>Visible on Import</th>
|
|
||||||
<th>Title</th>
|
<th>Title</th>
|
||||||
|
<th>ID</th>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Mapping</th>
|
<th>Mapping</th>
|
||||||
<th>Default Value</th>
|
<th>Default Value</th>
|
||||||
@@ -152,15 +150,8 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
<td>
|
<td>
|
||||||
<input type="checkbox" class="main-field-checkbox" data-mapping-id="<?php echo $mapping['id']; ?>" <?php echo $mapping['main_field'] == 1 ? 'checked' : ''; ?>>
|
<input type="checkbox" class="main-field-checkbox" data-mapping-id="<?php echo $mapping['id']; ?>" <?php echo $mapping['main_field'] == 1 ? 'checked' : ''; ?>>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td><?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?></td>
|
||||||
<input type="checkbox" class="visible-import-checkbox" data-mapping-id="<?php echo $mapping['id']; ?>" <?php echo (isset($mapping['is_visible_import']) ? $mapping['is_visible_import'] : 1) == 1 ? 'checked' : ''; ?>>
|
<td><?php echo htmlspecialchars($mapping['field_id'] ?? 'N/A'); ?></td>
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?>
|
|
||||||
<?php if ($mapping['is_required'] == 1): ?>
|
|
||||||
<span class="badge bg-danger ms-2">Required</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
|
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
|
||||||
<td>
|
<td>
|
||||||
<?php
|
<?php
|
||||||
@@ -437,7 +428,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
|
|
||||||
async function updateSchemaDetails() {
|
async function updateSchemaDetails() {
|
||||||
if (!schemaId) {
|
if (!schemaId) {
|
||||||
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-warning">No schema associated.</td></tr>';
|
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-warning">No schema associated.</td></tr>'; // Aggiornato colspan a 6
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,7 +445,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
await saveSchemaJson(templateId, JSON.stringify(data));
|
await saveSchemaJson(templateId, JSON.stringify(data));
|
||||||
alert('Schema updated successfully. Refresh the page to see changes.');
|
alert('Schema updated successfully. Refresh the page to see changes.');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-danger">Error: ' + error.message + '</td></tr>';
|
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-danger">Error: ' + error.message + '</td></tr>'; // Aggiornato colspan a 6
|
||||||
} finally {
|
} finally {
|
||||||
updateSchemaButton.disabled = false;
|
updateSchemaButton.disabled = false;
|
||||||
updateSchemaButton.textContent = 'Update Schema Details';
|
updateSchemaButton.textContent = 'Update Schema Details';
|
||||||
@@ -533,11 +524,16 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
console.error("❌ Error updating main_field:", data.message);
|
console.error("❌ Error updating main_field:", data.message);
|
||||||
|
// Revert checkbox state on error
|
||||||
checkbox.checked = !checkbox.checked;
|
checkbox.checked = !checkbox.checked;
|
||||||
|
// Riselezione visiva degli altri se errore
|
||||||
|
if (value === 1) {
|
||||||
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
||||||
cb.checked = cb.dataset.originalChecked === 'true';
|
cb.checked = cb.dataset.originalChecked === 'true';
|
||||||
});
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Aggiorna lo stato originale dopo successo
|
||||||
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
||||||
cb.dataset.originalChecked = cb.checked;
|
cb.dataset.originalChecked = cb.checked;
|
||||||
});
|
});
|
||||||
@@ -546,36 +542,12 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error("❌ Fetch error:", error);
|
console.error("❌ Fetch error:", error);
|
||||||
checkbox.checked = !checkbox.checked;
|
checkbox.checked = !checkbox.checked;
|
||||||
|
// Riselezione visiva degli altri se errore
|
||||||
|
if (value === 1) {
|
||||||
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
||||||
cb.checked = cb.dataset.originalChecked === 'true';
|
cb.checked = cb.dataset.originalChecked === 'true';
|
||||||
});
|
});
|
||||||
});
|
|
||||||
} else if (event.target.classList.contains('visible-import-checkbox')) {
|
|
||||||
const checkbox = event.target;
|
|
||||||
const mappingId = checkbox.dataset.mappingId;
|
|
||||||
const value = checkbox.checked ? 1 : 0;
|
|
||||||
|
|
||||||
fetch('update_visible_import.php', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
template_id: <?php echo $id; ?>,
|
|
||||||
mapping_id: mappingId,
|
|
||||||
value: value
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (!data.success) {
|
|
||||||
console.error("❌ Error updating is_visible_import:", data.message);
|
|
||||||
checkbox.checked = !checkbox.checked;
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error("❌ Fetch error:", error);
|
|
||||||
checkbox.checked = !checkbox.checked;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -597,7 +569,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
mappedColumn = document.createElement('span');
|
mappedColumn = document.createElement('span');
|
||||||
mappedColumn.className = 'mapped-column';
|
mappedColumn.className = 'mapped-column';
|
||||||
mappedColumn.style.marginLeft = '5px';
|
mappedColumn.style.marginLeft = '5px';
|
||||||
tr.querySelector('td:nth-child(5)').appendChild(mappedColumn);
|
tr.querySelector('td:nth-child(5)').appendChild(mappedColumn); // Aggiornato a nth-child(5) per la nuova colonna
|
||||||
}
|
}
|
||||||
if (!removeBtn) {
|
if (!removeBtn) {
|
||||||
removeBtn = document.createElement('button');
|
removeBtn = document.createElement('button');
|
||||||
@@ -605,7 +577,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
removeBtn.textContent = 'X';
|
removeBtn.textContent = 'X';
|
||||||
removeBtn.style.marginLeft = '5px';
|
removeBtn.style.marginLeft = '5px';
|
||||||
removeBtn.setAttribute('data-id', mappingId);
|
removeBtn.setAttribute('data-id', mappingId);
|
||||||
tr.querySelector('td:nth-child(5)').appendChild(removeBtn);
|
tr.querySelector('td:nth-child(5)').appendChild(removeBtn); // Aggiornato a nth-child(5)
|
||||||
|
|
||||||
removeBtn.addEventListener('click', function(e) {
|
removeBtn.addEventListener('click', function(e) {
|
||||||
let tr = e.target.closest('tr');
|
let tr = e.target.closest('tr');
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- Modal modificato con pulsante per riconoscimento vocale -->
|
<!-- Modal -->
|
||||||
<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">
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
<input type="checkbox" id="showMixParts" name="showMixParts" style="margin-right: 5px;">
|
<input type="checkbox" id="showMixParts" name="showMixParts" style="margin-right: 5px;">
|
||||||
<label for="showMixParts" style="font-size: 0.9rem; margin-right: 10px;">Mix</label>
|
<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-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>
|
||||||
</div>
|
</div>
|
||||||
<ul id="partsList" class="list-group"></ul>
|
<ul id="partsList" class="list-group"></ul>
|
||||||
@@ -97,11 +96,6 @@
|
|||||||
font-size: 0.6rem !important;
|
font-size: 0.6rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#partsModal {
|
|
||||||
z-index: 1060 !important;
|
|
||||||
/* Aumentato per superare il modale foto (z-index: 1050) */
|
|
||||||
}
|
|
||||||
|
|
||||||
#partsModal .modal-content {
|
#partsModal .modal-content {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
|
|||||||
+280
-307
File diff suppressed because it is too large
Load Diff
+98
-545
@@ -1,21 +1,20 @@
|
|||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
// Funzione per caricare il contenuto del popup
|
// Funzione per caricare il contenuto del popup
|
||||||
async function loadPopupContent(iddatadb, idquotations) {
|
async function loadPopupContent(iddatadb) {
|
||||||
const popupContent = document.getElementById("popupContent");
|
const popupContent = document.getElementById("popupContent");
|
||||||
if (!popupContent) {
|
if (!popupContent) {
|
||||||
console.error("Elemento popupContent non trovato");
|
console.error("Elemento popupContent non trovato");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const endpoint = idquotations
|
console.log("Caricamento contenuto per iddatadb:", iddatadb);
|
||||||
? `photos_popup.php?idquotations=${idquotations}`
|
const response = await fetch(
|
||||||
: `photos_popup.php?iddatadb=${iddatadb}`;
|
`photos_popup.php?iddatadb=${iddatadb}`,
|
||||||
console.log("Caricamento popup da:", endpoint);
|
);
|
||||||
const response = await fetch(endpoint);
|
|
||||||
if (!response.ok)
|
if (!response.ok)
|
||||||
throw new Error("Errore nella risposta del server");
|
throw new Error("Errore nella risposta del server");
|
||||||
popupContent.innerHTML = await response.text();
|
popupContent.innerHTML = await response.text();
|
||||||
attachPhotoEventListeners(iddatadb, idquotations);
|
attachPhotoEventListeners(iddatadb);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
popupContent.innerHTML = `<p>Errore durante il caricamento: ${error.message}</p>`;
|
popupContent.innerHTML = `<p>Errore durante il caricamento: ${error.message}</p>`;
|
||||||
console.error("Errore in loadPopupContent:", error);
|
console.error("Errore in loadPopupContent:", error);
|
||||||
@@ -23,7 +22,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Funzione per gestire la webcam
|
// Funzione per gestire la webcam
|
||||||
function setupWebcam(iddatadb, idquotations) {
|
function setupWebcam(iddatadb) {
|
||||||
const openWebcamBtn = document.getElementById("openWebcamBtn");
|
const openWebcamBtn = document.getElementById("openWebcamBtn");
|
||||||
const webcamArea = document.getElementById("webcamArea");
|
const webcamArea = document.getElementById("webcamArea");
|
||||||
const webcamVideo = document.getElementById("webcamVideo");
|
const webcamVideo = document.getElementById("webcamVideo");
|
||||||
@@ -57,18 +56,22 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Funzione per avviare la webcam con un deviceId specifico
|
||||||
async function startWebcam(deviceId = null) {
|
async function startWebcam(deviceId = null) {
|
||||||
try {
|
try {
|
||||||
|
// Ferma il flusso video esistente, se presente
|
||||||
if (stream) {
|
if (stream) {
|
||||||
stream.getTracks().forEach((track) => track.stop());
|
stream.getTracks().forEach((track) => track.stop());
|
||||||
stream = null;
|
stream = null;
|
||||||
webcamVideo.srcObject = null;
|
webcamVideo.srcObject = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configura i vincoli per getUserMedia
|
||||||
const constraints = {
|
const constraints = {
|
||||||
video: deviceId ? { deviceId: { exact: deviceId } } : true,
|
video: deviceId ? { deviceId: { exact: deviceId } } : true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Avvia il flusso video
|
||||||
stream = await navigator.mediaDevices.getUserMedia(constraints);
|
stream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||||
webcamVideo.srcObject = stream;
|
webcamVideo.srcObject = stream;
|
||||||
webcamArea.style.display = "block";
|
webcamArea.style.display = "block";
|
||||||
@@ -83,17 +86,21 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Funzione per popolare il dropdown delle webcam
|
||||||
async function populateWebcamSelect() {
|
async function populateWebcamSelect() {
|
||||||
try {
|
try {
|
||||||
|
// Richiedi i permessi per accedere ai dispositivi
|
||||||
await navigator.mediaDevices.getUserMedia({ video: true });
|
await navigator.mediaDevices.getUserMedia({ video: true });
|
||||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
const videoDevices = devices.filter(
|
const videoDevices = devices.filter(
|
||||||
(device) => device.kind === "videoinput",
|
(device) => device.kind === "videoinput",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Svuota il dropdown
|
||||||
webcamSelect.innerHTML =
|
webcamSelect.innerHTML =
|
||||||
'<option value="">Select a webcam</option>';
|
'<option value="">Select a webcam</option>';
|
||||||
|
|
||||||
|
// Popola il dropdown con le webcam disponibili
|
||||||
videoDevices.forEach((device) => {
|
videoDevices.forEach((device) => {
|
||||||
const option = document.createElement("option");
|
const option = document.createElement("option");
|
||||||
option.value = device.deviceId;
|
option.value = device.deviceId;
|
||||||
@@ -102,9 +109,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
webcamSelect.appendChild(option);
|
webcamSelect.appendChild(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Mostra il dropdown solo se ci sono più webcam
|
||||||
webcamSelect.style.display =
|
webcamSelect.style.display =
|
||||||
videoDevices.length > 1 ? "block" : "none";
|
videoDevices.length > 1 ? "block" : "none";
|
||||||
|
|
||||||
|
// Avvia la webcam predefinita se ce n'è almeno una
|
||||||
if (videoDevices.length > 0) {
|
if (videoDevices.length > 0) {
|
||||||
await startWebcam(videoDevices[0].deviceId);
|
await startWebcam(videoDevices[0].deviceId);
|
||||||
} else {
|
} else {
|
||||||
@@ -120,10 +129,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apri la webcam e popola il dropdown
|
||||||
openWebcamBtn.addEventListener("click", async () => {
|
openWebcamBtn.addEventListener("click", async () => {
|
||||||
await populateWebcamSelect();
|
await populateWebcamSelect();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Gestisci il cambio della webcam selezionata
|
||||||
webcamSelect.addEventListener("change", async (e) => {
|
webcamSelect.addEventListener("change", async (e) => {
|
||||||
const deviceId = e.target.value;
|
const deviceId = e.target.value;
|
||||||
if (deviceId) {
|
if (deviceId) {
|
||||||
@@ -131,6 +142,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Chiudi la webcam
|
||||||
closeWebcamBtn.addEventListener("click", () => {
|
closeWebcamBtn.addEventListener("click", () => {
|
||||||
if (stream) {
|
if (stream) {
|
||||||
stream.getTracks().forEach((track) => track.stop());
|
stream.getTracks().forEach((track) => track.stop());
|
||||||
@@ -142,6 +154,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
dropArea.style.display = "block";
|
dropArea.style.display = "block";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Cattura la foto
|
||||||
captureBtn.addEventListener("click", () => {
|
captureBtn.addEventListener("click", () => {
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
canvas.width = webcamVideo.videoWidth;
|
canvas.width = webcamVideo.videoWidth;
|
||||||
@@ -158,9 +171,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
);
|
);
|
||||||
const loader = document.getElementById("loader");
|
const loader = document.getElementById("loader");
|
||||||
if (loader) {
|
if (loader) {
|
||||||
|
console.log("Mostro loader per upload webcam");
|
||||||
loader.style.display = "flex";
|
loader.style.display = "flex";
|
||||||
}
|
}
|
||||||
await handleFiles([file], iddatadb, idquotations);
|
await handleFiles([file], iddatadb);
|
||||||
if (stream) {
|
if (stream) {
|
||||||
stream.getTracks().forEach((track) => track.stop());
|
stream.getTracks().forEach((track) => track.stop());
|
||||||
stream = null;
|
stream = null;
|
||||||
@@ -173,7 +187,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleFiles(files, iddatadb, idquotations) {
|
// Funzione per gestire il caricamento dei file
|
||||||
|
async function handleFiles(files, iddatadb) {
|
||||||
const loader = document.getElementById("loader");
|
const loader = document.getElementById("loader");
|
||||||
if (!loader) {
|
if (!loader) {
|
||||||
console.error("Elemento loader non trovato");
|
console.error("Elemento loader non trovato");
|
||||||
@@ -191,16 +206,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Inizio upload del file:", file.name);
|
||||||
loader.style.display = "flex";
|
loader.style.display = "flex";
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("photo", file);
|
formData.append("photo", file);
|
||||||
if (idquotations) {
|
|
||||||
formData.append("idquotations", idquotations);
|
|
||||||
} else {
|
|
||||||
formData.append("iddatadb", iddatadb);
|
formData.append("iddatadb", iddatadb);
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch("upload_photo.php", {
|
const response = await fetch("upload_photo.php", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -208,19 +219,24 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
loadPopupContent(iddatadb, idquotations);
|
console.log(
|
||||||
|
"Upload completato con successo, ricarico popup",
|
||||||
|
);
|
||||||
|
loadPopupContent(iddatadb);
|
||||||
} else {
|
} else {
|
||||||
alert("Errore durante il caricamento: " + result.message);
|
alert("Errore durante il caricamento: " + result.message);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert("Errore durante il caricamento: " + error.message);
|
alert("Errore durante il caricamento: " + error.message);
|
||||||
} finally {
|
} finally {
|
||||||
|
console.log("Nascondo loader dopo upload");
|
||||||
loader.style.display = "none";
|
loader.style.display = "none";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function attachPhotoEventListeners(iddatadb, idquotations) {
|
// Funzione per attaccare gli event listener al contenuto del popup
|
||||||
|
function attachPhotoEventListeners(iddatadb) {
|
||||||
const dropArea = document.getElementById("dropArea");
|
const dropArea = document.getElementById("dropArea");
|
||||||
const photoInput = document.getElementById("photoInput");
|
const photoInput = document.getElementById("photoInput");
|
||||||
const photosModal = document.getElementById("photosModal");
|
const photosModal = document.getElementById("photosModal");
|
||||||
@@ -234,6 +250,9 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Event listener associati per iddatadb:", iddatadb);
|
||||||
|
|
||||||
|
// Gestione drag-and-drop
|
||||||
const preventDefaults = (e) => {
|
const preventDefaults = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -262,9 +281,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
dropArea.addEventListener(
|
dropArea.addEventListener(
|
||||||
"drop",
|
"drop",
|
||||||
(e) => {
|
(e) => {
|
||||||
|
console.log("Evento drop attivato");
|
||||||
const files = e.dataTransfer.files;
|
const files = e.dataTransfer.files;
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
handleFiles(files, iddatadb, idquotations);
|
handleFiles(files, iddatadb);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
@@ -273,6 +293,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
dropArea.addEventListener(
|
dropArea.addEventListener(
|
||||||
"click",
|
"click",
|
||||||
() => {
|
() => {
|
||||||
|
console.log("Click su dropArea, apro input file");
|
||||||
photoInput.click();
|
photoInput.click();
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
@@ -281,15 +302,18 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
photoInput.addEventListener(
|
photoInput.addEventListener(
|
||||||
"change",
|
"change",
|
||||||
(e) => {
|
(e) => {
|
||||||
|
console.log("Evento change su photoInput");
|
||||||
const files = e.target.files;
|
const files = e.target.files;
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
handleFiles(files, iddatadb, idquotations);
|
handleFiles(files, iddatadb);
|
||||||
}
|
}
|
||||||
|
// Resetta l'input per consentire il caricamento dello stesso file
|
||||||
e.target.value = "";
|
e.target.value = "";
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Gestione rimozione foto
|
||||||
document.querySelectorAll(".delete-photo-btn").forEach((button) => {
|
document.querySelectorAll(".delete-photo-btn").forEach((button) => {
|
||||||
button.addEventListener("click", async function () {
|
button.addEventListener("click", async function () {
|
||||||
const photoId = this.getAttribute("data-photo-id");
|
const photoId = this.getAttribute("data-photo-id");
|
||||||
@@ -305,7 +329,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
loadPopupContent(iddatadb, idquotations);
|
console.log(
|
||||||
|
"Foto eliminata con successo, ricarico popup",
|
||||||
|
);
|
||||||
|
loadPopupContent(iddatadb);
|
||||||
} else {
|
} else {
|
||||||
alert(
|
alert(
|
||||||
"Errore durante l'eliminazione: " +
|
"Errore durante l'eliminazione: " +
|
||||||
@@ -321,17 +348,21 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Gestione ingrandimento immagini
|
||||||
document.querySelectorAll(".thumbnail").forEach((img) => {
|
document.querySelectorAll(".thumbnail").forEach((img) => {
|
||||||
img.addEventListener("click", function () {
|
img.addEventListener("click", function () {
|
||||||
|
console.log("Click su thumbnail, apro modale immagine");
|
||||||
const enlargedImage = document.getElementById("enlargedImage");
|
const enlargedImage = document.getElementById("enlargedImage");
|
||||||
enlargedImage.src = this.src;
|
enlargedImage.src = this.src;
|
||||||
document.getElementById("imageModal").style.display = "block";
|
document.getElementById("imageModal").style.display = "block";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Gestione chiusura modale immagine
|
||||||
const imageCloseBtn = document.querySelector(".image-modal-close");
|
const imageCloseBtn = document.querySelector(".image-modal-close");
|
||||||
if (imageCloseBtn) {
|
if (imageCloseBtn) {
|
||||||
imageCloseBtn.addEventListener("click", () => {
|
imageCloseBtn.addEventListener("click", () => {
|
||||||
|
console.log("Chiusura modale immagine");
|
||||||
document.getElementById("imageModal").style.display = "none";
|
document.getElementById("imageModal").style.display = "none";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -339,43 +370,37 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
.getElementById("imageModal")
|
.getElementById("imageModal")
|
||||||
.addEventListener("click", function (event) {
|
.addEventListener("click", function (event) {
|
||||||
if (event.target === this) {
|
if (event.target === this) {
|
||||||
|
console.log(
|
||||||
|
"Chiusura modale immagine cliccando sullo sfondo",
|
||||||
|
);
|
||||||
this.style.display = "none";
|
this.style.display = "none";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setupWebcam(iddatadb, idquotations);
|
// Inizializza la gestione della webcam
|
||||||
|
setupWebcam(iddatadb);
|
||||||
|
|
||||||
|
// Gestione bottone Crea Collage
|
||||||
const createCollageBtn = document.getElementById("createCollageBtn");
|
const createCollageBtn = document.getElementById("createCollageBtn");
|
||||||
if (createCollageBtn) {
|
if (createCollageBtn) {
|
||||||
createCollageBtn.addEventListener("click", () => {
|
createCollageBtn.addEventListener("click", () => {
|
||||||
|
console.log("Apertura modale collage");
|
||||||
document.getElementById("collageModal").style.display = "block";
|
document.getElementById("collageModal").style.display = "block";
|
||||||
initCollageCanvas();
|
initCollageCanvas();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Chiusura modale collage
|
||||||
const closeCollageBtn = document.querySelector(".close-collage");
|
const closeCollageBtn = document.querySelector(".close-collage");
|
||||||
if (closeCollageBtn) {
|
if (closeCollageBtn) {
|
||||||
closeCollageBtn.addEventListener("click", () => {
|
closeCollageBtn.addEventListener("click", () => {
|
||||||
|
console.log("Chiusura modale collage");
|
||||||
document.getElementById("collageModal").style.display = "none";
|
document.getElementById("collageModal").style.display = "none";
|
||||||
if (isCropping) {
|
|
||||||
exitCropMode();
|
|
||||||
}
|
|
||||||
if (isRemovingBackground) {
|
|
||||||
exitBackgroundRemovalMode();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inizializza canvas con Fabric.js
|
||||||
let canvas;
|
let canvas;
|
||||||
let cropRect = null;
|
|
||||||
let isCropping = false;
|
|
||||||
let croppedImage = null;
|
|
||||||
let isApplyingCrop = false;
|
|
||||||
let isRemovingBackground = false;
|
|
||||||
let backgroundRemovalImage = null;
|
|
||||||
let history = [];
|
|
||||||
const maxHistory = 20;
|
|
||||||
|
|
||||||
function initCollageCanvas() {
|
function initCollageCanvas() {
|
||||||
if (typeof fabric === "undefined") {
|
if (typeof fabric === "undefined") {
|
||||||
console.error("Fabric.js non è caricato!");
|
console.error("Fabric.js non è caricato!");
|
||||||
@@ -388,436 +413,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
selection: true,
|
selection: true,
|
||||||
});
|
});
|
||||||
fabric.Object.prototype.set({
|
// Abilita ridimensionamento e trascinamento
|
||||||
cornerColor: "black",
|
canvas.on("object:modified", () => canvas.renderAll());
|
||||||
cornerStrokeColor: "black",
|
|
||||||
cornerSize: 12,
|
|
||||||
borderColor: "black",
|
|
||||||
transparentCorners: false,
|
|
||||||
});
|
|
||||||
canvas.on("object:modified", () => {
|
|
||||||
saveCanvasState();
|
|
||||||
updateLayersPanel();
|
|
||||||
canvas.renderAll();
|
|
||||||
});
|
|
||||||
canvas.on("object:added", () => {
|
|
||||||
updateLayersPanel();
|
|
||||||
});
|
|
||||||
canvas.on("object:removed", () => {
|
|
||||||
updateLayersPanel();
|
|
||||||
});
|
|
||||||
canvas.on("selection:created", () => {
|
|
||||||
updateButtons();
|
|
||||||
});
|
|
||||||
canvas.on("selection:updated", () => {
|
|
||||||
updateButtons();
|
|
||||||
});
|
|
||||||
canvas.on("selection:cleared", () => {
|
|
||||||
if (!isCropping && !isRemovingBackground) {
|
|
||||||
updateButtons();
|
|
||||||
} else if (isCropping && cropRect) {
|
|
||||||
canvas.setActiveObject(cropRect);
|
|
||||||
canvas.renderAll();
|
|
||||||
} else if (isRemovingBackground) {
|
|
||||||
canvas.setActiveObject(backgroundRemovalImage);
|
|
||||||
canvas.renderAll();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
canvas.on("mouse:down", (event) => {
|
|
||||||
if (isRemovingBackground && backgroundRemovalImage) {
|
|
||||||
handleBackgroundColorSelection(event);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
saveCanvasState();
|
|
||||||
updateLayersPanel();
|
|
||||||
updateButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLayersPanel() {
|
|
||||||
const layersList = document.getElementById("layersList");
|
|
||||||
if (!layersList) {
|
|
||||||
console.error("Elemento layersList non trovato");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
layersList.innerHTML = "";
|
|
||||||
|
|
||||||
const images = canvas.getObjects("image");
|
|
||||||
images.forEach((img, index) => {
|
|
||||||
let thumbSrc;
|
|
||||||
try {
|
|
||||||
const thumbCanvas = document.createElement("canvas");
|
|
||||||
thumbCanvas.width = 50;
|
|
||||||
thumbCanvas.height = 50;
|
|
||||||
const thumbFabric = new fabric.Canvas(thumbCanvas);
|
|
||||||
|
|
||||||
const clonedImg = fabric.util.object.clone(img);
|
|
||||||
const scaleFactor = Math.min(
|
|
||||||
50 / img.width,
|
|
||||||
50 / img.height,
|
|
||||||
);
|
|
||||||
clonedImg.scaleX = scaleFactor;
|
|
||||||
clonedImg.scaleY = scaleFactor;
|
|
||||||
clonedImg.left = (50 - img.width * scaleFactor) / 2;
|
|
||||||
clonedImg.top = (50 - img.height * scaleFactor) / 2;
|
|
||||||
clonedImg.setCoords();
|
|
||||||
thumbFabric.add(clonedImg);
|
|
||||||
thumbFabric.renderAll();
|
|
||||||
|
|
||||||
thumbSrc = thumbCanvas.toDataURL("image/png");
|
|
||||||
thumbFabric.dispose();
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(
|
|
||||||
"Errore nella generazione della thumbnail per immagine",
|
|
||||||
index + 1,
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
thumbSrc = img.getSrc(); // Fallback all'URL originale
|
|
||||||
}
|
|
||||||
|
|
||||||
const layerItem = document.createElement("li");
|
|
||||||
const thumbImg = document.createElement("img");
|
|
||||||
thumbImg.src = thumbSrc;
|
|
||||||
thumbImg.title = `Layer ${index + 1}`;
|
|
||||||
thumbImg.addEventListener("click", () => {
|
|
||||||
canvas.setActiveObject(img);
|
|
||||||
canvas.renderAll();
|
|
||||||
});
|
|
||||||
|
|
||||||
layerItem.appendChild(thumbImg);
|
|
||||||
layersList.appendChild(layerItem);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveCanvasState() {
|
|
||||||
if (isCropping || isRemovingBackground) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const state = JSON.stringify(
|
|
||||||
canvas.toJSON([
|
|
||||||
"cornerColor",
|
|
||||||
"cornerStrokeColor",
|
|
||||||
"cornerSize",
|
|
||||||
"borderColor",
|
|
||||||
"transparentCorners",
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
history.push(state);
|
|
||||||
if (history.length > maxHistory) {
|
|
||||||
history.shift();
|
|
||||||
}
|
|
||||||
updateButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
function undo() {
|
|
||||||
if (history.length <= 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
history.pop();
|
|
||||||
const previousState = history[history.length - 1];
|
|
||||||
if (previousState) {
|
|
||||||
canvas.clear();
|
|
||||||
canvas.loadFromJSON(previousState, () => {
|
|
||||||
canvas.renderAll();
|
|
||||||
updateLayersPanel();
|
|
||||||
updateButtons();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.warn("Nessuno stato precedente disponibile");
|
|
||||||
canvas.clear();
|
|
||||||
canvas.setBackgroundColor("#fff");
|
|
||||||
canvas.renderAll();
|
|
||||||
updateLayersPanel();
|
|
||||||
updateButtons();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
if (isCropping && cropRect) {
|
|
||||||
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) {
|
|
||||||
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
|
|
||||||
) {
|
|
||||||
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 {
|
|
||||||
cropBtn.disabled = true;
|
|
||||||
applyCropBtn.disabled = true;
|
|
||||||
cancelCropBtn.disabled = true;
|
|
||||||
removeBackgroundBtn.disabled = true;
|
|
||||||
removeImageBtn.disabled = true;
|
|
||||||
undoBtn.disabled = history.length <= 1;
|
|
||||||
instruction.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
isCropping = true;
|
|
||||||
croppedImage = activeObject;
|
|
||||||
canvas.discardActiveObject();
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
function exitCropMode() {
|
|
||||||
if (cropRect) {
|
|
||||||
canvas.remove(cropRect);
|
|
||||||
cropRect = null;
|
|
||||||
}
|
|
||||||
isCropping = false;
|
|
||||||
croppedImage = null;
|
|
||||||
isApplyingCrop = false;
|
|
||||||
canvas.discardActiveObject();
|
|
||||||
canvas.renderAll();
|
|
||||||
updateButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyCrop() {
|
|
||||||
if (isApplyingCrop) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
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;
|
|
||||||
|
|
||||||
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);
|
|
||||||
canvas.remove(cropRect);
|
|
||||||
canvas.add(newImg);
|
|
||||||
canvas.setActiveObject(newImg);
|
|
||||||
exitCropMode();
|
|
||||||
saveCanvasState();
|
|
||||||
updateLayersPanel();
|
|
||||||
canvas.renderAll();
|
|
||||||
},
|
|
||||||
{ crossOrigin: "anonymous" },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
isRemovingBackground = true;
|
|
||||||
backgroundRemovalImage = activeObject;
|
|
||||||
updateButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
function exitBackgroundRemovalMode() {
|
|
||||||
isRemovingBackground = false;
|
|
||||||
backgroundRemovalImage = null;
|
|
||||||
canvas.discardActiveObject();
|
|
||||||
canvas.renderAll();
|
|
||||||
updateButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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 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;
|
|
||||||
|
|
||||||
const pixelData = ctx.getImageData(x, y, 1, 1).data;
|
|
||||||
const targetColor = {
|
|
||||||
r: pixelData[0],
|
|
||||||
g: pixelData[1],
|
|
||||||
b: pixelData[2],
|
|
||||||
};
|
|
||||||
|
|
||||||
removeBackground(img, targetColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeBackground(img, 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;
|
|
||||||
|
|
||||||
for (let i = 0; i < data.length; i += 4) {
|
|
||||||
const r = data[i];
|
|
||||||
const g = data[i + 1];
|
|
||||||
const b = data[i + 2];
|
|
||||||
if (
|
|
||||||
Math.abs(r - targetColor.r) <= tolerance &&
|
|
||||||
Math.abs(g - targetColor.g) <= tolerance &&
|
|
||||||
Math.abs(b - targetColor.b) <= tolerance
|
|
||||||
) {
|
|
||||||
data[i + 3] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.putImageData(imageData, 0, 0);
|
|
||||||
const newImageUrl = tempCanvas.toDataURL("image/png");
|
|
||||||
|
|
||||||
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);
|
|
||||||
canvas.add(newImg);
|
|
||||||
canvas.setActiveObject(newImg);
|
|
||||||
exitBackgroundRemovalMode();
|
|
||||||
saveCanvasState();
|
|
||||||
updateLayersPanel();
|
|
||||||
canvas.renderAll();
|
|
||||||
},
|
|
||||||
{ crossOrigin: "anonymous" },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
canvas.remove(activeObject);
|
|
||||||
canvas.discardActiveObject();
|
|
||||||
saveCanvasState();
|
|
||||||
updateLayersPanel();
|
|
||||||
canvas.renderAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Aggiungi foto selezionate al canvas
|
||||||
const addToCanvasBtn = document.getElementById("addToCanvasBtn");
|
const addToCanvasBtn = document.getElementById("addToCanvasBtn");
|
||||||
if (addToCanvasBtn) {
|
if (addToCanvasBtn) {
|
||||||
addToCanvasBtn.addEventListener("click", () => {
|
addToCanvasBtn.addEventListener("click", () => {
|
||||||
@@ -830,40 +430,29 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
checkboxes.forEach((cb) => {
|
checkboxes.forEach((cb) => {
|
||||||
const imgPath = cb.getAttribute("data-path");
|
const imgPath = cb.getAttribute("data-path");
|
||||||
fabric.Image.fromURL(
|
fabric.Image.fromURL(imgPath, (img) => {
|
||||||
imgPath,
|
|
||||||
(img) => {
|
|
||||||
img.set({
|
img.set({
|
||||||
left: Math.random() * 600,
|
left: Math.random() * 600, // Posizione random iniziale
|
||||||
top: Math.random() * 400,
|
top: Math.random() * 400,
|
||||||
scaleX: 0.5,
|
scaleX: 0.5, // Scala iniziale
|
||||||
scaleY: 0.5,
|
scaleY: 0.5,
|
||||||
hasControls: true,
|
hasControls: true, // Abilita resize/rotate
|
||||||
hasBorders: true,
|
hasBorders: true,
|
||||||
cornerColor: "black",
|
|
||||||
cornerStrokeColor: "black",
|
|
||||||
cornerSize: 12,
|
|
||||||
borderColor: "black",
|
|
||||||
transparentCorners: false,
|
|
||||||
});
|
});
|
||||||
canvas.add(img);
|
canvas.add(img);
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
},
|
|
||||||
{ crossOrigin: "anonymous" },
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
// Deseleziona checkbox dopo aggiunta
|
||||||
checkboxes.forEach((cb) => (cb.checked = false));
|
checkboxes.forEach((cb) => (cb.checked = false));
|
||||||
saveCanvasState();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Salva collage
|
||||||
const saveCollageBtn = document.getElementById("saveCollageBtn");
|
const saveCollageBtn = document.getElementById("saveCollageBtn");
|
||||||
if (saveCollageBtn) {
|
if (saveCollageBtn) {
|
||||||
saveCollageBtn.addEventListener("click", async () => {
|
saveCollageBtn.addEventListener("click", async () => {
|
||||||
if (
|
if (canvas.getObjects().length === 0) {
|
||||||
canvas.getObjects().length === 0 &&
|
|
||||||
!canvas.backgroundImage
|
|
||||||
) {
|
|
||||||
alert("Il canvas è vuoto! Aggiungi almeno una foto.");
|
alert("Il canvas è vuoto! Aggiungi almeno una foto.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -876,12 +465,25 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
type: "image/jpeg",
|
type: "image/jpeg",
|
||||||
});
|
});
|
||||||
|
|
||||||
await handleFiles([file], iddatadb, idquotations);
|
// Upload come nuova foto
|
||||||
|
await handleFiles([file], iddatadb);
|
||||||
|
// Chiudi modale e ricarica popup
|
||||||
document.getElementById("collageModal").style.display = "none";
|
document.getElementById("collageModal").style.display = "none";
|
||||||
loadPopupContent(iddatadb, idquotations);
|
loadPopupContent(iddatadb);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pulisci canvas
|
||||||
|
const clearCanvasBtn = document.getElementById("clearCanvasBtn");
|
||||||
|
if (clearCanvasBtn) {
|
||||||
|
clearCanvasBtn.addEventListener("click", () => {
|
||||||
|
canvas.clear();
|
||||||
|
canvas.setBackgroundColor("#fff");
|
||||||
|
canvas.renderAll();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestione livelli delle immagini
|
||||||
const bringToFrontBtn = document.getElementById("bringToFrontBtn");
|
const bringToFrontBtn = document.getElementById("bringToFrontBtn");
|
||||||
if (bringToFrontBtn) {
|
if (bringToFrontBtn) {
|
||||||
bringToFrontBtn.addEventListener("click", () => {
|
bringToFrontBtn.addEventListener("click", () => {
|
||||||
@@ -889,8 +491,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (activeObject) {
|
if (activeObject) {
|
||||||
canvas.bringToFront(activeObject);
|
canvas.bringToFront(activeObject);
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
saveCanvasState();
|
|
||||||
updateLayersPanel();
|
|
||||||
} else {
|
} else {
|
||||||
alert("Seleziona un'immagine sul canvas!");
|
alert("Seleziona un'immagine sul canvas!");
|
||||||
}
|
}
|
||||||
@@ -904,8 +504,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (activeObject) {
|
if (activeObject) {
|
||||||
canvas.sendToBack(activeObject);
|
canvas.sendToBack(activeObject);
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
saveCanvasState();
|
|
||||||
updateLayersPanel();
|
|
||||||
} else {
|
} else {
|
||||||
alert("Seleziona un'immagine sul canvas!");
|
alert("Seleziona un'immagine sul canvas!");
|
||||||
}
|
}
|
||||||
@@ -919,8 +517,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (activeObject) {
|
if (activeObject) {
|
||||||
canvas.bringForward(activeObject);
|
canvas.bringForward(activeObject);
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
saveCanvasState();
|
|
||||||
updateLayersPanel();
|
|
||||||
} else {
|
} else {
|
||||||
alert("Seleziona un'immagine sul canvas!");
|
alert("Seleziona un'immagine sul canvas!");
|
||||||
}
|
}
|
||||||
@@ -934,64 +530,21 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (activeObject) {
|
if (activeObject) {
|
||||||
canvas.sendBackwards(activeObject);
|
canvas.sendBackwards(activeObject);
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
saveCanvasState();
|
|
||||||
updateLayersPanel();
|
|
||||||
} else {
|
} else {
|
||||||
alert("Seleziona un'immagine sul canvas!");
|
alert("Seleziona un'immagine sul canvas!");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const cropImageBtn = document.getElementById("cropImageBtn");
|
// Assicurati che il loader sia nascosto all'apertura del popup
|
||||||
if (cropImageBtn) {
|
|
||||||
cropImageBtn.addEventListener("click", () => {
|
|
||||||
enterCropMode();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const applyCropBtn = document.getElementById("applyCropBtn");
|
|
||||||
if (applyCropBtn) {
|
|
||||||
applyCropBtn.addEventListener("click", () => {
|
|
||||||
applyCrop();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancelCropBtn = document.getElementById("cancelCropBtn");
|
|
||||||
if (cancelCropBtn) {
|
|
||||||
cancelCropBtn.addEventListener("click", () => {
|
|
||||||
exitCropMode();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeBackgroundBtn = document.getElementById(
|
|
||||||
"removeBackgroundBtn",
|
|
||||||
);
|
|
||||||
if (removeBackgroundBtn) {
|
|
||||||
removeBackgroundBtn.addEventListener("click", () => {
|
|
||||||
enterBackgroundRemovalMode();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeImageBtn = document.getElementById("removeImageBtn");
|
|
||||||
if (removeImageBtn) {
|
|
||||||
removeImageBtn.addEventListener("click", () => {
|
|
||||||
removeImage();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const undoBtn = document.getElementById("undoBtn");
|
|
||||||
if (undoBtn) {
|
|
||||||
undoBtn.addEventListener("click", () => {
|
|
||||||
undo();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const loader = document.getElementById("loader");
|
const loader = document.getElementById("loader");
|
||||||
if (loader) {
|
if (loader) {
|
||||||
|
console.log("Nascondo loader all'apertura del popup");
|
||||||
loader.style.display = "none";
|
loader.style.display = "none";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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");
|
||||||
const closeBtn = document.querySelector(".close-btn");
|
const closeBtn = document.querySelector(".close-btn");
|
||||||
@@ -999,20 +552,19 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (photosButtons.length && photosModal && closeBtn) {
|
if (photosButtons.length && photosModal && closeBtn) {
|
||||||
photosButtons.forEach((button) => {
|
photosButtons.forEach((button) => {
|
||||||
button.addEventListener("click", function () {
|
button.addEventListener("click", function () {
|
||||||
const iddatadb = this.getAttribute("data-iddatadb") || null;
|
console.log(
|
||||||
const idquotations =
|
"Pulsante Photos cliccato per iddatadb:",
|
||||||
this.getAttribute("data-idquotations") || null;
|
this.getAttribute("data-iddatadb"),
|
||||||
console.log("Apertura modale foto con:", {
|
);
|
||||||
iddatadb,
|
const iddatadb = this.getAttribute("data-iddatadb");
|
||||||
idquotations,
|
loadPopupContent(iddatadb);
|
||||||
});
|
|
||||||
loadPopupContent(iddatadb, idquotations);
|
|
||||||
photosModal.style.display = "block";
|
photosModal.style.display = "block";
|
||||||
document.querySelector(".overlay").style.display = "none";
|
document.querySelector(".overlay").style.display = "none";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
closeBtn.addEventListener("click", function () {
|
closeBtn.addEventListener("click", function () {
|
||||||
|
console.log("Chiusura modale photos");
|
||||||
photosModal.style.display = "none";
|
photosModal.style.display = "none";
|
||||||
document.querySelector(".overlay").style.display = "none";
|
document.querySelector(".overlay").style.display = "none";
|
||||||
document.body.style.pointerEvents = "auto";
|
document.body.style.pointerEvents = "auto";
|
||||||
@@ -1020,6 +572,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
|
|
||||||
window.addEventListener("click", function (event) {
|
window.addEventListener("click", function (event) {
|
||||||
if (event.target === photosModal) {
|
if (event.target === photosModal) {
|
||||||
|
console.log("Chiusura modale photos cliccando sullo sfondo");
|
||||||
photosModal.style.display = "none";
|
photosModal.style.display = "none";
|
||||||
document.querySelector(".overlay").style.display = "none";
|
document.querySelector(".overlay").style.display = "none";
|
||||||
document.body.style.pointerEvents = "auto";
|
document.body.style.pointerEvents = "auto";
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ include('include/headscript.php');
|
|||||||
// Includi Fabric.js solo per questa pagina
|
// 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
|
<?php
|
||||||
// Includi l'autoloader di Composer
|
// Includi l'autoloader di Composer
|
||||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||||
@@ -17,131 +15,70 @@ use Endroid\QrCode\QrCode;
|
|||||||
use Endroid\QrCode\RoundBlockSizeMode;
|
use Endroid\QrCode\RoundBlockSizeMode;
|
||||||
use Endroid\QrCode\Writer\PngWriter;
|
use Endroid\QrCode\Writer\PngWriter;
|
||||||
|
|
||||||
// Abilita logging per debug
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
ini_set('display_startup_errors', 1);
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
ini_set('log_errors', 1);
|
|
||||||
ini_set('error_log', __DIR__ . '/photos_popup_debug.log');
|
|
||||||
|
|
||||||
// Log iniziale
|
|
||||||
error_log("Richiesta a photos_popup.php: " . print_r($_GET, true));
|
|
||||||
|
|
||||||
// Carica le variabili d'ambiente
|
// Carica le variabili d'ambiente
|
||||||
try {
|
try {
|
||||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../../');
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../../');
|
||||||
$dotenv->load();
|
$dotenv->load();
|
||||||
|
error_log("File .env caricato correttamente da " . __DIR__ . '/../../.env');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
error_log("Errore nel caricamento del file .env: " . $e->getMessage());
|
error_log("Errore nel caricamento del file .env: " . $e->getMessage());
|
||||||
?>
|
echo json_encode(['error' => 'Errore nel caricamento del file di configurazione']);
|
||||||
<div class="popup-content">
|
|
||||||
<p>Errore: Impossibile caricare il file di configurazione.</p>
|
|
||||||
</div>
|
|
||||||
<?php
|
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verifica che BASE_URL sia definito
|
// Verifica che BASE_URL sia definito
|
||||||
if (!isset($_ENV['BASE_URL'])) {
|
if (!isset($_ENV['BASE_URL'])) {
|
||||||
error_log("Errore: la variabile BASE_URL non è definita nel file .env");
|
error_log("Errore: la variabile BASE_URL non è definita nel file .env");
|
||||||
?>
|
echo json_encode(['error' => 'Variabile BASE_URL non definita']);
|
||||||
<div class="popup-content">
|
|
||||||
<p>Errore: Variabile BASE_URL non definita.</p>
|
|
||||||
</div>
|
|
||||||
<?php
|
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
// Verifica che almeno uno degli ID sia passato
|
// Verifica che l'iddatadb sia stato passato
|
||||||
$iddatadb = isset($_GET['iddatadb']) && !empty($_GET['iddatadb']) ? intval($_GET['iddatadb']) : null;
|
if (!isset($_GET['iddatadb']) || empty($_GET['iddatadb'])) {
|
||||||
$idquotations = isset($_GET['idquotations']) && !empty($_GET['idquotations']) ? intval($_GET['idquotations']) : null;
|
echo json_encode(['error' => 'ID riga non fornito']);
|
||||||
|
|
||||||
if (!$iddatadb && !$idquotations) {
|
|
||||||
error_log("Errore: ID riga o ID quotations non fornito");
|
|
||||||
?>
|
|
||||||
<div class="popup-content">
|
|
||||||
<p>Errore: ID riga o ID quotations non fornito.</p>
|
|
||||||
</div>
|
|
||||||
<?php
|
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($iddatadb && $idquotations) {
|
$iddatadb = intval($_GET['iddatadb']);
|
||||||
error_log("Errore: Non è possibile specificare sia iddatadb che idquotations");
|
|
||||||
?>
|
// Recupera i dettagli della riga (idriga e sample_code)
|
||||||
<div class="popup-content">
|
$stmt = $pdo->prepare("SELECT iddatadb, sample_code FROM datadb WHERE iddatadb = ?");
|
||||||
<p>Errore: Non è possibile specificare sia iddatadb che idquotations.</p>
|
$stmt->execute([$iddatadb]);
|
||||||
</div>
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
<?php
|
|
||||||
|
if (!$row) {
|
||||||
|
echo json_encode(['error' => 'Riga non trovata']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determina quale ID e tabella usare
|
$idriga = $row['iddatadb'];
|
||||||
$paramName = $iddatadb ? 'iddatadb' : 'id'; // Usa 'id' per quotations
|
$sampleCode = $row['sample_code'] ?? 'Non disponibile';
|
||||||
$paramValue = $iddatadb ?: $idquotations;
|
|
||||||
$table = $iddatadb ? 'datadb' : 'quotations';
|
|
||||||
$field = $iddatadb ? 'sample_code' : 'description'; // Usa 'description' per quotations
|
|
||||||
$photoTable = 'datadb_photos'; // Usa sempre datadb_photos
|
|
||||||
$photoParamName = $iddatadb ? 'iddatadb' : 'idquotations'; // Usa 'idquotations' per datadb_photos
|
|
||||||
|
|
||||||
// Recupera i dettagli della riga
|
|
||||||
try {
|
|
||||||
$stmt = $pdo->prepare("SELECT {$paramName}, {$field} FROM {$table} WHERE {$paramName} = ?");
|
|
||||||
$stmt->execute([$paramValue]);
|
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (!$row) {
|
|
||||||
error_log("Errore: Riga non trovata per {$paramName} = {$paramValue}");
|
|
||||||
?>
|
|
||||||
<div class="popup-content">
|
|
||||||
<p>Errore: Riga non trovata.</p>
|
|
||||||
</div>
|
|
||||||
<?php
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
error_log("Errore query dettagli riga: " . $e->getMessage());
|
|
||||||
?>
|
|
||||||
<div class="popup-content">
|
|
||||||
<p>Errore: Impossibile recuperare i dettagli della riga.</p>
|
|
||||||
</div>
|
|
||||||
<?php
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = $row[$paramName];
|
|
||||||
$code = $row[$field] ?? 'Non disponibile';
|
|
||||||
|
|
||||||
// Recupera le foto associate alla riga
|
// Recupera le foto associate alla riga
|
||||||
try {
|
$stmt = $pdo->prepare("SELECT id, file_path, file_name, description, uploaded_at FROM datadb_photos WHERE iddatadb = ? ORDER BY uploaded_at DESC");
|
||||||
$stmt = $pdo->prepare("SELECT id, file_path, file_name, description, uploaded_at FROM {$photoTable} WHERE {$photoParamName} = ? ORDER BY uploaded_at DESC");
|
$stmt->execute([$iddatadb]);
|
||||||
$stmt->execute([$paramValue]);
|
$photos = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
$photos = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
error_log("Errore query foto: " . $e->getMessage());
|
|
||||||
$photos = []; // Imposta array vuoto in caso di errore
|
|
||||||
}
|
|
||||||
|
|
||||||
// Definisci il percorso base per le foto
|
// Definisci il percorso base per le foto
|
||||||
$photoBasePath = '../photostrf/';
|
$photoBasePath = '../photostrf/';
|
||||||
|
|
||||||
// Usa la variabile d'ambiente BASE_URL
|
// Usa la variabile d'ambiente BASE_URL
|
||||||
$baseUrl = rtrim($_ENV['BASE_URL'], '/');
|
$baseUrl = rtrim($_ENV['BASE_URL'], '/'); // Rimuove eventuali slash finali
|
||||||
$uploadUrl = $iddatadb
|
$uploadUrl = $baseUrl . "/upload_photos_mobile.php?iddatadb=" . $iddatadb;
|
||||||
? $baseUrl . "/upload_photos_mobile.php?iddatadb=" . $iddatadb
|
|
||||||
: $baseUrl . "/upload_photos_mobile.php?idquotations=" . $idquotations;
|
|
||||||
|
|
||||||
// Genera il QR code con endroid/qr-code 6.0.6
|
// Genera il QR code con endroid/qr-code 6.0.6
|
||||||
$qrCodeDir = '../photostrf/qrcodes/';
|
$qrCodeDir = '../photostrf/qrcodes/';
|
||||||
if (!is_dir($qrCodeDir)) {
|
if (!is_dir($qrCodeDir)) {
|
||||||
mkdir($qrCodeDir, 0755, true);
|
mkdir($qrCodeDir, 0755, true);
|
||||||
}
|
}
|
||||||
$qrCodeFile = $qrCodeDir . "qrcode_{$id}.png";
|
$qrCodeFile = $qrCodeDir . "qrcode_{$iddatadb}.png";
|
||||||
|
|
||||||
$writer = new PngWriter();
|
$writer = new PngWriter();
|
||||||
|
|
||||||
|
// Crea il QR code usando il costruttore
|
||||||
$qrCode = new QrCode(
|
$qrCode = new QrCode(
|
||||||
data: $uploadUrl,
|
data: $uploadUrl,
|
||||||
encoding: new Encoding('UTF-8'),
|
encoding: new Encoding('UTF-8'),
|
||||||
@@ -165,13 +102,13 @@ $result->saveToFile($qrCodeFile);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3>Manage Photos</h3>
|
<h3>Manage Photos</h3>
|
||||||
<p><strong>ID:</strong> <?= htmlspecialchars($id) ?></p>
|
<p><strong>ID Row:</strong> <?= htmlspecialchars($idriga) ?></p>
|
||||||
<p><strong>Code:</strong> <?= htmlspecialchars($code) ?></p>
|
<p><strong>Sample Code:</strong> <?= htmlspecialchars($sampleCode) ?></p>
|
||||||
|
|
||||||
<!-- QR Code per il caricamento da mobile -->
|
<!-- QR Code per il caricamento da mobile -->
|
||||||
<div style="text-align: center; margin-bottom: 20px;">
|
<div style="text-align: center; margin-bottom: 20px;">
|
||||||
<p>Scan the QR Code with the mobile to take photo with camera:</p>
|
<p>Scan the QR Code with the mobile to take photo with camera:</p>
|
||||||
<img src="../photostrf/qrcodes/qrcode_<?= htmlspecialchars($id) ?>.png" alt="QR Code" style="max-width: 150px;">
|
<img src="../photostrf/qrcodes/qrcode_<?= $iddatadb ?>.png" alt="QR Code" style="max-width: 150px;">
|
||||||
<p style="margin-top: 10px;">
|
<p style="margin-top: 10px;">
|
||||||
<a href="<?= htmlspecialchars($uploadUrl) ?>" target="_blank"><?= htmlspecialchars($uploadUrl) ?></a>
|
<a href="<?= htmlspecialchars($uploadUrl) ?>" target="_blank"><?= htmlspecialchars($uploadUrl) ?></a>
|
||||||
</p>
|
</p>
|
||||||
@@ -198,7 +135,7 @@ $result->saveToFile($qrCodeFile);
|
|||||||
<!-- Elenco delle foto -->
|
<!-- Elenco delle foto -->
|
||||||
<div id="photosList">
|
<div id="photosList">
|
||||||
<?php if (empty($photos)): ?>
|
<?php if (empty($photos)): ?>
|
||||||
<p>Nessuna foto presente.</p>
|
<p>No Photos present.</p>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?php foreach ($photos as $photo): ?>
|
<?php foreach ($photos as $photo): ?>
|
||||||
<?php
|
<?php
|
||||||
@@ -234,10 +171,10 @@ $result->saveToFile($qrCodeFile);
|
|||||||
<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 -->
|
<!-- Nuovo modale per collage -->
|
||||||
<div id="collageModal" class="modal" style="display: none; position: fixed; z-index: 1002; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.8);">
|
<div 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;">
|
<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; font-weight: bold; color: #333; cursor: pointer; z-index: 1003; background: #fff; padding: 5px 10px; border-radius: 50%;">×</span>
|
<span class="close-collage" style="position: absolute; top: 10px; right: 20px; font-size: 30px; cursor: pointer;">×</span>
|
||||||
<h3>Crea Collage</h3>
|
<h3>Crea Collage</h3>
|
||||||
|
|
||||||
<!-- Lista foto selezionabili -->
|
<!-- Lista foto selezionabili -->
|
||||||
@@ -256,26 +193,14 @@ $result->saveToFile($qrCodeFile);
|
|||||||
<!-- Canvas per editing -->
|
<!-- Canvas per editing -->
|
||||||
<canvas id="collageCanvas" width="800" height="600" style="border: 1px solid #ccc; margin-top: 20px;"></canvas>
|
<canvas id="collageCanvas" width="800" height="600" style="border: 1px solid #ccc; margin-top: 20px;"></canvas>
|
||||||
|
|
||||||
<!-- Pannello dei livelli -->
|
|
||||||
<div id="layersPanel" style="width: 120px; max-height: 600px; overflow-y: auto; background: #f8f9fa; padding: 10px; position: absolute; right: 0; top: 60px;">
|
|
||||||
<h4 style="margin: 0 0 10px 0; font-size: 16px;">Livelli</h4>
|
|
||||||
<ul id="layersList" style="list-style: none; padding: 0;"></ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Bottoni azioni -->
|
<!-- Bottoni azioni -->
|
||||||
<div style="margin-top: 20px; display: flex; flex-wrap: wrap; gap: 5px;">
|
<div style="margin-top: 20px;">
|
||||||
<button id="saveCollageBtn" title="Salva il collage"><i class="fas fa-save"></i></button>
|
<button id="saveCollageBtn">Salva Collage</button>
|
||||||
<button id="bringToFrontBtn" title="Porta in primo piano"><i class="fas fa-arrow-up"></i></button>
|
<button id="clearCanvasBtn">Pulisci Canvas</button>
|
||||||
<button id="sendToBackBtn" title="Manda in fondo"><i class="fas fa-arrow-down"></i></button>
|
<button id="bringToFrontBtn" title="Porta in primo piano">In Alto</button>
|
||||||
<button id="bringForwardBtn" title="Sposta avanti di un livello"><i class="fas fa-arrow-circle-up"></i></button>
|
<button id="sendToBackBtn" title="Manda in fondo">In Fondo</button>
|
||||||
<button id="sendBackwardBtn" title="Sposta indietro di un livello"><i class="fas fa-arrow-circle-down"></i></button>
|
<button id="bringForwardBtn" title="Sposta avanti di un livello">Avanti</button>
|
||||||
<button id="cropImageBtn" title="Ritaglia immagine selezionata" disabled><i class="fas fa-crop"></i></button>
|
<button id="sendBackwardBtn" title="Sposta indietro di un livello">Indietro</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>
|
</div>
|
||||||
@@ -358,71 +283,4 @@ $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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#bringToFrontBtn,
|
|
||||||
#sendToBackBtn,
|
|
||||||
#bringForwardBtn,
|
|
||||||
#sendBackwardBtn {
|
|
||||||
background: #007bff;
|
|
||||||
color: white;
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#cropImageBtn,
|
|
||||||
#applyCropBtn,
|
|
||||||
#cancelCropBtn,
|
|
||||||
#removeBackgroundBtn,
|
|
||||||
#removeImageBtn,
|
|
||||||
#undoBtn {
|
|
||||||
background: #ffc107;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
#collageModal button:disabled {
|
|
||||||
background: #ccc;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
#collageModal button i {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stile per il pannello dei livelli */
|
|
||||||
#layersPanel {
|
|
||||||
z-index: 1002;
|
|
||||||
}
|
|
||||||
|
|
||||||
#layersPanel li {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#layersPanel img {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
border: 2px solid #ccc;
|
|
||||||
cursor: pointer;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
#layersPanel img:hover {
|
|
||||||
border-color: #007bff;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@@ -1,429 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Abilita errori per debug
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
ini_set('display_startup_errors', 1);
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
ini_set('log_errors', 1);
|
|
||||||
ini_set('error_log', __DIR__ . '/quotations_debug.log');
|
|
||||||
if (!file_exists(__DIR__ . '/quotations_debug.log')) {
|
|
||||||
file_put_contents(__DIR__ . '/quotations_debug.log', "Inizio operazioni alle " . date('Y-m-d H:i:s') . "\n", FILE_APPEND);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log iniziale
|
|
||||||
error_log("Inizio operazioni alle " . date('Y-m-d H:i:s'));
|
|
||||||
|
|
||||||
include('include/headscript.php');
|
|
||||||
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $db->getConnection();
|
|
||||||
|
|
||||||
// Recupera l'ID dell'utente loggato
|
|
||||||
$user_id = $iduserlogin ?? 1;
|
|
||||||
|
|
||||||
// Gestione creazione nuova quotation (crea record vuoto su conferma)
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'create') {
|
|
||||||
$description = '';
|
|
||||||
$customer = '';
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO quotations (description, customer, iduser) VALUES (?, ?, ?)");
|
|
||||||
$stmt->execute([$description, $customer, $user_id]);
|
|
||||||
$newId = $pdo->lastInsertId();
|
|
||||||
|
|
||||||
// Log creazione
|
|
||||||
error_log("Creata nuova quotation ID: $newId");
|
|
||||||
|
|
||||||
// Reindirizza alla modifica della nuova quotation
|
|
||||||
header("Location: quotations.php?edit_id=" . $newId . "&status=success&message=" . urlencode("Quotation creata con successo"));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gestione modifica quotation
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'update' && isset($_POST['id'])) {
|
|
||||||
$id = intval($_POST['id']);
|
|
||||||
$description = $_POST['description'] ?? '';
|
|
||||||
$customer = $_POST['customer'] ?? '';
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare("UPDATE quotations SET description = ?, customer = ? WHERE id = ? AND iduser = ?");
|
|
||||||
$stmt->execute([$description, $customer, $id, $user_id]);
|
|
||||||
|
|
||||||
// Log modifica
|
|
||||||
error_log("Modificata quotation ID: $id");
|
|
||||||
|
|
||||||
// Reindirizza alla lista delle quotations
|
|
||||||
header("Location: quotations.php?status=success&message=" . urlencode("Quotation modificata con successo"));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gestione cancellazione quotation
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'delete' && isset($_POST['id'])) {
|
|
||||||
$id = intval($_POST['id']);
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM quotations WHERE id = ? AND iduser = ?");
|
|
||||||
$stmt->execute([$id, $user_id]);
|
|
||||||
|
|
||||||
// Log cancellazione
|
|
||||||
error_log("Cancellata quotation ID: $id");
|
|
||||||
|
|
||||||
header("Location: quotations.php?status=success&message=" . urlencode("Quotation cancellata con successo"));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recupera tutte le quotations per l'utente
|
|
||||||
$stmt = $pdo->prepare("SELECT * FROM quotations WHERE iduser = ? ORDER BY creation_date DESC");
|
|
||||||
$stmt->execute([$user_id]);
|
|
||||||
$quotations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
// Verifica se è richiesta la modifica di una quotation
|
|
||||||
$editQuotation = null;
|
|
||||||
if (isset($_GET['edit_id'])) {
|
|
||||||
$editId = intval($_GET['edit_id']);
|
|
||||||
$stmt = $pdo->prepare("SELECT * FROM quotations WHERE id = ? AND iduser = ?");
|
|
||||||
$stmt->execute([$editId, $user_id]);
|
|
||||||
$editQuotation = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
|
||||||
<?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://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css">
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
|
|
||||||
<style>
|
|
||||||
/* Stili simili alla pagina fornita, adattati */
|
|
||||||
.cell-changed {
|
|
||||||
background-color: #fff3b0 !important;
|
|
||||||
transition: background-color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.manual-input,
|
|
||||||
select.manual-input {
|
|
||||||
background-color: #fff3cd;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.required-input,
|
|
||||||
select.required-input {
|
|
||||||
background-color: #f8d7da;
|
|
||||||
}
|
|
||||||
|
|
||||||
input,
|
|
||||||
select,
|
|
||||||
textarea {
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: 1px solid #ced4da;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 5px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-btn {
|
|
||||||
padding: 6px 8px;
|
|
||||||
margin-right: 5px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 35px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.save-btn {
|
|
||||||
background-color: #28a745;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-btn {
|
|
||||||
background-color: #dc3545;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.photos-btn {
|
|
||||||
background-color: #007bff;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.parts-btn {
|
|
||||||
background-color: #ffc107;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group textarea {
|
|
||||||
height: 100px;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flash-success {
|
|
||||||
background-color: #d4edda !important;
|
|
||||||
transition: background-color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quotation-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal {
|
|
||||||
display: none;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 1050;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-content {
|
|
||||||
background-color: #fefefe;
|
|
||||||
margin: 15% auto;
|
|
||||||
padding: 20px;
|
|
||||||
border: 1px solid #888;
|
|
||||||
width: 80%;
|
|
||||||
max-width: 600px;
|
|
||||||
border-radius: 8px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-btn {
|
|
||||||
color: #aaa;
|
|
||||||
float: right;
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: bold;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-btn:hover,
|
|
||||||
.close-btn:focus {
|
|
||||||
color: #000;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<title>Gestione Quotations - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="wrapper">
|
|
||||||
<?php include('include/navbar.php'); ?>
|
|
||||||
<?php include('include/topbar.php'); ?>
|
|
||||||
<div class="page-wrapper">
|
|
||||||
<div class="page-content">
|
|
||||||
<div class="card radius-10">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<div>
|
|
||||||
<h6 class="mb-0">Gestione Quotations</h6>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<?php if ($editQuotation): ?>
|
|
||||||
<!-- Modifica Quotation -->
|
|
||||||
<h6 class="mb-3">Modifica Quotation ID: <?= $editQuotation['id'] ?></h6>
|
|
||||||
<form id="editForm" method="post">
|
|
||||||
<input type="hidden" name="action" value="update">
|
|
||||||
<input type="hidden" name="id" value="<?= $editQuotation['id'] ?>">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="description">Descrizione</label>
|
|
||||||
<textarea id="description" name="description" class="manual-input required-input" required><?= htmlspecialchars($editQuotation['description']) ?></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="customer">Cliente</label>
|
|
||||||
<input type="text" id="customer" name="customer" class="manual-input required-input" value="<?= htmlspecialchars($editQuotation['customer']) ?>" required>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary">Salva Modifiche</button>
|
|
||||||
<a href="quotations.php" class="btn btn-secondary">Torna alla Lista</a>
|
|
||||||
</form>
|
|
||||||
<div class="quotation-actions">
|
|
||||||
<h6 class="mb-3">Azioni</h6>
|
|
||||||
<button type="button" class="photos-btn action-btn" data-row="<?= $index ?>" data-idquotations="<?= $editQuotation['id'] ?>" style="background: #007bff; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; flex: 1;"><i class="fas fa-camera"></i></button>
|
|
||||||
<button class="parts-btn" data-iddatadb="" data-idquotations="456" data-row="0">Parti</button>
|
|
||||||
</div>
|
|
||||||
<?php else: ?>
|
|
||||||
<!-- Lista Quotations -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createModal">Crea Nuova Quotation</button>
|
|
||||||
</div>
|
|
||||||
<h6 class="mb-3">Quotations Esistenti</h6>
|
|
||||||
<table id="quotationsTable" class="table table-striped table-bordered">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>Data Creazione</th>
|
|
||||||
<th>Descrizione</th>
|
|
||||||
<th>Cliente</th>
|
|
||||||
<th>Azioni</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($quotations as $row): ?>
|
|
||||||
<tr data-id="<?= $row['id'] ?>">
|
|
||||||
<td><?= htmlspecialchars($row['id']) ?></td>
|
|
||||||
<td><?= htmlspecialchars($row['creation_date']) ?></td>
|
|
||||||
<td>
|
|
||||||
<textarea name="description" class="cell-input manual-input form-control"><?= htmlspecialchars($row['description']) ?></textarea>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="customer" class="cell-input manual-input form-control" value="<?= htmlspecialchars($row['customer']) ?>">
|
|
||||||
</td>
|
|
||||||
<!-- In quotations.php, nella tabella delle quotations -->
|
|
||||||
<td>
|
|
||||||
<button type="button" class="save-btn action-btn edit-btn" data-id="<?= $row['id'] ?>" title="Salva Modifiche"><i class="fas fa-save"></i></button>
|
|
||||||
<button type="button" class="delete-btn action-btn" data-id="<?= $row['id'] ?>" title="Cancella" data-bs-toggle="modal" data-bs-target="#deleteModal"><i class="fas fa-trash"></i></button>
|
|
||||||
<button type="button" class="photos-btn action-btn" data-entity-type="quotation" data-idquotations="<?= $row['id'] ?>" title="Photos"><i class="fas fa-camera"></i></button>
|
|
||||||
<button type="button" class="parts-btn action-btn" data-entity-type="quotation" data-idquotations="<?= $row['id'] ?>" title="Parts"><i class="fas fa-puzzle-piece"></i></button>
|
|
||||||
<a href="quotations.php?edit_id=<?= $row['id'] ?>" class="btn btn-secondary action-btn" title="Modifica Dettagliata"><i class="fas fa-edit"></i></a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Modal per conferma creazione nuova quotation -->
|
|
||||||
<div class="modal fade" id="createModal" tabindex="-1" aria-labelledby="createModalLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="createModalLabel">Conferma Creazione Nuova Quotation</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<p>Vuoi creare una nuova quotation?</p>
|
|
||||||
<form id="createModalForm" method="post">
|
|
||||||
<input type="hidden" name="action" value="create">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annulla</button>
|
|
||||||
<button type="button" class="btn btn-primary" id="confirmCreate">Conferma</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Modal per conferma cancellazione -->
|
|
||||||
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="deleteModalLabel">Conferma Cancellazione</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<p>Sicuro di voler cancellare questa quotation?</p>
|
|
||||||
<form id="deleteForm" method="post">
|
|
||||||
<input type="hidden" name="action" value="delete">
|
|
||||||
<input type="hidden" name="id" id="deleteQuotationId">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annulla</button>
|
|
||||||
<button type="button" class="btn btn-danger" id="confirmDelete">Conferma</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="overlay toggle-icon"></div>
|
|
||||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
|
||||||
<?php include('include/footer.php'); ?>
|
|
||||||
</div>
|
|
||||||
<?php include('modal_parts.php'); ?>
|
|
||||||
<?php include('photos_functions.php'); ?>
|
|
||||||
<?php include('jsinclude.php'); ?>
|
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
||||||
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
|
||||||
<script src="photos.js"></script>
|
|
||||||
<script src="parts.js"></script>
|
|
||||||
<script>
|
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
|
||||||
// Inizializza DataTables se non siamo in modalità modifica
|
|
||||||
if (!document.querySelector('#editForm')) {
|
|
||||||
$('#quotationsTable').DataTable({
|
|
||||||
"paging": true,
|
|
||||||
"searching": true,
|
|
||||||
"ordering": true,
|
|
||||||
"info": true,
|
|
||||||
"autoWidth": false,
|
|
||||||
"responsive": true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gestione conferma creazione nel modal
|
|
||||||
document.getElementById('confirmCreate').addEventListener('click', function() {
|
|
||||||
document.getElementById('createModalForm').submit();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Gestione modifica inline e save nella lista
|
|
||||||
document.querySelectorAll('.edit-btn').forEach(btn => {
|
|
||||||
btn.addEventListener('click', function() {
|
|
||||||
const row = this.closest('tr');
|
|
||||||
const id = row.dataset.id;
|
|
||||||
const description = row.querySelector('textarea[name="description"]').value;
|
|
||||||
const customer = row.querySelector('input[name="customer"]').value;
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('action', 'update');
|
|
||||||
formData.append('id', id);
|
|
||||||
formData.append('description', description);
|
|
||||||
formData.append('customer', customer);
|
|
||||||
|
|
||||||
fetch('quotations.php', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
}).then(response => {
|
|
||||||
if (response.ok) {
|
|
||||||
row.classList.add('flash-success');
|
|
||||||
setTimeout(() => row.classList.remove('flash-success'), 500);
|
|
||||||
alert('Quotation modificata con successo!');
|
|
||||||
} else {
|
|
||||||
alert('Errore durante la modifica.');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Gestione apertura modal di cancellazione
|
|
||||||
document.querySelectorAll('.delete-btn').forEach(btn => {
|
|
||||||
btn.addEventListener('click', function() {
|
|
||||||
const id = this.dataset.id;
|
|
||||||
document.getElementById('deleteQuotationId').value = id;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Gestione conferma cancellazione nel modal
|
|
||||||
document.getElementById('confirmDelete').addEventListener('click', function() {
|
|
||||||
document.getElementById('deleteForm').submit();
|
|
||||||
});
|
|
||||||
|
|
||||||
// I bottoni photos e parts usano gli script esistenti (photos.js, parts.js), passando data-idquotations
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Modale per le foto in quotations.php -->
|
|
||||||
<div class="modal" id="photosModal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<span class="close-btn">×</span>
|
|
||||||
<div class="popup-content"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
<?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);
|
|
||||||
|
|
||||||
$idquotations = $data['idquotations'] ?? null;
|
|
||||||
$parts = $data['parts'] ?? [];
|
|
||||||
|
|
||||||
if (!$idquotations || empty($parts)) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$pdo->beginTransaction();
|
|
||||||
|
|
||||||
// Elimina tutte le parti esistenti per idquotations
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM identification_parts WHERE idquotations = :idquotations");
|
|
||||||
$stmt->execute([':idquotations' => $idquotations]);
|
|
||||||
|
|
||||||
// Prepara l'inserimento delle nuove parti
|
|
||||||
$stmt = $pdo->prepare("
|
|
||||||
INSERT INTO identification_parts
|
|
||||||
(idquotations, part_number, part_description, mix, created_at, updated_at)
|
|
||||||
VALUES (:idquotations, :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([
|
|
||||||
':idquotations' => $idquotations,
|
|
||||||
':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()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
<?php
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
include('include/headscript.php');
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
|
|
||||||
$dataURL = $_POST['dataURL'] ?? null;
|
|
||||||
$filename = $_POST['filename'] ?? null;
|
|
||||||
$idquotations = $_POST['idquotations'] ?? null;
|
|
||||||
|
|
||||||
if (!$dataURL || !$filename || !$idquotations) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Verifica che idquotations esista nella tabella quotations
|
|
||||||
$dbHandler = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $dbHandler->getConnection();
|
|
||||||
$stmt = $pdo->prepare("SELECT idquotations FROM quotations WHERE idquotations = :idquotations");
|
|
||||||
$stmt->execute([':idquotations' => $idquotations]);
|
|
||||||
if (!$stmt->fetch()) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'idquotations non valido']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Salva l'immagine
|
|
||||||
$data = explode(',', $dataURL)[1];
|
|
||||||
$decodedData = base64_decode($data);
|
|
||||||
|
|
||||||
$dirPath = '../photostrf/annotated';
|
|
||||||
if (!file_exists($dirPath)) {
|
|
||||||
mkdir($dirPath, 0777, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$filePath = $dirPath . '/' . $filename;
|
|
||||||
file_put_contents($filePath, $decodedData);
|
|
||||||
|
|
||||||
// Registra nel database
|
|
||||||
$stmt = $pdo->prepare("
|
|
||||||
INSERT INTO datadb_photos (idquotations, file_path, file_name, uploaded_at, uploaded_by)
|
|
||||||
VALUES (:idquotations, :file_path, :file_name, NOW(), :uploaded_by)
|
|
||||||
");
|
|
||||||
$stmt->execute([
|
|
||||||
':idquotations' => $idquotations,
|
|
||||||
':file_path' => $filePath,
|
|
||||||
':file_name' => $filename,
|
|
||||||
':uploaded_by' => $iduserlogin
|
|
||||||
]);
|
|
||||||
|
|
||||||
echo json_encode([
|
|
||||||
'success' => true,
|
|
||||||
'file_path' => $filePath,
|
|
||||||
'message' => 'Foto salvata con successo e registrata nel DB'
|
|
||||||
]);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
<?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);
|
|
||||||
|
|
||||||
$idquotations = $data['idquotations'] ?? null;
|
|
||||||
$parts = $data['parts'] ?? [];
|
|
||||||
|
|
||||||
if (!$idquotations || empty($parts)) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$part = $parts[0];
|
|
||||||
$partId = $part['id'] ?? null;
|
|
||||||
$partNumber = $part['part_number'] ?? null;
|
|
||||||
$partDescription = $part['part_description'] ?? '';
|
|
||||||
$mix = $part['mix'] ?? 'N';
|
|
||||||
|
|
||||||
if ($partDescription) {
|
|
||||||
try {
|
|
||||||
if ($partId) {
|
|
||||||
// UPDATE se esiste già la parte
|
|
||||||
$stmt = $pdo->prepare("UPDATE identification_parts
|
|
||||||
SET part_number = :part_number,
|
|
||||||
part_description = :part_description,
|
|
||||||
mix = :mix,
|
|
||||||
updated_at = NOW()
|
|
||||||
WHERE id = :id");
|
|
||||||
$stmt->execute([
|
|
||||||
':id' => $partId,
|
|
||||||
':part_number' => $partNumber,
|
|
||||||
':part_description' => $partDescription,
|
|
||||||
':mix' => $mix
|
|
||||||
]);
|
|
||||||
echo json_encode(['success' => true, 'part_id' => $partId, 'part_number' => $partNumber, 'message' => 'Parte aggiornata con successo']);
|
|
||||||
} else {
|
|
||||||
// INSERT se è nuova
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO identification_parts
|
|
||||||
(idquotations, part_number, part_description, mix, created_at, updated_at)
|
|
||||||
VALUES (:idquotations, :part_number, :part_description, :mix, NOW(), NOW())");
|
|
||||||
$stmt->execute([
|
|
||||||
':idquotations' => $idquotations,
|
|
||||||
':part_number' => $partNumber,
|
|
||||||
':part_description' => $partDescription,
|
|
||||||
':mix' => $mix
|
|
||||||
]);
|
|
||||||
$newId = $pdo->lastInsertId();
|
|
||||||
echo json_encode(['success' => true, 'part_id' => $newId, 'part_number' => $partNumber, 'message' => 'Parte salvata con successo']);
|
|
||||||
}
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio: ' . $e->getMessage()]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Descrizione mancante']);
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once 'include/headscript.php';
|
|
||||||
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
$data = json_decode(file_get_contents('php://input'), true);
|
|
||||||
$template_id = $data['template_id'] ?? null;
|
|
||||||
$mapping_id = $data['mapping_id'] ?? null;
|
|
||||||
$value = $data['value'] ?? null;
|
|
||||||
|
|
||||||
if (!$template_id || !$mapping_id || !isset($value)) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Invalid input']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $db->getConnection();
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare("UPDATE template_mapping SET is_visible_import = ? WHERE id = ? AND template_id = ?");
|
|
||||||
$result = $stmt->execute([$value, $mapping_id, $template_id]);
|
|
||||||
|
|
||||||
if ($result) {
|
|
||||||
echo json_encode(['success' => true]);
|
|
||||||
} else {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Failed to update is_visible_import']);
|
|
||||||
}
|
|
||||||
@@ -4,23 +4,13 @@ include('include/headscript.php');
|
|||||||
|
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_FILES['photo']) || (!isset($_POST['iddatadb']) && !isset($_POST['idquotations']))) {
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_FILES['photo']) || !isset($_POST['iddatadb'])) {
|
||||||
echo json_encode(['success' => false, 'message' => 'Richiesta non valida']);
|
echo json_encode(['success' => false, 'message' => 'Richiesta non valida']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$iddatadb = isset($_POST['iddatadb']) ? intval($_POST['iddatadb']) : null;
|
$iddatadb = intval($_POST['iddatadb']);
|
||||||
$idquotations = isset($_POST['idquotations']) ? intval($_POST['idquotations']) : null;
|
$photo = $_FILES['photo'];
|
||||||
|
|
||||||
if ($iddatadb && $idquotations) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Non è possibile specificare sia iddatadb che idquotations']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$iddatadb && !$idquotations) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'ID TRF o ID quotations mancante']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verifica che l'utente loggato esista in auth_users
|
// Verifica che l'utente loggato esista in auth_users
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
@@ -35,28 +25,6 @@ if (!$userExists) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verifica l'esistenza dell'ID nella tabella corrispondente
|
|
||||||
try {
|
|
||||||
if ($iddatadb) {
|
|
||||||
$stmt = $pdo->prepare("SELECT iddatadb FROM datadb WHERE iddatadb = ?");
|
|
||||||
$stmt->execute([$iddatadb]);
|
|
||||||
if (!$stmt->fetch()) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'iddatadb non valido']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$stmt = $pdo->prepare("SELECT id FROM quotations WHERE id = ?");
|
|
||||||
$stmt->execute([$idquotations]);
|
|
||||||
if (!$stmt->fetch()) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'idquotations non valido']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore nella validazione: ' . $e->getMessage()]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usa un percorso assoluto per la cartella photostrf
|
// Usa un percorso assoluto per la cartella photostrf
|
||||||
$uploadDir = realpath(__DIR__ . '/../photostrf') . '/';
|
$uploadDir = realpath(__DIR__ . '/../photostrf') . '/';
|
||||||
if (!is_dir($uploadDir)) {
|
if (!is_dir($uploadDir)) {
|
||||||
@@ -73,7 +41,6 @@ if (!is_writable($uploadDir)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verifica che il file sia un'immagine (inclusi HEIC/HEIF)
|
// Verifica che il file sia un'immagine (inclusi HEIC/HEIF)
|
||||||
$photo = $_FILES['photo'];
|
|
||||||
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/heic', 'image/heif'];
|
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/heic', 'image/heif'];
|
||||||
if (!in_array($photo['type'], $allowedTypes)) {
|
if (!in_array($photo['type'], $allowedTypes)) {
|
||||||
echo json_encode(['success' => false, 'message' => 'Il file deve essere un\'immagine (JPEG, PNG, GIF, HEIC)']);
|
echo json_encode(['success' => false, 'message' => 'Il file deve essere un\'immagine (JPEG, PNG, GIF, HEIC)']);
|
||||||
@@ -86,11 +53,10 @@ if (!file_exists($photo['tmp_name']) || !is_uploaded_file($photo['tmp_name'])) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rinomina il file: id-timestamp-nomeoriginale.estensione
|
// Rinomina il file: idriga-timestamp-nomeoriginale.estensione
|
||||||
$timestamp = date('YmdHis');
|
$timestamp = date('YmdHis');
|
||||||
$originalName = pathinfo($photo['name'], PATHINFO_FILENAME);
|
$originalName = pathinfo($photo['name'], PATHINFO_FILENAME);
|
||||||
$extension = strtolower(pathinfo($photo['name'], PATHINFO_EXTENSION));
|
$extension = strtolower(pathinfo($photo['name'], PATHINFO_EXTENSION));
|
||||||
$id = $iddatadb ?: $idquotations;
|
|
||||||
|
|
||||||
// Se il file è HEIC/HEIF, convertilo in JPEG
|
// Se il file è HEIC/HEIF, convertilo in JPEG
|
||||||
if (in_array($photo['type'], ['image/heic', 'image/heif'])) {
|
if (in_array($photo['type'], ['image/heic', 'image/heif'])) {
|
||||||
@@ -108,11 +74,11 @@ if (in_array($photo['type'], ['image/heic', 'image/heif'])) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Crea un nuovo nome per il file JPEG
|
// Crea un nuovo nome per il file JPEG
|
||||||
$newFileName = "{$id}-{$timestamp}-{$originalName}.jpg";
|
$newFileName = "{$iddatadb}-{$timestamp}-{$originalName}.jpg";
|
||||||
$destination = $uploadDir . $newFileName;
|
$destination = $uploadDir . $newFileName;
|
||||||
|
|
||||||
// Salva l'immagine come JPEG
|
// Salva l'immagine come JPEG
|
||||||
if (!imagejpeg($image, $destination, 90)) {
|
if (!imagejpeg($image, $destination, 90)) { // 90 è la qualità JPEG
|
||||||
imagedestroy($image);
|
imagedestroy($image);
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore durante la conversione del file HEIC in JPEG']);
|
echo json_encode(['success' => false, 'message' => 'Errore durante la conversione del file HEIC in JPEG']);
|
||||||
exit;
|
exit;
|
||||||
@@ -122,7 +88,7 @@ if (in_array($photo['type'], ['image/heic', 'image/heif'])) {
|
|||||||
imagedestroy($image);
|
imagedestroy($image);
|
||||||
} else {
|
} else {
|
||||||
// Per i formati non HEIC, usa il nome e l'estensione originali
|
// Per i formati non HEIC, usa il nome e l'estensione originali
|
||||||
$newFileName = "{$id}-{$timestamp}-{$originalName}.{$extension}";
|
$newFileName = "{$iddatadb}-{$timestamp}-{$originalName}.{$extension}";
|
||||||
$destination = $uploadDir . $newFileName;
|
$destination = $uploadDir . $newFileName;
|
||||||
|
|
||||||
// Salva il file
|
// Salva il file
|
||||||
@@ -139,12 +105,7 @@ error_log("Destination: $destination");
|
|||||||
error_log("Temp file: " . $photo['tmp_name']);
|
error_log("Temp file: " . $photo['tmp_name']);
|
||||||
|
|
||||||
// Salva il riferimento nel database
|
// Salva il riferimento nel database
|
||||||
try {
|
$stmt = $pdo->prepare("INSERT INTO datadb_photos (iddatadb, file_path, file_name, uploaded_by) VALUES (?, ?, ?, ?)");
|
||||||
$stmt = $pdo->prepare("INSERT INTO datadb_photos (iddatadb, idquotations, file_path, file_name, uploaded_by) VALUES (?, ?, ?, ?, ?)");
|
$stmt->execute([$iddatadb, $newFileName, $newFileName, $iduserlogin]);
|
||||||
$stmt->execute([$iddatadb, $idquotations, $newFileName, $newFileName, $iduserlogin]);
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore durante il salvataggio nel database: ' . $e->getMessage()]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
echo json_encode(['success' => true, 'message' => 'Foto caricata con successo']);
|
echo json_encode(['success' => true, 'message' => 'Foto caricata con successo']);
|
||||||
|
|||||||
@@ -5,41 +5,24 @@ include('include/headscript.php');
|
|||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
// Verifica che almeno uno degli ID sia passato
|
// Verifica che l'iddatadb sia stato passato
|
||||||
$iddatadb = isset($_GET['iddatadb']) && !empty($_GET['iddatadb']) ? intval($_GET['iddatadb']) : null;
|
if (!isset($_GET['iddatadb']) || empty($_GET['iddatadb'])) {
|
||||||
$idquotations = isset($_GET['idquotations']) && !empty($_GET['idquotations']) ? intval($_GET['idquotations']) : null;
|
die('ID riga non fornito');
|
||||||
|
|
||||||
if (!$iddatadb && !$idquotations) {
|
|
||||||
die('ID riga o ID quotations non fornito');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($iddatadb && $idquotations) {
|
$iddatadb = intval($_GET['iddatadb']);
|
||||||
die('Non è possibile specificare sia iddatadb che idquotations');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verifica che l'utente loggato esista
|
// Recupera i dettagli della riga (idriga e sample_code)
|
||||||
$stmt = $pdo->prepare("SELECT id FROM auth_users WHERE id = ?");
|
$stmt = $pdo->prepare("SELECT iddatadb, sample_code FROM datadb WHERE iddatadb = ?");
|
||||||
$stmt->execute([$iduserlogin]);
|
$stmt->execute([$iddatadb]);
|
||||||
if (!$stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
||||||
die('Utente non valido');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determina quale ID usare e verifica l'esistenza
|
|
||||||
$paramName = $iddatadb ? 'iddatadb' : 'idquotations';
|
|
||||||
$paramValue = $iddatadb ?: $idquotations;
|
|
||||||
$table = $iddatadb ? 'datadb' : 'quotations';
|
|
||||||
$field = $iddatadb ? 'sample_code' : 'quotation_code';
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT {$paramName}, {$field} FROM {$table} WHERE {$paramName} = ?");
|
|
||||||
$stmt->execute([$paramValue]);
|
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
if (!$row) {
|
if (!$row) {
|
||||||
die('Riga non trovata');
|
die('Riga non trovata');
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = $row[$paramName];
|
$idriga = $row['iddatadb'];
|
||||||
$code = $row[$field] ?? 'Non disponibile';
|
$sampleCode = $row['sample_code'] ?? 'Non disponibile';
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@@ -49,23 +32,17 @@ $code = $row[$field] ?? 'Non disponibile';
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Carica Foto da Mobile</title>
|
<title>Carica Foto da Mobile</title>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: #f4f4f4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-area {
|
.upload-area {
|
||||||
border: 2px dashed #ccc;
|
border: 2px dashed #ccc;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin: 20px auto;
|
margin: 20px 0;
|
||||||
max-width: 500px;
|
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-area.highlight {
|
.upload-area.highlight {
|
||||||
@@ -79,121 +56,57 @@ $code = $row[$field] ?? 'Non disponibile';
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
max-width: 500px;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.photo-item img {
|
.photo-item img {
|
||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
max-height: 100px;
|
max-height: 100px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loader {
|
|
||||||
display: none;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
z-index: 1000;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loader i {
|
|
||||||
font-size: 40px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-message {
|
|
||||||
color: red;
|
|
||||||
margin: 10px 0;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.delete-photo-btn {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: #dc3545;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h2>Carica Foto per ID: <?= htmlspecialchars($id) ?></h2>
|
<h2>Carica Foto per ID Riga: <?= htmlspecialchars($idriga) ?></h2>
|
||||||
<p><strong>Codice:</strong> <?= htmlspecialchars($code) ?></p>
|
<p><strong>Sample Code:</strong> <?= htmlspecialchars($sampleCode) ?></p>
|
||||||
<div class="loader" id="loader">
|
|
||||||
<div>
|
|
||||||
<i class="fas fa-spinner fa-spin"></i>
|
|
||||||
<p>Caricamento in corso...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="error-message" id="errorMessage"></div>
|
|
||||||
<div class="upload-area" id="uploadArea">
|
<div class="upload-area" id="uploadArea">
|
||||||
<p>Scatta una foto o seleziona immagini</p>
|
<p>Scatta una foto o seleziona un'immagine</p>
|
||||||
<input type="file" id="photoInput" accept="image/*" capture="camera" multiple style="display: none;">
|
<input type="file" id="photoInput" accept="image/*" capture="camera">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="photosList">
|
||||||
|
<!-- Le foto verranno caricate dinamicamente -->
|
||||||
</div>
|
</div>
|
||||||
<div id="photosList"></div>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const uploadArea = document.getElementById('uploadArea');
|
const uploadArea = document.getElementById('uploadArea');
|
||||||
const photoInput = document.getElementById('photoInput');
|
const photoInput = document.getElementById('photoInput');
|
||||||
const photosList = document.getElementById('photosList');
|
const photosList = document.getElementById('photosList');
|
||||||
const loader = document.getElementById('loader');
|
|
||||||
const errorMessage = document.getElementById('errorMessage');
|
|
||||||
const iddatadb = '<?= $iddatadb ?>';
|
|
||||||
const idquotations = '<?= $idquotations ?>';
|
|
||||||
const endpoint = idquotations ? 'load_photo_quotation.php' : 'load_photo.php';
|
|
||||||
const dataParam = idquotations ? {
|
|
||||||
idquotations: idquotations
|
|
||||||
} : {
|
|
||||||
iddatadb: iddatadb
|
|
||||||
};
|
|
||||||
|
|
||||||
// Carica le foto esistenti all'avvio
|
// Carica le foto esistenti all'avvio
|
||||||
loadPhotos();
|
loadPhotos();
|
||||||
|
|
||||||
// Gestione drag-and-drop
|
// Gestione del click sull'area di upload
|
||||||
uploadArea.addEventListener('dragover', (e) => {
|
uploadArea.addEventListener('click', () => {
|
||||||
e.preventDefault();
|
photoInput.click();
|
||||||
uploadArea.classList.add('highlight');
|
|
||||||
});
|
|
||||||
uploadArea.addEventListener('dragleave', () => {
|
|
||||||
uploadArea.classList.remove('highlight');
|
|
||||||
});
|
|
||||||
uploadArea.addEventListener('drop', (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
uploadArea.classList.remove('highlight');
|
|
||||||
handleFiles(e.dataTransfer.files);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Gestione click sull'area di upload
|
// Gestione del caricamento delle foto
|
||||||
uploadArea.addEventListener('click', () => photoInput.click());
|
photoInput.addEventListener('change', () => {
|
||||||
|
handleFiles(photoInput.files);
|
||||||
// Gestione caricamento foto
|
});
|
||||||
photoInput.addEventListener('change', () => handleFiles(photoInput.files));
|
|
||||||
|
|
||||||
async function handleFiles(files) {
|
async function handleFiles(files) {
|
||||||
loader.style.display = 'flex';
|
|
||||||
errorMessage.style.display = 'none';
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (!file.type.startsWith('image/')) {
|
if (!file.type.startsWith('image/')) {
|
||||||
showError('Per favore, carica solo immagini!');
|
alert('Per favore, carica solo immagini!');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('photo', file);
|
formData.append('photo', file);
|
||||||
if (iddatadb) formData.append('iddatadb', iddatadb);
|
formData.append('iddatadb', '<?= $iddatadb ?>');
|
||||||
if (idquotations) formData.append('idquotations', idquotations);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('upload_photo.php', {
|
const response = await fetch('upload_photo.php', {
|
||||||
@@ -201,85 +114,31 @@ $code = $row[$field] ?? 'Non disponibile';
|
|||||||
body: formData
|
body: formData
|
||||||
});
|
});
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (!result.success) {
|
|
||||||
showError('Errore durante il caricamento: ' + result.message);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
showError('Errore di rete: ' + error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loadPhotos();
|
|
||||||
loader.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadPhotos() {
|
|
||||||
loader.style.display = 'flex';
|
|
||||||
errorMessage.style.display = 'none';
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${endpoint}?${new URLSearchParams(dataParam)}`);
|
|
||||||
const result = await response.json();
|
|
||||||
photosList.innerHTML = '';
|
|
||||||
if (result.success && result.photos && result.photos.length > 0) {
|
|
||||||
for (const photo of result.photos) {
|
|
||||||
const photoName = photo.split('/').pop();
|
|
||||||
const photoItem = document.createElement('div');
|
|
||||||
photoItem.className = 'photo-item';
|
|
||||||
photoItem.innerHTML = `
|
|
||||||
<img src="${photo}" alt="${photoName}">
|
|
||||||
<div style="flex: 1; text-align: left;">
|
|
||||||
<strong>Nome:</strong> ${photoName}<br>
|
|
||||||
<strong>Caricata il:</strong> Non disponibile
|
|
||||||
</div>
|
|
||||||
<button class="delete-photo-btn" data-photo-path="${photo}">
|
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
</button>`;
|
|
||||||
photosList.appendChild(photoItem);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
photosList.innerHTML = '<p>Nessuna foto presente.</p>';
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
showError('Errore durante il caricamento delle foto: ' + error.message);
|
|
||||||
}
|
|
||||||
loader.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
function showError(message) {
|
|
||||||
errorMessage.textContent = message;
|
|
||||||
errorMessage.style.display = 'block';
|
|
||||||
setTimeout(() => errorMessage.style.display = 'none', 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gestione eliminazione foto
|
|
||||||
photosList.addEventListener('click', async (e) => {
|
|
||||||
if (e.target.closest('.delete-photo-btn')) {
|
|
||||||
const button = e.target.closest('.delete-photo-btn');
|
|
||||||
const photoPath = button.dataset.photoPath;
|
|
||||||
if (confirm('Sei sicuro di voler eliminare questa foto?')) {
|
|
||||||
loader.style.display = 'flex';
|
|
||||||
try {
|
|
||||||
const response = await fetch('delete_photo.php', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
file_path: photoPath
|
|
||||||
})
|
|
||||||
});
|
|
||||||
const result = await response.json();
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
loadPhotos();
|
loadPhotos(); // Ricarica le foto
|
||||||
} else {
|
} else {
|
||||||
showError('Errore durante l\'eliminazione: ' + result.message);
|
alert('Errore durante il caricamento: ' + result.message);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showError('Errore durante l\'eliminazione: ' + error.message);
|
alert('Errore durante il caricamento: ' + error.message);
|
||||||
}
|
}
|
||||||
loader.style.display = 'none';
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funzione per caricare le foto esistenti
|
||||||
|
async function loadPhotos() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`photos_popup.php?iddatadb=<?= $iddatadb ?>`);
|
||||||
|
const html = await response.text();
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(html, 'text/html');
|
||||||
|
const photosListContent = doc.querySelector('#photosList').innerHTML;
|
||||||
|
photosList.innerHTML = photosListContent;
|
||||||
|
} catch (error) {
|
||||||
|
photosList.innerHTML = `<p>Errore durante il caricamento delle foto: ${error.message}</p>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user