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..a192fd5 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,23 +66,25 @@ try { if ($interval) { $dueDate->add($interval); if ($checkDate) $checkDate->add($interval); + if ($documentDate) $documentDate->add($interval); $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, - $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..c365288 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']; } } ?> @@ -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..6a8bd59 --- /dev/null +++ b/public/userarea/scadenzario/include/deadline_form_data.php @@ -0,0 +1,41 @@ +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); +} + +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 new file mode 100644 index 0000000..a50d8e8 --- /dev/null +++ b/public/userarea/scadenzario/include/deadline_modal.php @@ -0,0 +1,162 @@ + + + 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..98c16d9 --- /dev/null +++ b/public/userarea/scadenzario/include/deadline_modal_js.php @@ -0,0 +1,280 @@ + → 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 916eb6c..a5bdc49 100644 --- a/public/userarea/scadenzario/index.php +++ b/public/userarea/scadenzario/index.php @@ -71,34 +71,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); - -$functions = $pdo->query(" - SELECT id, name - FROM scad_functions - WHERE status = 'active' - ORDER BY name ASC -")->fetchAll(PDO::FETCH_ASSOC); +require __DIR__ . '/include/deadline_form_data.php'; $today = date('Y-m-d'); @@ -937,7 +910,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="">
- - + @@ -1265,120 +1089,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%' - }); - - $('#dlFunction').select2({ - theme: 'bootstrap-5', - placeholder: 'Seleziona funzione...', - 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; @@ -1540,157 +1250,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'); - $('#dlFunction').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: 'Eliminare allegato?', - icon: 'warning', - showCancelButton: true, - confirmButtonColor: '#dc3545', - cancelButtonText: 'Annulla', - confirmButtonText: 'Elimina' - }).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([]); - } - } else { - Swal.fire('Errore', data.message, 'error'); - } - }); - } - }); + if (window.openDeadlineCreate) window.openDeadlineCreate(); }); // Edit with confirmation @@ -1707,92 +1268,103 @@ 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'); - $('#dlFunction').val(d.function_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'); - }); }); }); // Complete + function submitComplete(id, createNext, copyAttachments) { + var fd = new FormData(); + fd.append('id', 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() { + // Open the new deadline's detail page with the edit modal auto-opening + if (data.new_id) { + window.location = 'scadenzario/detail.php?id=' + data.new_id + '#edit'; + } else { + location.reload(); + } + }); + } else { + Swal.fire('Errore', data.message, 'error'); + } + }) + .catch(function() { + Swal.fire('Errore', 'Errore di connessione.', 'error'); + }); + } + $(document).on('click', '.btn-complete', function() { var el = $(this).closest('[data-id]'); var id = el.data('id'); + var recurrence = el.data('recurrence') || 'once'; + var attCount = parseInt(el.data('att-count'), 10) || 0; + + // Non-recurring: simple confirm, no new deadline is created + 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) { + submitComplete(id, false, false); + } + }); + return; + } + + // Recurring: ask whether to create the next deadline; optionally carry attachments over + 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=' + 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); } }); }); @@ -1838,39 +1410,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'); - $('#dlFunction').val(d.function_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 = []; @@ -1893,6 +1432,7 @@ function getContrastTextColor($hexColor) if (btnStampaMobile) btnStampaMobile.addEventListener('click', doStampa); }); + - \ No newline at end of file +