diff --git a/amplify/backend/function/createDeadline/src/index.js b/amplify/backend/function/createDeadline/src/index.js index 6284229..7f5a2c2 100644 --- a/amplify/backend/function/createDeadline/src/index.js +++ b/amplify/backend/function/createDeadline/src/index.js @@ -1,7 +1,15 @@ const { createDbConnection } = require("/opt/nodejs/dbConnection"); -const { validateDeadline } = require("/opt/nodejs/helper"); +const { + validateDeadline, + createCorsResponse, + handleOptionsRequest, +} = require("/opt/nodejs/helper"); exports.handler = async (event) => { + if (event.httpMethod === "OPTIONS") { + return handleOptionsRequest(); + } + let connection; try { @@ -19,15 +27,7 @@ exports.handler = async (event) => { type, }); if (error) { - return { - statusCode: 400, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ message: error.details[0].message }), - }; + return createCorsResponse(400, { message: error.details[0].message }); } connection = await createDbConnection({ @@ -42,15 +42,7 @@ exports.handler = async (event) => { user_id, ]); if (user.length === 0) { - return { - statusCode: 400, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ message: "Utente non trovato." }), - }; + return createCorsResponse(400, { message: "Utente non trovato." }); } // Inserisci la deadline @@ -59,35 +51,16 @@ exports.handler = async (event) => { [title, description, due_date, notifications_on, user_id, type] ); - return { - statusCode: 201, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token", - "Access-Control-Allow-Methods": - "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: "Scadenza creata con successo", - id: result.insertId, - }), - }; + return createCorsResponse(201, { + message: "Scadenza creata con successo", + id: result.insertId, + }); } catch (err) { console.error("❌ Errore in createDeadline:", err); - return { - statusCode: 500, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: "Errore durante la creazione della deadline.", - error: err, - }), - }; + return createCorsResponse(500, { + message: "Errore durante la creazione della deadline.", + error: err.message, + }); } finally { if (connection) await connection.end(); } diff --git a/amplify/backend/function/createUsers/src/index.js b/amplify/backend/function/createUsers/src/index.js index 9aed8b5..04c8b6b 100644 --- a/amplify/backend/function/createUsers/src/index.js +++ b/amplify/backend/function/createUsers/src/index.js @@ -1,7 +1,15 @@ const { createDbConnection } = require("/opt/nodejs/dbConnection"); -const { getS3AvatarConfig } = require("/opt/nodejs/helper"); +const { + getS3AvatarConfig, + createCorsResponse, + handleOptionsRequest, +} = require("/opt/nodejs/helper"); exports.handler = async (event) => { + if (event.httpMethod === "OPTIONS") { + return handleOptionsRequest(); + } + let connection; try { @@ -28,18 +36,10 @@ exports.handler = async (event) => { ); if (existingUsers.length > 0) { - return { - statusCode: 409, // Conflict - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: "Un utente con questa email esiste già.", - code: "USER_ALREADY_EXISTS", - }), - }; + return createCorsResponse(409, { + message: "Un utente con questa email esiste già.", + code: "USER_ALREADY_EXISTS", + }); } // Inserisci il nuovo utente @@ -56,49 +56,22 @@ exports.handler = async (event) => { profileImageUrl: `https://${s3Config.bucketName}.s3.${s3Config.region}.amazonaws.com/${s3Config.folderName}/${profileImageId}.jpg`, }; - return { - statusCode: 201, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token", - "Access-Control-Allow-Methods": - "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT", - "Content-Type": "application/json", - }, - body: JSON.stringify(newUser), - }; + return createCorsResponse(201, newUser); } catch (err) { console.error("❌ Errore in createUsers:", err); // Gestisci errori specifici del database if (err.code === "ER_DUP_ENTRY") { - return { - statusCode: 409, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: "Un utente con questa email esiste già.", - code: "USER_ALREADY_EXISTS", - }), - }; + return createCorsResponse(409, { + message: "Un utente con questa email esiste già.", + code: "USER_ALREADY_EXISTS", + }); } - return { - statusCode: 500, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: "Errore del server durante la creazione dell'utente.", - error: err.message, - }), - }; + return createCorsResponse(500, { + message: "Errore del server durante la creazione dell'utente.", + error: err.message, + }); } finally { if (connection) { await connection.end(); diff --git a/amplify/backend/function/deleteDeadlines/src/index.js b/amplify/backend/function/deleteDeadlines/src/index.js index 548b765..d2d4489 100644 --- a/amplify/backend/function/deleteDeadlines/src/index.js +++ b/amplify/backend/function/deleteDeadlines/src/index.js @@ -1,6 +1,14 @@ const { createDbConnection } = require("/opt/nodejs/dbConnection"); +const { + createCorsResponse, + handleOptionsRequest, +} = require("/opt/nodejs/helper"); exports.handler = async (event) => { + if (event.httpMethod === "OPTIONS") { + return handleOptionsRequest(); + } + let connection; try { @@ -16,15 +24,7 @@ exports.handler = async (event) => { console.log("ID ricevuto per delete:", id, "Body:", body); if (!id) { - return { - statusCode: 400, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ message: "ID obbligatorio." }), - }; + return createCorsResponse(400, { message: "ID obbligatorio." }); } connection = await createDbConnection({ @@ -40,43 +40,18 @@ exports.handler = async (event) => { ); if (result.affectedRows === 0) { - return { - statusCode: 404, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ message: "Scadenza non trovata" }), - }; + return createCorsResponse(404, { message: "Scadenza non trovata" }); } - return { - statusCode: 200, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token", - "Access-Control-Allow-Methods": - "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT", - "Content-Type": "application/json", - }, - body: JSON.stringify({ message: "Scadenza eliminata con successo" }), - }; + return createCorsResponse(200, { + message: "Scadenza eliminata con successo", + }); } catch (err) { console.error("❌ Errore in deleteDeadlines:", err); - return { - statusCode: 500, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: "Errore del server. Impossibile eliminare la scadenza.", - error: err.message, - }), - }; + return createCorsResponse(500, { + message: "Errore del server. Impossibile eliminare la scadenza.", + error: err.message, + }); } finally { if (connection) await connection.end(); } diff --git a/amplify/backend/function/deleteUser/src/index.js b/amplify/backend/function/deleteUser/src/index.js index 912f49e..ce086f8 100644 --- a/amplify/backend/function/deleteUser/src/index.js +++ b/amplify/backend/function/deleteUser/src/index.js @@ -1,6 +1,14 @@ const { createDbConnection } = require("/opt/nodejs/dbConnection"); +const { + createCorsResponse, + handleOptionsRequest, +} = require("/opt/nodejs/helper"); exports.handler = async (event) => { + if (event.httpMethod === "OPTIONS") { + return handleOptionsRequest(); + } + let connection; try { @@ -19,34 +27,15 @@ exports.handler = async (event) => { // Elimina l'utente await connection.execute("DELETE FROM users WHERE id = ?", [id]); - return { - statusCode: 200, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token", - "Access-Control-Allow-Methods": - "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: `Utente con id ${id} eliminato con successo.`, - }), - }; + return createCorsResponse(200, { + message: `Utente con id ${id} eliminato con successo.`, + }); } catch (err) { console.error("❌ Errore in deleteUser:", err); - return { - statusCode: 500, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: "Errore del server durante l'eliminazione dell'utente.", - error: err, - }), - }; + return createCorsResponse(500, { + message: "Errore del server durante l'eliminazione dell'utente.", + error: err.message, + }); } finally { if (connection) { await connection.end(); diff --git a/amplify/backend/function/getAppConfig/src/index.js b/amplify/backend/function/getAppConfig/src/index.js index b060992..7b59bde 100644 --- a/amplify/backend/function/getAppConfig/src/index.js +++ b/amplify/backend/function/getAppConfig/src/index.js @@ -1,7 +1,15 @@ -const { getS3LogoConfig } = require("/opt/nodejs/helper"); +const { + getS3LogoConfig, + createCorsResponse, + handleOptionsRequest, +} = require("/opt/nodejs/helper"); const AWS = require("aws-sdk"); exports.handler = async (event) => { + if (event.httpMethod === "OPTIONS") { + return handleOptionsRequest(); + } + console.log("=== getAppConfig START ==="); console.log("getAppConfig - Event:", JSON.stringify(event, null, 2)); @@ -102,18 +110,7 @@ exports.handler = async (event) => { console.log("Final app config:", appConfig); console.log("=== getAppConfig SUCCESS ==="); - return { - statusCode: 200, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token", - "Access-Control-Allow-Methods": - "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT", - "Content-Type": "application/json", - }, - body: JSON.stringify(appConfig), - }; + return createCorsResponse(200, appConfig); } catch (err) { console.error("=== getAppConfig ERROR ==="); console.error("Error details:", { @@ -123,19 +120,11 @@ exports.handler = async (event) => { statusCode: err.statusCode, }); - return { - statusCode: 500, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: "Errore del server durante il recupero della configurazione.", - error: err.message, - logos: {}, - logoUrl: null, - }), - }; + return createCorsResponse(500, { + message: "Errore del server durante il recupero della configurazione.", + error: err.message, + logos: {}, + logoUrl: null, + }); } }; diff --git a/amplify/backend/function/homecloudSharedUtils/homecloudSharedUtils-awscloudformation-template.json b/amplify/backend/function/homecloudSharedUtils/homecloudSharedUtils-awscloudformation-template.json index 431239c..3b498c7 100644 --- a/amplify/backend/function/homecloudSharedUtils/homecloudSharedUtils-awscloudformation-template.json +++ b/amplify/backend/function/homecloudSharedUtils/homecloudSharedUtils-awscloudformation-template.json @@ -20,7 +20,7 @@ } }, "Resources": { - "LambdaLayerVersiona6cb83be": { + "LambdaLayerVersionbfdad44b": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "CompatibleRuntimes": { @@ -51,12 +51,12 @@ "DeletionPolicy": "Delete", "UpdateReplacePolicy": "Retain" }, - "LambdaLayerPermissionPrivatea6cb83be": { + "LambdaLayerPermissionPrivatebfdad44b": { "Type": "AWS::Lambda::LayerVersionPermission", "Properties": { "Action": "lambda:GetLayerVersion", "LayerVersionArn": { - "Ref": "LambdaLayerVersiona6cb83be" + "Ref": "LambdaLayerVersionbfdad44b" }, "Principal": { "Ref": "AWS::AccountId" @@ -67,7 +67,7 @@ "Outputs": { "Arn": { "Value": { - "Ref": "LambdaLayerVersiona6cb83be" + "Ref": "LambdaLayerVersionbfdad44b" } } } diff --git a/amplify/backend/function/homecloudSharedUtils/lib/nodejs/helper.js b/amplify/backend/function/homecloudSharedUtils/lib/nodejs/helper.js index cc84330..84d7ecc 100644 --- a/amplify/backend/function/homecloudSharedUtils/lib/nodejs/helper.js +++ b/amplify/backend/function/homecloudSharedUtils/lib/nodejs/helper.js @@ -58,8 +58,31 @@ function validateDeadline(data) { return deadlineSchema.validate(data); } +// Funzione helper per le risposte CORS +const createCorsResponse = (statusCode, body) => { + const corsHeaders = { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Headers": + "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token", + "Access-Control-Allow-Methods": "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT", + "Content-Type": "application/json", + }; + + return { + statusCode, + headers: corsHeaders, + body: JSON.stringify(body), + }; +}; + +const handleOptionsRequest = () => { + return createCorsResponse(200, ""); +}; + module.exports = { getS3AvatarConfig, getS3LogoConfig, validateDeadline, + createCorsResponse, + handleOptionsRequest, }; diff --git a/amplify/backend/function/homecloudSharedUtils/parameters.json b/amplify/backend/function/homecloudSharedUtils/parameters.json index 4f1be15..60247c5 100644 --- a/amplify/backend/function/homecloudSharedUtils/parameters.json +++ b/amplify/backend/function/homecloudSharedUtils/parameters.json @@ -2,5 +2,5 @@ "runtimes": [ "nodejs22.x" ], - "description": "Updated layer version 2025-07-24T17:43:32.969Z" + "description": "Updated layer version 2025-07-25T19:09:59.330Z" } \ No newline at end of file diff --git a/amplify/backend/function/readDeadlines/src/index.js b/amplify/backend/function/readDeadlines/src/index.js index fffdd0a..f5401c3 100644 --- a/amplify/backend/function/readDeadlines/src/index.js +++ b/amplify/backend/function/readDeadlines/src/index.js @@ -1,6 +1,14 @@ const { createDbConnection } = require("/opt/nodejs/dbConnection"); +const { + createCorsResponse, + handleOptionsRequest, +} = require("/opt/nodejs/helper"); exports.handler = async (event) => { + if (event.httpMethod === "OPTIONS") { + return handleOptionsRequest(); + } + let connection; try { @@ -13,32 +21,13 @@ exports.handler = async (event) => { const [results] = await connection.query("SELECT * FROM deadlines"); - return { - statusCode: 200, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token", - "Access-Control-Allow-Methods": - "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT", - "Content-Type": "application/json", - }, - body: JSON.stringify(results), - }; + return createCorsResponse(200, results); } catch (err) { console.error("❌ Errore in readDeadlines:", err); - return { - statusCode: 500, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: "Errore del server. Impossibile ottenere le scadenze.", - error: err.message, - }), - }; + return createCorsResponse(500, { + message: "Errore del server. Impossibile ottenere le scadenze.", + error: err.message, + }); } finally { if (connection) await connection.end(); } diff --git a/amplify/backend/function/readUsers/src/index.js b/amplify/backend/function/readUsers/src/index.js index 5e9465d..636f6b0 100644 --- a/amplify/backend/function/readUsers/src/index.js +++ b/amplify/backend/function/readUsers/src/index.js @@ -1,7 +1,15 @@ const { createDbConnection } = require("/opt/nodejs/dbConnection"); -const { getS3AvatarConfig } = require("/opt/nodejs/helper"); +const { + getS3AvatarConfig, + createCorsResponse, + handleOptionsRequest, +} = require("/opt/nodejs/helper"); exports.handler = async (event) => { + if (event.httpMethod === "OPTIONS") { + return handleOptionsRequest(); + } + let connection; try { @@ -28,30 +36,13 @@ exports.handler = async (event) => { profileImageUrl: `https://${s3Config.bucketName}.s3.${s3Config.region}.amazonaws.com/${s3Config.folderName}/${user.profileImageId}.jpg`, })); - return { - statusCode: 200, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token", - "Access-Control-Allow-Methods": - "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT", - }, - body: JSON.stringify(usersWithProfileImage), - }; + return createCorsResponse(200, usersWithProfileImage); } catch (err) { console.error("❌ Errore in readUsers:", err); - return { - statusCode: 500, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - }, - body: JSON.stringify({ - message: "Errore del server durante la lettura degli utenti.", - error: err, - }), - }; + return createCorsResponse(500, { + message: "Errore del server durante la lettura degli utenti.", + error: err.message, + }); } finally { if (connection) { await connection.end(); diff --git a/amplify/backend/function/updateDeadlines/src/index.js b/amplify/backend/function/updateDeadlines/src/index.js index c1256da..6ce7620 100644 --- a/amplify/backend/function/updateDeadlines/src/index.js +++ b/amplify/backend/function/updateDeadlines/src/index.js @@ -1,20 +1,20 @@ const { createDbConnection } = require("/opt/nodejs/dbConnection"); -const { validateDeadline } = require("/opt/nodejs/helper"); +const { + validateDeadline, + createCorsResponse, + handleOptionsRequest, +} = require("/opt/nodejs/helper"); exports.handler = async (event) => { + if (event.httpMethod === "OPTIONS") { + return handleOptionsRequest(); + } + let connection; try { if (!event.body) { - return { - statusCode: 400, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ message: "Richiesta senza body." }), - }; + return createCorsResponse(400, { message: "Richiesta senza body." }); } const body = JSON.parse(event.body); @@ -38,15 +38,9 @@ exports.handler = async (event) => { !user_id || !type ) { - return { - statusCode: 400, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ message: "Tutti i campi sono obbligatori." }), - }; + return createCorsResponse(400, { + message: "Tutti i campi sono obbligatori.", + }); } const { error } = validateDeadline({ @@ -58,15 +52,7 @@ exports.handler = async (event) => { type, }); if (error) { - return { - statusCode: 400, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ message: error.details[0].message }), - }; + return createCorsResponse(400, { message: error.details[0].message }); } connection = await createDbConnection({ @@ -83,45 +69,18 @@ exports.handler = async (event) => { ); if (result.affectedRows === 0) { - return { - statusCode: 404, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ message: "Scadenza non trovata" }), - }; + return createCorsResponse(404, { message: "Scadenza non trovata" }); } - return { - statusCode: 200, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token", - "Access-Control-Allow-Methods": - "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: "Scadenza aggiornata con successo", - }), - }; + return createCorsResponse(200, { + message: "Scadenza aggiornata con successo", + }); } catch (err) { console.error("❌ Errore in updateDeadlines:", err); - return { - statusCode: 500, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: "Errore del server. Impossibile aggiornare la scadenza.", - error: err.message, - }), - }; + return createCorsResponse(500, { + message: "Errore del server. Impossibile aggiornare la scadenza.", + error: err.message, + }); } finally { if (connection) await connection.end(); } diff --git a/amplify/backend/function/updateUser/src/index.js b/amplify/backend/function/updateUser/src/index.js index b18e53a..1595b28 100644 --- a/amplify/backend/function/updateUser/src/index.js +++ b/amplify/backend/function/updateUser/src/index.js @@ -1,7 +1,15 @@ const { createDbConnection } = require("/opt/nodejs/dbConnection"); -const { getS3AvatarConfig } = require("/opt/nodejs/helper"); +const { + getS3AvatarConfig, + createCorsResponse, + handleOptionsRequest, +} = require("/opt/nodejs/helper"); exports.handler = async (event) => { + if (event.httpMethod === "OPTIONS") { + return handleOptionsRequest(); + } + let connection; try { @@ -41,32 +49,13 @@ exports.handler = async (event) => { profileImageUrl: `https://${s3Config.bucketName}.s3.${s3Config.region}.amazonaws.com/${s3Config.folderName}/${profileImageId}.jpg`, }; - return { - statusCode: 200, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token", - "Access-Control-Allow-Methods": - "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT", - "Content-Type": "application/json", - }, - body: JSON.stringify(updatedUser), - }; + return createCorsResponse(200, updatedUser); } catch (err) { console.error("❌ Errore in updateUser:", err); - return { - statusCode: 500, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "*", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: "Errore del server durante l'aggiornamento dell'utente.", - error: err, - }), - }; + return createCorsResponse(500, { + message: "Errore del server durante l'aggiornamento dell'utente.", + error: err.message, + }); } finally { if (connection) { await connection.end(); diff --git a/src/pages/AddDeadline.js b/src/pages/AddDeadline.js index 5b9e929..9f43c27 100644 --- a/src/pages/AddDeadline.js +++ b/src/pages/AddDeadline.js @@ -8,6 +8,7 @@ import { faCalendarDay, faChevronDown, faCheck, + faBars, } from "@fortawesome/free-solid-svg-icons"; import { useNavigate } from "react-router-dom"; import axios from "axios"; @@ -72,6 +73,7 @@ const AddDeadline = () => { const [typeDropdownOpen, setTypeDropdownOpen] = useState(false); const [selectedType, setSelectedType] = useState(""); const [isNotificationOn, setIsNotificationOn] = useState(false); + const [isSidebarOpen, setIsSidebarOpen] = useState(false); useEffect(() => { const fetchUser = async () => { @@ -118,6 +120,24 @@ const AddDeadline = () => { setTypeDropdownOpen(false); }; + // Funzione per toggle della sidebar mobile + const toggleSidebar = () => { + setIsSidebarOpen(!isSidebarOpen); + }; + + // Chiude la sidebar quando si clicca su un link (mobile) + const closeSidebar = () => { + setIsSidebarOpen(false); + }; + + // Aggiorna il form quando selectedType cambia + useEffect(() => { + setForm((prev) => ({ + ...prev, + type: selectedType, + })); + }, [selectedType]); + const handleSubmit = async (e) => { e.preventDefault(); setMessage(null); @@ -162,11 +182,28 @@ const AddDeadline = () => { user_id: user.id, }; - await axios.post( + // Debug logs + console.log( + "Request URL:", + `${process.env.REACT_APP_BACKEND_URL}/deadlines` + ); + console.log("Request data:", deadlineData); + console.log("Request headers:", { + "Content-Type": "application/json", + }); + + const response = await axios.post( `${process.env.REACT_APP_BACKEND_URL}/deadlines`, - deadlineData + deadlineData, + { + headers: { + "Content-Type": "application/json", + }, + } ); + console.log("Response:", response); + setMessage({ type: "success", text: "Scadenza aggiunta con successo!", @@ -188,6 +225,11 @@ const AddDeadline = () => { navigate("/dashboard"); }, 1000); } catch (error) { + console.error("Full error:", error); + console.error("Error response:", error.response); + console.error("Error status:", error.response?.status); + console.error("Error data:", error.response?.data); + setMessage({ type: "error", text: @@ -201,11 +243,21 @@ const AddDeadline = () => { return (
+ {/* Header mobile con hamburger */} +
+ +

Aggiungi Scadenza

+
+ { + const date = new Date(dateString); + const day = date.getDate().toString().padStart(2, "0"); + const month = (date.getMonth() + 1).toString().padStart(2, "0"); + const year = date.getFullYear(); + return `${day}/${month}/${year}`; + }; + const handleSort = (sortOption) => { let sortedDeadlines = [...otherDeadlines]; @@ -366,7 +374,7 @@ function Dashboard() { } className="deadline-icon" /> - {deadline.title} + {deadline.title}
- {deadline.title} + {deadline.title}
- {formatDate(deadline.due_date)} + {formatDateShort(deadline.due_date)} {deadline.description} - {deadline.title} + {deadline.title}
- Scadenza: {formatDate(deadline.due_date)} + Scadenza:{" "} + {formatDateShort(deadline.due_date)}
Descrizione: {deadline.description} diff --git a/src/pages/DeadlineDetails.js b/src/pages/DeadlineDetails.js index cb32814..14f0295 100644 --- a/src/pages/DeadlineDetails.js +++ b/src/pages/DeadlineDetails.js @@ -14,6 +14,7 @@ import { faTrash, faCheck, faChevronDown, + faBars, } from "@fortawesome/free-solid-svg-icons"; import axios from "axios"; import { getLoggedUser, logoutAndRedirect } from "../helpers/authHelper"; @@ -53,6 +54,7 @@ function DeadlineDetails() { const [selectedType, setSelectedType] = useState(deadline?.type || ""); const [editTitle, setEditTitle] = useState(""); const [editDescription, setEditDescription] = useState(""); + const [isSidebarOpen, setIsSidebarOpen] = useState(false); const typeOptions = [ "Casa", @@ -306,6 +308,16 @@ function DeadlineDetails() { setTypeDropdownOpen(false); }; + // Funzione per toggle della sidebar mobile + const toggleSidebar = () => { + setIsSidebarOpen(!isSidebarOpen); + }; + + // Chiude la sidebar quando si clicca su un link (mobile) + const closeSidebar = () => { + setIsSidebarOpen(false); + }; + const menuItems = [ { label: "Dettagli Scadenza", @@ -318,11 +330,21 @@ function DeadlineDetails() { return (
+ {/* Header mobile con hamburger */} +
+ +

Dettagli Scadenza

+
+
diff --git a/src/styles/AddDeadline.css b/src/styles/AddDeadline.css index 39ae037..77a5151 100644 --- a/src/styles/AddDeadline.css +++ b/src/styles/AddDeadline.css @@ -19,11 +19,11 @@ width: 100%; display: flex; flex-direction: column; - gap: 28px; + gap: 24px; background: var(--white); border-radius: 10px; box-shadow: 0 2px 10px var(--card-shadow); - padding: 42px; + padding: 35px; max-width: 600px; margin-inline: auto; margin-block: auto; @@ -38,7 +38,7 @@ } .add-deadline-form label p { - font-size: 1.2rem; + font-size: 1.1rem; color: var(--card-title-icon-color); margin-bottom: 4px; } @@ -48,11 +48,11 @@ .add-deadline-form textarea, .add-deadline-form select { width: 100%; - padding: 14px; + padding: 12px; border: 1px solid var(--secondary-color); color: currentColor; border-radius: 6px; - font-size: 1rem; + font-size: 0.95rem; letter-spacing: 0.3px; } @@ -73,11 +73,11 @@ color: var(--white); border: none; border-radius: 8px; - padding: 16px; - font-size: 1.2rem; + padding: 14px; + font-size: 1.1rem; font-weight: 600; cursor: pointer; - margin-top: 12px; + margin-top: 8px; transition: background 0.3s; } @@ -280,3 +280,394 @@ label.add-deadline-notifications-input-container .notification-toggle { .no-pointer { pointer-events: none; } + +/* Dropdown custom per tipo */ +.type-input-container { + position: relative; +} + +.custom-type-dropdown { + position: relative; + width: 100%; + cursor: pointer; + user-select: none; +} + +.custom-type-dropdown:focus { + outline: none; +} + +.dropdown-selected { + width: 100%; + padding: 12px; + border: 1px solid var(--secondary-color); + border-radius: 6px; + background-color: var(--white); + color: currentColor; + font-size: 0.95rem; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + transition: border-color 0.3s ease; +} + +.dropdown-selected:hover { + border-color: var(--accent-color); +} + +.dropdown-selected.open { + border-color: var(--accent-color); + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.type-selected-placeholder { + color: currentColor; + margin: 0; + font-size: 0.95rem; +} + +.dropdown-arrow { + color: var(--text-color-light); + transition: transform 0.3s ease, color 0.3s ease; +} + +.dropdown-selected.open .dropdown-arrow { + transform: rotate(180deg); + color: var(--accent-color); +} + +.dropdown-options { + position: absolute; + top: 100%; + left: 0; + right: 0; + background-color: var(--white); + border: 1px solid var(--accent-color); + border-top: none; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + z-index: 1000; + max-height: 200px; + overflow-y: auto; + box-shadow: 0 4px 20px var(--hover-shadow); +} + +.dropdown-option { + padding: 12px; + cursor: pointer; + transition: background-color 0.3s ease; + font-size: 0.95rem; + color: currentColor; + border-bottom: 1px solid var(--secondary-color); +} + +.dropdown-option:last-child { + border-bottom: none; +} + +.dropdown-option:hover { + background-color: var(--select-hover-color); +} + +.dropdown-option.selected { + background-color: var(--accent-color); + color: var(--white); +} + +/* =================== RESPONSIVE DESIGN =================== */ + +/* Mobile Header - Solo per AddDeadline */ +.deadline-details-container .mobile-header { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + height: 60px; + background: var(--white); + border-bottom: 1px solid var(--secondary-color); + padding: 0 20px; + align-items: center; + gap: 15px; + z-index: 1001; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +.deadline-details-container .hamburger-btn { + background: none; + border: none; + font-size: 1.2rem; + color: var(--text-color); + cursor: pointer; + padding: 8px; + border-radius: 6px; + transition: background-color 0.3s ease; +} + +.deadline-details-container .hamburger-btn:hover { + background-color: var(--secondary-color); +} + +.deadline-details-container .mobile-title { + font-size: 1.2rem; + font-weight: 600; + color: var(--text-color); + margin: 0; +} + +/* =================== TABLET (862px - 1024px) =================== */ +@media (max-width: 1024px) { + /* Mostra header mobile solo per AddDeadline */ + .deadline-details-container .mobile-header { + display: flex; + } + + /* Nascondi sidebar desktop solo per AddDeadline */ + .deadline-details-container .sidebar { + transform: translateX(-100%); + transition: transform 0.3s ease; + z-index: 2000; + } + + /* Mostra sidebar quando aperta solo per AddDeadline */ + .deadline-details-container .sidebar.mobile-open { + transform: translateX(0); + } + + .deadline-details-container.dashboard-container { + flex-direction: column; + } + + .deadline-details-container .adddeadline.main-content { + margin-left: 0; + padding: 80px 40px 40px; + width: 100%; + } + + .deadline-details-container .add-deadline-form { + max-width: 90%; + padding: 30px; + gap: 22px; + } + + .deadline-details-container .add-deadline-form label p { + font-size: 1.1rem; + } + + .deadline-details-container .add-deadline-form input[type="text"], + .deadline-details-container .add-deadline-form textarea, + .deadline-details-container .dropdown-selected { + padding: 12px; + font-size: 0.95rem; + } + + .deadline-details-container .add-deadline-form .save-button { + padding: 14px; + font-size: 1.1rem; + } + + .deadline-details-container .custom-date-picker { + padding: 12px; + padding-right: 40px; + font-size: 0.95rem; + } + + .deadline-details-container .custom-date-input .calendar-icon { + font-size: 1.3rem; + right: 16px; + } +} + +/* =================== MOBILE (862px e inferiore) =================== */ +@media (max-width: 862px) { + .deadline-details-container .adddeadline.main-content { + padding: 75px 20px 35px; + } + + .deadline-details-container .add-deadline-form { + max-width: 100%; + padding: 22px; + gap: 18px; + margin-inline: 0; + } + + .deadline-details-container .add-deadline-form label p { + font-size: 0.95rem; + } + + .deadline-details-container .add-deadline-form input[type="text"], + .deadline-details-container .add-deadline-form textarea, + .deadline-details-container .dropdown-selected { + padding: 11px; + font-size: 0.9rem; + } + + .deadline-details-container .add-deadline-form textarea { + min-height: 65px; + } + + .deadline-details-container .add-deadline-form .save-button { + padding: 13px; + font-size: 0.95rem; + margin-top: 6px; + } + + .deadline-details-container .custom-date-picker { + padding: 11px; + padding-right: 38px; + font-size: 0.9rem; + } + + .deadline-details-container .custom-date-input .calendar-icon { + font-size: 1.1rem; + right: 13px; + } + + .deadline-details-container .notification-toggle { + flex-direction: row; + align-items: center; + gap: 10px; + } + + .deadline-details-container .notification-toggle span { + font-size: 0.9rem; + } + + /* DatePicker responsive solo per AddDeadline */ + .deadline-details-container .react-datepicker { + font-size: 0.85rem; + } + + .deadline-details-container .react-datepicker__current-month { + font-size: 1.1rem; + } + + .deadline-details-container .react-datepicker__day { + width: 2.1rem; + height: 2.1rem; + line-height: 2.1rem; + font-size: 0.85rem; + } + + .deadline-details-container .react-datepicker__day-name { + width: 2.3rem; + line-height: 2.1rem; + font-size: 0.85rem; + } + + .deadline-details-container .mobile-title { + font-size: 1.1rem; + } +} + +/* =================== SMALL MOBILE (480px e inferiore) =================== */ +@media (max-width: 480px) { + .deadline-details-container .adddeadline.main-content { + padding: 70px 15px 25px; + } + + .deadline-details-container .add-deadline-form { + padding: 18px; + gap: 16px; + } + + .deadline-details-container .add-deadline-form label p { + font-size: 0.9rem; + } + + .deadline-details-container .add-deadline-form input[type="text"], + .deadline-details-container .add-deadline-form textarea, + .deadline-details-container .dropdown-selected { + padding: 10px; + font-size: 0.85rem; + } + + .deadline-details-container .add-deadline-form .save-button { + padding: 11px; + font-size: 0.9rem; + } + + .deadline-details-container .custom-date-picker { + padding: 10px; + padding-right: 35px; + font-size: 0.85rem; + } + + .deadline-details-container .custom-date-input .calendar-icon { + font-size: 1rem; + right: 11px; + } + + .deadline-details-container .notification-toggle span { + font-size: 0.85rem; + } + + .deadline-details-container .mobile-title { + font-size: 1rem; + } + + .deadline-details-container .mobile-header { + height: 55px; + } + + .deadline-details-container .hamburger-btn { + font-size: 1.1rem; + } +} + +/* =================== EXTRA SMALL (360px e inferiore) =================== */ +@media (max-width: 360px) { + .deadline-details-container .adddeadline.main-content { + padding: 65px 10px 20px; + } + + .deadline-details-container .add-deadline-form { + padding: 15px; + gap: 14px; + } + + .deadline-details-container .add-deadline-form label p { + font-size: 0.85rem; + } + + .deadline-details-container .add-deadline-form input[type="text"], + .deadline-details-container .add-deadline-form textarea, + .deadline-details-container .dropdown-selected { + padding: 9px; + font-size: 0.8rem; + } + + .deadline-details-container .add-deadline-form .save-button { + padding: 10px; + font-size: 0.85rem; + } + + .deadline-details-container .custom-date-picker { + padding: 9px; + padding-right: 32px; + font-size: 0.8rem; + } + + .deadline-details-container .custom-date-input .calendar-icon { + font-size: 0.9rem; + right: 9px; + } + + .deadline-details-container .notification-toggle span { + font-size: 0.8rem; + } + + .deadline-details-container .mobile-title { + font-size: 0.95rem; + } + + .deadline-details-container .mobile-header { + height: 50px; + } + + .deadline-details-container .hamburger-btn { + font-size: 1rem; + } +} diff --git a/src/styles/Dashboard.css b/src/styles/Dashboard.css index 675918e..7820a36 100644 --- a/src/styles/Dashboard.css +++ b/src/styles/Dashboard.css @@ -108,6 +108,8 @@ section { display: flex; align-items: center; font-size: clamp(1.2rem, 1.4vw, 1.6rem); + flex: 1; + min-width: 0; } .card-title .deadline-icon { @@ -116,6 +118,19 @@ section { height: clamp(1.2rem, 1.5vw, 1.375rem); margin-right: 10px; color: var(--card-title-icon-color); + flex-shrink: 0; + margin-top: 2px; +} + +.card-title .title-text { + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; + line-height: 1.3; } .card-type { @@ -170,6 +185,12 @@ section { .card-body .description { font-size: clamp(1rem, 1vw, 1.1rem); color: var(--card-description-color); + display: -webkit-box; + -webkit-line-clamp: 3; + line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; } .card-gradient-1 { diff --git a/src/styles/DeadlineDetails.css b/src/styles/DeadlineDetails.css index fdf7dbd..60515bb 100644 --- a/src/styles/DeadlineDetails.css +++ b/src/styles/DeadlineDetails.css @@ -598,3 +598,432 @@ input[type="date"]::-webkit-calendar-picker-indicator { color: currentColor; transform: rotate(180deg); } + +/* =================== RESPONSIVE DESIGN =================== */ + +/* Mobile Header - Solo per DeadlineDetails */ +.deadline-details-container .mobile-header { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + height: 60px; + background: var(--white); + border-bottom: 1px solid var(--secondary-color); + padding: 0 20px; + align-items: center; + gap: 15px; + z-index: 1001; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +.deadline-details-container .hamburger-btn { + background: none; + border: none; + font-size: 1.2rem; + color: var(--text-color); + cursor: pointer; + padding: 8px; + border-radius: 6px; + transition: background-color 0.3s ease; +} + +.deadline-details-container .hamburger-btn:hover { + background-color: var(--secondary-color); +} + +.deadline-details-container .mobile-title { + font-size: 1.2rem; + font-weight: 600; + color: var(--text-color); + margin: 0; +} + +/* =================== TABLET (862px - 1024px) =================== */ +@media (max-width: 1024px) { + /* Mostra header mobile solo per DeadlineDetails */ + .deadline-details-container .mobile-header { + display: flex; + } + + /* Nascondi sidebar desktop solo per DeadlineDetails */ + .deadline-details-container .sidebar { + transform: translateX(-100%); + transition: transform 0.3s ease; + z-index: 2000; + } + + /* Mostra sidebar quando aperta solo per DeadlineDetails */ + .deadline-details-container .sidebar.mobile-open { + transform: translateX(0); + } + + .deadline-details-container { + flex-direction: column; + } + + .deadline-details-container .main-content { + margin-left: 0; + padding: 80px 40px 40px; + } + + .deadline-details-container .details-card { + padding: 30px; + max-width: 90%; + } + + .deadline-details-container .details-card h2 { + font-size: 2.1rem; + margin-bottom: 50px; + } + + .deadline-details-container .details-content { + gap: 24px; + } + + .deadline-details-container .actions button { + padding: 14px 18px; + font-size: 1.05rem; + width: 130px; + } + + .deadline-details-container .details-container .deadline-date, + .deadline-details-container .details-container .deadline-description { + font-size: 1.5rem; + } + + .deadline-details-container .details-container .deadline-type, + .deadline-details-container .details-container .deadline-notification { + font-size: 1.1rem; + } + + .deadline-details-container .modal { + width: 450px; + padding: 24px; + } + + .deadline-details-container .modal h2 { + font-size: 1.8rem; + margin-bottom: 24px; + } +} + +/* =================== MOBILE (862px e inferiore) =================== */ +@media (max-width: 862px) { + .deadline-details-container .main-content { + padding: 75px 20px 35px; + } + + .deadline-details-container .details-card { + padding: 24px; + max-width: 100%; + } + + .deadline-details-container .details-card h2 { + font-size: 1.8rem; + margin-bottom: 35px; + } + + .deadline-details-container .details-card .details-card-header { + margin-bottom: 50px; + } + + .deadline-details-container .details-card .details-card-header { + flex-direction: column; + align-items: flex-start; + gap: 20px; + } + + .deadline-details-container .details-card .details-card-header .actions { + width: 100%; + justify-content: space-between; + gap: 25px; + } + + .deadline-details-container .actions button { + padding: 12px 16px; + font-size: 0.95rem; + width: 120px; + flex: 1; + } + + .deadline-details-container .details-content { + grid-template-columns: 1fr; + gap: 25px; + } + + .deadline-details-container .details-card .details-content { + align-items: center; + } + + .deadline-details-container .details-container { + margin-bottom: 25px; + } + + .deadline-details-container .details-container.countdown-container { + grid-column: span 1; + margin-bottom: 0; + } + + .deadline-details-container .details-container .deadline-date, + .deadline-details-container .details-container .deadline-description { + font-size: 1.3rem; + margin-bottom: 18px; + } + + .deadline-details-container .details-container .deadline-description { + max-width: 90%; + text-align: center; + margin: 0 auto; + } + + .deadline-details-container .details-container .deadline-type, + .deadline-details-container .details-container .deadline-notification { + font-size: 1rem; + padding: 10px 18px; + } + + .deadline-details-container .details-content p:first-child { + font-size: 1rem; + margin-bottom: 20px; + text-align: center; + } + + .deadline-details-container .modal { + width: 90%; + padding: 22px; + } + + .deadline-details-container .modal h2 { + font-size: 1.6rem; + margin-bottom: 22px; + } +} + +@media (max-width: 861px) { + .deadline-details-container .details-card { + padding: 20px; + max-width: 100%; + width: auto; + box-shadow: none; + } + + .deadline-details-container .details-card .details-card-header { + flex-direction: column; + align-items: center; + gap: 10px; + } + + .deadline-details-container .details-card h2 { + font-size: 1.6rem; + margin-bottom: 20px; + text-align: center; + } + + .deadline-details-container .details-card .details-content { + display: flex; + flex-direction: column; + gap: 15px; + } + + .deadline-details-container .details-card .details-container { + margin-bottom: 15px; + } + + .deadline-details-container + .details-card + .details-container.countdown-container { + margin-bottom: 0; + } + + .deadline-details-container .details-card .actions { + flex-direction: column; + gap: 10px; + width: 100%; + } +} + +/* =================== SMALL MOBILE (480px e inferiore) =================== */ +@media (max-width: 480px) { + .deadline-details-container .main-content { + padding: 70px 15px 25px; + } + + .deadline-details-container .details-card { + padding: 20px; + } + + .deadline-details-container .details-card h2 { + font-size: 1.5rem; + margin-bottom: 25px; + } + + .deadline-details-container .details-card .details-card-header .actions { + flex-direction: column; + gap: 20px; + } + + .deadline-details-container .actions button { + padding: 11px 14px; + font-size: 0.9rem; + width: 100%; + max-width: 40%; + min-width: 120px; + } + + .deadline-details-container .details-content { + gap: 22px; + } + + .deadline-details-container .details-container { + margin-bottom: 22px; + } + + .deadline-details-container .details-container.countdown-container { + margin-bottom: 0; + } + + .deadline-details-container .details-container .deadline-date, + .deadline-details-container .details-container .deadline-description { + font-size: 1.1rem; + margin-bottom: 16px; + } + + .deadline-details-container .details-container .deadline-type, + .deadline-details-container .details-container .deadline-notification { + font-size: 0.9rem; + padding: 9px 16px; + } + + .deadline-details-container .details-content p:first-child { + font-size: 0.9rem; + margin-bottom: 18px; + } + + .deadline-details-container .deadline-countdown { + font-size: 0.9rem; + } + + .deadline-details-container .mobile-title { + font-size: 1rem; + } + + .deadline-details-container .mobile-header { + height: 55px; + } + + .deadline-details-container .hamburger-btn { + font-size: 1.1rem; + } + + .deadline-details-container .modal { + padding: 18px; + } + + .deadline-details-container .modal h2 { + font-size: 1.4rem; + margin-bottom: 18px; + } + + .deadline-details-container .modal .form-group { + margin-bottom: 16px; + } + + .deadline-details-container .modal .form-group label { + font-size: 0.9rem; + } + + .deadline-details-container .modal input, + .deadline-details-container .modal textarea { + padding: 10px; + font-size: 0.85rem; + } + + .deadline-details_container .modal .modal-actions button { + padding: 10px 14px; + font-size: 0.85rem; + } +} + +/* =================== EXTRA SMALL (360px e inferiore) =================== */ +@media (max-width: 360px) { + .deadline-details-container .main-content { + padding: 65px 10px 20px; + } + + .deadline-details-container .details-card { + padding: 16px; + } + + .deadline-details-container .details-card h2 { + font-size: 1.3rem; + margin-bottom: 20px; + } + + .deadline-details-container .actions button { + padding: 10px 12px; + font-size: 0.85rem; + } + + .deadline-details-container .details-container .deadline-date, + .deadline-details-container .details-container .deadline-description { + font-size: 1rem; + margin-bottom: 14px; + } + + .deadline-details-container .details-container .deadline-type, + .deadline-details-container .details-container .deadline-notification { + font-size: 0.85rem; + padding: 8px 14px; + } + + .deadline-details-container .details-content p:first-child { + font-size: 0.85rem; + margin-bottom: 16px; + } + + .deadline-details-container .deadline-countdown { + font-size: 0.8rem; + } + + .deadline-details-container .mobile-title { + font-size: 0.95rem; + } + + .deadline-details-container .mobile-header { + height: 50px; + } + + .deadline-details-container .hamburger-btn { + font-size: 1rem; + } + + .deadline-details-container .modal { + padding: 15px; + } + + .deadline-details-container .modal h2 { + font-size: 1.2rem; + margin-bottom: 15px; + } + + .deadline-details-container .modal .form-group { + margin-bottom: 14px; + } + + .deadline-details-container .modal .form-group label { + font-size: 0.85rem; + } + + .deadline-details-container .modal input, + .deadline-details-container .modal textarea { + padding: 9px; + font-size: 0.8rem; + } + + .deadline-details-container .modal .modal-actions button { + padding: 9px 12px; + font-size: 0.8rem; + } +}