diff --git a/app/src/app/Views/Admin/Contract/Create.php b/app/src/app/Views/Admin/Contract/Create.php index a2eddcb0..9b09184a 100644 --- a/app/src/app/Views/Admin/Contract/Create.php +++ b/app/src/app/Views/Admin/Contract/Create.php @@ -1,18 +1,20 @@
- - - Return to Contract + Return to Contracts

Create Contract

-
- + + + +
Start Date End Date Invoice Proposed -
-
- - Invoice Agreed +
-
- - Invoice Paid +
- -
- -
+
-
\ No newline at end of file + diff --git a/app/src/app/Views/Admin/Contract/Edit.php b/app/src/app/Views/Admin/Contract/Edit.php index e499f38e..e6cd8e3c 100644 --- a/app/src/app/Views/Admin/Contract/Edit.php +++ b/app/src/app/Views/Admin/Contract/Edit.php @@ -1,7 +1,6 @@
- - @@ -11,15 +10,18 @@ class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-4 rounded-lg

Edit Contract

-
- + + + +
-
-
-
-
-
- -
- -
+
-
\ No newline at end of file +
diff --git a/app/src/app/Views/Admin/Contracts.php b/app/src/app/Views/Admin/Contracts.php index f6d13ae6..a122ad14 100644 --- a/app/src/app/Views/Admin/Contracts.php +++ b/app/src/app/Views/Admin/Contracts.php @@ -12,7 +12,7 @@
- Create Contract @@ -63,7 +63,7 @@ class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-4 rounded-lg - @@ -72,7 +72,7 @@ class="text-blue-500 hover:text-blue-700" title="Edit"> -

Create Tree Type

-
+ @@ -41,11 +41,9 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc
-
- -
+ - \ No newline at end of file + diff --git a/app/src/app/Views/Admin/TreeType/Edit.php b/app/src/app/Views/Admin/TreeType/Edit.php index 5fd08736..668cb7f6 100644 --- a/app/src/app/Views/Admin/TreeType/Edit.php +++ b/app/src/app/Views/Admin/TreeType/Edit.php @@ -11,7 +11,8 @@ class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-4 rounded-lg

Edit Tree Type

-
@@ -39,11 +40,9 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc -
- -
+
-
\ No newline at end of file + diff --git a/app/src/public/assets/js/app.js b/app/src/public/assets/js/app.js index 5b924882..1757403c 100644 --- a/app/src/public/assets/js/app.js +++ b/app/src/public/assets/js/app.js @@ -1,58 +1,265 @@ -function validateFormTreeType(event) { - // Contenidor per a tots els errors - const errorMessagesDiv = document.getElementById("errorMessages"); - - // Netejar errors anteriors - errorMessagesDiv.innerHTML = ""; // Eliminar contingut d'errors - errorMessagesDiv.classList.add("hidden"); // Amagar inicialment - - let hasError = false; // Controlar si hi ha errors - let errorMessage = ""; // Acumular missatges d'error - - // Expressió regular per validar noms (només lletres i espais) - const namePattern = /^[a-zA-Z\s]+$/; - - // Validació de cada camp - const family = document.getElementById("family").value.trim(); - const genus = document.getElementById("genus").value.trim(); - const species = document.getElementById("species").value.trim(); - - if (!family) { - errorMessage += "

- La família és obligatòria.

"; - hasError = true; - } else if (!namePattern.test(family)) { - errorMessage += - "

- La família només pot contenir lletres i espais.

"; - hasError = true; - } +const errorMessagesDiv = document.getElementById("errorMessages"); - if (!genus) { - errorMessage += "

- El gènere és obligatori.

"; - hasError = true; - } else if (!namePattern.test(genus)) { - errorMessage += - "

- El gènere només pot contenir lletres i espais.

"; - hasError = true; - } +const regexPatterns = [ + { + name: "lettersAndSpaces", + regex: /^[a-zA-Z\s]+$/, + explanation: "El camp només pot contenir lletres i espais.", + }, + { + name: "numbersOnly", + regex: /^\d+$/, + explanation: "Només s'accepten números.", + }, +]; + +function createErrorElement(message) { + const errorMsg = document.createElement("p"); + errorMsg.textContent = message; + errorMessagesDiv.appendChild(errorMsg); + return false; +} + +/** + * Validate if a field is empty and return an error message if it is + */ +function validateEmptyField(value, fieldName) { + return !value + ? createErrorElement(`${fieldName}: El camp no pot estar buit.`) + : true; +} - if (!species) { - errorMessage += "

- L'espècie és obligatòria.

"; - hasError = true; - } else if (!namePattern.test(species)) { - errorMessage += - "

- L'espècie només pot contenir lletres i espais.

"; - hasError = true; +/** + * Validate a field based on a regex pattern name and return an error message if invalid + */ +function validateField(value, regexName, fieldName) { + const pattern = regexPatterns.find((pattern) => pattern.name === regexName); + const explanation = pattern ? pattern.explanation : "El valor no és vàlid."; + return pattern && !pattern.regex.test(value) + ? createErrorElement(`${fieldName}: ${explanation}`) + : true; +} + +/** + * Validate that one date is not after another date + */ +function dateCannotBeAfter(startDate, endDate, fieldName) { + return new Date(startDate) > new Date(endDate) + ? createErrorElement( + `${fieldName}: La data de finalització no pot ser anterior a la data d'inici.` + ) + : true; +} + +/** + * Validate that a field contains a positive integer + */ +function validatePositiveInteger(value, fieldName) { + const pattern = /^[+]?\d+([eE][+]?\d+)?$/; + return pattern.test(value) && parseFloat(value) > 0 + ? true + : createErrorElement( + `${fieldName}: El valor ha de ser un número positiu.` + ); +} + +/** + * Validate that a field does not exceed the maximum value + */ +function validateMaxValue(value, max, fieldName) { + return parseFloat(value) <= max + ? true + : createErrorElement( + `${fieldName}: El valor no pot ser superior a ${max}.` + ); +} + +function getFieldName(fieldId) { + const label = document.querySelector(`label[for="${fieldId}"]`); + return label ? label.textContent.trim() : fieldId; +} + +function validateForm(event, fields) { + errorMessagesDiv.innerHTML = ""; + errorMessagesDiv.classList.add("hidden"); + + let isValid = true; + fields.forEach((field) => { + const element = document.getElementById(field.id); + const value = element ? element.value.trim() : null; + const fieldName = getFieldName(field.id); + const max = element ? parseFloat(element.max) : null; + + for (const check of field.checks) { + let validation; + switch (check.type) { + case "empty": + validation = validateEmptyField(value, fieldName); + break; + case "regex": + validation = validateField( + value, + check.regexName, + fieldName + ); + break; + case "daterange": + const startDate = document + .getElementById(check.startDateId) + .value.trim(); + const endDate = document + .getElementById(check.endDateId) + .value.trim(); + validation = dateCannotBeAfter( + startDate, + endDate, + fieldName + ); + break; + case "positiveInteger": + validation = validatePositiveInteger(value, fieldName); + break; + case "maxValue": + validation = validateMaxValue(value, max, fieldName); + break; + default: + validation = true; + } + if (!validation) { + isValid = false; + break; + } + } + }); + + if (!isValid) { + event.preventDefault(); + errorMessagesDiv.classList.remove("hidden"); } +} - // Si hi ha errors, mostrar el contenidor i prevenir l'enviament del formulari - if (hasError) { - errorMessagesDiv.innerHTML = errorMessage; // Inserir errors al quadre - errorMessagesDiv.classList.remove("hidden"); // Mostrar el contenidor - event.preventDefault(); // Evitar l'enviament del formulari +function addFormValidation(formId, fields) { + const form = document.getElementById(formId); + if (form) { + form.addEventListener("submit", (event) => { + validateForm(event, fields); + }); } } -// Assignar l'esdeveniment submit al formulari -document - .getElementById("treeForm") - .addEventListener("submit", validateFormTreeType); +/** + * Event listeners for form validation + */ +addFormValidation("contractForm", [ + { + id: "name", + checks: [ + { + type: "empty", + }, + { + type: "regex", + regexName: "lettersAndSpaces", + }, + ], + }, + { + id: "start_date", + checks: [ + { + type: "empty", + }, + ], + }, + { + id: "end_date", + checks: [ + { + type: "daterange", + startDateId: "start_date", + endDateId: "end_date", + }, + ], + }, + { + id: "invoice_proposed", + checks: [ + { + type: "empty", + }, + { + type: "positiveInteger", + }, + { + type: "maxValue", + }, + ], + }, + { + id: "invoice_agreed", + checks: [ + { + type: "empty", + }, + { + type: "positiveInteger", + }, + { + type: "maxValue", + }, + ], + }, + { + id: "invoice_paid", + checks: [ + { + type: "empty", + }, + { + type: "positiveInteger", + }, + { + type: "maxValue", + }, + ], + }, +]); + +addFormValidation("treeTypeForm", [ + { + id: "family", + checks: [ + { + type: "empty", + }, + { + type: "regex", + regexName: "lettersAndSpaces", + }, + ], + }, + { + id: "genus", + checks: [ + { + type: "empty", + }, + { + type: "regex", + regexName: "lettersAndSpaces", + }, + ], + }, + { + id: "species", + checks: [ + { + type: "empty", + }, + { + type: "regex", + regexName: "lettersAndSpaces", + }, + ], + }, +]); diff --git a/database/start-scripts/0-init.sql b/database/start-scripts/0-init.sql index 36848995..3052b8eb 100644 --- a/database/start-scripts/0-init.sql +++ b/database/start-scripts/0-init.sql @@ -29,8 +29,8 @@ create table users ( create table contracts ( id int auto_increment primary key, name varchar(255) not null, - start_date timestamp not null, - end_date timestamp, + start_date date not null, + end_date date, invoice_proposed float, invoice_agreed float, invoice_paid float,