Manual Tecnico

PhysCalc Pro — Documentacion de Arquitectura, Codigo y Despliegue

Version 1.0 | Abril 2026 | TecnoIndustria S.A.S.

1. Descripcion General del Sistema

AtributoValor
NombrePhysCalc Pro
TipoAplicacion web estatica (SPA-like)
PropositoCalculadora de propiedades fisicas industriales con 7 modulos
ClienteTecnoIndustria S.A.S.
Usuarios objetivoIngenieros, tecnicos y operarios de planta
BackendNinguno (100% client-side)
Base de datosNinguna
Dependencias externasNinguna (cero frameworks, cero librerias)
CompatibilidadChrome 80+, Firefox 75+, Edge 80+, Safari 13+

2. Stack Tecnologico

CapaTecnologiaVersionJustificacion
EstructuraHTML55Semantico, accesible, estandar web
EstilosCSS33Variables CSS, grid, flexbox, animaciones nativas
LogicaJavaScriptES6+Vanilla JS sin dependencias para maxima portabilidad
HostingGitHub Pages-Gratuito, HTTPS, CDN global, despliegue desde repositorio
Control de versionesGit2.x+Versionado estandar de la industria
Decisiones de diseno: Se eligio Vanilla JS (sin React, Vue, Angular ni jQuery) para garantizar: cero dependencias, carga instantanea, facil mantenimiento y portabilidad total. El proyecto funciona abriendo el HTML directamente desde el sistema de archivos.

3. Estructura de Archivos

PhysCalc-Pro/
|
|-- index.html              # Landing page / blog del proyecto
|-- app.html                # Aplicacion de calculadora (7 modulos)
|-- manual-usuario.html     # Manual de usuario
|-- manual-tecnico.html     # Manual tecnico (este archivo)
|
|-- css/
|   |-- style.css           # Estilos principales (app + blog)
|   |-- manual.css          # Estilos de los manuales
|
|-- js/
|   |-- app.js              # Logica completa de la calculadora
|
|-- img/                    # Directorio para imagenes (vacio por defecto)
|
|-- README.md               # Documentacion del repositorio
        

Tamano de archivos

ArchivoLineas aprox.Funcion
app.html~400Estructura de los 7 paneles + historial
js/app.js~490Config de formulas, motor generico, validacion, historial
css/style.css~650Todos los estilos (blog + app + responsive)
index.html~220Blog / landing page

4. Arquitectura del Software

El sistema sigue un patron Config-Driven donde la logica de cada formula se define como datos, no como codigo imperativo.

+--------------------------+
|        app.html          |   Capa de Presentacion
|  (Estructura + Paneles)  |   - HTML semantico
+--------------------------+   - Selectores, inputs, botones
            |
            v
+--------------------------+
|       style.css          |   Capa de Estilos
|  (Visual + Responsive)   |   - Variables CSS
+--------------------------+   - Grid/Flexbox responsive
            |                  - Estados (error, target, disabled)
            v
+--------------------------+
|        app.js            |   Capa de Logica
|                          |
|  +--------------------+  |
|  | FORMULAS (config)  |  |   Configuracion declarativa:
|  | - variables        |  |   - Variables con restricciones
|  | - solve functions  |  |   - Funciones de calculo
|  | - denominators     |  |   - Denominadores para div/0
|  | - labels           |  |   - Labels de botones
|  +--------------------+  |
|           |               |
|  +--------------------+  |
|  | Motor generico     |  |   Funciones genericas:
|  | - onSolveForChange |  |   - Cambio de variable objetivo
|  | - validateFormula  |  |   - Validacion en tiempo real
|  | - calcFormula      |  |   - Calculo y resultado
|  +--------------------+  |
|           |               |
|  +--------------------+  |
|  | Utilidades         |  |   Funciones compartidas:
|  | - Field errors     |  |   - Manejo de errores en campos
|  | - History          |  |   - Historial de calculos
|  | - Tabs             |  |   - Navegacion por pestanas
|  +--------------------+  |
+--------------------------+
        

5. Capa de Presentacion (HTML)

Estructura de un panel de calculo

Cada panel sigue esta estructura estandar:

<div class="calc-panel" id="panel-{modulo}">
  <h2>Titulo del modulo</h2>
  <div class="formula-display">Formula</div>
  <p class="description">Descripcion detallada...</p>

  <!-- Selector de variable a resolver -->
  <div class="solve-for-group">
    <label>Resolver para:</label>
    <select id="{modulo}-solve" class="solve-for-select">
      <option value="var1">...</option>
      <option value="var2">...</option>
    </select>
  </div>

  <!-- Campos para TODAS las variables -->
  <div class="form-group">
    <label for="{id}">Variable (simbolo) - unidad</label>
    <input type="number" id="{id}" step="any">
    <small>Descripcion y restricciones</small>
  </div>

  <button class="btn-calc" onclick="calcFormula('panel-{modulo}')">
    Calcular {variable}
  </button>

  <div class="result-box" aria-live="polite">
    <div class="result-label"></div>
    <div class="result-value"></div>
    <div class="result-detail"></div>
  </div>
</div>
        

Convencion de IDs

ElementoPatron de IDEjemplo
Panelpanel-{modulo}panel-calor
Selector resolver{modulo}-solvecalor-solve
Input de variable{modulo}-{var}calor-m, calor-q

6. Capa de Estilos (CSS)

Variables CSS (custom properties)

:root {
  --primary: #0d6efd;       /* Azul principal */
  --primary-dark: #0a58ca;  /* Azul hover */
  --accent: #00c853;        /* Verde acentuado */
  --dark: #1a1a2e;          /* Fondo oscuro */
  --dark2: #16213e;         /* Fondo oscuro secundario */
  --light: #f8f9fa;         /* Fondo claro */
  --gray: #6c757d;          /* Texto secundario */
  --white: #ffffff;
  --shadow: 0 4px 20px rgba(0,0,0,0.1);
  --radius: 12px;
}
        

Estados de los inputs

EstadoClase CSSVisual
Normal(ninguna)Borde gris #e0e0e0, fondo #fafafa
Enfocado:focusBorde azul, fondo blanco
Error.input-errorBorde rojo #e53935, fondo #fff5f5, animacion shake
Campo objetivo.solve-targetBorde verde punteado, fondo verde claro, readonly
Boton deshabilitado:disabledFondo gris #b0bec5, cursor not-allowed

Responsive breakpoints

BreakpointCambio
768pxNavbar se convierte en menu hamburguesa
500pxInput-row pasa de 2 columnas a 1 columna

7. Capa de Logica (JavaScript)

Funciones utilitarias

FuncionParametrosRetornoDescripcion
getVal(id)ID del inputnumber | nullLee y parsea el valor de un input. Retorna null si vacio o NaN.
isEmpty(id)ID del inputbooleanVerifica si el campo esta vacio.
formatNum(n)NumerostringFormatea a max 4 decimales, elimina ceros finales.
setFieldError(id, msg)ID, mensajevoidAgrega clase .input-error y mensaje de error al campo.
clearFieldError(id)IDvoidRemueve error visual y mensaje del campo.
setBtn(panelId, enabled, text)Panel, estado, textovoidHabilita/deshabilita boton y cambia su texto.
showResult(...)Panel, label, value, unit, detailvoidMuestra resultado en la caja verde.

Funciones del motor generico

FuncionDescripcion
onSolveForChange(panelId)Se ejecuta al cambiar el selector "Resolver para". Configura readonly en el campo objetivo, restaura defaults en los demas, actualiza la formula mostrada y el texto del boton.
validateFormula(panelId)Valida todos los campos de entrada (excepto el objetivo) en tiempo real. Aplica restricciones de positive, notZero, min, max y denominador. Habilita/deshabilita el boton.
calcFormula(panelId)Lee los valores, ejecuta la funcion solve correspondiente, escribe el resultado en el campo objetivo y en la result-box, y guarda en el historial.

Funciones del historial

FuncionDescripcion
addToHistory(module, formula, result, detail)Agrega una entrada al inicio del array (max 10).
renderHistory()Renderiza el HTML del historial.
clearHistory()Vacia el array y re-renderiza.

8. Sistema de Configuracion de Formulas

Cada modulo de calculo esta definido como un objeto en FORMULAS:

FORMULAS['panel-calor'] = {
  name: 'Calor',                    // Nombre para historial
  selectId: 'calor-solve',          // ID del <select> "Resolver para"

  variables: {
    Q: {
      id: 'calor-q',               // ID del input
      label: 'Calor (Q)',          // Nombre para UI
      unit: 'J',                    // Unidad
      positive: false,              // ¿Debe ser >= 0?
      notZero: false,               // ¿No puede ser 0?
      default: null,                // Valor por defecto (null = sin default)
      min: undefined,               // Valor minimo
      max: undefined                // Valor maximo
    },
    m: { ... },
    c: { ... },
    dT: { ... }
  },

  rearranged: {                     // Formula mostrada segun variable objetivo
    Q:  'Q = m · c · ΔT',
    m:  'm = Q / (c · ΔT)',
    c:  'c = Q / (m · ΔT)',
    dT: 'ΔT = Q / (m · c)'
  },

  solve: {                          // Funciones de calculo
    Q:  (v) => v.m * v.c * v.dT,
    m:  (v) => v.Q / (v.c * v.dT),
    c:  (v) => v.Q / (v.m * v.dT),
    dT: (v) => v.Q / (v.m * v.c)
  },

  btnLabels: {                      // Texto del boton segun variable
    Q: 'Calcular Calor',
    m: 'Calcular Masa', ...
  },

  denominators: {                   // Variables que son denominador
    Q: [],                          // Q = m*c*dT (no divide)
    m: ['c', 'dT'],                 // m = Q/(c*dT) -> c y dT no pueden ser 0
    c: ['m', 'dT'],
    dT: ['m', 'c']
  },

  // Opcionales:
  postValidate: (target, vals) => { ... }, // Validacion extra
  postCalc: (target, result, vals) => { ... } // Info extra post-calculo
};
        

Propiedades de variables

PropiedadTipoDescripcion
idstringID del elemento input en el DOM
labelstringNombre visible para resultados e historial
unitstringUnidad de medida
positivebooleanSi true, no acepta valores negativos
notZerobooleanSi true, no acepta valor 0
defaultstring|nullValor precargado (ej: '4186' para agua, '9.8' para gravedad)
minnumber|undefinedValor minimo permitido
maxnumber|undefinedValor maximo permitido

9. Sistema de Validacion

Capas de validacion

CapaDondeQue valida
1. HTMLAtributos min, max, stepPrimera barrera nativa del navegador
2. Tiempo real (JS)validateFormula() en eventos input/changeRestricciones fisicas por campo individual
3. Contexto (JS)Verificacion de denominadoresDivision por cero segun la variable objetivo
4. Post-validacionpostValidate() opcionalReglas entre multiples campos (ej: Eutil ≤ Etotal)
5. Post-calculoVerificacion de isFinite()Resultado no es Infinity ni NaN

Flujo de validacion

Usuario escribe en un input
         |
         v
[evento 'input' dispara validateFormula(panelId)]
         |
         v
Para cada variable (excepto la objetivo):
  - ¿Campo vacio? -> no marcar error, allValid = false
  - ¿Valor null/NaN? -> error "Valor no valido"
  - ¿positive && val < 0? -> error "No puede ser negativo"
  - ¿notZero && val === 0? -> error "No puede ser cero"
  - ¿val < min? -> error "Minimo: {min}"
  - ¿val > max? -> error "Maximo: {max}"
  - ¿Es denominador && val === 0? -> error "No puede ser cero (division)"
  - Todo OK -> clearFieldError()
         |
         v
[postValidate() si existe] -> validacion cruzada
         |
         v
setBtn(panelId, allValid) -> habilitar/deshabilitar boton
        
Principio: Los campos vacios NO se marcan con error (el usuario aun no ha interactuado). Solo se marcan cuando el usuario ha escrito un valor invalido.

10. Flujo de Datos Completo

[Carga de pagina]
    |
    v
DOMContentLoaded -> initAll()
    |
    +--> initTabs()               -- Configura pestanas
    +--> initRealTimeValidation   -- Bind eventos input/change
    |     |
    |     +--> Para cada panelId en FORMULAS:
    |           +--> Bind input/change en cada variable -> validateFormula()
    |           +--> Bind change en selector -> onSolveForChange()
    |           +--> onSolveForChange() inicial (estado por defecto)
    |
    +--> renderHistory()          -- Historial vacio

[Usuario cambia "Resolver para:"]
    |
    v
onSolveForChange(panelId)
    |
    +--> Actualiza formula-display con formula reorganizada
    +--> Campo objetivo: readonly, placeholder "= ?", clase .solve-target
    +--> Otros campos: editables, restaurar defaults
    +--> Actualiza texto del boton
    +--> Oculta resultado previo
    +--> Llama validateFormula() para actualizar estado del boton

[Usuario escribe en un campo]
    |
    v
validateFormula(panelId)
    |
    +--> Valida cada campo -> setFieldError() o clearFieldError()
    +--> setBtn(panelId, allValid)

[Usuario presiona "Calcular"]
    |
    v
calcFormula(panelId)
    |
    +--> Lee valores de inputs
    +--> Ejecuta FORMULAS[panelId].solve[target](vals)
    +--> Verifica isFinite(result)
    +--> Escribe resultado en campo objetivo
    +--> Muestra en result-box
    +--> addToHistory() -> renderHistory()
        

11. Despliegue en GitHub Pages

Requisitos previos

  • Cuenta de GitHub
  • Git instalado localmente

Pasos de despliegue

# 1. Crear repositorio en GitHub (nombre: PhysCalc-Pro)

# 2. Vincular repositorio local
cd C:/Users/Ardisa/TecnoIndustria
git remote add origin https://github.com/TU-USUARIO/PhysCalc-Pro.git
git branch -M main
git push -u origin main

# 3. Configurar GitHub Pages
# - Ir a Settings > Pages
# - Source: Deploy from a branch
# - Branch: main / (root)
# - Save

# 4. Esperar 1-2 minutos. URL sera:
# https://TU-USUARIO.github.io/PhysCalc-Pro/
        

Actualizaciones

# Hacer cambios en los archivos, luego:
git add -A
git commit -m "Descripcion del cambio"
git push

# GitHub Pages se actualiza automaticamente en 1-2 minutos
        

12. Consideraciones de Seguridad

AspectoEstadoDetalle
XSSProtegidoNo se usa innerHTML con datos del usuario. El historial usa datos calculados internamente, no input directo.
Inyeccion SQLN/ANo hay base de datos.
HTTPSSi (GitHub Pages)Certificado SSL gratuito incluido.
Datos sensiblesNingunoNo se recopilan datos personales. Todo se procesa en el navegador.
CookiesNingunaNo se usan cookies ni almacenamiento local.
eval()No se usaLas formulas se ejecutan como funciones JavaScript nativas.

13. Rendimiento

MetricaValor estimado
Tamano total (sin imagenes)~35 KB (sin comprimir)
Requests HTTP3 (HTML + CSS + JS)
Tiempo de carga< 1 segundo
Dependencias externas0
Frameworks0
Tiempo de calculo< 1 ms por operacion
Funciona offlineSi (una vez cargada)

14. Accesibilidad

  • aria-live="polite" en las cajas de resultado para lectores de pantalla.
  • aria-label en selectores de formula.
  • Todos los campos tienen <label> asociado via for.
  • Contraste de colores cumple WCAG AA.
  • Navegacion completa por teclado (Tab, Enter, flechas).
  • Elementos interactivos nativos (button, input, select).
  • Responsive desde 320px.

15. Guia de Mantenimiento

Cambiar un valor por defecto

Editar la propiedad default en el objeto FORMULAS en js/app.js.

Cambiar restricciones de un campo

Modificar las propiedades positive, notZero, min, max del campo en FORMULAS.

Cambiar colores

Editar las variables CSS en :root de css/style.css.

Cambiar clasificacion de eficiencia

Modificar la funcion postCalc en FORMULAS['panel-eficiencia'].

16. Como Agregar un Nuevo Modulo

Pasos para agregar una nueva formula (ej: Ley de Ohm V = I · R):

Paso 1: Agregar pestana en app.html

<button class="calc-tab" data-target="panel-ohm">Ley de Ohm</button>
        

Paso 2: Agregar panel HTML en app.html

<div class="calc-panel" id="panel-ohm">
  <h2>Ley de Ohm</h2>
  <div class="formula-display">V = I · R</div>
  <p class="description">...</p>

  <div class="solve-for-group">
    <label for="ohm-solve">Resolver para:</label>
    <select id="ohm-solve" class="solve-for-select">
      <option value="V">V - Voltaje (V)</option>
      <option value="I">I - Corriente (A)</option>
      <option value="R">R - Resistencia (Ω)</option>
    </select>
  </div>

  <div class="form-group">
    <label for="ohm-v">Voltaje (V) - en Voltios (V)</label>
    <input type="number" id="ohm-v" step="any">
  </div>
  <!-- ... campos para I y R ... -->

  <button class="btn-calc" onclick="calcFormula('panel-ohm')" disabled>
    Calcular
  </button>
  <div class="result-box" aria-live="polite">...</div>
</div>
        

Paso 3: Agregar configuracion en js/app.js

FORMULAS['panel-ohm'] = {
  name: 'Ley de Ohm',
  selectId: 'ohm-solve',
  variables: {
    V: { id: 'ohm-v', label: 'Voltaje (V)', unit: 'V',
         positive: true, notZero: false, default: null },
    I: { id: 'ohm-i', label: 'Corriente (I)', unit: 'A',
         positive: true, notZero: true, default: null },
    R: { id: 'ohm-r', label: 'Resistencia (R)', unit: 'Ω',
         positive: true, notZero: true, default: null }
  },
  rearranged: { V: 'V = I · R', I: 'I = V / R', R: 'R = V / I' },
  solve: {
    V: v => v.I * v.R,
    I: v => v.V / v.R,
    R: v => v.V / v.I
  },
  btnLabels: { V: 'Calcular Voltaje', I: 'Calcular Corriente',
               R: 'Calcular Resistencia' },
  denominators: { V: [], I: ['R'], R: ['I'] }
};
        
Eso es todo. No se necesita modificar ninguna funcion. El motor generico detecta automaticamente el nuevo modulo en el Object.keys(FORMULAS) al inicializar. La validacion, el cambio de variable objetivo, el calculo y el historial funcionan sin codigo adicional.