diff --git a/app/Http/Controllers/Web/Auth/LoginController.php b/app/Http/Controllers/Web/Auth/LoginController.php index 5cfd769..7f60e66 100644 --- a/app/Http/Controllers/Web/Auth/LoginController.php +++ b/app/Http/Controllers/Web/Auth/LoginController.php @@ -111,6 +111,14 @@ class LoginController extends Controller return redirect()->to('userarea/production_dashboard.php'); } elseif ($user->hasRole('User')) { return redirect()->to('userarea/production_dashboard.php'); + } elseif ($user->hasRole('HR')) { + return redirect()->to('userarea/production_dashboard.php'); + } elseif ($user->hasRole('SuperUser')) { + return redirect()->to('userarea/production_dashboard.php'); + } elseif ($user->hasRole('Management')) { + return redirect()->to('userarea/production_dashboard.php'); + } elseif ($user->hasRole('Quality')) { + return redirect()->to('userarea/production_dashboard.php'); } // Se il ruolo non è specificato, reindirizza alla home predefinita diff --git a/public/userarea/include/topbar.php b/public/userarea/include/topbar.php index 4d54919..735cf14 100644 --- a/public/userarea/include/topbar.php +++ b/public/userarea/include/topbar.php @@ -100,7 +100,7 @@
  • - + Utente
  • @@ -117,4 +117,4 @@ - \ No newline at end of file + diff --git a/public/userarea/production_dashboard.php b/public/userarea/production_dashboard.php index 08ccc5c..ce821aa 100644 --- a/public/userarea/production_dashboard.php +++ b/public/userarea/production_dashboard.php @@ -51,7 +51,7 @@ $dashboardSections = [ 'permission' => 'warehouse.dashboard.view', ], [ - 'label' => 'Scadenziario', + 'label' => 'Smart-Alert', 'icon' => '⏰', 'class' => 'btn-scadenziario', 'url' => 'scadenzario/index.php', @@ -187,7 +187,7 @@ $dashboardSections = [ - Dashboard Produzione - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?> + Dashboard <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?> @@ -539,7 +539,7 @@ $dashboardSections = [ -

    Dashboard Produzione

    +

    Dashboard

    diff --git a/public/userarea/scadenzario/include/my_deadlines_widget.php b/public/userarea/scadenzario/include/my_deadlines_widget.php index f1756a3..8bc8e42 100644 --- a/public/userarea/scadenzario/include/my_deadlines_widget.php +++ b/public/userarea/scadenzario/include/my_deadlines_widget.php @@ -1,4 +1,5 @@ overdue deadlines (scaduta) @@ -44,63 +45,90 @@ if (!$_emp || ($_overdue === 0 && $_approaching === 0)) { ?>
    - 0): ?> - - - - - Scadenz scadut - - - - - 0): ?> - - - - - In scadenza a breve — - - - - + 0): ?> + + + + + Task scadut + + + + + 0): ?> + + + + + In scadenza a breve — + + + +
    diff --git a/public/userarea/scadenzario/index.php b/public/userarea/scadenzario/index.php index 51414b8..c273b6b 100644 --- a/public/userarea/scadenzario/index.php +++ b/public/userarea/scadenzario/index.php @@ -1115,15 +1115,15 @@ function getContrastTextColor($hexColor)
    - +
    - +
    - +
    @@ -1221,6 +1221,33 @@ function getContrastTextColor($hexColor) var fpDue = flatpickr('#filterDueRange', fpOpts); var fpCheck = flatpickr('#filterCheckRange', fpOpts); + // --- Flatpickr Italian date fields in deadline modal --- + // Visible format: dd/mm/yyyy + // Submitted format: yyyy-mm-dd, compatible with MySQL DATE + var fpDocDate = flatpickr('#dlDocDate', { + dateFormat: 'Y-m-d', + altInput: true, + altFormat: 'd/m/Y', + locale: 'it', + allowInput: true + }); + + var fpDueDate = flatpickr('#dlDueDate', { + dateFormat: 'Y-m-d', + altInput: true, + altFormat: 'd/m/Y', + locale: 'it', + allowInput: true + }); + + var fpCheckDate = flatpickr('#dlCheckDate', { + dateFormat: 'Y-m-d', + altInput: true, + altFormat: 'd/m/Y', + locale: 'it', + allowInput: true + }); + // --- Select2 --- $('#dlSubject').select2({ theme: 'bootstrap-5', @@ -1295,7 +1322,7 @@ function getContrastTextColor($hexColor) var iso = d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0'); - document.getElementById('dlDueDate').value = iso; + fpDueDate.setDate(iso, true, 'Y-m-d'); } $('#dlDocDate, #dlRecurrence').on('change', computeDueDate); @@ -1464,16 +1491,24 @@ function getContrastTextColor($hexColor) var modal = new bootstrap.Modal(document.getElementById('deadlineModal')); var form = document.getElementById('deadlineForm'); + // Add // Add document.getElementById('btnAddDeadline').addEventListener('click', function() { form.reset(); + document.getElementById('dlId').value = ''; document.getElementById('dlNotifDays').value = '7'; document.getElementById('modalTitle').textContent = 'Nuova Scadenza'; document.getElementById('dlFiles').value = ''; + + fpDocDate.clear(); + fpDueDate.clear(); + fpCheckDate.clear(); + $('#dlSubject').val('').trigger('change'); $('#dlDepartments').val(null).trigger('change'); $('#dlEmployees').val(null).trigger('change'); + renderAttachments([]); modal.show(); }); @@ -1639,9 +1674,9 @@ function getContrastTextColor($hexColor) document.getElementById('dlTopic').value = d.topic || ''; document.getElementById('dlLaw').value = d.law_regulation || ''; document.getElementById('dlRecurrence').value = d.recurrence_type || 'once'; - document.getElementById('dlDocDate').value = d.document_date || ''; - document.getElementById('dlDueDate').value = d.due_date || ''; - document.getElementById('dlCheckDate').value = d.check_date || ''; + fpDocDate.setDate(d.document_date || null, false, 'Y-m-d'); + fpDueDate.setDate(d.due_date || null, false, 'Y-m-d'); + fpCheckDate.setDate(d.check_date || null, false, 'Y-m-d'); document.getElementById('dlNotifDays').value = d.notification_days || 7; document.getElementById('dlStorage').value = d.storage_location || ''; document.getElementById('dlNotes').value = d.notes || ''; @@ -1765,9 +1800,9 @@ function getContrastTextColor($hexColor) document.getElementById('dlTopic').value = d.topic || ''; document.getElementById('dlLaw').value = d.law_regulation || ''; document.getElementById('dlRecurrence').value = d.recurrence_type || 'once'; - document.getElementById('dlDocDate').value = d.document_date || ''; - document.getElementById('dlDueDate').value = d.due_date || ''; - document.getElementById('dlCheckDate').value = d.check_date || ''; + fpDocDate.setDate(d.document_date || null, false, 'Y-m-d'); + fpDueDate.setDate(d.due_date || null, false, 'Y-m-d'); + fpCheckDate.setDate(d.check_date || null, false, 'Y-m-d'); document.getElementById('dlNotifDays').value = d.notification_days || 7; document.getElementById('dlStorage').value = d.storage_location || ''; document.getElementById('dlNotes').value = d.notes || ''; diff --git a/public/userarea/user_settings.php b/public/userarea/user_settings.php new file mode 100644 index 0000000..d4909b9 --- /dev/null +++ b/public/userarea/user_settings.php @@ -0,0 +1,868 @@ + +getConnection(); + +$userId = (int)($iduserlogin ?? 0); + +if ($userId <= 0) { + die('Utente non valido.'); +} + +if (session_status() === PHP_SESSION_NONE) { + session_start(); +} + +if (empty($_SESSION['user_settings_csrf'])) { + $_SESSION['user_settings_csrf'] = bin2hex(random_bytes(32)); +} + +$csrfToken = $_SESSION['user_settings_csrf']; + +$successMessage = ''; +$errorMessage = ''; + +// Load countries. +$countries = []; +try { + $stmtCountries = $pdo->query(" + SELECT id, name, iso_3166_2 + FROM auth_countries + ORDER BY name ASC + "); + $countries = $stmtCountries->fetchAll(PDO::FETCH_ASSOC); +} catch (Exception $e) { + $countries = []; +} + +// Load current user. +$stmtProfileUser = $pdo->prepare(" + SELECT + id, + email, + password, + first_name, + last_name, + phone, + avatar, + address, + country_id, + birthday, + role_id, + status, + last_login + FROM auth_users + WHERE id = ? + LIMIT 1 +"); +$stmtProfileUser->execute([$userId]); +$profileUser = $stmtProfileUser->fetch(PDO::FETCH_ASSOC); + +if (!$profileUser) { + die('Utente non trovato.'); +} + +function e($value) +{ + return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); +} + +function normalizeAvatarPath($avatar) +{ + $avatar = trim((string)$avatar); + + if ($avatar === '') { + return ''; + } + + // If the database already contains a complete relative path, use it as it is. + if ( + str_starts_with($avatar, '../') || + str_starts_with($avatar, './') || + str_starts_with($avatar, '/') || + str_starts_with($avatar, 'http://') || + str_starts_with($avatar, 'https://') + ) { + return $avatar; + } + + // If the database contains only the filename, build the expected user upload path. + return '../upload/users/' . $avatar; +} + +function getAvatarInitials($profileUser) +{ + $first = trim((string)($profileUser['first_name'] ?? '')); + $last = trim((string)($profileUser['last_name'] ?? '')); + $email = trim((string)($profileUser['email'] ?? '')); + + $initials = ''; + + if ($first !== '') { + $initials .= mb_substr($first, 0, 1); + } + + if ($last !== '') { + $initials .= mb_substr($last, 0, 1); + } + + if ($initials === '' && $email !== '') { + $initials = mb_substr($email, 0, 1); + } + + return strtoupper($initials ?: 'U'); +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $postedToken = $_POST['csrf_token'] ?? ''; + + if (!hash_equals($csrfToken, $postedToken)) { + $errorMessage = 'Sessione non valida. Ricarica la pagina e riprova.'; + } else { + $email = trim($_POST['email'] ?? ''); + $firstName = trim($_POST['first_name'] ?? ''); + $lastName = trim($_POST['last_name'] ?? ''); + $phone = trim($_POST['phone'] ?? ''); + $address = trim($_POST['address'] ?? ''); + $countryId = $_POST['country_id'] !== '' ? (int)$_POST['country_id'] : null; + $birthday = trim($_POST['birthday'] ?? ''); + + $currentPassword = $_POST['current_password'] ?? ''; + $newPassword = $_POST['new_password'] ?? ''; + $confirmPassword = $_POST['confirm_password'] ?? ''; + + $birthdayValue = null; + $avatarToSave = $profileUser['avatar']; + + if ($birthday !== '') { + $dateObj = DateTime::createFromFormat('Y-m-d', $birthday); + + if (!$dateObj || $dateObj->format('Y-m-d') !== $birthday) { + $errorMessage = 'La data di nascita non è valida.'; + } else { + $birthdayValue = $birthday; + } + } + + if (!$errorMessage && $email === '') { + $errorMessage = 'L’email è obbligatoria.'; + } + + if (!$errorMessage && !filter_var($email, FILTER_VALIDATE_EMAIL)) { + $errorMessage = 'L’email inserita non è valida.'; + } + + // Check unique email. + if (!$errorMessage) { + $stmtCheckEmail = $pdo->prepare(" + SELECT id + FROM auth_users + WHERE email = ? AND id <> ? + LIMIT 1 + "); + $stmtCheckEmail->execute([$email, $userId]); + + if ($stmtCheckEmail->fetchColumn()) { + $errorMessage = 'Questa email è già utilizzata da un altro utente.'; + } + } + + // Avatar upload. + if (!$errorMessage && isset($_FILES['avatar']) && $_FILES['avatar']['error'] !== UPLOAD_ERR_NO_FILE) { + if ($_FILES['avatar']['error'] !== UPLOAD_ERR_OK) { + $errorMessage = 'Errore durante il caricamento dell’avatar.'; + } else { + $maxFileSize = 2 * 1024 * 1024; // 2 MB + + if ($_FILES['avatar']['size'] > $maxFileSize) { + $errorMessage = 'L’avatar non può superare 2 MB.'; + } else { + $tmpFile = $_FILES['avatar']['tmp_name']; + $originalName = $_FILES['avatar']['name']; + + $allowedMimeTypes = [ + 'image/jpeg' => 'jpg', + 'image/png' => 'png', + 'image/webp' => 'webp', + 'image/gif' => 'gif', + ]; + + $finfo = new finfo(FILEINFO_MIME_TYPE); + $mimeType = $finfo->file($tmpFile); + + if (!array_key_exists($mimeType, $allowedMimeTypes)) { + $errorMessage = 'Formato avatar non valido. Sono consentiti JPG, PNG, WEBP o GIF.'; + } else { + $uploadDir = __DIR__ . '/../upload/users/'; + + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0755, true); + } + + $extension = $allowedMimeTypes[$mimeType]; + $safeOriginalName = preg_replace('/[^A-Za-z0-9_\-\.]/', '_', pathinfo($originalName, PATHINFO_FILENAME)); + $fileName = time() . '_' . $userId . '_' . $safeOriginalName . '.' . $extension; + + $destination = $uploadDir . $fileName; + + if (!move_uploaded_file($tmpFile, $destination)) { + $errorMessage = 'Impossibile salvare il file avatar.'; + } else { + // Path used by pages inside userarea, for example: + // + $avatarToSave = $fileName; + } + } + } + } + } + + $passwordToSave = null; + $wantsPasswordChange = ($currentPassword !== '' || $newPassword !== '' || $confirmPassword !== ''); + + if (!$errorMessage && $wantsPasswordChange) { + if ($currentPassword === '') { + $errorMessage = 'Inserisci la password attuale.'; + } elseif ($newPassword === '') { + $errorMessage = 'Inserisci la nuova password.'; + } elseif (strlen($newPassword) < 8) { + $errorMessage = 'La nuova password deve contenere almeno 8 caratteri.'; + } elseif ($newPassword !== $confirmPassword) { + $errorMessage = 'La conferma password non corrisponde.'; + } elseif (!password_verify($currentPassword, $profileUser['password'])) { + $errorMessage = 'La password attuale non è corretta.'; + } else { + // Password is encrypted before saving. + $passwordToSave = password_hash($newPassword, PASSWORD_DEFAULT); + } + } + + if (!$errorMessage) { + try { + $pdo->beginTransaction(); + + $stmtUpdate = $pdo->prepare(" + UPDATE auth_users + SET + email = :email, + first_name = :first_name, + last_name = :last_name, + phone = :phone, + avatar = :avatar, + address = :address, + country_id = :country_id, + birthday = :birthday, + updated_at = NOW() + WHERE id = :id + LIMIT 1 + "); + + $stmtUpdate->execute([ + ':email' => $email, + ':first_name' => $firstName !== '' ? $firstName : null, + ':last_name' => $lastName !== '' ? $lastName : null, + ':phone' => $phone !== '' ? $phone : null, + ':avatar' => $avatarToSave !== '' ? $avatarToSave : null, + ':address' => $address !== '' ? $address : null, + ':country_id' => $countryId, + ':birthday' => $birthdayValue, + ':id' => $userId, + ]); + + if ($passwordToSave !== null) { + $stmtPassword = $pdo->prepare(" + UPDATE auth_users + SET password = ?, updated_at = NOW() + WHERE id = ? + LIMIT 1 + "); + $stmtPassword->execute([$passwordToSave, $userId]); + } + + $pdo->commit(); + + $successMessage = $passwordToSave !== null + ? 'Profilo, avatar e password aggiornati correttamente.' + : 'Profilo aggiornato correttamente.'; + + // Reload updated user. + $stmtProfileUser->execute([$userId]); + $profileUser = $stmtProfileUser->fetch(PDO::FETCH_ASSOC); + + $_SESSION['user_settings_csrf'] = bin2hex(random_bytes(32)); + $csrfToken = $_SESSION['user_settings_csrf']; + } catch (Exception $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + + $errorMessage = 'Errore durante il salvataggio delle impostazioni.'; + } + } + } +} + +$avatarPath = normalizeAvatarPath($profileUser['avatar'] ?? ''); +?> + + + + + + + + + Impostazioni Utente <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?> + + + + + +
    + + + +
    +
    + +
    +

    Impostazioni Utente

    + + +
    + +
    + + + +
    + +
    + + +
    + + +
    +
    +
    👤
    +
    +

    Profilo personale

    +

    Dati anagrafici, contatti e avatar utente

    +
    +
    + +
    + +
    + Ruolo, stato account e impostazioni di sicurezza avanzate non sono modificabili da questa pagina. +
    + +
    +
    +
    + + user avatar + + + +
    + +

    + +

    + +

    + +

    + + + + + +
    + +
    + Formati consentiti: JPG, PNG, WEBP, GIF.
    + Dimensione massima: 2 MB. +
    + +
    + Stato account: + +
    Ultimo accesso: + +
    +
    + +
    +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    🔐
    +
    +

    Cambio password

    +

    Compila questa sezione solo se vuoi modificare la password

    +
    +
    + +
    +
    +
    +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + +
    + Se lasci questi campi vuoti, la password attuale rimane invariata. + La nuova password deve avere almeno 8 caratteri. +
    +
    + +
    + ← Torna alla dashboard + +
    +
    +
    +
    +
    + +
    +
    + + + +
    + + + + + \ No newline at end of file