Compare commits
10 Commits
main
...
feature/de
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bbeb11726 | |||
| dd5edab2f3 | |||
| 1fadc22178 | |||
| d3ee9a3790 | |||
| c387b71cae | |||
| b2cfec77df | |||
| 73b9a3d890 | |||
| 0550ffe923 | |||
| d2e5cc8b2b | |||
| d7b6a58407 |
@ -7,16 +7,21 @@ ini_set('display_errors', 1);
|
|||||||
ini_set('display_startup_errors', 1);
|
ini_set('display_startup_errors', 1);
|
||||||
error_reporting(E_ALL | E_STRICT);
|
error_reporting(E_ALL | E_STRICT);
|
||||||
// This should be equal to: PATH_TO_VANGUARD_FOLDER/extra/auth.php
|
// This should be equal to: PATH_TO_VANGUARD_FOLDER/extra/auth.php
|
||||||
include('../../extra/auth.php');
|
include(__DIR__ . '/../../../extra/auth.php');
|
||||||
//require_once __DIR__ . '/extra/auth.php';
|
//require_once __DIR__ . '/extra/auth.php';
|
||||||
|
|
||||||
// Here we just check if user is not
|
// Here we just check if user is not
|
||||||
// logged in, and in that case we redirect
|
// logged in, and in that case we redirect
|
||||||
// the user to vanguard login page.
|
// the user to vanguard login page.
|
||||||
|
|
||||||
if (! Auth::check()) {
|
if (! Auth::check()) {
|
||||||
|
// Cut everything at /userarea/ and append /login.
|
||||||
redirectTo('../../public/login');
|
$scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
|
||||||
|
$basePath = substr($scriptName, 0, strpos($scriptName, '/userarea/'));
|
||||||
|
if ($basePath === false || $basePath === '') {
|
||||||
|
$basePath = '';
|
||||||
|
}
|
||||||
|
redirectTo($basePath . '/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
@ -58,4 +63,4 @@ require_once(__DIR__ . '/../../languages/en/general.php');
|
|||||||
|
|
||||||
//include("generalsettings.php");
|
//include("generalsettings.php");
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@ -59,6 +59,22 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="javascript:;" class="has-arrow">
|
||||||
|
<div class="parent-icon"><i class="bx bx-calendar-check"></i>
|
||||||
|
</div>
|
||||||
|
<div class="menu-title">Scadenzario</div>
|
||||||
|
</a>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="scadenzario/index.php"><i class='bx bx-radio-circle'></i>Lista Scadenze</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="scadenzario/calendar.php"><i class='bx bx-radio-circle'></i>Calendario</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="menu-label">Others</li>
|
<li class="menu-label">Others</li>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -296,6 +296,11 @@
|
|||||||
<div class="page-wrapper">
|
<div class="page-wrapper">
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||||
|
include(__DIR__ . '/scadenzario/include/my_deadlines_widget.php');
|
||||||
|
?>
|
||||||
|
|
||||||
<h3 class="dashboard-title">Dashboard Produzione</h3>
|
<h3 class="dashboard-title">Dashboard Produzione</h3>
|
||||||
|
|
||||||
<!-- ===== STATISTICHE PRINCIPALI ===== -->
|
<!-- ===== STATISTICHE PRINCIPALI ===== -->
|
||||||
@ -381,7 +386,7 @@
|
|||||||
<div>Magazzino</div>
|
<div>Magazzino</div>
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
<button class="dash-btn btn-scadenziario" onclick="location.href='activities_deadlines.php'">
|
<button class="dash-btn btn-scadenziario" onclick="location.href='scadenzario/index.php'">
|
||||||
<div class="dash-icon">⏰</div>
|
<div class="dash-icon">⏰</div>
|
||||||
<div>Scadenziario</div>
|
<div>Scadenziario</div>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
75
public/userarea/scadenzario/INSTALLATION.md
Normal file
75
public/userarea/scadenzario/INSTALLATION.md
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# Installation — Scadenzario
|
||||||
|
|
||||||
|
## 1. Database
|
||||||
|
|
||||||
|
Run the schema script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mysql -u <user> -p <database> < public/userarea/scadenzario/sql/1_create_tables.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates 5 tables:
|
||||||
|
|
||||||
|
| Table | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `scad_deadlines` | Main deadline records |
|
||||||
|
| `scad_deadline_employee` | M2M assignment of individual employees |
|
||||||
|
| `scad_deadline_attachments` | File attachments |
|
||||||
|
| `scad_deadline_histories` | Audit log (created/updated/completed/...) |
|
||||||
|
| `scad_deadline_notifications` | Sent-notification log (deduplication) |
|
||||||
|
|
||||||
|
Departments are stored as a comma-separated string in `scad_deadlines.departments` (matching `employees.department` values). No separate `departments` table.
|
||||||
|
|
||||||
|
## 2. Filesystem permissions
|
||||||
|
|
||||||
|
The `attachments/` folder must be writable by the web server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod 755 public/userarea/scadenzario/attachments
|
||||||
|
chown www-data:www-data public/userarea/scadenzario/attachments
|
||||||
|
```
|
||||||
|
|
||||||
|
The included `.htaccess` denies direct web access. Files are served only through the auth-protected `ajax/download_attachment.php` endpoint.
|
||||||
|
|
||||||
|
## 3. SMTP configuration
|
||||||
|
|
||||||
|
Email notifications use the project-wide SMTP settings in `.env`:
|
||||||
|
|
||||||
|
```env
|
||||||
|
MAIL_MAILER=smtp
|
||||||
|
MAIL_HOST=smtp.example.com
|
||||||
|
MAIL_PORT=465
|
||||||
|
MAIL_USERNAME=your_user
|
||||||
|
MAIL_PASSWORD=your_password
|
||||||
|
MAIL_ENCRYPTION=ssl
|
||||||
|
MAIL_FROM_ADDRESS=scadenzario@your-domain.com
|
||||||
|
MAIL_FROM_NAME="Scadenzario"
|
||||||
|
APP_URL=https://your-domain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Cron schedule
|
||||||
|
|
||||||
|
Add to the system crontab (run as the web user):
|
||||||
|
|
||||||
|
```cron
|
||||||
|
0 7 * * * php /var/www/html/public/userarea/scadenzario/cron/send_notifications.php >> /var/log/scadenzario.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
This sends notifications daily at 07:00 for:
|
||||||
|
|
||||||
|
- **Approaching** — `due_date <= today + notification_days` (per-deadline lead time)
|
||||||
|
- **Overdue** — `due_date < today`
|
||||||
|
|
||||||
|
Completed deadlines are skipped. Recipients without an `auth_user_id` are silently skipped.
|
||||||
|
|
||||||
|
## 5. Linking employees to auth users
|
||||||
|
|
||||||
|
For an employee to receive email notifications:
|
||||||
|
|
||||||
|
1. The corresponding `auth_users` row must exist with a valid `email`.
|
||||||
|
2. Set `employees.auth_user_id` to that user ID:
|
||||||
|
```sql
|
||||||
|
UPDATE employees SET auth_user_id = <user_id> WHERE id = <employee_id>;
|
||||||
|
```
|
||||||
|
|
||||||
|
Employees without `auth_user_id` are silently skipped by the cron.
|
||||||
18
public/userarea/scadenzario/ajax/auth_check.php
Normal file
18
public/userarea/scadenzario/ajax/auth_check.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Auth check for AJAX endpoints.
|
||||||
|
* Include this at the top of every ajax handler.
|
||||||
|
* Sets $currentUserId from session or returns 401 JSON.
|
||||||
|
*/
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($_SESSION['iduserlogin'])) {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
http_response_code(401);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Non autorizzato. Effettua il login.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentUserId = (int)$_SESSION['iduserlogin'];
|
||||||
110
public/userarea/scadenzario/ajax/complete_deadline.php
Normal file
110
public/userarea/scadenzario/ajax/complete_deadline.php
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/auth_check.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once(__DIR__ . '/../../class/db-functions.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'ID non valido.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = (int)$_GET['id'];
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM scad_deadlines WHERE id = ? AND status = 'active'");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$deadline = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$deadline) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Scadenza non trovata o già completata.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
// Mark as completed
|
||||||
|
$pdo->prepare("UPDATE scad_deadlines SET status = 'completed', completed_at = NOW(), completed_by = ? WHERE id = ?")
|
||||||
|
->execute([$currentUserId, $id]);
|
||||||
|
|
||||||
|
// History
|
||||||
|
$pdo->prepare("INSERT INTO scad_deadline_histories (deadline_id, user_id, action) VALUES (?, ?, 'completed')")
|
||||||
|
->execute([$id, $currentUserId]);
|
||||||
|
|
||||||
|
$newId = null;
|
||||||
|
|
||||||
|
// If recurring, create next deadline
|
||||||
|
if ($deadline['recurrence_type'] !== 'once') {
|
||||||
|
$dueDate = new DateTime($deadline['due_date']);
|
||||||
|
$checkDate = $deadline['check_date'] ? new DateTime($deadline['check_date']) : null;
|
||||||
|
|
||||||
|
switch ($deadline['recurrence_type']) {
|
||||||
|
case 'monthly': $interval = new DateInterval('P1M'); break;
|
||||||
|
case 'quarterly': $interval = new DateInterval('P3M'); break;
|
||||||
|
case 'semiannual': $interval = new DateInterval('P6M'); break;
|
||||||
|
case 'annual': $interval = new DateInterval('P1Y'); break;
|
||||||
|
case 'biennial': $interval = new DateInterval('P2Y'); break;
|
||||||
|
case 'triennial': $interval = new DateInterval('P3Y'); break;
|
||||||
|
case 'quadriennial': $interval = new DateInterval('P4Y'); break;
|
||||||
|
case 'quinquennial': $interval = new DateInterval('P5Y'); break;
|
||||||
|
case 'decennial': $interval = new DateInterval('P10Y'); break;
|
||||||
|
case 'quindecennial': $interval = new DateInterval('P15Y'); break;
|
||||||
|
default: $interval = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($interval) {
|
||||||
|
$dueDate->add($interval);
|
||||||
|
if ($checkDate) $checkDate->add($interval);
|
||||||
|
|
||||||
|
$ins = $pdo->prepare("
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
");
|
||||||
|
$ins->execute([
|
||||||
|
$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'],
|
||||||
|
$deadline['notification_days'], $deadline['storage_location'],
|
||||||
|
$deadline['notes'], $deadline['created_by'], $deadline['departments']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$newId = $pdo->lastInsertId();
|
||||||
|
|
||||||
|
// Copy employee assignments
|
||||||
|
$empStmt = $pdo->prepare("SELECT employee_id FROM scad_deadline_employee WHERE deadline_id = ?");
|
||||||
|
$empStmt->execute([$id]);
|
||||||
|
$empIds = $empStmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
if (!empty($empIds)) {
|
||||||
|
$insertEmp = $pdo->prepare("INSERT INTO scad_deadline_employee (deadline_id, employee_id) VALUES (?, ?)");
|
||||||
|
foreach ($empIds as $empId) {
|
||||||
|
$insertEmp->execute([$newId, $empId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
|
||||||
|
$msg = 'Scadenza completata con successo.';
|
||||||
|
if ($newId) {
|
||||||
|
$msg .= ' Nuova scadenza creata con data ' . $dueDate->format('d/m/Y') . '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'message' => $msg, 'new_id' => $newId]);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if (isset($pdo) && $pdo->inTransaction()) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
}
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
43
public/userarea/scadenzario/ajax/delete_attachment.php
Normal file
43
public/userarea/scadenzario/ajax/delete_attachment.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/auth_check.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once(__DIR__ . '/../../class/db-functions.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'ID non valido.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = (int)$_GET['id'];
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM scad_deadline_attachments WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$att = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$att) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Allegato non trovato.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete file
|
||||||
|
$filePath = __DIR__ . '/../attachments/' . $att['stored_name'];
|
||||||
|
if (file_exists($filePath)) {
|
||||||
|
unlink($filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete DB record
|
||||||
|
$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']]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Allegato eliminato.']);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
26
public/userarea/scadenzario/ajax/delete_deadline.php
Normal file
26
public/userarea/scadenzario/ajax/delete_deadline.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/auth_check.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once(__DIR__ . '/../../class/db-functions.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'ID non valido.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = (int)$_GET['id'];
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM scad_deadlines WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
|
||||||
|
if ($stmt->rowCount() > 0) {
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Scadenza eliminata con successo.']);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Scadenza non trovata.']);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
36
public/userarea/scadenzario/ajax/download_attachment.php
Normal file
36
public/userarea/scadenzario/ajax/download_attachment.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/auth_check.php');
|
||||||
|
require_once(__DIR__ . '/../../class/db-functions.php');
|
||||||
|
|
||||||
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo 'ID non valido.';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = (int)$_GET['id'];
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM scad_deadline_attachments WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$att = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$att) {
|
||||||
|
http_response_code(404);
|
||||||
|
echo 'Allegato non trovato.';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filePath = __DIR__ . '/../attachments/' . $att['stored_name'];
|
||||||
|
if (!file_exists($filePath)) {
|
||||||
|
http_response_code(404);
|
||||||
|
echo 'File non trovato sul server.';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-Type: ' . ($att['mime_type'] ?: 'application/octet-stream'));
|
||||||
|
header('Content-Disposition: attachment; filename="' . addslashes($att['original_name']) . '"');
|
||||||
|
header('Content-Length: ' . filesize($filePath));
|
||||||
|
readfile($filePath);
|
||||||
|
exit;
|
||||||
94
public/userarea/scadenzario/ajax/get_calendar_events.php
Normal file
94
public/userarea/scadenzario/ajax/get_calendar_events.php
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/auth_check.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once(__DIR__ . '/../../class/db-functions.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$start = $_GET['start'] ?? null;
|
||||||
|
$end = $_GET['end'] ?? null;
|
||||||
|
$filterStatus = $_GET['status'] ?? '';
|
||||||
|
$filterDept = $_GET['department'] ?? '';
|
||||||
|
$filterEmployee = $_GET['employee'] ?? '';
|
||||||
|
|
||||||
|
$sql = "SELECT DISTINCT d.id, d.topic, d.due_date, d.status, d.notification_days, d.departments,
|
||||||
|
s.name AS subject_name, s.color AS subject_color
|
||||||
|
FROM scad_deadlines d
|
||||||
|
LEFT JOIN scad_subjects s ON s.id = d.subject_id
|
||||||
|
LEFT JOIN scad_deadline_employee de ON de.deadline_id = d.id
|
||||||
|
LEFT JOIN employees e ON e.id = de.employee_id";
|
||||||
|
|
||||||
|
$where = [];
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
if ($start && $end) {
|
||||||
|
$where[] = "d.due_date >= ? AND d.due_date <= ?";
|
||||||
|
$params[] = $start;
|
||||||
|
$params[] = $end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($filterStatus === 'non-completata') {
|
||||||
|
$where[] = "d.status != 'completed'";
|
||||||
|
} elseif ($filterStatus === 'completata') {
|
||||||
|
$where[] = "d.status = 'completed'";
|
||||||
|
} elseif ($filterStatus === 'scaduta') {
|
||||||
|
$where[] = "d.status = 'active' AND d.due_date < CURDATE()";
|
||||||
|
} elseif ($filterStatus === 'in-scadenza') {
|
||||||
|
$where[] = "d.status = 'active' AND d.due_date >= CURDATE() AND d.due_date <= DATE_ADD(CURDATE(), INTERVAL d.notification_days DAY)";
|
||||||
|
} elseif ($filterStatus === 'attiva') {
|
||||||
|
$where[] = "d.status = 'active' AND d.due_date > DATE_ADD(CURDATE(), INTERVAL d.notification_days DAY)";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($filterDept) {
|
||||||
|
$where[] = "(e.department = ? OR FIND_IN_SET(?, d.departments))";
|
||||||
|
$params[] = $filterDept;
|
||||||
|
$params[] = $filterDept;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($filterEmployee) {
|
||||||
|
$where[] = "CONCAT(e.first_name, ' ', e.last_name) = ?";
|
||||||
|
$params[] = $filterEmployee;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($where)) {
|
||||||
|
$sql .= " WHERE " . implode(' AND ', $where);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$deadlines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$today = date('Y-m-d');
|
||||||
|
$events = [];
|
||||||
|
|
||||||
|
foreach ($deadlines as $d) {
|
||||||
|
$isCompleted = $d['status'] === 'completed';
|
||||||
|
$isOverdue = !$isCompleted && $d['due_date'] < $today;
|
||||||
|
$approachDate = date('Y-m-d', strtotime($today . ' + ' . (int)$d['notification_days'] . ' days'));
|
||||||
|
$isApproaching = !$isCompleted && !$isOverdue && $d['due_date'] <= $approachDate;
|
||||||
|
|
||||||
|
if ($isCompleted) { $color = '#198754'; }
|
||||||
|
elseif ($isOverdue) { $color = '#dc3545'; }
|
||||||
|
elseif ($isApproaching) { $color = '#e8930c'; }
|
||||||
|
else { $color = '#5a8fd8'; }
|
||||||
|
|
||||||
|
$title = $d['topic'];
|
||||||
|
if (!empty($d['subject_name'])) $title = $d['subject_name'] . ': ' . $title;
|
||||||
|
|
||||||
|
$events[] = [
|
||||||
|
'id' => $d['id'],
|
||||||
|
'title' => $title,
|
||||||
|
'start' => $d['due_date'],
|
||||||
|
'backgroundColor' => $color,
|
||||||
|
'borderColor' => !empty($d['subject_color']) ? $d['subject_color'] : $color,
|
||||||
|
'url' => 'scadenzario/detail.php?id=' . $d['id'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($events);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode([]);
|
||||||
|
}
|
||||||
45
public/userarea/scadenzario/ajax/get_deadline.php
Normal file
45
public/userarea/scadenzario/ajax/get_deadline.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/auth_check.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once(__DIR__ . '/../../class/db-functions.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'ID non valido.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = (int)$_GET['id'];
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM scad_deadlines WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$deadline = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$deadline) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Scadenza non trovata.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get assigned employee IDs
|
||||||
|
$empStmt = $pdo->prepare("SELECT employee_id FROM scad_deadline_employee WHERE deadline_id = ?");
|
||||||
|
$empStmt->execute([$id]);
|
||||||
|
$deadline['employee_ids'] = $empStmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
// Parse departments into array
|
||||||
|
$deadline['department_names'] = [];
|
||||||
|
if (!empty($deadline['departments'])) {
|
||||||
|
$deadline['department_names'] = array_map('trim', explode(',', $deadline['departments']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get attachments
|
||||||
|
$attStmt = $pdo->prepare("SELECT id, original_name, mime_type, size, created_at FROM scad_deadline_attachments WHERE deadline_id = ? ORDER BY created_at DESC");
|
||||||
|
$attStmt->execute([$id]);
|
||||||
|
$deadline['attachments'] = $attStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'data' => $deadline]);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
49
public/userarea/scadenzario/ajax/get_history.php
Normal file
49
public/userarea/scadenzario/ajax/get_history.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/auth_check.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once(__DIR__ . '/../../class/db-functions.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'ID non valido.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = (int)$_GET['id'];
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT h.*,
|
||||||
|
au.first_name as user_first_name,
|
||||||
|
au.last_name as user_last_name
|
||||||
|
FROM scad_deadline_histories h
|
||||||
|
LEFT JOIN auth_users au ON au.id = h.user_id
|
||||||
|
WHERE h.deadline_id = ?
|
||||||
|
ORDER BY h.created_at DESC
|
||||||
|
");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$history = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Format for display
|
||||||
|
$actionLabels = [
|
||||||
|
'created' => 'Creata',
|
||||||
|
'updated' => 'Modificata',
|
||||||
|
'completed' => 'Completata',
|
||||||
|
'attachment_added' => 'Allegato aggiunto',
|
||||||
|
'attachment_removed' => 'Allegato rimosso',
|
||||||
|
'notification_sent' => 'Notifica inviata'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($history as &$h) {
|
||||||
|
$h['action_label'] = $actionLabels[$h['action']] ?? $h['action'];
|
||||||
|
$h['user_name'] = trim(($h['user_first_name'] ?? '') . ' ' . ($h['user_last_name'] ?? '')) ?: 'Sistema';
|
||||||
|
$h['changes'] = $h['changes'] ? json_decode($h['changes'], true) : null;
|
||||||
|
unset($h['user_first_name'], $h['user_last_name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'data' => $history]);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
116
public/userarea/scadenzario/ajax/save_deadline.php
Normal file
116
public/userarea/scadenzario/ajax/save_deadline.php
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/auth_check.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once(__DIR__ . '/../../class/db-functions.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$id = isset($_POST['id']) && is_numeric($_POST['id']) ? (int)$_POST['id'] : null;
|
||||||
|
$subject_id = isset($_POST['subject_id']) && is_numeric($_POST['subject_id']) && (int)$_POST['subject_id'] > 0 ? (int)$_POST['subject_id'] : null;
|
||||||
|
$topic = trim($_POST['topic'] ?? '');
|
||||||
|
$law_regulation = trim($_POST['law_regulation'] ?? '') ?: null;
|
||||||
|
$recurrence_type = $_POST['recurrence_type'] ?? 'once';
|
||||||
|
$due_date = $_POST['due_date'] ?? '';
|
||||||
|
$check_date = trim($_POST['check_date'] ?? '') ?: null;
|
||||||
|
$document_date = trim($_POST['document_date'] ?? '') ?: null;
|
||||||
|
$notification_days = isset($_POST['notification_days']) && is_numeric($_POST['notification_days']) ? (int)$_POST['notification_days'] : 7;
|
||||||
|
$storage_location = trim($_POST['storage_location'] ?? '') ?: null;
|
||||||
|
$notes = trim($_POST['notes'] ?? '') ?: null;
|
||||||
|
$employee_ids = $_POST['employee_ids'] ?? [];
|
||||||
|
$department_names = $_POST['department_names'] ?? [];
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
if ($topic === '') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Il campo Tema è obbligatorio.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if ($due_date === '' || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $due_date)) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'La data di scadenza è obbligatoria.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$validRecurrences = ['once', 'monthly', 'quarterly', 'semiannual', 'annual', 'biennial', 'triennial', 'quadriennial', 'quinquennial', 'decennial', 'quindecennial'];
|
||||||
|
if (!in_array($recurrence_type, $validRecurrences)) {
|
||||||
|
$recurrence_type = 'once';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($employee_ids)) {
|
||||||
|
$employee_ids = [];
|
||||||
|
}
|
||||||
|
$employee_ids = array_filter(array_map('intval', $employee_ids));
|
||||||
|
|
||||||
|
if (!is_array($department_names)) {
|
||||||
|
$department_names = [];
|
||||||
|
}
|
||||||
|
$department_names = array_filter(array_map('trim', $department_names));
|
||||||
|
$departmentsStr = !empty($department_names) ? implode(', ', $department_names) : null;
|
||||||
|
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
if ($id) {
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
UPDATE scad_deadlines SET
|
||||||
|
subject_id = ?, topic = ?, law_regulation = ?, recurrence_type = ?,
|
||||||
|
due_date = ?, check_date = ?, document_date = ?, notification_days = ?,
|
||||||
|
storage_location = ?, notes = ?, departments = ?
|
||||||
|
WHERE id = ?
|
||||||
|
");
|
||||||
|
$stmt->execute([
|
||||||
|
$subject_id, $topic, $law_regulation, $recurrence_type,
|
||||||
|
$due_date, $check_date, $document_date, $notification_days,
|
||||||
|
$storage_location, $notes, $departmentsStr, $id
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Re-link employees
|
||||||
|
$pdo->prepare("DELETE FROM scad_deadline_employee WHERE deadline_id = ?")->execute([$id]);
|
||||||
|
|
||||||
|
// History
|
||||||
|
$pdo->prepare("INSERT INTO scad_deadline_histories (deadline_id, user_id, action) VALUES (?, ?, 'updated')")
|
||||||
|
->execute([$id, $currentUserId ?: null]);
|
||||||
|
|
||||||
|
$deadlineId = $id;
|
||||||
|
} else {
|
||||||
|
// INSERT
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
");
|
||||||
|
$stmt->execute([
|
||||||
|
$subject_id, $topic, $law_regulation, $recurrence_type,
|
||||||
|
$due_date, $check_date, $document_date, $notification_days,
|
||||||
|
$storage_location, $notes, $currentUserId, $departmentsStr
|
||||||
|
]);
|
||||||
|
|
||||||
|
$deadlineId = $pdo->lastInsertId();
|
||||||
|
|
||||||
|
// History
|
||||||
|
$pdo->prepare("INSERT INTO scad_deadline_histories (deadline_id, user_id, action) VALUES (?, ?, 'created')")
|
||||||
|
->execute([$deadlineId, $currentUserId ?: null]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link employees
|
||||||
|
if (!empty($employee_ids)) {
|
||||||
|
$insertEmployee = $pdo->prepare("INSERT INTO scad_deadline_employee (deadline_id, employee_id) VALUES (?, ?)");
|
||||||
|
foreach ($employee_ids as $empId) {
|
||||||
|
$insertEmployee->execute([$deadlineId, $empId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => $id ? 'Scadenza aggiornata con successo.' : 'Scadenza creata con successo.',
|
||||||
|
'id' => $deadlineId
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if (isset($pdo) && $pdo->inTransaction()) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
}
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
72
public/userarea/scadenzario/ajax/upload_attachment.php
Normal file
72
public/userarea/scadenzario/ajax/upload_attachment.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/auth_check.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once(__DIR__ . '/../../class/db-functions.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!isset($_POST['deadline_id']) || !is_numeric($_POST['deadline_id'])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'ID scadenza non valido.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if (empty($_FILES['files']['name'][0])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Nessun file selezionato.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$deadlineId = (int)$_POST['deadline_id'];
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
// Verify deadline exists
|
||||||
|
$check = $pdo->prepare("SELECT id FROM scad_deadlines WHERE id = ?");
|
||||||
|
$check->execute([$deadlineId]);
|
||||||
|
if (!$check->fetch()) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Scadenza non trovata.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$uploadDir = __DIR__ . '/../attachments/';
|
||||||
|
if (!is_dir($uploadDir)) {
|
||||||
|
mkdir($uploadDir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$inserted = [];
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
INSERT INTO scad_deadline_attachments (deadline_id, original_name, stored_name, mime_type, size, uploaded_by)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
");
|
||||||
|
$histStmt = $pdo->prepare("INSERT INTO scad_deadline_histories (deadline_id, user_id, action, notes) VALUES (?, ?, 'attachment_added', ?)");
|
||||||
|
|
||||||
|
$fileCount = count($_FILES['files']['name']);
|
||||||
|
for ($i = 0; $i < $fileCount; $i++) {
|
||||||
|
if ($_FILES['files']['error'][$i] !== UPLOAD_ERR_OK) continue;
|
||||||
|
|
||||||
|
$originalName = $_FILES['files']['name'][$i];
|
||||||
|
$mimeType = $_FILES['files']['type'][$i];
|
||||||
|
$size = $_FILES['files']['size'][$i];
|
||||||
|
$storedName = uniqid('att_') . '_' . preg_replace('/[^a-zA-Z0-9._-]/', '_', $originalName);
|
||||||
|
|
||||||
|
if (!move_uploaded_file($_FILES['files']['tmp_name'][$i], $uploadDir . $storedName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->execute([$deadlineId, $originalName, $storedName, $mimeType, $size, $currentUserId]);
|
||||||
|
$histStmt->execute([$deadlineId, $currentUserId, $originalName]);
|
||||||
|
$inserted[] = ['id' => $pdo->lastInsertId(), 'original_name' => $originalName, 'stored_name' => $storedName];
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => count($inserted) . ' file caricato/i con successo.',
|
||||||
|
'files' => $inserted
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if (isset($pdo) && $pdo->inTransaction()) $pdo->rollBack();
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
3
public/userarea/scadenzario/attachments/.gitignore
vendored
Normal file
3
public/userarea/scadenzario/attachments/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
!.htaccess
|
||||||
1
public/userarea/scadenzario/attachments/.htaccess
Normal file
1
public/userarea/scadenzario/attachments/.htaccess
Normal file
@ -0,0 +1 @@
|
|||||||
|
Deny from all
|
||||||
275
public/userarea/scadenzario/calendar.php
Normal file
275
public/userarea/scadenzario/calendar.php
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
<?php include('../include/headscript.php'); ?>
|
||||||
|
<?php
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
$employees = $pdo->query("SELECT id, first_name, last_name, department FROM employees WHERE status = 'active' ORDER BY first_name")->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
$departments = $pdo->query("SELECT DISTINCT department FROM employees WHERE department IS NOT NULL AND department != '' ORDER BY department")->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
?>
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<?php
|
||||||
|
$scriptDir = dirname($_SERVER['SCRIPT_NAME']);
|
||||||
|
$baseHref = dirname($scriptDir) . '/';
|
||||||
|
?>
|
||||||
|
<base href="<?= $baseHref ?>">
|
||||||
|
<?php include('../cssinclude.php'); ?>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.9/index.global.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@fullcalendar/core@6.1.9/locales/it.global.min.js"></script>
|
||||||
|
<title>Calendario - Scadenzario</title>
|
||||||
|
<script>if(window.innerWidth>1024)document.addEventListener('DOMContentLoaded',function(){document.getElementById('appWrapper').classList.add('toggled')})</script>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--scad-primary: #5a8fd8;
|
||||||
|
--scad-primary-hover: #4578c0;
|
||||||
|
--scad-heading: #2c3e6b;
|
||||||
|
--scad-card-bg: linear-gradient(135deg, #f0f4ff 0%, #e8eeff 100%);
|
||||||
|
--scad-card-border: #dde4f0;
|
||||||
|
}
|
||||||
|
.scad-card {
|
||||||
|
border: none; border-radius: 0.75rem;
|
||||||
|
box-shadow: 0 2px 12px rgba(0,0,0,0.06); overflow: hidden;
|
||||||
|
}
|
||||||
|
.scad-card .card-header {
|
||||||
|
background: var(--scad-card-bg);
|
||||||
|
border-bottom: 1px solid var(--scad-card-border);
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
}
|
||||||
|
.scad-card .card-header h5 {
|
||||||
|
font-weight: 700; color: var(--scad-heading);
|
||||||
|
margin: 0; font-size: 1.05rem;
|
||||||
|
}
|
||||||
|
.scad-card .card-body { padding: 1.25rem; }
|
||||||
|
.btn-scad-outline {
|
||||||
|
background: transparent; border: 1.5px solid var(--scad-primary); color: var(--scad-primary);
|
||||||
|
font-weight: 600; font-size: 0.85rem; padding: 0.45rem 1rem; border-radius: 0.5rem; transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.btn-scad-outline:hover { background: var(--scad-primary); color: #fff; }
|
||||||
|
.scad-breadcrumb { background: transparent; padding: 0; margin-bottom: 1rem; }
|
||||||
|
.scad-breadcrumb .breadcrumb-item a { color: var(--scad-primary); text-decoration: none; font-weight: 500; }
|
||||||
|
.scad-breadcrumb .breadcrumb-item a:hover { color: var(--scad-primary-hover); }
|
||||||
|
.scad-breadcrumb .breadcrumb-item.active { color: #6c757d; font-weight: 600; }
|
||||||
|
|
||||||
|
/* FullCalendar overrides */
|
||||||
|
.fc { font-size: 0.9rem; }
|
||||||
|
.fc .fc-toolbar-title { font-size: 1.15rem; font-weight: 700; color: var(--scad-heading); }
|
||||||
|
.fc .fc-button-primary {
|
||||||
|
background: var(--scad-primary); border-color: var(--scad-primary);
|
||||||
|
font-weight: 600; font-size: 0.82rem; border-radius: 0.4rem;
|
||||||
|
}
|
||||||
|
.fc .fc-button-primary:hover { background: var(--scad-primary-hover); border-color: var(--scad-primary-hover); }
|
||||||
|
.fc .fc-button-primary:disabled { background: #9bbce6; border-color: #9bbce6; }
|
||||||
|
.fc .fc-button-primary:not(:disabled).fc-button-active { background: var(--scad-heading); border-color: var(--scad-heading); }
|
||||||
|
.fc .fc-daygrid-day-number { color: var(--scad-heading); font-weight: 500; }
|
||||||
|
.fc .fc-daygrid-day.fc-day-today { background: #f0f4ff; }
|
||||||
|
.fc .fc-event { border-radius: 0.3rem; padding: 2px 4px; font-weight: 600; cursor: pointer; }
|
||||||
|
.fc .fc-event:hover { filter: brightness(0.9); }
|
||||||
|
.fc .fc-list-event:hover td { background: #f0f4ff; }
|
||||||
|
|
||||||
|
/* Legend */
|
||||||
|
.legend { display: flex; flex-wrap: wrap; gap: 1rem; margin-bottom: 1rem; }
|
||||||
|
.legend-item { display: flex; align-items: center; gap: 0.4rem; font-size: 0.82rem; color: #6c757d; }
|
||||||
|
.legend-dot { width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0; }
|
||||||
|
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
.fc .fc-toolbar { flex-direction: column; gap: 0.5rem; }
|
||||||
|
.fc .fc-toolbar-title { font-size: 1rem; }
|
||||||
|
/* Stack list events vertically on mobile */
|
||||||
|
.fc .fc-list-table { display: block; }
|
||||||
|
.fc .fc-list-table tbody { display: block; }
|
||||||
|
.fc .fc-list-day { display: block; }
|
||||||
|
.fc .fc-list-day th { display: block; padding: 8px 12px; }
|
||||||
|
.fc .fc-list-event { display: flex; flex-direction: column; padding: 8px 12px; border-bottom: 1px solid #e8eeff; }
|
||||||
|
.fc .fc-list-event td { display: block; border: none; padding: 0; }
|
||||||
|
.fc .fc-list-event-time { font-size: 0.75rem; color: #8e99b0; order: 2; }
|
||||||
|
.fc .fc-list-event-graphic { display: none; }
|
||||||
|
.fc .fc-list-event-title { font-size: 0.9rem; word-break: break-word; white-space: normal; order: 1; margin-bottom: 2px; }
|
||||||
|
.fc .fc-list-event-dot { display: inline-block; margin-right: 6px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrapper" id="appWrapper">
|
||||||
|
<?php include('../include/navbar.php'); ?>
|
||||||
|
<?php include('../include/topbar.php'); ?>
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<div class="page-content">
|
||||||
|
|
||||||
|
<?php include(__DIR__ . '/include/my_deadlines_widget.php'); ?>
|
||||||
|
|
||||||
|
<div class="d-flex gap-2 mb-3 flex-wrap align-items-center">
|
||||||
|
<button type="button" class="btn btn-scad-outline d-inline-flex align-items-center gap-2" data-bs-toggle="modal" data-bs-target="#filtersModal">
|
||||||
|
<i class="fa-solid fa-filter"></i>
|
||||||
|
<span>Filtri</span>
|
||||||
|
<span id="filterCountBadge" class="badge bg-primary rounded-pill d-none" style="font-size:0.7rem">0</span>
|
||||||
|
</button>
|
||||||
|
<button id="btnResetFilters" type="button" class="btn btn-light border d-inline-flex align-items-center justify-content-center gap-1" title="Reset filtri" style="min-width:38px;height:38px">
|
||||||
|
<i class="fa-solid fa-rotate-left"></i>
|
||||||
|
<span class="d-none d-sm-inline">Reset</span>
|
||||||
|
</button>
|
||||||
|
<span id="activeFiltersSummary" class="text-muted small text-truncate d-none d-md-inline"></span>
|
||||||
|
</div>
|
||||||
|
<div id="activeFiltersSummaryMobile" class="text-muted small d-md-none mb-2" style="padding-left:0.25rem"></div>
|
||||||
|
|
||||||
|
<!-- Filters Modal -->
|
||||||
|
<div class="modal fade" id="filtersModal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title"><i class="fa-solid fa-filter me-2"></i>Filtri calendario</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Chiudi"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Stato</label>
|
||||||
|
<select id="filterStatus" class="form-select">
|
||||||
|
<option value="non-completata" selected>Non completate</option>
|
||||||
|
<option value="">Tutti</option>
|
||||||
|
<option value="attiva">Attive</option>
|
||||||
|
<option value="in-scadenza">In scadenza</option>
|
||||||
|
<option value="scaduta">Scadute</option>
|
||||||
|
<option value="completata">Completate</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Reparto</label>
|
||||||
|
<select id="filterDepartment" class="form-select">
|
||||||
|
<option value="">Tutti</option>
|
||||||
|
<?php foreach ($departments as $dept): ?>
|
||||||
|
<option value="<?= htmlspecialchars($dept, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($dept, ENT_QUOTES, 'UTF-8') ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-0">
|
||||||
|
<label class="form-label fw-semibold">Responsabile</label>
|
||||||
|
<select id="filterEmployee" class="form-select">
|
||||||
|
<option value="">Tutti</option>
|
||||||
|
<?php foreach ($employees as $emp): ?>
|
||||||
|
<option value="<?= htmlspecialchars(trim($emp['first_name'] . ' ' . $emp['last_name']), ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars(trim($emp['first_name'] . ' ' . $emp['last_name']), ENT_QUOTES, 'UTF-8') ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-light border" id="btnResetFiltersModal">
|
||||||
|
<i class="fa-solid fa-rotate-left me-1"></i> Reset
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-scad-primary" data-bs-dismiss="modal">
|
||||||
|
<i class="fa-solid fa-check me-1"></i> Applica
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card scad-card">
|
||||||
|
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||||
|
<h5 class="d-none d-md-flex align-items-center mb-0"><i class="fa-solid fa-calendar-days me-2"></i>Calendario Scadenze</h5>
|
||||||
|
<div class="header-actions d-flex gap-2 flex-wrap ms-auto">
|
||||||
|
<a href="scadenzario/index.php" class="btn btn-scad-outline d-inline-flex align-items-center gap-2">
|
||||||
|
<i class="fa-solid fa-list"></i><span>Lista Scadenze</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="legend">
|
||||||
|
<div class="legend-item"><span class="legend-dot" style="background:#5a8fd8"></span> Attiva</div>
|
||||||
|
<div class="legend-item"><span class="legend-dot" style="background:#e8930c"></span> In scadenza</div>
|
||||||
|
<div class="legend-item"><span class="legend-dot" style="background:#dc3545"></span> Scaduta</div>
|
||||||
|
<div class="legend-item"><span class="legend-dot" style="background:#198754"></span> Completata</div>
|
||||||
|
</div>
|
||||||
|
<div id="calendar"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php include('../include/footer.php'); ?>
|
||||||
|
</div>
|
||||||
|
<?php include('../jsinclude.php'); ?>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var isMobile = window.innerWidth < 768;
|
||||||
|
var calendarEl = document.getElementById('calendar');
|
||||||
|
var calendar = new FullCalendar.Calendar(calendarEl, {
|
||||||
|
locale: 'it',
|
||||||
|
initialView: isMobile ? 'listWeek' : 'dayGridMonth',
|
||||||
|
headerToolbar: {
|
||||||
|
left: 'prev,next today',
|
||||||
|
center: 'title',
|
||||||
|
right: isMobile ? 'listWeek,dayGridMonth' : 'dayGridMonth,listWeek'
|
||||||
|
},
|
||||||
|
height: 'auto',
|
||||||
|
navLinks: true,
|
||||||
|
eventSources: [{
|
||||||
|
url: 'scadenzario/ajax/get_calendar_events.php',
|
||||||
|
extraParams: function() {
|
||||||
|
return {
|
||||||
|
status: document.getElementById('filterStatus').value,
|
||||||
|
department: document.getElementById('filterDepartment').value,
|
||||||
|
employee: document.getElementById('filterEmployee').value
|
||||||
|
};
|
||||||
|
},
|
||||||
|
failure: function() {
|
||||||
|
Swal.fire('Errore', 'Impossibile caricare gli eventi.', 'error');
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
eventClick: function(info) {
|
||||||
|
info.jsEvent.preventDefault();
|
||||||
|
if (info.event.url) {
|
||||||
|
window.location.href = info.event.url;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
windowResize: function(view) {
|
||||||
|
if (window.innerWidth < 768) {
|
||||||
|
calendar.changeView('listWeek');
|
||||||
|
} else {
|
||||||
|
calendar.changeView('dayGridMonth');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
calendar.render();
|
||||||
|
|
||||||
|
// Filters
|
||||||
|
function updateFilterBadge() {
|
||||||
|
var active = 0;
|
||||||
|
var summary = [];
|
||||||
|
var st = document.getElementById('filterStatus');
|
||||||
|
var stv = st.value;
|
||||||
|
if (stv && stv !== 'non-completata') { active++; summary.push(st.options[st.selectedIndex].text); }
|
||||||
|
var dept = document.getElementById('filterDepartment').value;
|
||||||
|
if (dept) { active++; summary.push('Reparto: ' + dept); }
|
||||||
|
var emp = document.getElementById('filterEmployee').value;
|
||||||
|
if (emp) { active++; summary.push('Responsabile: ' + emp); }
|
||||||
|
|
||||||
|
var badge = document.getElementById('filterCountBadge');
|
||||||
|
if (active > 0) { badge.textContent = active; badge.classList.remove('d-none'); }
|
||||||
|
else { badge.classList.add('d-none'); }
|
||||||
|
|
||||||
|
var summaryText = summary.length ? summary.slice(0, 2).join(' • ') + (summary.length > 2 ? ' +' + (summary.length - 2) : '') : '';
|
||||||
|
document.getElementById('activeFiltersSummary').textContent = summaryText;
|
||||||
|
document.getElementById('activeFiltersSummaryMobile').textContent = summaryText;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('#filterStatus, #filterDepartment, #filterEmployee').forEach(function(el) {
|
||||||
|
el.addEventListener('change', function() { calendar.refetchEvents(); updateFilterBadge(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
function resetFilters() {
|
||||||
|
document.getElementById('filterStatus').value = 'non-completata';
|
||||||
|
document.getElementById('filterDepartment').value = '';
|
||||||
|
document.getElementById('filterEmployee').value = '';
|
||||||
|
calendar.refetchEvents();
|
||||||
|
updateFilterBadge();
|
||||||
|
}
|
||||||
|
document.getElementById('btnResetFilters').addEventListener('click', resetFilters);
|
||||||
|
document.getElementById('btnResetFiltersModal').addEventListener('click', resetFilters);
|
||||||
|
|
||||||
|
updateFilterBadge();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
221
public/userarea/scadenzario/cron/send_notifications.php
Normal file
221
public/userarea/scadenzario/cron/send_notifications.php
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Scadenzario — Email notification cron script
|
||||||
|
* Run daily: 0 7 * * * php /var/www/html/public/userarea/scadenzario/cron/send_notifications.php
|
||||||
|
*
|
||||||
|
* Sends "approaching" emails N days before due_date (per-deadline notification_days).
|
||||||
|
* Sends "overdue" emails when due_date has passed.
|
||||||
|
* Skips completed deadlines and already-sent notifications (same deadline+employee+type+date).
|
||||||
|
* Email is taken from employees.auth_user_id → auth_users.email.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../../class/db-functions.php';
|
||||||
|
require_once __DIR__ . '/../../../../vendor/autoload.php';
|
||||||
|
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
use PHPMailer\PHPMailer\PHPMailer;
|
||||||
|
use PHPMailer\PHPMailer\Exception;
|
||||||
|
|
||||||
|
$dotenv = Dotenv::createImmutable(__DIR__ . '/../../../../');
|
||||||
|
$dotenv->load();
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$today = date('Y-m-d');
|
||||||
|
$appUrl = rtrim($_ENV['APP_URL'] ?? 'http://localhost:8001', '/');
|
||||||
|
|
||||||
|
$sent = 0;
|
||||||
|
$skipped = 0;
|
||||||
|
$errors = 0;
|
||||||
|
|
||||||
|
// Get active deadlines that are approaching or overdue
|
||||||
|
$stmt = $pdo->query("
|
||||||
|
SELECT d.id, d.topic, s.name AS subject_name, d.due_date, d.notification_days
|
||||||
|
FROM scad_deadlines d
|
||||||
|
LEFT JOIN scad_subjects s ON s.id = d.subject_id
|
||||||
|
WHERE d.status = 'active'
|
||||||
|
AND d.due_date <= DATE_ADD(CURDATE(), INTERVAL d.notification_days DAY)
|
||||||
|
");
|
||||||
|
$deadlines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (empty($deadlines)) {
|
||||||
|
echo date('Y-m-d H:i:s') . " — Nessuna scadenza da notificare.\n";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare statements
|
||||||
|
$getRecipients = $pdo->prepare("
|
||||||
|
SELECT DISTINCT e.id as employee_id, au.email, e.first_name, e.last_name
|
||||||
|
FROM scad_deadline_employee de
|
||||||
|
JOIN employees e ON e.id = de.employee_id
|
||||||
|
JOIN auth_users au ON au.id = e.auth_user_id
|
||||||
|
WHERE de.deadline_id = ?
|
||||||
|
AND e.auth_user_id IS NOT NULL
|
||||||
|
AND au.email IS NOT NULL
|
||||||
|
AND au.email != ''
|
||||||
|
");
|
||||||
|
|
||||||
|
// Also get employees from assigned departments
|
||||||
|
$getDeptRecipients = $pdo->prepare("
|
||||||
|
SELECT DISTINCT e.id as employee_id, au.email, e.first_name, e.last_name
|
||||||
|
FROM employees e
|
||||||
|
JOIN auth_users au ON au.id = e.auth_user_id
|
||||||
|
WHERE e.department IN (SELECT TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(d.departments, ',', n.n), ',', -1))
|
||||||
|
FROM scad_deadlines d
|
||||||
|
CROSS JOIN (SELECT 1 n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) n
|
||||||
|
WHERE d.id = ?
|
||||||
|
AND d.departments IS NOT NULL
|
||||||
|
AND n.n <= 1 + LENGTH(d.departments) - LENGTH(REPLACE(d.departments, ',', '')))
|
||||||
|
AND e.auth_user_id IS NOT NULL
|
||||||
|
AND au.email IS NOT NULL
|
||||||
|
AND au.email != ''
|
||||||
|
");
|
||||||
|
|
||||||
|
$checkSent = $pdo->prepare("
|
||||||
|
SELECT COUNT(*) FROM scad_deadline_notifications
|
||||||
|
WHERE deadline_id = ? AND employee_id = ? AND type = ? AND DATE(sent_at) = CURDATE()
|
||||||
|
");
|
||||||
|
|
||||||
|
$insertNotif = $pdo->prepare("
|
||||||
|
INSERT INTO scad_deadline_notifications (deadline_id, employee_id, type) VALUES (?, ?, ?)
|
||||||
|
");
|
||||||
|
|
||||||
|
$insertHistory = $pdo->prepare("
|
||||||
|
INSERT INTO scad_deadline_histories (deadline_id, user_id, action, notes) VALUES (?, NULL, 'notification_sent', ?)
|
||||||
|
");
|
||||||
|
|
||||||
|
foreach ($deadlines as $dl) {
|
||||||
|
$isOverdue = $dl['due_date'] < $today;
|
||||||
|
$type = $isOverdue ? 'overdue' : 'approaching';
|
||||||
|
$daysLeft = (int)((strtotime($dl['due_date']) - strtotime($today)) / 86400);
|
||||||
|
|
||||||
|
// Collect all recipients (direct + department)
|
||||||
|
$recipients = [];
|
||||||
|
|
||||||
|
$getRecipients->execute([$dl['id']]);
|
||||||
|
foreach ($getRecipients->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
||||||
|
$recipients[$r['employee_id']] = $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
$getDeptRecipients->execute([$dl['id']]);
|
||||||
|
foreach ($getDeptRecipients->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
||||||
|
$recipients[$r['employee_id']] = $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($recipients)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($recipients as $emp) {
|
||||||
|
// Check if already sent today
|
||||||
|
$checkSent->execute([$dl['id'], $emp['employee_id'], $type]);
|
||||||
|
if ($checkSent->fetchColumn() > 0) {
|
||||||
|
$skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send email
|
||||||
|
try {
|
||||||
|
$mail = new PHPMailer(true);
|
||||||
|
|
||||||
|
// SMTP config from .env
|
||||||
|
$mailer = $_ENV['MAIL_MAILER'] ?? 'mail';
|
||||||
|
if ($mailer === 'smtp') {
|
||||||
|
$mail->isSMTP();
|
||||||
|
$mail->Host = $_ENV['MAIL_HOST'] ?? 'localhost';
|
||||||
|
$mail->Port = (int)($_ENV['MAIL_PORT'] ?? 587);
|
||||||
|
if (!empty($_ENV['MAIL_USERNAME']) && $_ENV['MAIL_USERNAME'] !== 'null') {
|
||||||
|
$mail->SMTPAuth = true;
|
||||||
|
$mail->Username = $_ENV['MAIL_USERNAME'];
|
||||||
|
$mail->Password = $_ENV['MAIL_PASSWORD'] ?? '';
|
||||||
|
}
|
||||||
|
$enc = $_ENV['MAIL_ENCRYPTION'] ?? '';
|
||||||
|
if ($enc && $enc !== 'null') {
|
||||||
|
$mail->SMTPSecure = $enc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail->CharSet = 'UTF-8';
|
||||||
|
$mail->setFrom(
|
||||||
|
$_ENV['MAIL_FROM_ADDRESS'] ?? 'noreply@zibogomma.it',
|
||||||
|
$_ENV['MAIL_FROM_NAME'] ?? 'Scadenzario ZIBOGOMMA'
|
||||||
|
);
|
||||||
|
$mail->addAddress($emp['email'], trim($emp['first_name'] . ' ' . $emp['last_name']));
|
||||||
|
|
||||||
|
$detailUrl = $appUrl . '/userarea/scadenzario/detail.php?id=' . $dl['id'];
|
||||||
|
$topicText = (!empty($dl['subject_name']) ? $dl['subject_name'] . ' — ' : '') . $dl['topic'];
|
||||||
|
|
||||||
|
if ($isOverdue) {
|
||||||
|
$mail->Subject = '⚠️ Scadenza superata: ' . $dl['topic'];
|
||||||
|
$mail->Body = buildHtml(
|
||||||
|
'Scadenza superata',
|
||||||
|
$topicText,
|
||||||
|
'La scadenza era prevista per il <strong>' . date('d/m/Y', strtotime($dl['due_date'])) . '</strong> ed è stata superata da <strong>' . abs($daysLeft) . ' giorni</strong>.',
|
||||||
|
'#dc3545',
|
||||||
|
$detailUrl
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$mail->Subject = '📅 Scadenza in arrivo: ' . $dl['topic'];
|
||||||
|
$daysText = $daysLeft === 0 ? 'oggi' : 'tra <strong>' . $daysLeft . ' giorni</strong>';
|
||||||
|
$mail->Body = buildHtml(
|
||||||
|
'Scadenza in arrivo',
|
||||||
|
$topicText,
|
||||||
|
'La scadenza è prevista per il <strong>' . date('d/m/Y', strtotime($dl['due_date'])) . '</strong> (' . $daysText . ').',
|
||||||
|
'#e8930c',
|
||||||
|
$detailUrl
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail->isHTML(true);
|
||||||
|
$mail->AltBody = strip_tags(str_replace('<br>', "\n", $mail->Body));
|
||||||
|
|
||||||
|
$mail->send();
|
||||||
|
|
||||||
|
// Record notification
|
||||||
|
$insertNotif->execute([$dl['id'], $emp['employee_id'], $type]);
|
||||||
|
$sent++;
|
||||||
|
|
||||||
|
echo date('H:i:s') . " ✓ {$type} → {$emp['email']} — {$dl['topic']}\n";
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$errors++;
|
||||||
|
echo date('H:i:s') . " ✗ Errore {$emp['email']}: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// History (one per deadline, not per recipient)
|
||||||
|
$recipientNames = implode(', ', array_map(fn($r) => trim($r['first_name'] . ' ' . $r['last_name']), $recipients));
|
||||||
|
$insertHistory->execute([$dl['id'], "Notifica {$type} inviata a: {$recipientNames}"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n" . date('Y-m-d H:i:s') . " — Completato. Inviate: {$sent}, Saltate: {$skipped}, Errori: {$errors}\n";
|
||||||
|
|
||||||
|
// --- HTML email template ---
|
||||||
|
function buildHtml(string $title, string $topic, string $message, string $accentColor, string $url): string
|
||||||
|
{
|
||||||
|
return '
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><meta charset="UTF-8"></head>
|
||||||
|
<body style="margin:0;padding:0;background:#f4f6f9;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif">
|
||||||
|
<table width="100%" cellpadding="0" cellspacing="0" style="padding:30px 0">
|
||||||
|
<tr><td align="center">
|
||||||
|
<table width="560" cellpadding="0" cellspacing="0" style="background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.06)">
|
||||||
|
<tr><td style="background:' . $accentColor . ';padding:20px 30px">
|
||||||
|
<h1 style="margin:0;color:#fff;font-size:18px">' . htmlspecialchars($title) . '</h1>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td style="padding:30px">
|
||||||
|
<h2 style="margin:0 0 15px;color:#2c3e6b;font-size:16px">' . htmlspecialchars($topic) . '</h2>
|
||||||
|
<p style="margin:0 0 20px;color:#444;font-size:14px;line-height:1.6">' . $message . '</p>
|
||||||
|
<a href="' . htmlspecialchars($url) . '" style="display:inline-block;background:#5a8fd8;color:#fff;padding:10px 24px;border-radius:6px;text-decoration:none;font-weight:600;font-size:14px">Vai alla scadenza</a>
|
||||||
|
</td></tr>
|
||||||
|
<tr><td style="padding:15px 30px;background:#f8f9fb;border-top:1px solid #eee">
|
||||||
|
<p style="margin:0;color:#999;font-size:11px">ZIBOGOMMA — Scadenzario</p>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>';
|
||||||
|
}
|
||||||
476
public/userarea/scadenzario/detail.php
Normal file
476
public/userarea/scadenzario/detail.php
Normal file
@ -0,0 +1,476 @@
|
|||||||
|
<?php include('../include/headscript.php'); ?>
|
||||||
|
<?php
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
$error = null;
|
||||||
|
$deadline = null;
|
||||||
|
|
||||||
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
|
$error = 'ID non valido.';
|
||||||
|
} else {
|
||||||
|
$id = (int)$_GET['id'];
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT d.*, s.name AS subject_name, s.color AS subject_color
|
||||||
|
FROM scad_deadlines d
|
||||||
|
LEFT JOIN scad_subjects s ON s.id = d.subject_id
|
||||||
|
WHERE d.id = ?
|
||||||
|
");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$deadline = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$deadline) {
|
||||||
|
$error = 'Scadenza non trovata.';
|
||||||
|
} else {
|
||||||
|
$empStmt = $pdo->prepare("
|
||||||
|
SELECT e.first_name, e.last_name, e.department
|
||||||
|
FROM scad_deadline_employee de
|
||||||
|
JOIN employees e ON e.id = de.employee_id
|
||||||
|
WHERE de.deadline_id = ?
|
||||||
|
ORDER BY e.first_name
|
||||||
|
");
|
||||||
|
$empStmt->execute([$id]);
|
||||||
|
$employees = $empStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$attStmt = $pdo->prepare("SELECT * FROM scad_deadline_attachments WHERE deadline_id = ? ORDER BY created_at DESC");
|
||||||
|
$attStmt->execute([$id]);
|
||||||
|
$attachments = $attStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$histStmt = $pdo->prepare("
|
||||||
|
SELECT h.*, au.first_name as user_fname, au.last_name as user_lname
|
||||||
|
FROM scad_deadline_histories h
|
||||||
|
LEFT JOIN auth_users au ON au.id = h.user_id
|
||||||
|
WHERE h.deadline_id = ?
|
||||||
|
ORDER BY h.created_at DESC
|
||||||
|
");
|
||||||
|
$histStmt->execute([$id]);
|
||||||
|
$history = $histStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$today = date('Y-m-d');
|
||||||
|
$isCompleted = $deadline['status'] === 'completed';
|
||||||
|
$isOverdue = !$isCompleted && $deadline['due_date'] < $today;
|
||||||
|
$approachDate = date('Y-m-d', strtotime($today . ' + ' . (int)$deadline['notification_days'] . ' days'));
|
||||||
|
$isApproaching = !$isCompleted && !$isOverdue && $deadline['due_date'] <= $approachDate;
|
||||||
|
|
||||||
|
if ($isCompleted) { $statusLabel = 'Completata'; $statusClass = 'badge-completata'; }
|
||||||
|
elseif ($isOverdue) { $statusLabel = 'Scaduta'; $statusClass = 'badge-scaduta'; }
|
||||||
|
elseif ($isApproaching) { $statusLabel = 'In scadenza'; $statusClass = 'badge-in-scadenza'; }
|
||||||
|
else { $statusLabel = 'Attiva'; $statusClass = 'badge-attiva'; }
|
||||||
|
|
||||||
|
$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'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<?php
|
||||||
|
$scriptDir = dirname($_SERVER['SCRIPT_NAME']);
|
||||||
|
$baseHref = dirname($scriptDir) . '/';
|
||||||
|
?>
|
||||||
|
<base href="<?= $baseHref ?>">
|
||||||
|
<?php include('../cssinclude.php'); ?>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<title><?= $deadline ? htmlspecialchars($deadline['topic'], ENT_QUOTES, 'UTF-8') . ' — ' : '' ?>Scadenzario</title>
|
||||||
|
<script>if(window.innerWidth>1024)document.addEventListener('DOMContentLoaded',function(){document.getElementById('appWrapper').classList.add('toggled')})</script>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--scad-primary: #5a8fd8;
|
||||||
|
--scad-primary-hover: #4578c0;
|
||||||
|
--scad-heading: #2c3e6b;
|
||||||
|
--scad-card-bg: linear-gradient(135deg, #f0f4ff 0%, #e8eeff 100%);
|
||||||
|
--scad-card-border: #dde4f0;
|
||||||
|
--scad-red: #dc3545;
|
||||||
|
--scad-orange: #e8930c;
|
||||||
|
--scad-green: #198754;
|
||||||
|
}
|
||||||
|
.scad-card {
|
||||||
|
border: none; border-radius: 0.75rem;
|
||||||
|
box-shadow: 0 2px 12px rgba(0,0,0,0.06); overflow: hidden;
|
||||||
|
}
|
||||||
|
.scad-card .card-header {
|
||||||
|
background: var(--scad-card-bg);
|
||||||
|
border-bottom: 1px solid var(--scad-card-border);
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
}
|
||||||
|
.scad-card .card-header h5 {
|
||||||
|
font-weight: 700; color: var(--scad-heading);
|
||||||
|
margin: 0; font-size: 1.05rem;
|
||||||
|
}
|
||||||
|
.scad-card .card-body { padding: 1.25rem; }
|
||||||
|
|
||||||
|
.btn-scad-primary {
|
||||||
|
background: var(--scad-primary); border: none; color: #fff;
|
||||||
|
font-weight: 600; font-size: 0.85rem; padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.5rem; transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.btn-scad-primary:hover { background: var(--scad-primary-hover); color: #fff; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(90,143,216,0.35); }
|
||||||
|
.btn-scad-outline {
|
||||||
|
background: transparent; border: 1.5px solid var(--scad-primary); color: var(--scad-primary);
|
||||||
|
font-weight: 600; font-size: 0.85rem; padding: 0.45rem 1rem; border-radius: 0.5rem; transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.btn-scad-outline:hover { background: var(--scad-primary); color: #fff; transform: translateY(-1px); }
|
||||||
|
.btn-scad-green {
|
||||||
|
background: var(--scad-green); border: none; color: #fff;
|
||||||
|
font-weight: 600; font-size: 0.85rem; padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.5rem; transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.btn-scad-green:hover { background: #157347; color: #fff; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(25,135,84,0.35); }
|
||||||
|
|
||||||
|
.badge-status {
|
||||||
|
font-weight: 600; font-size: 0.8rem; padding: 0.4em 0.75em;
|
||||||
|
border-radius: 2rem; display: inline-block;
|
||||||
|
}
|
||||||
|
.badge-attiva { background: #e8eeff; color: #3a6bb5; }
|
||||||
|
.badge-scaduta { background: #fde8e8; color: #b91c1c; }
|
||||||
|
.badge-in-scadenza{ background: #fef3cd; color: #92600a; }
|
||||||
|
.badge-completata { background: #d1f2e0; color: #0f5132; }
|
||||||
|
|
||||||
|
.detail-label {
|
||||||
|
font-size: 0.75rem; font-weight: 700; text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em; color: #8e99b0; margin-bottom: 0.2rem;
|
||||||
|
}
|
||||||
|
.detail-value {
|
||||||
|
font-size: 0.95rem; color: var(--scad-heading); margin-bottom: 1rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.detail-value.text-danger-date { color: var(--scad-red); font-weight: 600; }
|
||||||
|
.detail-value.text-warning-date { color: var(--scad-orange); font-weight: 600; }
|
||||||
|
|
||||||
|
.person-chip {
|
||||||
|
display: inline-flex; align-items: center; gap: 0.4rem;
|
||||||
|
background: #f0f4ff; border: 1px solid #dde4f0; border-radius: 2rem;
|
||||||
|
padding: 0.3rem 0.75rem 0.3rem 0.5rem; font-size: 0.85rem;
|
||||||
|
margin: 0.2rem 0.15rem; color: var(--scad-heading);
|
||||||
|
}
|
||||||
|
.person-chip i { color: var(--scad-primary); font-size: 0.75rem; }
|
||||||
|
.person-chip .chip-dept { color: #8e99b0; font-size: 0.78rem; }
|
||||||
|
.dept-chip {
|
||||||
|
display: inline-flex; align-items: center; gap: 0.4rem;
|
||||||
|
background: #eef6ee; border: 1px solid #c8e6c9; border-radius: 2rem;
|
||||||
|
padding: 0.3rem 0.75rem 0.3rem 0.5rem; font-size: 0.85rem;
|
||||||
|
margin: 0.2rem 0.15rem; color: #2e5e2e;
|
||||||
|
}
|
||||||
|
.dept-chip i { color: #4caf50; font-size: 0.75rem; }
|
||||||
|
|
||||||
|
/* Attachments */
|
||||||
|
.att-row {
|
||||||
|
display: flex; align-items: center; gap: 0.75rem;
|
||||||
|
padding: 0.65rem 0; border-bottom: 1px solid #f0f2f5;
|
||||||
|
}
|
||||||
|
.att-row:last-child { border-bottom: none; }
|
||||||
|
.att-icon { width: 36px; height: 36px; border-radius: 0.4rem; display: flex; align-items: center; justify-content: center; font-size: 1rem; flex-shrink: 0; }
|
||||||
|
.att-icon-pdf { background: #fde8e8; color: #b91c1c; }
|
||||||
|
.att-icon-img { background: #e8f5e9; color: #2e7d32; }
|
||||||
|
.att-icon-file { background: #e8eeff; color: #3a6bb5; }
|
||||||
|
.att-info { flex: 1; min-width: 0; }
|
||||||
|
.att-name { font-weight: 600; color: var(--scad-heading); font-size: 0.9rem; text-decoration: none; word-break: break-all; }
|
||||||
|
.att-name:hover { color: var(--scad-primary); }
|
||||||
|
.att-meta { font-size: 0.78rem; color: #8e99b0; }
|
||||||
|
|
||||||
|
/* Timeline */
|
||||||
|
.timeline { position: relative; padding-left: 2rem; }
|
||||||
|
.timeline::before {
|
||||||
|
content: ''; position: absolute; left: 0.55rem; top: 0.5rem; bottom: 0.5rem;
|
||||||
|
width: 2px; background: #e2e8f0;
|
||||||
|
}
|
||||||
|
.timeline-item { position: relative; padding-bottom: 1.25rem; }
|
||||||
|
.timeline-item:last-child { padding-bottom: 0; }
|
||||||
|
.timeline-dot {
|
||||||
|
position: absolute; left: -1.7rem; top: 0.15rem;
|
||||||
|
width: 22px; height: 22px; border-radius: 50%;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
font-size: 0.6rem; color: #fff; z-index: 1;
|
||||||
|
box-shadow: 0 0 0 3px #fff;
|
||||||
|
}
|
||||||
|
.timeline-header {
|
||||||
|
display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
|
||||||
|
margin-bottom: 0.15rem;
|
||||||
|
}
|
||||||
|
.timeline-action { font-weight: 700; font-size: 0.88rem; color: var(--scad-heading); }
|
||||||
|
.timeline-user { font-size: 0.82rem; color: #6c757d; }
|
||||||
|
.timeline-date { font-size: 0.78rem; color: #adb5bd; }
|
||||||
|
.timeline-notes { font-size: 0.83rem; color: #6c757d; margin-top: 0.15rem; }
|
||||||
|
.timeline-changes { font-size: 0.82rem; margin-top: 0.3rem; background: #f8f9fb; border-radius: 0.4rem; padding: 0.5rem 0.75rem; }
|
||||||
|
.timeline-changes .change-field { font-weight: 600; color: var(--scad-heading); }
|
||||||
|
.timeline-changes .change-old { color: var(--scad-red); text-decoration: line-through; }
|
||||||
|
.timeline-changes .change-new { color: var(--scad-green); }
|
||||||
|
|
||||||
|
.scad-breadcrumb { background: transparent; padding: 0; margin-bottom: 1rem; }
|
||||||
|
.scad-breadcrumb .breadcrumb-item a { color: var(--scad-primary); text-decoration: none; font-weight: 500; transition: color 0.2s; }
|
||||||
|
.scad-breadcrumb .breadcrumb-item a:hover { color: var(--scad-primary-hover); }
|
||||||
|
.scad-breadcrumb .breadcrumb-item.active { color: #6c757d; font-weight: 600; }
|
||||||
|
|
||||||
|
@media (max-width: 575.98px) {
|
||||||
|
.action-bar { flex-direction: column; }
|
||||||
|
.action-bar .btn { width: 100%; justify-content: center; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.sidebar-wrapper, .topbar, .page-footer, .action-bar, .scad-breadcrumb { display: none !important; }
|
||||||
|
.page-wrapper { margin: 0 !important; }
|
||||||
|
.scad-card { box-shadow: none; border: 1px solid #ddd; }
|
||||||
|
@page { size: portrait; margin: 1cm; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrapper" id="appWrapper">
|
||||||
|
<?php include('../include/navbar.php'); ?>
|
||||||
|
<?php include('../include/topbar.php'); ?>
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<div class="page-content">
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<i class="fa-solid fa-triangle-exclamation me-2"></i><?= htmlspecialchars($error, ENT_QUOTES, 'UTF-8') ?>
|
||||||
|
<a href="scadenzario/index.php" class="alert-link ms-2">Torna alla lista</a>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<!-- Breadcrumb -->
|
||||||
|
<nav class="scad-breadcrumb" aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb mb-0">
|
||||||
|
<li class="breadcrumb-item"><a href="scadenzario/index.php">Scadenzario</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="scadenzario/index.php">Lista Scadenze</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page"><?= htmlspecialchars($deadline['topic'], ENT_QUOTES, 'UTF-8') ?></li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Action Bar -->
|
||||||
|
<div class="action-bar d-flex gap-2 mb-3 flex-wrap">
|
||||||
|
<a href="scadenzario/index.php" class="btn btn-scad-outline d-inline-flex align-items-center gap-2">
|
||||||
|
<i class="fa-solid fa-arrow-left"></i><span>Torna alla lista</span>
|
||||||
|
</a>
|
||||||
|
<?php if (!$isCompleted): ?>
|
||||||
|
<button class="btn btn-scad-primary d-inline-flex align-items-center gap-2" id="btnModifica">
|
||||||
|
<i class="fa-solid fa-pen"></i><span>Modifica</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-scad-green d-inline-flex align-items-center gap-2" id="btnCompleta">
|
||||||
|
<i class="fa-solid fa-check"></i><span>Completa</span>
|
||||||
|
</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
<button class="btn btn-scad-outline d-inline-flex align-items-center gap-2" onclick="window.print()">
|
||||||
|
<i class="fa-solid fa-print"></i><span>Stampa</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Main Detail Card -->
|
||||||
|
<div class="card scad-card mb-3">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5><i class="fa-solid fa-file-lines me-2"></i>Dettagli Scadenza</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<!-- Left Column -->
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<?php if (!empty($deadline['subject_name'])): ?>
|
||||||
|
<div class="detail-label">Argomento</div>
|
||||||
|
<div class="detail-value">
|
||||||
|
<span style="display:inline-block;padding:0.25rem 0.7rem;border-radius:1rem;color:#fff;font-weight:600;font-size:0.85rem;background: <?= htmlspecialchars($deadline['subject_color'] ?: '#6c757d', ENT_QUOTES, 'UTF-8') ?>">
|
||||||
|
<?= htmlspecialchars($deadline['subject_name'], ENT_QUOTES, 'UTF-8') ?>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="detail-label">Dettaglio</div>
|
||||||
|
<div class="detail-value" style="font-size:1.15rem; font-weight:700;">
|
||||||
|
<?= htmlspecialchars($deadline['topic'], ENT_QUOTES, 'UTF-8') ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($deadline['law_regulation']): ?>
|
||||||
|
<div class="detail-label">Legge / Articolo</div>
|
||||||
|
<div class="detail-value"><?= htmlspecialchars($deadline['law_regulation'], ENT_QUOTES, 'UTF-8') ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="detail-label">Periodicità</div>
|
||||||
|
<div class="detail-value"><?= htmlspecialchars($recurrenceLabels[$deadline['recurrence_type']] ?? $deadline['recurrence_type'], ENT_QUOTES, 'UTF-8') ?></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right Column -->
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<div class="detail-label">Stato</div>
|
||||||
|
<div class="detail-value">
|
||||||
|
<span class="badge-status <?= $statusClass ?>"><?= $statusLabel ?></span>
|
||||||
|
<?php if ($isCompleted && $deadline['completed_at']): ?>
|
||||||
|
<span class="text-muted ms-2" style="font-size:0.82rem"><?= date('d/m/Y H:i', strtotime($deadline['completed_at'])) ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detail-label">Data scadenza</div>
|
||||||
|
<div class="detail-value <?= $isOverdue ? 'text-danger-date' : ($isApproaching ? 'text-warning-date' : '') ?>">
|
||||||
|
<i class="fa-regular fa-calendar me-1"></i><?= date('d/m/Y', strtotime($deadline['due_date'])) ?>
|
||||||
|
<?php if ($isOverdue): ?><span class="ms-1" style="font-size:0.8rem">(scaduta)</span><?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($deadline['document_date']): ?>
|
||||||
|
<div class="detail-label">Data documento</div>
|
||||||
|
<div class="detail-value"><?= date('d/m/Y', strtotime($deadline['document_date'])) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="detail-label">Data ultimo controllo</div>
|
||||||
|
<div class="detail-value"><?= $deadline['check_date'] ? date('d/m/Y', strtotime($deadline['check_date'])) : '—' ?></div>
|
||||||
|
|
||||||
|
<div class="detail-label">Giorni preavviso notifica</div>
|
||||||
|
<div class="detail-value"><?= (int)$deadline['notification_days'] ?> giorni</div>
|
||||||
|
|
||||||
|
<?php if ($deadline['storage_location']): ?>
|
||||||
|
<div class="detail-label">Luogo archiviazione</div>
|
||||||
|
<div class="detail-value"><i class="fa-regular fa-folder-open me-1"></i><?= htmlspecialchars($deadline['storage_location'], ENT_QUOTES, 'UTF-8') ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($deadline['notes']): ?>
|
||||||
|
<div class="detail-label">Note</div>
|
||||||
|
<div class="detail-value"><?= nl2br(htmlspecialchars($deadline['notes'], ENT_QUOTES, 'UTF-8')) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Responsabili -->
|
||||||
|
<?php if ($deadline['departments'] || !empty($employees)): ?>
|
||||||
|
<hr class="my-3" style="border-color:#e8eeff">
|
||||||
|
<?php if ($deadline['departments']): ?>
|
||||||
|
<div class="detail-label">Reparti responsabili</div>
|
||||||
|
<div class="detail-value">
|
||||||
|
<?php foreach (array_map('trim', explode(',', $deadline['departments'])) as $dept): ?>
|
||||||
|
<span class="dept-chip"><i class="fa-solid fa-building"></i><?= htmlspecialchars($dept, ENT_QUOTES, 'UTF-8') ?></span>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (!empty($employees)): ?>
|
||||||
|
<div class="detail-label">Singoli responsabili</div>
|
||||||
|
<div class="detail-value">
|
||||||
|
<?php foreach ($employees as $emp): ?>
|
||||||
|
<span class="person-chip">
|
||||||
|
<i class="fa-solid fa-user"></i>
|
||||||
|
<?= htmlspecialchars(trim($emp['first_name'] . ' ' . $emp['last_name']), ENT_QUOTES, 'UTF-8') ?>
|
||||||
|
<?php if ($emp['department']): ?>
|
||||||
|
<span class="chip-dept">(<?= htmlspecialchars($emp['department'], ENT_QUOTES, 'UTF-8') ?>)</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</span>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Attachments Card -->
|
||||||
|
<?php if (!empty($attachments)): ?>
|
||||||
|
<div class="card scad-card mb-3">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5><i class="fa-solid fa-paperclip me-2"></i>Allegati (<?= count($attachments) ?>)</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<?php foreach ($attachments as $att):
|
||||||
|
$mime = $att['mime_type'] ?? '';
|
||||||
|
if (strpos($mime, 'pdf') !== false) { $iconClass = 'att-icon-pdf'; $icon = 'fa-file-pdf'; }
|
||||||
|
elseif (strpos($mime, 'image') !== false) { $iconClass = 'att-icon-img'; $icon = 'fa-file-image'; }
|
||||||
|
else { $iconClass = 'att-icon-file'; $icon = 'fa-file'; }
|
||||||
|
$sizeKB = round(($att['size'] ?? 0) / 1024, 1);
|
||||||
|
$sizeStr = $sizeKB >= 1024 ? round($sizeKB / 1024, 1) . ' MB' : $sizeKB . ' KB';
|
||||||
|
?>
|
||||||
|
<div class="att-row">
|
||||||
|
<div class="att-icon <?= $iconClass ?>"><i class="fa-solid <?= $icon ?>"></i></div>
|
||||||
|
<div class="att-info">
|
||||||
|
<a href="scadenzario/ajax/download_attachment.php?id=<?= (int)$att['id'] ?>" class="att-name"><?= htmlspecialchars($att['original_name'], ENT_QUOTES, 'UTF-8') ?></a>
|
||||||
|
<div class="att-meta"><?= $sizeStr ?> · <?= date('d/m/Y', strtotime($att['created_at'])) ?></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<!-- History Card -->
|
||||||
|
<?php if (!empty($history)): ?>
|
||||||
|
<div class="card scad-card mb-3">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5><i class="fa-solid fa-clock-rotate-left me-2"></i>Cronologia</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="timeline">
|
||||||
|
<?php foreach ($history as $h):
|
||||||
|
$color = $actionColors[$h['action']] ?? '#adb5bd';
|
||||||
|
$iconCls = $actionIcons[$h['action']] ?? 'fa-circle';
|
||||||
|
$label = $actionLabels[$h['action']] ?? $h['action'];
|
||||||
|
$userName = trim(($h['user_fname'] ?? '') . ' ' . ($h['user_lname'] ?? '')) ?: 'Sistema';
|
||||||
|
$changes = $h['changes'] ? json_decode($h['changes'], true) : null;
|
||||||
|
?>
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-dot" style="background:<?= $color ?>"><i class="fa-solid <?= $iconCls ?>"></i></div>
|
||||||
|
<div class="timeline-header">
|
||||||
|
<span class="timeline-action"><?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?></span>
|
||||||
|
<span class="timeline-user">da <?= htmlspecialchars($userName, ENT_QUOTES, 'UTF-8') ?></span>
|
||||||
|
<span class="timeline-date"><?= date('d/m/Y H:i', strtotime($h['created_at'])) ?></span>
|
||||||
|
</div>
|
||||||
|
<?php if ($h['notes']): ?>
|
||||||
|
<div class="timeline-notes"><i class="fa-regular fa-comment me-1"></i><?= htmlspecialchars($h['notes'], ENT_QUOTES, 'UTF-8') ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($changes && is_array($changes)): ?>
|
||||||
|
<div class="timeline-changes">
|
||||||
|
<?php foreach ($changes as $field => $vals): ?>
|
||||||
|
<div>
|
||||||
|
<span class="change-field"><?= htmlspecialchars($field, ENT_QUOTES, 'UTF-8') ?>:</span>
|
||||||
|
<span class="change-old"><?= htmlspecialchars($vals['old'] ?? '—', ENT_QUOTES, 'UTF-8') ?></span>
|
||||||
|
→
|
||||||
|
<span class="change-new"><?= htmlspecialchars($vals['new'] ?? '—', ENT_QUOTES, 'UTF-8') ?></span>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php include('../include/footer.php'); ?>
|
||||||
|
</div>
|
||||||
|
<?php include('../jsinclude.php'); ?>
|
||||||
|
<?php if ($deadline && !$isCompleted): ?>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#btnModifica').on('click', function() {
|
||||||
|
window.location.href = 'scadenzario/index.php?edit=<?= (int)$deadline['id'] ?>';
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#btnCompleta').on('click', function() {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Completare la scadenza?',
|
||||||
|
icon: 'question',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: '#198754',
|
||||||
|
cancelButtonText: 'Annulla',
|
||||||
|
confirmButtonText: 'Completa'
|
||||||
|
}).then(function(result) {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
fetch('scadenzario/ajax/complete_deadline.php?id=<?= (int)$deadline['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'); });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php endif; ?>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
91
public/userarea/scadenzario/include/my_deadlines_widget.php
Normal file
91
public/userarea/scadenzario/include/my_deadlines_widget.php
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Renders two status banners for the current user:
|
||||||
|
* - red -> overdue deadlines (scaduta)
|
||||||
|
* - orange -> approaching deadlines (in scadenza)
|
||||||
|
* Scope: deadlines assigned directly to the user OR to their department.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$_empStmt = $pdo->prepare("SELECT id, department FROM employees WHERE auth_user_id = ? LIMIT 1");
|
||||||
|
$_empStmt->execute([(int)$iduserlogin]);
|
||||||
|
$_emp = $_empStmt->fetch(PDO::FETCH_ASSOC) ?: null;
|
||||||
|
|
||||||
|
$_overdue = 0;
|
||||||
|
$_approaching = 0;
|
||||||
|
|
||||||
|
if ($_emp) {
|
||||||
|
$_empId = (int)$_emp['id'];
|
||||||
|
$_deptRaw = (string)($_emp['department'] ?? '');
|
||||||
|
$_dept = trim($_deptRaw);
|
||||||
|
|
||||||
|
$_sql = "
|
||||||
|
SELECT
|
||||||
|
SUM(CASE WHEN d.due_date < CURDATE() THEN 1 ELSE 0 END) AS overdue_cnt,
|
||||||
|
SUM(CASE WHEN d.due_date >= CURDATE()
|
||||||
|
AND d.due_date <= DATE_ADD(CURDATE(), INTERVAL d.notification_days DAY)
|
||||||
|
THEN 1 ELSE 0 END) AS approaching_cnt
|
||||||
|
FROM scad_deadlines d
|
||||||
|
WHERE d.status <> 'completed'
|
||||||
|
AND (
|
||||||
|
d.id IN (SELECT deadline_id FROM scad_deadline_employee WHERE employee_id = ?)
|
||||||
|
OR (? <> '' AND FIND_IN_SET(?, REPLACE(d.departments, ', ', ',')) > 0)
|
||||||
|
)
|
||||||
|
";
|
||||||
|
$_st = $pdo->prepare($_sql);
|
||||||
|
$_st->execute([$_empId, $_dept, $_dept]);
|
||||||
|
$_row = $_st->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||||
|
$_overdue = (int)($_row['overdue_cnt'] ?? 0);
|
||||||
|
$_approaching = (int)($_row['approaching_cnt'] ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$_emp || ($_overdue === 0 && $_approaching === 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<style>
|
||||||
|
.my-deadlines-widgets { display: flex; gap: 0.75rem; margin-bottom: 1rem; flex-wrap: wrap; }
|
||||||
|
.my-deadlines-widgets .mdw {
|
||||||
|
flex: 1 1 260px;
|
||||||
|
display: flex; align-items: center; gap: 0.9rem;
|
||||||
|
padding: 0.85rem 1rem;
|
||||||
|
border-radius: 0.6rem;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 2px 6px rgba(0,0,0,0.08);
|
||||||
|
transition: transform 0.15s, box-shadow 0.15s;
|
||||||
|
}
|
||||||
|
.my-deadlines-widgets .mdw:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(0,0,0,0.15); color: #fff; }
|
||||||
|
.my-deadlines-widgets .mdw-red { background: linear-gradient(135deg, #dc3545 0%, #b02a37 100%); }
|
||||||
|
.my-deadlines-widgets .mdw-orange { background: linear-gradient(135deg, #e8930c 0%, #c77a00 100%); }
|
||||||
|
.my-deadlines-widgets .mdw-icon {
|
||||||
|
width: 42px; height: 42px; border-radius: 50%;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
background: rgba(255,255,255,0.22); font-size: 1.2rem; flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.my-deadlines-widgets .mdw-body { flex: 1; line-height: 1.2; }
|
||||||
|
.my-deadlines-widgets .mdw-count { font-size: 1.6rem; font-weight: 700; }
|
||||||
|
.my-deadlines-widgets .mdw-label { font-size: 0.8rem; opacity: 0.95; }
|
||||||
|
.my-deadlines-widgets .mdw-arrow { opacity: 0.7; font-size: 0.9rem; }
|
||||||
|
</style>
|
||||||
|
<div class="my-deadlines-widgets">
|
||||||
|
<?php if ($_overdue > 0): ?>
|
||||||
|
<a class="mdw mdw-red" href="scadenzario/index.php?filter_my=1&filter_status=scaduta">
|
||||||
|
<span class="mdw-icon"><i class="fa-solid fa-triangle-exclamation"></i></span>
|
||||||
|
<span class="mdw-body">
|
||||||
|
<span class="mdw-count"><?= $_overdue ?></span>
|
||||||
|
<span class="mdw-label d-block">Scadenz<?= $_overdue === 1 ? 'a' : 'e' ?> scadut<?= $_overdue === 1 ? 'a' : 'e' ?> — <?= $_dept !== '' ? htmlspecialchars($_dept, ENT_QUOTES, 'UTF-8') : 'personali' ?></span>
|
||||||
|
</span>
|
||||||
|
<span class="mdw-arrow"><i class="fa-solid fa-arrow-right"></i></span>
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($_approaching > 0): ?>
|
||||||
|
<a class="mdw mdw-orange" href="scadenzario/index.php?filter_my=1&filter_status=in-scadenza">
|
||||||
|
<span class="mdw-icon"><i class="fa-solid fa-clock"></i></span>
|
||||||
|
<span class="mdw-body">
|
||||||
|
<span class="mdw-count"><?= $_approaching ?></span>
|
||||||
|
<span class="mdw-label d-block">In scadenza a breve — <?= $_dept !== '' ? htmlspecialchars($_dept, ENT_QUOTES, 'UTF-8') : 'personali' ?></span>
|
||||||
|
</span>
|
||||||
|
<span class="mdw-arrow"><i class="fa-solid fa-arrow-right"></i></span>
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
1245
public/userarea/scadenzario/index.php
Normal file
1245
public/userarea/scadenzario/index.php
Normal file
File diff suppressed because it is too large
Load Diff
202
public/userarea/scadenzario/print.php
Normal file
202
public/userarea/scadenzario/print.php
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
<?php include('../include/headscript.php'); ?>
|
||||||
|
<?php
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$sql = "
|
||||||
|
SELECT d.*,
|
||||||
|
s.name AS subject_name,
|
||||||
|
s.color AS subject_color,
|
||||||
|
GROUP_CONCAT(DISTINCT CONCAT(e.first_name, ' ', e.last_name) ORDER BY e.first_name SEPARATOR ', ') as responsabili,
|
||||||
|
GROUP_CONCAT(DISTINCT e.department ORDER BY e.department SEPARATOR ', ') as reparti_persone,
|
||||||
|
d.departments as reparti_assegnati
|
||||||
|
FROM scad_deadlines d
|
||||||
|
LEFT JOIN scad_subjects s ON s.id = d.subject_id
|
||||||
|
LEFT JOIN scad_deadline_employee de ON de.deadline_id = d.id
|
||||||
|
LEFT JOIN employees e ON e.id = de.employee_id
|
||||||
|
";
|
||||||
|
|
||||||
|
$where = [];
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
$filterStatus = $_GET['status'] ?? '';
|
||||||
|
$filterDept = $_GET['department'] ?? '';
|
||||||
|
$filterEmployee = $_GET['employee'] ?? '';
|
||||||
|
|
||||||
|
if ($filterStatus === 'non-completata') {
|
||||||
|
$where[] = "d.status != 'completed'";
|
||||||
|
} elseif ($filterStatus === 'completata') {
|
||||||
|
$where[] = "d.status = 'completed'";
|
||||||
|
} elseif ($filterStatus === 'scaduta') {
|
||||||
|
$where[] = "d.status = 'active' AND d.due_date < CURDATE()";
|
||||||
|
} elseif ($filterStatus === 'in-scadenza') {
|
||||||
|
$where[] = "d.status = 'active' AND d.due_date >= CURDATE() AND d.due_date <= DATE_ADD(CURDATE(), INTERVAL d.notification_days DAY)";
|
||||||
|
} elseif ($filterStatus === 'attiva') {
|
||||||
|
$where[] = "d.status = 'active' AND d.due_date > DATE_ADD(CURDATE(), INTERVAL d.notification_days DAY)";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($filterEmployee) {
|
||||||
|
$where[] = "EXISTS (SELECT 1 FROM scad_deadline_employee de2 JOIN employees e2 ON e2.id = de2.employee_id WHERE de2.deadline_id = d.id AND CONCAT(e2.first_name, ' ', e2.last_name) = ?)";
|
||||||
|
$params[] = $filterEmployee;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dueFrom = $_GET['due_from'] ?? '';
|
||||||
|
$dueTo = $_GET['due_to'] ?? '';
|
||||||
|
$checkFrom = $_GET['check_from'] ?? '';
|
||||||
|
$checkTo = $_GET['check_to'] ?? '';
|
||||||
|
|
||||||
|
if ($dueFrom) { $where[] = "d.due_date >= ?"; $params[] = $dueFrom; }
|
||||||
|
if ($dueTo) { $where[] = "d.due_date <= ?"; $params[] = $dueTo; }
|
||||||
|
if ($checkFrom) { $where[] = "d.check_date >= ?"; $params[] = $checkFrom; }
|
||||||
|
if ($checkTo) { $where[] = "d.check_date <= ?"; $params[] = $checkTo; }
|
||||||
|
|
||||||
|
if (!empty($where)) {
|
||||||
|
$sql .= " WHERE " . implode(' AND ', $where);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql .= " GROUP BY d.id ORDER BY (d.status = 'completed') ASC, d.due_date ASC";
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$deadlines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$today = date('Y-m-d');
|
||||||
|
$recurrenceLabels = ['once'=>'Una tantum','monthly'=>'Mensile','quarterly'=>'Trimestrale','semiannual'=>'Semestrale','annual'=>'Annuale','biennial'=>'Biennale','triennial'=>'Triennale','quadriennial'=>'Quadriennale','quinquennial'=>'Quinquennale','decennial'=>'Decennale','quindecennial'=>'Quindicennale'];
|
||||||
|
|
||||||
|
$filterLabel = '';
|
||||||
|
if ($filterStatus) {
|
||||||
|
$statusLabels = ['non-completata'=>'Non completate','attiva'=>'Attive','in-scadenza'=>'In scadenza','scaduta'=>'Scadute','completata'=>'Completate'];
|
||||||
|
$filterLabel = $statusLabels[$filterStatus] ?? '';
|
||||||
|
}
|
||||||
|
if ($filterDept) {
|
||||||
|
$filterLabel .= ($filterLabel ? ' — ' : '') . 'Reparto: ' . $filterDept;
|
||||||
|
}
|
||||||
|
if ($filterEmployee) {
|
||||||
|
$filterLabel .= ($filterLabel ? ' — ' : '') . 'Responsabile: ' . $filterEmployee;
|
||||||
|
}
|
||||||
|
if ($dueFrom || $dueTo) {
|
||||||
|
$filterLabel .= ($filterLabel ? ' — ' : '') . 'Scadenza: ' . ($dueFrom ? date('d/m/Y', strtotime($dueFrom)) : '...') . ' → ' . ($dueTo ? date('d/m/Y', strtotime($dueTo)) : '...');
|
||||||
|
}
|
||||||
|
if ($checkFrom || $checkTo) {
|
||||||
|
$filterLabel .= ($filterLabel ? ' — ' : '') . 'Controllo: ' . ($checkFrom ? date('d/m/Y', strtotime($checkFrom)) : '...') . ' → ' . ($checkTo ? date('d/m/Y', strtotime($checkTo)) : '...');
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Stampa Scadenzario</title>
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 11px; color: #222; background: #fff; }
|
||||||
|
.print-header { padding: 20px 20px 10px; border-bottom: 2px solid #2c3e6b; margin-bottom: 10px; }
|
||||||
|
.print-header h1 { font-size: 18px; color: #2c3e6b; margin: 0; }
|
||||||
|
.print-header .print-meta { font-size: 10px; color: #666; margin-top: 4px; }
|
||||||
|
.print-actions { padding: 10px 20px; display: flex; gap: 10px; }
|
||||||
|
.print-actions button { padding: 8px 20px; font-size: 13px; font-weight: 600; border-radius: 6px; cursor: pointer; border: none; }
|
||||||
|
.btn-print { background: #5a8fd8; color: #fff; }
|
||||||
|
.btn-back { background: #f0f0f0; color: #333; }
|
||||||
|
table { width: 100%; border-collapse: collapse; margin: 0 20px; }
|
||||||
|
table { width: calc(100% - 40px); }
|
||||||
|
th { background: #2c3e6b; color: #fff; font-weight: 600; font-size: 10px; text-transform: uppercase; letter-spacing: 0.03em; padding: 6px 8px; text-align: left; white-space: nowrap; }
|
||||||
|
td { padding: 5px 8px; border-bottom: 1px solid #e0e0e0; vertical-align: top; font-size: 10.5px; line-height: 1.4; }
|
||||||
|
tr:nth-child(even) { background: #f9fafb; }
|
||||||
|
tr.row-overdue { background: #fff5f5; }
|
||||||
|
tr.row-overdue td { color: #991b1b; }
|
||||||
|
tr.row-approaching { background: #fffbeb; }
|
||||||
|
tr.row-completed { opacity: 0.5; }
|
||||||
|
.status-badge { font-size: 9px; font-weight: 700; padding: 2px 6px; border-radius: 10px; white-space: nowrap; }
|
||||||
|
.st-attiva { background: #e8eeff; color: #3a6bb5; }
|
||||||
|
.st-scaduta { background: #fde8e8; color: #b91c1c; }
|
||||||
|
.st-in-scadenza { background: #fef3cd; color: #92600a; }
|
||||||
|
.st-completata { background: #d1f2e0; color: #0f5132; }
|
||||||
|
.print-footer { padding: 10px 20px; font-size: 9px; color: #999; border-top: 1px solid #e0e0e0; margin-top: 10px; text-align: right; }
|
||||||
|
@media print {
|
||||||
|
.print-actions { display: none; }
|
||||||
|
body { font-size: 9px; }
|
||||||
|
th { font-size: 8.5px; padding: 4px 6px; }
|
||||||
|
td { font-size: 9px; padding: 3px 6px; }
|
||||||
|
@page { size: landscape; margin: 0.8cm; }
|
||||||
|
}
|
||||||
|
@media screen {
|
||||||
|
table { margin: 0 auto; width: calc(100% - 40px); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="print-header">
|
||||||
|
<h1>ELENCO PRESCRIZIONI LEGALI ED ALTRE PRESCRIZIONI APPLICABILI<br>SICUREZZA ED AMBIENTE CON VERIFICA DEL RISPETTO DELLE STESSE</h1>
|
||||||
|
<div class="print-meta">
|
||||||
|
Stampato il <?= date('d/m/Y H:i') ?>
|
||||||
|
<?php if ($filterLabel): ?> — Filtro: <?= htmlspecialchars($filterLabel, ENT_QUOTES, 'UTF-8') ?><?php endif; ?>
|
||||||
|
— Totale: <?= count($deadlines) ?> scadenze
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="print-actions">
|
||||||
|
<button class="btn-print" onclick="window.print()"><i class="fa-solid fa-print"></i> Stampa</button>
|
||||||
|
<button class="btn-back" onclick="window.close()">Chiudi</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Argomento</th>
|
||||||
|
<th>Legge / Art.</th>
|
||||||
|
<th>Dettaglio</th>
|
||||||
|
<th>Periodicità Scadenza</th>
|
||||||
|
<th>Data Documento</th>
|
||||||
|
<th>Data Scadenza</th>
|
||||||
|
<th>Data Ultimo Controllo</th>
|
||||||
|
<th>Responsabilità</th>
|
||||||
|
<th>Luogo di Archiviazione</th>
|
||||||
|
<th>Note</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($deadlines as $row):
|
||||||
|
$dueDate = $row['due_date'];
|
||||||
|
$nDays = (int)$row['notification_days'];
|
||||||
|
$approachDate = date('Y-m-d', strtotime($today . ' + ' . $nDays . ' days'));
|
||||||
|
$isCompleted = $row['status'] === 'completed';
|
||||||
|
$isOverdue = !$isCompleted && $dueDate < $today;
|
||||||
|
$isApproaching = !$isCompleted && !$isOverdue && $dueDate <= $approachDate;
|
||||||
|
|
||||||
|
if ($isCompleted) { $statusLabel = 'Completata'; $stClass = 'st-completata'; $rowClass = 'row-completed'; }
|
||||||
|
elseif ($isOverdue) { $statusLabel = 'Scaduta'; $stClass = 'st-scaduta'; $rowClass = 'row-overdue'; }
|
||||||
|
elseif ($isApproaching) { $statusLabel = 'In scadenza'; $stClass = 'st-in-scadenza'; $rowClass = 'row-approaching'; }
|
||||||
|
else { $statusLabel = 'Attiva'; $stClass = 'st-attiva'; $rowClass = ''; }
|
||||||
|
|
||||||
|
// Merge departments
|
||||||
|
$allDepts = [];
|
||||||
|
if (!empty($row['reparti_assegnati'])) $allDepts = array_map('trim', explode(',', $row['reparti_assegnati']));
|
||||||
|
if (!empty($row['reparti_persone'])) $allDepts = array_merge($allDepts, array_map('trim', explode(',', $row['reparti_persone'])));
|
||||||
|
$reparti = implode(', ', array_unique(array_filter($allDepts)));
|
||||||
|
?>
|
||||||
|
<tr class="<?= $rowClass ?>"<?= !empty($row['subject_color']) ? ' style="border-left: 4px solid ' . htmlspecialchars($row['subject_color'], ENT_QUOTES, 'UTF-8') . '"' : '' ?>>
|
||||||
|
<td><strong><?= htmlspecialchars($row['subject_name'] ?? '', ENT_QUOTES, 'UTF-8') ?></strong></td>
|
||||||
|
<td><?= htmlspecialchars($row['law_regulation'] ?? '', ENT_QUOTES, 'UTF-8') ?></td>
|
||||||
|
<td><?= htmlspecialchars($row['topic'], ENT_QUOTES, 'UTF-8') ?></td>
|
||||||
|
<td><?= htmlspecialchars($recurrenceLabels[$row['recurrence_type']] ?? $row['recurrence_type'], ENT_QUOTES, 'UTF-8') ?></td>
|
||||||
|
<td style="white-space:nowrap"><?= $row['document_date'] ? date('d/m/Y', strtotime($row['document_date'])) : '' ?></td>
|
||||||
|
<td style="white-space:nowrap"><?= date('d/m/Y', strtotime($dueDate)) ?></td>
|
||||||
|
<td style="white-space:nowrap"><?= $row['check_date'] ? date('d/m/Y', strtotime($row['check_date'])) : '' ?></td>
|
||||||
|
<td><?php
|
||||||
|
$resp = [];
|
||||||
|
if ($reparti) $resp[] = $reparti;
|
||||||
|
if ($row['responsabili']) $resp[] = $row['responsabili'];
|
||||||
|
echo htmlspecialchars(implode(', ', $resp), ENT_QUOTES, 'UTF-8');
|
||||||
|
?></td>
|
||||||
|
<td><?= htmlspecialchars($row['storage_location'] ?? '', ENT_QUOTES, 'UTF-8') ?></td>
|
||||||
|
<td><?= htmlspecialchars($row['notes'] ?? '', ENT_QUOTES, 'UTF-8') ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="print-footer">
|
||||||
|
ZIBOGOMMA — Scadenzario — <?= date('d/m/Y') ?>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
70
public/userarea/scadenzario/sql/1_create_tables.sql
Normal file
70
public/userarea/scadenzario/sql/1_create_tables.sql
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
-- Scadenzario tables
|
||||||
|
-- Responsible persons = employees (existing table)
|
||||||
|
-- Departments = employees.department (varchar field)
|
||||||
|
-- Notification email = employees.auth_user_id -> auth_users.email
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS scad_deadlines (
|
||||||
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
category VARCHAR(100) NULL,
|
||||||
|
topic VARCHAR(500) NOT NULL,
|
||||||
|
law_regulation VARCHAR(500) NULL,
|
||||||
|
details TEXT NULL,
|
||||||
|
recurrence_type VARCHAR(20) NOT NULL DEFAULT 'once',
|
||||||
|
due_date DATE NOT NULL,
|
||||||
|
check_date DATE NULL,
|
||||||
|
document_date DATE NULL,
|
||||||
|
notification_days SMALLINT UNSIGNED NOT NULL DEFAULT 7,
|
||||||
|
storage_location VARCHAR(500) NULL,
|
||||||
|
notes TEXT NULL,
|
||||||
|
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
||||||
|
completed_at TIMESTAMP NULL,
|
||||||
|
completed_by INT UNSIGNED NULL,
|
||||||
|
created_by INT UNSIGNED NOT NULL,
|
||||||
|
departments VARCHAR(500) NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
INDEX idx_status (status),
|
||||||
|
INDEX idx_due_date (due_date)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS scad_deadline_employee (
|
||||||
|
deadline_id INT UNSIGNED NOT NULL,
|
||||||
|
employee_id INT UNSIGNED NOT NULL,
|
||||||
|
PRIMARY KEY (deadline_id, employee_id),
|
||||||
|
CONSTRAINT fk_de_deadline FOREIGN KEY (deadline_id) REFERENCES scad_deadlines(id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_de_employee FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS scad_deadline_attachments (
|
||||||
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
deadline_id INT UNSIGNED NOT NULL,
|
||||||
|
original_name VARCHAR(500) NOT NULL,
|
||||||
|
stored_name VARCHAR(500) NOT NULL,
|
||||||
|
mime_type VARCHAR(100) NULL,
|
||||||
|
size INT UNSIGNED NULL,
|
||||||
|
uploaded_by INT UNSIGNED NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT fk_att_deadline FOREIGN KEY (deadline_id) REFERENCES scad_deadlines(id) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS scad_deadline_histories (
|
||||||
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
deadline_id INT UNSIGNED NOT NULL,
|
||||||
|
user_id INT UNSIGNED NULL,
|
||||||
|
action VARCHAR(50) NOT NULL,
|
||||||
|
changes JSON NULL,
|
||||||
|
notes TEXT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
INDEX idx_hist_deadline (deadline_id),
|
||||||
|
CONSTRAINT fk_hist_deadline FOREIGN KEY (deadline_id) REFERENCES scad_deadlines(id) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS scad_deadline_notifications (
|
||||||
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
deadline_id INT UNSIGNED NOT NULL,
|
||||||
|
employee_id INT UNSIGNED NOT NULL,
|
||||||
|
sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
type VARCHAR(30) NOT NULL,
|
||||||
|
INDEX idx_notif_deadline (deadline_id),
|
||||||
|
CONSTRAINT fk_notif_deadline FOREIGN KEY (deadline_id) REFERENCES scad_deadlines(id) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
25
public/userarea/scadenzario/sql/2_migrate_subjects.sql
Normal file
25
public/userarea/scadenzario/sql/2_migrate_subjects.sql
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
-- One-time migration: move scad_deadlines.category into dedicated scad_subjects table
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS scad_subjects (
|
||||||
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
color VARCHAR(7) NOT NULL DEFAULT '#6c757d',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE KEY uniq_name (name)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
INSERT IGNORE INTO scad_subjects (name)
|
||||||
|
SELECT DISTINCT TRIM(category) FROM scad_deadlines
|
||||||
|
WHERE category IS NOT NULL AND TRIM(category) <> '';
|
||||||
|
|
||||||
|
ALTER TABLE scad_deadlines
|
||||||
|
ADD COLUMN subject_id INT UNSIGNED NULL AFTER id,
|
||||||
|
ADD INDEX idx_subject (subject_id),
|
||||||
|
ADD CONSTRAINT fk_deadlines_subject FOREIGN KEY (subject_id) REFERENCES scad_subjects(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
UPDATE scad_deadlines d
|
||||||
|
JOIN scad_subjects s ON s.name = TRIM(d.category)
|
||||||
|
SET d.subject_id = s.id;
|
||||||
|
|
||||||
|
ALTER TABLE scad_deadlines DROP COLUMN category;
|
||||||
531
public/userarea/scadenzario/sql/3_import_scadenzario.sql
Normal file
531
public/userarea/scadenzario/sql/3_import_scadenzario.sql
Normal file
@ -0,0 +1,531 @@
|
|||||||
|
START TRANSACTION;
|
||||||
|
|
||||||
|
DELETE FROM scad_deadline_attachments;
|
||||||
|
DELETE FROM scad_deadlines;
|
||||||
|
DELETE FROM scad_subjects;
|
||||||
|
|
||||||
|
ALTER TABLE scad_deadlines AUTO_INCREMENT = 1;
|
||||||
|
ALTER TABLE scad_subjects AUTO_INCREMENT = 1;
|
||||||
|
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Ambiente', '#198754') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Manutenzioni', '#5a8fd8') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Sicurezza', '#e8930c') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Ambienti - Scarichi idrici', '#0dcaf0') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Prescrizioni generali', '#6f42c1') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Antincendio', '#dc3545') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Ambiente - Manutenzione', '#20c997') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Ambiente - Rifiuti', '#795548') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Medicina del Lavoro', '#d63384') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Prescrizioni generali Medicina del Lavoro', '#343a40') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Adempimenti', '#b88a44') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Attività in appalto', '#8b4513') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Formazione', '#17a2b8') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Appaltatori', '#212529') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Ambienti - Emissioni in atmosfera', '#0d6efd') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Ambiente - Emissioni in atmosfera', '#28a745') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Prevenzione - Sicurezza', '#ffc107') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
INSERT INTO scad_subjects (name, color) VALUES ('Pronto Soccorso', '#e83e8c') ON DUPLICATE KEY UPDATE color = VALUES(color);
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES (NULL, 'Autorizzazione raccolta e trasporto rifiuti speciali non pericolosi TRAMONTO ANTONIO', 'D.M. 10.03.1998, UNI 9994', 'quinquennial', '2025-12-22', '2021-01-02', '2020-12-22', 7, NULL, NULL, 1, 'Amministrazione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente'), 'Autorizzazione raccolta e trasporto rifiuti speciali pericolosi TRAMONTO ANTONIO', 'D.Lgs 81/08 art. 174, 176', 'quinquennial', '2026-01-09', '2021-01-19', '2021-01-09', 7, NULL, NULL, 1, 'Amministrazione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Manutenzioni'), 'Manutenzione Transpalett manuali', 'D.Lgs 81/08 all.V', 'quarterly', '2026-03-31', '2026-02-24', '2025-12-23', 7, 'Locale Archivio', NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Sicurezza'), 'Manutenzione Carrelli Elevatori', 'D.Lgs 81/08 all.V', 'quarterly', '2026-03-31', '2026-02-24', '2025-12-23', 7, 'Locale Archivio', NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambienti - Scarichi idrici'), 'Sostituzione valvola Recipienti Pressione', NULL, 'triennial', '2026-04-03', '2024-07-05', '2023-07-04', 7, 'Locale Archivio', 'Autoclave l 1.000 pozzi', 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Rinnovo CPI', 'D.M. 10.03.1998, D.M. 16.06.1982, DPR 151 del 2011', 'quinquennial', '2026-04-30', '2021-10-21', '2021-10-21', 7, NULL, 'Scadenza 09/06/2026', 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Verifica funzionamento impianto schiuma', 'D.Lgs. 81/2008 (art. 18, c 1, u) D.Lgs. 81/2008 (art. 26)', 'semiannual', '2026-04-30', '2026-02-24', '2026-02-19', 7, 'Locale Archivio', 'Attività in appalto a Ditta specializzata', 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Verifica funzionamento stazione di pompaggio antincendio', 'D.Lgs. 81/2008 (art. 18, c 1, u) D.Lgs. 81/2008 (art. 26)', 'quarterly', '2026-04-30', '2026-02-24', '2026-02-19', 7, 'Locale Archivio', 'Attività in appalto a Ditta specializzata', 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Verifica funzionamento valvola a diluvio impianto schiuma', 'D.Lgs. 81/2008 (art. 18, c 1, u) D.Lgs. 81/2008 (art. 26)', 'quarterly', '2026-04-30', '2026-02-24', '2026-02-19', 7, 'Locale Archivio', 'Attività in appalto a Ditta specializzata', 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Manutenzioni'), 'Verifica impianto rivelazione incendi', 'D.Lgs. 81/2008 (art. 18, c 1, u) D.Lgs. 81/2008 (art. 26)', 'semiannual', '2026-05-12', '2026-03-21', '2025-11-13', 7, 'Locale Archivio', NULL, 1, 'Delegato di funzioni');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambienti - Scarichi idrici'), 'Sanificazione filtri rubinetti e pigne docce con candeggina', 'D.Lgs 81/08 art. 209', 'quarterly', '2026-05-31', '2026-02-24', '2026-02-24', 7, NULL, NULL, 1, 'Ditta Incaricata');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES (NULL, 'Manutenzione e controllo fughe pompa di calore ELCO', 'D.M. 17/03/03', 'annual', '2026-05-31', '2026-02-24', NULL, 7, NULL, 'Presentazione a cura delle ditte incaricate', 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente'), 'Gestione Rischi Impianto Trattamento Aria ((Unità trattamento aria - Filtri Aria - Umidificazione a vapore - Batterie di scambio termico - Ventilazione UTA - Raffreddatori di liquido - Apparecchi terminali) e registrazione su quaderno e libretti', 'D.Lgs 81/08', 'once', '2026-05-31', '2026-03-21', '2025-10-13', 7, 'Locale Archivio', NULL, 1, 'Delegato di funzioni');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente - Manutenzione'), 'Dichiarazione MUD', NULL, 'annual', '2026-06-30', '2026-02-24', '2025-06-19', 7, NULL, NULL, 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente - Rifiuti'), 'Comunicazione BANCA DATI GAS FLUORURATI refrigeratori produzione riguardante le emissioni in atmosfera di gas fluorurati', 'Accordo Stato regioni 07.02.2013', 'annual', '2026-06-30', '2026-02-24', NULL, 7, NULL, 'Presentazione a cura delle ditte incaricate', 1, 'DdL-RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Comunicazione BANCA DATI GAS FLUORURATI pompa di calore riguardante le emissioni in atmosfera di gas fluorurati', 'Accordo Stato regioni 07.02.2013', 'annual', '2026-06-30', '2026-02-24', NULL, 7, NULL, 'Presentazione a cura delle ditte incaricate', 1, 'DdL-RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Manutenzione e controllo fughe refrigeratori produzione', 'D.M. 17/03/03', 'annual', '2026-06-30', '2026-02-24', NULL, 7, NULL, NULL, 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Formazione RLS - 4 ore anno', 'D.Lgs 81/08 art. 18, 35', 'annual', '2026-06-30', '2026-02-24', '2025-06-12', 7, 'Locale Archivio', NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Medicina del Lavoro'), 'Derattizzazione', NULL, 'semiannual', '2026-06-30', '2026-02-24', NULL, 7, 'Locale Matrici', NULL, 1, 'Incaricato');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Manutenzioni'), 'Pulizia piazzali', NULL, 'semiannual', '2026-06-30', '2026-02-24', NULL, 7, NULL, 'Audit Interno', 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Autorizzazione raccolta e trasporto rifiuti speciali non pericolosi FEROLMET', 'D.Lgs 81/08 art. 19, Accordo Stato Regioni 2011', 'once', '2026-07-30', '2023-02-07', NULL, 7, NULL, NULL, 1, 'Amministrazione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente'), 'Autorizzazione raccolta e trasporto rifiuti speciali pericolosi FEROLMET', 'D.Lgs 81/08 art. 202', 'once', '2026-07-30', '2023-02-07', NULL, 7, NULL, NULL, 1, 'Amministrazione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Autocontrolli periodici emissioni in atmosfera', 'D.Lgs 81/08 art. 223', 'annual', '2026-07-31', '2026-02-24', '2025-06-18', 7, NULL, NULL, 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES (NULL, 'Comunicazione BANCA DATI GAS FLUORURATI Pompa di Calore Evaporatore riguardante le emissioni in atmosfera di gas fluorurati', 'D.Lgs 81/08 art. 53', 'annual', '2026-07-31', '2026-03-21', NULL, 7, 'Locale Archivio', 'Presentazione a cura della ditta incaricata EcoTechno', 1, 'Delegato di funzioni');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Manutenzioni'), 'Controllo documentazione Sicurezza', 'D.Lgs 152/2006 Parte III (art. 124)', 'semiannual', '2026-08-31', '2026-02-24', '2026-02-23', 7, 'Locale Archivio', 'Audit Interno', 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES (NULL, 'Comunicazione BANCA DATI GAS FLUORURATI refrigeratore raffrescamento inizio linee riguardante le emissioni in atmosfera di gas fluorurati', 'Accordo Stato regioni 07.02.2013', 'annual', '2026-08-31', '2026-02-24', NULL, 7, NULL, 'Presentazione a cura delle ditte incaricate', 1, 'DdL-RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Manutenzioni'), 'Verifica dispositivi su uscite di emergenza', 'D.Lgs 81/08 art. 26', 'semiannual', '2026-08-31', '2026-02-24', '2026-02-26', 7, 'Locale Archivio', NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Verifica integrità ed efficienza porte tagliafuoco', 'D.Lgs. 81/2008 (art. 18, c 1, u) D.Lgs. 81/2008 (art. 26)', 'semiannual', '2026-08-31', '2026-02-24', '2026-02-26', 7, 'Locale Archivio', NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali Medicina del Lavoro'), 'Controllo dispositivi lotta antincendio (estintori, idranti, lance, manichette)', 'D.Lgs 81/08 art. 37, 73, Accordo Stato Regioni 2011', 'semiannual', '2026-08-31', '2026-02-24', '2026-02-26', 7, 'Locale Archivio', NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Adempimenti'), 'Formazione del Preposto', NULL, 'quinquennial', '2026-09-20', '2023-02-07', '2021-09-21', 7, NULL, 'Accordo Stato Regioni 21.12.2011', 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Indagine Ambientale', 'D.Lgs 81/08 - Salute e Sicurezza nei luoghi di lavoro', 'annual', '2026-09-30', '2026-02-24', '2025-09-10', 7, 'Locale Archivio', NULL, 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente'), 'Controllo fumi e funzionamento caldaia', 'D.Lgs 81/08 art. 190', 'annual', '2026-10-31', '2026-03-21', '2025-05-15', 7, 'Locale Archivio', NULL, 1, 'Delegato di funzioni');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Attività in appalto'), 'Riunione periodica art. 35', 'D.Lgs 152/2006 Parte III (art. 124)', 'annual', '2026-11-30', '2025-11-20', '2025-11-20', 7, 'Locale Archivio', NULL, 1, 'Delegato di Funzioni');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Valutazione Rischio Vibrazioni', 'D.M. 10.03.1998', 'quadriennial', '2026-12-01', '2023-02-07', '2022-12-02', 7, 'Locale Archivio', 'Accordo Stato Regioni 21.12.2011', 1, 'DdL - RSPP Medico - RLS');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Medicina del Lavoro'), 'Valutazione stress lavoro-correllato', 'D. 20 del 24/01/2011', 'biennial', '2026-12-19', '2024-12-20', '2024-12-20', 7, 'Locale Archivio', NULL, 1, 'DdL - RSPP Medico - RLS');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Formazione'), 'Verifica scadenza sostanze assorbenti e neutralizzanti al fine di prevenire l''inquinamento del suolo da parte dell''elettrolita delle batterie', 'D.Lgs 152/2006 Parte V', 'annual', '2026-12-31', '2025-12-22', '2025-12-22', 7, NULL, 'Audit Interno', 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambienti - Scarichi idrici'), 'Emergenze (alluvione e sversamento oli) Verifica stato tombini e armadio materiale emergenza alluvione e presenza materiale per pulizia olii', 'D.Lgs 81/08 art. 216', 'annual', '2026-12-31', '2025-12-22', '2025-12-22', 7, NULL, 'Verifica stato tombini eseguita da Roberto+Daniele', 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Formazione'), 'Simulazione periodica di evacuazione antincendio', 'D.Lgs 81/08 art. 25', 'annual', '2026-12-31', '2025-12-22', '2025-12-22', 7, 'Locale Archivio', NULL, 1, 'Delegato di Funzioni');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Contributo CONAI', 'D.Lgs 152/2006 Parte V', 'once', '2027-01-20', '2026-01-20', '2026-01-20', 7, NULL, NULL, 1, '--');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Sicurezza'), 'Nomina 3° resp. per caldaie >35kw', 'D.Lgs 81/08 art. 71', 'once', '2027-01-31', '2026-01-19', '2026-01-19', 7, NULL, NULL, 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente'), 'Analisi schiuma delll''impianto', 'D.Lgs. 81/2008 (art. 18, c 1, u) D.Lgs. 81/2008 (art. 26)', 'annual', '2027-01-31', '2026-03-21', '2026-02-04', 7, 'Locale Archivio', 'Attività in appalto a Ditta specializzata', 1, 'Delegato di funzioni');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Verifica impianto di Messa a Terra', 'DPR 462/01', 'biennial', '2027-02-12', '2025-02-19', '2025-02-12', 7, 'Locale Archivio', NULL, 1, 'Delegato di funzioni');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Test carrellisti uso sostanze dopanti', 'D.Lgs 81/08', 'annual', '2027-02-28', '2025-02-10', '2026-02-09', 7, NULL, NULL, 1, 'Medico competente');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Visite Mediche ai dipendenti', 'D.Lgs 81/08 art. 18, 38, 41', 'annual', '2027-02-28', '2025-02-19', '2026-02-09', 7, 'Locale Archivio', NULL, 1, 'Medico Competente');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambienti - Scarichi idrici'), 'Controllo Legionella Acqua', 'D.Lgs 81/08 art. 32, Accordo Stato Regioni 2006', 'annual', '2027-02-28', '2026-02-24', '2026-02-19', 7, 'Locale Archivio', NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Appaltatori'), 'Verbale di Sopralluogo dei Locali del Medico Competente', 'D.Lgs 81/08', 'annual', '2027-02-28', '2026-02-24', '2026-02-09', 7, 'Locale Archivio', NULL, 1, 'Medico Competente');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Manutenzioni'), 'Dichiarazione acque derivate da pozzi', 'ART. 22 DEL D.LGS. 11 MAGGIO 1999, N. 152, COSÌ COME MODIFICATO DALL’ART. 6, COMMA 1, LETT. A) DEL D.LGS. 18 AGOSTO 2000, N. 258', 'annual', '2027-03-31', '2026-03-21', '2026-03-20', 7, 'Locale Archivio', NULL, 1, 'Delegato di funzioni');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Formazione'), 'Verifica Periodica Recipienti Pressione', NULL, 'triennial', '2027-04-04', '2024-07-05', '2024-07-05', 7, 'Locale Archivio', 'Autoclave l 1.000 pozzi', 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Revisione scala a gabbia Stp sul tetto del fabbricato tra porzione uffici (più alta) e porzione reparto', 'D.Lgs. n. 81/2008', 'quinquennial', '2027-10-24', '2023-01-31', '2022-10-25', 7, 'Locale Archivio', NULL, 1, 'DdL-RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Visita medica per Addetti VDT', 'D.Lgs 152/2006 Parte III (art. 124)', 'biennial', '2028-02-28', '2026-02-09', '2026-02-09', 7, 'Locale Archivio', NULL, 1, 'Medico Competente');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Formazione'), 'Valutazione Rischio da ROA', 'D.P.R. 147 del 15/02/2006', 'quadriennial', '2028-07-14', '2024-11-23', '2024-07-15', 7, 'Locale Archivio', 'Accordo Stato Regioni 21.12.2011', 1, 'DdL - RSPP Medico - RLS');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Valutazione Rischio da CEM', 'LEGGE N. 70/94 D.M. 17/12/2009', 'quadriennial', '2028-07-14', '2024-11-23', '2024-07-15', 7, 'Locale Archivio', 'Accordo Stato Regioni 21.12.2011', 1, 'DdL - RSPP Medico - RLS');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Sicurezza'), 'Valutazione Rischio Rumore', 'D.M. 16/03/1998 L. n° 447/95', 'quadriennial', '2028-09-30', '2024-11-23', '2024-10-02', 7, 'Locale Archivio', NULL, 1, 'DdL - RSPP Medico - RLS');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Protezione contro i fulmini - Valutazione del rischio e scelta delle misure di protezione', 'D.Lgs. 81/2008 / CEI EN 62305-1-2-3-4 / CEI 81-29 / CEI EN IEC 62858', 'quinquennial', '2028-12-31', '2023-09-04', '2023-07-31', 7, 'Locale Archivio', 'Rivalutazione del valore NG', 1, 'DdL-RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambienti - Scarichi idrici'), 'Autorizzazione allo scarico rete fognaria di acque reflue domestiche', 'D.Lgs 152/2006 Parte III (art. 124)', 'quadriennial', '2028-05-21', '2026-03-21', '2025-05-22', 7, NULL, 'Validità 4 anni sino al 21/05/2029. Domanda di rinnovo almeno 1 anno prima della scadenza', 1, 'Condominio Fornace Venere in persona dell''Amministratore');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambienti - Emissioni in atmosfera'), 'Bombola acetilene l 5 omologata 2020/01', 'D.Lgs 03.04.2006 n.152 Legge R. 21.06.1999 n.18', 'decennial', '2030-01-31', '2021-02-23', '2021-02-23', 7, 'Locale Archivio', NULL, 1, 'DdL');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Formazione'), 'Bombola ossigeno l 5 omologata 2020/11', 'D.Lgs 81/08 art. 36, 73,', 'decennial', '2030-11-30', '2021-02-23', '2021-02-23', 7, 'Locale Archivio', NULL, 1, 'DdL');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Sicurezza'), 'Revisione parapetti sul tetto del fabbricato (n. 3 sui lati corti)', 'EN14122 - NTC2018', 'decennial', '2032-10-24', '2023-01-31', '2022-10-25', 7, 'Locale Archivio', NULL, 1, 'DdL-RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Sicurezza'), 'Revisione parapetti soletta locali tecnici (refrigeratori)', 'EN ISO 14122-3:2001+A1:2010', 'decennial', '2032-12-18', '2023-01-31', '2022-12-19', 7, 'Locale Archivio', NULL, 1, 'DdL-RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente - Emissioni in atmosfera'), 'Rinnovo Autorizzazione alle emissioni in atmosfera', 'D.Lgs 152/2006 Parte III (art. 124)', 'quindecennial', '2037-08-23', '2023-02-24', '2023-02-24', 7, 'Locale Archivio', NULL, 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Medicina del Lavoro'), 'Informazione ai lavoratori specifica', 'D.Lgs 81/08 art. 18', 'once', '2027-04-18', NULL, NULL, 7, NULL, 'Accordo Stato Regioni 21.12.2011', 1, 'Delegato di funzioni');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Formazione ai lavoratori specifica', 'D.Lgs 81/08 art. 17,28,29', 'once', '2027-04-18', NULL, NULL, 7, NULL, 'Accordo Stato Regioni 21.12.2011', 1, 'Delegato di funzioni');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Documento Valutazione dei Rischi', 'D.Lgs 81/08 art. 18 D.M. 10.03.1998', 'once', '2026-10-13', '2025-10-13', '2025-10-13', 7, 'Locale Archivio', NULL, 1, 'DdL');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Piano di Emergenza', 'D.Lgs 81/08 art. 47', 'once', '2026-01-15', '2025-01-15', '2025-01-15', 7, 'Locale Archivio', NULL, 1, 'Delegato di Funzioni');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Nomina Medico Competente', 'D.Lgs 81/08 art. 47', 'once', '2026-11-20', '2025-11-20', '2025-11-20', 7, 'Locale Archivio', NULL, 1, 'DdL');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Segnalazione all''INAIL nominativo RLS', 'D.Lgs 81/08 art. 31', 'once', '2027-04-18', '2023-02-07', NULL, 7, 'Locale Archivio', NULL, 1, 'DdL');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prevenzione - Sicurezza'), 'Nomina RSPP', 'D.P.R. 43/12 attuazione del Regolamento CE 842/2006', 'once', '2023-02-01', '2023-02-07', '2022-02-01', 7, NULL, NULL, 1, 'DdL');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES (NULL, 'Elezione RLS', NULL, 'once', '2020-02-18', '2023-02-07', '2019-02-18', 7, 'Locale Archivio', NULL, 1, 'Direzione-Lavoratori');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Formazione'), 'Valutazione impatto acustico', 'D.Lgs 81/08 art. 19, Accordo Stato Regioni 2011', 'once', '2018-11-07', '2023-02-07', '2017-11-07', 7, NULL, NULL, 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Adempimenti'), 'Nomina Addetti Prev. Incendi', 'Lege n° 457/78', 'once', '2027-04-18', '2023-02-07', NULL, 7, 'Locale Archivio', 'Vedi Organigramma', 1, 'DdL');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Adempimenti'), 'Comunicazione dei requisiti come Medico Competente', 'Regolamento Comunale 56', 'once', '2027-04-18', '2023-02-07', NULL, 7, 'Locale Archivio', NULL, 1, 'Medico Competente');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Adempimenti'), 'Protocollo Sanitario', 'D.M. 37/08', 'annual', '2022-12-02', '2023-02-07', '2021-12-02', 7, 'Locale Archivio', 'Rivalidato tramite Riunione Periodica', 1, 'Medico Competente');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Adempimenti'), 'Nomina Dirigenti e Preposti', 'D.M. 37/08', 'once', '2027-04-18', '2023-02-07', NULL, 7, 'Locale Archivio', 'v. incarichi', 1, 'DdL');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Adempimenti'), 'Cassetta di Pronto Soccorso, controllo presenza e scadenza dispositivi e medicinali', 'D.M. 37/08', 'monthly', '2026-05-18', '2023-02-07', NULL, 7, 'Cassette P.S. Reparto e magazzino', NULL, 1, 'Incaricata');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Adempimenti'), 'Nomina Addetti Primo Soccorso', 'D.Lgs 334/99', 'once', '2027-04-18', '2023-02-07', NULL, 7, 'Locale Archivio', 'Vedi Organigramma', 1, 'DdL');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente'), 'Formazione RSPP', 'SIS - Corporate', 'quinquennial', '2031-04-18', '2023-02-07', NULL, 7, NULL, 'Non applicabile. Nomina R.S.P.P. esterno', 1, 'DdL');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente'), 'Decontaminazione/Sanificazione ambienti uffici-spogliatoi-servizi igienici-parti comuni con perossido di idrogeno', NULL, 'once', '2022-12-18', '2023-02-07', '2021-12-18', 7, NULL, NULL, 1, 'Ditta Incaricata');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Controllo scaffalature', 'D.Lgs 81/08 art. 71', 'annual', '2023-01-17', '2023-02-07', '2022-01-17', 7, NULL, 'Audit Interno', 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Valutazione Rischio Esplosione', 'D.M. 10.03.1998, UNI 9994', 'once', '2018-03-24', '2023-02-07', '2017-03-24', 7, 'Locale Archivio', NULL, 1, 'DdL - RSPP Medico - RLS');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente - Rifiuti'), 'Valutazione Rischio Chimico', 'D.Lgs 152/2006 Parte IV', 'once', '2027-03-05', '2026-03-21', '2026-03-05', 7, 'Locale Archivio', 'Accordo Stato Regioni 21.12.2011', 1, 'DdL - RSPP Medico - RLS');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente - Rifiuti'), 'Agibilità locali', 'D.Lgs 152/2006 Parte IV', 'once', '2027-04-18', NULL, NULL, 7, 'Locale Archivio', NULL, 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente - Rifiuti'), 'Autorizzazione Sanitaria', 'D.M. 17/12/2009', 'once', '2027-04-18', NULL, NULL, 7, 'Locale Archivio', NULL, 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente - Rifiuti'), 'Conformità Impianto Elettrico', 'D.M. 17/12/2009', 'once', '2027-04-18', NULL, NULL, 7, 'Locale Archivio', NULL, 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente - Rifiuti'), 'Conformità Impianto Gas', 'D.Lgs 152/2006 Parte IV', 'once', '2027-04-18', NULL, NULL, 7, 'Locale Archivio', NULL, 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente - Rifiuti'), 'Conformità Impianto Idrico', 'D.Lgs 152/2006 Parte IV', 'once', '2027-04-18', NULL, NULL, 7, 'Locale Archivio', NULL, 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Notifica per aziende a rischio rilevante d''incendio', NULL, 'once', '2027-04-18', NULL, NULL, 7, NULL, NULL, 1, '--');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambienti - Scarichi idrici'), 'Rendicontazione indicatori ambientali', 'D.Lgs 152/2006 Parte III (art. 124)', 'annual', '2027-04-18', NULL, NULL, 7, NULL, NULL, 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente - Rifiuti'), 'Etichettatura contenitori rifiuti pericolosi', 'D.M. 10.03.1998, UNI 9994', 'once', '2027-04-18', NULL, NULL, 7, NULL, 'Audit Interno', 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente - Rifiuti'), 'Formulario d’identificazione-compilazione 1° copia e rientro 4° entro 3 mesi', 'D.M. 10.03.1998, UNI 9994', 'once', '2027-04-18', NULL, NULL, 7, NULL, 'Audit Interno', 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente - Rifiuti'), 'Tenuta dei registri di carico e scarico rifiuti', 'DPR 151/2011 (art. 6) D.M. 10/03/1998', 'once', '2027-04-18', NULL, NULL, 7, NULL, 'Audit Interno', 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente - Rifiuti'), 'Verifica autorizzazione trasportatori-smaltitori', 'D.Lgs 81/08 art. 290', 'annual', '2027-04-18', NULL, NULL, 7, NULL, 'Audit Interno', 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente'), 'Monitoraggio consumi: acqua, energia elettrica, metano. materia prima trasformate e rottamata', 'D.M. 10.03.1998', 'monthly', '2026-05-18', NULL, NULL, 7, NULL, 'Audit Interno', 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente'), 'Aggiornamento quaderno conduzione impianti. Verifica ed eventuale pulizia/sostituzione filtri cappe emissioni', 'D.LGS. 06.11.2011', 'once', '2027-04-18', NULL, NULL, 7, NULL, 'vedi registro', 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Ambiente'), 'Invio analisi dati analitici acque scaricate', 'D.M. 10.03.1998, UNI 9994', 'once', '2027-04-18', NULL, NULL, 7, NULL, 'Presenza di scarichi assimilabili ai domestici', 1, '--');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Controllo segnaletica / cartellonistica antincendio', 'D.M. 10.03.1998, UNI 9994', 'semiannual', '2026-10-18', NULL, NULL, 7, 'Locale Archivio', NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Tenuta di REGISTO ANTINCENDIO per attività soggette al DPR 151/2011', 'D.M. 10.03.1998, UNI 9994', 'once', '2027-04-18', NULL, NULL, 7, NULL, NULL, 1, '--');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Valutazione Rischio Incendio', 'D.M. 10.03.1998, UNI 9994', 'once', '2027-04-18', NULL, NULL, 7, NULL, NULL, 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Antincendio'), 'Verifica Maniglioni antipanico non certificati CE', 'D.M. 10.03.1998, UNI 9994', 'semiannual', '2026-10-18', NULL, NULL, 7, NULL, 'Maniglioni certificati CE', 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Formazione'), 'Verifica periodica funzionamento impianto luci di emergenza', 'Norma CEI 11-27:2014 " lavoratori su quadri elettrici"', 'semiannual', '2026-10-18', NULL, NULL, 7, 'Locale Archivio', NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Formazione'), 'Qualifica Appaltatori', 'D.M. 10.03.1998', 'once', '2027-04-18', NULL, NULL, 7, 'Locale Archivio', NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Formazione'), 'DURC aggiornato', 'D.M. 388/2003', 'once', '2027-04-18', NULL, NULL, 7, NULL, NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Formazione'), 'Presenza DUVRI per attività in appalto (aggiornamento o prima emissione)', 'Accordo Stato Regioni 22.02.2012', 'once', '2027-04-18', NULL, NULL, 7, NULL, NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Formazione'), 'Presenza verbale di sopralluogo per coordinamento', 'D.Lgs 81/08 art. 19, Accordo Stato Regioni 2011', 'once', '2027-04-18', NULL, NULL, 7, NULL, 'All''avvio dei lavori, se necessario', 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Manutenzioni'), 'Richiesta all''appaltatore / prestatore d''Opera dell''idoneità tecnico professionale: iscrizione Camera di Commercio, autocertificazione i.d.p.', 'D.M. 329/04', 'once', '2027-04-18', NULL, NULL, 7, NULL, NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Manutenzioni'), 'Verificare che i lavoratori in regime di appalto mostrino apposita tessera di riconoscimento (fotografia, generalità del lavoratore e indicazione del datore di lavoro)', 'D.M. 17/03/03', 'once', '2027-04-18', NULL, NULL, 7, NULL, NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Medicina del Lavoro'), 'Formazione addetti che effettuano lavoro sotto tensione', 'D.Lgs 81/08 art. 25, 40', 'quinquennial', '2031-04-18', NULL, NULL, 7, NULL, 'vedi Elenco formazione', 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Medicina del Lavoro'), 'Formazione Addetti Prev. Incendi', 'D.Lgs 81/08 art. 25', 'triennial', '2029-04-18', NULL, NULL, 7, 'Locale Archivio', 'vedi Elenco formazione', 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Formazione Addetti Primo Soccorso', 'D.Lgs 81/08', 'triennial', '2029-04-18', NULL, NULL, 7, 'Locale Archivio', 'vedi Elenco formazione', 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Formazione Carrellisti', 'D.Lgs 81/08 art. 18', 'quinquennial', '2031-04-18', NULL, NULL, 7, 'Locale Archivio', 'vedi Elenco formazione', 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Formazione del Dirigente', 'D.Lgs 81/08 art. 63, allegato IV', 'quinquennial', '2031-04-18', NULL, NULL, 7, NULL, 'Accordo Stato Regioni 21.12.2011', 1, '--');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Comunicazione Messa in Servizio rec. Pressione', 'D.Lgs 81/08 art. 18,74, 75, 76, 77, 78, 79', 'once', '2019-09-07', NULL, '2018-09-07', 7, NULL, NULL, 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Controllo dispositivi lotta antincendio', 'D.Lgs 81/08 art. 71', 'monthly', '2026-05-18', NULL, NULL, 7, 'Locale Archivio', NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Comunicazione all''INAIL degli infortuni', 'D.Lgs 81/08 art. 71', 'once', '2027-04-18', NULL, NULL, 7, NULL, NULL, 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Conformità Ambienti di Lavoro', 'D.Lgs 81/08 art. 19, 37', 'annual', '2027-04-18', NULL, NULL, 7, NULL, 'Audit Interno', 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Consegna e Gestione DPI', 'D.Lgs 81/08 art. 161', 'once', '2027-04-18', NULL, NULL, 7, NULL, NULL, 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Controllo scale', 'D.Lgs 81/08 art. 167', 'once', '2027-04-18', NULL, NULL, 7, NULL, 'Audit Interno', 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Controllo imbragature, golfari', 'D.Lgs 81/08 art. 172', 'once', '2027-04-18', NULL, NULL, 7, NULL, NULL, 1, '--');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Controllo macchinari', 'D.Lgs 81/08', 'once', '2027-04-18', NULL, NULL, 7, NULL, 'Audit Interno', 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Presenza segnaletica', 'D.Lgs 81/08 art. 271', 'once', '2027-04-18', NULL, NULL, 7, 'Locale Archivio', 'Audit Interno', 1, 'RSPP');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Valutazione del Rischio da MMC', 'D.Lgs 81/08 art. 249', 'once', '2027-04-18', NULL, NULL, 7, 'Locale Archivio', 'in caso di variazioni', 1, 'DdL - RSPP Medico - RLS');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Medicina del Lavoro'), 'Valutazione del Rischio da VDT', 'D.Lgs 81/08 art. 236', 'once', '2027-04-18', NULL, NULL, 7, 'Locale Archivio', 'in caso di variazioni', 1, 'DdL - RSPP Medico - RLS');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Pronto Soccorso'), 'Valutazione Protezione Fulmini', 'D.M. 388/2003', 'once', '2027-04-18', NULL, NULL, 7, 'Locale Archivio', NULL, 1, 'Direzione');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Pronto Soccorso'), 'Valutazione Rischio da Agenti Biologici', 'D.Lgs 81/08 art. 18, 43, 45', 'annual', '2018-05-22', NULL, '2017-05-22', 7, 'Locale Archivio', 'Riunione periodica', 1, 'DdL - RSPP Medico - RLS');
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Valutazione Rischio da Amianto', NULL, 'once', '2027-04-18', NULL, NULL, 7, NULL, NULL, 1, NULL);
|
||||||
|
INSERT INTO scad_deadlines
|
||||||
|
(subject_id, topic, law_regulation, recurrence_type, due_date, check_date,
|
||||||
|
document_date, notification_days, storage_location, notes, created_by, departments)
|
||||||
|
VALUES ((SELECT id FROM scad_subjects WHERE name = 'Prescrizioni generali'), 'Valutazione Rischio da Cancerogeni', NULL, 'once', '2027-04-18', NULL, NULL, 7, NULL, NULL, 1, NULL);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
34
public/userarea/scadenzario/subjects/ajax/delete_subject.php
Normal file
34
public/userarea/scadenzario/subjects/ajax/delete_subject.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/../../ajax/auth_check.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once(__DIR__ . '/../../../class/db-functions.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$id = isset($_POST['id']) && is_numeric($_POST['id']) ? (int)$_POST['id'] : 0;
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'ID non valido.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT COUNT(*) FROM scad_deadlines WHERE subject_id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$inUse = (int)$stmt->fetchColumn();
|
||||||
|
|
||||||
|
if ($inUse > 0) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => "Impossibile eliminare: l'argomento è utilizzato in $inUse scadenz" . ($inUse === 1 ? 'a' : 'e') . '.',
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->prepare("DELETE FROM scad_subjects WHERE id = ?")->execute([$id]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Argomento eliminato.']);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
59
public/userarea/scadenzario/subjects/ajax/save_subject.php
Normal file
59
public/userarea/scadenzario/subjects/ajax/save_subject.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/../../ajax/auth_check.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once(__DIR__ . '/../../../class/db-functions.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$id = isset($_POST['id']) && is_numeric($_POST['id']) ? (int)$_POST['id'] : null;
|
||||||
|
$name = trim($_POST['name'] ?? '');
|
||||||
|
$color = trim($_POST['color'] ?? '');
|
||||||
|
|
||||||
|
if ($name === '') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Il nome è obbligatorio.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if (mb_strlen($name) > 100) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Il nome supera 100 caratteri.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if (!preg_match('/^#[0-9A-Fa-f]{6}$/', $color)) {
|
||||||
|
$color = '#6c757d';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uniqueness check
|
||||||
|
if ($id) {
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM scad_subjects WHERE name = ? AND id <> ?");
|
||||||
|
$stmt->execute([$name, $id]);
|
||||||
|
} else {
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM scad_subjects WHERE name = ?");
|
||||||
|
$stmt->execute([$name]);
|
||||||
|
}
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Esiste già un argomento con questo nome.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($id) {
|
||||||
|
$stmt = $pdo->prepare("UPDATE scad_subjects SET name = ?, color = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$name, $color, $id]);
|
||||||
|
$savedId = $id;
|
||||||
|
} else {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO scad_subjects (name, color) VALUES (?, ?)");
|
||||||
|
$stmt->execute([$name, $color]);
|
||||||
|
$savedId = (int)$pdo->lastInsertId();
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => $id ? 'Argomento aggiornato.' : 'Argomento creato.',
|
||||||
|
'id' => $savedId,
|
||||||
|
'name' => $name,
|
||||||
|
'color' => $color,
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
346
public/userarea/scadenzario/subjects/index.php
Normal file
346
public/userarea/scadenzario/subjects/index.php
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
<?php include('../../include/headscript.php'); ?>
|
||||||
|
<?php
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$subjects = $pdo->query("
|
||||||
|
SELECT s.*,
|
||||||
|
(SELECT COUNT(*) FROM scad_deadlines d WHERE d.subject_id = s.id) AS deadline_count,
|
||||||
|
(SELECT COUNT(*) FROM scad_deadlines d WHERE d.subject_id = s.id AND d.status <> 'completed') AS open_count
|
||||||
|
FROM scad_subjects s
|
||||||
|
ORDER BY s.name
|
||||||
|
")->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
?>
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<?php
|
||||||
|
$scriptDir = dirname($_SERVER['SCRIPT_NAME']);
|
||||||
|
// subjects/index.php -> scadenzario -> userarea
|
||||||
|
$baseHref = dirname(dirname($scriptDir)) . '/';
|
||||||
|
?>
|
||||||
|
<base href="<?= $baseHref ?>">
|
||||||
|
<?php include('../../cssinclude.php'); ?>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<title>Scadenzario - Argomenti</title>
|
||||||
|
<script>if(window.innerWidth>1024)document.addEventListener('DOMContentLoaded',function(){document.getElementById('appWrapper').classList.add('toggled')})</script>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--scad-primary: #5a8fd8;
|
||||||
|
--scad-primary-hover: #4578c0;
|
||||||
|
--scad-heading: #2c3e6b;
|
||||||
|
--scad-card-bg: linear-gradient(135deg, #f0f4ff 0%, #e8eeff 100%);
|
||||||
|
--scad-card-border: #dde4f0;
|
||||||
|
}
|
||||||
|
.scad-card { border: none; border-radius: 0.75rem; box-shadow: 0 2px 12px rgba(0,0,0,0.06); overflow: hidden; }
|
||||||
|
.scad-card .card-header { background: var(--scad-card-bg); border-bottom: 1px solid var(--scad-card-border); padding: 1rem 1.25rem; }
|
||||||
|
.scad-card .card-header h5 { font-weight: 700; color: var(--scad-heading); margin: 0; font-size: 1.1rem; letter-spacing: -0.01em; }
|
||||||
|
.scad-card .card-body { padding: 1.25rem; }
|
||||||
|
|
||||||
|
.btn-scad-primary { background: var(--scad-primary); border: none; color: #fff; font-weight: 600; font-size: 0.85rem; padding: 0.5rem 1rem; border-radius: 0.5rem; transition: all 0.2s; }
|
||||||
|
.btn-scad-primary:hover { background: var(--scad-primary-hover); color: #fff; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(90,143,216,0.35); }
|
||||||
|
.btn-scad-outline { background: transparent; border: 1.5px solid var(--scad-primary); color: var(--scad-primary); font-weight: 600; font-size: 0.85rem; padding: 0.45rem 1rem; border-radius: 0.5rem; transition: all 0.2s; }
|
||||||
|
.btn-scad-outline:hover { background: var(--scad-primary); color: #fff; transform: translateY(-1px); }
|
||||||
|
|
||||||
|
.btn-action { width: 32px; height: 32px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border: none; border-radius: 0.4rem; font-size: 0.85rem; transition: all 0.15s; }
|
||||||
|
.btn-action-edit { background: rgba(90,143,216,0.12); color: var(--scad-primary); }
|
||||||
|
.btn-action-edit:hover { background: var(--scad-primary); color: #fff; }
|
||||||
|
.btn-action-delete { background: rgba(220,53,69,0.12); color: #dc3545; }
|
||||||
|
.btn-action-delete:hover { background: #dc3545; color: #fff; }
|
||||||
|
.btn-action-history { background: rgba(108,117,125,0.12); color: #495057; }
|
||||||
|
.btn-action-history:hover { background: #495057; color: #fff; }
|
||||||
|
|
||||||
|
.color-swatch { width: 28px; height: 28px; border-radius: 6px; display: inline-block; border: 1px solid rgba(0,0,0,0.08); vertical-align: middle; }
|
||||||
|
.subject-row { border-left: 4px solid var(--row-color, #e9ecef); }
|
||||||
|
|
||||||
|
/* Mobile cards */
|
||||||
|
.subject-card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid var(--scad-card-border);
|
||||||
|
border-left: 5px solid var(--row-color, #e9ecef);
|
||||||
|
border-radius: 0.6rem;
|
||||||
|
padding: 0.85rem 0.95rem;
|
||||||
|
margin-bottom: 0.6rem;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
.subject-card .sc-header {
|
||||||
|
display: flex; align-items: center; gap: 0.6rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.subject-card .sc-swatch {
|
||||||
|
width: 22px; height: 22px; border-radius: 5px;
|
||||||
|
border: 1px solid rgba(0,0,0,0.08); flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.subject-card .sc-name {
|
||||||
|
font-weight: 700; color: var(--scad-heading);
|
||||||
|
font-size: 0.95rem; flex: 1; word-break: break-word;
|
||||||
|
}
|
||||||
|
.subject-card .sc-stats {
|
||||||
|
display: flex; gap: 0.75rem; font-size: 0.8rem; color: #6c757d;
|
||||||
|
margin-bottom: 0.6rem;
|
||||||
|
}
|
||||||
|
.subject-card .sc-stats strong { color: var(--scad-heading); }
|
||||||
|
.subject-card .sc-actions {
|
||||||
|
display: flex; gap: 0.4rem; justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state { text-align: center; padding: 3rem 1rem; color: #6c757d; }
|
||||||
|
.empty-state i { font-size: 3rem; opacity: 0.3; margin-bottom: 1rem; }
|
||||||
|
|
||||||
|
/* Color picker swatches */
|
||||||
|
.color-picker-grid { display: grid; grid-template-columns: repeat(10, 1fr); gap: 0.4rem; margin-bottom: 0.75rem; }
|
||||||
|
.color-picker-swatch { width: 100%; aspect-ratio: 1; border-radius: 6px; cursor: pointer; border: 2px solid transparent; transition: all 0.15s; }
|
||||||
|
.color-picker-swatch:hover { transform: scale(1.1); }
|
||||||
|
.color-picker-swatch.selected { border-color: #2c3e6b; transform: scale(1.1); box-shadow: 0 2px 8px rgba(44,62,107,0.3); }
|
||||||
|
|
||||||
|
@media (max-width: 575.98px) {
|
||||||
|
.scad-card .card-header { flex-direction: column; gap: 0.75rem; align-items: flex-start !important; }
|
||||||
|
.header-actions { width: 100%; }
|
||||||
|
.header-actions .btn { width: 100%; justify-content: center; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrapper" id="appWrapper">
|
||||||
|
<?php include('../../include/navbar.php'); ?>
|
||||||
|
<?php include('../../include/topbar.php'); ?>
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<div class="page-content">
|
||||||
|
|
||||||
|
<nav aria-label="breadcrumb" class="mb-3">
|
||||||
|
<ol class="breadcrumb" style="background:transparent;padding:0;margin:0;font-size:0.85rem">
|
||||||
|
<li class="breadcrumb-item"><a href="scadenzario/index.php">Scadenzario</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">Argomenti</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="card scad-card">
|
||||||
|
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||||
|
<h5><i class="fa-solid fa-tags me-2"></i>Argomenti</h5>
|
||||||
|
<div class="header-actions d-flex gap-2 flex-wrap">
|
||||||
|
<a href="scadenzario/index.php" class="btn btn-scad-outline d-inline-flex align-items-center gap-2">
|
||||||
|
<i class="fa-solid fa-arrow-left"></i><span>Scadenzario</span>
|
||||||
|
</a>
|
||||||
|
<button class="btn btn-scad-primary d-inline-flex align-items-center gap-2" id="btnAddSubject">
|
||||||
|
<i class="fa-solid fa-plus"></i><span>Nuovo Argomento</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<?php if (count($subjects) === 0): ?>
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="fa-solid fa-tags"></i>
|
||||||
|
<p>Nessun argomento definito.<br>Clicca <strong>"Nuovo Argomento"</strong> per aggiungere il primo.</p>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div id="subjectsList">
|
||||||
|
<!-- MOBILE: Cards (< md) -->
|
||||||
|
<div class="d-md-none">
|
||||||
|
<?php foreach ($subjects as $s): ?>
|
||||||
|
<div class="subject-card"
|
||||||
|
style="--row-color: <?= htmlspecialchars($s['color'], ENT_QUOTES, 'UTF-8') ?>"
|
||||||
|
data-id="<?= (int)$s['id'] ?>"
|
||||||
|
data-name="<?= htmlspecialchars($s['name'], ENT_QUOTES, 'UTF-8') ?>"
|
||||||
|
data-color="<?= htmlspecialchars($s['color'], ENT_QUOTES, 'UTF-8') ?>"
|
||||||
|
data-in-use="<?= (int)$s['deadline_count'] ?>">
|
||||||
|
<div class="sc-header">
|
||||||
|
<span class="sc-swatch" style="background: <?= htmlspecialchars($s['color'], ENT_QUOTES, 'UTF-8') ?>"></span>
|
||||||
|
<span class="sc-name"><?= htmlspecialchars($s['name'], ENT_QUOTES, 'UTF-8') ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="sc-stats">
|
||||||
|
<span>Scadenze: <strong><?= (int)$s['deadline_count'] ?></strong></span>
|
||||||
|
<span>Aperte: <strong><?= (int)$s['open_count'] ?></strong></span>
|
||||||
|
</div>
|
||||||
|
<div class="sc-actions">
|
||||||
|
<a href="scadenzario/index.php?subject_id=<?= (int)$s['id'] ?>" class="btn-action btn-action-history" title="Storico scadenze">
|
||||||
|
<i class="fa-solid fa-clock-rotate-left"></i>
|
||||||
|
</a>
|
||||||
|
<button class="btn-action btn-action-edit btn-edit" title="Modifica"><i class="fa-solid fa-pen"></i></button>
|
||||||
|
<button class="btn-action btn-action-delete btn-delete" title="Elimina"><i class="fa-solid fa-trash"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- DESKTOP: Table (>= md) -->
|
||||||
|
<div class="d-none d-md-block">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:60px">Colore</th>
|
||||||
|
<th>Nome</th>
|
||||||
|
<th class="text-center" style="width:120px">Scadenze</th>
|
||||||
|
<th class="text-center" style="width:120px">Aperte</th>
|
||||||
|
<th class="text-center" style="width:180px">Azioni</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($subjects as $s): ?>
|
||||||
|
<tr class="subject-row"
|
||||||
|
style="--row-color: <?= htmlspecialchars($s['color'], ENT_QUOTES, 'UTF-8') ?>"
|
||||||
|
data-id="<?= (int)$s['id'] ?>"
|
||||||
|
data-name="<?= htmlspecialchars($s['name'], ENT_QUOTES, 'UTF-8') ?>"
|
||||||
|
data-color="<?= htmlspecialchars($s['color'], ENT_QUOTES, 'UTF-8') ?>"
|
||||||
|
data-in-use="<?= (int)$s['deadline_count'] ?>">
|
||||||
|
<td><span class="color-swatch" style="background: <?= htmlspecialchars($s['color'], ENT_QUOTES, 'UTF-8') ?>"></span></td>
|
||||||
|
<td class="fw-semibold" style="color:var(--scad-heading)"><?= htmlspecialchars($s['name'], ENT_QUOTES, 'UTF-8') ?></td>
|
||||||
|
<td class="text-center"><?= (int)$s['deadline_count'] ?></td>
|
||||||
|
<td class="text-center"><?= (int)$s['open_count'] ?></td>
|
||||||
|
<td class="text-center">
|
||||||
|
<div class="d-inline-flex gap-1">
|
||||||
|
<a href="scadenzario/index.php?subject_id=<?= (int)$s['id'] ?>" class="btn-action btn-action-history" title="Storico scadenze">
|
||||||
|
<i class="fa-solid fa-clock-rotate-left"></i>
|
||||||
|
</a>
|
||||||
|
<button class="btn-action btn-action-edit btn-edit" title="Modifica"><i class="fa-solid fa-pen"></i></button>
|
||||||
|
<button class="btn-action btn-action-delete btn-delete" title="Elimina"><i class="fa-solid fa-trash"></i></button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php include('../../include/footer.php'); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Subject Modal -->
|
||||||
|
<div class="modal fade" id="subjectModal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="subjectModalTitle">Nuovo Argomento</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Chiudi"></button>
|
||||||
|
</div>
|
||||||
|
<form id="subjectForm">
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" id="subjId" name="id" value="">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="subjName" class="form-label fw-semibold">Nome <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control" id="subjName" name="name" maxlength="100" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Colore</label>
|
||||||
|
<div class="color-picker-grid" id="colorPickerGrid"></div>
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<input type="color" class="form-control form-control-color" id="subjColor" name="color" value="#6c757d" style="width:56px;height:38px;padding:2px">
|
||||||
|
<input type="text" class="form-control" id="subjColorText" maxlength="7" placeholder="#RRGGBB" style="max-width:130px;font-family:monospace">
|
||||||
|
<span class="text-muted small">Personalizzato</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Annulla</button>
|
||||||
|
<button type="submit" class="btn btn-scad-primary">Salva</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php include('../../jsinclude.php'); ?>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
const PRESET_COLORS = [
|
||||||
|
'#dc3545','#e8930c','#ffc107','#198754','#20c997',
|
||||||
|
'#0dcaf0','#0d6efd','#5a8fd8','#6f42c1','#d63384',
|
||||||
|
'#6c757d','#495057','#212529','#8b4513','#795548',
|
||||||
|
'#b88a44','#e83e8c','#17a2b8','#28a745','#343a40'
|
||||||
|
];
|
||||||
|
|
||||||
|
function buildPicker(selected) {
|
||||||
|
const $grid = $('#colorPickerGrid').empty();
|
||||||
|
PRESET_COLORS.forEach(c => {
|
||||||
|
const $sw = $('<div class="color-picker-swatch"></div>')
|
||||||
|
.css('background', c)
|
||||||
|
.attr('data-color', c);
|
||||||
|
if (c.toLowerCase() === (selected || '').toLowerCase()) $sw.addClass('selected');
|
||||||
|
$sw.on('click', function () {
|
||||||
|
$('#colorPickerGrid .color-picker-swatch').removeClass('selected');
|
||||||
|
$(this).addClass('selected');
|
||||||
|
$('#subjColor').val(c);
|
||||||
|
$('#subjColorText').val(c);
|
||||||
|
});
|
||||||
|
$grid.append($sw);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openModal(data) {
|
||||||
|
const isEdit = !!data;
|
||||||
|
$('#subjectModalTitle').text(isEdit ? 'Modifica Argomento' : 'Nuovo Argomento');
|
||||||
|
$('#subjId').val(isEdit ? data.id : '');
|
||||||
|
$('#subjName').val(isEdit ? data.name : '');
|
||||||
|
const color = isEdit ? data.color : '#6c757d';
|
||||||
|
$('#subjColor').val(color);
|
||||||
|
$('#subjColorText').val(color);
|
||||||
|
buildPicker(color);
|
||||||
|
new bootstrap.Modal('#subjectModal').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#btnAddSubject').on('click', () => openModal(null));
|
||||||
|
|
||||||
|
$('#subjectsList').on('click', '.btn-edit', function () {
|
||||||
|
const $tr = $(this).closest('[data-id]');
|
||||||
|
openModal({ id: $tr.data('id'), name: $tr.data('name'), color: $tr.data('color') });
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#subjectsList').on('click', '.btn-delete', function () {
|
||||||
|
const $tr = $(this).closest('[data-id]');
|
||||||
|
const inUse = parseInt($tr.data('in-use') || 0, 10);
|
||||||
|
const name = $tr.data('name');
|
||||||
|
if (inUse > 0) {
|
||||||
|
Swal.fire({ icon: 'warning', title: 'Impossibile eliminare',
|
||||||
|
text: `L'argomento "${name}" è utilizzato in ${inUse} scadenz${inUse === 1 ? 'a' : 'e'}.` });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Swal.fire({
|
||||||
|
title: `Eliminare "${name}"?`,
|
||||||
|
icon: 'warning', showCancelButton: true,
|
||||||
|
confirmButtonText: 'Elimina', cancelButtonText: 'Annulla',
|
||||||
|
confirmButtonColor: '#dc3545'
|
||||||
|
}).then(r => {
|
||||||
|
if (!r.isConfirmed) return;
|
||||||
|
$.post('scadenzario/subjects/ajax/delete_subject.php', { id: $tr.data('id') })
|
||||||
|
.done(res => {
|
||||||
|
if (res.success) { location.reload(); }
|
||||||
|
else { Swal.fire({ icon: 'error', title: 'Errore', text: res.message }); }
|
||||||
|
})
|
||||||
|
.fail(() => Swal.fire({ icon: 'error', title: 'Errore di rete' }));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#subjColor').on('input', function () { $('#subjColorText').val($(this).val()); });
|
||||||
|
$('#subjColorText').on('input', function () {
|
||||||
|
const v = $(this).val();
|
||||||
|
if (/^#[0-9A-Fa-f]{6}$/.test(v)) $('#subjColor').val(v);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#subjectForm').on('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const payload = {
|
||||||
|
id: $('#subjId').val(),
|
||||||
|
name: $('#subjName').val().trim(),
|
||||||
|
color: $('#subjColor').val()
|
||||||
|
};
|
||||||
|
if (!payload.name) { Swal.fire({ icon: 'warning', title: 'Nome obbligatorio' }); return; }
|
||||||
|
$.post('scadenzario/subjects/ajax/save_subject.php', payload)
|
||||||
|
.done(res => {
|
||||||
|
if (res.success) { location.reload(); }
|
||||||
|
else { Swal.fire({ icon: 'error', title: 'Errore', text: res.message }); }
|
||||||
|
})
|
||||||
|
.fail(() => Swal.fire({ icon: 'error', title: 'Errore di rete' }));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user