From efffe824b60b027498be5f0b68b9d8fb89a4b1eb Mon Sep 17 00:00:00 2001 From: MariaJoseMontepequeZet Date: Tue, 4 Aug 2026 14:12:21 -0600 Subject: [PATCH] Resolver ejercicio 80 intermedio --- .../maria-montepeque/maria-montepeque.css | 65 ++++++ .../maria-montepeque/maria-montepeque.html | 33 +++ .../maria-montepeque/maria-montepeque.js | 201 ++++++++++++++++++ 3 files changed, 299 insertions(+) create mode 100644 intermedio/ejercicios/80-cotizador-arquitectura/resoluciones/maria-montepeque/maria-montepeque.css create mode 100644 intermedio/ejercicios/80-cotizador-arquitectura/resoluciones/maria-montepeque/maria-montepeque.html create mode 100644 intermedio/ejercicios/80-cotizador-arquitectura/resoluciones/maria-montepeque/maria-montepeque.js diff --git a/intermedio/ejercicios/80-cotizador-arquitectura/resoluciones/maria-montepeque/maria-montepeque.css b/intermedio/ejercicios/80-cotizador-arquitectura/resoluciones/maria-montepeque/maria-montepeque.css new file mode 100644 index 0000000..e347cc4 --- /dev/null +++ b/intermedio/ejercicios/80-cotizador-arquitectura/resoluciones/maria-montepeque/maria-montepeque.css @@ -0,0 +1,65 @@ +body { + font-family: Arial, Helvetica, sans-serif; + background-color: #f2f0eb; + color: #2a271f; + padding: 24px; + max-width: 560px; + margin: 0 auto; +} + +h1 { + margin-bottom: 4px; +} + +#formulario-cotizacion { + display: flex; + flex-direction: column; + gap: 10px; + background-color: #ffffff; + border: 1px solid #ddd4c0; + border-radius: 8px; + padding: 14px; + margin: 16px 0; +} + +#formulario-cotizacion input, +#formulario-cotizacion select { + padding: 7px 8px; + border: 1px solid #c8bda0; + border-radius: 4px; +} + +#formulario-cotizacion button { + padding: 8px 16px; + background-color: #8a6d3b; + color: #fff; + border: none; + border-radius: 4px; + cursor: pointer; + align-self: flex-start; +} + +#resumen-cotizacion ul { + list-style: none; + padding: 0; + background-color: #ffffff; + border: 1px solid #ddd4c0; + border-radius: 8px; + padding: 12px 16px; +} + +#resumen-cotizacion li { + padding: 3px 0; + font-size: 14px; +} + +.total { + font-size: 18px; + font-weight: bold; + color: #8a6d3b; +} + +.error { + color: #b23b3b; + font-weight: bold; +} diff --git a/intermedio/ejercicios/80-cotizador-arquitectura/resoluciones/maria-montepeque/maria-montepeque.html b/intermedio/ejercicios/80-cotizador-arquitectura/resoluciones/maria-montepeque/maria-montepeque.html new file mode 100644 index 0000000..375cc3d --- /dev/null +++ b/intermedio/ejercicios/80-cotizador-arquitectura/resoluciones/maria-montepeque/maria-montepeque.html @@ -0,0 +1,33 @@ + + + + + + 80. Cotizador arquitectura 3D + + + +

Cotizador de render arquitectonico

+

Completa los datos del proyecto para ver el detalle del precio.

+ +
+ + + + + +
+ +
+ + + + diff --git a/intermedio/ejercicios/80-cotizador-arquitectura/resoluciones/maria-montepeque/maria-montepeque.js b/intermedio/ejercicios/80-cotizador-arquitectura/resoluciones/maria-montepeque/maria-montepeque.js new file mode 100644 index 0000000..bbb8ae7 --- /dev/null +++ b/intermedio/ejercicios/80-cotizador-arquitectura/resoluciones/maria-montepeque/maria-montepeque.js @@ -0,0 +1,201 @@ +// 80. Cotizador arquitectura 3D +// Resolucion: maria-montepeque +// +// Idea: el precio final se arma en pasos pequenos y encadenados (precio base por m2, costo de +// vistas adicionales, multiplicador de calidad, recargo por urgencia y descuento por proyecto +// grande), cada uno en su propia funcion para poder leer y probar el calculo paso a paso. +// El mismo archivo corre con `node maria-montepeque.js` (casos de prueba por consola) +// y en el navegador (maria-montepeque.html) como un formulario de cotizacion real. + +const PRECIO_POR_M2 = 8000; +const PRECIO_POR_VISTA_ADICIONAL = 150000; +const MULTIPLICADOR_CALIDAD = { estandar: 1, premium: 1.4, ultra: 1.8 }; +const RECARGO_URGENCIA = { normal: 0, urgente: 0.25, express: 0.5 }; +const M2_PARA_DESCUENTO = 200; +const PORCENTAJE_DESCUENTO_GRANDE = 0.1; + +// --------------------------------------------------------------------------- +// Validacion de los datos del formulario +// --------------------------------------------------------------------------- + +function validarDatosCotizacion(datos) { + if (!datos || typeof datos !== 'object') { + return { valido: false, motivo: 'Los datos de la cotizacion no son validos.' }; + } + if (!Number.isFinite(Number(datos.m2)) || Number(datos.m2) <= 0) { + return { valido: false, motivo: 'Los metros cuadrados deben ser un numero mayor a 0.' }; + } + if (!Number.isInteger(Number(datos.vistas)) || Number(datos.vistas) < 1) { + return { valido: false, motivo: 'Las vistas deben ser un numero entero de al menos 1.' }; + } + if (!MULTIPLICADOR_CALIDAD[datos.calidad]) { + return { valido: false, motivo: `Calidad invalida: "${datos.calidad}". Usa ${Object.keys(MULTIPLICADOR_CALIDAD).join(', ')}.` }; + } + if (!(datos.urgencia in RECARGO_URGENCIA)) { + return { valido: false, motivo: `Urgencia invalida: "${datos.urgencia}". Usa ${Object.keys(RECARGO_URGENCIA).join(', ')}.` }; + } + return { valido: true }; +} + +// --------------------------------------------------------------------------- +// Pasos del calculo (cada uno pequeno y encadenable) +// --------------------------------------------------------------------------- + +function calcularPrecioBase(m2) { + return Number(m2) * PRECIO_POR_M2; +} + +function calcularCostoVistas(vistas) { + const vistasAdicionales = Math.max(Number(vistas) - 1, 0); + return vistasAdicionales * PRECIO_POR_VISTA_ADICIONAL; +} + +function aplicarMultiplicadorCalidad(subtotal, calidad) { + return subtotal * MULTIPLICADOR_CALIDAD[calidad]; +} + +function calcularRecargoUrgencia(subtotal, urgencia) { + return subtotal * RECARGO_URGENCIA[urgencia]; +} + +// Reto extra: descuento automatico para proyectos grandes. +function calcularDescuentoPorTamano(m2, total) { + return Number(m2) >= M2_PARA_DESCUENTO ? Math.round(total * PORCENTAJE_DESCUENTO_GRANDE) : 0; +} + +// --------------------------------------------------------------------------- +// Cotizacion completa (encadena todos los pasos) +// --------------------------------------------------------------------------- + +function generarCotizacion(datos) { + const validacion = validarDatosCotizacion(datos); + if (!validacion.valido) { + return { exito: false, motivo: validacion.motivo }; + } + + const precioBase = calcularPrecioBase(datos.m2); + const costoVistas = calcularCostoVistas(datos.vistas); + const subtotal = precioBase + costoVistas; + const subtotalConCalidad = aplicarMultiplicadorCalidad(subtotal, datos.calidad); + const recargoUrgencia = calcularRecargoUrgencia(subtotalConCalidad, datos.urgencia); + const totalAntesDeDescuento = subtotalConCalidad + recargoUrgencia; + const descuento = calcularDescuentoPorTamano(datos.m2, totalAntesDeDescuento); + const total = Math.round(totalAntesDeDescuento - descuento); + + return { + exito: true, + detalle: { precioBase, costoVistas, subtotal, subtotalConCalidad, recargoUrgencia, descuento, total } + }; +} + +// --------------------------------------------------------------------------- +// Salida por consola +// --------------------------------------------------------------------------- + +function formatearMoneda(valor) { + return `$${Math.round(valor).toLocaleString('es-CO')}`; +} + +function mostrarCotizacionEnConsola(etiqueta, datos, resultado) { + console.log(`\n${etiqueta}`); + console.log('Datos:', datos); + if (!resultado.exito) { + console.log('Rechazada:', resultado.motivo); + return; + } + const { precioBase, costoVistas, subtotalConCalidad, recargoUrgencia, descuento, total } = resultado.detalle; + console.log(`Precio base (m2): ${formatearMoneda(precioBase)}`); + console.log(`Costo vistas adicionales: ${formatearMoneda(costoVistas)}`); + console.log(`Subtotal con calidad aplicada: ${formatearMoneda(subtotalConCalidad)}`); + console.log(`Recargo por urgencia: ${formatearMoneda(recargoUrgencia)}`); + console.log(`Descuento por proyecto grande: ${formatearMoneda(descuento)}`); + console.log(`TOTAL: ${formatearMoneda(total)}`); +} + +// --------------------------------------------------------------------------- +// Interfaz en el DOM +// --------------------------------------------------------------------------- + +function mostrarResumenEnDOM(contenedor, resultado) { + if (!resultado.exito) { + contenedor.innerHTML = `

${resultado.motivo}

`; + return; + } + const { precioBase, costoVistas, subtotalConCalidad, recargoUrgencia, descuento, total } = resultado.detalle; + contenedor.innerHTML = ` + +

TOTAL: ${formatearMoneda(total)}

+ `; +} + +function iniciarAplicacionDOM() { + const formulario = document.querySelector('#formulario-cotizacion'); + const resumenElemento = document.querySelector('#resumen-cotizacion'); + + if (!formulario || !resumenElemento) return; + + formulario.addEventListener('submit', (evento) => { + evento.preventDefault(); + const datosFormulario = new FormData(formulario); + const resultado = generarCotizacion({ + m2: datosFormulario.get('m2'), + vistas: datosFormulario.get('vistas'), + calidad: datosFormulario.get('calidad'), + urgencia: datosFormulario.get('urgencia') + }); + mostrarResumenEnDOM(resumenElemento, resultado); + }); +} + +// --------------------------------------------------------------------------- +// Ejecucion por consola (node maria-montepeque.js) con al menos dos casos de prueba +// --------------------------------------------------------------------------- + +function resolverEjercicio() { + console.log('=== 80. Cotizador arquitectura 3D ==='); + + // Caso 1: proyecto normal y proyecto grande con descuento. + const proyectoNormal = { m2: 80, vistas: 2, calidad: 'premium', urgencia: 'normal' }; + mostrarCotizacionEnConsola('Caso 1 - proyecto normal', proyectoNormal, generarCotizacion(proyectoNormal)); + + const proyectoGrande = { m2: 250, vistas: 3, calidad: 'ultra', urgencia: 'urgente' }; + mostrarCotizacionEnConsola('Caso 1 - proyecto grande (aplica descuento)', proyectoGrande, generarCotizacion(proyectoGrande)); + + // Caso 2: casos limite y validaciones - m2 invalido, vistas invalidas, + // calidad invalida y urgencia invalida. + console.log('\nCaso 2 - validaciones y casos limite:'); + mostrarCotizacionEnConsola( + 'm2 en 0 (invalido)', + { m2: 0, vistas: 1, calidad: 'estandar', urgencia: 'normal' }, + generarCotizacion({ m2: 0, vistas: 1, calidad: 'estandar', urgencia: 'normal' }) + ); + mostrarCotizacionEnConsola( + 'vistas decimales (invalido)', + { m2: 50, vistas: 1.5, calidad: 'estandar', urgencia: 'normal' }, + generarCotizacion({ m2: 50, vistas: 1.5, calidad: 'estandar', urgencia: 'normal' }) + ); + mostrarCotizacionEnConsola( + 'calidad invalida', + { m2: 50, vistas: 1, calidad: 'lujo', urgencia: 'normal' }, + generarCotizacion({ m2: 50, vistas: 1, calidad: 'lujo', urgencia: 'normal' }) + ); + mostrarCotizacionEnConsola( + 'urgencia invalida', + { m2: 50, vistas: 1, calidad: 'estandar', urgencia: 'ya-mismo' }, + generarCotizacion({ m2: 50, vistas: 1, calidad: 'estandar', urgencia: 'ya-mismo' }) + ); + + return { proyectoNormal: generarCotizacion(proyectoNormal), proyectoGrande: generarCotizacion(proyectoGrande) }; +} + +if (typeof document !== 'undefined') { + document.addEventListener('DOMContentLoaded', iniciarAplicacionDOM); +} else { + resolverEjercicio(); +}