feat: Agrega módulo de MySQL.

Completa CRUD de usuarios.
This commit is contained in:
Ricardo García Jiménez 2022-08-06 01:48:35 -05:00
parent b9f1d0a0bc
commit d9e6f992cf
14 changed files with 378 additions and 28 deletions

1
app.js
View File

@ -11,6 +11,7 @@ app.set('view engine', 'ejs')
/**
* Middlewares.
*/
app.use(express.urlencoded({ extended: false }))
app.use(express.static(path.join(__dirname, 'public')))
/**

9
config.js Normal file
View File

@ -0,0 +1,9 @@
module.exports = {
mysql: {
host: 'localhost',
user: 'root',
password: 'root',
database: 'nodejs',
charset: 'utf8mb4_spanish_ci'
}
}

13
connection.js Normal file
View File

@ -0,0 +1,13 @@
const mysql = require('mysql')
const connection = mysql.createConnection(require('./config').mysql)
connection.connect((err) => {
if (err) {
console.error('error connecting: ' + err.sqlMessage)
return
}
console.log('connected as id ' + connection.threadId)
})
module.exports = connection

View File

@ -1,29 +1,92 @@
const conn = require('../connection')
module.exports = {
/**
* Vista para crear un nuevo usuario.
*/
new (req, res) {
res.render('users/new', { title: 'Nuevo usuario' })
res.render('users/new', { title: 'Crear usuario' })
},
/**
* Guarda un nuevo usuario.
*/
create (req, res) {
res.send('Crear usuario')
const query = 'INSERT INTO users (name, age) VALUES (?, ?)'
conn.query(query, [req.body.name, req.body.age], (err, results, fields) => {
if (err) {
console.error(err.sqlMessage)
throw err
}
})
res.redirect('/users')
},
/**
* Lista de usuarios.
*/
index (req, res) {
res.render('users/index', { title: 'Lista de usuarios' })
},
show (req, res) {
res.render('users/show', { title: `Mostrar usuario con ID: ${req.params.id}` })
const query = 'SELECT * FROM users ORDER BY id ASC'
conn.query(query, (err, results, fields) => {
if (err) {
console.error(err.sqlMessage)
throw err
}
res.render('users/index', { title: 'Lista de usuarios', users: results })
})
},
/**
* Vista para editar un usuario.
*/
edit (req, res) {
res.render('users/edit', { title: `Editar usuario con ID: ${req.params.id}` })
const query = 'SELECT * FROM users WHERE id = ? LIMIT 1'
conn.query(query, [req.params.id], (err, results, fields) => {
if (err) {
console.error(err.sqlMessage)
throw err
}
res.render('users/edit', { title: results[0].name, user: results[0] })
})
},
/**
* Actualiza la información de un usuario.
*/
update (req, res) {
res.send(`Actualizar usuario con ID: ${req.params.id}`)
const id = req.params.id
const query = 'UPDATE users SET name = ?, age = ? WHERE id = ?'
conn.query(query, [req.body.name, req.body.age, req.params.id], (err, results, fields) => {
if (err) {
console.error(err.sqlMessage)
throw err
}
})
res.redirect(`/users/${id}/edit`)
},
/**
* Elimina un usuario.
*/
delete (req, res) {
res.send(`Eliminar usuario con ID: ${req.params.id}`)
const query = 'DELETE FROM users WHERE id = ?'
conn.query(query, [req.params.id], (err, results, fields) => {
if (err) {
console.error(err.sqlMessage)
throw err
}
})
res.redirect('/users')
}
}

13
db.sql Normal file
View File

@ -0,0 +1,13 @@
CREATE DATABASE IF NOT EXISTS nodejs
CHARACTER SET = 'utf8mb4'
COLLATE = 'utf8mb4_spanish_ci';
use nodejs;
CREATE TABLE IF NOT EXISTS users (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(64) NOT NULL,
age TINYINT UNSIGNED NOT NULL,
CONSTRAINT users_id_pk PRIMARY KEY (id),
CONSTRAINT users_name_uk UNIQUE (name)
);

174
package-lock.json generated
View File

@ -10,7 +10,8 @@
"license": "CC0-1.0",
"dependencies": {
"ejs": "^3.1.8",
"express": "^4.18.1"
"express": "^4.18.1",
"mysql": "^2.18.1"
},
"devDependencies": {
"nodemon": "^2.0.19"
@ -76,6 +77,14 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/bignumber.js": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==",
"engines": {
"node": "*"
}
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@ -276,6 +285,11 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"node_modules/debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
@ -643,6 +657,11 @@
"node": ">=0.12.0"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
"node_modules/jake": {
"version": "10.8.5",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz",
@ -727,6 +746,25 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/mysql": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
"dependencies": {
"bignumber.js": "9.0.0",
"readable-stream": "2.3.7",
"safe-buffer": "5.1.2",
"sqlstring": "2.3.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mysql/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@ -832,6 +870,11 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@ -886,6 +929,25 @@
"node": ">= 0.8"
}
},
"node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/readable-stream/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@ -1020,6 +1082,14 @@
"semver": "bin/semver.js"
}
},
"node_modules/sqlstring": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
"integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@ -1028,6 +1098,19 @@
"node": ">= 0.8"
}
},
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/string_decoder/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@ -1098,6 +1181,11 @@
"node": ">= 0.8"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@ -1164,6 +1252,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"bignumber.js": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A=="
},
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@ -1317,6 +1410,11 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
"core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
@ -1607,6 +1705,11 @@
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
"jake": {
"version": "10.8.5",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz",
@ -1664,6 +1767,24 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"mysql": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
"requires": {
"bignumber.js": "9.0.0",
"readable-stream": "2.3.7",
"safe-buffer": "5.1.2",
"sqlstring": "2.3.1"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@ -1731,6 +1852,11 @@
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@ -1770,6 +1896,27 @@
"unpipe": "1.0.0"
}
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@ -1875,11 +2022,31 @@
}
}
},
"sqlstring": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
"integrity": "sha512-ooAzh/7dxIG5+uDik1z/Rd1vli0+38izZhGzSa34FwR7IbelPWCCKSNIl8jlL/F7ERvy8CB2jNeM1E9i9mXMAQ=="
},
"statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@ -1932,6 +2099,11 @@
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",

View File

@ -14,6 +14,7 @@
},
"dependencies": {
"ejs": "^3.1.8",
"express": "^4.18.1"
"express": "^4.18.1",
"mysql": "^2.18.1"
}
}

View File

@ -0,0 +1,27 @@
table {
margin: 0 auto;
}
td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.btn-update {
background-color: green;
border: none;
color: white;
padding: 10px 15px;
text-align: center;
text-decoration: none;
}
.btn-delete {
background-color: red;
border: none;
color: white;
padding: 10px 15px;
text-align: center;
text-decoration: none;
}

View File

@ -5,9 +5,8 @@ const userController = require('../controllers/users')
router.get('/new', userController.new)
router.post('/', userController.create)
router.get('/', userController.index)
router.get('/:id', userController.show)
router.get('/:id/edit', userController.edit)
router.put('/:id', userController.update)
router.delete('/:id', userController.delete)
router.post('/:id', userController.update)
router.get('/:id', userController.delete)
module.exports = router

View File

@ -1,7 +1,7 @@
<%- include('../layouts/header', { title }); -%>
<%- include('../layouts/header', { title }) -%>
<h1><%= title %></h1>
<script src="/js/index.js"></script>
<%- include('../layouts/footer'); -%>
<%- include('../layouts/footer') -%>

View File

@ -1,5 +1,21 @@
<%- include('../layouts/header', { title }); -%>
<%- include('../layouts/header', { title }) -%>
<h1><%= title %></h1>
<%- include('../layouts/footer'); -%>
<a href="/users">Regresar</a>
<form method="post" action="/users/<%= user.id %>">
<div>
<label>Name:
<input type="text" name="name" value="<%= user.name %>" required>
</label>
</div>
<div>
<label>Age:
<input type="number" name="age" value="<%= user.age %>" required>
</label>
</div>
<input type="submit" value="Save">
</form>
<%- include('../layouts/footer') -%>

View File

@ -1,5 +1,30 @@
<%- include('../layouts/header', { title }); -%>
<%- include('../layouts/header', { title }) -%>
<h1><%= title %></h1>
<%- include('../layouts/footer'); -%>
<a href="/users/new">Crear usuario</a>
<table>
<thead>
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Edad</th>
<th>Modificar</th>
<th>Eliminar</th>
</tr>
</thead>
<tbody>
<% users.forEach(user => { %>
<tr>
<td><%= user.id %></td>
<td><%= user.name %></td>
<td><%= user.age %></td>
<td><a class="btn-update" href="/users/<%= user.id %>/edit">Modificar</a></td>
<td><a class="btn-delete" href="/users/<%= user.id %>">Eliminar</a></td>
</tr>
<% }) %>
</tbody>
</table>
<%- include('../layouts/footer') -%>

View File

@ -1,5 +1,21 @@
<%- include('../layouts/header', { title }); -%>
<%- include('../layouts/header', { title }) -%>
<h1><%= title %></h1>
<%- include('../layouts/footer'); -%>
<a href="/users">Regresar</a>
<form method="post" action="/users">
<div>
<label>Name:
<input type="text" name="name" required>
</label>
</div>
<div>
<label>Age:
<input type="number" name="age" required>
</label>
</div>
<input type="submit" value="Save">
</form>
<%- include('../layouts/footer') -%>

View File

@ -1,5 +0,0 @@
<%- include('../layouts/header', { title }); -%>
<h1><%= title %></h1>
<%- include('../layouts/footer'); -%>