Prototipando con código para pruebas con usuarios
Formularios y grandes volúmenes de datos con HTML5, PHP y JavaScript
Un prototipo no es una versión preliminar del diseño: es una herramienta económica para lograr respuestas. De qué estará hecho, cómo y con qué nivel de detalle, dependerá de las preguntas que tengan sentido en ese momento del proyecto.
Les comparto algunos ejemplos de cómo prototipando con código, podemos hacer y responder mejores preguntas de UX Research, tanto en procesos de evaluación interna, como con personas usuarias reales.
Prototipos HTML5: formularios, sacar selfies…
Por ejemplo, cuando las preguntas que necesitamos hacernos tienen que ver con la usabilidad de un formulario, no servirá de nada un “prototipo” estático en Figma, donde haciendo click mágicamente se completen los campos y los usuarios puedan avanzar manoteando el mouse y sin mirar la pantalla.
Para evaluar la usabilidad de un formulario, necesitaremos poner a prueba un prototipo operable del formulario.
Puede alcanzar con unas pocas líneas de HTML que nos permitan ver la experiencia de los usuarios completando los datos que les pedimos:
<label for="cuil">Número de CUIL:</label><br>
<input id="cuil" type="number" name="cuil" size="11" min="20000000000">
<br><br>
<input type="reset">
<input type="submit" value="Continuar">
Sólo así podremos hacer y contestar preguntas sobre los usuarios como:
- ¿Se saben el dato de memoria, o necesitan buscarlo?
- ¿Lo tipean, o lo copian y lo pegan?
- ¿Lo ingresan sin guiones, como quiere IT? ¿O podemos demostrar que esa restricción es innecesaria y perjudicial?
- ¿Hace sentido la validación que estamos usando?
- ¿Cuál teclado es mejor para completar este dato en móvil? ¿El que presenta INPUT type=”number”, o el de type=”tel”?
- ¿Tiene sentido el “stepper” que muestra el INPUT type=”number” en la versión desktop?
- ¿Es necesario el botón “restablecer” que insisten en poner los de sistemas? ¿O podemos demostrar al Negocio que es una pésima idea, y lograr que IT deje de opinar sobre este tema?
Con este enfoque, en Kambrica pudimos no sólo resolver casos de ingreso de información, sino también prototipar el uso de cámara en móvil – y así identificar los verdaderos problemas que tenían los usuarios para registrarse en una App financiera. Problemas que sólo se manifestaban al usar las cámaras del celular, una de las tantas cosas que se pueden hacer con HTML5:
<!--
// Santiago Bustelo, Kambrica.com
// Publicado bajo licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional
El elemento HTML5 para la cámara es INPUT:file.
Como es MUY diferente al elemento que necesitamos mostrar en el prototipo,
y no podemos maquillarlo con CSS, usaremos un botón común para la acción.
Ese botón hará click en el INPUT:file invisible.
-->
<!-- Usar cámara para selfies -->
<button
onclick="document.getElementById('camara-selfie').click()"
>Sacar selfie</button>
<input
style="visibility:hidden;"
id="camara-selfie"
type="file" accept="image/*, video/*" capture="user">
<br><br>
<!-- Usar cámara para tomar fotos: -->
<button
onclick="document.getElementById('camara-principal').click()"
>Sacar foto al DNI</button>
<input
style="visibility:hidden;"
id="camara-principal"
type="file" accept="image/*, video/*" capture="environment">
Prototipando datos… un montón de datos
Cuando las preguntas que nos queremos hacer tienen que ver con cómo nuestros usuarios visualizan y manejan grandes volúmenes de datos, no podemos mostrarles un “lorem ipsum” repetido 100 veces.
Inventar un volumen de datos grande y variado no sólo resulta tedioso – es muy complicado mantenerlos de forma consistente en todas las pantallas que tengamos que prototipar. Y si necesitamos mostrar muchos datos distintos sobre una misma vista (por ejemplo, mostrar diversas combinaciones de datos sobre la plantilla “ficha del cliente”), crear las variantes a mano puede resultar absurdo.
Resulta mucho mejor generar estos datos de manera mecánica.
Por ejemplo, podemos crear números de teléfono (entre 1000–0000 y 5999–9999) como números aleatorios. En una planilla de cálculo, podrímos usar la fórmula RANDBETWEEN( mínimo, máximo ):
HTML no tiene esa funcionalidad. Pero sirviendo nuestro prototipo desde un entorno local como MAMP o un hosting Apache del montón, es bastante sencillo “inyectar” funcionalidad PHP en medio del código HTML:
<!-- HTML original: 100 filas y números a mano -->
<table>
<tr><td>1</td><td>28828534</td></tr>
<tr><td>2</td><td>46326679</td></tr>
<tr><td>3</td><td>24144353</td></tr>
…
<tr><td>100</td><td>24464010</td></tr>
</table>
<!-- Versión PHP, generando 100 filas y números de forma mecánica -->
<table>
<?php for ($i = 1; $i <= 100; $i++) { ?>
<tr><td><?= $i ?></td><td><?= rand( 10000000, 59999999 ) ?></td></tr>
<?php } ?>
</table>
Con muy poco esfuerzo, habremos logrado 100 filas de “números de teléfono”… que nos deberían inspirar a ponerles un guión en el medio para hacerlos más legibles.
Aquí, antes y después:
Para crear el ejemplo de la derecha, generamos dos números aleatorios con un guión en el medio: <?= rand( 1000, 5999 ) ?>-<?= rand( 1000, 9999 ) ?>
Este enfoque aplica muy bien cuando los datos para prototipar son numéricos, y es verosímil que cambien cada vez que volvemos a cargar la página.
Pero… ¿Qué sucede cuando los datos que queremos prototipar no son numéricos? ¿Y qué pasa si necesitamos que sean siempre los mismos cada vez que se visita la misma pantalla?
Prototipando datos pseudo aleatorios
Vamos a empezar con la pseudo-aleatoriedad: generar los datos de una manera mecánica no predecible, y que sea siempre la misma. Es decir, en lugar de “1,2,3,4”, mostrar por ejemplo “21,6,89,53” cada vez que presentemos los primeros cuatro números de la lista.
En lugar de una función rand() que nos devuelva un número distinto cada vez que la llamamos, necesitamos una función noTanRand($i) que nos devuelva un valor “raro” para el $i que le pasamos, siendo $i el número de item: el que en el código de ejemplo anterior, usamos en for( $i… ).
Esa función que necesitamos, la podemos construir con un algoritmo de hash. Usando la función nativa de PHP, podemos tomar un string cualquiera, y devolver un número entre 0 y 1 (como haría rand() sin argumentos):
<?php
// Santiago Bustelo, Kambrica.com
// Publicado bajo licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional
function noTanRand($seed){
$hash = md5($seed); //Obtenemos hash hexadecimal, ej. "cfcd208495d565ef66e7dff9f98764da">
$result = hexdec(substr($hash, 0, 8)) //tomamos los primeros 8 caracteres, de string a número: 4 bytes (2 ^ 8 * 4 )
/ (pow(2, 32) - 1); //y dividimos por el máximo posible para obtener nuestro número entre 0 y 1
return $result;
}
// Probamos la función:
for( $i = 0; $i <= 10; $i++) {
echo ( noTanRand($i).", " );
}
// Resultado:
// 0.81172374002908, 0.76871122251468, 0.78171459161251, 0.92498448466067, 0.65820255727931, 0.8939549624673, 0.087784356458062, 0.5589125378893, 0.78883317620233, 0.27253036533308, 0.82753398474947,
?>
Eso está bastante bien. Aunque para prototipar, quizás nos vendría aún mejor una función que, desde PHP o JavaScript, nos devolviera el mismo resultado. A la que podamos pasarle un string, un número, o cualquier cosa. Y a la que podamos opcionalmente definirle un número mínimo y un máximo.
Como en JavaScript no disponemos de la misma función nativa que en PHP, necesitamos construir un algoritmo de hash para las dos versiones. Después de algunas pruebas, logramos el siguiente código adaptando el algoritmo ‘times 33’ de Daniel J. Bernstein implementado por Matt Barker. En honor a D.J., llamaramos a nuestra función… djash:
<?php
/* djash - Versión PHP: */
// Santiago Bustelo, Kambrica.com
// Publicado bajo licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional
function djash($input,$desde=0,$hasta=1) {
$salt = "Salt para darle gusto al hash";
// Convierte el input a una cadena si no lo es
if (!is_string($input)) {
if (is_object($input)) {
$input = json_encode($input);
} else {
$input = strval($input);
}
}
// Agrega el salt al input
$input .= $salt;
$hashValue = 5381;
for ($i = 0; $i < strlen($input); $i++) {
$char = ord($input[$i]);
$hashValue = ($hashValue * 33) ^ $char;
$hashValue = $hashValue & 0xFFFFFFFF; // Recorta el número a 32 bits
}
// Normalizamos el número al rango [0, 1] y aplicamos desde/hasta:
$resultado = ( $hashValue / (pow(2, 32) - 1) ) * ($hasta - $desde) + $desde;
if( $desde != 0 || $hasta != 1){ $resultado = floor ( $resultado ); }
return $resultado;
}
?>
<script>
/* djash - Versión JavaScript: */
// Santiago Bustelo, Kambrica.com
// Publicado bajo licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional
function djash(input, desde = 0, hasta = 1) {
const salt = "Salt para darle gusto al hash";
// Convierte el input a una cadena si no lo es
if (typeof input !== "string") {
if (typeof input === "object") {
input = JSON.stringify(input);
} else {
input = input.toString();
}
}
// Agrega el salt al input
input += salt;
let hashValue = 5381;
for (let i = 0; i < input.length; i++) {
const char = input.charCodeAt(i);
hashValue = (hashValue * 33) ^ char;
hashValue = hashValue >>> 0; // Convertir a 32 bits
}
// Normalizamos el número al rango [0, 1] y aplicamos desde/hasta:
let resultado = (hashValue / (Math.pow(2, 32) - 1)) * (hasta - desde) + desde;
if (desde !== 0 || hasta !== 1) {
resultado = Math.floor(resultado);
}
return resultado;
}
</script>
Después de algún que otro bug, comprobamos que –dígito más, dígito menos– las dos funciones nos dan el mismo valor para cada $i:
Bueno, ya tenemos nuestra función para armar datos numéricos. Podemos usarla para pedirle nos devuelva un elemento pseudo-aleatorio de un array:
<?php
/* djitem - Versión PHP: */
// Santiago Bustelo, Kambrica.com
// Publicado bajo licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional
function djitem( $array, $ix ) {
//$ix: admite un número entre 0 y 1; caso contrario obtenemos djash de $ix
if ($ix <= 0 || $ix >= 1) { $ix = djash($ix); }
$totalItems = count($array);
$weightedIndex = floor($ix * $totalItems);
return $array[$weightedIndex];
}
/* djitem - Versión JavaScript: */
// Santiago Bustelo, Kambrica.com
// Publicado bajo licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional
function djitem(array, ix) {
// Si ix está fuera del rango [0, 1], calculamos djash de ix
if (ix <= 0 || ix >= 1) { ix = djash(ix); }
var totalItems = array.length;
var weightedIndex = Math.floor(ix * totalItems);
return array[weightedIndex];
}
?>
Ese array puede ser una lista de provincias, apellidos… Por ejemplo, si tomamos una lista de 100 apellidos, y usamos dos para cada item variando el $ix en el segundo (concatenándole algún texto cualquiera), podemos lograr 10.000 combinaciones:
Resultados para ( djitem($DB_apellidos, ($i)). “ “ . djitem( $DB_apellidos, ($i.” dos”)) );
Y usando doble nombre y apellido, podríamos generar millones de clientes ficticios para un sistema de gestión:
Y podemos hacer que el link nos lleve a la ficha del cliente, con más datos. Datos que generaremos en base al índice del cliente, por lo que serán siempre consistentes para el mismo link.
Como la dirección, que creamos con un apellido y un número, más piso y departamento. Y cuidando, para que sea bien verosímil, de llamar “PB” al piso “0”. Así como de usar letras para el departamento en el 70% de los casos, y números en el resto.
También podemos mostrar planes de pagos para los clientes con balance negativo. O hasta una serie de muestras de color basadas en el ID del cliente:
O sea, podemos hacer cualquier cosa que necesitemos, siguiendo la lógica de cada caso. Cosas como:
- Establecer diferentes condiciones según su probabilidad (ej. “Roaming activado” para el 63% de los clientes, “Sin 4G” para el 7.5%, etc.)
- Fechas de nacimiento, números de DNI y CUIL verosímiles para la demografía de nuestros usuarios,
- Productos en sus últimas compras (tomados de nuestro catálogo),
- Calcular series numéricas y balances financieros,
- Mostrar imágenes aleatorias de una lista,
- Poner un punto en el mapa con latitud y longitud (dentro de un rango definido, para evitar caer en el medio del mar),
- Mostrar textos (ej. mensajes enviados por el cliente) en base a una o más listas de modelos de frases… según probabilidad de las casuísticas de consulta / gestión / reclamo.
Aplicando estos criterios, en Kambrica logramos prototipar un sistema con más de 50.000 clientes y reglas de negocio sumamente complejas. Ajustando el prototipo de manera iterativa en base a las observaciones con usuarios, encontramos formas más claras de presentar la información y flujos de operación más eficientes – al punto de que los usuarios pudieron empezar a ver y resolver casos que llevaban meses sin haber detectado.
Demo y código PHP del ejemplo
Pueden ver este prototipo de ejemplo funcionando en: https://demos.kambrica.com/djash
Para que puedan incorporar este enfoque en sus proyectos, les dejo más abajo el código de estos últimos ejemplos.
Espero les resulte útil y no duden en escribirnos si necesitan apoyo o quieren llegar más lejos: en Kambrica llevamos más de 10 años trabajando procesos de capacitación, consultoría y mentoría con equipos de UX, IT y Producto para mejorar sus procesos y resultados 🙂
<?php
// *** _funciones.php ***
// Funciones para prototipar datos en gran volumen.
// Santiago Bustelo, Kambrica.com
// Publicado bajo licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional
// https://creativecommons.org/licenses/by-sa/4.0/deed.es
$DB_nombres = [
"M" => [ /* Masculinos: primeros por frecuencia en Argentina según https://forebears.io/argentina/forename, corregidos (no tenían acentos)s */
"Juan","José","Carlos","Jorge","Luis","Miguel","Héctor","Ramón","Roberto","Oscar","Daniel","Mario","Pedro","Ricardo","Raúl","Pablo","Eduardo","Sergio","Marcelo","Julio","Hugo","Alberto","Rubén","Gustavo","Antonio","Víctor","Francisco","Diego","Ángel","Alejandro","Claudio","Walter","Fernando","Néstor","Guillermo","Manuel","Cristian","Martín","Alfredo","Enrique","Osvaldo","Javier","Gabriel","Horacio","César","Rodolfo","Omar","Andrés","Ariel","Ernesto","Leonardo","Domingo","Marcos","Nicolás","Sebastián","Adrián","Norberto","Mariano","Esteban","Darío","Fabián","Edgardo","Rafael","Vicente","Orlando","Gerardo","Jesús","Hernán","Federico","Félix","Aldo","Armando","Adolfo","David","Emilio","Germán","Aníbal","Lucas","Mauricio","Humberto","Ignacio","Agustín","Felipe","Leandro","Tomás","Abel","Gregorio","Luciano","Segundo","Rolando","Gastón","Nelson","Maximiliano","Salvador","Lorenzo","Christian","Silvio","Arturo","Rodrigo","Mauro","Dante","Bernardo","Gonzalo","Rogelio","Julián","Eugenio","Ismael","Marcelino","Santiago"
],
"F" => [ /* Femeninos, primeros por frecuencia en Argentina según https://forebears.io/argentina/forenames, corregidos (no tenían acentos) */
"María","Ana","Silvia","Rosa","Norma","Marta","Claudia","Graciela","Mónica","Susana","Mirta","Alicia","Patricia","Nélida","Juana","Liliana","Sandra","Laura","Olga","Andrea","Elsa","Ramona","Adriana","Marcela","Carmen","Lidia","Teresa","Gabriela","Gladys","Verónica","Blanca","Natalia","Margarita","Irma","Beatriz","Mercedes","Alejandra","Dora","Elena","Ángela","Cristina","Lorena","Delia","Isabel","Stella","Mariana","Mariela","Nilda","Estela","Nancy","Sara","Nora","Viviana","Lucía","Analía","Julia","Luisa","Miriam","Karina","Antonia","Paola","Carolina","Cecilia","Sonia","Hilda","Mabel","Elba","Valeria","Rosana","Silvana","Silvina","Paula","Carina","Yolanda","Francisca","Noemí","Elvira","Romina","Celia","Marisa","Gloria","Clara","Angélica","Inés","Daniela","Josefa","Catalina","Eva","Raquel","Martha","Marina","Élida","Rita","Roxana","Griselda","Haidé","Vanesa","Esther","Amalia","Elisa","Amelia","Victoria","Zulema"
]
];
$DB_apellidos = [ /* primeros por frecuencia en Argentina según https://forebears.io/argentina/surnames, corregidos (no tenían acentos) */
"González","Rodríguez","Gómez","Fernández","López","Díaz","Martínez","Pérez","García","Sánchez","Romero","Sosa","Torres","Álvarez","Ruiz","Ramírez","Flores","Benítez","Acosta","Medina","Herrera","Suárez","Aguirre","Giménez","Gutiérrez","Pereyra","Rojas","Molina","Castro","Ortiz","Silva","Núñez","Luna","Juárez","Cabrera","Ríos","Morales","Godoy","Moreno","Ferreyra","Domínguez","Carrizo","Peralta","Castillo","Ledesma","Quiroga","Vega","Vera","Muñoz","Ojeda","Ponce","Villalba","Cardozo","Navarro","Coronel","Vázquez","Ramos","Vargas","Cáceres","Arias","Figueroa","Córdoba","Correa","Maldonado","Paz","Rivero","Miranda","Mansilla","Farías","Roldán","Méndez","Guzmán","Agüero","Hernández","Lucero","Cruz","Páez","Escobar","Mendoza","Barrios","Bustos","Ávila","Ayala","Blanco","Soria","Maidana","Acuña","Leiva","Duarte","Moyano","Campos","Soto","Martín","Valdez","Bravo","Chávez","Velázquez","Olivera","Toledo","Franco","Ibañez","Leguizamón","Montenegro","Delgado","Arce","Ibarra","Gallardo","Santillán"
];
// Aprovecharemos función nativa PHP para mostrar currency
$formatUSD = new NumberFormatter('en_US', NumberFormatter::CURRENCY);
function djash($input) {
// Devuelve un número entre 0 y 1, siempre el mismo para el mismo $input.
// Salt hardcodeado
$salt = "Salt para darle gusto al hash";
if (!is_string($input)) {
if (is_object($input)) {
$input = json_encode($input);
} else {
$input = strval($input);
}
}
// Agrega el salt al input
$input .= $salt;
// Función de hash básica basada en el algoritmo 'times 33' de Daniel J. Bernstein.
$hashValue = 5381;
for ($i = 0; $i < strlen($input); $i++) {
$char = ord($input[$i]);
$hashValue = ($hashValue * 33) ^ $char;
$hashValue = $hashValue & 0xFFFFFFFF; // Recorta el número a 32 bits
}
// Normalizamos el número al rango [0, 1]
return $hashValue / (pow(2, 32) - 1);
}
function djitem( $array, $ix ) {
// Devuelve un item ($ix: djash) de un $array.
// $ix puede ser: djash entre 0 y 1, o un string/número/objeto del cual obtener un djash.
//$ix: admite un número entre 0 y 1; caso contrario obtenemos djash de $ix
if ( !is_numeric($ix) || ( $ix <= 0 || $ix >= 1) ) { $ix = djash($ix); }
$totalItems = count($array);
$weightedIndex = floor($ix * $totalItems);
return $array[$weightedIndex];
}
function djnombre( $seed, $what = "N A" ){
// Devuelve un string según djash $seed, siguiendo instrucciones de $what
// Ejemplo de uso: djnombre( "0", "[s] n n a a: (11) dddd-dddd" );
// $seed = string para alimentar djash.
// $what = cada caracter codifica un térmono a devolver:
// D: dígito.
// M: nombre masculino.
// F: nombre femenino.
// N: nombre masculino o femenino (50% de probabilidad inicial; siguientes usos en $what tendrán mismo género).
// A: apellido.
// S: devuelve literalmente "m" o "f" (50% de probabilidad cada opción, ), para uso en funciones.
// Cualquier otro caracter se devuelve como se recibió.
// Default: "n a" (nombre, espacio, apellido).
global $DB_nombres ;
global $DB_apellidos ;
$result = "";
$what = str_split( $what );
$whatix = 0;
$sexo = djash($seed) > .5 ? "M" : "F" ; //Calculamos M/F al azar 50/50 chances.
foreach( $what as $whatChar ){
$pr = djash( $seed . $whatix . $result ); //Para cada caracter usaremos un seed distinto.
switch ( $whatChar ) {
case "D": //digito entre 0 y 9
$result .= floor( $pr * 10 );
$whatix++;
break;
case "S": //imprimimos el $sexo generado por djash (con break)
case "N": //…o solo lo usamos para continuar en los casos m/f: (no hacemos break)
if( $whatChar == "s" ){
$result .= $sexo;
break;
}
case "M":
case "F":
//nombre masculino o femenino
//estos dos casos toman $sexo de $whatChar:
if( $whatChar == "M" || $whatChar == "F" ){
$sexo = $whatChar;
}
$result .= djitem( $DB_nombres[ $sexo ], $pr);
$whatix++;
break;
case "A":
//apellido
$result .= djitem( $DB_apellidos, $pr);
$whatix++;
break;
default:
//si no es un caracter codificado, lo concatenamos en output como vino.
$result .= $whatChar ;
//no afectamos $whatix
};
};
return ( $result );
}
?>
<?php // *** clientes.php ***
include_once('_funciones.php');
?><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=360, initial-scale=1" />
<style>
BODY{
font-family: arial,sans-serif;
font-size:14pt;
line-height:140%
}
</style>
</head>
<body>
<ol>
<?php
// Loop
for ($i = 0; $i <= 1000; $i++) {
//calculamos balance del cliente, puede ser negativo:
$balance = ( djash($i)*100000 ) - 30000;
$balanceColor = $balance < 0 ? "red" : "#999";
?>
<li style="float:left; width:380px" id="cliente-<?= $i ?>">
<!-- Pasamos el $i como id del cliente por URL (variable $_GET de PHP) -->
<a href="cliente.php?id=<?= $i ?>"><?= djnombre( $i, "N N A A" ) ?></a><br>
<?= djnombre( $i, "11 DDDD-DDDD" ) ?><br>
<span style="color:<?= $balanceColor ?>"><?= $formatUSD->formatCurrency( $balance , 'USD' ) ?></span><br>
</li>
<?php
} // Fin loop
?>
</ol>
</body>
</html>
<?php //*** cliente.php ***
include_once('_funciones.php');
// Tomamos $id del URL (variable de $_GET):
$id = $_GET['id'] ?? 0;
?><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=360, initial-scale=1" />
<style>
BODY{
font-family: arial,sans-serif;
font-size:14pt;
line-height:140%
}
</style>
</head>
<body>
<?php
//calculamos balance del cliente, puede ser negativo:
$balance = ( djash($id)*100000 ) - 30000;
$balanceColor = $balance < 0 ? "red" : "#999";
//calculamos piso y departamento para la dirección.
$direccion_depto = //el depto puede ser numerico (30% de los casos) o una letra (resto)
djash( $id." salt-piso-numerico" ) < 0.3 ?
floor( djash($id." salt-piso")*9 * 3 + 1 ) //hasta 3 deptos por piso
:
djitem( [ "A","B","C","D" ] , ( $id . " salt-piso") )
;
$direccion_piso = floor( djash($id." salt-piso")*9 );
//creamos el string de Piso y depto:
$direccion_pisodepto = "Piso " . $direccion_piso . " Depto. " . $direccion_depto;
// … excepto en casos de Planta Baja.
if( $direccion_piso == 0 ){
$direccion_pisodepto = "PB Depto. " . $direccion_depto;
}
?>
<p>
<span style="float:right">
Cliente #<?= $id ?>
<a href="?id=<?= intval($id) -1 ?>">« anterior</a>
|
<a href="?id=<?= intval($id) +1 ?>">siguiente »</a>
</span>
<span><a href="clientes.php#cliente-<?=$id?>">« volver</a></span>
</p>
<hr>
<h1 style="width:480px;line-height:100%"><?= djnombre( $id, "N N A A" ) ?></h1>
<p>
<b>Balance:</b> <span style="color:<?= $balanceColor ?>"><?= $formatUSD->formatCurrency( $balance , 'USD' ) ?></span>
</p>
<?php //si el balance es negativo, planteamos planes de pagos de 3, 6 o 12 cuotas, con 10%, 20% o 30% de recargo
if( $balance <0){
$cuota3 = $balance * -1 / 3 * 1.1;
$cuota6 = $balance * -1 / 6 * 1.2;
$cuota12 = $balance * -1 / 12 * 1.3;
?>
<h4>Plan de pagos:</h4>
<ul>
<li>
<?= $formatUSD->formatCurrency( $cuota3, 'USD' ) ?>
x 3 cuotas =
<?= $formatUSD->formatCurrency( $cuota3 * 3, 'USD' ) ?>
</li>
<li>
<?= $formatUSD->formatCurrency( $cuota6, 'USD' ) ?>
x 6 cuotas =
<?= $formatUSD->formatCurrency( $cuota6 * 6, 'USD' ) ?>
</li>
<li>
<?= $formatUSD->formatCurrency( $cuota12, 'USD' ) ?>
x 12 cuotas =
<?= $formatUSD->formatCurrency( $cuota12 * 12, 'USD' ) ?>
</li>
</ul>
<?php
} //endif balance negativo
?>
<h2>Datos del Cliente</h2>
<ul>
<li><b>Teléfono:</b> <?= djnombre( $id, "11 DDDD-DDDD" ) ?></li>
<li><b>Dirección:</b> <?=
djnombre( $id." salt-calle" , "A " ) .
floor( djash($id)*5000 ) . " " .
$direccion_pisodepto
?></li>
</ul>
<h2>Muestras de color:</h2>
<?php
for ($i = 1; $i <= 5; $i++) {
// Creamos un color hex basado en $id y $i:
$djcolor = dechex( intval( djash( $id . " color " . $i) * hexdec( "ffffff") ) ); // intval() hace la conversión implícita para evitar Warning "PHP Deprecated: Implicit conversion from float to int loses precision"
?><div style="display:inline-block; width:80px; height:80px; border:1px solid #ccc; margin:0 5px 5px 0; background-color: #<?= $djcolor ?>"></div><?php
} //end for
?>
</body>
</html>