diff --git a/app/Http/Requests/Auth/PasswordResetRequest.php b/app/Http/Requests/Auth/PasswordResetRequest.php index b26a08e..b9b53f0 100644 --- a/app/Http/Requests/Auth/PasswordResetRequest.php +++ b/app/Http/Requests/Auth/PasswordResetRequest.php @@ -16,7 +16,24 @@ class PasswordResetRequest extends Request return [ 'token' => 'required', '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 (@$!%*?&).') ]; } diff --git a/app/Http/Requests/Auth/RegisterRequest.php b/app/Http/Requests/Auth/RegisterRequest.php index 9d07dd0..2134998 100644 --- a/app/Http/Requests/Auth/RegisterRequest.php +++ b/app/Http/Requests/Auth/RegisterRequest.php @@ -14,10 +14,15 @@ class RegisterRequest extends Request */ public function rules() { - $rules = [ + $rules = [ 'email' => 'required|email|unique:users,email', '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')) { @@ -36,10 +41,11 @@ class RegisterRequest extends Request * * @return array */ - public function messages() +public function messages() { 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 (@$!%*?&).') ]; } diff --git a/app/Http/Requests/User/CreateUserRequest.php b/app/Http/Requests/User/CreateUserRequest.php index 52f82a6..201b565 100644 --- a/app/Http/Requests/User/CreateUserRequest.php +++ b/app/Http/Requests/User/CreateUserRequest.php @@ -17,7 +17,12 @@ class CreateUserRequest extends Request $rules = [ 'email' => 'required|email|unique:users,email', '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', 'role_id' => 'required|exists:roles,id', 'verified' => 'boolean' @@ -29,4 +34,16 @@ class CreateUserRequest extends Request 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 (@$!%*?&).') + ]; + } } diff --git a/app/Http/Requests/User/UpdateLoginDetailsRequest.php b/app/Http/Requests/User/UpdateLoginDetailsRequest.php index f46f149..8e2811f 100644 --- a/app/Http/Requests/User/UpdateLoginDetailsRequest.php +++ b/app/Http/Requests/User/UpdateLoginDetailsRequest.php @@ -19,7 +19,24 @@ class UpdateLoginDetailsRequest extends Request return [ 'email' => 'required|email|unique:users,email,' . $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 (@$!%*?&).') ]; } diff --git a/app/Http/Requests/User/UpdateUserRequest.php b/app/Http/Requests/User/UpdateUserRequest.php index a502ca5..c338e9a 100644 --- a/app/Http/Requests/User/UpdateUserRequest.php +++ b/app/Http/Requests/User/UpdateUserRequest.php @@ -21,11 +21,28 @@ class UpdateUserRequest extends Request return [ 'email' => 'email|unique:users,email,' . $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', 'role_id' => 'exists:roles,id', 'country_id' => 'exists:countries,id', '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 (@$!%*?&).') + ]; + } } diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index dd689ef..d9e2564 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -33,7 +33,10 @@ class EventServiceProvider extends ServiceProvider ], Verified::class => [ ActivateUser::class - ] + ], + \SocialiteProviders\Manager\SocialiteWasCalled::class => [ + 'SocialiteProviders\\Azure\\AzureExtendSocialite@handle', // Usa una stringa con @handle + ], ]; /** diff --git a/composer.json b/composer.json index b5bf334..6f46186 100644 --- a/composer.json +++ b/composer.json @@ -42,6 +42,7 @@ "laravel/ui": "^4.0", "laravelcollective/html": "^6.3", "proengsoft/laravel-jsvalidation": "^4.0.0", + "socialiteproviders/microsoft-azure": "^5.2", "spatie/laravel-query-builder": "^5.0", "vanguardapp/activity-log": "^5.0", "vanguardapp/announcements": "^5.0", diff --git a/composer.lock b/composer.lock index 545cbf3..e05a6aa 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0868d0ed54d8695b929de611fc8beeab", + "content-hash": "df98df06998bb59ef615306b9f0d421b", "packages": [ { "name": "akaunting/laravel-setting", @@ -4145,6 +4145,131 @@ }, "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", "version": "1.14.2", diff --git a/config/app.php b/config/app.php index 84ea40f..7e1da9c 100644 --- a/config/app.php +++ b/config/app.php @@ -198,7 +198,7 @@ return [ Illuminate\Pagination\PaginationServiceProvider::class, Illuminate\Pipeline\PipelineServiceProvider::class, Illuminate\Queue\QueueServiceProvider::class, -// Illuminate\Redis\RedisServiceProvider::class, + // Illuminate\Redis\RedisServiceProvider::class, Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, Illuminate\Session\SessionServiceProvider::class, Illuminate\Translation\TranslationServiceProvider::class, @@ -227,6 +227,7 @@ return [ Vanguard\Providers\RouteServiceProvider::class, Vanguard\Services\Auth\TwoFactor\AuthyServiceProvider::class, Vanguard\Providers\VanguardServiceProvider::class, + \SocialiteProviders\Manager\ServiceProvider::class, ], /* diff --git a/config/auth.php b/config/auth.php index 7fd7a5d..78fa04b 100644 --- a/config/auth.php +++ b/config/auth.php @@ -12,10 +12,10 @@ return [ */ 'social' => [ - 'providers' => ['facebook', 'twitter', 'google'] + 'providers' => ['azure'] ], - /* + /* |-------------------------------------------------------------------------- | JSON API |-------------------------------------------------------------------------- diff --git a/config/services.php b/config/services.php index 1c658f3..90a27cd 100644 --- a/config/services.php +++ b/config/services.php @@ -65,5 +65,11 @@ return [ 'authy' => [ 'key' => env('AUTHY_KEY') + ], + + 'azure' => [ + 'client_id' => env('AZURE_CLIENT_ID'), + 'client_secret' => env('AZURE_CLIENT_SECRET'), + 'redirect' => env('AZURE_REDIRECT_URI'), ] ]; diff --git a/public/userarea.zip b/public/userarea.zip new file mode 100644 index 0000000..2ce9d70 Binary files /dev/null and b/public/userarea.zip differ diff --git a/public/userarea/apilogic/api-to-temp - Copia050325.php b/public/userarea/apilogic/api-to-temp - Copia050325.php new file mode 100644 index 0000000..c7bb0a4 --- /dev/null +++ b/public/userarea/apilogic/api-to-temp - Copia050325.php @@ -0,0 +1,167 @@ +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(); diff --git a/public/userarea/apilogic/api-to-temp.php b/public/userarea/apilogic/api-to-temp.php index c7bb0a4..f324c8d 100644 --- a/public/userarea/apilogic/api-to-temp.php +++ b/public/userarea/apilogic/api-to-temp.php @@ -23,73 +23,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // 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'])) { + // Check only for the required product_refnumber + 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." @@ -97,9 +32,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { 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) + // Generate a UUID to uniquely identify the record + $uuid = uniqid(); // Alternatively, use UUID() in MySQL + + // 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 // Query to insert data into the temp_json_queue table diff --git a/public/userarea/apilogic/api-to-temp110325.php b/public/userarea/apilogic/api-to-temp110325.php new file mode 100644 index 0000000..115f44b --- /dev/null +++ b/public/userarea/apilogic/api-to-temp110325.php @@ -0,0 +1,168 @@ +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(); diff --git a/public/userarea/class/db-functions.php b/public/userarea/class/db-functions.php new file mode 100644 index 0000000..509e483 --- /dev/null +++ b/public/userarea/class/db-functions.php @@ -0,0 +1,40 @@ +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; + } +} diff --git a/public/userarea/class/mailer.php b/public/userarea/class/mailer.php new file mode 100644 index 0000000..851e4cf --- /dev/null +++ b/public/userarea/class/mailer.php @@ -0,0 +1,61 @@ +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}"]; + } +} diff --git a/public/userarea/cssinclude.php b/public/userarea/cssinclude.php new file mode 100644 index 0000000..b439e93 --- /dev/null +++ b/public/userarea/cssinclude.php @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/public/userarea/include/headscript.php b/public/userarea/include/headscript.php index bec308e..838d6bb 100644 --- a/public/userarea/include/headscript.php +++ b/public/userarea/include/headscript.php @@ -4,11 +4,8 @@ ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL | E_STRICT); -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/'); +require_once __DIR__ . '/path_definition.php'; + // This should be equal to: PATH_TO_VANGUARD_FOLDER/extra/auth.php require_once(BASE_PATH . '/extra/auth.php'); @@ -75,11 +72,7 @@ if (isset($_SESSION["infobox"])) { ?> diff --git a/public/userarea/include/path_definition.php b/public/userarea/include/path_definition.php new file mode 100644 index 0000000..d4d95d6 --- /dev/null +++ b/public/userarea/include/path_definition.php @@ -0,0 +1,8 @@ + getConnection(); // Query 1: Numero totale di prodotti $totalProductsQuery = "SELECT COUNT(DISTINCT p.idproducts) AS totalProducts FROM products p WHERE 1=1"; -$totalProductsResult = $conn->query($totalProductsQuery); -$totalProducts = $totalProductsResult->fetch_assoc()['totalProducts']; +$stmt = $pdo->query($totalProductsQuery); +$totalProducts = $stmt->fetch(PDO::FETCH_ASSOC)['totalProducts']; // Query 2: Numero totale di report $totalReportsQuery = " SELECT COUNT(DISTINCT r.idreports) AS totalReports FROM reports r LEFT JOIN products p ON r.idproducts = p.idproducts"; -$totalReportsResult = $conn->query($totalReportsQuery); -$totalReports = $totalReportsResult->fetch_assoc()['totalReports']; +$stmt = $pdo->query($totalReportsQuery); +$totalReports = $stmt->fetch(PDO::FETCH_ASSOC)['totalReports']; // Query 3: Numero di report "fail" $failedReportsQuery = " @@ -22,39 +26,29 @@ $failedReportsQuery = " FROM reports r LEFT JOIN products p ON r.idproducts = p.idproducts WHERE UPPER(r.reportsRating) IN ('FAIL', 'F', 'DOESN\'T COMPLY')"; -$failedReportsResult = $conn->query($failedReportsQuery); -$failedReports = $failedReportsResult->fetch_assoc()['failedReports']; +$stmt = $pdo->query($failedReportsQuery); +$failedReports = $stmt->fetch(PDO::FETCH_ASSOC)['failedReports']; +// Query 4: Numero totale di test $totalTestsQuery = " SELECT COUNT(DISTINCT ap.idreports, ap.idPart, ap.result_TestName) AS totalTests FROM analysis_project ap LEFT JOIN result_project rp ON ap.idAnalysis_Project = rp.idanalysis_project LEFT JOIN reports r ON ap.idreports = r.idreports LEFT JOIN products p ON r.idproducts = p.idproducts"; -$totalTestsResult = $conn->query($totalTestsQuery); -$totalTests = $totalTestsResult->fetch_assoc()['totalTests']; -// Verifica connessione -if ($conn->connect_error) { - die("Connessione fallita: " . $conn->connect_error); -} -?> -query($totalTestsQuery); +$totalTests = $stmt->fetch(PDO::FETCH_ASSOC)['totalTests']; +// Query per ottenere i moduli attivi e disattivi $query = " SELECT idmodules, activemod FROM activemodules - WHERE idcompany = ? + WHERE idcompany = :idcompany "; -$stmt = $conn->prepare($query); -$stmt->bind_param("i", $idcompany); -$stmt->execute(); -$result = $stmt->get_result(); - +$stmt = $pdo->prepare($query); +$stmt->execute(['idcompany' => $idcompany]); $modulesStatus = []; -while ($row = $result->fetch_assoc()) { +while ($row = $stmt->fetch()) { $modulesStatus[$row['idmodules']] = $row['activemod']; } ?> @@ -72,14 +66,10 @@ while ($row = $result->fetch_assoc()) { - - - - + - @@ -95,15 +85,13 @@ while ($row = $result->fetch_assoc()) { -
-
- +
@@ -120,6 +108,8 @@ while ($row = $result->fetch_assoc()) {
+ +
@@ -194,10 +184,8 @@ while ($row = $result->fetch_assoc()) {
- - +
- @@ -217,7 +204,6 @@ while ($row = $result->fetch_assoc()) {
Reports

-
@@ -229,7 +215,6 @@ while ($row = $result->fetch_assoc()) {

-
@@ -241,15 +226,12 @@ while ($row = $result->fetch_assoc()) {


-
- -
@@ -258,7 +240,6 @@ while ($row = $result->fetch_assoc()) {


-
@@ -271,7 +252,6 @@ while ($row = $result->fetch_assoc()) {
SayTRL

-
@@ -284,7 +264,6 @@ while ($row = $result->fetch_assoc()) {
Rate&Go

- @@ -296,50 +275,22 @@ while ($row = $result->fetch_assoc()) {
ReEvaluate

- - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - + diff --git a/public/userarea/jsinclude.php b/public/userarea/jsinclude.php new file mode 100644 index 0000000..9106ee3 --- /dev/null +++ b/public/userarea/jsinclude.php @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/userarea/products/cssinclude.php b/public/userarea/products/cssinclude.php new file mode 100644 index 0000000..6c952f3 --- /dev/null +++ b/public/userarea/products/cssinclude.php @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/public/userarea/products/products.php b/public/userarea/products/products.php index fa2e92f..cbdf1e5 100644 --- a/public/userarea/products/products.php +++ b/public/userarea/products/products.php @@ -3,27 +3,44 @@ ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); -include('../include/headscript.php'); ?> -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 -$query = "SELECT column_visibility, column_order FROM user_table_settings WHERE iduser = ? AND page = ?"; -$stmt = $conn->prepare($query); -$stmt->bind_param("is", $iduserlogin, $page); -$stmt->execute(); -$stmt->bind_result($column_visibility, $column_order); -$stmt->fetch(); -$stmt->close(); -?> +try { + $query = "SELECT column_visibility, column_order FROM user_table_settings WHERE iduser = :iduser AND page = :page"; + $stmt = $pdo->prepare($query); + $stmt->execute(['iduser' => $iduserlogin, 'page' => $page]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $column_visibility = $row['column_visibility'] ?? null; + $column_order = $row['column_order'] ?? null; +} catch (PDOException $e) { + error_log("Errore query user_table_settings: " . $e->getMessage()); + die("Errore nel caricamento delle impostazioni."); +} -query($query); +// Query per ottenere i prodotti con colonne specifiche +try { + // Rimossa la condizione WHERE iduser per compatibilitĂ  (potrebbe non esserci il campo iduser nella tabella products) + $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"; + $stmt = $pdo->prepare($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')); +} ?> @@ -45,7 +62,6 @@ $result = $conn->query($query); - @@ -54,15 +70,8 @@ $result = $conn->query($query); - - - - - - - - + -
@@ -134,7 +139,6 @@ $result = $conn->query($query);
-
@@ -142,7 +146,6 @@ $result = $conn->query($query);
-
@@ -174,27 +177,27 @@ $result = $conn->query($query); - fetch_assoc()) { ?> - - - - - - - - - - - - - - - - - - - - + fetch(PDO::FETCH_ASSOC)) { ?> + + + + + + + + + + + + + + + + + + + + @@ -207,31 +210,27 @@ $result = $conn->query($query);
- - -
-
-
+
+ + - - \ No newline at end of file diff --git a/public/userarea/reports/reports.php b/public/userarea/reports/reports.php index 8bae1b3..607d08b 100644 --- a/public/userarea/reports/reports.php +++ b/public/userarea/reports/reports.php @@ -1,26 +1,48 @@ - -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 -$query = "SELECT column_visibility, column_order FROM user_table_settings WHERE iduser = ? AND page = ?"; -$stmt = $conn->prepare($query); -$stmt->bind_param("is", $iduserlogin, $page); -$stmt->execute(); -$stmt->bind_result($column_visibility, $column_order); -$stmt->fetch(); -$stmt->close(); +try { + $stmt = $conn->prepare("SELECT column_visibility, column_order FROM user_table_settings WHERE iduser = :iduser AND page = :page"); + $stmt->execute(['iduser' => $iduserlogin, 'page' => $page]); + $userSettings = $stmt->fetch(PDO::FETCH_ASSOC); + $column_visibility = $userSettings['column_visibility'] ?? null; + $column_order = $userSettings['column_order'] ?? null; +} 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 = " - SELECT r.*, p.products_refnumber, p.products_description - FROM reports r - LEFT JOIN products p ON r.idproducts = p.idproducts"; -$result = $conn->query($query); +try { + $stmt = $conn->prepare(" + SELECT r.idreports, r.reportsNumberLab, r.reportDateIn, r.reportsRating, p.products_refnumber, p.products_description + FROM reports r + 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')); +} ?> @@ -31,8 +53,8 @@ $result = $conn->query($query); Reports + - @@ -52,15 +74,8 @@ $result = $conn->query($query); - - - - - - - - + -
@@ -97,7 +111,6 @@ $result = $conn->query($query);
-
@@ -105,13 +118,12 @@ $result = $conn->query($query);
-
- +
@@ -123,31 +135,29 @@ $result = $conn->query($query); - fetch_assoc()) { ?> - - - - - - + + + + + + +
Report Number
- Details + Details
-
- -
-
-
+
+ + @@ -155,13 +165,10 @@ $result = $conn->query($query); - - - - \ No newline at end of file diff --git a/public/userarea/statkpi/css-statkpi.php b/public/userarea/statkpi/css-statkpi.php new file mode 100644 index 0000000..ae906d8 --- /dev/null +++ b/public/userarea/statkpi/css-statkpi.php @@ -0,0 +1,226 @@ + + \ No newline at end of file diff --git a/public/userarea/statkpi/js-dragdrop.php b/public/userarea/statkpi/js-dragdrop.php new file mode 100644 index 0000000..740f4c6 --- /dev/null +++ b/public/userarea/statkpi/js-dragdrop.php @@ -0,0 +1,68 @@ + \ No newline at end of file diff --git a/public/userarea/statkpi/js-statkpi.php b/public/userarea/statkpi/js-statkpi.php new file mode 100644 index 0000000..8ac2705 --- /dev/null +++ b/public/userarea/statkpi/js-statkpi.php @@ -0,0 +1,1148 @@ + \ No newline at end of file diff --git a/public/userarea/statkpi/js/charts/analysisDistributionChart.js b/public/userarea/statkpi/js/charts/analysisDistributionChart.js new file mode 100644 index 0000000..f90be21 --- /dev/null +++ b/public/userarea/statkpi/js/charts/analysisDistributionChart.js @@ -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 = + '

No valid data to display.

'; + 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}`, + ); + } +} diff --git a/public/userarea/statkpi/js/charts/analytesFailChart.js b/public/userarea/statkpi/js/charts/analytesFailChart.js new file mode 100644 index 0000000..7d4a688 --- /dev/null +++ b/public/userarea/statkpi/js/charts/analytesFailChart.js @@ -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 = + '

No failures to display.

'; + const tableContainer = document.querySelector(`#${tableContainerId}`); + if (tableContainer) { + tableContainer.innerHTML = + '

No failures to display.

'; + } + 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 = + '

No failures to display.

'; + const tableContainer = document.querySelector(`#${tableContainerId}`); + if (tableContainer) { + tableContainer.innerHTML = + '

No failures to display.

'; + } + 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 = ` + + + + + + + + + ${analytesData + .map( + (item) => ` + + + + + `, + ) + .join("")} + +
AnalyteFailures
${item.AnalyteName || "Unknown"}${parseInt(item.FailCount, 10) || 0}
+ `; + + // 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); +} diff --git a/public/userarea/statkpi/js/charts/barChart.js b/public/userarea/statkpi/js/charts/barChart.js new file mode 100644 index 0000000..664d227 --- /dev/null +++ b/public/userarea/statkpi/js/charts/barChart.js @@ -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}`, + ); + } +} diff --git a/public/userarea/statkpi/js/charts/horizontalBarChart.js b/public/userarea/statkpi/js/charts/horizontalBarChart.js new file mode 100644 index 0000000..97aeee4 --- /dev/null +++ b/public/userarea/statkpi/js/charts/horizontalBarChart.js @@ -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 = ` + + + + + + + + + + + ${horizontalBarData + .map( + (item) => ` + + + + + + + `, + ) + .join("")} + +
Grouping ValuePassFailOthers
${item.groupingValue}${item.passCount}${item.failCount}${item.otherCount}
+ `; + + const tableContainer = document.querySelector(`#${tableContainerId}`); + if (tableContainer) { + tableContainer.innerHTML = tableHTML; + } else { + console.error( + `Contenitore della tabella non trovato: #${tableContainerId}`, + ); + } +} diff --git a/public/userarea/statkpi/js/charts/phaseBarChart.js b/public/userarea/statkpi/js/charts/phaseBarChart.js new file mode 100644 index 0000000..d2bd786 --- /dev/null +++ b/public/userarea/statkpi/js/charts/phaseBarChart.js @@ -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 = + '

No data available for Phase Rating Distribution.

'; + const tableContainer = document.querySelector(`#${tableContainerId}`); + if (tableContainer) { + tableContainer.innerHTML = + '

No data available.

'; + } + 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 = + '

No ratings to display.

'; + const tableContainer = document.querySelector(`#${tableContainerId}`); + if (tableContainer) { + tableContainer.innerHTML = + '

No ratings to display.

'; + } + 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 = ` + + + + + + + + + + + ${phaseRatingsData + .map( + (item) => ` + + + + + + + `, + ) + .join("")} + +
PhasePassFailOthers
${item.phase || "Unknown"}${parseInt(item.passCount) || 0}${parseInt(item.failCount) || 0}${parseInt(item.otherCount) || 0}
+ `; + + // 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); +} diff --git a/public/userarea/statkpi/js/charts/phasePieChart.js b/public/userarea/statkpi/js/charts/phasePieChart.js new file mode 100644 index 0000000..cf22709 --- /dev/null +++ b/public/userarea/statkpi/js/charts/phasePieChart.js @@ -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 = + '

No data available for Products Distribution by Phase.

'; + const tableContainer = document.querySelector(`#${tableContainerId}`); + if (tableContainer) { + tableContainer.innerHTML = + '

No data available.

'; + } + 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 = + '

No valid data to display.

'; + const tableContainer = document.querySelector(`#${tableContainerId}`); + if (tableContainer) { + tableContainer.innerHTML = + '

No valid data to display.

'; + } + 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); +} diff --git a/public/userarea/statkpi/js/charts/pieChart.js b/public/userarea/statkpi/js/charts/pieChart.js new file mode 100644 index 0000000..aa22cb1 --- /dev/null +++ b/public/userarea/statkpi/js/charts/pieChart.js @@ -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, + ); +} diff --git a/public/userarea/statkpi/js/charts/productBySupplierChart.js b/public/userarea/statkpi/js/charts/productBySupplierChart.js new file mode 100644 index 0000000..13c5397 --- /dev/null +++ b/public/userarea/statkpi/js/charts/productBySupplierChart.js @@ -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 = ` + + + + + + + + + ${productBySupplier + .map( + (item) => ` + + + + + `, + ) + .join("")} + +
SupplierTotal Products
${item.supplier}${item.totalProducts}
+ `; + + const tableContainer = document.querySelector(`#${tableContainerId}`); + if (tableContainer) { + tableContainer.innerHTML = tableHTML; + } else { + console.error( + `Contenitore della tabella non trovato: #${tableContainerId}`, + ); + } +} diff --git a/public/userarea/statkpi/js/charts/worstSuppliersChart.js b/public/userarea/statkpi/js/charts/worstSuppliersChart.js new file mode 100644 index 0000000..0615db8 --- /dev/null +++ b/public/userarea/statkpi/js/charts/worstSuppliersChart.js @@ -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 = ` + + + + + + + + + + + ${worstSuppliers + .map( + (item) => ` + + + + + + + `, + ) + .join("")} + +
SupplierFail PercentageTotal ReportsFailed Reports
${item.supplier}${item.failPercentage.toFixed(2)}%${item.totalReports}${item.failedReports}
+ `; + + const tableContainer = document.querySelector(`#${tableContainerId}`); + if (tableContainer) { + tableContainer.innerHTML = tableHTML; + } else { + console.error( + `Contenitore della tabella non trovato: #${tableContainerId}`, + ); + } +} diff --git a/public/userarea/statkpi/js/charts/worsttenanalysis.js b/public/userarea/statkpi/js/charts/worsttenanalysis.js new file mode 100644 index 0000000..d707d25 --- /dev/null +++ b/public/userarea/statkpi/js/charts/worsttenanalysis.js @@ -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 = + '

No failed tests to display.

'; + const tableContainer = document.querySelector(`#${tableContainerId}`); + if (tableContainer) { + tableContainer.innerHTML = + '

No failed tests to display.

'; + } + 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 = + '

No failed tests to display.

'; + const tableContainer = document.querySelector(`#${tableContainerId}`); + if (tableContainer) { + tableContainer.innerHTML = + '

No failed tests to display.

'; + } + 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 = ` + + + + + + + + + ${analysisData + .map( + (item) => ` + + + + + `, + ) + .join("")} + +
AnalysisFailed Tests
${item.name || "Unknown"}${parseInt(item.failCount, 10) || 0}
+ `; + + // 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); +} diff --git a/public/userarea/statkpi/js/main.js b/public/userarea/statkpi/js/main.js new file mode 100644 index 0000000..58b4fc0 --- /dev/null +++ b/public/userarea/statkpi/js/main.js @@ -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 +}); diff --git a/public/userarea/statkpi/js/utils/ajaxUtils.js b/public/userarea/statkpi/js/utils/ajaxUtils.js new file mode 100644 index 0000000..caedaf2 --- /dev/null +++ b/public/userarea/statkpi/js/utils/ajaxUtils.js @@ -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; + }); +} diff --git a/public/userarea/statkpi/js/utils/tableGenerator.js b/public/userarea/statkpi/js/utils/tableGenerator.js new file mode 100644 index 0000000..f792073 --- /dev/null +++ b/public/userarea/statkpi/js/utils/tableGenerator.js @@ -0,0 +1,24 @@ +export function generatePieTable(labels, series) { + let tableHTML = ` + + + + + + + + + ${labels + .map( + (label, i) => ` + + + + + `, + ) + .join("")} + +
CategoryCount
${label}${series[i]}
`; + return tableHTML; +} diff --git a/public/userarea/statkpi/jsinclude-statkpi.php b/public/userarea/statkpi/jsinclude-statkpi.php new file mode 100644 index 0000000..cfe5c67 --- /dev/null +++ b/public/userarea/statkpi/jsinclude-statkpi.php @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/userarea/statkpi/statkpi.php b/public/userarea/statkpi/statkpi.php index 40fb34d..085bc73 100644 --- a/public/userarea/statkpi/statkpi.php +++ b/public/userarea/statkpi/statkpi.php @@ -7,274 +7,49 @@ include('parsedatachart.php'); - + - - + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - -
-
-
-
-
- - - - - - @@ -566,11 +341,13 @@ include('parsedatachart.php'); -
+
-
Reports Breakdown
+
Reports Bar Chart
+ +
@@ -693,13 +470,18 @@ include('parsedatachart.php');
- +
-
Report Rating Distribution
+
Report Rating Distribution
+ + -
Analysis Rating Distribution
+ +
Analysis Rating Distribution
+ + @@ -729,7 +511,7 @@ include('parsedatachart.php'); - + @@ -753,1224 +535,14 @@ include('parsedatachart.php'); - - - - - + - - - - - - - - - - - - - - - - - - + diff --git a/resources/views/auth/social/buttons.blade.php b/resources/views/auth/social/buttons.blade.php index 0ad1fdb..5973ccd 100644 --- a/resources/views/auth/social/buttons.blade.php +++ b/resources/views/auth/social/buttons.blade.php @@ -1,30 +1,38 @@ @if ($socialProviders) - + -
- @if (in_array('facebook', $socialProviders)) -
- - - -
- @endif - - @if (in_array('twitter', $socialProviders)) -
- - - -
- @endif - - @if (in_array('google', $socialProviders)) -
- - - -
- @endif +
+ @if (in_array('facebook', $socialProviders)) + + @endif + + @if (in_array('twitter', $socialProviders)) +
+ + + +
+ @endif + + @if (in_array('google', $socialProviders)) +
+ + + +
+ @endif + + @if (in_array('azure', $socialProviders)) +
+ + + +
+ @endif +
@endif \ No newline at end of file