From 3e94fdb6d94bb15b2a76e1b1ebda63bd8bbd9509 Mon Sep 17 00:00:00 2001 From: AV77web Date: Tue, 14 Jul 2026 10:31:30 +0200 Subject: [PATCH] commit per adattare il data model alla web app Academy aziendale --- initdb/01_schema.sql | 80 ++++++++++++++++++++++++--- initdb/02_seed.sql | 92 +++++++++++++++++++++++++++++--- scripts/apply-academy-schema.sql | 68 +++++++++++++++++++++++ scripts/apply-test-users.sql | 7 +-- src/errors/db.ts | 73 ++++++++++++------------- src/middleware/auth.ts | 16 ++++-- src/routes/utenti.ts | 10 +++- src/types/index.ts | 47 +++++++++------- src/validation/constraints.ts | 59 +++++++++----------- 9 files changed, 340 insertions(+), 112 deletions(-) create mode 100644 scripts/apply-academy-schema.sql diff --git a/initdb/01_schema.sql b/initdb/01_schema.sql index f15adbe..25ad131 100644 --- a/initdb/01_schema.sql +++ b/initdb/01_schema.sql @@ -1,28 +1,94 @@ -- ============================================================ --- Schema tabelle – esameits24-26 --- Allineato alle API: /utenti, +-- Schema tabelle – esameits24-26 (Academy aziendale) +-- Allineato alle API: /utenti, /corsi, /assegnazioni-corsi, /statistiche -- ============================================================ USE `esameits24-26`; -- ------------------------------------------------------------ --- Tabella UTENTI --- Ruoli: Operatore | Amministratore +-- Tabella UTENTE +-- Ruoli: Dipendente | Referente Academy -- ------------------------------------------------------------ CREATE TABLE IF NOT EXISTS utente ( - UtenteID INT UNSIGNED NOT NULL AUTO_INCREMENT, + UtenteID INT UNSIGNED NOT NULL AUTO_INCREMENT, Nome VARCHAR(100) NOT NULL, Cognome VARCHAR(100) NOT NULL, Email VARCHAR(255) NOT NULL, PasswordHash VARCHAR(255) NOT NULL COMMENT 'Password salvata come hash (es. bcrypt)', - Ruolo ENUM("Amministratore", "Operatore") NOT NULL DEFAULT "Operatore", + Ruolo ENUM('Dipendente', 'Referente Academy') NOT NULL DEFAULT 'Dipendente', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (UtenteID), - UNIQUE KEY uk_utente_email (Email) + UNIQUE KEY uk_utente_email (Email), + KEY idx_utente_ruolo (Ruolo) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +-- ------------------------------------------------------------ +-- Tabella CORSO (catalogo Academy) +-- ------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS corso ( + CorsoID INT UNSIGNED NOT NULL AUTO_INCREMENT, + Titolo VARCHAR(200) NOT NULL, + Descrizione TEXT NULL, + Categoria VARCHAR(100) NOT NULL, + DurataOre SMALLINT UNSIGNED NOT NULL, + Obbligatorio TINYINT(1) NOT NULL DEFAULT 0, + Attivo TINYINT(1) NOT NULL DEFAULT 1, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (CorsoID), + KEY idx_corso_categoria (Categoria), + KEY idx_corso_attivo (Attivo), + CONSTRAINT chk_corso_durata_ore CHECK (DurataOre > 0) +) ENGINE=InnoDB + DEFAULT CHARSET=utf8mb4 + COLLATE=utf8mb4_unicode_ci; + +-- ------------------------------------------------------------ +-- Tabella ASSEGNAZIONE_CORSO +-- Stati: Assegnato | Completato | Scaduto | Annullato +-- ------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS assegnazione_corso ( + AssegnazioneID INT UNSIGNED NOT NULL AUTO_INCREMENT, + CorsoID INT UNSIGNED NOT NULL, + DipendenteID INT UNSIGNED NOT NULL, + DataAssegnazione DATE NOT NULL, + DataScadenza DATE NOT NULL, + Stato ENUM('Assegnato', 'Completato', 'Scaduto', 'Annullato') NOT NULL DEFAULT 'Assegnato', + DataCompletamento DATE NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + PRIMARY KEY (AssegnazioneID), + KEY idx_assegnazione_corso (CorsoID), + KEY idx_assegnazione_dipendente (DipendenteID), + KEY idx_assegnazione_stato (Stato), + KEY idx_assegnazione_data_assegnazione (DataAssegnazione), + KEY idx_assegnazione_data_scadenza (DataScadenza), + + CONSTRAINT fk_assegnazione_corso + FOREIGN KEY (CorsoID) REFERENCES corso (CorsoID) + ON UPDATE CASCADE ON DELETE RESTRICT, + + CONSTRAINT fk_assegnazione_dipendente + FOREIGN KEY (DipendenteID) REFERENCES utente (UtenteID) + ON UPDATE CASCADE ON DELETE RESTRICT, + + CONSTRAINT chk_assegnazione_scadenza + CHECK (DataScadenza >= DataAssegnazione), + + CONSTRAINT chk_assegnazione_completamento + CHECK (DataCompletamento IS NULL OR DataCompletamento >= DataAssegnazione), + + CONSTRAINT chk_assegnazione_completato + CHECK ( + (Stato = 'Completato' AND DataCompletamento IS NOT NULL) + OR (Stato <> 'Completato' AND DataCompletamento IS NULL) + ) +) ENGINE=InnoDB + DEFAULT CHARSET=utf8mb4 + COLLATE=utf8mb4_unicode_ci; diff --git a/initdb/02_seed.sql b/initdb/02_seed.sql index 2dcf7ab..5abad2a 100644 --- a/initdb/02_seed.sql +++ b/initdb/02_seed.sql @@ -1,9 +1,11 @@ -- ============================================================ --- Dati iniziali di test – esameits24-26 +-- Dati iniziali di test – esameits24-26 (Academy aziendale) -- Password per tutti gli utenti: Password123! --- 1 Andrea Villari – admin@example.it (amministratore) --- 2 Maria Bianchi – maria.bianchi@example.it (operatore) --- 3 Luca Conti – luca.conti@example.it (operatore) +-- +-- 1 Andrea Villari – admin@academy.it (Referente Academy) +-- 2 Maria Bianchi – maria.bianchi@academy.it (Dipendente) +-- 3 Luca Conti – luca.conti@academy.it (Dipendente) +-- 4 Elena Rossi – elena.rossi@academy.it (Dipendente) -- ============================================================ USE `esameits24-26`; @@ -12,6 +14,82 @@ USE `esameits24-26`; -- Utenti -- ------------------------------------------------------------ INSERT INTO utente (Nome, Cognome, Email, PasswordHash, Ruolo) VALUES - ('Andrea', 'Villari', 'admin@example.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Amministratore'), - ('Maria', 'Bianchi', 'maria.bianchi@example.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Operatore'), - ('Luca', 'Conti', 'luca.conti@example.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Operatore'); \ No newline at end of file + ('Andrea', 'Villari', 'admin@academy.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Referente Academy'), + ('Maria', 'Bianchi', 'maria.bianchi@academy.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Dipendente'), + ('Luca', 'Conti', 'luca.conti@academy.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Dipendente'), + ('Elena', 'Rossi', 'elena.rossi@academy.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Dipendente'); + +-- ------------------------------------------------------------ +-- Catalogo corsi +-- ------------------------------------------------------------ +INSERT INTO corso (Titolo, Descrizione, Categoria, DurataOre, Obbligatorio, Attivo) VALUES + ( + 'Sicurezza sul lavoro', + 'Norme base su rischi, DPI e procedure di emergenza in azienda.', + 'Sicurezza', + 8, + 1, + 1 + ), + ( + 'GDPR e privacy', + 'Trattamento dei dati personali, diritti degli interessati e documentazione privacy.', + 'Compliance', + 4, + 1, + 1 + ), + ( + 'Comunicazione efficace', + 'Tecniche di ascolto attivo, feedback e gestione dei conflitti nel team.', + 'Soft Skills', + 6, + 0, + 1 + ), + ( + 'Excel avanzato', + 'Tabelle pivot, funzioni avanzate e automazioni per report aziendali.', + 'IT', + 10, + 0, + 1 + ), + ( + 'Primo soccorso (edizione 2023)', + 'Corso storico non più disponibile per nuove assegnazioni.', + 'Sicurezza', + 4, + 0, + 0 + ); + +-- ------------------------------------------------------------ +-- Assegnazioni corsi +-- UtenteID: 2 Maria, 3 Luca, 4 Elena +-- CorsoID: 1 Sicurezza, 2 GDPR, 3 Comunicazione, 4 Excel, 5 Primo soccorso +-- ------------------------------------------------------------ +INSERT INTO assegnazione_corso ( + CorsoID, + DipendenteID, + DataAssegnazione, + DataScadenza, + Stato, + DataCompletamento +) VALUES + -- Maria: mix di stati per test filtri e statistiche maggio 2026 + (1, 2, '2026-05-01', '2026-06-30', 'Assegnato', NULL), + (2, 2, '2026-04-10', '2026-05-20', 'Completato', '2026-05-12'), + (4, 2, '2026-01-15', '2026-03-01', 'Scaduto', NULL), + (3, 2, '2026-03-01', '2026-04-30', 'Annullato', NULL), + + -- Luca: completamenti e corsi in corso + (1, 3, '2026-05-05', '2026-06-15', 'Completato', '2026-05-18'), + (2, 3, '2026-05-08', '2026-06-30', 'Assegnato', NULL), + (3, 3, '2026-02-01', '2026-03-15', 'Scaduto', NULL), + + -- Elena: dati aggiuntivi per riepiloghi per categoria + (1, 4, '2026-05-10', '2026-07-01', 'Completato', '2026-05-25'), + (2, 4, '2026-05-12', '2026-06-20', 'Completato', '2026-05-28'), + (4, 4, '2026-05-15', '2026-07-15', 'Assegnato', NULL), + (3, 4, '2026-04-01', '2026-05-10', 'Completato', '2026-05-05'); diff --git a/scripts/apply-academy-schema.sql b/scripts/apply-academy-schema.sql new file mode 100644 index 0000000..f0e9cec --- /dev/null +++ b/scripts/apply-academy-schema.sql @@ -0,0 +1,68 @@ +-- ============================================================ +-- Migrazione manuale per database già inizializzati +-- Eseguire se il volume MySQL esisteva prima dell'aggiornamento schema Academy. +-- ============================================================ + +USE `esameits24-26`; + +-- Allinea enum ruoli utente (se la tabella esisteva con valori precedenti) +ALTER TABLE utente + MODIFY Ruolo ENUM('Dipendente', 'Referente Academy') NOT NULL DEFAULT 'Dipendente'; + +UPDATE utente SET Ruolo = 'Referente Academy' WHERE Ruolo IN ('Amministratore', 'Refernete Academy'); +UPDATE utente SET Ruolo = 'Dipendente' WHERE Ruolo = 'Operatore'; + +-- Catalogo corsi +CREATE TABLE IF NOT EXISTS corso ( + CorsoID INT UNSIGNED NOT NULL AUTO_INCREMENT, + Titolo VARCHAR(200) NOT NULL, + Descrizione TEXT NULL, + Categoria VARCHAR(100) NOT NULL, + DurataOre SMALLINT UNSIGNED NOT NULL, + Obbligatorio TINYINT(1) NOT NULL DEFAULT 0, + Attivo TINYINT(1) NOT NULL DEFAULT 1, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (CorsoID), + KEY idx_corso_categoria (Categoria), + KEY idx_corso_attivo (Attivo), + CONSTRAINT chk_corso_durata_ore CHECK (DurataOre > 0) +) ENGINE=InnoDB + DEFAULT CHARSET=utf8mb4 + COLLATE=utf8mb4_unicode_ci; + +-- Assegnazioni corsi +CREATE TABLE IF NOT EXISTS assegnazione_corso ( + AssegnazioneID INT UNSIGNED NOT NULL AUTO_INCREMENT, + CorsoID INT UNSIGNED NOT NULL, + DipendenteID INT UNSIGNED NOT NULL, + DataAssegnazione DATE NOT NULL, + DataScadenza DATE NOT NULL, + Stato ENUM('Assegnato', 'Completato', 'Scaduto', 'Annullato') NOT NULL DEFAULT 'Assegnato', + DataCompletamento DATE NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (AssegnazioneID), + KEY idx_assegnazione_corso (CorsoID), + KEY idx_assegnazione_dipendente (DipendenteID), + KEY idx_assegnazione_stato (Stato), + KEY idx_assegnazione_data_assegnazione (DataAssegnazione), + KEY idx_assegnazione_data_scadenza (DataScadenza), + CONSTRAINT fk_assegnazione_corso + FOREIGN KEY (CorsoID) REFERENCES corso (CorsoID) + ON UPDATE CASCADE ON DELETE RESTRICT, + CONSTRAINT fk_assegnazione_dipendente + FOREIGN KEY (DipendenteID) REFERENCES utente (UtenteID) + ON UPDATE CASCADE ON DELETE RESTRICT, + CONSTRAINT chk_assegnazione_scadenza + CHECK (DataScadenza >= DataAssegnazione), + CONSTRAINT chk_assegnazione_completamento + CHECK (DataCompletamento IS NULL OR DataCompletamento >= DataAssegnazione), + CONSTRAINT chk_assegnazione_completato + CHECK ( + (Stato = 'Completato' AND DataCompletamento IS NOT NULL) + OR (Stato <> 'Completato' AND DataCompletamento IS NULL) + ) +) ENGINE=InnoDB + DEFAULT CHARSET=utf8mb4 + COLLATE=utf8mb4_unicode_ci; diff --git a/scripts/apply-test-users.sql b/scripts/apply-test-users.sql index 1055387..381ed09 100644 --- a/scripts/apply-test-users.sql +++ b/scripts/apply-test-users.sql @@ -4,6 +4,7 @@ USE `esameits24-26`; INSERT IGNORE INTO utente (Nome, Cognome, Email, PasswordHash, Ruolo) VALUES - ('Andrea', 'Villari', 'admin@example.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Amministratore'), - ('Maria', 'Bianchi', 'maria.bianchi@example.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Operatore'), - ('Luca', 'Conti', 'luca.conti@example.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Operatore'); + ('Andrea', 'Villari', 'admin@academy.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Referente Academy'), + ('Maria', 'Bianchi', 'maria.bianchi@academy.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Dipendente'), + ('Luca', 'Conti', 'luca.conti@academy.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Dipendente'), + ('Elena', 'Rossi', 'elena.rossi@academy.it', '$2b$10$7Z80qZcDydZCdLrOcaH/VOa9knKWjQVyyg8MtEgBAvgfNMsiLEZze', 'Dipendente'); diff --git a/src/errors/db.ts b/src/errors/db.ts index bd63a44..7242243 100644 --- a/src/errors/db.ts +++ b/src/errors/db.ts @@ -17,13 +17,6 @@ export function mapDbError(error: unknown): DbErrorResponse | null { const message = err.sqlMessage ?? err.message ?? ""; if (err.code === "ER_DUP_ENTRY") { - if (message.includes("uk_cliente_email")) { - return { - status: 409, - error: "Email già registrata", - fields: { email: "Email già registrata" }, - }; - } if (message.includes("uk_utente_email")) { return { status: 409, @@ -31,39 +24,36 @@ export function mapDbError(error: unknown): DbErrorResponse | null { fields: { email: "Email già registrata" }, }; } - if (message.includes("uk_chiaveConsegna")) { - return { - status: 409, - error: "Chiave consegna già registrata", - fields: { chiaveConsegna: "Chiave consegna già registrata" }, - }; - } return { status: 409, error: "Valore duplicato" }; } - if (err.code === "ER_SIGNAL_EXCEPTION" || err.errno === 1644) { - if (message.includes("DataRitiro")) { - return { - status: 400, - error: "Data ritiro non valida", - fields: { dataRitiro: "Data ritiro non può essere anteriore alla data odierna" }, - }; - } - if (message.includes("DataConsegna")) { - return { - status: 400, - error: "Data consegna non valida", - fields: { dataConsegna: "Data consegna non può essere anteriore alla data odierna" }, - }; - } - } - if (err.code === "ER_CHECK_CONSTRAINT_VIOLATED") { - if (message.includes("chk_consegna_almeno_una_data")) { + if (message.includes("chk_corso_durata_ore")) { return { status: 400, - error: "Inserire almeno una data di ritiro o di consegna", - fields: { _form: "Inserire almeno una data di ritiro o di consegna" }, + error: "Durata corso non valida", + fields: { durataOre: "La durata prevista deve essere maggiore di zero" }, + }; + } + if (message.includes("chk_assegnazione_scadenza")) { + return { + status: 400, + error: "Date assegnazione non valide", + fields: { dataScadenza: "La data di scadenza non può essere precedente alla data di assegnazione" }, + }; + } + if (message.includes("chk_assegnazione_completamento")) { + return { + status: 400, + error: "Data completamento non valida", + fields: { dataCompletamento: "La data di completamento non può essere precedente alla data di assegnazione" }, + }; + } + if (message.includes("chk_assegnazione_completato")) { + return { + status: 400, + error: "Stato assegnazione non coerente", + fields: { stato: "Lo stato Completato richiede una data di completamento" }, }; } } @@ -71,16 +61,23 @@ export function mapDbError(error: unknown): DbErrorResponse | null { if (err.code === "ER_ROW_IS_REFERENCED_2") { return { status: 409, - error: "Impossibile eliminare: esistono riferimenti collegati", + error: "Impossibile eliminare: esistono assegnazioni collegate al corso", }; } if (err.code === "ER_NO_REFERENCED_ROW_2") { - if (message.includes("fk_consegna_cliente")) { + if (message.includes("fk_assegnazione_corso")) { return { status: 400, - error: "Cliente non valido", - fields: { clienteId: "Cliente non trovato" }, + error: "Corso non valido", + fields: { corsoId: "Corso non trovato" }, + }; + } + if (message.includes("fk_assegnazione_dipendente")) { + return { + status: 400, + error: "Dipendente non valido", + fields: { dipendenteId: "Dipendente non trovato" }, }; } return { status: 400, error: "Riferimento non valido" }; diff --git a/src/middleware/auth.ts b/src/middleware/auth.ts index ab90c7c..3164752 100644 --- a/src/middleware/auth.ts +++ b/src/middleware/auth.ts @@ -1,3 +1,10 @@ +//==================================================== +// File: auth.ts +// File middleware per gestionre dell'autenticazione +// e le autorizzazini dei dipendenti/referenti academy +// author: "villari.andrea@libero.it" +// version: "1.0.0 2026-07-14" +//==================================================== import { getSession } from "@auth/express"; import type { NextFunction, Request, Response } from "express"; import { authConfig } from "../auth/config.js"; @@ -18,14 +25,17 @@ export async function requireAuth(req: Request, res: Response, next: NextFunctio next(); } -export function requireAmministratore(req: Request, res: Response, next: NextFunction) { - if (req.user?.ruolo !== "Amministratore") { - res.status(403).json({ error: "Operazione riservata agli amministratori" }); +export function requireReferenteAcademy(req: Request, res: Response, next: NextFunction) { + if (req.user?.ruolo !== "Referente Academy") { + res.status(403).json({ error: "Operazione riservata ai referenti Academy" }); return; } next(); } +/** @deprecated Usare requireReferenteAcademy */ +export const requireAmministratore = requireReferenteAcademy; + declare global { namespace Express { interface Request { diff --git a/src/routes/utenti.ts b/src/routes/utenti.ts index 5ba240f..0f3b695 100644 --- a/src/routes/utenti.ts +++ b/src/routes/utenti.ts @@ -1,9 +1,15 @@ +//============================================= +// File: index.ts +// Script per la gestine degli endpoint /utente +// author: "villari.andrea@libero.it" +// version: "1.0.0 2026-07-14" +//============================================= import bcrypt from "bcryptjs"; import { Router } from "express"; import type { ResultSetHeader, RowDataPacket } from "mysql2"; import pool from "../db/pool.js"; import { mapDbError } from "../errors/db.js"; -import { requireAmministratore, requireAuth } from "../middleware/auth.js"; +import { requireAuth, requireReferenteAcademy } from "../middleware/auth.js"; import type { Ruolo } from "../types/index.js"; import { validateBody } from "../validation/middleware.js"; import { @@ -41,7 +47,7 @@ async function findUtenteById(id: number): Promise { return rows[0]; } -router.use(requireAuth, requireAmministratore); +router.use(requireAuth, requireReferenteAcademy); router.get("/", async (_req, res) => { const [rows] = await pool.execute( diff --git a/src/types/index.ts b/src/types/index.ts index 6fcbb13..7dcf71c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,8 +1,18 @@ -import { CONSEGNA_CONSTRAINTS, UTENTE_CONSTRAINTS } from "../validation/constraints.js"; +//============================================== +// File: index.ts +// Script con i tipi relativi alle varie entità +// author: "villari.andrea@libero.it" +// version: "1.0.0 2026-07-14" +//============================================== +import { + ASSEGNAZIONE_CONSTRAINTS, + UTENTE_CONSTRAINTS, +} from "../validation/constraints.js"; export type Ruolo = (typeof UTENTE_CONSTRAINTS.ruolo.allowedValues)[number]; -export type StatoConsegna = (typeof CONSEGNA_CONSTRAINTS.stato.allowedValues)[number]; +export type StatoAssegnazione = + (typeof ASSEGNAZIONE_CONSTRAINTS.stato.allowedValues)[number]; export interface Utente { UtenteID: number; @@ -13,23 +23,22 @@ export interface Utente { Ruolo: Ruolo; } -export interface Cliente { - ClienteID: number; - Nome: string; - Cognome: string; - Via: string; - Comune: string; - Provincia: string; - Telefono: string; - Email: string; - Note: string | null; +export interface Corso { + CorsoID: number; + Titolo: string; + Descrizione: string | null; + Categoria: string; + DurataOre: number; + Obbligatorio: boolean; + Attivo: boolean; } -export interface Consegna { - ConsegnaID: number; - ClienteID: number; - DataRitiro: Date | null; - DataConsegna: Date | null; - Stato: StatoConsegna; - ChiaveConsegna: string; +export interface AssegnazioneCorso { + AssegnazioneID: number; + CorsoID: number; + DipendenteID: number; + DataAssegnazione: Date; + DataScadenza: Date; + Stato: StatoAssegnazione; + DataCompletamento: Date | null; } diff --git a/src/validation/constraints.ts b/src/validation/constraints.ts index bb02fca..d7a562e 100644 --- a/src/validation/constraints.ts +++ b/src/validation/constraints.ts @@ -1,40 +1,33 @@ //========================================= -// File: constraints.ts +// File: constraints.ts // Vincoli derivati da initdb/01_schema.sql. // author: "villari.andrea@libero.it" -// version: "1.0.0 2026-06-18" +// version: "1.0.0 2026-14-07" //========================================= export const UTENTE_CONSTRAINTS = { - nome: { maxLength: 100, required: true }, - cognome: { maxLength: 100, required: true }, - email: { maxLength: 255, required: true }, - password: { minLength: 8, maxLength: 72, required: true }, - ruolo: {allowedValues: ["Operatore", "Amministratore"], + nome: { maxLength: 100, required: true }, + cognome: { maxLength: 100, required: true }, + email: { maxLength: 255, required: true }, + password: { minLength: 8, maxLength: 72, required: true }, + ruolo: { + allowedValues: ["Dipendente", "Referente Academy"] as const, required: true, - } - } as const; - - export const CLIENTE_CONSTRAINTS = { - nome: { maxLength: 100, required: true }, - cognome: { maxLength: 100, required: true }, - via: {maxLength: 100, required: true}, - comune: {maxLength: 100, required: true}, - provincia: {maxLength: 2, required: true}, - telefono: { maxLength: 30, required: true }, - email: { maxLength: 255, required: true }, - note: { required: false }, - } as const; - - -export const CONSEGNA_CONSTRAINTS = { - dataRitiro: { required: false }, - dataConsegna: { required: false }, - stato: { - allowedValues: ["da ritirare", "in consegna", "in giacenza", "in deposito"], - required: true, - }, - chiaveConsegna: { maxLength: 8, required: true }, - clienteId: { required: true }, - } as const; - \ No newline at end of file + }, +} as const; + +export const CORSO_CONSTRAINTS = { + titolo: { maxLength: 200, required: true }, + descrizione: { maxLength: 65535, required: false }, + categoria: { maxLength: 100, required: true }, + durataOre: { min: 1, max: 65535, required: true }, + obbligatorio: { required: false }, + attivo: { required: false }, +} as const; + +export const ASSEGNAZIONE_CONSTRAINTS = { + stato: { + allowedValues: ["Assegnato", "Completato", "Scaduto", "Annullato"] as const, + required: true, + }, +} as const;