refactor: Update CriterioEvaluacionRow structure and related logic for consistency
This commit was merged in pull request #157.
This commit is contained in:
@@ -39,14 +39,14 @@ export interface AsignaturaResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CriterioEvaluacionRow = {
|
type CriterioEvaluacionRow = {
|
||||||
label: string
|
criterio: string
|
||||||
value: number
|
porcentaje: number
|
||||||
}
|
}
|
||||||
|
|
||||||
type CriterioEvaluacionRowDraft = {
|
type CriterioEvaluacionRowDraft = {
|
||||||
id: string
|
id: string
|
||||||
label: string
|
criterio: string
|
||||||
value: string // allow empty while editing
|
porcentaje: string // allow empty while editing
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
@@ -134,20 +134,20 @@ function DatosGenerales({
|
|||||||
const rows: Array<CriterioEvaluacionRow> = []
|
const rows: Array<CriterioEvaluacionRow> = []
|
||||||
for (const item of raw) {
|
for (const item of raw) {
|
||||||
if (!isRecord(item)) continue
|
if (!isRecord(item)) continue
|
||||||
const label = typeof item.label === 'string' ? item.label : ''
|
const criterio = typeof item.criterio === 'string' ? item.criterio : ''
|
||||||
const valueNum =
|
const porcentajeNum =
|
||||||
typeof item.value === 'number'
|
typeof item.porcentaje === 'number'
|
||||||
? item.value
|
? item.porcentaje
|
||||||
: typeof item.value === 'string'
|
: typeof item.porcentaje === 'string'
|
||||||
? Number(item.value)
|
? Number(item.porcentaje)
|
||||||
: NaN
|
: NaN
|
||||||
|
|
||||||
if (!label.trim()) continue
|
if (!criterio.trim()) continue
|
||||||
if (!Number.isFinite(valueNum)) continue
|
if (!Number.isFinite(porcentajeNum)) continue
|
||||||
const value = Math.trunc(valueNum)
|
const porcentaje = Math.trunc(porcentajeNum)
|
||||||
if (value < 1 || value > 100) continue
|
if (porcentaje < 1 || porcentaje > 100) continue
|
||||||
|
|
||||||
rows.push({ label: label.trim(), value })
|
rows.push({ criterio: criterio.trim(), porcentaje: porcentaje })
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows
|
return rows
|
||||||
@@ -354,22 +354,22 @@ function InfoCard({
|
|||||||
const raw = Array.isArray(initialContent) ? initialContent : []
|
const raw = Array.isArray(initialContent) ? initialContent : []
|
||||||
const rows: Array<CriterioEvaluacionRowDraft> = raw
|
const rows: Array<CriterioEvaluacionRowDraft> = raw
|
||||||
.map((r: any): CriterioEvaluacionRowDraft | null => {
|
.map((r: any): CriterioEvaluacionRowDraft | null => {
|
||||||
const label = typeof r?.label === 'string' ? r.label : ''
|
const criterio = typeof r?.criterio === 'string' ? r.criterio : ''
|
||||||
const valueNum =
|
const porcentajeNum =
|
||||||
typeof r?.value === 'number'
|
typeof r?.porcentaje === 'number'
|
||||||
? r.value
|
? r.porcentaje
|
||||||
: typeof r?.value === 'string'
|
: typeof r?.porcentaje === 'string'
|
||||||
? Number(r.value)
|
? Number(r.porcentaje)
|
||||||
: NaN
|
: NaN
|
||||||
|
|
||||||
const value = Number.isFinite(valueNum)
|
const porcentaje = Number.isFinite(porcentajeNum)
|
||||||
? String(Math.trunc(valueNum))
|
? String(Math.trunc(porcentajeNum))
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
label,
|
criterio,
|
||||||
value,
|
porcentaje,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(Boolean) as Array<CriterioEvaluacionRowDraft>
|
.filter(Boolean) as Array<CriterioEvaluacionRowDraft>
|
||||||
@@ -396,25 +396,25 @@ function InfoCard({
|
|||||||
if (type === 'evaluation') {
|
if (type === 'evaluation') {
|
||||||
const cleaned: Array<CriterioEvaluacionRow> = []
|
const cleaned: Array<CriterioEvaluacionRow> = []
|
||||||
for (const r of evalRows) {
|
for (const r of evalRows) {
|
||||||
const label = String(r.label).trim()
|
const criterio = String(r.criterio).trim()
|
||||||
const valueStr = String(r.value).trim()
|
const porcentajeStr = String(r.porcentaje).trim()
|
||||||
if (!label) continue
|
if (!criterio) continue
|
||||||
if (!valueStr) continue
|
if (!porcentajeStr) continue
|
||||||
|
|
||||||
const n = Number(valueStr)
|
const n = Number(porcentajeStr)
|
||||||
if (!Number.isFinite(n)) continue
|
if (!Number.isFinite(n)) continue
|
||||||
const value = Math.trunc(n)
|
const porcentaje = Math.trunc(n)
|
||||||
if (value < 1 || value > 100) continue
|
if (porcentaje < 1 || porcentaje > 100) continue
|
||||||
|
|
||||||
cleaned.push({ label, value })
|
cleaned.push({ criterio, porcentaje })
|
||||||
}
|
}
|
||||||
|
|
||||||
setData(cleaned)
|
setData(cleaned)
|
||||||
setEvalRows(
|
setEvalRows(
|
||||||
cleaned.map((x) => ({
|
cleaned.map((x) => ({
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
label: x.label,
|
criterio: x.criterio,
|
||||||
value: String(x.value),
|
porcentaje: String(x.porcentaje),
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
setIsEditing(false)
|
setIsEditing(false)
|
||||||
@@ -451,13 +451,13 @@ function InfoCard({
|
|||||||
const evaluationTotal = useMemo(() => {
|
const evaluationTotal = useMemo(() => {
|
||||||
if (type !== 'evaluation') return 0
|
if (type !== 'evaluation') return 0
|
||||||
return evalRows.reduce((acc, r) => {
|
return evalRows.reduce((acc, r) => {
|
||||||
const v = String(r.value).trim()
|
const v = String(r.porcentaje).trim()
|
||||||
if (!v) return acc
|
if (!v) return acc
|
||||||
const n = Number(v)
|
const n = Number(v)
|
||||||
if (!Number.isFinite(n)) return acc
|
if (!Number.isFinite(n)) return acc
|
||||||
const value = Math.trunc(n)
|
const porcentaje = Math.trunc(n)
|
||||||
if (value < 1 || value > 100) return acc
|
if (porcentaje < 1 || porcentaje > 100) return acc
|
||||||
return acc + value
|
return acc + porcentaje
|
||||||
}, 0)
|
}, 0)
|
||||||
}, [type, evalRows])
|
}, [type, evalRows])
|
||||||
|
|
||||||
@@ -550,14 +550,14 @@ function InfoCard({
|
|||||||
className="grid grid-cols-[2fr_1fr_1ch_32px] items-center gap-2"
|
className="grid grid-cols-[2fr_1fr_1ch_32px] items-center gap-2"
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
value={row.label}
|
value={row.criterio}
|
||||||
placeholder="Criterio (label)"
|
placeholder="Criterio"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const nextLabel = e.target.value
|
const nextCriterio = e.target.value
|
||||||
setEvalRows((prev) =>
|
setEvalRows((prev) =>
|
||||||
prev.map((r) =>
|
prev.map((r) =>
|
||||||
r.id === row.id
|
r.id === row.id
|
||||||
? { ...r, label: nextLabel }
|
? { ...r, criterio: nextCriterio }
|
||||||
: r,
|
: r,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -565,7 +565,7 @@ function InfoCard({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
value={row.value}
|
value={row.porcentaje}
|
||||||
placeholder="%"
|
placeholder="%"
|
||||||
type="number"
|
type="number"
|
||||||
min={1}
|
min={1}
|
||||||
@@ -580,7 +580,13 @@ function InfoCard({
|
|||||||
if (raw === '') {
|
if (raw === '') {
|
||||||
setEvalRows((prev) =>
|
setEvalRows((prev) =>
|
||||||
prev.map((r) =>
|
prev.map((r) =>
|
||||||
r.id === row.id ? { ...r, value: '' } : r,
|
r.id === row.id
|
||||||
|
? {
|
||||||
|
id: r.id,
|
||||||
|
criterio: r.criterio,
|
||||||
|
porcentaje: '',
|
||||||
|
}
|
||||||
|
: r,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@@ -588,17 +594,23 @@ function InfoCard({
|
|||||||
|
|
||||||
const n = Number(raw)
|
const n = Number(raw)
|
||||||
if (!Number.isFinite(n)) return
|
if (!Number.isFinite(n)) return
|
||||||
const value = Math.trunc(n)
|
const porcentaje = Math.trunc(n)
|
||||||
if (value < 1 || value > 100) return
|
if (porcentaje < 1 || porcentaje > 100) return
|
||||||
|
|
||||||
// No permitir suma > 100
|
// No permitir suma > 100
|
||||||
setEvalRows((prev) => {
|
setEvalRows((prev) => {
|
||||||
const next = prev.map((r) =>
|
const next = prev.map((r) =>
|
||||||
r.id === row.id ? { ...r, value: raw } : r,
|
r.id === row.id
|
||||||
|
? {
|
||||||
|
id: r.id,
|
||||||
|
criterio: r.criterio,
|
||||||
|
porcentaje: raw,
|
||||||
|
}
|
||||||
|
: r,
|
||||||
)
|
)
|
||||||
|
|
||||||
const total = next.reduce((acc, r) => {
|
const total = next.reduce((acc, r) => {
|
||||||
const v = String(r.value).trim()
|
const v = String(r.porcentaje).trim()
|
||||||
if (!v) return acc
|
if (!v) return acc
|
||||||
const nn = Number(v)
|
const nn = Number(v)
|
||||||
if (!Number.isFinite(nn)) return acc
|
if (!Number.isFinite(nn)) return acc
|
||||||
@@ -638,7 +650,14 @@ function InfoCard({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-muted-foreground text-xs">
|
<span
|
||||||
|
className={
|
||||||
|
'text-sm ' +
|
||||||
|
(evaluationTotal === 100
|
||||||
|
? 'text-muted-foreground'
|
||||||
|
: 'text-destructive font-semibold')
|
||||||
|
}
|
||||||
|
>
|
||||||
Total: {evaluationTotal}/100
|
Total: {evaluationTotal}/100
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -652,8 +671,8 @@ function InfoCard({
|
|||||||
...prev,
|
...prev,
|
||||||
{
|
{
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
label: '',
|
criterio: '',
|
||||||
value: '',
|
porcentaje: '',
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
}}
|
}}
|
||||||
@@ -679,14 +698,15 @@ function InfoCard({
|
|||||||
if (type === 'evaluation') {
|
if (type === 'evaluation') {
|
||||||
const raw = Array.isArray(data) ? data : []
|
const raw = Array.isArray(data) ? data : []
|
||||||
setEvalRows(
|
setEvalRows(
|
||||||
raw.map((r: any) => ({
|
raw.map((r: CriterioEvaluacionRow) => ({
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
label: typeof r?.label === 'string' ? r.label : '',
|
criterio:
|
||||||
value:
|
typeof r.criterio === 'string' ? r.criterio : '',
|
||||||
typeof r?.value === 'number'
|
porcentaje:
|
||||||
? String(Math.trunc(r.value))
|
typeof r.porcentaje === 'number'
|
||||||
: typeof r?.value === 'string'
|
? String(Math.trunc(r.porcentaje))
|
||||||
? String(Math.trunc(Number(r.value)))
|
: typeof r.porcentaje === 'string'
|
||||||
|
? String(Math.trunc(Number(r.porcentaje)))
|
||||||
: '',
|
: '',
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
@@ -714,7 +734,9 @@ function InfoCard({
|
|||||||
<p className="text-slate-400 italic">Sin información.</p>
|
<p className="text-slate-400 italic">Sin información.</p>
|
||||||
))}
|
))}
|
||||||
{type === 'requirements' && <RequirementsView items={data} />}
|
{type === 'requirements' && <RequirementsView items={data} />}
|
||||||
{type === 'evaluation' && <EvaluationView items={data} />}
|
{type === 'evaluation' && (
|
||||||
|
<EvaluationView items={data as Array<CriterioEvaluacionRow>} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@@ -745,7 +767,11 @@ function RequirementsView({ items }: { items: Array<any> }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Vista de Evaluación
|
// Vista de Evaluación
|
||||||
function EvaluationView({ items }: { items: Array<any> }) {
|
function EvaluationView({ items }: { items: Array<CriterioEvaluacionRow> }) {
|
||||||
|
const porcentajeTotal = items.reduce(
|
||||||
|
(total, item) => total + Number(item.porcentaje),
|
||||||
|
0,
|
||||||
|
)
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{items.map((item, i) => (
|
{items.map((item, i) => (
|
||||||
@@ -753,10 +779,15 @@ function EvaluationView({ items }: { items: Array<any> }) {
|
|||||||
key={i}
|
key={i}
|
||||||
className="flex justify-between border-b border-slate-50 pb-1.5 text-sm italic"
|
className="flex justify-between border-b border-slate-50 pb-1.5 text-sm italic"
|
||||||
>
|
>
|
||||||
<span className="text-slate-500">{item.label}</span>
|
<span className="text-slate-500">{item.criterio}</span>
|
||||||
<span className="font-bold text-blue-600">{item.value}%</span>
|
<span className="font-bold text-blue-600">{item.porcentaje}%</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
{porcentajeTotal < 100 && (
|
||||||
|
<p className="text-destructive text-sm font-medium">
|
||||||
|
El porcentaje total es menor a 100%.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -813,12 +844,12 @@ function parseCriteriosEvaluacionToPlainText(value: unknown): string {
|
|||||||
const lines: Array<string> = []
|
const lines: Array<string> = []
|
||||||
for (const item of value) {
|
for (const item of value) {
|
||||||
if (!isRecord(item)) continue
|
if (!isRecord(item)) continue
|
||||||
const label = typeof item.label === 'string' ? item.label.trim() : ''
|
const label = typeof item.criterio === 'string' ? item.criterio.trim() : ''
|
||||||
const valueNum =
|
const valueNum =
|
||||||
typeof item.value === 'number'
|
typeof item.porcentaje === 'number'
|
||||||
? item.value
|
? item.porcentaje
|
||||||
: typeof item.value === 'string'
|
: typeof item.porcentaje === 'string'
|
||||||
? Number(item.value)
|
? Number(item.porcentaje)
|
||||||
: NaN
|
: NaN
|
||||||
|
|
||||||
if (!label) continue
|
if (!label) continue
|
||||||
|
|||||||
Reference in New Issue
Block a user