From e5bf546ae79f0ede7f8e9e87c86d0d78476decc2 Mon Sep 17 00:00:00 2001 From: solocla Date: Fri, 5 Jun 2026 14:35:19 +0200 Subject: [PATCH] fixed funzioni aziendali --- ...lter_scad_functions_add_contact_fields.php | 138 ++++ public/userarea/company-functions.php | 617 ++++++++++++++++++ public/userarea/include/navbar.php | 5 + .../userarea/scadenzario/functions/index.php | 417 ++++++++++-- 4 files changed, 1127 insertions(+), 50 deletions(-) create mode 100644 db/migrations/20260605095127_alter_scad_functions_add_contact_fields.php create mode 100644 public/userarea/company-functions.php diff --git a/db/migrations/20260605095127_alter_scad_functions_add_contact_fields.php b/db/migrations/20260605095127_alter_scad_functions_add_contact_fields.php new file mode 100644 index 0000000..8a57493 --- /dev/null +++ b/db/migrations/20260605095127_alter_scad_functions_add_contact_fields.php @@ -0,0 +1,138 @@ +hasTable('scad_functions')) { + throw new RuntimeException('Table scad_functions does not exist.'); + } + + $table = $this->table('scad_functions'); + + if (!$table->hasColumn('person_full_name')) { + $table->addColumn('person_full_name', 'string', [ + 'limit' => 200, + 'null' => true, + 'after' => 'description', + 'comment' => 'Full name and surname of the person assigned to the function', + ]); + } + + if (!$table->hasColumn('phone')) { + $table->addColumn('phone', 'string', [ + 'limit' => 80, + 'null' => true, + 'after' => 'person_full_name', + ]); + } + + if (!$table->hasColumn('email')) { + $table->addColumn('email', 'string', [ + 'limit' => 190, + 'null' => true, + 'after' => 'phone', + ]); + } + + if (!$table->hasColumn('notes')) { + $table->addColumn('notes', 'text', [ + 'null' => true, + 'after' => 'email', + ]); + } + + if (!$table->hasColumn('sort_order')) { + $table->addColumn('sort_order', 'integer', [ + 'signed' => false, + 'null' => false, + 'default' => 0, + 'after' => 'status', + ]); + } + + if (!$table->hasIndexByName('idx_scad_functions_name')) { + $table->addIndex(['name'], [ + 'name' => 'idx_scad_functions_name', + ]); + } + + if (!$table->hasIndexByName('idx_scad_functions_person_full_name')) { + $table->addIndex(['person_full_name'], [ + 'name' => 'idx_scad_functions_person_full_name', + ]); + } + + if (!$table->hasIndexByName('idx_scad_functions_email')) { + $table->addIndex(['email'], [ + 'name' => 'idx_scad_functions_email', + ]); + } + + if (!$table->hasIndexByName('idx_scad_functions_status_sort')) { + $table->addIndex(['status', 'sort_order'], [ + 'name' => 'idx_scad_functions_status_sort', + ]); + } + + $table->update(); + + // Set a default order for existing rows without changing their names. + $this->execute(" + UPDATE scad_functions + SET sort_order = id * 10 + WHERE sort_order = 0 + "); + } + + public function down(): void + { + if (!$this->hasTable('scad_functions')) { + return; + } + + $table = $this->table('scad_functions'); + + if ($table->hasIndexByName('idx_scad_functions_status_sort')) { + $table->removeIndexByName('idx_scad_functions_status_sort'); + } + + if ($table->hasIndexByName('idx_scad_functions_email')) { + $table->removeIndexByName('idx_scad_functions_email'); + } + + if ($table->hasIndexByName('idx_scad_functions_person_full_name')) { + $table->removeIndexByName('idx_scad_functions_person_full_name'); + } + + if ($table->hasIndexByName('idx_scad_functions_name')) { + $table->removeIndexByName('idx_scad_functions_name'); + } + + if ($table->hasColumn('sort_order')) { + $table->removeColumn('sort_order'); + } + + if ($table->hasColumn('notes')) { + $table->removeColumn('notes'); + } + + if ($table->hasColumn('email')) { + $table->removeColumn('email'); + } + + if ($table->hasColumn('phone')) { + $table->removeColumn('phone'); + } + + if ($table->hasColumn('person_full_name')) { + $table->removeColumn('person_full_name'); + } + + $table->update(); + } +} diff --git a/public/userarea/company-functions.php b/public/userarea/company-functions.php new file mode 100644 index 0000000..49443af --- /dev/null +++ b/public/userarea/company-functions.php @@ -0,0 +1,617 @@ +getConnection(); + +function jsonResponse(array $data): void +{ + header('Content-Type: application/json; charset=utf-8'); + echo json_encode($data); + exit; +} + +function normalizeNullableInt($value): ?int +{ + return (isset($value) && $value !== '') ? (int)$value : null; +} + +function normalizeBoolValue($value): int +{ + return ((string)$value === '0') ? 0 : 1; +} + +function cleanString(?string $value): string +{ + return trim((string)$value); +} + +/* ========================================== + AJAX HANDLERS + ========================================== */ +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax']) && $_POST['ajax'] == '1') { + $action = $_POST['action'] ?? ''; + + try { + if ($action === 'add') { + $functionName = cleanString($_POST['function_name'] ?? ''); + $personFullName = cleanString($_POST['person_full_name'] ?? ''); + $phone = cleanString($_POST['phone'] ?? ''); + $email = cleanString($_POST['email'] ?? ''); + $notes = cleanString($_POST['notes'] ?? ''); + $sortOrder = normalizeNullableInt($_POST['sort_order'] ?? '0') ?? 0; + $isActive = normalizeBoolValue($_POST['is_active'] ?? '1'); + + if ($functionName === '') { + jsonResponse(['success' => false, 'message' => 'Il nome funzione è obbligatorio.']); + } + + if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) { + jsonResponse(['success' => false, 'message' => 'Email non valida.']); + } + + $stmt = $pdo->prepare("\n INSERT INTO company_functions\n (function_name, person_full_name, phone, email, notes, sort_order, is_active, created_at, updated_at)\n VALUES\n (:function_name, :person_full_name, :phone, :email, :notes, :sort_order, :is_active, NOW(), NOW())\n "); + + $stmt->execute([ + 'function_name' => $functionName, + 'person_full_name' => $personFullName !== '' ? $personFullName : '', + 'phone' => $phone !== '' ? $phone : null, + 'email' => $email !== '' ? $email : null, + 'notes' => $notes !== '' ? $notes : null, + 'sort_order' => $sortOrder, + 'is_active' => $isActive, + ]); + + jsonResponse(['success' => true, 'message' => 'Funzione salvata correttamente.']); + } + + if ($action === 'edit') { + $id = (int)($_POST['id'] ?? 0); + $functionName = cleanString($_POST['function_name'] ?? ''); + $personFullName = cleanString($_POST['person_full_name'] ?? ''); + $phone = cleanString($_POST['phone'] ?? ''); + $email = cleanString($_POST['email'] ?? ''); + $notes = cleanString($_POST['notes'] ?? ''); + $sortOrder = normalizeNullableInt($_POST['sort_order'] ?? '0') ?? 0; + $isActive = normalizeBoolValue($_POST['is_active'] ?? '1'); + + if ($id <= 0) { + jsonResponse(['success' => false, 'message' => 'ID funzione non valido.']); + } + + if ($functionName === '') { + jsonResponse(['success' => false, 'message' => 'Il nome funzione è obbligatorio.']); + } + + if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) { + jsonResponse(['success' => false, 'message' => 'Email non valida.']); + } + + $stmt = $pdo->prepare("\n UPDATE company_functions\n SET function_name = :function_name,\n person_full_name = :person_full_name,\n phone = :phone,\n email = :email,\n notes = :notes,\n sort_order = :sort_order,\n is_active = :is_active,\n updated_at = NOW()\n WHERE id = :id\n "); + + $stmt->execute([ + 'function_name' => $functionName, + 'person_full_name' => $personFullName !== '' ? $personFullName : '', + 'phone' => $phone !== '' ? $phone : null, + 'email' => $email !== '' ? $email : null, + 'notes' => $notes !== '' ? $notes : null, + 'sort_order' => $sortOrder, + 'is_active' => $isActive, + 'id' => $id, + ]); + + jsonResponse(['success' => true, 'message' => 'Funzione aggiornata correttamente.']); + } + + if ($action === 'delete') { + $id = (int)($_POST['id'] ?? 0); + + if ($id <= 0) { + jsonResponse(['success' => false, 'message' => 'ID funzione non valido.']); + } + + $stmt = $pdo->prepare("DELETE FROM company_functions WHERE id = :id"); + $stmt->execute(['id' => $id]); + + jsonResponse(['success' => true, 'message' => 'Funzione cancellata correttamente.']); + } + + jsonResponse(['success' => false, 'message' => 'Azione non riconosciuta.']); + } catch (Exception $e) { + jsonResponse(['success' => false, 'message' => $e->getMessage()]); + } +} + +/* ========================================== + PAGE DATA + ========================================== */ +$stmtFunctions = $pdo->query("\n SELECT id, function_name, person_full_name, phone, email, notes, sort_order, is_active, created_at, updated_at\n FROM company_functions\n ORDER BY is_active DESC, sort_order ASC, function_name ASC, person_full_name ASC\n"); +$functions = $stmtFunctions->fetchAll(PDO::FETCH_ASSOC); +?> + + + + + + + + + + Funzioni Aziendali - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?> + + + + + + + + + + + + +
+ + + +
+
+
+
+
Funzioni Aziendali
+ +
+ +
+
+
+
Elenco Funzioni
+
Gestione di RSPP, medico del lavoro, RLS e altre funzioni aziendali.
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
FunzioneNominativoContattiNoteOrdineStatoAzioni
+
+
+ +
+ + Da definire + +
+ + + 📞 + + + + + + ✉️ + + + + + Nessun contatto + + + +
+ +
+ + + +
+ + Attiva + + Non attiva + + + + + +
+
+
+
+
+
+ + +
+ + + + + + + + + + diff --git a/public/userarea/include/navbar.php b/public/userarea/include/navbar.php index 7c2e5af..ba0b24b 100644 --- a/public/userarea/include/navbar.php +++ b/public/userarea/include/navbar.php @@ -359,6 +359,11 @@ Calendario +
  • + + Funzioni Aziendali + +
  • diff --git a/public/userarea/scadenzario/functions/index.php b/public/userarea/scadenzario/functions/index.php index d1da875..8b48ff1 100644 --- a/public/userarea/scadenzario/functions/index.php +++ b/public/userarea/scadenzario/functions/index.php @@ -1,15 +1,122 @@ getConnection(); -$functions = $pdo->query(" - SELECT f.*, - (SELECT COUNT(*) FROM scad_deadlines d WHERE d.function_id = f.id) AS deadline_count, - (SELECT COUNT(*) FROM scad_deadlines d WHERE d.function_id = f.id AND d.status <> 'completed') AS open_count - FROM scad_functions f - ORDER BY f.name ASC -")->fetchAll(PDO::FETCH_ASSOC); +function scadJsonResponse(array $data): void +{ + header('Content-Type: application/json; charset=utf-8'); + echo json_encode($data); + exit; +} + +function scadNullableString($value): ?string +{ + $value = trim((string)($value ?? '')); + return $value !== '' ? $value : null; +} + +function scadNormalizeStatus(string $status): string +{ + return in_array($status, ['active', 'inactive'], true) ? $status : 'active'; +} + +function scadValidateEmail($email): ?string +{ + $email = scadNullableString($email); + + if ($email !== null && !filter_var($email, FILTER_VALIDATE_EMAIL)) { + throw new Exception('Email non valida.'); + } + + return $email; +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax']) && $_POST['ajax'] === '1') { + try { + $action = $_POST['action'] ?? ''; + + if ($action === 'save') { + $id = isset($_POST['id']) && $_POST['id'] !== '' ? (int)$_POST['id'] : 0; + $name = trim($_POST['name'] ?? ''); + $description = scadNullableString($_POST['description'] ?? null); + $personFullName = scadNullableString($_POST['person_full_name'] ?? null); + $phone = scadNullableString($_POST['phone'] ?? null); + $email = scadValidateEmail($_POST['email'] ?? null); + $notes = scadNullableString($_POST['notes'] ?? null); + $sortOrder = isset($_POST['sort_order']) && $_POST['sort_order'] !== '' ? (int)$_POST['sort_order'] : 0; + $status = scadNormalizeStatus(trim($_POST['status'] ?? 'active')); + + if ($name === '') { + scadJsonResponse(['success' => false, 'message' => 'Il nome funzione è obbligatorio.']); + } + + if ($id > 0) { + $stmt = $pdo->prepare("\n UPDATE scad_functions\n SET name = :name,\n description = :description,\n person_full_name = :person_full_name,\n phone = :phone,\n email = :email,\n notes = :notes,\n sort_order = :sort_order,\n status = :status,\n updated_at = NOW()\n WHERE id = :id\n "); + $stmt->execute([ + 'name' => $name, + 'description' => $description, + 'person_full_name' => $personFullName, + 'phone' => $phone, + 'email' => $email, + 'notes' => $notes, + 'sort_order' => $sortOrder, + 'status' => $status, + 'id' => $id, + ]); + + scadJsonResponse(['success' => true, 'message' => 'Funzione aggiornata correttamente.']); + } + + $stmt = $pdo->prepare("\n INSERT INTO scad_functions\n (name, description, person_full_name, phone, email, notes, sort_order, status, created_at, updated_at)\n VALUES\n (:name, :description, :person_full_name, :phone, :email, :notes, :sort_order, :status, NOW(), NOW())\n "); + $stmt->execute([ + 'name' => $name, + 'description' => $description, + 'person_full_name' => $personFullName, + 'phone' => $phone, + 'email' => $email, + 'notes' => $notes, + 'sort_order' => $sortOrder, + 'status' => $status, + ]); + + scadJsonResponse(['success' => true, 'message' => 'Funzione creata correttamente.']); + } + + if ($action === 'delete') { + $id = (int)($_POST['id'] ?? 0); + + if ($id <= 0) { + scadJsonResponse(['success' => false, 'message' => 'ID funzione non valido.']); + } + + $stmtUse = $pdo->prepare('SELECT COUNT(*) FROM scad_deadlines WHERE function_id = ?'); + $stmtUse->execute([$id]); + $inUse = (int)$stmtUse->fetchColumn(); + + if ($inUse > 0) { + scadJsonResponse([ + 'success' => false, + 'message' => 'Impossibile eliminare: la funzione è utilizzata in ' . $inUse . ' scadenza/e.' + ]); + } + + $stmt = $pdo->prepare('DELETE FROM scad_functions WHERE id = :id'); + $stmt->execute(['id' => $id]); + + scadJsonResponse(['success' => true, 'message' => 'Funzione eliminata correttamente.']); + } + + scadJsonResponse(['success' => false, 'message' => 'Azione non riconosciuta.']); + } catch (Exception $e) { + scadJsonResponse(['success' => false, 'message' => $e->getMessage()]); + } +} + +$functions = $pdo->query("\n SELECT f.*,\n (SELECT COUNT(*) FROM scad_deadlines d WHERE d.function_id = f.id) AS deadline_count,\n (SELECT COUNT(*) FROM scad_deadlines d WHERE d.function_id = f.id AND d.status <> 'completed') AS open_count\n FROM scad_functions f\n ORDER BY COALESCE(f.sort_order, 0) ASC, f.name ASC\n")->fetchAll(PDO::FETCH_ASSOC); ?> @@ -24,11 +131,13 @@ $functions = $pdo->query(" + Scadenzario - Funzioni @@ -39,6 +148,7 @@ $functions = $pdo->query(" --scad-heading: #2c3e6b; --scad-card-bg: linear-gradient(135deg, #f0f4ff 0%, #e8eeff 100%); --scad-card-border: #dde4f0; + --scad-muted: #6c757d; } .scad-card { @@ -127,6 +237,63 @@ $functions = $pdo->query(" color: #fff; } + .function-table th { + font-size: 0.82rem; + color: var(--scad-heading); + background: #f8fafc; + border-bottom: 1px solid var(--scad-card-border); + white-space: nowrap; + } + + .function-table td { + font-size: 0.9rem; + vertical-align: middle; + } + + .function-name { + font-weight: 700; + color: var(--scad-heading); + } + + .function-description, + .function-notes { + color: var(--scad-muted); + font-size: 0.82rem; + margin-top: 2px; + max-width: 280px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .contact-line { + font-size: 0.85rem; + line-height: 1.45; + } + + .contact-line a { + text-decoration: none; + } + + .badge-function-status { + display: inline-flex; + align-items: center; + border-radius: 999px; + padding: 4px 10px; + font-size: 0.78rem; + font-weight: 700; + } + + .badge-function-status.active { + background: #d1fae5; + color: #065f46; + } + + .badge-function-status.inactive { + background: #e5e7eb; + color: #374151; + } + .function-card { background: #fff; border: 1px solid var(--scad-card-border); @@ -141,12 +308,19 @@ $functions = $pdo->query(" font-size: 0.95rem; } + .function-card .fc-meta { + font-size: 0.82rem; + color: var(--scad-muted); + margin-top: 0.35rem; + } + .function-card .fc-stats { display: flex; gap: 0.75rem; font-size: 0.8rem; - color: #6c757d; + color: var(--scad-muted); margin: 0.5rem 0; + flex-wrap: wrap; } .function-card .fc-stats strong { @@ -156,7 +330,7 @@ $functions = $pdo->query(" .empty-state { text-align: center; padding: 3rem 1rem; - color: #6c757d; + color: var(--scad-muted); } .empty-state i { @@ -165,6 +339,15 @@ $functions = $pdo->query(" margin-bottom: 1rem; } + .modal-content { + border-radius: 0.75rem; + } + + .modal-header { + background: var(--scad-card-bg); + border-bottom: 1px solid var(--scad-card-border); + } + @media (max-width: 575.98px) { .scad-card .card-header { flex-direction: column; @@ -223,25 +406,48 @@ $functions = $pdo->query("
    -
    +
    -
    +
    +
    + +
    -
    +
    + + + +
    Referente:
    + + + +
    + 📞 + ' : '' ?>✉️ +
    Scadenze: Aperte: + Ordine:
    @@ -258,30 +464,63 @@ $functions = $pdo->query("
    - +
    - - - - - + + + + + + + + + - + - + +
    NomeDescrizioneScadenzeAperteAzioniOrd.FunzioneReferenteContattiStatoScadenzeAperteAzioni
    - + +
    + +
    + +
    + + +
    + Note: +
    +
    - + —' ?> + +
    📞
    + + +
    ✉️
    + + + +
    @@ -300,9 +539,7 @@ $functions = $pdo->query("
    -
    -
    @@ -314,7 +551,7 @@ $functions = $pdo->query("