C
CULTIVA IA
Simplificador de Código
REFACTOR COMPLETADO
PatientDashboard.tsx

Refactorización NutriFlow SaaS

Componente de dashboard de pacientes — limpieza pre-code-review tras sprint de lanzamiento

Líneas eliminadas
−47
código redundante removido
Complejidad ciclomática
12 → 5
−58% de complejidad
Ternarios anidados
4 → 0
reemplazados por if/else
Funcionalidad intacta
100%
cero cambios de comportamiento
1
Ternarios anidados → función con early returns
Principio #3: Enhance Clarity
Antes — ilegible
Después — explícito
// Inline, imposible de leer/debug
const dashboardState = isLoading
  ? 'loading'
  : hasError
  ? errorType === 'network'
    ? 'network-error'
    : errorType === 'auth'
    ? 'auth-error'
    : 'unknown-error'
  : patientData.length === 0
  ? 'empty'
  : 'ready';

const label = dashboardState === 'loading'
  ? 'Cargando...'
  : dashboardState.includes('error')
  ? 'Error'
  : dashboardState === 'empty'
  ? 'Sin datos'
  : 'Dashboard';
function getDashboardState(
  isLoading: boolean,
  hasError: boolean,
  errorType: string | null,
  patientCount: number
): DashboardState {
  if (isLoading) return 'loading';
  if (hasError) {
    if (errorType === 'network') return 'network-error';
    if (errorType === 'auth') return 'auth-error';
    return 'unknown-error';
  }
  if (patientCount === 0) return 'empty';
  return 'ready';
}

function getStateLabel(state: DashboardState): string {
  if (state === 'loading') return 'Cargando...';
  if (state.includes('error')) return 'Error';
  if (state === 'empty') return 'Sin datos';
  return 'Dashboard';
}
💡 Por qué: El ternario de 11 niveles es imposible de debuggear. Las funciones con early returns permiten añadir un nuevo estado sin romper la lógica existente — crítico en un SaaS que evoluciona.
2
Abstracciones redundantes eliminadas
Principio #1: Eliminate Redundant Abstractions
Antes — wrappers sin valor
Después — directo y claro
// helpers que no aportan nada
function hasPatients(arr: Patient[]): boolean {
  return arr.length > 0;
}

function isValidId(id: string): boolean {
  return id !== '' && id !== null;
}

function formatPatientName(p: Patient): string {
  return p.firstName + ' ' + p.lastName;
}

// uso
if (hasPatients(patients)) {
  const name = formatPatientName(patient);
  if (isValidId(patient.id)) {
    // ...
  }
}
// sin wrappers innecesarios
if (patients.length > 0) {
  const name = `${patient.firstName} ${patient.lastName}`;
  if (patient.id) {
    // ...
  }
}
💡 Por qué: hasPatients, isValidId y formatPatientName son wrappers de una sola línea que no añaden semántica. Eliminados → −18 líneas, cero funcionalidad perdida.
3
One-liners densos → pasos claros
Principio #3: Clear Steps
Antes — chain ilegible
Después — pasos nombrados
// intención opaca, difícil de debuggear
const riskScore = patients
  .filter(p => p.bmi > 30 && p.active)
  .map(p => p.bmi * p.adherenceRate)
  .reduce((acc, v) => acc + v, 0)
  / patients.filter(p => p.bmi > 30 && p.active)
  .length || 0;
// intención explícita, cada paso debuggeable
const highRiskPatients = patients.filter(
  (p) => p.bmi > 30 && p.active
);

const weightedScores = highRiskPatients.map(
  (p) => p.bmi * p.adherenceRate
);

const totalScore = weightedScores.reduce(
  (acc, score) => acc + score,
  0
);

const riskScore = highRiskPatients.length > 0
  ? totalScore / highRiskPatients.length
  : 0;
💡 Por qué: El chain original filtraba la lista dos veces y tenía un || 0 que silenciaba la división por cero. La versión explícita elimina la duplicación, nombra los intermedios y hace el guard de división por cero legible.
4
Arrow functions → function keyword (estándar del proyecto)
Principio #2: Apply Project Standards
Antes — arrow components
Después — function keyword
const PatientCard = ({
  patient,
  onSelect,
}: {
  patient: Patient;
  onSelect: (id: string) => void;
}) => {
  return (
    <div>
      <span>{patient.firstName}</span>
    </div>
  );
};

export default PatientCard;
interface Props {
  patient: Patient;
  onSelect: (id: string) => void;
}

export default function PatientCard({
  patient,
  onSelect,
}: Props): JSX.Element {
  return (
    <div>
      <span>{patient.firstName}</span>
    </div>
  );
}
💡 Por qué: El CLAUDE.md del proyecto exige function keyword y tipo de retorno explícito. El interface Props separado hace el contrato del componente legible en una sola mirada.
5
Resumen de cambios
📋 PatientDashboard.tsx — cambios aplicados (funcionalidad 100% preservada)
  • REEMPLAZADO 4 ternarios anidados por funciones getDashboardState y getStateLabel con early returns. Añade tipo DashboardState para exhaustividad.
  • ELIMINADO Helpers de una línea sin valor semántico: hasPatients, isValidId, formatPatientName. Usos sustituidos por expresiones directas.
  • EXTRAÍDO Chain de filter/map/reduce partido en 3 variables intermedias: highRiskPatients, weightedScores, totalScore. Duplicación de filtro eliminada.
  • ESTÁNDAR Todos los componentes convertidos de const Arrow = a export default function con return type explícito e interfaces Props separadas.
  • SIN CAMBIO Toda la lógica de negocio, callbacks, efectos, queries y renderizado condicional — idénticos. Zero breaking changes. Tests existentes pasan sin modificación.
6
Principios aplicados
🔒
Funcionalidad preservada
Cero cambios de comportamiento. Mismo output, mismos efectos secundarios, misma API pública del componente.
📐
Estándares del proyecto
function keyword, tipos explícitos, interface Props separado — todo alineado con el CLAUDE.md de NutriFlow.
👁
Claridad sobre brevedad
Código más largo pero explícito. Variables intermedias con nombres que narran la intención del negocio.
⚖️
Balance mantenido
No se eliminaron abstracciones útiles. Solo se removió lo que no aportaba claridad ni seguridad.
🎯
Scope focalizado
Solo PatientDashboard.tsx — el componente tocado en este sprint. Sin refactor oportunista de código estable.
📝
Cambios documentados
Solo los cambios con impacto en comprensión están anotados. No se añadieron comentarios que describen lo obvio.