From 6069486f2094eb1d7a4ec2aa52da168e80efd67a Mon Sep 17 00:00:00 2001 From: Jose Date: Sun, 1 Feb 2026 14:42:22 +0100 Subject: [PATCH] Fix: custom dates on socios, ingresos and gastos. Add: new endpoint for getting socios dropdown on ingresos. --- public/config/settings.dev.json | 1 + public/config/settings.prod.json | 1 + src/components/Gastos/GastoCard.jsx | 6 +-- src/components/Ingresos/IngresoCard.jsx | 35 +++++---------- src/components/Socios/SocioCard.jsx | 14 +++--- src/components/Solicitudes/NewUserForm.jsx | 2 +- src/pages/Ingresos.jsx | 51 ++++++++++++++-------- src/pages/ListaEspera.jsx | 18 +++++--- src/util/parsers/dateParser.js | 2 +- 9 files changed, 69 insertions(+), 61 deletions(-) diff --git a/public/config/settings.dev.json b/public/config/settings.dev.json index 2c24ce0..606dbb3 100644 --- a/public/config/settings.dev.json +++ b/public/config/settings.dev.json @@ -13,6 +13,7 @@ "all": "/users", "byId": "/users/:userId", "me": "/users/me", + "dropdown": "/users/dropdown", "latestNumber": "/users/latest-number", "waitlist": "/users/waitlist", "waitlistLimited": "/users/waitlist/limited", diff --git a/public/config/settings.prod.json b/public/config/settings.prod.json index 62f214b..c2d2629 100644 --- a/public/config/settings.prod.json +++ b/public/config/settings.prod.json @@ -13,6 +13,7 @@ "all": "/users", "byId": "/users/:userId", "me": "/users/me", + "dropdown": "/users/dropdown", "latestNumber": "/users/latest-number", "waitlist": "/users/waitlist", "waitlistLimited": "/users/waitlist/limited", diff --git a/src/components/Gastos/GastoCard.jsx b/src/components/Gastos/GastoCard.jsx index d736234..3b74ad3 100644 --- a/src/components/Gastos/GastoCard.jsx +++ b/src/components/Gastos/GastoCard.jsx @@ -48,7 +48,7 @@ const GastoCard = ({ gasto, isNew = false, onCreate, onUpdate, onDelete, onCance supplier: gasto.supplier || '', invoice: gasto.invoice || '', type: gasto.type ?? 0, - createdAt: gasto.createdAt?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''), + createdAt: gasto.createdAt || (isNew ? getNowAsLocalDatetime() : ''), }); const getFieldError = (field) => fieldErrors?.[field] ?? null; @@ -61,7 +61,7 @@ const GastoCard = ({ gasto, isNew = false, onCreate, onUpdate, onDelete, onCance supplier: gasto.supplier || '', invoice: gasto.invoice || '', type: gasto.type ?? 0, - createdAt: gasto.createdAt?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''), + createdAt: gasto.createdAt || (isNew ? getNowAsLocalDatetime() : ''), }); } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -109,7 +109,7 @@ const GastoCard = ({ gasto, isNew = false, onCreate, onUpdate, onDelete, onCance - handleChange('createdAt', date.toISOString().slice(0, 16)) + handleChange('createdAt', date.toISOString()) } /> ) : ( diff --git a/src/components/Ingresos/IngresoCard.jsx b/src/components/Ingresos/IngresoCard.jsx index fb794bd..dce9c9b 100644 --- a/src/components/Ingresos/IngresoCard.jsx +++ b/src/components/Ingresos/IngresoCard.jsx @@ -49,7 +49,7 @@ const IngresoCard = ({ onCancel, className = '', editable = true, - members = [], + dropdown = new Map(), fieldErrors }) => { const createMode = isNew; @@ -64,7 +64,7 @@ const IngresoCard = ({ memberNumber: income.memberNumber, userId: income.userId, displayName: income.displayName || '', - createdAt: income.createdAt?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''), + createdAt: income.createdAt || (isNew ? getNowAsLocalDatetime() : ''), }); const getFieldError = (field) => fieldErrors?.[field] ?? null; @@ -79,26 +79,12 @@ const IngresoCard = ({ userId: income.userId, memberNumber: income.memberNumber, displayName: income.displayName || '', - createdAt: income.createdAt?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''), + createdAt: income.createdAt || (isNew ? getNowAsLocalDatetime() : ''), }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [income, editMode]); - useEffect(() => { - if (formData.memberNumber && !formData.userId) { - const member = members.find(m => m.memberNumber === formData.memberNumber); - if (member) { - setFormData(prev => ({ - ...prev, - userId: member.userId, - displayName: member.displayName - })); - } - } - }, [formData.memberNumber, formData.userId, members]); - - const handleChange = (field, value) => setFormData(prev => ({ ...prev, [field]: value })); @@ -115,9 +101,12 @@ const IngresoCard = ({ const handleDelete = () => typeof onDelete === 'function' && onDelete(income.incomeId); - const uniqueMembers = Array.from( - new Map(members.map(item => [item.memberNumber, item])).values() - ).sort((a, b) => a.memberNumber - b.memberNumber); + const uniqueMembers = Array.from(dropdown.entries()) + .map(([memberNumber, member]) => ({ + memberNumber, + ...member + })) + .sort((a, b) => a.memberNumber - b.memberNumber); return ( @@ -148,7 +137,7 @@ const IngresoCard = ({ - handleChange('createdAt', date.toISOString().slice(0, 16)) + handleChange('createdAt', date.toISOString()) } /> ) : ( @@ -188,8 +177,8 @@ const IngresoCard = ({ size="sm" value={formData.memberNumber ?? ""} onChange={(e) => { - const memberNumber = parseInt(e.target.value); - const member = members.find(m => m.memberNumber === memberNumber); + const memberNumber = parseInt(e.target.value, 10); + const member = dropdown.get(memberNumber); handleChange('memberNumber', memberNumber); handleChange('userId', member?.userId ?? null); diff --git a/src/components/Socios/SocioCard.jsx b/src/components/Socios/SocioCard.jsx index 277b6cb..e81c55e 100644 --- a/src/components/Socios/SocioCard.jsx +++ b/src/components/Socios/SocioCard.jsx @@ -33,7 +33,7 @@ const renderDateField = (label, icon, dateValue, editMode, fieldKey, handleChang - date ? handleChange(fieldKey, date.toISOString().slice(0, 16)) : handleChange(fieldKey, null) + date ? handleChange(fieldKey, date.toISOString()) : handleChange(fieldKey, null) } /> ) : ( @@ -108,9 +108,9 @@ const SocioCard = ({ identity, isNew = false, onCreate, onUpdate, onDelete, onCa notes: identity?.metadata.notes || '', status: identity?.account.status, type: identity?.metadata.type, - createdAt: identity?.metadata.createdAt?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''), - assignedAt: identity?.metadata.assignedAt?.slice(0, 16) || undefined, - deactivatedAt: identity?.metadata.deactivatedAt?.slice(0, 16) || undefined, + createdAt: identity?.metadata.createdAt || (isNew ? getNowAsLocalDatetime() : ''), + assignedAt: identity?.metadata.assignedAt || undefined, + deactivatedAt: identity?.metadata.deactivatedAt || undefined, globalRole: 0, password: createMode && !editMode ? generateSecurePassword() : null, }); @@ -130,9 +130,9 @@ const SocioCard = ({ identity, isNew = false, onCreate, onUpdate, onDelete, onCa notes: identity.metadata.notes || '', status: identity.account.status, type: identity.metadata.type, - createdAt: identity.metadata.createdAt?.slice(0, 16) || (isNew ? getNowAsLocalDatetime() : ''), - assignedAt: identity.metadata.assignedAt?.slice(0, 16) || undefined, - deactivatedAt: identity.metadata.deactivatedAt?.slice(0, 16) || undefined, + createdAt: identity.metadata.createdAt || (isNew ? getNowAsLocalDatetime() : ''), + assignedAt: identity.metadata.assignedAt || undefined, + deactivatedAt: identity.metadata.deactivatedAt || undefined, globalRole: 0, password: createMode ? generateSecurePassword() : '' }); diff --git a/src/components/Solicitudes/NewUserForm.jsx b/src/components/Solicitudes/NewUserForm.jsx index 80bb37a..4a6609d 100644 --- a/src/components/Solicitudes/NewUserForm.jsx +++ b/src/components/Solicitudes/NewUserForm.jsx @@ -140,7 +140,7 @@ NewUserForm.propTypes = { userType: PropTypes.number.isRequired, plotNumber: PropTypes.number.isRequired, onSubmit: PropTypes.func.isRequired, - errors: PropTypes.object + fieldErrors: PropTypes.object }; export default NewUserForm; diff --git a/src/pages/Ingresos.jsx b/src/pages/Ingresos.jsx index ee1dcf6..285b233 100644 --- a/src/pages/Ingresos.jsx +++ b/src/pages/Ingresos.jsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { useConfig } from '../hooks/useConfig'; import { DataProvider } from '../context/DataContext'; import { useDataContext } from '../hooks/useDataContext'; @@ -32,7 +32,7 @@ const Ingresos = () => { const reqConfig = { baseUrl: config.apiConfig.baseUrl + config.apiConfig.endpoints.incomes.withInfo, rawUrl: config.apiConfig.baseUrl + config.apiConfig.endpoints.incomes.all, - usersUrl: config.apiConfig.baseUrl + config.apiConfig.endpoints.users.all, + dropdownUrl: config.apiConfig.baseUrl + config.apiConfig.endpoints.users.dropdown, params: {} }; @@ -44,24 +44,35 @@ const Ingresos = () => { }; const IngresosContent = ({ reqConfig }) => { - const { data, dataLoading, postData, putData, deleteData } = useDataContext(); + const { data, dataLoading, getData, postData, putData, deleteData } = useDataContext(); const [showPDFModal, setShowPDFModal] = useState(false); const [creatingIngreso, setCreatingIngreso] = useState(false); const [tempIngreso, setTempIngreso] = useState(null); const [deleteTargetId, setDeleteTargetId] = useState(null); const [fieldErrors, setFieldErrors] = useState(null); + const [dropdown, setDropdown] = useState(new Map()); - const members = data - ? Array.from( - new Map( - data.map(i => [i.memberNumber, { - memberNumber: i.memberNumber, - displayName: i.displayName, - userId: i.userId - }]) - ).values() - ).sort((a, b) => a.memberNumber - b.memberNumber) - : []; + useEffect(() => { + const fetchData = async () => { + try { + const response = await getData(reqConfig.dropdownUrl); + const map = new Map(); + response + .sort((a, b) => a.memberNumber - b.memberNumber) + .forEach(item => { + map.set(item.memberNumber, { + userId: item.userId, + displayName: item.displayName + }); + }); + setDropdown(map); + } catch (e) { + console.error(e); + } + }; + fetchData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [reqConfig.dropdownUrl]); const { filtered, @@ -100,13 +111,15 @@ const IngresosContent = ({ reqConfig }) => { }); const handleCreate = () => { - const firstMember = members[0]; + if (dropdown.size === 0) return; + const firstEntry = dropdown.entries().next().value; + const [memberNumber, member] = firstEntry ?? []; setCreatingIngreso(true); setTempIngreso({ incomeId: null, - memberNumber: firstMember?.memberNumber ?? null, - userId: firstMember?.userId ?? null, + memberNumber: memberNumber ?? null, + userId: member?.userId ?? null, concept: '', amount: 0.0, frequency: CONSTANTS.PAYMENT_FREQUENCY_YEARLY, @@ -162,7 +175,7 @@ const IngresosContent = ({ reqConfig }) => { searchTerm={searchTerm} onSearchChange={setSearchTerm} filtersComponent={} - onCreate={handleCreate} + onCreate={dropdown.size > 0 ? handleCreate : null} onPDF={() => setShowPDFModal(true)} /> @@ -175,7 +188,7 @@ const IngresosContent = ({ reqConfig }) => { isNew onCreate={handleCreateSubmit} onCancel={handleCancelCreate} - members={members} + dropdown={dropdown} fieldErrors={fieldErrors} /> )} diff --git a/src/pages/ListaEspera.jsx b/src/pages/ListaEspera.jsx index 0a412ad..773a482 100644 --- a/src/pages/ListaEspera.jsx +++ b/src/pages/ListaEspera.jsx @@ -66,9 +66,9 @@ const ListaEsperaContent = ({ reqConfig }) => { try { const request = await postData( - reqConfig.requestUrl, - { - type: 0, + reqConfig.requestUrl, + { + type: 0, status: 0, metadata: { displayName: formData.displayName, @@ -90,9 +90,13 @@ const ListaEsperaContent = ({ reqConfig }) => { setShowNewUserFormModal(false); setShowConfirmationModal(true); - // eslint-disable-next-line no-unused-vars - } catch (_err) { - setValidationErrors({ general: "Error inesperado al enviar la solicitud" }); + // eslint-disable-next-line no-unused-vars + } catch (err) { + if (err.status === 422 && err.errors) { + setValidationErrors(err.errors); + } else { + setShowNewUserFormModal(false); + } } }; @@ -152,7 +156,7 @@ const ListaEsperaContent = ({ reqConfig }) => { userType={0} plotNumber={0} onSubmit={handleRegisterSubmit} - errors={validationErrors} + fieldErrors={validationErrors} /> diff --git a/src/util/parsers/dateParser.js b/src/util/parsers/dateParser.js index dc80506..cb42fe0 100644 --- a/src/util/parsers/dateParser.js +++ b/src/util/parsers/dateParser.js @@ -15,7 +15,7 @@ export const DateParser = { if (!isoString) return '—'; const date = new Date(isoString); - if (isNaN(date)) return '—'; // Para proteger aún más por si llega basura + if (isNaN(date)) return '—'; return new Intl.DateTimeFormat('es-ES', { day: '2-digit',