From 9001eff3174d8ad2d1574fa9c9688fad75265591 Mon Sep 17 00:00:00 2001 From: "r.mubarakzyanov" Date: Thu, 21 May 2026 23:31:36 +0300 Subject: [PATCH 1/3] file repo, cc, auto-open --- .env.example | 6 +- .../scadenzario/ajax/complete_deadline.php | 48 ++++++- .../scadenzario/ajax/delete_attachment.php | 34 +++-- .../scadenzario/ajax/delete_deadline.php | 19 +++ .../scadenzario/cron/send_notifications.php | 16 +++ public/userarea/scadenzario/detail.php | 106 +++++++++++---- public/userarea/scadenzario/index.php | 124 ++++++++++++++---- 7 files changed, 279 insertions(+), 74 deletions(-) diff --git a/.env.example b/.env.example index 42476a2..fefcfa5 100644 --- a/.env.example +++ b/.env.example @@ -31,6 +31,8 @@ MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null +MANAGER_USER_ID= + PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= @@ -55,5 +57,5 @@ AZURE_REDIRECT_URI=https://your-app.com/auth/azure/callback AZURE_TENANT_ID= MICROSOFT_CLIENT_ID=your_client_id_here -MICROSOFT_CLIENT_SECRET=your_client_secret_here -MICROSOFT_REDIRECT_URI="${APP_URL}/auth/microsoft/callback" \ No newline at end of file +MICROSOFT_CLIENT_SECRET=your_client_secret_here +MICROSOFT_REDIRECT_URI="${APP_URL}/auth/microsoft/callback" diff --git a/public/userarea/scadenzario/ajax/complete_deadline.php b/public/userarea/scadenzario/ajax/complete_deadline.php index 7bfc4c8..1f6e9fe 100644 --- a/public/userarea/scadenzario/ajax/complete_deadline.php +++ b/public/userarea/scadenzario/ajax/complete_deadline.php @@ -4,12 +4,19 @@ header('Content-Type: application/json'); require_once(__DIR__ . '/../../class/db-functions.php'); try { - if (!isset($_GET['id']) || !is_numeric($_GET['id'])) { + $rawId = $_POST['id'] ?? $_GET['id'] ?? null; + if ($rawId === null || !is_numeric($rawId)) { echo json_encode(['success' => false, 'message' => 'ID non valido.']); exit; } - $id = (int)$_GET['id']; + $id = (int)$rawId; + + // Whether to create the next (recurring) deadline. Absent or '1' => create; '0' => complete only. + $createNext = ($_POST['create_next'] ?? '1') !== '0'; + + // Whether to carry the attachment links over to the new deadline. Default ON ("default all activate"). + $copyAttachments = ($_POST['copy_attachments'] ?? '1') !== '0'; $db = DBHandlerSelect::getInstance(); $pdo = $db->getConnection(); @@ -34,11 +41,13 @@ try { ->execute([$id, $currentUserId]); $newId = null; + $newDueDate = null; - // If recurring, create next deadline - if ($deadline['recurrence_type'] !== 'once') { + // If recurring AND the user asked for it, create the next deadline + if ($deadline['recurrence_type'] !== 'once' && $createNext) { $dueDate = new DateTime($deadline['due_date']); $checkDate = $deadline['check_date'] ? new DateTime($deadline['check_date']) : null; + $documentDate = $deadline['document_date'] ? new DateTime($deadline['document_date']) : null; switch ($deadline['recurrence_type']) { case 'monthly': $interval = new DateInterval('P1M'); break; @@ -57,6 +66,7 @@ try { if ($interval) { $dueDate->add($interval); if ($checkDate) $checkDate->add($interval); + if ($documentDate) $documentDate->add($interval); $ins = $pdo->prepare(" INSERT INTO scad_deadlines @@ -68,12 +78,13 @@ try { $deadline['subject_id'], $deadline['topic'], $deadline['law_regulation'], $deadline['recurrence_type'], $dueDate->format('Y-m-d'), $checkDate ? $checkDate->format('Y-m-d') : null, - $deadline['document_date'], + $documentDate ? $documentDate->format('Y-m-d') : null, $deadline['notification_days'], $deadline['storage_location'], $deadline['notes'], $deadline['created_by'], $deadline['departments'] ]); $newId = $pdo->lastInsertId(); + $newDueDate = $dueDate; // Copy employee assignments $empStmt = $pdo->prepare("SELECT employee_id FROM scad_deadline_employee WHERE deadline_id = ?"); @@ -87,6 +98,31 @@ try { } } + // Carry forward ALL attachment links from the source deadline (shared physical file, same stored_name). + // Individual links can later be removed on the new deadline without deleting the file. + if ($copyAttachments) { + $attSel = $pdo->prepare(" + SELECT original_name, stored_name, mime_type, size + FROM scad_deadline_attachments + WHERE deadline_id = ? + "); + $attSel->execute([$id]); + $attRows = $attSel->fetchAll(PDO::FETCH_ASSOC); + + if ($attRows) { + $attIns = $pdo->prepare(" + INSERT INTO scad_deadline_attachments + (deadline_id, original_name, stored_name, mime_type, size, uploaded_by) + VALUES (?, ?, ?, ?, ?, ?) + "); + $attHist = $pdo->prepare("INSERT INTO scad_deadline_histories (deadline_id, user_id, action, notes) VALUES (?, ?, 'attachment_linked', ?)"); + foreach ($attRows as $a) { + $attIns->execute([$newId, $a['original_name'], $a['stored_name'], $a['mime_type'], $a['size'], $currentUserId]); + $attHist->execute([$newId, $currentUserId, $a['original_name']]); + } + } + } + // History for new $pdo->prepare("INSERT INTO scad_deadline_histories (deadline_id, user_id, action, notes) VALUES (?, ?, 'created', ?)") ->execute([$newId, $currentUserId, 'Creata automaticamente dalla scadenza #' . $id]); @@ -97,7 +133,7 @@ try { $msg = 'Scadenza completata con successo.'; if ($newId) { - $msg .= ' Nuova scadenza creata con data ' . $dueDate->format('d/m/Y') . '.'; + $msg .= ' Nuova scadenza creata con data ' . $newDueDate->format('d/m/Y') . '.'; } echo json_encode(['success' => true, 'message' => $msg, 'new_id' => $newId]); diff --git a/public/userarea/scadenzario/ajax/delete_attachment.php b/public/userarea/scadenzario/ajax/delete_attachment.php index ba9d2d0..da4df5d 100644 --- a/public/userarea/scadenzario/ajax/delete_attachment.php +++ b/public/userarea/scadenzario/ajax/delete_attachment.php @@ -23,20 +23,32 @@ try { exit; } - // Delete file - $filePath = __DIR__ . '/../attachments/' . $att['stored_name']; - if (file_exists($filePath)) { - unlink($filePath); - } - - // Delete DB record + // Remove this link (DB record) first $pdo->prepare("DELETE FROM scad_deadline_attachments WHERE id = ?")->execute([$id]); - // History - $pdo->prepare("INSERT INTO scad_deadline_histories (deadline_id, user_id, action, notes) VALUES (?, ?, 'attachment_removed', ?)") - ->execute([$att['deadline_id'], $currentUserId, $att['original_name']]); + // The same physical file may be shared with other deadlines (carried forward on completion). + // Only unlink it when no other link references the same stored file. + $refStmt = $pdo->prepare("SELECT COUNT(*) FROM scad_deadline_attachments WHERE stored_name = ?"); + $refStmt->execute([$att['stored_name']]); + $stillReferenced = (int)$refStmt->fetchColumn() > 0; - echo json_encode(['success' => true, 'message' => 'Allegato eliminato.']); + if ($stillReferenced) { + $action = 'attachment_unlinked'; + $message = 'Collegamento rimosso. Il file è conservato (usato da un\'altra scadenza).'; + } else { + $filePath = __DIR__ . '/../attachments/' . $att['stored_name']; + if (file_exists($filePath)) { + unlink($filePath); + } + $action = 'attachment_removed'; + $message = 'Allegato eliminato.'; + } + + // History + $pdo->prepare("INSERT INTO scad_deadline_histories (deadline_id, user_id, action, notes) VALUES (?, ?, ?, ?)") + ->execute([$att['deadline_id'], $currentUserId, $action, $att['original_name']]); + + echo json_encode(['success' => true, 'message' => $message]); } catch (Exception $e) { echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]); diff --git a/public/userarea/scadenzario/ajax/delete_deadline.php b/public/userarea/scadenzario/ajax/delete_deadline.php index d81b041..98de3d2 100644 --- a/public/userarea/scadenzario/ajax/delete_deadline.php +++ b/public/userarea/scadenzario/ajax/delete_deadline.php @@ -13,10 +13,29 @@ try { $db = DBHandlerSelect::getInstance(); $pdo = $db->getConnection(); + // Collect the physical files referenced by this deadline before the FK cascade removes its links + $attStmt = $pdo->prepare("SELECT DISTINCT stored_name FROM scad_deadline_attachments WHERE deadline_id = ?"); + $attStmt->execute([$id]); + $storedNames = $attStmt->fetchAll(PDO::FETCH_COLUMN); + + // Deleting the deadline cascades to its attachment/employee/history rows (FK ON DELETE CASCADE) $stmt = $pdo->prepare("DELETE FROM scad_deadlines WHERE id = ?"); $stmt->execute([$id]); if ($stmt->rowCount() > 0) { + // Unlink physical files no longer referenced by any other deadline (shared-file safe) + if (!empty($storedNames)) { + $refStmt = $pdo->prepare("SELECT COUNT(*) FROM scad_deadline_attachments WHERE stored_name = ?"); + foreach ($storedNames as $storedName) { + $refStmt->execute([$storedName]); + if ((int)$refStmt->fetchColumn() === 0) { + $filePath = __DIR__ . '/../attachments/' . $storedName; + if (file_exists($filePath)) { + unlink($filePath); + } + } + } + } echo json_encode(['success' => true, 'message' => 'Scadenza eliminata con successo.']); } else { echo json_encode(['success' => false, 'message' => 'Scadenza non trovata.']); diff --git a/public/userarea/scadenzario/cron/send_notifications.php b/public/userarea/scadenzario/cron/send_notifications.php index a8930b7..051e286 100644 --- a/public/userarea/scadenzario/cron/send_notifications.php +++ b/public/userarea/scadenzario/cron/send_notifications.php @@ -25,6 +25,17 @@ $pdo = $db->getConnection(); $today = date('Y-m-d'); $appUrl = rtrim($_ENV['APP_URL'] ?? 'http://localhost:8001', '/'); +// Manager email for Cc — taken from MANAGER_USER_ID → auth_users.email +$managerCcEmail = null; +if (!empty($_ENV['MANAGER_USER_ID']) && is_numeric($_ENV['MANAGER_USER_ID'])) { + $mgrStmt = $pdo->prepare("SELECT email FROM auth_users WHERE id = ?"); + $mgrStmt->execute([(int)$_ENV['MANAGER_USER_ID']]); + $mgrEmail = $mgrStmt->fetchColumn(); + if (!empty($mgrEmail)) { + $managerCcEmail = $mgrEmail; + } +} + $sent = 0; $skipped = 0; $errors = 0; @@ -143,6 +154,11 @@ foreach ($deadlines as $dl) { ); $mail->addAddress($emp['email'], trim($emp['first_name'] . ' ' . $emp['last_name'])); + // Cc the manager (unless they are the direct recipient) + if ($managerCcEmail && strcasecmp($managerCcEmail, $emp['email']) !== 0) { + $mail->addCC($managerCcEmail); + } + $detailUrl = $appUrl . '/userarea/scadenzario/detail.php?id=' . $dl['id']; $topicText = (!empty($dl['subject_name']) ? $dl['subject_name'] . ' — ' : '') . $dl['topic']; diff --git a/public/userarea/scadenzario/detail.php b/public/userarea/scadenzario/detail.php index c6bda13..78c55ea 100644 --- a/public/userarea/scadenzario/detail.php +++ b/public/userarea/scadenzario/detail.php @@ -66,9 +66,9 @@ if (!isset($_GET['id']) || !is_numeric($_GET['id'])) { } $recurrenceLabels = ['once' => 'Una tantum', 'monthly' => 'Mensile', 'quarterly' => 'Trimestrale', 'semiannual' => 'Semestrale', 'annual' => 'Annuale', 'biennial' => 'Biennale', 'triennial' => 'Triennale', 'quadriennial' => 'Quadriennale', 'quinquennial' => 'Quinquennale', 'decennial' => 'Decennale', 'quindecennial' => 'Quindicennale']; - $actionLabels = ['created' => 'Creata', 'updated' => 'Modificata', 'completed' => 'Completata', 'attachment_added' => 'Allegato aggiunto', 'attachment_removed' => 'Allegato rimosso', 'notification_sent' => 'Notifica inviata']; - $actionColors = ['created' => '#198754', 'updated' => '#5a8fd8', 'completed' => '#6f42c1', 'attachment_added' => '#e8930c', 'attachment_removed' => '#e8930c', 'notification_sent' => '#adb5bd']; - $actionIcons = ['created' => 'fa-plus', 'updated' => 'fa-pen', 'completed' => 'fa-check', 'attachment_added' => 'fa-paperclip', 'attachment_removed' => 'fa-trash', 'notification_sent' => 'fa-bell']; + $actionLabels = ['created' => 'Creata', 'updated' => 'Modificata', 'completed' => 'Completata', 'attachment_added' => 'Allegato aggiunto', 'attachment_removed' => 'Allegato rimosso', 'attachment_linked' => 'Allegato collegato', 'attachment_unlinked' => 'Collegamento rimosso', 'notification_sent' => 'Notifica inviata']; + $actionColors = ['created' => '#198754', 'updated' => '#5a8fd8', 'completed' => '#6f42c1', 'attachment_added' => '#e8930c', 'attachment_removed' => '#e8930c', 'attachment_linked' => '#0dcaf0', 'attachment_unlinked' => '#adb5bd', 'notification_sent' => '#adb5bd']; + $actionIcons = ['created' => 'fa-plus', 'updated' => 'fa-pen', 'completed' => 'fa-check', 'attachment_added' => 'fa-paperclip', 'attachment_removed' => 'fa-trash', 'attachment_linked' => 'fa-link', 'attachment_unlinked' => 'fa-link-slash', 'notification_sent' => 'fa-bell']; } } ?> @@ -763,39 +763,91 @@ if (!isset($_GET['id']) || !is_numeric($_GET['id'])) { window.location.href = 'scadenzario/index.php?edit='; }); + function detailSubmitComplete(createNext, copyAttachments) { + var fd = new FormData(); + fd.append('id', ''); + fd.append('create_next', createNext ? '1' : '0'); + fd.append('copy_attachments', copyAttachments ? '1' : '0'); + + fetch('scadenzario/ajax/complete_deadline.php', { + method: 'POST', + body: fd + }) + .then(function(r) { + return r.json(); + }) + .then(function(data) { + if (data.success) { + Swal.fire({ + icon: 'success', + title: 'Completata', + text: data.message, + timer: 1800, + showConfirmButton: false + }) + .then(function() { + if (data.new_id) { + window.location.href = 'scadenzario/index.php?edit=' + data.new_id; + } else { + window.location.href = 'scadenzario/index.php'; + } + }); + } else { + Swal.fire('Errore', data.message, 'error'); + } + }) + .catch(function() { + Swal.fire('Errore', 'Errore di connessione.', 'error'); + }); + } + $('#btnCompleta').on('click', function() { + var recurrence = ; + var attCount = ; + + if (recurrence === 'once') { + Swal.fire({ + title: 'Completare la scadenza?', + text: 'La scadenza verrà contrassegnata come completata.', + icon: 'question', + showCancelButton: true, + confirmButtonColor: '#198754', + cancelButtonText: 'Annulla', + confirmButtonText: 'Completa', + reverseButtons: true + }).then(function(result) { + if (result.isConfirmed) { + detailSubmitComplete(false, false); + } + }); + return; + } + + var attCheckbox = attCount > 0 ? + '
' + + '' + + '' + + '
' : + ''; + Swal.fire({ title: 'Completare la scadenza?', + html: 'Vuoi creare automaticamente la prossima scadenza ricorrente?' + attCheckbox, icon: 'question', showCancelButton: true, + showDenyButton: true, confirmButtonColor: '#198754', + denyButtonColor: '#6c757d', + confirmButtonText: 'Completa e crea la prossima', + denyButtonText: 'Completa senza nuova', cancelButtonText: 'Annulla', - confirmButtonText: 'Completa' + reverseButtons: true }).then(function(result) { if (result.isConfirmed) { - fetch('scadenzario/ajax/complete_deadline.php?id=') - .then(function(r) { - return r.json(); - }) - .then(function(data) { - if (data.success) { - Swal.fire({ - icon: 'success', - title: 'Completata', - text: data.message, - timer: 2500, - showConfirmButton: false - }) - .then(function() { - window.location.href = 'scadenzario/index.php'; - }); - } else { - Swal.fire('Errore', data.message, 'error'); - } - }) - .catch(function() { - Swal.fire('Errore', 'Errore di connessione.', 'error'); - }); + var copy = attCount > 0 ? document.getElementById('swCopyAtt').checked : false; + detailSubmitComplete(true, copy); + } else if (result.isDenied) { + detailSubmitComplete(false, false); } }); }); diff --git a/public/userarea/scadenzario/index.php b/public/userarea/scadenzario/index.php index c273b6b..8fe672f 100644 --- a/public/userarea/scadenzario/index.php +++ b/public/userarea/scadenzario/index.php @@ -923,7 +923,9 @@ function getContrastTextColor($hexColor) data-department="" data-employees="" data-due-date="" - data-check-date=""> + data-check-date="" + data-recurrence="" + data-att-count="">
" data-employees="" data-due-date="" - data-check-date=""> + data-check-date="" + data-recurrence="" + data-att-count=""> 0 ? + '
' + + '' + + '' + + '
' : + ''; + Swal.fire({ title: 'Completare la scadenza?', + html: 'Vuoi creare automaticamente la prossima scadenza ricorrente?' + attCheckbox, icon: 'question', showCancelButton: true, + showDenyButton: true, confirmButtonColor: '#198754', + denyButtonColor: '#6c757d', + confirmButtonText: 'Completa e crea la prossima', + denyButtonText: 'Completa senza nuova', cancelButtonText: 'Annulla', - confirmButtonText: 'Completa' + reverseButtons: true }).then(function(result) { if (result.isConfirmed) { - fetch('scadenzario/ajax/complete_deadline.php?id=' + id) - .then(function(r) { - return r.json(); - }) - .then(function(data) { - if (data.success) { - Swal.fire({ - icon: 'success', - title: 'Completata', - text: data.message, - timer: 2500, - showConfirmButton: false - }) - .then(function() { - location.reload(); - }); - } else { - Swal.fire('Errore', data.message, 'error'); - } - }) - .catch(function() { - Swal.fire('Errore', 'Errore di connessione.', 'error'); - }); + var copy = attCount > 0 ? document.getElementById('swCopyAtt').checked : false; + submitComplete(id, true, copy); + } else if (result.isDenied) { + submitComplete(id, false, false); } }); }); From 0b470f290e6e5ffca47260456208f2a043e27666 Mon Sep 17 00:00:00 2001 From: "r.mubarakzyanov" Date: Sat, 23 May 2026 23:46:33 +0300 Subject: [PATCH 2/3] fix auto-open --- public/userarea/scadenzario/detail.php | 22 +- .../include/deadline_form_data.php | 32 ++ .../scadenzario/include/deadline_modal.php | 145 +++++ .../include/deadline_modal_css.php | 99 ++++ .../scadenzario/include/deadline_modal_js.php | 277 ++++++++++ public/userarea/scadenzario/index.php | 510 +----------------- 6 files changed, 582 insertions(+), 503 deletions(-) create mode 100644 public/userarea/scadenzario/include/deadline_form_data.php create mode 100644 public/userarea/scadenzario/include/deadline_modal.php create mode 100644 public/userarea/scadenzario/include/deadline_modal_css.php create mode 100644 public/userarea/scadenzario/include/deadline_modal_js.php diff --git a/public/userarea/scadenzario/detail.php b/public/userarea/scadenzario/detail.php index 78c55ea..c365288 100644 --- a/public/userarea/scadenzario/detail.php +++ b/public/userarea/scadenzario/detail.php @@ -85,6 +85,14 @@ if (!isset($_GET['id']) || !is_numeric($_GET['id'])) { + + + + + + + + <?= $deadline ? htmlspecialchars($deadline['topic'], ENT_QUOTES, 'UTF-8') . ' — ' : '' ?>Scadenzario + diff --git a/public/userarea/scadenzario/include/deadline_form_data.php b/public/userarea/scadenzario/include/deadline_form_data.php new file mode 100644 index 0000000..56bd8c6 --- /dev/null +++ b/public/userarea/scadenzario/include/deadline_form_data.php @@ -0,0 +1,32 @@ +getConnection(); +} + +if (!isset($employees)) { + $employees = $pdo->query(" + SELECT e.id, e.first_name, e.last_name, e.department_id, dep.name AS department_name + FROM employees e + LEFT JOIN departments dep ON dep.id = e.department_id + WHERE e.status = 'active' + ORDER BY e.first_name, e.last_name + ")->fetchAll(PDO::FETCH_ASSOC); +} + +if (!isset($departments)) { + $departments = $pdo->query(" + SELECT id, name, code, color + FROM departments + WHERE is_active = 1 + ORDER BY sort_order ASC, name ASC + ")->fetchAll(PDO::FETCH_ASSOC); +} + +if (!isset($subjects)) { + $subjects = $pdo->query("SELECT id, name, color FROM scad_subjects ORDER BY name")->fetchAll(PDO::FETCH_ASSOC); +} diff --git a/public/userarea/scadenzario/include/deadline_modal.php b/public/userarea/scadenzario/include/deadline_modal.php new file mode 100644 index 0000000..e0a4b58 --- /dev/null +++ b/public/userarea/scadenzario/include/deadline_modal.php @@ -0,0 +1,145 @@ + + + diff --git a/public/userarea/scadenzario/include/deadline_modal_css.php b/public/userarea/scadenzario/include/deadline_modal_css.php new file mode 100644 index 0000000..093126f --- /dev/null +++ b/public/userarea/scadenzario/include/deadline_modal_css.php @@ -0,0 +1,99 @@ + + diff --git a/public/userarea/scadenzario/include/deadline_modal_js.php b/public/userarea/scadenzario/include/deadline_modal_js.php new file mode 100644 index 0000000..d8356cb --- /dev/null +++ b/public/userarea/scadenzario/include/deadline_modal_js.php @@ -0,0 +1,277 @@ + → opens edit for that id + * #edit → opens edit for window.SCAD_DETAIL_ID (used by detail.php) + */ +?> + diff --git a/public/userarea/scadenzario/index.php b/public/userarea/scadenzario/index.php index 8fe672f..99be297 100644 --- a/public/userarea/scadenzario/index.php +++ b/public/userarea/scadenzario/index.php @@ -69,27 +69,7 @@ $stmt = $pdo->prepare($sql); $stmt->execute($params); $deadlines = $stmt->fetchAll(PDO::FETCH_ASSOC); -$employees = $pdo->query(" - SELECT - e.id, - e.first_name, - e.last_name, - e.department_id, - dep.name AS department_name - FROM employees e - LEFT JOIN departments dep ON dep.id = e.department_id - WHERE e.status = 'active' - ORDER BY e.first_name, e.last_name -")->fetchAll(PDO::FETCH_ASSOC); - -$departments = $pdo->query(" - SELECT id, name, code, color - FROM departments - WHERE is_active = 1 - ORDER BY sort_order ASC, name ASC -")->fetchAll(PDO::FETCH_ASSOC); - -$subjects = $pdo->query("SELECT id, name, color FROM scad_subjects ORDER BY name")->fetchAll(PDO::FETCH_ASSOC); +require __DIR__ . '/include/deadline_form_data.php'; $today = date('Y-m-d'); @@ -1059,143 +1039,7 @@ function getContrastTextColor($hexColor)
- - + @@ -1225,111 +1069,6 @@ 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', - placeholder: 'Seleziona argomento...', - allowClear: true, - dropdownParent: $('#deadlineModal .modal-body'), - language: 'it', - width: '100%' - }); - - $('#dlDepartments').select2({ - theme: 'bootstrap-5', - placeholder: 'Seleziona reparti...', - allowClear: true, - dropdownParent: $('#deadlineModal .modal-body'), - language: 'it', - width: '100%' - }); - - $('#dlEmployees').select2({ - theme: 'bootstrap-5', - placeholder: 'Seleziona persone...', - allowClear: true, - dropdownParent: $('#deadlineModal .modal-body'), - language: 'it', - width: '100%' - }); - - // --- Auto-calc due_date from document_date + recurrence --- - var RECURRENCE_OFFSETS = { - monthly: { - months: 1 - }, - quarterly: { - months: 3 - }, - semiannual: { - months: 6 - }, - annual: { - years: 1 - }, - biennial: { - years: 2 - }, - triennial: { - years: 3 - }, - quadriennial: { - years: 4 - }, - quinquennial: { - years: 5 - }, - decennial: { - years: 10 - }, - quindecennial: { - years: 15 - } - }; - - function computeDueDate() { - var docVal = document.getElementById('dlDocDate').value; - var recurrence = document.getElementById('dlRecurrence').value; - var offset = RECURRENCE_OFFSETS[recurrence]; - if (!docVal || !offset) return; - var d = new Date(docVal + 'T00:00:00'); - if (isNaN(d.getTime())) return; - if (offset.months) d.setMonth(d.getMonth() + offset.months); - if (offset.years) d.setFullYear(d.getFullYear() + offset.years); - var iso = d.getFullYear() + '-' + - String(d.getMonth() + 1).padStart(2, '0') + '-' + - String(d.getDate()).padStart(2, '0'); - fpDueDate.setDate(iso, true, 'Y-m-d'); - } - $('#dlDocDate, #dlRecurrence').on('change', computeDueDate); - // --- DataTables custom filters --- $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) { if (settings.nTable.id !== 'deadlinesTable') return true; @@ -1491,165 +1230,8 @@ function getContrastTextColor($hexColor) // Apply default filter on load applyFiltersRefresh(); - // --- Modal --- - 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(); - }); - - // Save - var isSaving = false; - form.addEventListener('submit', function(e) { - e.preventDefault(); - if (isSaving) return; - isSaving = true; - var saveBtn = form.querySelector('[type="submit"]'); - saveBtn.disabled = true; - saveBtn.innerHTML = ' Salvataggio...'; - var formData = new FormData(form); - - fetch('scadenzario/ajax/save_deadline.php', { - method: 'POST', - body: formData - }) - .then(function(r) { - return r.json(); - }) - .then(function(data) { - if (data.success) { - var deadlineId = data.id; - var fileInput = document.getElementById('dlFiles'); - if (fileInput.files.length > 0) { - // Upload files - var fileData = new FormData(); - fileData.append('deadline_id', deadlineId); - for (var i = 0; i < fileInput.files.length; i++) { - fileData.append('files[]', fileInput.files[i]); - } - return fetch('scadenzario/ajax/upload_attachment.php', { - method: 'POST', - body: fileData - }) - .then(function(r) { - return r.json(); - }) - .then(function(upData) { - modal.hide(); - Swal.fire({ - icon: 'success', - title: 'Salvato', - text: data.message + ' ' + upData.message, - timer: 2000, - showConfirmButton: false - }) - .then(function() { - location.reload(); - }); - }); - } else { - modal.hide(); - Swal.fire({ - icon: 'success', - title: 'Salvato', - text: data.message, - timer: 1500, - showConfirmButton: false - }) - .then(function() { - location.reload(); - }); - } - } else { - Swal.fire('Errore', data.message, 'error'); - isSaving = false; - saveBtn.disabled = false; - saveBtn.innerHTML = ' Salva'; - } - }) - .catch(function() { - Swal.fire('Errore', 'Errore di connessione.', 'error'); - isSaving = false; - saveBtn.disabled = false; - saveBtn.innerHTML = ' Salva'; - }); - }); - - // Render attachments list - function renderAttachments(attachments) { - var container = document.getElementById('attachmentsList'); - if (!attachments || attachments.length === 0) { - container.innerHTML = '
Nessun allegato
'; - return; - } - container.innerHTML = attachments.map(function(a) { - return '
' + - '' + $('').text(a.original_name).html() + '' + - '' + - '' + - '' + - '
'; - }).join(''); - } - - // Delete attachment - $(document).on('click', '.att-remove', function(e) { - e.preventDefault(); - var btn = $(this); - var attId = btn.data('att-id'); - Swal.fire({ - title: 'Rimuovere l\'allegato?', - text: 'Il collegamento verrà rimosso da questa scadenza. Il file resta disponibile se è usato da altre scadenze.', - icon: 'warning', - showCancelButton: true, - confirmButtonColor: '#dc3545', - cancelButtonText: 'Annulla', - confirmButtonText: 'Rimuovi', - reverseButtons: true - }).then(function(result) { - if (result.isConfirmed) { - fetch('scadenzario/ajax/delete_attachment.php?id=' + attId) - .then(function(r) { - return r.json(); - }) - .then(function(data) { - if (data.success) { - btn.closest('.att-item').remove(); - if ($('#attachmentsList .att-item').length === 0) { - renderAttachments([]); - } - Swal.fire({ - icon: 'success', - title: 'Fatto', - text: data.message, - timer: 1800, - showConfirmButton: false - }); - } else { - Swal.fire('Errore', data.message, 'error'); - } - }); - } - }); + if (window.openDeadlineCreate) window.openDeadlineCreate(); }); // Edit with confirmation @@ -1666,52 +1248,9 @@ function getContrastTextColor($hexColor) confirmButtonText: 'Sì, modifica', reverseButtons: true }).then(function(result) { - if (!result.isConfirmed) { - return; + if (result.isConfirmed && window.openDeadlineEdit) { + window.openDeadlineEdit(id); } - - fetch('scadenzario/ajax/get_deadline.php?id=' + id) - .then(function(r) { - return r.json(); - }) - .then(function(data) { - if (!data.success) { - Swal.fire('Errore', data.message, 'error'); - return; - } - - var d = data.data; - - document.getElementById('dlId').value = d.id; - $('#dlSubject').val(d.subject_id || '').trigger('change'); - document.getElementById('dlTopic').value = d.topic || ''; - document.getElementById('dlLaw').value = d.law_regulation || ''; - document.getElementById('dlRecurrence').value = d.recurrence_type || 'once'; - 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 || ''; - document.getElementById('dlFiles').value = ''; - - document.getElementById('modalTitle').textContent = 'Modifica Scadenza'; - - $('#dlDepartments').val(d.department_names || []).trigger('change'); - - if (Array.isArray(d.employee_ids)) { - $('#dlEmployees').val(d.employee_ids.map(String)).trigger('change'); - } else { - $('#dlEmployees').val(null).trigger('change'); - } - - renderAttachments(d.attachments || []); - - modal.show(); - }) - .catch(function() { - Swal.fire('Errore', 'Errore di connessione.', 'error'); - }); }); }); @@ -1739,9 +1278,9 @@ function getContrastTextColor($hexColor) showConfirmButton: false }) .then(function() { - // Auto-open the newly created deadline; otherwise just refresh the list + // Open the new deadline's detail page with the edit modal auto-opening if (data.new_id) { - window.location = 'scadenzario/index.php?edit=' + data.new_id; + window.location = 'scadenzario/detail.php?id=' + data.new_id + '#edit'; } else { location.reload(); } @@ -1851,38 +1390,6 @@ function getContrastTextColor($hexColor) }); }); - // Auto-open edit modal from ?edit=ID - var urlParams = new URLSearchParams(window.location.search); - var editId = urlParams.get('edit'); - if (editId) { - history.replaceState(null, '', 'scadenzario/index.php'); - fetch('scadenzario/ajax/get_deadline.php?id=' + editId) - .then(function(r) { - return r.json(); - }) - .then(function(data) { - if (!data.success) return; - var d = data.data; - document.getElementById('dlId').value = d.id; - $('#dlSubject').val(d.subject_id || '').trigger('change'); - document.getElementById('dlTopic').value = d.topic || ''; - document.getElementById('dlLaw').value = d.law_regulation || ''; - document.getElementById('dlRecurrence').value = d.recurrence_type || 'once'; - 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 || ''; - document.getElementById('dlFiles').value = ''; - document.getElementById('modalTitle').textContent = 'Modifica Scadenza'; - $('#dlDepartments').val(d.department_names || []).trigger('change'); - $('#dlEmployees').val(d.employee_ids.map(String)).trigger('change'); - renderAttachments(d.attachments || []); - modal.show(); - }); - } - // Stampa function doStampa() { var params = []; @@ -1905,6 +1412,7 @@ function getContrastTextColor($hexColor) if (btnStampaMobile) btnStampaMobile.addEventListener('click', doStampa); }); + - \ No newline at end of file + From 9ec5419a86f3f70012a64ba03c8949cf87c21d53 Mon Sep 17 00:00:00 2001 From: "r.mubarakzyanov" Date: Sun, 24 May 2026 00:15:09 +0300 Subject: [PATCH 3/3] dlFunction fix --- .../scadenzario/ajax/complete_deadline.php | 6 +++--- .../scadenzario/include/deadline_form_data.php | 9 +++++++++ .../scadenzario/include/deadline_modal.php | 17 +++++++++++++++++ .../scadenzario/include/deadline_modal_js.php | 3 +++ public/userarea/scadenzario/index.php | 7 ------- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/public/userarea/scadenzario/ajax/complete_deadline.php b/public/userarea/scadenzario/ajax/complete_deadline.php index 1f6e9fe..a192fd5 100644 --- a/public/userarea/scadenzario/ajax/complete_deadline.php +++ b/public/userarea/scadenzario/ajax/complete_deadline.php @@ -70,12 +70,12 @@ try { $ins = $pdo->prepare(" INSERT INTO scad_deadlines - (subject_id, topic, law_regulation, recurrence_type, due_date, check_date, + (subject_id, function_id, topic, law_regulation, recurrence_type, due_date, check_date, document_date, notification_days, storage_location, notes, created_by, departments) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "); $ins->execute([ - $deadline['subject_id'], $deadline['topic'], $deadline['law_regulation'], + $deadline['subject_id'], $deadline['function_id'], $deadline['topic'], $deadline['law_regulation'], $deadline['recurrence_type'], $dueDate->format('Y-m-d'), $checkDate ? $checkDate->format('Y-m-d') : null, $documentDate ? $documentDate->format('Y-m-d') : null, diff --git a/public/userarea/scadenzario/include/deadline_form_data.php b/public/userarea/scadenzario/include/deadline_form_data.php index 56bd8c6..6a8bd59 100644 --- a/public/userarea/scadenzario/include/deadline_form_data.php +++ b/public/userarea/scadenzario/include/deadline_form_data.php @@ -30,3 +30,12 @@ if (!isset($departments)) { if (!isset($subjects)) { $subjects = $pdo->query("SELECT id, name, color FROM scad_subjects ORDER BY name")->fetchAll(PDO::FETCH_ASSOC); } + +if (!isset($functions)) { + $functions = $pdo->query(" + SELECT id, name + FROM scad_functions + WHERE status = 'active' + ORDER BY name ASC + ")->fetchAll(PDO::FETCH_ASSOC); +} diff --git a/public/userarea/scadenzario/include/deadline_modal.php b/public/userarea/scadenzario/include/deadline_modal.php index e0a4b58..a50d8e8 100644 --- a/public/userarea/scadenzario/include/deadline_modal.php +++ b/public/userarea/scadenzario/include/deadline_modal.php @@ -35,6 +35,23 @@ +
+ +
+ + + + + +
+
diff --git a/public/userarea/scadenzario/include/deadline_modal_js.php b/public/userarea/scadenzario/include/deadline_modal_js.php index d8356cb..98c16d9 100644 --- a/public/userarea/scadenzario/include/deadline_modal_js.php +++ b/public/userarea/scadenzario/include/deadline_modal_js.php @@ -39,6 +39,7 @@ $('#dlSubject').select2($.extend({}, s2Opts, { placeholder: 'Seleziona argomento...' })); $('#dlDepartments').select2($.extend({}, s2Opts, { placeholder: 'Seleziona reparti...' })); $('#dlEmployees').select2($.extend({}, s2Opts, { placeholder: 'Seleziona persone...' })); + $('#dlFunction').select2($.extend({}, s2Opts, { placeholder: 'Seleziona funzione...' })); // --- Auto-calc due_date from document_date + recurrence --- var RECURRENCE_OFFSETS = { @@ -104,6 +105,7 @@ $('#dlSubject').val('').trigger('change'); $('#dlDepartments').val(null).trigger('change'); $('#dlEmployees').val(null).trigger('change'); + $('#dlFunction').val('').trigger('change'); renderAttachments([]); modal.show(); }; @@ -123,6 +125,7 @@ document.getElementById('dlId').value = d.id; $('#dlSubject').val(d.subject_id || '').trigger('change'); document.getElementById('dlTopic').value = d.topic || ''; + $('#dlFunction').val(d.function_id || '').trigger('change'); document.getElementById('dlLaw').value = d.law_regulation || ''; document.getElementById('dlRecurrence').value = d.recurrence_type || 'once'; fpDocDate.setDate(d.document_date || null, false, 'Y-m-d'); diff --git a/public/userarea/scadenzario/index.php b/public/userarea/scadenzario/index.php index 742337a..a5bdc49 100644 --- a/public/userarea/scadenzario/index.php +++ b/public/userarea/scadenzario/index.php @@ -73,13 +73,6 @@ $deadlines = $stmt->fetchAll(PDO::FETCH_ASSOC); require __DIR__ . '/include/deadline_form_data.php'; -$functions = $pdo->query(" - SELECT id, name - FROM scad_functions - WHERE status = 'active' - ORDER BY name ASC -")->fetchAll(PDO::FETCH_ASSOC); - $today = date('Y-m-d'); function getContrastTextColor($hexColor)