update entra id

This commit is contained in:
Claudio 2025-05-13 15:03:09 +02:00
parent 6752d3515f
commit 2a96d24de7
44 changed files with 3835 additions and 1856 deletions

View File

@ -16,7 +16,24 @@ class PasswordResetRequest extends Request
return [ return [
'token' => 'required', 'token' => 'required',
'email' => 'required|email', 'email' => 'required|email',
'password' => 'required|confirmed|min:8' 'password' => [
'required',
'confirmed',
'min:8',
'regex:/^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/'
],
];
}
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages()
{
return [
'password.regex' => __('The password must be at least 8 characters long and contain at least one number and one special character (@$!%*?&).')
]; ];
} }

View File

@ -14,10 +14,15 @@ class RegisterRequest extends Request
*/ */
public function rules() public function rules()
{ {
$rules = [ $rules = [
'email' => 'required|email|unique:users,email', 'email' => 'required|email|unique:users,email',
'username' => 'required|unique:users,username', 'username' => 'required|unique:users,username',
'password' => 'required|confirmed|min:8', 'password' => [
'required',
'confirmed',
'min:8',
'regex:/^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/'
],
]; ];
if (setting('registration.captcha.enabled')) { if (setting('registration.captcha.enabled')) {
@ -36,10 +41,11 @@ class RegisterRequest extends Request
* *
* @return array * @return array
*/ */
public function messages() public function messages()
{ {
return [ return [
'tos.accepted' => __('You have to accept Terms of Service.') 'tos.accepted' => __('You have to accept Terms of Service.'),
'password.regex' => __('The password must be at least 8 characters long and contain at least one number and one special character (@$!%*?&).')
]; ];
} }

View File

@ -17,7 +17,12 @@ class CreateUserRequest extends Request
$rules = [ $rules = [
'email' => 'required|email|unique:users,email', 'email' => 'required|email|unique:users,email',
'username' => 'nullable|unique:users,username', 'username' => 'nullable|unique:users,username',
'password' => 'required|min:6|confirmed', 'password' => [
'required',
'confirmed',
'min:8',
'regex:/^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/'
],
'birthday' => 'nullable|date', 'birthday' => 'nullable|date',
'role_id' => 'required|exists:roles,id', 'role_id' => 'required|exists:roles,id',
'verified' => 'boolean' 'verified' => 'boolean'
@ -29,4 +34,16 @@ class CreateUserRequest extends Request
return $rules; return $rules;
} }
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages()
{
return [
'password.regex' => __('The password must be at least 8 characters long and contain at least one number and one special character (@$!%*?&).')
];
}
} }

View File

@ -19,7 +19,24 @@ class UpdateLoginDetailsRequest extends Request
return [ return [
'email' => 'required|email|unique:users,email,' . $user->id, 'email' => 'required|email|unique:users,email,' . $user->id,
'username' => 'nullable|unique:users,username,' . $user->id, 'username' => 'nullable|unique:users,username,' . $user->id,
'password' => 'nullable|min:8|confirmed' 'password' => [
'nullable',
'confirmed',
'min:8',
'regex:/^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/'
],
];
}
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages()
{
return [
'password.regex' => __('The password must be at least 8 characters long and contain at least one number and one special character (@$!%*?&).')
]; ];
} }

View File

@ -21,11 +21,28 @@ class UpdateUserRequest extends Request
return [ return [
'email' => 'email|unique:users,email,' . $user->id, 'email' => 'email|unique:users,email,' . $user->id,
'username' => 'nullable|unique:users,username,' . $user->id, 'username' => 'nullable|unique:users,username,' . $user->id,
'password' => 'min:6|confirmed', 'password' => [
'nullable',
'confirmed',
'min:8',
'regex:/^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/'
],
'birthday' => 'nullable|date', 'birthday' => 'nullable|date',
'role_id' => 'exists:roles,id', 'role_id' => 'exists:roles,id',
'country_id' => 'exists:countries,id', 'country_id' => 'exists:countries,id',
'status' => Rule::in(array_keys(UserStatus::lists())) 'status' => Rule::in(array_keys(UserStatus::lists()))
]; ];
} }
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages()
{
return [
'password.regex' => __('The password must be at least 8 characters long and contain at least one number and one special character (@$!%*?&).')
];
}
} }

View File

@ -33,7 +33,10 @@ class EventServiceProvider extends ServiceProvider
], ],
Verified::class => [ Verified::class => [
ActivateUser::class ActivateUser::class
] ],
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
'SocialiteProviders\\Azure\\AzureExtendSocialite@handle', // Usa una stringa con @handle
],
]; ];
/** /**

View File

@ -42,6 +42,7 @@
"laravel/ui": "^4.0", "laravel/ui": "^4.0",
"laravelcollective/html": "^6.3", "laravelcollective/html": "^6.3",
"proengsoft/laravel-jsvalidation": "^4.0.0", "proengsoft/laravel-jsvalidation": "^4.0.0",
"socialiteproviders/microsoft-azure": "^5.2",
"spatie/laravel-query-builder": "^5.0", "spatie/laravel-query-builder": "^5.0",
"vanguardapp/activity-log": "^5.0", "vanguardapp/activity-log": "^5.0",
"vanguardapp/announcements": "^5.0", "vanguardapp/announcements": "^5.0",

127
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "0868d0ed54d8695b929de611fc8beeab", "content-hash": "df98df06998bb59ef615306b9f0d421b",
"packages": [ "packages": [
{ {
"name": "akaunting/laravel-setting", "name": "akaunting/laravel-setting",
@ -4145,6 +4145,131 @@
}, },
"time": "2024-07-01T07:33:21+00:00" "time": "2024-07-01T07:33:21+00:00"
}, },
{
"name": "socialiteproviders/manager",
"version": "v4.8.1",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Manager.git",
"reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/8180ec14bef230ec2351cff993d5d2d7ca470ef4",
"reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4",
"shasum": ""
},
"require": {
"illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0",
"laravel/socialite": "^5.5",
"php": "^8.1"
},
"require-dev": {
"mockery/mockery": "^1.2",
"phpunit/phpunit": "^9.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"SocialiteProviders\\Manager\\ServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"SocialiteProviders\\Manager\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Andy Wendt",
"email": "andy@awendt.com"
},
{
"name": "Anton Komarev",
"email": "a.komarev@cybercog.su"
},
{
"name": "Miguel Piedrafita",
"email": "soy@miguelpiedrafita.com"
},
{
"name": "atymic",
"email": "atymicq@gmail.com",
"homepage": "https://atymic.dev"
}
],
"description": "Easily add new or override built-in providers in Laravel Socialite.",
"homepage": "https://socialiteproviders.com",
"keywords": [
"laravel",
"manager",
"oauth",
"providers",
"socialite"
],
"support": {
"issues": "https://github.com/socialiteproviders/manager/issues",
"source": "https://github.com/socialiteproviders/manager"
},
"time": "2025-02-24T19:33:30+00:00"
},
{
"name": "socialiteproviders/microsoft-azure",
"version": "5.2.0",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Microsoft-Azure.git",
"reference": "453d62c9d7e3b3b76e94c913fb46e68a33347b16"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/453d62c9d7e3b3b76e94c913fb46e68a33347b16",
"reference": "453d62c9d7e3b3b76e94c913fb46e68a33347b16",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^8.0",
"socialiteproviders/manager": "^4.4"
},
"type": "library",
"autoload": {
"psr-4": {
"SocialiteProviders\\Azure\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Chris Hemmings",
"email": "chris@hemmin.gs"
}
],
"description": "Microsoft Azure OAuth2 Provider for Laravel Socialite",
"keywords": [
"azure",
"laravel",
"microsoft",
"oauth",
"provider",
"socialite"
],
"support": {
"docs": "https://socialiteproviders.com/microsoft-azure",
"issues": "https://github.com/socialiteproviders/providers/issues",
"source": "https://github.com/socialiteproviders/providers"
},
"time": "2024-03-15T03:02:10+00:00"
},
{ {
"name": "spatie/laravel-package-tools", "name": "spatie/laravel-package-tools",
"version": "1.14.2", "version": "1.14.2",

View File

@ -198,7 +198,7 @@ return [
Illuminate\Pagination\PaginationServiceProvider::class, Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class, Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class, Illuminate\Queue\QueueServiceProvider::class,
// Illuminate\Redis\RedisServiceProvider::class, // Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class, Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Translation\TranslationServiceProvider::class,
@ -227,6 +227,7 @@ return [
Vanguard\Providers\RouteServiceProvider::class, Vanguard\Providers\RouteServiceProvider::class,
Vanguard\Services\Auth\TwoFactor\AuthyServiceProvider::class, Vanguard\Services\Auth\TwoFactor\AuthyServiceProvider::class,
Vanguard\Providers\VanguardServiceProvider::class, Vanguard\Providers\VanguardServiceProvider::class,
\SocialiteProviders\Manager\ServiceProvider::class,
], ],
/* /*

View File

@ -12,10 +12,10 @@ return [
*/ */
'social' => [ 'social' => [
'providers' => ['facebook', 'twitter', 'google'] 'providers' => ['azure']
], ],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| JSON API | JSON API
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -65,5 +65,11 @@ return [
'authy' => [ 'authy' => [
'key' => env('AUTHY_KEY') 'key' => env('AUTHY_KEY')
],
'azure' => [
'client_id' => env('AZURE_CLIENT_ID'),
'client_secret' => env('AZURE_CLIENT_SECRET'),
'redirect' => env('AZURE_REDIRECT_URI'),
] ]
]; ];

BIN
public/userarea.zip Normal file

Binary file not shown.

View File

@ -0,0 +1,167 @@
<?php
// Database connection
include('../../Connections/repnew.php');
// Starts the session to handle session variables
$conn = new mysqli($servername, $username, $password, $database);
// Check the connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Check if POST request was received
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Array to collect messages about file processing
$file_messages = [];
// Receive JSON from the laboratory via a field in the form (e.g., 'json_data')
if (isset($_POST['json_data'])) {
$json_data = $_POST['json_data'];
// Decode JSON for optional validation
$decoded_data = json_decode($json_data, true);
// If the JSON is valid
if (json_last_error() === JSON_ERROR_NONE) {
// Authenticate using key, secret_key, and reflab
if (!isset($decoded_data['key']) || !isset($decoded_data['secret_key']) || !isset($decoded_data['reflab'])) {
echo json_encode([
"status" => "error",
"message" => "Missing authentication fields (key, secret_key, reflab)."
]);
exit;
}
$api_key = $decoded_data['key'];
$secret_key = $decoded_data['secret_key'];
$reflab = $decoded_data['reflab'];
$query = "SELECT * FROM laboratories WHERE reflab = ? AND api_key = ?";
$stmt = $conn->prepare($query);
$stmt->bind_param("ss", $reflab, $api_key);
$stmt->execute();
$result = $stmt->get_result();
// Check if a valid laboratory was found with `reflab` and `api_key`
if ($result->num_rows > 0) {
$row = $result->fetch_assoc();
// Verify the status of the laboratory
if ($row['status'] !== 'active') {
echo json_encode([
"status" => "error",
"message" => "Laboratory is inactive."
]);
exit;
}
// Verify the secret key using `password_verify`
if (!password_verify($secret_key, $row['api_secret'])) {
echo json_encode([
"status" => "error",
"message" => "Invalid secret key."
]);
exit;
}
} else {
// Check if the `reflab` is valid, but the `api_key` doesn't match
$query = "SELECT * FROM laboratories WHERE reflab = ?";
$stmt = $conn->prepare($query);
$stmt->bind_param("s", $reflab);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
echo json_encode([
"status" => "error",
"message" => "Invalid API key."
]);
} else {
echo json_encode([
"status" => "error",
"message" => "Invalid reflab."
]);
}
exit;
}
// Generate a UUID to uniquely identify the record
$uuid = uniqid(); // Alternatively, use UUID() in MySQL
// Extract some information from JSON
if (!isset($decoded_data['product']['products_refnumber'])) {
echo json_encode([
"status" => "error",
"message" => "Missing product reference number."
]);
exit;
}
$product_refnumber = $decoded_data['product']['products_refnumber']; // Product number
$report_number = $decoded_data['product']['reports'][0]['reportsNumberLab'] ?? null; // Report number
$rating = $decoded_data['product']['reports'][0]['reportsRating'] ?? null; // Report rating (e.g., Pass/Fail)
$saved_at = date("Y-m-d H:i:s"); // Save date
// Query to insert data into the temp_json_queue table
$stmt = $conn->prepare("INSERT INTO temp_json_queue (uuid, lab_id, json_data) VALUES (?, ?, ?)");
$lab_id = 1; // Set lab_id to a fixed value for testing purposes
$stmt->bind_param("sss", $uuid, $lab_id, $json_data);
if ($stmt->execute()) {
// Handle file uploads if they exist
if (!empty($_FILES)) {
include('process_files.php'); // Include file processing logic here
// Retrieve any messages added in process_files.php for files
if (!empty($GLOBALS['file_messages'])) {
$file_messages = $GLOBALS['file_messages'];
}
}
// Set a session variable to notify the report import
$_SESSION['new_report'] = [
'report_number' => $report_number,
'rating' => $rating,
'timestamp' => time() // You can use a timestamp to manage the expiration of the notification
];
echo json_encode([
"status" => "success",
"message" => "Data successfully saved.",
"uuid" => $uuid,
"product_refnumber" => $product_refnumber, // Product number
"report_number" => $report_number, // Report number
"rating" => $rating, // Report rating
"saved_at" => $saved_at, // Save date
"file_messages" => $file_messages // Include file messages
]);
} else {
echo json_encode([
"status" => "error",
"message" => "Failed to save data."
]);
}
$stmt->close();
} else {
// If the JSON is invalid
echo json_encode([
"status" => "error",
"message" => "Invalid JSON format."
]);
}
} else {
echo json_encode([
"status" => "error",
"message" => "Missing JSON data."
]);
}
} else {
echo json_encode([
"status" => "error",
"message" => "Invalid request method."
]);
}
// Close the database connection
$conn->close();

View File

@ -23,73 +23,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// If the JSON is valid // If the JSON is valid
if (json_last_error() === JSON_ERROR_NONE) { if (json_last_error() === JSON_ERROR_NONE) {
// Authenticate using key, secret_key, and reflab // Check only for the required product_refnumber
if (!isset($decoded_data['key']) || !isset($decoded_data['secret_key']) || !isset($decoded_data['reflab'])) { if (!isset($decoded_data['product']) || !is_array($decoded_data['product']) || !isset($decoded_data['product'][0]['products_refnumber'])) {
echo json_encode([
"status" => "error",
"message" => "Missing authentication fields (key, secret_key, reflab)."
]);
exit;
}
$api_key = $decoded_data['key'];
$secret_key = $decoded_data['secret_key'];
$reflab = $decoded_data['reflab'];
$query = "SELECT * FROM laboratories WHERE reflab = ? AND api_key = ?";
$stmt = $conn->prepare($query);
$stmt->bind_param("ss", $reflab, $api_key);
$stmt->execute();
$result = $stmt->get_result();
// Check if a valid laboratory was found with `reflab` and `api_key`
if ($result->num_rows > 0) {
$row = $result->fetch_assoc();
// Verify the status of the laboratory
if ($row['status'] !== 'active') {
echo json_encode([
"status" => "error",
"message" => "Laboratory is inactive."
]);
exit;
}
// Verify the secret key using `password_verify`
if (!password_verify($secret_key, $row['api_secret'])) {
echo json_encode([
"status" => "error",
"message" => "Invalid secret key."
]);
exit;
}
} else {
// Check if the `reflab` is valid, but the `api_key` doesn't match
$query = "SELECT * FROM laboratories WHERE reflab = ?";
$stmt = $conn->prepare($query);
$stmt->bind_param("s", $reflab);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
echo json_encode([
"status" => "error",
"message" => "Invalid API key."
]);
} else {
echo json_encode([
"status" => "error",
"message" => "Invalid reflab."
]);
}
exit;
}
// Generate a UUID to uniquely identify the record
$uuid = uniqid(); // Alternatively, use UUID() in MySQL
// Extract some information from JSON
if (!isset($decoded_data['product']['products_refnumber'])) {
echo json_encode([ echo json_encode([
"status" => "error", "status" => "error",
"message" => "Missing product reference number." "message" => "Missing product reference number."
@ -97,9 +32,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
exit; exit;
} }
$product_refnumber = $decoded_data['product']['products_refnumber']; // Product number // Generate a UUID to uniquely identify the record
$report_number = $decoded_data['product']['reports'][0]['reportsNumberLab'] ?? null; // Report number $uuid = uniqid(); // Alternatively, use UUID() in MySQL
$rating = $decoded_data['product']['reports'][0]['reportsRating'] ?? null; // Report rating (e.g., Pass/Fail)
// Extract some information from JSON
$product_refnumber = $decoded_data['product'][0]['products_refnumber'];
$report_number = $decoded_data['product'][0]['reports'][0]['reportsNumberLab'] ?? null;
$rating = $decoded_data['product'][0]['reports'][0]['reportsRating'] ?? null;
$saved_at = date("Y-m-d H:i:s"); // Save date $saved_at = date("Y-m-d H:i:s"); // Save date
// Query to insert data into the temp_json_queue table // Query to insert data into the temp_json_queue table

View File

@ -0,0 +1,168 @@
<?php
// Database connection
include('../../Connections/repnew.php');
// Starts the session to handle session variables
$conn = new mysqli($servername, $username, $password, $database);
// Check the connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Check if POST request was received
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Array to collect messages about file processing
$file_messages = [];
// Receive JSON from the laboratory via a field in the form (e.g., 'json_data')
if (isset($_POST['json_data'])) {
$json_data = $_POST['json_data'];
// Decode JSON for optional validation
$decoded_data = json_decode($json_data, true);
// If the JSON is valid
if (json_last_error() === JSON_ERROR_NONE) {
// Authenticate using key, secret_key, and reflab
if (!isset($decoded_data['key']) || !isset($decoded_data['secret_key']) || !isset($decoded_data['reflab'])) {
echo json_encode([
"status" => "error",
"message" => "Missing authentication fields (key, secret_key, reflab)."
]);
exit;
}
$api_key = $decoded_data['key'];
$secret_key = $decoded_data['secret_key'];
$reflab = $decoded_data['reflab'];
$query = "SELECT * FROM laboratories WHERE reflab = ? AND api_key = ?";
$stmt = $conn->prepare($query);
$stmt->bind_param("ss", $reflab, $api_key);
$stmt->execute();
$result = $stmt->get_result();
// Check if a valid laboratory was found with `reflab` and `api_key`
if ($result->num_rows > 0) {
$row = $result->fetch_assoc();
// Verify the status of the laboratory
if ($row['status'] !== 'active') {
echo json_encode([
"status" => "error",
"message" => "Laboratory is inactive."
]);
exit;
}
// Verify the secret key using `password_verify`
if (!password_verify($secret_key, $row['api_secret'])) {
echo json_encode([
"status" => "error",
"message" => "Invalid secret key."
]);
exit;
}
} else {
// Check if the `reflab` is valid, but the `api_key` doesn't match
$query = "SELECT * FROM laboratories WHERE reflab = ?";
$stmt = $conn->prepare($query);
$stmt->bind_param("s", $reflab);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
echo json_encode([
"status" => "error",
"message" => "Invalid API key."
]);
} else {
echo json_encode([
"status" => "error",
"message" => "Invalid reflab."
]);
}
exit;
}
// Generate a UUID to uniquely identify the record
$uuid = uniqid(); // Alternatively, use UUID() in MySQL
// Extract some information from JSON
// Estrai products_refnumber dal primo elemento dell'array product
if (!isset($decoded_data['product']) || !is_array($decoded_data['product']) || !isset($decoded_data['product'][0]['products_refnumber'])) {
echo json_encode([
"status" => "error",
"message" => "Missing product reference number."
]);
exit;
}
$product_refnumber = $decoded_data['product'][0]['products_refnumber'];
$report_number = $decoded_data['product'][0]['reports'][0]['reportsNumberLab'] ?? null;
$rating = $decoded_data['product'][0]['reports'][0]['reportsRating'] ?? null;
$saved_at = date("Y-m-d H:i:s"); // Save date
// Query to insert data into the temp_json_queue table
$stmt = $conn->prepare("INSERT INTO temp_json_queue (uuid, lab_id, json_data) VALUES (?, ?, ?)");
$lab_id = 1; // Set lab_id to a fixed value for testing purposes
$stmt->bind_param("sss", $uuid, $lab_id, $json_data);
if ($stmt->execute()) {
// Handle file uploads if they exist
if (!empty($_FILES)) {
include('process_files.php'); // Include file processing logic here
// Retrieve any messages added in process_files.php for files
if (!empty($GLOBALS['file_messages'])) {
$file_messages = $GLOBALS['file_messages'];
}
}
// Set a session variable to notify the report import
$_SESSION['new_report'] = [
'report_number' => $report_number,
'rating' => $rating,
'timestamp' => time() // You can use a timestamp to manage the expiration of the notification
];
echo json_encode([
"status" => "success",
"message" => "Data successfully saved.",
"uuid" => $uuid,
"product_refnumber" => $product_refnumber, // Product number
"report_number" => $report_number, // Report number
"rating" => $rating, // Report rating
"saved_at" => $saved_at, // Save date
"file_messages" => $file_messages // Include file messages
]);
} else {
echo json_encode([
"status" => "error",
"message" => "Failed to save data."
]);
}
$stmt->close();
} else {
// If the JSON is invalid
echo json_encode([
"status" => "error",
"message" => "Invalid JSON format."
]);
}
} else {
echo json_encode([
"status" => "error",
"message" => "Missing JSON data."
]);
}
} else {
echo json_encode([
"status" => "error",
"message" => "Invalid request method."
]);
}
// Close the database connection
$conn->close();

View File

@ -0,0 +1,40 @@
<?php
require_once __DIR__ . '/../../../vendor/autoload.php';
use Dotenv\Dotenv;
class DBHandlerSelect
{
private $pdo;
public function __construct()
{
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 3));
$dotenv->load();
$host = $_ENV['DB_HOST'];
$db = $_ENV['DB_DATABASE'];
$user = $_ENV['DB_USERNAME'];
$pass = $_ENV['DB_PASSWORD'];
$charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$this->pdo = new PDO($dsn, $user, $pass, $options);
} catch (PDOException $e) {
die("Database connection failed: " . $e->getMessage());
}
}
public function getConnection()
{
return $this->pdo;
}
}

View File

@ -0,0 +1,61 @@
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use Dotenv\Dotenv;
// Carica le variabili di ambiente
require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; // Assicurati che PHPMailer e Dotenv siano installati con Composer
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 2)); // Se la cartella `class` è a 2 livelli sopra la root
$dotenv->load();
function sendEmail($to, $subject, $body, $attachments = [], $cc = [], $bcc = [])
{
// Configurazione SMTP
$mail = new PHPMailer(true);
try {
// Configurazione server SMTP con dati da .env
$mail->isSMTP();
$mail->Host = $_ENV['MAIL_HOST'] ?? 'smtp.example.com';
$mail->SMTPAuth = true;
$mail->Username = $_ENV['MAIL_USERNAME'] ?? 'email@example.com';
$mail->Password = $_ENV['MAIL_PASSWORD'] ?? 'password';
$mail->SMTPSecure = $_ENV['MAIL_ENCRYPTION'] ?? PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = $_ENV['MAIL_PORT'] ?? 587;
// Mittente
$mail->setFrom($_ENV['MAIL_FROM_ADDRESS'] ?? 'default@example.com', $_ENV['MAIL_FROM_NAME'] ?? 'Default Name');
// Destinatari principali
foreach ((array)$to as $recipient) {
$mail->addAddress($recipient);
}
// Destinatari CC
foreach ((array)$cc as $recipient) {
$mail->addCC($recipient);
}
// Destinatari BCC
foreach ((array)$bcc as $recipient) {
$mail->addBCC($recipient);
}
// Allegati
foreach ((array)$attachments as $file) {
$mail->addAttachment($file);
}
// Contenuto dell'email
$mail->isHTML(true);
$mail->Subject = $subject;
$mail->Body = $body;
// Invia l'email
$mail->send();
return ['success' => true, 'message' => 'Email inviata con successo.'];
} catch (Exception $e) {
return ['success' => false, 'message' => "Errore nell'invio dell'email: {$mail->ErrorInfo}"];
}
}

View File

@ -0,0 +1,5 @@
<link href="assets/css/bootstrap.min.css" rel="stylesheet" type="text/css">
<link href="assets/css/icons.css" rel="stylesheet" type="text/css">
<link href="assets/css/style.css" rel="stylesheet" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/boxicons@2.0.7/css/boxicons.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@10/dist/sweetalert2.min.css">

View File

@ -4,11 +4,8 @@ ini_set('display_errors', 1);
ini_set('display_startup_errors', 1); ini_set('display_startup_errors', 1);
error_reporting(E_ALL | E_STRICT); error_reporting(E_ALL | E_STRICT);
define('BASE_PATH', realpath(__DIR__ . '/../../..')); require_once __DIR__ . '/path_definition.php';
define('BASE_URL', '/reportifynew/public/');
define('USERAREA_PATH', '/reportifynew/public/userarea/');
define('INCLUDE_PATH', BASE_URL . 'userarea/include/');
define('ASSETS_PATH', BASE_URL . 'userarea/include/assets/');
// This should be equal to: PATH_TO_VANGUARD_FOLDER/extra/auth.php // This should be equal to: PATH_TO_VANGUARD_FOLDER/extra/auth.php
require_once(BASE_PATH . '/extra/auth.php'); require_once(BASE_PATH . '/extra/auth.php');
@ -75,11 +72,7 @@ if (isset($_SESSION["infobox"])) {
?> ?>
<?php <?php
require_once(BASE_PATH . '/public/Connections/repnew.php');
require_once(BASE_PATH . '\public\Connections\repnew.php');
?> ?>
<?php require_once(BASE_PATH . '/public/webassist/mysqli/rsobj.php'); ?> <?php require_once(BASE_PATH . '/public/webassist/mysqli/rsobj.php'); ?>

View File

@ -0,0 +1,8 @@
<?php
// Define paths that may change based on the server
define('BASE_PATH', realpath(__DIR__ . '/../../..'));
define('BASE_URL', '/reportifynew/public/');
define('USERAREA_PATH', '/reportifynew/public/userarea/');
define('INCLUDE_PATH', BASE_URL . 'userarea/include/');
define('ASSETS_PATH', BASE_URL . 'userarea/include/assets/');

View File

@ -1,20 +1,24 @@
<?php include('include/headscript.php'); ?> <?php include('include/headscript.php'); ?>
<?php <?php
// Connessione al database // Includi la classe DBHandlerSelect
$conn = new mysqli($servername, $username, $password, $database); require_once 'class/db-functions.php';
// Inizializza la connessione con DBHandlerSelect
$dbHandler = new DBHandlerSelect();
$pdo = $dbHandler->getConnection();
// Query 1: Numero totale di prodotti // Query 1: Numero totale di prodotti
$totalProductsQuery = "SELECT COUNT(DISTINCT p.idproducts) AS totalProducts FROM products p WHERE 1=1"; $totalProductsQuery = "SELECT COUNT(DISTINCT p.idproducts) AS totalProducts FROM products p WHERE 1=1";
$totalProductsResult = $conn->query($totalProductsQuery); $stmt = $pdo->query($totalProductsQuery);
$totalProducts = $totalProductsResult->fetch_assoc()['totalProducts']; $totalProducts = $stmt->fetch(PDO::FETCH_ASSOC)['totalProducts'];
// Query 2: Numero totale di report // Query 2: Numero totale di report
$totalReportsQuery = " $totalReportsQuery = "
SELECT COUNT(DISTINCT r.idreports) AS totalReports SELECT COUNT(DISTINCT r.idreports) AS totalReports
FROM reports r FROM reports r
LEFT JOIN products p ON r.idproducts = p.idproducts"; LEFT JOIN products p ON r.idproducts = p.idproducts";
$totalReportsResult = $conn->query($totalReportsQuery); $stmt = $pdo->query($totalReportsQuery);
$totalReports = $totalReportsResult->fetch_assoc()['totalReports']; $totalReports = $stmt->fetch(PDO::FETCH_ASSOC)['totalReports'];
// Query 3: Numero di report "fail" // Query 3: Numero di report "fail"
$failedReportsQuery = " $failedReportsQuery = "
@ -22,39 +26,29 @@ $failedReportsQuery = "
FROM reports r FROM reports r
LEFT JOIN products p ON r.idproducts = p.idproducts LEFT JOIN products p ON r.idproducts = p.idproducts
WHERE UPPER(r.reportsRating) IN ('FAIL', 'F', 'DOESN\'T COMPLY')"; WHERE UPPER(r.reportsRating) IN ('FAIL', 'F', 'DOESN\'T COMPLY')";
$failedReportsResult = $conn->query($failedReportsQuery); $stmt = $pdo->query($failedReportsQuery);
$failedReports = $failedReportsResult->fetch_assoc()['failedReports']; $failedReports = $stmt->fetch(PDO::FETCH_ASSOC)['failedReports'];
// Query 4: Numero totale di test
$totalTestsQuery = " $totalTestsQuery = "
SELECT COUNT(DISTINCT ap.idreports, ap.idPart, ap.result_TestName) AS totalTests SELECT COUNT(DISTINCT ap.idreports, ap.idPart, ap.result_TestName) AS totalTests
FROM analysis_project ap FROM analysis_project ap
LEFT JOIN result_project rp ON ap.idAnalysis_Project = rp.idanalysis_project LEFT JOIN result_project rp ON ap.idAnalysis_Project = rp.idanalysis_project
LEFT JOIN reports r ON ap.idreports = r.idreports LEFT JOIN reports r ON ap.idreports = r.idreports
LEFT JOIN products p ON r.idproducts = p.idproducts"; LEFT JOIN products p ON r.idproducts = p.idproducts";
$totalTestsResult = $conn->query($totalTestsQuery); $stmt = $pdo->query($totalTestsQuery);
$totalTests = $totalTestsResult->fetch_assoc()['totalTests']; $totalTests = $stmt->fetch(PDO::FETCH_ASSOC)['totalTests'];
// Verifica connessione
if ($conn->connect_error) {
die("Connessione fallita: " . $conn->connect_error);
}
?>
<?php
// Connessione al database
$conn = new mysqli($servername, $username, $password, $database);
// Query per ottenere i moduli attivi e disattivi
// Query per ottenere i moduli attivi e disattivi
$query = " $query = "
SELECT idmodules, activemod SELECT idmodules, activemod
FROM activemodules FROM activemodules
WHERE idcompany = ? WHERE idcompany = :idcompany
"; ";
$stmt = $conn->prepare($query); $stmt = $pdo->prepare($query);
$stmt->bind_param("i", $idcompany); $stmt->execute(['idcompany' => $idcompany]);
$stmt->execute();
$result = $stmt->get_result();
$modulesStatus = []; $modulesStatus = [];
while ($row = $result->fetch_assoc()) { while ($row = $stmt->fetch()) {
$modulesStatus[$row['idmodules']] = $row['activemod']; $modulesStatus[$row['idmodules']] = $row['activemod'];
} }
?> ?>
@ -72,14 +66,10 @@ while ($row = $result->fetch_assoc()) {
<link rel="shortcut icon" href="assets/images/favicon.ico"> <link rel="shortcut icon" href="assets/images/favicon.ico">
<link href="assets/css/bootstrap.min.css" rel="stylesheet" type="text/css">
<link href="assets/css/icons.css" rel="stylesheet" type="text/css">
<link href="assets/css/style.css" rel="stylesheet" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/boxicons@2.0.7/css/boxicons.min.css" rel="stylesheet">
<?php include('cssinclude.php'); ?>
</head> </head>
<body class="fixed-left"> <body class="fixed-left">
<!-- Loader --> <!-- Loader -->
@ -95,15 +85,13 @@ while ($row = $result->fetch_assoc()) {
<?php include('include/navigationbar.php'); ?> <?php include('include/navigationbar.php'); ?>
<!-- Start right Content here --> <!-- Start right Content here -->
<div class="content-page"> <div class="content-page">
<!-- Start content --> <!-- Start content -->
<div class="content"> <div class="content">
<?php include('include/topbar.php'); ?> <?php include('include/topbar.php'); ?>
<div class="page-content-wrapper "> <div class="page-content-wrapper">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
@ -120,6 +108,8 @@ while ($row = $result->fetch_assoc()) {
</div> </div>
</div> </div>
<!-- end page title end breadcrumb --> <!-- end page title end breadcrumb -->
<!-- Header Stats -->
<div class="row"> <div class="row">
<div class="col-lg-3"> <div class="col-lg-3">
<div class="card"> <div class="card">
@ -194,10 +184,8 @@ while ($row = $result->fetch_assoc()) {
</div> </div>
</div> </div>
<!-- Button Index -->
<div class="row"> <div class="row">
<div class="col-lg-3"> <div class="col-lg-3">
<div class="card card-body"> <div class="card card-body">
<a href="<?php echo USERAREA_PATH; ?>products/products.php"> <a href="<?php echo USERAREA_PATH; ?>products/products.php">
@ -205,7 +193,6 @@ while ($row = $result->fetch_assoc()) {
<i class="mdi mdi-database-import d-block display-4 mt-2 mb-3 text-warning"></i> <i class="mdi mdi-database-import d-block display-4 mt-2 mb-3 text-warning"></i>
<h5 class="text-primary"><?php echo $products; ?></h5> <h5 class="text-primary"><?php echo $products; ?></h5>
<p class="text-primary"><?php echo $productsslogan; ?></p><br> <p class="text-primary"><?php echo $productsslogan; ?></p><br>
</div> </div>
</a> </a>
</div> </div>
@ -217,7 +204,6 @@ while ($row = $result->fetch_assoc()) {
<i class="mdi mdi-cloud-sync d-block display-4 mt-2 mb-3 text-primary"></i> <i class="mdi mdi-cloud-sync d-block display-4 mt-2 mb-3 text-primary"></i>
<h5 class="text-primary">Reports</h5> <h5 class="text-primary">Reports</h5>
<p class="text-primary"><?php echo $reportsslogan; ?></p> <p class="text-primary"><?php echo $reportsslogan; ?></p>
</div> </div>
</a> </a>
</div> </div>
@ -229,7 +215,6 @@ while ($row = $result->fetch_assoc()) {
<i class="mdi mdi-signal d-block display-4 mt-2 mb-3 text-info"></i> <i class="mdi mdi-signal d-block display-4 mt-2 mb-3 text-info"></i>
<h5 class="text-primary"><?php echo $statkpi; ?></h5> <h5 class="text-primary"><?php echo $statkpi; ?></h5>
<p class="text-primary"><?php echo $statkpislogan; ?></p> <p class="text-primary"><?php echo $statkpislogan; ?></p>
</div> </div>
</a> </a>
</div> </div>
@ -241,15 +226,12 @@ while ($row = $result->fetch_assoc()) {
<i class="mdi mdi-database-import d-block display-4 mt-2 mb-3 text-success"></i> <i class="mdi mdi-database-import d-block display-4 mt-2 mb-3 text-success"></i>
<h5 class="text-primary"><?php echo $importify; ?></h5> <h5 class="text-primary"><?php echo $importify; ?></h5>
<p class="text-primary"><?php echo $importifyslogan; ?></p><br> <p class="text-primary"><?php echo $importifyslogan; ?></p><br>
</div> </div>
</a> </a>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-3"> <div class="col-lg-3">
<div class="card card-body"> <div class="card card-body">
@ -258,7 +240,6 @@ while ($row = $result->fetch_assoc()) {
<i class="mdi mdi-view-list d-block display-4 mt-2 mb-3 text-danger"></i> <i class="mdi mdi-view-list d-block display-4 mt-2 mb-3 text-danger"></i>
<h5 class="text-danger"><?php echo $rsl; ?></h5> <h5 class="text-danger"><?php echo $rsl; ?></h5>
<p><?php echo $easyspecslogan; ?></p><br> <p><?php echo $easyspecslogan; ?></p><br>
</div> </div>
</a> </a>
</div> </div>
@ -271,7 +252,6 @@ while ($row = $result->fetch_assoc()) {
<i class="mdi mdi-database-import d-block display-4 mt-2 mb-3 text-danger"></i> <i class="mdi mdi-database-import d-block display-4 mt-2 mb-3 text-danger"></i>
<h5 class="text-danger">SayTRL</h5> <h5 class="text-danger">SayTRL</h5>
<p class="text-danger"><?php echo $saytrlslogan; ?></p> <p class="text-danger"><?php echo $saytrlslogan; ?></p>
</div> </div>
</a> </a>
</div> </div>
@ -284,7 +264,6 @@ while ($row = $result->fetch_assoc()) {
<i class="mdi mdi-file-document-box d-block display-4 mt-2 mb-3 text-info"></i> <i class="mdi mdi-file-document-box d-block display-4 mt-2 mb-3 text-info"></i>
<h5 class="text-primary">Rate&Go</h5> <h5 class="text-primary">Rate&Go</h5>
<p class="text-primary"><?php echo $rategoslogan; ?></p> <p class="text-primary"><?php echo $rategoslogan; ?></p>
</div> </div>
</a> </a>
</div> </div>
@ -296,50 +275,22 @@ while ($row = $result->fetch_assoc()) {
<i class="fas fa-random display-4 mt-2 mb-3 text-warning"></i> <i class="fas fa-random display-4 mt-2 mb-3 text-warning"></i>
<h5 class="text-primary">ReEvaluate</h5> <h5 class="text-primary">ReEvaluate</h5>
<p class="text-primary"><?php echo $reevaluateslogan; ?></p> <p class="text-primary"><?php echo $reevaluateslogan; ?></p>
</div> </div>
</a> </a>
</div> </div>
</div> </div>
</div> </div>
<!-- end row -->
</div><!-- container --> </div><!-- container -->
</div> <!-- Page content Wrapper --> </div> <!-- Page content Wrapper -->
</div> <!-- content --> </div> <!-- content -->
<!-- Footer -->
<?php include('include/footer.php'); ?> <?php include('include/footer.php'); ?>
</div> </div>
<!-- End Right content here --> <!-- End Right content here -->
</div> </div>
<!-- END wrapper --> <!-- END wrapper -->
<?php include('jsinclude.php'); ?>
<!-- jQuery -->
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/popper.min.js"></script>
<script src="assets/js/bootstrap.min.js"></script>
<script src="assets/js/modernizr.min.js"></script>
<script src="assets/js/detect.js"></script>
<script src="assets/js/fastclick.js"></script>
<script src="assets/js/jquery.slimscroll.js"></script>
<script src="assets/js/jquery.blockUI.js"></script>
<script src="assets/js/waves.js"></script>
<script src="assets/js/jquery.nicescroll.js"></script>
<script src="assets/js/jquery.scrollTo.min.js"></script>
<script src="assets/plugins/chart.js/chart.min.js"></script>
<script src="assets/pages/dashboard.js"></script>
<!-- App js -->
<script src="assets/js/app.js"></script>
</body> </body>

View File

@ -0,0 +1,20 @@
<!-- jQuery -->
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/popper.min.js"></script>
<script src="assets/js/bootstrap.min.js"></script>
<script src="assets/js/modernizr.min.js"></script>
<script src="assets/js/detect.js"></script>
<script src="assets/js/fastclick.js"></script>
<script src="assets/js/jquery.slimscroll.js"></script>
<script src="assets/js/jquery.blockUI.js"></script>
<script src="assets/js/waves.js"></script>
<script src="assets/js/jquery.nRole: System Assistant: icescroll.js"></script>
<script src="assets/js/jquery.scrollTo.min.js"></script>
<script src="assets/plugins/chart.js/chart.min.js"></script>
<script src="assets/pages/dashboard.js"></script>
<!-- App js -->
<script src="assets/js/app.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10/dist/sweetalert2.min.js"></script>

View File

@ -0,0 +1,5 @@
<link href="../assets/css/bootstrap.min.css" rel="stylesheet" type="text/css">
<link href="../assets/css/icons.css" rel="stylesheet" type="text/css">
<link href="../assets/css/style.css" rel="stylesheet" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/boxicons@2.0.7/css/boxicons.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@10/dist/sweetalert2.min.css">

View File

@ -3,27 +3,44 @@ ini_set('display_errors', 1);
ini_set('display_startup_errors', 1); ini_set('display_startup_errors', 1);
error_reporting(E_ALL); error_reporting(E_ALL);
include('../include/headscript.php'); ?> include('../include/headscript.php');
<?php include("../class/company.php"); include("../class/company.php");
require_once '../class/db-functions.php'; // Includo DBHandlerSelect
$page = 'products.php'; $page = 'products.php';
$conn = new mysqli($servername, $username, $password, $database);
// Inizializzo la connessione con DBHandlerSelect
$dbHandler = new DBHandlerSelect();
$pdo = $dbHandler->getConnection();
// Validazione iduserlogin
if (!isset($iduserlogin) || !is_numeric($iduserlogin)) {
die("Errore: ID utente non valido.");
}
// Recupera le impostazioni delle colonne e dell'ordinamento dal database // Recupera le impostazioni delle colonne e dell'ordinamento dal database
$query = "SELECT column_visibility, column_order FROM user_table_settings WHERE iduser = ? AND page = ?"; try {
$stmt = $conn->prepare($query); $query = "SELECT column_visibility, column_order FROM user_table_settings WHERE iduser = :iduser AND page = :page";
$stmt->bind_param("is", $iduserlogin, $page); $stmt = $pdo->prepare($query);
$stmt->execute(); $stmt->execute(['iduser' => $iduserlogin, 'page' => $page]);
$stmt->bind_result($column_visibility, $column_order); $row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->fetch(); $column_visibility = $row['column_visibility'] ?? null;
$stmt->close(); $column_order = $row['column_order'] ?? null;
?> } catch (PDOException $e) {
error_log("Errore query user_table_settings: " . $e->getMessage());
die("Errore nel caricamento delle impostazioni.");
}
<?php // Query per ottenere i prodotti con colonne specifiche
$conn = new mysqli($servername, $username, $password, $database); try {
// Rimossa la condizione WHERE iduser per compatibilità (potrebbe non esserci il campo iduser nella tabella products)
// Query per ottenere tutti i prodotti $query = "SELECT idproducts, products_refnumber, products_description, products_style, products_color, products_season, products_market, products_sku, products_order, product_buyer, products_fibercontentresult, namesupplier, iddepartment, dateprod, dateinlab, dateoutlab, labservice, agerange, products_division, products_phase FROM products";
$query = "SELECT * FROM products"; $stmt = $pdo->prepare($query);
$result = $conn->query($query); $stmt->execute();
} catch (PDOException $e) {
error_log("Errore query products: " . $e->getMessage());
die("Errore nel caricamento dei prodotti: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8'));
}
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
@ -45,7 +62,6 @@ $result = $conn->query($query);
<link rel="stylesheet" href="https://cdn.datatables.net/buttons/2.3.1/css/buttons.dataTables.min.css"> <link rel="stylesheet" href="https://cdn.datatables.net/buttons/2.3.1/css/buttons.dataTables.min.css">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/colreorder/1.5.6/css/colReorder.dataTables.min.css"> <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/colreorder/1.5.6/css/colReorder.dataTables.min.css">
<!-- DataTables JS --> <!-- DataTables JS -->
<script src="https://cdn.datatables.net/1.13.1/js/jquery.dataTables.min.js"></script> <script src="https://cdn.datatables.net/1.13.1/js/jquery.dataTables.min.js"></script>
<!-- DataTables Buttons JS --> <!-- DataTables Buttons JS -->
@ -54,15 +70,8 @@ $result = $conn->query($query);
<script src="https://cdn.datatables.net/buttons/2.3.1/js/buttons.colVis.min.js"></script> <script src="https://cdn.datatables.net/buttons/2.3.1/js/buttons.colVis.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/colreorder/1.5.6/js/dataTables.colReorder.min.js"></script> <script type="text/javascript" src="https://cdn.datatables.net/colreorder/1.5.6/js/dataTables.colReorder.min.js"></script>
<!-- Altri riferimenti al CSS e JS --> <!-- Altri riferimenti al CSS e JS -->
<link href="../assets/css/bootstrap.min.css" rel="stylesheet" type="text/css"> <?php include('cssinclude.php'); ?>
<link href="../assets/css/icons.css" rel="stylesheet" type="text/css">
<link href="../assets/css/style.css" rel="stylesheet" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/boxicons@2.0.7/css/boxicons.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10/dist/sweetalert2.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@10/dist/sweetalert2.min.css">
</head> </head>
<style> <style>
@ -111,22 +120,18 @@ $result = $conn->query($query);
color: white !important; color: white !important;
} }
/* Forza il contrasto per i pulsanti del selettore */
.dt-button-collection button.active { .dt-button-collection button.active {
background-color: #007bff !important; background-color: #007bff !important;
/* Colore per il bottone selezionato */
color: #007bff !important; color: #007bff !important;
} }
.dt-button-collection button { .dt-button-collection button {
background-color: #f8f9fa !important; background-color: #f8f9fa !important;
/* Colore per i bottoni non attivi */
color: #000 !important; color: #000 !important;
} }
</style> </style>
<body class="fixed-left"> <body class="fixed-left">
<div id="wrapper"> <div id="wrapper">
<?php include('../include/navigationbar.php'); ?> <?php include('../include/navigationbar.php'); ?>
<div class="content-page"> <div class="content-page">
@ -134,7 +139,6 @@ $result = $conn->query($query);
<?php include('../include/topbar.php'); ?> <?php include('../include/topbar.php'); ?>
<div class="page-content-wrapper"> <div class="page-content-wrapper">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="page-title-box"> <div class="page-title-box">
@ -142,7 +146,6 @@ $result = $conn->query($query);
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xl-12"> <div class="col-xl-12">
<div class="card"> <div class="card">
@ -174,27 +177,27 @@ $result = $conn->query($query);
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php while ($row = $result->fetch_assoc()) { ?> <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { ?>
<tr data-productid="<?php echo $row['idproducts']; ?>"> <tr data-productid="<?php echo htmlspecialchars($row['idproducts'], ENT_QUOTES, 'UTF-8'); ?>">
<td><?php echo $row['products_refnumber']; ?></td> <td><?php echo htmlspecialchars($row['products_refnumber'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['products_description']; ?></td> <td><?php echo htmlspecialchars($row['products_description'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['products_style']; ?></td> <td><?php echo htmlspecialchars($row['products_style'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['products_color']; ?></td> <td><?php echo htmlspecialchars($row['products_color'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['products_season']; ?></td> <td><?php echo htmlspecialchars($row['products_season'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['products_market']; ?></td> <td><?php echo htmlspecialchars($row['products_market'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['products_sku']; ?></td> <td><?php echo htmlspecialchars($row['products_sku'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['products_order']; ?></td> <td><?php echo htmlspecialchars($row['products_order'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['product_buyer']; ?></td> <td><?php echo htmlspecialchars($row['product_buyer'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['products_fibercontentresult']; ?></td> <td><?php echo htmlspecialchars($row['products_fibercontentresult'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['namesupplier']; ?></td> <td><?php echo htmlspecialchars($row['namesupplier'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['iddepartment']; ?></td> <td><?php echo htmlspecialchars($row['iddepartment'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['dateprod']; ?></td> <td><?php echo htmlspecialchars($row['dateprod'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['dateinlab']; ?></td> <td><?php echo htmlspecialchars($row['dateinlab'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['dateoutlab']; ?></td> <td><?php echo htmlspecialchars($row['dateoutlab'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['labservice']; ?></td> <td><?php echo htmlspecialchars($row['labservice'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['agerange']; ?></td> <td><?php echo htmlspecialchars($row['agerange'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['products_division']; ?></td> <td><?php echo htmlspecialchars($row['products_division'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['products_phase']; ?></td> <td><?php echo htmlspecialchars($row['products_phase'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td> <td>
<button class="btn btn-info btn-sm show-reports">Reports</button> <button class="btn btn-info btn-sm show-reports">Reports</button>
</td> </td>
@ -207,31 +210,27 @@ $result = $conn->query($query);
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
</div><!-- container --> </div>
</div><!-- Page content Wrapper -->
</div><!-- content -->
<?php include('../include/footer.php'); ?> <?php include('../include/footer.php'); ?>
</div> </div>
</div> </div>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
var hasUserInteracted = false; // Flag per tracciare l'interazione dell'utente var hasUserInteracted = false;
// Carica le impostazioni salvate dal server prima di inizializzare DataTables
var stateLoaded = false; var stateLoaded = false;
var columnSettings = {}; // Variabile per memorizzare le impostazioni della tabella var columnSettings = {};
// Recupera le impostazioni salvate dal server
$.ajax({ $.ajax({
url: 'get_user_table_settings.php', // PHP che recupera i settaggi url: 'get_user_table_settings.php',
method: 'POST', method: 'POST',
data: { data: {
user: "<?php echo $iduserlogin; ?>", // ID utente user: "<?php echo htmlspecialchars($iduserlogin, ENT_QUOTES, 'UTF-8'); ?>",
table: 'products' // Nome della tabella table: 'products'
}, },
async: false, // Carica in modo sincrono per garantire che le impostazioni siano pronte prima di DataTables async: false,
success: function(response) { success: function(response) {
columnSettings = JSON.parse(response); columnSettings = JSON.parse(response);
console.log("Impostazioni recuperate:", columnSettings); console.log("Impostazioni recuperate:", columnSettings);
@ -241,42 +240,36 @@ $result = $conn->query($query);
} }
}); });
// Inizializza DataTables con ColReorder, filtri e ColVis
var table = $('#productsTable').DataTable({ var table = $('#productsTable').DataTable({
responsive: true, responsive: true,
pageLength: 50, pageLength: 50,
order: [ order: [
[1, 'asc'] [1, 'asc']
], // Ordina per descrizione ],
colReorder: { colReorder: {
order: columnSettings && columnSettings.ColReorder ? columnSettings.ColReorder : null // Usa le impostazioni di ColReorder se presenti order: columnSettings && columnSettings.ColReorder ? columnSettings.ColReorder : null
}, },
dom: 'Bfrtip', // Integra i bottoni dom: 'Bfrtip',
buttons: [{ buttons: [{
extend: 'colvis', // ColVis per mostrare/nascondere colonne extend: 'colvis',
collectionLayout: 'fixed four-column', // Layout del dropdown collectionLayout: 'fixed four-column',
text: 'Select Columns', // Testo del bottone dropdown text: 'Select Columns',
postfixButtons: ['colvisRestore'], // Pulsante per ripristinare la configurazione iniziale postfixButtons: ['colvisRestore'],
columns: ':not(:last-child)' // Non nascondere l'ultima colonna (Actions) columns: ':not(:last-child)'
}], }],
initComplete: function() { initComplete: function() {
console.log("DataTables initComplete"); console.log("DataTables initComplete");
// Se sono state caricate impostazioni di visibilità delle colonne, applicale
if (columnSettings && columnSettings.columns) { if (columnSettings && columnSettings.columns) {
console.log("Applico visibilità colonne:", columnSettings.columns); console.log("Applico visibilità colonne:", columnSettings.columns);
this.api().columns().every(function(index) { this.api().columns().every(function(index) {
var column = this; var column = this;
column.visible(columnSettings.columns[index].visible, false); // Non ridisegnare ogni volta column.visible(columnSettings.columns[index].visible, false);
}); });
} }
this.api().columns.adjust().draw(false);
this.api().columns.adjust().draw(false); // Ridisegna la tabella alla fine
// Aggiungi i filtri per ogni colonna (eccetto la colonna delle azioni)
this.api().columns().every(function() { this.api().columns().every(function() {
var column = this; var column = this;
if (column.index() !== 6) { // Escludi la colonna "Action" if (column.index() !== 19) {
var input = $('<input class="form-control form-control-sm" type="text" placeholder="Search"/>') var input = $('<input class="form-control form-control-sm" type="text" placeholder="Search"/>')
.appendTo($(column.header())) .appendTo($(column.header()))
.on('keyup change clear', function() { .on('keyup change clear', function() {
@ -290,16 +283,14 @@ $result = $conn->query($query);
stateSave: true, stateSave: true,
stateSaveCallback: function(settings, data) { stateSaveCallback: function(settings, data) {
console.log("Salvataggio stato tabella"); console.log("Salvataggio stato tabella");
// Salva le impostazioni sul server solo se l'utente ha interagito
if (hasUserInteracted) { if (hasUserInteracted) {
$.ajax({ $.ajax({
url: 'save_user_table_settings.php', // PHP che salva i settaggi url: 'save_user_table_settings.php',
method: 'POST', method: 'POST',
data: { data: {
user: "<?php echo $iduserlogin; ?>", // ID utente user: "<?php echo htmlspecialchars($iduserlogin, ENT_QUOTES, 'UTF-8'); ?>",
table: 'products', // Nome della tabella table: 'products',
settings: JSON.stringify(data) // Impostazioni della tabella settings: JSON.stringify(data)
}, },
success: function(response) { success: function(response) {
console.log('Impostazioni salvate:', response); console.log('Impostazioni salvate:', response);
@ -312,7 +303,6 @@ $result = $conn->query($query);
} }
}); });
// Imposta il flag `hasUserInteracted` quando l'utente modifica qualcosa (colonne o riordina)
table.on('column-reorder', function() { table.on('column-reorder', function() {
hasUserInteracted = true; hasUserInteracted = true;
console.log("Colonne riordinate"); console.log("Colonne riordinate");
@ -323,7 +313,6 @@ $result = $conn->query($query);
console.log("Visibilità colonna cambiata"); console.log("Visibilità colonna cambiata");
}); });
// Gestione del click su "Reports" per visualizzare la tabella child con i report e le analisi
$('#productsTable').on('click', '.show-reports', function() { $('#productsTable').on('click', '.show-reports', function() {
var tr = $(this).closest('tr'); var tr = $(this).closest('tr');
var productId = tr.data('productid'); var productId = tr.data('productid');
@ -333,7 +322,6 @@ $result = $conn->query($query);
button.prop('disabled', true); button.prop('disabled', true);
button.html('<i class="fa fa-spinner fa-spin"></i> Loading...'); button.html('<i class="fa fa-spinner fa-spin"></i> Loading...');
// Se la riga child è già visibile, nascondila
if (row.child.isShown()) { if (row.child.isShown()) {
row.child.hide(); row.child.hide();
tr.removeClass('shown'); tr.removeClass('shown');
@ -342,7 +330,6 @@ $result = $conn->query($query);
return; return;
} }
// Carica i report e le analisi tramite AJAX
$.ajax({ $.ajax({
url: 'get_reports_and_analysis.php', url: 'get_reports_and_analysis.php',
type: 'POST', type: 'POST',
@ -357,15 +344,14 @@ $result = $conn->query($query);
button.prop('disabled', false); button.prop('disabled', false);
button.html('Reports'); button.html('Reports');
}, },
error: function() { error: function(xhr) {
Swal.fire('Error!', 'There was an error loading the reports and analysis.', 'error'); Swal.fire('Errore!', xhr.status === 404 ? 'Risorsa non trovata.' : 'Errore nel caricamento dei report.', 'error');
button.prop('disabled', false); button.prop('disabled', false);
button.html('Reports'); button.html('Reports');
} }
}); });
}); });
// Funzione per formattare i report e le analisi nella stessa tabella
function formatReportsAndAnalysis(data) { function formatReportsAndAnalysis(data) {
var parsedData = JSON.parse(data); var parsedData = JSON.parse(data);
var reports = parsedData.reports; var reports = parsedData.reports;
@ -374,10 +360,8 @@ $result = $conn->query($query);
html += '<tbody>'; html += '<tbody>';
$.each(reports, function(index, report) { $.each(reports, function(index, report) {
// Definisci la classe di stile per il rating del report
var ratingClass = ''; var ratingClass = '';
var ratingValue = report.reportsRating.toLowerCase(); // Converte il valore in minuscolo var ratingValue = report.reportsRating.toLowerCase();
if (ratingValue === 'fail') { if (ratingValue === 'fail') {
ratingClass = 'bg-danger text-white'; ratingClass = 'bg-danger text-white';
} else if (ratingValue === 'pass') { } else if (ratingValue === 'pass') {
@ -389,12 +373,11 @@ $result = $conn->query($query);
html += '<tr>'; html += '<tr>';
html += '<td>' + report.reportsNumberLab + '</td>'; html += '<td>' + report.reportsNumberLab + '</td>';
html += '<td>' + report.reportDateIn + '</td>'; html += '<td>' + report.reportDateIn + '</td>';
html += '<td class="' + ratingClass + '">' + report.reportsRating + '</td>'; // Applica la classe alla cella html += '<td class="' + ratingClass + '">' + report.reportsRating + '</td>';
html += '<td colspan="2"></td>'; // Righe vuote per mantenere l'allineamento html += '<td colspan="2"></td>';
html += '<td><button class="btn btn-primary btn-sm report-details" data-reportid="' + report.idreports + '">Details</button></td>'; html += '<td><button class="btn btn-primary btn-sm report-details" data-reportid="' + report.idreports + '">Details</button></td>';
html += '</tr>'; html += '</tr>';
// Se ci sono analisi associate, aggiungi le righe per ciascuna analisi
if (report.analysis.length > 0) { if (report.analysis.length > 0) {
$.each(report.analysis, function(i, analysis) { $.each(report.analysis, function(i, analysis) {
var ratingClass = ''; var ratingClass = '';
@ -420,20 +403,16 @@ $result = $conn->query($query);
} }
}); });
html += '</tbody></table>'; html += '</tbody></table>';
return html; return html;
} }
// Funzione per la visualizzazione dei dettagli del report
$(document).on('click', '.report-details', function() { $(document).on('click', '.report-details', function() {
var reportId = $(this).data('reportid'); var reportId = $(this).data('reportid');
window.location.href = 'reportdetails.php?idreports=' + reportId; window.location.href = 'reportdetails.php?idreports=' + reportId;
}); });
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,26 +1,48 @@
<?php include('../include/headscript.php'); ?> <?php
<?php include("../class/company.php"); ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$conn = new mysqli($servername, $username, $password, $database); include('../include/headscript.php');
include("../class/company.php");
require_once '../class/db-functions.php'; // Includo DBHandlerSelect
$page = 'reports'; // Nome della pagina specifica per salvare/ripristinare le impostazioni $page = 'reports';
$conn = new mysqli($servername, $username, $password, $database);
// Inizializzo la connessione con DBHandlerSelect
$dbHandler = new DBHandlerSelect();
$conn = $dbHandler->getConnection();
// Validazione iduserlogin
if (!isset($iduserlogin) || !is_numeric($iduserlogin)) {
die("Errore: ID utente non valido.");
}
// Recupera le impostazioni delle colonne e dell'ordinamento dal database // Recupera le impostazioni delle colonne e dell'ordinamento dal database
$query = "SELECT column_visibility, column_order FROM user_table_settings WHERE iduser = ? AND page = ?"; try {
$stmt = $conn->prepare($query); $stmt = $conn->prepare("SELECT column_visibility, column_order FROM user_table_settings WHERE iduser = :iduser AND page = :page");
$stmt->bind_param("is", $iduserlogin, $page); $stmt->execute(['iduser' => $iduserlogin, 'page' => $page]);
$stmt->execute(); $userSettings = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->bind_result($column_visibility, $column_order); $column_visibility = $userSettings['column_visibility'] ?? null;
$stmt->fetch(); $column_order = $userSettings['column_order'] ?? null;
$stmt->close(); } catch (PDOException $e) {
error_log("Errore query user_table_settings: " . $e->getMessage());
die("Errore nel caricamento delle impostazioni.");
}
// Query per ottenere tutti i report e i prodotti associati // Query per ottenere tutti i report e i prodotti associati
$query = " try {
SELECT r.*, p.products_refnumber, p.products_description $stmt = $conn->prepare("
FROM reports r SELECT r.idreports, r.reportsNumberLab, r.reportDateIn, r.reportsRating, p.products_refnumber, p.products_description
LEFT JOIN products p ON r.idproducts = p.idproducts"; FROM reports r
$result = $conn->query($query); LEFT JOIN products p ON r.idproducts = p.idproducts
");
$stmt->execute();
$reports = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
error_log("Errore query reports: " . $e->getMessage());
die("Errore nel caricamento dei report: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8'));
}
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
@ -31,8 +53,8 @@ $result = $conn->query($query);
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
<title>Reports</title> <title>Reports</title>
<link rel="shortcut icon" href="../assets/images/favicon.ico">
<!-- Includi prima jQuery -->
<!-- Includi prima jQuery --> <!-- Includi prima jQuery -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
@ -52,15 +74,8 @@ $result = $conn->query($query);
<!-- ColReorder JS --> <!-- ColReorder JS -->
<script src="https://cdn.datatables.net/colreorder/1.5.6/js/dataTables.colReorder.min.js"></script> <script src="https://cdn.datatables.net/colreorder/1.5.6/js/dataTables.colReorder.min.js"></script>
<!-- Altri riferimenti al CSS e JS --> <!-- Altri riferimenti al CSS e JS -->
<link href="../assets/css/bootstrap.min.css" rel="stylesheet" type="text/css"> <?php include('../products/cssinclude.php'); ?>
<link href="../assets/css/icons.css" rel="stylesheet" type="text/css">
<link href="../assets/css/style.css" rel="stylesheet" type="text/css">
<link rel="shortcut icon" href="../assets/images/favicon.ico">
<link href="https://cdn.jsdelivr.net/npm/boxicons@2.0.7/css/boxicons.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10/dist/sweetalert2.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@10/dist/sweetalert2.min.css">
</head> </head>
<style> <style>
@ -89,7 +104,6 @@ $result = $conn->query($query);
</style> </style>
<body class="fixed-left"> <body class="fixed-left">
<div id="wrapper"> <div id="wrapper">
<?php include('../include/navigationbar.php'); ?> <?php include('../include/navigationbar.php'); ?>
<div class="content-page"> <div class="content-page">
@ -97,7 +111,6 @@ $result = $conn->query($query);
<?php include('../include/topbar.php'); ?> <?php include('../include/topbar.php'); ?>
<div class="page-content-wrapper"> <div class="page-content-wrapper">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="page-title-box"> <div class="page-title-box">
@ -105,13 +118,12 @@ $result = $conn->query($query);
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xl-12"> <div class="col-xl-12">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <div class="table-responsive">
<table id="reportsTable" class="table table-striped table-hover" data-user="<?php echo $iduserlogin; ?>" data-table="reports"> <table id="reportsTable" class="table table-striped table-hover" data-user="<?php echo htmlspecialchars($iduserlogin, ENT_QUOTES, 'UTF-8'); ?>" data-table="reports">
<thead> <thead>
<tr> <tr>
<th>Report Number</th> <th>Report Number</th>
@ -123,31 +135,29 @@ $result = $conn->query($query);
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php while ($row = $result->fetch_assoc()) { ?> <?php foreach ($reports as $row) { ?>
<tr data-reportid="<?php echo $row['idreports']; ?>"> <tr data-reportid="<?php echo htmlspecialchars($row['idreports'], ENT_QUOTES, 'UTF-8'); ?>">
<td><?php echo $row['reportsNumberLab']; ?></td> <td><?php echo htmlspecialchars($row['reportsNumberLab'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['reportDateIn']; ?></td> <td><?php echo htmlspecialchars($row['reportDateIn'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['products_refnumber']; ?></td> <td><?php echo htmlspecialchars($row['products_refnumber'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['products_description']; ?></td> <td><?php echo htmlspecialchars($row['products_description'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $row['reportsRating']; ?></td> <td><?php echo htmlspecialchars($row['reportsRating'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
<td> <td>
<button class="btn btn-info btn-sm show-analysis">Analysis</button> <button class="btn btn-info btn-sm show-analysis">Analysis</button>
<a class="btn btn-primary btn-sm report-details" href="../products/reportdetails.php?idreports=<?php echo $row['idreports']; ?>">Details</a> <a class="btn btn-primary btn-sm report-details" href="../products/reportdetails.php?idreports=<?php echo htmlspecialchars($row['idreports'], ENT_QUOTES, 'UTF-8'); ?>">Details</a>
</td> </td>
</tr> </tr>
<?php } ?> <?php } ?>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
</div><!-- container --> </div>
</div><!-- Page content Wrapper --> </div>
</div><!-- content -->
<?php include('../include/footer.php'); ?> <?php include('../include/footer.php'); ?>
</div> </div>
</div> </div>
@ -155,13 +165,10 @@ $result = $conn->query($query);
<script> <script>
$(document).ready(function() { $(document).ready(function() {
var hasUserInteracted = false; var hasUserInteracted = false;
// Recupera le impostazioni salvate dal server prima di inizializzare DataTables
var columnSettings = {}; var columnSettings = {};
var userId = $('#reportsTable').data('user'); var userId = $('#reportsTable').data('user');
var tableName = $('#reportsTable').data('table'); var tableName = $('#reportsTable').data('table');
// Recupera le impostazioni salvate
$.ajax({ $.ajax({
url: 'get_user_table_settings_reports.php', url: 'get_user_table_settings_reports.php',
method: 'POST', method: 'POST',
@ -179,7 +186,6 @@ $result = $conn->query($query);
} }
}); });
// Inizializza DataTables con ColReorder, filtri e ColVis
var table = $('#reportsTable').DataTable({ var table = $('#reportsTable').DataTable({
responsive: true, responsive: true,
pageLength: 50, pageLength: 50,
@ -189,16 +195,15 @@ $result = $conn->query($query);
colReorder: { colReorder: {
order: columnSettings && columnSettings.ColReorder ? columnSettings.ColReorder : null order: columnSettings && columnSettings.ColReorder ? columnSettings.ColReorder : null
}, },
dom: 'Bfrtip', // Integra i bottoni dom: 'Bfrtip',
buttons: [{ buttons: [{
extend: 'colvis', extend: 'colvis',
text: 'Select Columns', text: 'Select Columns',
collectionLayout: 'fixed four-column', collectionLayout: 'fixed four-column',
postfixButtons: ['colvisRestore'], postfixButtons: ['colvisRestore'],
columns: ':not(:last-child)' // Escludi l'ultima colonna (Actions) columns: ':not(:last-child)'
}], }],
initComplete: function() { initComplete: function() {
// Applica le impostazioni di visibilità delle colonne, se presenti
if (columnSettings && columnSettings.columns) { if (columnSettings && columnSettings.columns) {
console.log("Applico visibilità colonne:", columnSettings.columns); console.log("Applico visibilità colonne:", columnSettings.columns);
this.api().columns().every(function(index) { this.api().columns().every(function(index) {
@ -206,13 +211,10 @@ $result = $conn->query($query);
column.visible(columnSettings.columns[index].visible, false); column.visible(columnSettings.columns[index].visible, false);
}); });
} }
this.api().columns.adjust().draw(false); this.api().columns.adjust().draw(false);
// Aggiungi i filtri per ogni colonna (eccetto la colonna delle azioni)
this.api().columns().every(function() { this.api().columns().every(function() {
var column = this; var column = this;
if (column.index() !== 5) { // Escludi la colonna "Action" if (column.index() !== 5) {
$('<input class="form-control form-control-sm" type="text" placeholder="Search"/>') $('<input class="form-control form-control-sm" type="text" placeholder="Search"/>')
.appendTo($(column.header())) .appendTo($(column.header()))
.on('keyup change clear', function() { .on('keyup change clear', function() {
@ -245,7 +247,6 @@ $result = $conn->query($query);
} }
}); });
// Imposta il flag `hasUserInteracted` quando l'utente modifica qualcosa (colonne o riordina)
table.on('column-reorder', function() { table.on('column-reorder', function() {
hasUserInteracted = true; hasUserInteracted = true;
console.log("Colonne riordinate"); console.log("Colonne riordinate");
@ -256,7 +257,6 @@ $result = $conn->query($query);
console.log("Visibilità colonna cambiata"); console.log("Visibilità colonna cambiata");
}); });
// Gestione del click su "Analysis" per visualizzare la tabella child con le analisi
$('#reportsTable').on('click', '.show-analysis', function() { $('#reportsTable').on('click', '.show-analysis', function() {
var tr = $(this).closest('tr'); var tr = $(this).closest('tr');
var reportId = tr.data('reportid'); var reportId = tr.data('reportid');
@ -288,8 +288,8 @@ $result = $conn->query($query);
button.prop('disabled', false); button.prop('disabled', false);
button.html('Analysis'); button.html('Analysis');
}, },
error: function() { error: function(xhr) {
Swal.fire('Error!', 'There was an error loading the analysis.', 'error'); Swal.fire('Errore!', xhr.status === 404 ? 'Risorsa non trovata.' : 'Errore nel caricamento delle analisi.', 'error');
button.prop('disabled', false); button.prop('disabled', false);
button.html('Analysis'); button.html('Analysis');
} }
@ -304,17 +304,18 @@ $result = $conn->query($query);
$.each(parsedData, function(index, analysis) { $.each(parsedData, function(index, analysis) {
var ratingClass = ''; var ratingClass = '';
if (analysis.finalRating === 'FAIL') { var ratingValue = (analysis.finalRating || '').toUpperCase();
if (ratingValue === 'FAIL') {
ratingClass = 'bg-danger text-white'; ratingClass = 'bg-danger text-white';
} else if (analysis.finalRating === 'PASS') { } else if (ratingValue === 'PASS') {
ratingClass = 'bg-success text-white'; ratingClass = 'bg-success text-white';
} else if (analysis.finalRating === '//' || analysis.finalRating === 'N/A' || analysis.finalRating === 'DATA') { } else if (ratingValue === '//' || ratingValue === 'N/A' || ratingValue === 'DATA') {
ratingClass = 'bg-warning text-dark'; ratingClass = 'bg-warning text-dark';
} }
html += '<tr>'; html += '<tr>';
html += '<td>' + (analysis.name ? analysis.name : 'N/A') + '</td>'; html += '<td>' + (analysis.name ? analysis.name : 'N/A') + '</td>';
html += '<td class="' + ratingClass + '">' + (analysis.finalRating ? analysis.finalRating : 'N/A') + '</td>'; html += '<td class="' + ratingClass + '">' + (ratingValue || 'N/A') + '</td>';
html += '</tr>'; html += '</tr>';
}); });
@ -323,10 +324,6 @@ $result = $conn->query($query);
} }
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@ -0,0 +1,226 @@
<style>
#activeFilters {
display: flex;
font-size: 16px;
flex-direction: row;
background: 0;
}
.badge {
display: flex;
flex-direction: row;
align-items: center;
gap: 5px;
}
.width-100 {
width: 100%;
}
.flex_center {
display: flex;
align-items: center;
}
.mg_none {
margin: 0 !important;
}
.hidden {
display: none !important;
}
.table-custom tr {
height: 40px;
line-height: 40px;
}
#ajax_preloader {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: transparent;
z-index: 9999999;
}
.table-custom td,
.table-custom th {
padding: 4px 8px;
}
.table-custom .btn {
padding: 2px 15px;
line-height: 1.7;
font-size: 14px;
}
.form-row {
display: flex;
align-items: center;
/* Questo allinea verticalmente gli elementi nella riga */
gap: 10px;
/* Questo crea una piccola distanza tra gli elementi nella riga */
}
.table-custom .form-control,
.table-custom .form-select {
height: 25px;
/* Puoi modificare questo valore per adattarlo al tuo design */
padding: 2px 6px;
/* riduce la dimensione del padding */
font-size: 14px;
/* riduce la dimensione del font */
}
.table-custom .form-control-sm.analysis-input {
height: 25px;
/* Questo modifica la dimensione degli input con classe 'form-control-sm' e 'analysis-input' */
padding: 2px 6px;
font-size: 12px;
}
.padding_none {
padding: 0 !important;
}
.select2-container--open {
z-index: 9999;
}
.card {
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
transition: transform 0.2s ease-in-out;
}
.card:hover {
transform: translateY(-3px);
}
.card-body {
padding: 20px;
}
.card h2 {
font-size: 32px;
font-weight: bold;
margin-bottom: 5px;
}
.card h5 {
font-size: 18px;
font-weight: 500;
margin-bottom: 10px;
}
.card {
min-height: 150px;
/* Imposta un'altezza minima per le card */
}
.percentage {
font-size: 18px;
font-weight: bold;
margin-top: 5px;
margin-left: 10px;
}
.bg-light {
background-color: #f8f9fa !important;
}
.border-primary {
border: 2px solid #007bff !important;
}
.border-info {
border: 2px solid #17a2b8 !important;
}
.border-danger {
border: 2px solid #dc3545 !important;
}
.border-success {
border: 2px solid #28a745 !important;
}
.border-warning {
border: 2px solid #ffc107 !important;
}
.row {
margin-top: 20px;
}
.col-md-2 {
margin-bottom: 20px;
}
@media (max-width: 767px) {
.col-md-2 {
flex: 0 0 100%;
max-width: 100%;
}
}
.filters-applied {
display: inline-block;
color: white;
border-radius: 5px;
padding: 5px 10px;
font-weight: bold;
margin-top: 10px;
}
.chart-container {
width: 100%;
margin: 20px;
}
.chart-box {
display: flex;
justify-content: center;
align-items: center;
}
.card-body {
background-color: white;
}
</style>
<style>
.filter-sidebar {
position: fixed;
top: 0;
right: 0;
width: 300px;
height: 100%;
background: #f8f9fa;
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
padding: 20px;
display: none;
z-index: 999;
}
.active-filters {
margin-bottom: 20px;
}
body {
transition: margin-right 0.3s;
/* Smooth transition */
}
#main-content {
transition: margin-right 0.3s;
/* Smooth transition */
}
.collapsed {
margin-right: 300px;
/* Hide the sidebar */
}
</style>

View File

@ -0,0 +1,68 @@
<script>
// Manages drag-and-drop functionality for charts, saving and restoring their order
document.addEventListener("DOMContentLoaded", function() {
const chartContainer = document.getElementById('charts');
const sortable = new Sortable(chartContainer, {
animation: 150,
onEnd: function() {
saveChartOrder(); // Save chart order after dragging
}
});
// Function to save chart order to localStorage
function saveChartOrder() {
const order = Array.from(chartContainer.children).map(chart => chart.dataset.id);
localStorage.setItem('chartOrder', JSON.stringify(order));
// lets save it for the user
$.ajax({
url: 'chartorder.php',
method: 'POST',
data: {
method: 'save',
order: order
},
success: function(response) {
},
error: function() {
alert('Error saving chart order.');
}
});
}
// Function to restore chart order from localStorage
function loadChartOrder() {
// Load the saved order from localStorage
const savedOrder = JSON.parse(localStorage.getItem('chartOrder'));
if (savedOrder) {
savedOrder.forEach(id => {
const chart = document.querySelector(`[data-id='${id}']`);
chartContainer.appendChild(chart); // Append charts in saved order
});
}
// Load the saved order from the database
$.ajax({
url: 'chartorder.php',
method: 'POST',
data: {
method: 'load'
},
success: function(response) {
const order = JSON.parse(response);
order.forEach(id => {
const chart = document.querySelector(`[data-id='${id}']`);
chartContainer.appendChild(chart); // Append charts in saved order
});
},
});
}
// Load the saved chart order when the page loads
loadChartOrder();
});
</script>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
import { generatePieTable } from "../utils/tableGenerator.js";
export function renderAnalysisDistributionChart(
chartData,
containerId,
tableContainerId,
) {
// Pulizia del contenitore del grafico
const chartContainer = document.querySelector(`#${containerId}`);
if (chartContainer) {
chartContainer.innerHTML = "";
} else {
console.error(`Contenitore del grafico non trovato: #${containerId}`);
return;
}
// Estrai i dati per il grafico a torta
const analysisDistribution = chartData || [];
const labels = analysisDistribution.map((item) => item.analysisName);
const series = analysisDistribution.map((item) => {
const value = parseInt(item.totalTests);
return isNaN(value) || value < 0 ? 0 : value;
});
// Verifica se ci sono dati validi da visualizzare
const hasValidData = series.some((value) => value > 0);
if (!hasValidData) {
chartContainer.innerHTML =
'<p class="text-center text-muted">No valid data to display.</p>';
return;
}
// Configurazione del grafico a torta
const options = {
series: series,
chart: {
type: "pie",
height: 350,
toolbar: {
show: true,
tools: {
download: true,
selection: false,
zoom: false,
zoomin: false,
zoomout: false,
pan: false,
reset: false,
},
},
},
labels: labels,
colors: [
"#FF4560",
"#008FFB",
"#00E396",
"#FEB019",
"#FF66FF",
"#775DD0",
"#546E7A",
"#26A69A",
"#D81B60",
"#F06292",
"#4FC3F7",
"#AED581",
"#FF8A65",
"#A1887F",
"#E0E0E0",
"#90A4AE",
"#FFCA28",
"#78909C",
"#D4E157",
"#F4FF81",
],
responsive: [
{
breakpoint: 480,
options: {
chart: {
width: 200,
},
legend: {
position: "bottom",
},
},
},
],
tooltip: {
y: {
formatter: function (val) {
return val + " tests";
},
},
},
};
// Render del grafico
const chart = new ApexCharts(chartContainer, options);
chart.render();
// Forza un aggiornamento del grafico dopo un breve ritardo
setTimeout(() => {
chart.updateOptions({});
}, 100);
// Genera la tabella usando la funzione esistente
const tableHTML = generatePieTable(labels, series);
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML = tableHTML;
} else {
console.error(
`Contenitore della tabella non trovato: #${tableContainerId}`,
);
}
}

View File

@ -0,0 +1,143 @@
export function renderAnalytesFailChart(
analytesData,
containerId,
tableContainerId,
) {
// Pulizia del contenitore del grafico
const chartContainer = document.querySelector(`#${containerId}`);
if (chartContainer) {
chartContainer.innerHTML = "";
} else {
console.error(`Contenitore del grafico non trovato: #${containerId}`);
return;
}
// Validazione dei dati
if (!Array.isArray(analytesData) || analytesData.length === 0) {
chartContainer.innerHTML =
'<p class="text-center text-muted">No failures to display.</p>';
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML =
'<p class="text-center text-muted">No failures to display.</p>';
}
return;
}
const labels = analytesData.map((item) => item.AnalyteName || "Unknown");
const data = analytesData.map((item) => parseInt(item.FailCount, 10) || 0);
// Verifica se ci sono dati validi da visualizzare
const hasValidData = analytesData.some((item) => item.FailCount > 0);
if (!hasValidData) {
chartContainer.innerHTML =
'<p class="text-center text-muted">No failures to display.</p>';
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML =
'<p class="text-center text-muted">No failures to display.</p>';
}
return;
}
// Configurazione del grafico a barre orizzontali
const options = {
series: [
{
name: "Failures",
data: data,
},
],
chart: {
type: "bar",
height: 400,
toolbar: {
show: true,
tools: {
download: true,
selection: false,
zoom: false,
zoomin: false,
zoomout: false,
pan: false,
reset: false,
},
},
},
plotOptions: {
bar: {
horizontal: true,
barHeight: "80%",
},
},
dataLabels: {
enabled: false,
},
xaxis: {
categories: labels,
title: {
text: "Number of Failures",
},
},
yaxis: {
title: {
text: "Analyte",
},
},
colors: ["#FF4D4D"],
fill: {
opacity: 1,
},
tooltip: {
y: {
formatter: function (val) {
return val + " failures";
},
},
},
};
// Render del grafico
const chart = new ApexCharts(chartContainer, options);
chart.render();
// Genera una tabella per i dati
const tableHTML = `
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Analyte</th>
<th>Failures</th>
</tr>
</thead>
<tbody>
${analytesData
.map(
(item) => `
<tr>
<td>${item.AnalyteName || "Unknown"}</td>
<td>${parseInt(item.FailCount, 10) || 0}</td>
</tr>
`,
)
.join("")}
</tbody>
</table>
`;
// Riprova a trovare il contenitore della tabella con un ritardo
const renderTable = () => {
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML = tableHTML;
} else {
console.error(
`Contenitore della tabella non trovato: #${tableContainerId}`,
);
}
};
// Prova immediatamente e riprova dopo un breve ritardo
renderTable();
setTimeout(renderTable, 100);
}

View File

@ -0,0 +1,91 @@
import { generatePieTable } from "../utils/tableGenerator.js";
export function renderBarChart(data, containerId, tableContainerId) {
// Pulizia del contenitore del grafico
const chartContainer = document.querySelector(`#${containerId}`);
if (chartContainer) {
chartContainer.innerHTML = "";
} else {
console.error(`Contenitore del grafico non trovato: #${containerId}`);
return;
}
// Dati per il grafico a barre (usa gli stessi dati del grafico a torta)
const intFailReports = parseInt(data.failReportsPie) || 0;
const intPassReports = parseInt(data.passReportsPie) || 0;
const intOtherReports = parseInt(data.otherReportsPie) || 0;
const barLabels = ["Fail", "Pass", "Others"];
const barSeries = [intFailReports, intPassReports, intOtherReports];
// Configurazione del grafico a barre
const options = {
series: [
{
name: "Reports",
data: barSeries,
},
],
chart: {
height: 350,
type: "bar",
toolbar: {
show: true,
tools: {
download: true,
selection: false,
zoom: false,
zoomin: false,
zoomout: false,
pan: false,
reset: false,
},
},
},
plotOptions: {
bar: {
horizontal: false,
columnWidth: "55%",
endingShape: "rounded",
},
},
dataLabels: {
enabled: false,
},
stroke: {
show: true,
width: 2,
colors: ["transparent"],
},
xaxis: {
categories: barLabels,
},
colors: ["#FF4D4D", "#28A745", "#FFA500"],
fill: {
opacity: 1,
},
tooltip: {
y: {
formatter: function (val) {
return val + " reports";
},
},
},
};
// Render del grafico
const chart = new ApexCharts(chartContainer, options);
chart.render();
// Genera la tabella e aggiungi un log
const tableHTML = generatePieTable(barLabels, barSeries);
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML = tableHTML;
} else {
console.error(
`Contenitore della tabella non trovato: #${tableContainerId}`,
);
}
}

View File

@ -0,0 +1,133 @@
export function renderHorizontalBarChart(
chartData,
containerId,
tableContainerId,
) {
// Pulizia del contenitore del grafico
const chartContainer = document.querySelector(`#${containerId}`);
if (chartContainer) {
chartContainer.innerHTML = "";
} else {
console.error(`Contenitore del grafico non trovato: #${containerId}`);
return;
}
// Estrai i dati per il grafico a barre orizzontali
const horizontalBarData = chartData || [];
const categories = horizontalBarData.map((item) => item.groupingValue);
const passData = horizontalBarData.map((item) => item.passCount);
const failData = horizontalBarData.map((item) => item.failCount);
const otherData = horizontalBarData.map((item) => item.otherCount);
// Configurazione del grafico a barre orizzontali
const options = {
series: [
{
name: "Pass",
data: passData,
},
{
name: "Fail",
data: failData,
},
{
name: "Others",
data: otherData,
},
],
chart: {
type: "bar",
height: 600,
stacked: true,
toolbar: {
show: true,
tools: {
download: true,
selection: false,
zoom: false,
zoomin: false,
zoomout: false,
pan: false,
reset: false,
},
},
},
plotOptions: {
bar: {
horizontal: true,
barHeight: "80%",
},
},
dataLabels: {
enabled: false,
},
xaxis: {
categories: categories,
title: {
text: "Number of Reports",
},
},
yaxis: {
title: {
text: "Grouping Value",
},
},
colors: ["#28A745", "#FF4D4D", "#FFA500"],
fill: {
opacity: 1,
},
tooltip: {
y: {
formatter: function (val) {
return val + " reports";
},
},
},
legend: {
position: "top",
horizontalAlign: "left",
offsetX: 40,
},
};
// Render del grafico
const chart = new ApexCharts(chartContainer, options);
chart.render();
// Genera una tabella per i dati
const tableHTML = `
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Grouping Value</th>
<th>Pass</th>
<th>Fail</th>
<th>Others</th>
</tr>
</thead>
<tbody>
${horizontalBarData
.map(
(item) => `
<tr>
<td>${item.groupingValue}</td>
<td>${item.passCount}</td>
<td>${item.failCount}</td>
<td>${item.otherCount}</td>
</tr>
`,
)
.join("")}
</tbody>
</table>
`;
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML = tableHTML;
} else {
console.error(
`Contenitore della tabella non trovato: #${tableContainerId}`,
);
}
}

View File

@ -0,0 +1,172 @@
export function renderPhaseBarChart(chartData, containerId, tableContainerId) {
// Pulizia del contenitore del grafico
const chartContainer = document.querySelector(`#${containerId}`);
if (chartContainer) {
chartContainer.innerHTML = "";
} else {
console.error(`Contenitore del grafico non trovato: #${containerId}`);
return;
}
// Estrai i dati per il grafico a barre
const phaseRatingsData = chartData || [];
// Validazione dei dati
if (!Array.isArray(phaseRatingsData) || phaseRatingsData.length === 0) {
chartContainer.innerHTML =
'<p class="text-center text-muted">No data available for Phase Rating Distribution.</p>';
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML =
'<p class="text-center text-muted">No data available.</p>';
}
return;
}
const phases = phaseRatingsData.map((item) => item.phase || "Unknown");
const passData = phaseRatingsData.map(
(item) => parseInt(item.passCount) || 0,
);
const failData = phaseRatingsData.map(
(item) => parseInt(item.failCount) || 0,
);
const otherData = phaseRatingsData.map(
(item) => parseInt(item.otherCount) || 0,
);
// Verifica se ci sono dati validi da visualizzare
const hasValidData =
passData.some((value) => value > 0) ||
failData.some((value) => value > 0) ||
otherData.some((value) => value > 0);
if (!hasValidData) {
chartContainer.innerHTML =
'<p class="text-center text-muted">No ratings to display.</p>';
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML =
'<p class="text-center text-muted">No ratings to display.</p>';
}
return;
}
// Configurazione del grafico a barre orizzontali
const options = {
series: [
{
name: "Pass",
data: passData,
},
{
name: "Fail",
data: failData,
},
{
name: "Others",
data: otherData,
},
],
chart: {
type: "bar",
height: 600,
stacked: true,
toolbar: {
show: true,
tools: {
download: true,
selection: false,
zoom: false,
zoomin: false,
zoomout: false,
pan: false,
reset: false,
},
},
},
plotOptions: {
bar: {
horizontal: true,
barHeight: "80%",
},
},
dataLabels: {
enabled: false,
},
xaxis: {
categories: phases,
title: {
text: "Number of Reports",
},
},
yaxis: {
title: {
text: "Phase",
},
},
colors: ["#28A745", "#FF4D4D", "#FFA500"],
fill: {
opacity: 1,
},
tooltip: {
y: {
formatter: function (val) {
return val + " reports";
},
},
},
legend: {
position: "top",
horizontalAlign: "left",
offsetX: 40,
},
};
// Render del grafico
const chart = new ApexCharts(chartContainer, options);
chart.render();
// Genera una tabella per i dati
const tableHTML = `
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Phase</th>
<th>Pass</th>
<th>Fail</th>
<th>Others</th>
</tr>
</thead>
<tbody>
${phaseRatingsData
.map(
(item) => `
<tr>
<td>${item.phase || "Unknown"}</td>
<td>${parseInt(item.passCount) || 0}</td>
<td>${parseInt(item.failCount) || 0}</td>
<td>${parseInt(item.otherCount) || 0}</td>
</tr>
`,
)
.join("")}
</tbody>
</table>
`;
// Riprova a trovare il contenitore della tabella con un ritardo
const renderTable = () => {
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML = tableHTML;
tableContainer.style.display = "block"; // Forza la visibilità
} else {
console.error(
`Contenitore della tabella non trovato: #${tableContainerId}`,
);
}
};
// Prova immediatamente e riprova dopo un breve ritardo
renderTable();
setTimeout(renderTable, 100);
}

View File

@ -0,0 +1,126 @@
import { generatePieTable } from "../utils/tableGenerator.js";
export function renderPhasePieChart(chartData, containerId, tableContainerId) {
// Pulizia del contenitore del grafico
const chartContainer = document.querySelector(`#${containerId}`);
if (chartContainer) {
chartContainer.innerHTML = "";
} else {
console.error(`Contenitore del grafico non trovato: #${containerId}`);
return;
}
// Estrai i dati per il grafico a torta
const phaseData = chartData || [];
// Validazione dei dati
if (!Array.isArray(phaseData) || phaseData.length === 0) {
chartContainer.innerHTML =
'<p class="text-center text-muted">No data available for Products Distribution by Phase.</p>';
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML =
'<p class="text-center text-muted">No data available.</p>';
}
return;
}
const labels = phaseData.map((item) => item.phase || "Unknown");
const series = phaseData.map((item) => {
const count = parseInt(item.totalProducts, 10);
return isNaN(count) ? 0 : count;
});
// Verifica se ci sono dati validi da visualizzare
const hasValidData = series.some((value) => value > 0);
if (!hasValidData) {
chartContainer.innerHTML =
'<p class="text-center text-muted">No valid data to display.</p>';
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML =
'<p class="text-center text-muted">No valid data to display.</p>';
}
return;
}
// Configurazione del grafico a torta
const options = {
series: series,
chart: {
type: "pie",
height: 350,
toolbar: {
show: true,
tools: {
download: true,
selection: false,
zoom: false,
zoomin: false,
zoomout: false,
pan: false,
reset: false,
},
},
},
labels: labels,
colors: [
"#FF4560",
"#008FFB",
"#00E396",
"#FEB019",
"#FF66FF",
"#775DD0",
],
responsive: [
{
breakpoint: 480,
options: {
chart: {
width: 200,
},
legend: {
position: "bottom",
},
},
},
],
tooltip: {
y: {
formatter: function (val) {
return val + " products";
},
},
},
};
// Render del grafico
const chart = new ApexCharts(chartContainer, options);
chart.render();
// Forza un aggiornamento del grafico dopo un breve ritardo
setTimeout(() => {
chart.updateOptions({});
}, 100);
// Genera la tabella usando la funzione esistente
const tableHTML = generatePieTable(labels, series);
// Riprova a trovare il contenitore della tabella con un ritardo
const renderTable = () => {
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML = tableHTML;
tableContainer.style.display = "block"; // Forza la visibilità
} else {
console.error(
`Contenitore della tabella non trovato: #${tableContainerId}`,
);
}
};
// Prova immediatamente e riprova dopo un breve ritardo
renderTable();
setTimeout(renderTable, 100);
}

View File

@ -0,0 +1,68 @@
import { generatePieTable } from "../utils/tableGenerator.js";
export function renderPieChart(data, containerId, tableContainerId) {
// Pulizia del contenitore
document.querySelector(`#${containerId}`).innerHTML = "";
// Dati per il grafico a torta
const intFailReports = parseInt(data.failReportsPie);
const intPassReports = parseInt(data.passReportsPie);
const intOtherReports = parseInt(data.otherReportsPie);
const pieLabels = ["Fail", "Pass", "Others"];
const pieSeries = [intFailReports, intPassReports, intOtherReports];
// Configurazione del grafico
const options = {
series: pieSeries,
chart: {
width: "100%",
type: "pie",
toolbar: {
show: true,
tools: {
download: true,
selection: false,
zoom: false,
zoomin: false,
zoomout: false,
pan: false,
reset: false,
},
},
},
labels: pieLabels,
colors: ["#FF4D4D", "#28A745", "#FFA500"],
responsive: [
{
breakpoint: 480,
options: {
chart: {
width: 250,
},
legend: {
position: "bottom",
},
},
},
],
legend: {
position: "bottom",
offsetY: 0,
height: 50,
},
};
// Render del grafico
const chart = new ApexCharts(
document.querySelector(`#${containerId}`),
options,
);
chart.render();
// Genera la tabella
document.querySelector(`#${tableContainerId}`).innerHTML = generatePieTable(
pieLabels,
pieSeries,
);
}

View File

@ -0,0 +1,113 @@
export function renderProductBySupplierChart(
chartData,
containerId,
tableContainerId,
) {
// Pulizia del contenitore del grafico
const chartContainer = document.querySelector(`#${containerId}`);
if (chartContainer) {
chartContainer.innerHTML = "";
} else {
console.error(`Contenitore del grafico non trovato: #${containerId}`);
return;
}
// Estrai i dati per il grafico
const productBySupplier = chartData || [];
const suppliers = productBySupplier.map((item) => item.supplier);
const totalProducts = productBySupplier.map((item) => item.totalProducts);
// Configurazione del grafico a barre orizzontali
const options = {
series: [
{
name: "Total Products",
data: totalProducts,
},
],
chart: {
type: "bar",
height: 600,
toolbar: {
show: true,
tools: {
download: true,
selection: false,
zoom: false,
zoomin: false,
zoomout: false,
pan: false,
reset: false,
},
},
},
plotOptions: {
bar: {
horizontal: true,
barHeight: "80%",
},
},
dataLabels: {
enabled: true,
},
xaxis: {
categories: suppliers,
title: {
text: "Number of Products",
},
},
yaxis: {
title: {
text: "Supplier",
},
},
colors: ["#007BFF"],
fill: {
opacity: 1,
},
tooltip: {
y: {
formatter: function (val) {
return val + " products";
},
},
},
};
// Render del grafico
const chart = new ApexCharts(chartContainer, options);
chart.render();
// Genera una tabella per i dati
const tableHTML = `
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Supplier</th>
<th>Total Products</th>
</tr>
</thead>
<tbody>
${productBySupplier
.map(
(item) => `
<tr>
<td>${item.supplier}</td>
<td>${item.totalProducts}</td>
</tr>
`,
)
.join("")}
</tbody>
</table>
`;
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML = tableHTML;
} else {
console.error(
`Contenitore della tabella non trovato: #${tableContainerId}`,
);
}
}

View File

@ -0,0 +1,120 @@
export function renderWorstSuppliersChart(
chartData,
containerId,
tableContainerId,
) {
// Pulizia del contenitore del grafico
const chartContainer = document.querySelector(`#${containerId}`);
if (chartContainer) {
chartContainer.innerHTML = "";
} else {
console.error(`Contenitore del grafico non trovato: #${containerId}`);
return;
}
// Estrai i dati per il grafico
const worstSuppliers = chartData || [];
const suppliers = worstSuppliers.map((item) => item.supplier);
const failPercentages = worstSuppliers.map((item) => item.failPercentage);
// Configurazione del grafico a barre orizzontali
const options = {
series: [
{
name: "Fail Percentage",
data: failPercentages,
},
],
chart: {
type: "bar",
height: 600,
toolbar: {
show: true,
tools: {
download: true,
selection: false,
zoom: false,
zoomin: false,
zoomout: false,
pan: false,
reset: false,
},
},
},
plotOptions: {
bar: {
horizontal: true,
barHeight: "80%",
},
},
dataLabels: {
enabled: true,
formatter: function (val) {
return val.toFixed(2) + "%";
},
},
xaxis: {
categories: suppliers,
title: {
text: "Fail Percentage (%)",
},
},
yaxis: {
title: {
text: "Supplier",
},
},
colors: ["#FF4D4D"],
fill: {
opacity: 1,
},
tooltip: {
y: {
formatter: function (val) {
return val.toFixed(2) + "%";
},
},
},
};
// Render del grafico
const chart = new ApexCharts(chartContainer, options);
chart.render();
// Genera una tabella per i dati
const tableHTML = `
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Supplier</th>
<th>Fail Percentage</th>
<th>Total Reports</th>
<th>Failed Reports</th>
</tr>
</thead>
<tbody>
${worstSuppliers
.map(
(item) => `
<tr>
<td>${item.supplier}</td>
<td>${item.failPercentage.toFixed(2)}%</td>
<td>${item.totalReports}</td>
<td>${item.failedReports}</td>
</tr>
`,
)
.join("")}
</tbody>
</table>
`;
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML = tableHTML;
} else {
console.error(
`Contenitore della tabella non trovato: #${tableContainerId}`,
);
}
}

View File

@ -0,0 +1,145 @@
export function renderWorstTenAnalysisChart(
analysisData,
containerId,
tableContainerId,
) {
// Pulizia del contenitore del grafico
const chartContainer = document.querySelector(`#${containerId}`);
if (chartContainer) {
chartContainer.innerHTML = "";
} else {
console.error(`Contenitore del grafico non trovato: #${containerId}`);
return;
}
// Validazione dei dati
if (!Array.isArray(analysisData) || analysisData.length === 0) {
chartContainer.innerHTML =
'<p class="text-center text-muted">No failed tests to display.</p>';
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML =
'<p class="text-center text-muted">No failed tests to display.</p>';
}
return;
}
const labels = analysisData.map((item) => item.name || "Unknown");
const data = analysisData.map((item) => parseInt(item.failCount, 10) || 0);
console.log("Dati per il grafico Worst Analysis:", { labels, data });
// Verifica se ci sono dati validi da visualizzare
const hasValidData = analysisData.some((item) => item.failCount > 0);
if (!hasValidData) {
chartContainer.innerHTML =
'<p class="text-center text-muted">No failed tests to display.</p>';
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML =
'<p class="text-center text-muted">No failed tests to display.</p>';
}
return;
}
// Configurazione del grafico a barre orizzontali
const options = {
series: [
{
name: "Failed Tests",
data: data,
},
],
chart: {
type: "bar",
height: 400,
toolbar: {
show: true,
tools: {
download: true,
selection: false,
zoom: false,
zoomin: false,
zoomout: false,
pan: false,
reset: false,
},
},
},
plotOptions: {
bar: {
horizontal: true,
barHeight: "80%",
},
},
dataLabels: {
enabled: false,
},
xaxis: {
categories: labels,
title: {
text: "Number of Failed Tests",
},
},
yaxis: {
title: {
text: "Analysis",
},
},
colors: ["#FF4D4D"],
fill: {
opacity: 1,
},
tooltip: {
y: {
formatter: function (val) {
return val + " failed tests";
},
},
},
};
// Render del grafico
const chart = new ApexCharts(chartContainer, options);
chart.render();
// Genera una tabella per i dati
const tableHTML = `
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Analysis</th>
<th>Failed Tests</th>
</tr>
</thead>
<tbody>
${analysisData
.map(
(item) => `
<tr>
<td>${item.name || "Unknown"}</td>
<td>${parseInt(item.failCount, 10) || 0}</td>
</tr>
`,
)
.join("")}
</tbody>
</table>
`;
// Riprova a trovare il contenitore della tabella con un ritardo
const renderTable = () => {
const tableContainer = document.querySelector(`#${tableContainerId}`);
if (tableContainer) {
tableContainer.innerHTML = tableHTML;
} else {
console.error(
`Contenitore della tabella non trovato: #${tableContainerId}`,
);
}
};
// Prova immediatamente e riprova dopo un breve ritardo
renderTable();
setTimeout(renderTable, 100);
}

View File

@ -0,0 +1,138 @@
import { fetchData } from "./utils/ajaxUtils.js";
import { renderPieChart } from "./charts/pieChart.js";
import { renderBarChart } from "./charts/barChart.js";
import { renderHorizontalBarChart } from "./charts/horizontalBarChart.js";
import { renderWorstSuppliersChart } from "./charts/worstSuppliersChart.js";
import { renderProductBySupplierChart } from "./charts/productBySupplierChart.js";
import { renderAnalysisDistributionChart } from "./charts/analysisDistributionChart.js";
import { renderWorstTenAnalysisChart } from "./charts/worsttenanalysis.js";
import { renderAnalytesFailChart } from "./charts/analytesFailChart.js";
import { renderPhasePieChart } from "./charts/phasePieChart.js";
import { renderPhaseBarChart } from "./charts/phaseBarChart.js";
$(document).ready(function () {
function getFilters() {
return {
startDate: $("#startDate").val(),
endDate: $("#endDate").val(),
supplier: $("#supplierFilter").val(),
productsRefnumber: $("#productsRefnumber").val(),
productsSeason: $("#productsSeason").val(),
ageRange: $("#ageRange").val(),
reportsLabName: $("#reportsLabName").val(),
reportsTestType: $("#reportsTestType").val(),
reportsNumberLab: $("#reportsNumberLab").val(),
groupingField: $("#groupingField").val(),
};
}
function updateCharts() {
const filters = getFilters();
// Aggiorna il titolo dinamicamente
const groupingText = $("#groupingField option:selected").text();
$("#dynamicChartTitle").text(
`Rating Distribution by Group: ${groupingText}`,
);
fetchData("parsedatachart.php", filters).then((data) => {
if (data) {
console.log("Dati ricevuti dal backend:", data); // Aggiungi log qui
console.log("topFailingAnalysis:", data.topFailingAnalysis);
console.log("failedAnalytes:", data.failedAnalytes);
// Aggiorna le card
$("#totalProducts").text(data.totalProducts);
$("#totalReports").text(data.totalReports);
$("#failedReports").text(data.failedReports);
$("#failedReportsPercent").text(
`(${data.failedReportsPercent.toFixed(2)}%)`,
);
$("#totalTests").text(data.totalTests);
$("#failedTests").text(data.failedTests);
$("#failedTestsPercent").text(
`(${data.failedTestsPercent.toFixed(2)}%)`,
);
// Renderizza i grafici
renderPieChart(data, "reportPieChart", "tableChart2");
renderBarChart(data, "reportBarChart", "tableChart3");
renderHorizontalBarChart(
data.horizontalBarData,
"horizontalBarChart",
"tableHorizontalBarChart",
);
renderHorizontalBarChart(
data.horizontalBarAnalysisData,
"horizontalBarAnalysisChart",
"tableHorizontalBarAnalysisChart",
);
renderWorstSuppliersChart(
data.worstSuppliers,
"worstSuppliersChart",
"tableChartWorstSuppliers",
);
renderProductBySupplierChart(
data.productBySupplier,
"productBySupplierChart",
"tableChartProductsBySupplier",
);
renderAnalysisDistributionChart(
data.analysisDistribution,
"analysisDistributionChart",
"tableChartAnalysisDistribution",
);
renderWorstTenAnalysisChart(
data.topFailingAnalysis,
"worsttenanalysis",
"tableChartWorst",
);
renderAnalytesFailChart(
data.failedAnalytes,
"analytesFailChart",
"tableChartAnalytesFail",
);
renderPhasePieChart(
data.phaseData,
"phasePieChart",
"tableChartPhasePie",
);
renderPhaseBarChart(
data.phaseRatingsData,
"phaseBarChart",
"tableChartPhaseBar",
);
}
});
}
function setupEventListeners() {
$(
"#startDate, #endDate, #supplierFilter, #productsRefnumber, #productsSeason, #ageRange, #reportsLabName, #reportsTestType, #reportsNumberLab, #groupingField",
).on("change", updateCharts);
$("#clearFilters").on("click", () => {
$("#startDate").val("");
$("#endDate").val("");
$("#supplierFilter").val("").trigger("change");
$("#productsRefnumber").val("").trigger("change");
$("#productsSeason").val("").trigger("change");
$("#ageRange").val("").trigger("change");
$("#reportsLabName").val("").trigger("change");
$("#reportsTestType").val("").trigger("change");
$("#reportsNumberLab").val("").trigger("change");
updateCharts();
});
// Evento per togglare la tabella con delegazione
$(document).on("click", ".toggle-table", function () {
const target = $(this).data("target");
console.log("Toggle table clicked, target:", target);
setTimeout(() => {
$(target).toggleClass("hidden");
}, 200);
});
}
setupEventListeners();
updateCharts(); // Caricamento iniziale
});

View File

@ -0,0 +1,23 @@
export function fetchData(url, filters) {
return $.ajax({
url: url,
method: "POST",
data: filters,
})
.then((response) => {
if (!response) {
alert("No data found.");
return null;
}
try {
return JSON.parse(response);
} catch (e) {
alert("Invalid data format.");
return null;
}
})
.catch(() => {
alert("Error retrieving data.");
return null;
});
}

View File

@ -0,0 +1,24 @@
export function generatePieTable(labels, series) {
let tableHTML = `
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Category</th>
<th>Count</th>
</tr>
</thead>
<tbody>
${labels
.map(
(label, i) => `
<tr>
<td>${label}</td>
<td>${series[i]}</td>
</tr>
`,
)
.join("")}
</tbody>
</table>`;
return tableHTML;
}

View File

@ -0,0 +1,18 @@
<script src="../assets/js/popper.min.js"></script>
<script src="../assets/js/bootstrap.min.js"></script>
<script src="../assets/js/modernizr.min.js"></script>
<script src="../assets/js/detect.js"></script>
<script src="../assets/js/fastclick.js"></script>
<script src="../assets/js/jquery.slimscroll.js"></script>
<script src="../assets/js/jquery.blockUI.js"></script>
<script src="../assets/js/waves.js"></script>
<script src="../assets/js/jquery.nicescroll.js"></script>
<script src="../assets/js/jquery.scrollTo.min.js"></script>
<script src="../assets/js/common_helper.js"></script>
<script src="../assets/plugins/chart.js/chart.min.js"></script>
<script src="../assets/pages/dashboard.js"></script>
<!-- App js -->
<script src="../assets/js/app.js"></script>
<script src="../assets/plugins/alertify/js/alertify.js"></script>

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +1,38 @@
@if ($socialProviders) @if ($socialProviders)
<?php $colSize = 12 / count($socialProviders); ?> <?php $colSize = 12 / count($socialProviders); ?>
<div class="row pb-3 pt-2"> <div class="row pb-3 pt-2">
@if (in_array('facebook', $socialProviders)) @if (in_array('facebook', $socialProviders))
<div class="col-{{ $colSize }} d-flex align-items-center justify-content-center"> <div class="col-{{ $colSize }} d-flex align-items-center justify-content-center">
<a href="{{ url('auth/facebook/login') }}" class="btn-facebook"> <a href="{{ url('auth/facebook/login') }}" class="btn-facebook">
<i class="fab fa-facebook fa-2x"></i> <i class="fab fa-facebook fa-2x"></i>
</a> </a>
</div>
@endif
@if (in_array('twitter', $socialProviders))
<div class="col-{{ $colSize }} d-flex align-items-center justify-content-center">
<a href="{{ url('auth/twitter/login') }}" class="btn-twitter">
<i class="fab fa-twitter fa-2x"></i>
</a>
</div>
@endif
@if (in_array('google', $socialProviders))
<div class="col-{{ $colSize }} d-flex align-items-center justify-content-center">
<a href="{{ url('auth/google/login') }}" class="btn-google">
<i class="fab fa-google-plus-square fa-2x"></i>
</a>
</div>
@endif
</div> </div>
@endif
@if (in_array('twitter', $socialProviders))
<div class="col-{{ $colSize }} d-flex align-items-center justify-content-center">
<a href="{{ url('auth/twitter/login') }}" class="btn-twitter">
<i class="fab fa-twitter fa-2x"></i>
</a>
</div>
@endif
@if (in_array('google', $socialProviders))
<div class="col-{{ $colSize }} d-flex align-items-center justify-content-center">
<a href="{{ url('auth/google/login') }}" class="btn-google">
<i class="fab fa-google-plus-square fa-2x"></i>
</a>
</div>
@endif
@if (in_array('azure', $socialProviders))
<div class="col-{{ $colSize }} d-flex align-items-center justify-content-center">
<a href="{{ url('auth/azure/login') }}" style="color: #0078d4;">
<i class="fab fa-microsoft fa-2x"></i>
</a>
</div>
@endif
</div>
@endif @endif