Meeting Minute
===
###### tags: `Templates` `Meeting`
:::info
- **Location:** Room A
- **Date:** Nov 1, 2030 2:30 PM (CET)
- **Agenda**
1. Walk through signup flow `45min`
> [name=Yukai]
2. Sprint planning `45min`
3. Revisit onboarding v1 `20min`
- **Participants:**
- Max (MX)
- Yukai (YK)
- Yuhsuan (YH)
- Arwen (YC)
- **Contact:** Max <max@example.com>
- **Host:** YK
- **Reference:** - [Last week meeting minute](/s/template-meeting-note)
:::
## Walk through signup flow
- [Slide to explain the flow](/p/slide-example)
:books: Sección 11 - Agregando Model View Controller - Modelos y Bases de Datos
---
### Que es un ORM (Object Relational Mapping)?
:::info
Permite almacenar o leer objectos de tu base de datos.
Un ORM es un modelo de programación que permite mapear las estructuras de una base de datos relacional (SQL Server, Oracle, MySQL, etc.), en adelante RDBMS (Relational Database Management System), sobre una estructura lógica de entidades con el objeto de simplificar y acelerar el desarrollo de nuestras aplicaciones.
Basado en primise (.then y .catch)
- **Object:** Los objetos se definen con un lenguaje de programacion
- **Relational:** Es la base de datos
- **Mapping:** es la union entre ambos
:::
:::info
ORM'S de Node
- **Sequelize:** Soporta MySql,PostgreSql, SqlServer y SqlLite
- **Mongoose:** Soporta Mongodb
:::
Instalar sequelize
```npm
npm install --save mysql2 sequelize
```
Conectar una base de datos en Node utilizando Sequelize,en el archivo de configuracion db.js
```javascript=1
const { Sequelize } = require('sequelize');
const db = new Sequelize('upTaskNode', 'root', 'root', {
host: 'localhost',
dialect: 'mysql',
port:3306,
define:{
timestamps:false
},
operatorsAliases: false,
pool:{
max:5,
min:0,
acquire:30000,
idle:10000
}
});
/**Exportamos para ser utilizado */
module.exports = db;
```
Crear el Modelo.js
```javascript=1
const Sequelize = require('sequelize');
/* Importar la configuracion de la base de datos*/
const db = require('../config/db');
/*Definir la estructura */
const Proyectos = db.define('proyectos',{
id:{
type: Sequelize.INTEGER,
primaryKey:true,
autoIncrement:true
},
nombre: Sequelize.STRING,
url: Sequelize.STRING
});
/* Exportar para utilizar en otros archivos*/
module.exports = Proyectos;
```
Agregar la configuracion y el modelo al index.js
```javascript=10
//importar el modelo
require('./models/Proyectos');
db.sync()
.then(()=>console.log('Conectado al servidor'))
.catch(error => console.log(error))
```
:::info
**bd.authenticate :** Solo se conecta al servidor
**db.sync :** Crea las tablas con el modelo previamente importado.
:::
:books: Sección 12 - Insertando Proyectos en la base de datos
---
En el controlador importar el modelo y usar el metodo create() de sequelize
```javascript=1
const Proyectos = require('../models/Proyectos');
exports.nuevoProyecto = async (req,res) =>{
const { nombre } = req.body;
//insertar en la base de datos
const propyecto = await Proyectos.create({nombre});
//redireccionar al home
res.redirect('/');
}
```
### Sanitizar entradas de datos
:::info
**Sanitizar:** Se utiliza para evitar que el usuario intente ingresar caracteres no validos, espacios en blanco entre otras cosas. por ejemplo que intente escribir <> ?? // <Script></Script>
:::
instalar express validator
```npm
npm install --save express-validator
```
Por lo general las vlidacion van en el router
```javascript=1
//importar express valitador
const { body } = require('express-validator/check');
module.exports = function (){
//agregar nuevo proyecto
router.post('/nuevo-proyecto',
body('nombre').not().isEmpty().trim().escape(),
proyectoController.nuevoProyecto);
return router;
}
```
### Generar Urls
Slug pasa de "Tienda Virtual" a "tienda-virtual" para ser agregada en las urls.
```npm
npm install --save slug
```
```javascript=1
const slug = require('slug');
const url = slug(nombre).toLowerCase();
const propyecto = await Proyectos.create({nombre,url});
```
Pueden haber dos datos con la misma url para solucionar esto se usan Hooks de Sequelize
:::info
**Hooks:** Los Hooks (también conocidos como eventos del ciclo de vida), son funciones que se llaman antes y después de que se ejecuten las llamadas en sequelize. Por ejemplo, si desea establecer siempre un valor en un modelo antes de guardarlo, puede agregar un enlace beforeUpdate.
[Hooks de Sequelize](https://sequelize.org/master/manual/hooks.html)
:::
Agregamos un Hook de sequelize en el Modelo,
para agregar un id unico al final de la url usamos ShorId
```node
npm install --save shortid
```
```javascript=1
const Sequelize = require('sequelize');
const slug = require('slug');
const shortid = require('shortid');
/* Importar la configuracion de la base de datos*/
const db = require('../config/db');
/*Definir la estructura */
const Proyectos = db.define('proyectos',{
id:{
type: Sequelize.INTEGER,
primaryKey:true,
autoIncrement:true
},
nombre: Sequelize.STRING,
url: Sequelize.STRING
},{
hooks:{
//se ejecuta antes de crean el dato
beforeCreate(proyecto){
const url = slug(proyecto.nombre).toLowerCase();
proyecto.url = `${url}-${shortid.generate()}`;
}
}
});
/* Exportar para utilizar en otros archivos*/
module.exports = Proyectos;
```
:books: Sección 13 - Mostrando Proyectos de la base de datos
---
En el controlador usar el metodo finall() para traer todos los datos de la base de datos. y pasar la informacion a la vista
```javascript=1
const Proyectos = require('../models/Proyectos');
exports.proyectosHome = async (req,res)=>{
const proyectos = await Proyectos.findAll();
res.render('index',{
nombrePagina:'Proyectos',
proyectos
});
}
```
En la vista iteramos el array **proyectos** que pasamos como parametro en el controlador
```pug=1
.panel.lista-proyectos
h2 Proyectos
ul#proyectos.proyectos
if(proyectos)
each proyecto in proyectos
li
a(href="#")= proyecto.nombre
block contenido
```
Ahora crearemos un **Helper** para ver los datos como un json ya que Node no lo trae.
Creamos un archivo **Helpers.js** con un metodo.(puede tener cuantos metodos queramos)
```javascript==1
exports.vardump = (objeto) => JSON.stringify(objeto,null,2);
```
ahora lo importamos en el index.js
```javascript==5
//helpers con algunas fuciones
const helpers = require('./helpers');
```
```javascript=33
//pasar vardump a la aplicacion
app.use((req,res,next) => {
//locals crear varables en este archivo y consumirlo en otro archivo
res.locals.vardump = helpers.vardump;
//siguiente
next();
});
```
:::info
**Midelware:** Un middleware es un bloque de código que se ejecuta entre la petición que hace el usuario (request) hasta que la petición llega al servidor.
:::
Ahora vamos a gregar los ruting para la navegacion y que tome la url unica que almacenamos y de acuerdo a eso muestre un proyecto y sus detalles.
en el archivo de routes agregar la direccion mas la url como comodin
```javascript=1
module.exports = function (){
//listar proyecto :url es el comodin
router.get('/proyectos/:url',proyectoController.proyectoPorUrl);
return router;
}
```
y en el controlador agregar el metodo **proyectoPorUrl**
```javascript=20
exports.proyectoPorUrl = async (req,res,next) => {
//buscar solo uno
const proyecto = await Proyectos.findOne({
//condicion
where:{
url:req.params.url
}
})
const proyectos = await Proyectos.findAll();
if(!proyecto) return next();
//render a la vista
res.render('tareas',{
nombrePagina:'tareas del proyecto',
proyecto,
proyectos
});
}
```
:books: Sección 14 - Editando el Proyecto Actual
---
Al dar click al boton editar se tiene que redireccionar a un formluario con los datos
**views/tareas.pug**
```pug=1
extends layout
block contenido
.contenido-principal
h1 #{nombrePagina} - #{proyecto.nombre}
//todo formulario
//listado de pendientes
//acciones
.contenedor-acciones
// pega el enponit para editar y le pasa el id
a(href=`/proyectos/editar/${proyecto.id}` class="boton") Editar Proyecto
button#eliminar-proyecto(type="button" class="boton eliminar") Eliminar Proyecto
```
y se agrega en las rutas
**routes/index.js**
```javascript=1
module.exports = function (){
//actualizar el proyecto
router.get('/proyectos/editar/:id',proyectoController.formularioEditar);
return router;
}
```
y en el controlaro de agrega el metodo formularioE
itar
**controllers/proyectoController.js**
```javascript=1
exports.formularioEditar = async (req,res) =>{
const { id } = req.params;
const proyectoPromise = Proyectos.findByPk(id);
const proyectosPromise = Proyectos.findAll();
// se utiliza promesas cuando un metodo no depende del otro
const [proyectos,proyecto] = await Promise.all([proyectosPromise,proyectoPromise]);
//utiliza la misma plantilla para agregar uno nuevo
res.render('nuevoProyecto',{
nombrePagina:'Editar Proyecto',
proyecto,
proyectos
})
}
```
Utilizamos Mixins para que en el mismo formulario de nuevo podamos editar y guardar.
Se crear una carpeta llamada mixins donde tiene un archivo nuevoProyecto
:::info
Los **Mixins** te permiten crear bloques reutilizables de Pug.
:::
**views/mixins/crearProyecto.pug**
```pug=
//recibe un objeto si no lo recibe lo deja en vacio
mixin crearProyecto(proyecto = {})
//condicional para el action si estamos editando o agregando uno nuevo
form.agregar-proyecto(action=`/nuevo-proyecto/${proyecto.id || ''}` method='POST')
.campo
label(for="nombre") Nombre Proyecto
input(value=proyecto.nombre type="text" id="nombre" name="nombre" placeholder="Nombre Proyecto")
.campo.eviar
//validar que nombre se mostrara en el boton
input(type="submit" value=`${proyecto.nombre ? 'Guardar':'Agregar'}` class="boton")
```
Se invoca en la plantilla
**views/nuevoProyecto.pug**
```pug=1
extends layout
//incluimos el archivo que creamos anteriormente
include mixins/crearProyecto
block contenido
.contenido-principal
h1 #{nombrePagina}
if errores
each error in errores
.alerta= error.texto
//utilizamos el mixins y pasamos el objeto
+crearProyecto(proyecto)
```
Agregamos la ruta del nuevo enpoint
**routes/index.js**
```javascript=1
module.exports = function (){
//editar y guardar
router.post('/nuevo-proyecto/:id',
body('nombre').not().isEmpty().trim().escape(),
proyectoController.actualizarProyecto);
return router;
}
```
luego agregamos en el controlador el nuevo metodo
**controller/proyectosController.js**
```javascript=1
exports.actualizarProyecto = async (req,res) => {
const { nombre} = req.body;
const { id } = req.params;
let errores = [];
const proyectos = await Proyectos.findAll();
if(!nombre){
errores.push({'texto':'agrega un nombre'});
}
if(errores.length > 0){
res.render('nuevoProyecto',{
nombrePagina:'Nuevo Proyecto',
errores,
proyectos
})
}else{
//buscamos y actualizamos en la base de datos
await Proyectos.update(
{nombre:nombre},
{where: {id:id} }
);
res.redirect('/');
}
}
```
:books: Sección 15 - Eliminando el Proyecto Actual
---
```npm
npm install --save-dev @babel/core babel-loader @babel/preset-env webpack
```
**concurrently** permite ejecutar diferentes scripts al mismo tiempo
```npm
npm install --save concurrently
```
Instalar **Axios** y **Sweetalert2**
```npm
npm install --save axios sweetalert2
```
:books: Sección 16 - Agregando Tareas en los Proyectos
---
1) Crear el Modelo de tareas.
**models/tareas.js**
```javascript=1
const Sequelize = require('sequelize');
const db = require('../config/db');
const Tareas = db.define('tareas',{
id:{
type:Sequelize.INTEGER(11),
primaryKey:true,
autoIncrement:true
},
tarea: Sequelize.STRING(100),
estado: Sequelize.INTEGER(1)
});
/*cada tarea pertenece a un proyecto
una o muchas tarea pertenece a un proyecto
*/
Tareas.belongsTo(Proyectos);
module.exports = Tareas;
```
2) Agregar el modelo en el index para que se genere las tablas
**index.js**
```javascript=20
//importar el modelo
require('./models/Proyectos');
require('./models/Tareas');
```
:books: Sprint Backlog
---
- Email invite feature
- Interview users
:mag: Sprint Retro
---
### What we can start Doing
- New initiatives and experiments we want to start improving
:closed_book: Tasks
--
==Importance== (1 - 5) / Name / **Estimate** (1, 2, 3, 5, 8, 13)
### Development Team:
- [ ] ==5== Email invite
- [x] ==4== Email registration page **5**
- [ ] ==5== Email invitees **3**
- [ ] ==4== Setup e2e test in production **2**
### Design Team:
- [ ] ==4== Interview users **8**
- [ ] ==5== Build roll-up display content **5**
- [ ] ==5== Help user discover new features **5**
## Notes
<!-- Other important details discussed during the meeting can be entered here. -->