From 1f78284fb63b3f887b394df6384c33468cc1a4a1 Mon Sep 17 00:00:00 2001 From: Guillermo Arrieta Medina Date: Fri, 6 Feb 2026 11:20:13 -0600 Subject: [PATCH 1/2] Orden de listado de planes issue #71 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #71: ahora los planes se listan por orden de creación descendente (los más recientes primero) --- src/data/api/plans.api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/api/plans.api.ts b/src/data/api/plans.api.ts index 9b869ab..2ac4fde 100644 --- a/src/data/api/plans.api.ts +++ b/src/data/api/plans.api.ts @@ -79,7 +79,7 @@ export async function plans_list( `, { count: 'exact' }, ) - .order('actualizado_en', { ascending: false }) + .order('creado_en', { ascending: false }) // 2. Aplicamos filtros dinámicos From 958b5581113eb69a47e0cb5b7b61e0ba91744751 Mon Sep 17 00:00:00 2001 From: Guillermo Arrieta Medina Date: Fri, 6 Feb 2026 12:23:17 -0600 Subject: [PATCH 2/2] =?UTF-8?q?Se=20limitaron=20el=20n=C3=BAmero=20de=20ca?= =?UTF-8?q?racteres=20y=20de=20digitos=20en=20los=20inputs=20de=20los=20wi?= =?UTF-8?q?zards?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../asignaturas/wizard/PasoBasicosForm.tsx | 219 ++++++++++-------- .../asignaturas/wizard/PasoDetallesPanel.tsx | 2 + .../PasoBasicosForm/PasoBasicosForm.tsx | 5 +- .../PasoDetallesPanel/PasoDetallesPanel.tsx | 2 + src/types/supabase.ts | 3 + supabase/.temp/cli-latest | 2 +- 6 files changed, 140 insertions(+), 93 deletions(-) diff --git a/src/components/asignaturas/wizard/PasoBasicosForm.tsx b/src/components/asignaturas/wizard/PasoBasicosForm.tsx index dff2726..6c6c0d3 100644 --- a/src/components/asignaturas/wizard/PasoBasicosForm.tsx +++ b/src/components/asignaturas/wizard/PasoBasicosForm.tsx @@ -27,14 +27,25 @@ export function PasoBasicosForm({ const [creditosInput, setCreditosInput] = useState(() => { const c = Number(wizard.datosBasicos.creditos ?? 0) - return c > 0 ? c.toFixed(2) : '' + let newC = c + console.log('antes', newC) + + if (Number.isFinite(c) && c > 999) { + newC = 999 + } + console.log('desp', newC) + return newC > 0 ? newC.toFixed(2) : '' }) const [creditosFocused, setCreditosFocused] = useState(false) useEffect(() => { if (creditosFocused) return const c = Number(wizard.datosBasicos.creditos ?? 0) - setCreditosInput(c > 0 ? c.toFixed(2) : '') + let newC = c + if (Number.isFinite(c) && c > 999) { + newC = 999 + } + setCreditosInput(newC > 0 ? newC.toFixed(2) : '') }, [wizard.datosBasicos.creditos, creditosFocused]) return ( @@ -44,6 +55,7 @@ export function PasoBasicosForm({ onChange( @@ -67,6 +79,7 @@ export function PasoBasicosForm({ onChange( @@ -123,6 +136,7 @@ export function PasoBasicosForm({ id="creditos" type="text" inputMode="decimal" + maxLength={6} pattern="^\\d*(?:[.,]\\d{0,2})?$" value={creditosInput} onKeyDown={(e) => { @@ -144,7 +158,7 @@ export function PasoBasicosForm({ } const normalized = raw.replace(',', '.') - const asNumber = Number.parseFloat(normalized) + let asNumber = Number.parseFloat(normalized) if (!Number.isFinite(asNumber) || asNumber <= 0) { setCreditosInput('') onChange((w) => ({ @@ -154,6 +168,9 @@ export function PasoBasicosForm({ return } + // Cap to 999 + if (asNumber > 999) asNumber = 999 + const fixed = asNumber.toFixed(2) setCreditosInput(fixed) onChange((w) => ({ @@ -174,6 +191,22 @@ export function PasoBasicosForm({ if (!/^\d*(?:[.,]\d{0,2})?$/.test(nextRaw)) return + // If typed number exceeds 999, cap it immediately (prevents entering >999) + const asNumberRaw = Number.parseFloat(nextRaw.replace(',', '.')) + if (Number.isFinite(asNumberRaw) && asNumberRaw > 999) { + // show capped value to the user + const cappedStr = '999.00' + setCreditosInput(cappedStr) + onChange((w) => ({ + ...w, + datosBasicos: { + ...w.datosBasicos, + creditos: 999, + }, + })) + return + } + setCreditosInput(nextRaw) const asNumber = Number.parseFloat(nextRaw.replace(',', '.')) @@ -191,94 +224,6 @@ export function PasoBasicosForm({ /> -
- - { - if (['.', ',', '-', 'e', 'E', '+'].includes(e.key)) { - e.preventDefault() - } - }} - onChange={(e: React.ChangeEvent) => - onChange( - (w): NewSubjectWizardState => ({ - ...w, - datosBasicos: { - ...w.datosBasicos, - horasAcademicas: (() => { - const raw = e.target.value - if (raw === '') return null - const asNumber = Number(raw) - if (Number.isNaN(asNumber)) return null - // Coerce to positive integer (natural numbers without zero) - const n = Math.floor(Math.abs(asNumber)) - return n >= 1 ? n : 1 - })(), - }, - }), - ) - } - className="placeholder:text-muted-foreground/70 font-medium not-italic placeholder:font-normal placeholder:italic" - placeholder="Ej. 48" - /> -
- -
- - { - if (['.', ',', '-', 'e', 'E', '+'].includes(e.key)) { - e.preventDefault() - } - }} - onChange={(e: React.ChangeEvent) => - onChange( - (w): NewSubjectWizardState => ({ - ...w, - datosBasicos: { - ...w.datosBasicos, - horasIndependientes: (() => { - const raw = e.target.value - if (raw === '') return null - const asNumber = Number(raw) - if (Number.isNaN(asNumber)) return null - // Coerce to positive integer (natural numbers without zero) - const n = Math.floor(Math.abs(asNumber)) - return n >= 1 ? n : 1 - })(), - }, - }), - ) - } - className="placeholder:text-muted-foreground/70 font-medium not-italic placeholder:font-normal placeholder:italic" - placeholder="Ej. 24" - /> -
-
{ + if (['.', ',', '-', 'e', 'E', '+'].includes(e.key)) { + e.preventDefault() + } + }} + onChange={(e: React.ChangeEvent) => + onChange( + (w): NewSubjectWizardState => ({ + ...w, + datosBasicos: { + ...w.datosBasicos, + horasAcademicas: (() => { + const raw = e.target.value + if (raw === '') return null + const asNumber = Number(raw) + if (Number.isNaN(asNumber)) return null + // Coerce to positive integer (natural numbers without zero) + const n = Math.floor(Math.abs(asNumber)) + const capped = Math.min(n >= 1 ? n : 1, 999) + return capped + })(), + }, + }), + ) + } + className="placeholder:text-muted-foreground/70 font-medium not-italic placeholder:font-normal placeholder:italic" + placeholder="Ej. 48" + /> +
+ +
+ + { + if (['.', ',', '-', 'e', 'E', '+'].includes(e.key)) { + e.preventDefault() + } + }} + onChange={(e: React.ChangeEvent) => + onChange( + (w): NewSubjectWizardState => ({ + ...w, + datosBasicos: { + ...w.datosBasicos, + horasIndependientes: (() => { + const raw = e.target.value + if (raw === '') return null + const asNumber = Number(raw) + if (Number.isNaN(asNumber)) return null + // Coerce to positive integer (natural numbers without zero) + const n = Math.floor(Math.abs(asNumber)) + const capped = Math.min(n >= 1 ? n : 1, 999) + return capped + })(), + }, + }), + ) + } + className="placeholder:text-muted-foreground/70 font-medium not-italic placeholder:font-normal placeholder:italic" + placeholder="Ej. 24" + /> +
) } diff --git a/src/components/asignaturas/wizard/PasoDetallesPanel.tsx b/src/components/asignaturas/wizard/PasoDetallesPanel.tsx index 612d4fc..7b672f1 100644 --- a/src/components/asignaturas/wizard/PasoDetallesPanel.tsx +++ b/src/components/asignaturas/wizard/PasoDetallesPanel.tsx @@ -56,6 +56,7 @@ export function PasoDetallesPanel({