Relação entre Entidades
Arquivos
invest-app-prismajs-relation
├── package-lock.json
├── package.json
├── prisma
│ ├── dev.db
│ ├── migrations
│ │ ├── 20230826214323_init
│ │ │ └── migration.sql
│ │ ├── 20241011213516_create_category_broker
│ │ │ └── migration.sql
│ │ └── migration_lock.toml
│ ├── schema.prisma
│ ├── seed.js
│ └── seeders.json
├── public
│ ├── css
│ │ └── style.css
│ ├── index.html
│ └── js
│ ├── index.js
│ ├── lib
│ │ └── format.js
│ └── services
│ └── api.js
├── requests.http
└── src
├── database
│ └── database.js
├── index.js
├── models
│ ├── Category.js
│ └── Investment.js
└── routes.js
Arquivos
invest-app-prismajs-relation
├── package-lock.json
├── package.json
├── prisma
│ ├── dev.db
│ ├── migrations
│ │ ├── 20230826214323_init
│ │ │ └── migration.sql
│ │ ├── 20241011213516_create_category_broker
│ │ │ └── migration.sql
│ │ └── migration_lock.toml
│ ├── schema.prisma
│ ├── seed.js
│ └── seeders.json
├── public
│ ├── css
│ │ └── style.css
│ ├── index.html
│ └── js
│ ├── index.js
│ ├── lib
│ │ └── format.js
│ └── services
│ └── api.js
├── requests.http
└── src
├── database
│ └── database.js
├── index.js
├── models
│ ├── Category.js
│ └── Investment.js
└── routes.js
Migration
/codes/expressjs/invest-app-prismajs-relation/prisma/schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model Investment {
id String @id @default(uuid())
name String
value Int
interest String
createdAt DateTime @default(now())
category Category @relation(fields: [categoryId], references: [id])
categoryId String
broker Broker @relation(fields: [brokerId], references: [id])
brokerId String
}
model Category {
id String @id @default(uuid())
name String @unique
color String @unique
investments Investment[]
}
model Broker {
id String @id @default(uuid())
name String @unique
investments Investment[]
}
/codes/expressjs/invest-app-prismajs-relation/prisma/schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model Investment {
id String @id @default(uuid())
name String
value Int
interest String
createdAt DateTime @default(now())
category Category @relation(fields: [categoryId], references: [id])
categoryId String
broker Broker @relation(fields: [brokerId], references: [id])
brokerId String
}
model Category {
id String @id @default(uuid())
name String @unique
color String @unique
investments Investment[]
}
model Broker {
id String @id @default(uuid())
name String @unique
investments Investment[]
}
$ npx prisma migrate reset
$ npx prisma migrate dev --name create_category_broker
$ npx prisma migrate reset
$ npx prisma migrate dev --name create_category_broker
Tabelas:
Seed
/codes/expressjs/invest-app-prismajs-relation/prisma/seed.js
import { resolve } from 'node:path';
import { readFileSync } from 'node:fs';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
const file = resolve('prisma', 'seeders.json');
const seed = JSON.parse(readFileSync(file));
await prisma.category.createMany({
data: seed.categories,
});
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
/codes/expressjs/invest-app-prismajs-relation/prisma/seed.js
import { resolve } from 'node:path';
import { readFileSync } from 'node:fs';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
const file = resolve('prisma', 'seeders.json');
const seed = JSON.parse(readFileSync(file));
await prisma.category.createMany({
data: seed.categories,
});
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
/codes/expressjs/invest-app-prismajs-relation/prisma/seeders.json
{
"categories": [
{
"name": "Pós",
"color": "#6366f1"
},
{
"name": "Pré",
"color": "#d946ef"
},
{
"name": "IPCA",
"color": "#f43f5e"
},
{
"name": "Renda Variável",
"color": "#eab308"
},
{
"name": "Fundo de Investimento",
"color": "#46efb1"
},
{
"name": "Outros",
"color": "#111111"
}
]
}
/codes/expressjs/invest-app-prismajs-relation/prisma/seeders.json
{
"categories": [
{
"name": "Pós",
"color": "#6366f1"
},
{
"name": "Pré",
"color": "#d946ef"
},
{
"name": "IPCA",
"color": "#f43f5e"
},
{
"name": "Renda Variável",
"color": "#eab308"
},
{
"name": "Fundo de Investimento",
"color": "#46efb1"
},
{
"name": "Outros",
"color": "#111111"
}
]
}
$ npx prisma migrate reset
$ npx prisma db seed
$ npx prisma studio
$ npx prisma migrate reset
$ npx prisma db seed
$ npx prisma studio
Model
/codes/expressjs/invest-app-prismajs-relation/src/models/Category.js
import prisma from '../database/database.js';
async function create(category) {
const newCategory = await prisma.category.create({
data: category,
});
return newCategory;
}
async function read(where) {
const categories = await prisma.category.findMany({
where,
});
if (categories.length === 1 && where) {
return categories[0];
}
return categories;
}
async function readById(id) {
const category = await prisma.category.findUnique({
where: {
id,
},
});
return category;
}
export default { create, read, readById };
/codes/expressjs/invest-app-prismajs-relation/src/models/Category.js
import prisma from '../database/database.js';
async function create(category) {
const newCategory = await prisma.category.create({
data: category,
});
return newCategory;
}
async function read(where) {
const categories = await prisma.category.findMany({
where,
});
if (categories.length === 1 && where) {
return categories[0];
}
return categories;
}
async function readById(id) {
const category = await prisma.category.findUnique({
where: {
id,
},
});
return category;
}
export default { create, read, readById };
/codes/expressjs/invest-app-prismajs-relation/src/models/Investment.js
import prisma from '../database/database.js';
async function create({
name,
value,
interest,
createdAt,
broker,
categoryId,
}) {
if (name && value && interest && createdAt && broker && categoryId) {
const createdInvestment = await prisma.investment.create({
data: {
name,
value,
interest,
createdAt,
category: {
connect: {
id: categoryId,
},
},
broker: {
connectOrCreate: {
where: {
name: broker,
},
create: {
name: broker,
},
},
},
},
include: {
category: true,
broker: true,
},
});
return createdInvestment;
} else {
throw new Error('Unable to create investment');
}
}
async function read(where) {
if (where?.name) {
where.name = {
contains: where.name,
};
}
const investments = await prisma.investment.findMany({
where,
include: {
category: true,
broker: true,
},
});
if (investments.length === 1 && where) {
return investments[0];
}
return investments;
}
async function readById(id) {
if (id) {
const investment = await prisma.investment.findUnique({
where: {
id,
},
include: {
category: true,
broker: true,
},
});
return investment;
} else {
throw new Error('Unable to find investment');
}
}
async function update({
id,
name,
value,
interest,
createdAt,
broker,
categoryId,
}) {
if (name && value && id && categoryId && broker && interest && createdAt) {
const updatedInvestment = await prisma.investment.update({
where: {
id,
},
data: {
name,
value,
interest,
createdAt,
category: {
connect: {
id: categoryId,
},
},
broker: {
connectOrCreate: {
where: {
name: broker,
},
create: {
name: broker,
},
},
},
},
include: {
category: true,
broker: true,
},
});
return updatedInvestment;
} else {
throw new Error('Unable to update investment');
}
}
async function remove(id) {
if (id) {
await prisma.investment.delete({
where: {
id,
},
});
return true;
} else {
throw new Error('Unable to remove investment');
}
}
export default { create, read, readById, update, remove };
/codes/expressjs/invest-app-prismajs-relation/src/models/Investment.js
import prisma from '../database/database.js';
async function create({
name,
value,
interest,
createdAt,
broker,
categoryId,
}) {
if (name && value && interest && createdAt && broker && categoryId) {
const createdInvestment = await prisma.investment.create({
data: {
name,
value,
interest,
createdAt,
category: {
connect: {
id: categoryId,
},
},
broker: {
connectOrCreate: {
where: {
name: broker,
},
create: {
name: broker,
},
},
},
},
include: {
category: true,
broker: true,
},
});
return createdInvestment;
} else {
throw new Error('Unable to create investment');
}
}
async function read(where) {
if (where?.name) {
where.name = {
contains: where.name,
};
}
const investments = await prisma.investment.findMany({
where,
include: {
category: true,
broker: true,
},
});
if (investments.length === 1 && where) {
return investments[0];
}
return investments;
}
async function readById(id) {
if (id) {
const investment = await prisma.investment.findUnique({
where: {
id,
},
include: {
category: true,
broker: true,
},
});
return investment;
} else {
throw new Error('Unable to find investment');
}
}
async function update({
id,
name,
value,
interest,
createdAt,
broker,
categoryId,
}) {
if (name && value && id && categoryId && broker && interest && createdAt) {
const updatedInvestment = await prisma.investment.update({
where: {
id,
},
data: {
name,
value,
interest,
createdAt,
category: {
connect: {
id: categoryId,
},
},
broker: {
connectOrCreate: {
where: {
name: broker,
},
create: {
name: broker,
},
},
},
},
include: {
category: true,
broker: true,
},
});
return updatedInvestment;
} else {
throw new Error('Unable to update investment');
}
}
async function remove(id) {
if (id) {
await prisma.investment.delete({
where: {
id,
},
});
return true;
} else {
throw new Error('Unable to remove investment');
}
}
export default { create, read, readById, update, remove };
Router
/codes/expressjs/invest-app-prismajs-relation/src/routes.js
import express from 'express';
import Category from './models/Category.js';
import Investment from './models/Investment.js';
class HTTPError extends Error {
constructor(message, code) {
super(message);
this.code = code;
}
}
const router = express.Router();
router.post('/investments', async (req, res) => {
try {
const investment = req.body;
if (investment.createdAt) {
investment.createdAt = new Date(
investment.createdAt + 'T00:00:00-03:00'
).toISOString();
}
const createdInvestment = await Investment.create(investment);
return res.json(createdInvestment);
} catch (error) {
throw new HTTPError('Unable to create investment', 400);
}
});
router.get('/investments', async (req, res) => {
try {
const { name } = req.query;
let investments;
if (name) {
investments = await Investment.read({ name });
} else {
investments = await Investment.read();
}
return res.json(investments);
} catch (error) {
throw new HTTPError('Unable to read investments', 400);
}
});
router.get('/investments/:id', async (req, res) => {
try {
const id = req.params.id;
const investment = await Investment.readById(id);
return res.json(investment);
} catch (error) {
throw new HTTPError('Unable to find investment', 400);
}
});
router.put('/investments/:id', async (req, res) => {
try {
const id = req.params.id;
const investment = req.body;
if (investment.createdAt) {
investment.createdAt = new Date(
investment.createdAt + 'T00:00:00-03:00'
).toISOString();
}
const updatedInvestment = await Investment.update({ ...investment, id });
return res.json(updatedInvestment);
} catch (error) {
throw new HTTPError('Unable to update investment', 400);
}
});
router.delete('/investments/:id', async (req, res) => {
try {
const id = req.params.id;
if (await Investment.remove(id)) {
return res.sendStatus(204);
} else {
throw new Error();
}
} catch (error) {
throw new HTTPError('Unable to remove investment', 400);
}
});
router.get('/categories', async (req, res) => {
try {
const { name } = req.query;
let categories;
if (name) {
categories = await Category.read({ name });
} else {
categories = await Category.read();
}
return res.json(categories);
} catch (error) {
throw new HTTPError('Unable to read investments', 400);
}
});
// 404 handler
router.use((req, res, next) => {
return res.status(404).json({ message: 'Content not found!' });
});
// Error handler
router.use((err, req, res, next) => {
// console.error(err.stack);
if (err instanceof HTTPError) {
return res.status(err.code).json({ message: err.message });
} else {
return res.status(500).json({ message: 'Something broke!' });
}
});
export default router;
/codes/expressjs/invest-app-prismajs-relation/src/routes.js
import express from 'express';
import Category from './models/Category.js';
import Investment from './models/Investment.js';
class HTTPError extends Error {
constructor(message, code) {
super(message);
this.code = code;
}
}
const router = express.Router();
router.post('/investments', async (req, res) => {
try {
const investment = req.body;
if (investment.createdAt) {
investment.createdAt = new Date(
investment.createdAt + 'T00:00:00-03:00'
).toISOString();
}
const createdInvestment = await Investment.create(investment);
return res.json(createdInvestment);
} catch (error) {
throw new HTTPError('Unable to create investment', 400);
}
});
router.get('/investments', async (req, res) => {
try {
const { name } = req.query;
let investments;
if (name) {
investments = await Investment.read({ name });
} else {
investments = await Investment.read();
}
return res.json(investments);
} catch (error) {
throw new HTTPError('Unable to read investments', 400);
}
});
router.get('/investments/:id', async (req, res) => {
try {
const id = req.params.id;
const investment = await Investment.readById(id);
return res.json(investment);
} catch (error) {
throw new HTTPError('Unable to find investment', 400);
}
});
router.put('/investments/:id', async (req, res) => {
try {
const id = req.params.id;
const investment = req.body;
if (investment.createdAt) {
investment.createdAt = new Date(
investment.createdAt + 'T00:00:00-03:00'
).toISOString();
}
const updatedInvestment = await Investment.update({ ...investment, id });
return res.json(updatedInvestment);
} catch (error) {
throw new HTTPError('Unable to update investment', 400);
}
});
router.delete('/investments/:id', async (req, res) => {
try {
const id = req.params.id;
if (await Investment.remove(id)) {
return res.sendStatus(204);
} else {
throw new Error();
}
} catch (error) {
throw new HTTPError('Unable to remove investment', 400);
}
});
router.get('/categories', async (req, res) => {
try {
const { name } = req.query;
let categories;
if (name) {
categories = await Category.read({ name });
} else {
categories = await Category.read();
}
return res.json(categories);
} catch (error) {
throw new HTTPError('Unable to read investments', 400);
}
});
// 404 handler
router.use((req, res, next) => {
return res.status(404).json({ message: 'Content not found!' });
});
// Error handler
router.use((err, req, res, next) => {
// console.error(err.stack);
if (err instanceof HTTPError) {
return res.status(err.code).json({ message: err.message });
} else {
return res.status(500).json({ message: 'Something broke!' });
}
});
export default router;
/codes/expressjs/invest-app-prismajs-relation/requests.http
@host = http://localhost:3000/api
@createdInvestmentId = {{createdInvestment.response.body.$.id}}
### Read Categories
GET {{host}}/categories
### Read Categories by Name
# @name categoryPos
GET {{host}}/categories?name=Pós
### Read Categories by Name
# @name categoryIpca
GET {{host}}/categories?name=IPCA
### Create Investment
@categoryIpcaId = {{categoryIpca.response.body.$.id}}
# @name createdInvestment
POST {{host}}/investments
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 10000,
"interest": "100% Selic",
"createdAt": "2023-09-01",
"broker": "Banco Inter",
"categoryId": "{{categoryIpcaId}}"
}
### Read Investments
GET {{host}}/investments
### Read Investments by Name
GET {{host}}/investments?name=Tesouro
### Update Investment
@categoryPosId = {{categoryPos.response.body.$.id}}
PUT {{host}}/investments/{{createdInvestmentId}}
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 20000,
"interest": "100% Selic",
"createdAt": "2023-09-01",
"broker": "Banco Inter",
"categoryId": "{{categoryPosId}}"
}
### Delete Investment
DELETE {{host}}/investments/{{createdInvestmentId}}
/codes/expressjs/invest-app-prismajs-relation/requests.http
@host = http://localhost:3000/api
@createdInvestmentId = {{createdInvestment.response.body.$.id}}
### Read Categories
GET {{host}}/categories
### Read Categories by Name
# @name categoryPos
GET {{host}}/categories?name=Pós
### Read Categories by Name
# @name categoryIpca
GET {{host}}/categories?name=IPCA
### Create Investment
@categoryIpcaId = {{categoryIpca.response.body.$.id}}
# @name createdInvestment
POST {{host}}/investments
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 10000,
"interest": "100% Selic",
"createdAt": "2023-09-01",
"broker": "Banco Inter",
"categoryId": "{{categoryIpcaId}}"
}
### Read Investments
GET {{host}}/investments
### Read Investments by Name
GET {{host}}/investments?name=Tesouro
### Update Investment
@categoryPosId = {{categoryPos.response.body.$.id}}
PUT {{host}}/investments/{{createdInvestmentId}}
Content-Type: application/json
{
"name": "Tesouro Selic 2029",
"value": 20000,
"interest": "100% Selic",
"createdAt": "2023-09-01",
"broker": "Banco Inter",
"categoryId": "{{categoryPosId}}"
}
### Delete Investment
DELETE {{host}}/investments/{{createdInvestmentId}}
View
/codes/expressjs/invest-app-prismajs-relation/public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Invest App</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="css/style.css" />
<script src="https://code.iconify.design/3/3.1.0/iconify.min.js"></script>
</head>
<body class="pb-5">
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container">
<a class="navbar-brand" href="#">Invest App</a>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container pb-5">
<h1 class="text-center my-5">Investimentos</h1>
<div class="investments">
<p class="text-center">Nenhum investimento cadastrado.</p>
<div
id="investment-grid"
class="row row-cols-1 row-cols-md-3 g-4"
></div>
</div>
</div>
<div>
<button
class="btn btn-secondary create-investment"
style="position: fixed; bottom: 24px; right: 24px"
type="button"
data-bs-toggle="offcanvas"
data-bs-target="#offcanvasRight"
aria-controls="offcanvasRight"
>
+
</button>
</div>
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasRight"
aria-labelledby="offcanvasRightLabel"
>
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="offcanvasRightLabel">
Cadastro de Investimento
</h5>
<button
type="button"
id="offcanvas-close"
class="btn-close"
data-bs-dismiss="offcanvas"
aria-label="Close"
></button>
</div>
<div class="offcanvas-body">
<form>
<div class="mb-3">
<label for="name" class="form-label">Nome</label>
<input
type="name"
class="form-control"
id="name"
name="name"
placeholder="Ex: Tesouro Selic"
/>
</div>
<div class="mb-3">
<label for="value" class="form-label">Valor</label>
<input
type="number"
class="form-control"
id="value"
name="value"
step="0.01"
placeholder="100"
/>
</div>
<div class="mb-3">
<label for="interest" class="form-label">Taxa</label>
<input
type="text"
class="form-control"
id="interest"
name="interest"
placeholder="100% Selic"
/>
</div>
<div class="mb-3">
<label for="categoryId" class="form-label">Categoria</label>
<select
class="form-control"
id="categoryId"
name="categoryId"
></select>
</div>
<div class="mb-3">
<label for="broker" class="form-label">Corretora</label>
<input
type="text"
class="form-control"
id="broker"
name="broker"
placeholder="Corretora"
/>
</div>
<div class="mb-3">
<label for="createdAt" class="form-label">Data</label>
<input
type="date"
class="form-control"
id="createdAt"
name="createdAt"
/>
</div>
<div class="mb-3">
<input
type="submit"
class="form-control btn btn-secondary"
id="submit"
placeholder="100"
/>
</div>
</form>
</div>
</div>
<div class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Remover Investimento</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<p>Deseja remover o investimento?</p>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
Fechar
</button>
<button type="button" class="btn btn-primary">Confirmar</button>
</div>
</div>
</div>
</div>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN"
crossorigin="anonymous"
></script>
<script src="./js/index.js" type="module"></script>
</body>
</html>
/codes/expressjs/invest-app-prismajs-relation/public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Invest App</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="css/style.css" />
<script src="https://code.iconify.design/3/3.1.0/iconify.min.js"></script>
</head>
<body class="pb-5">
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container">
<a class="navbar-brand" href="#">Invest App</a>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container pb-5">
<h1 class="text-center my-5">Investimentos</h1>
<div class="investments">
<p class="text-center">Nenhum investimento cadastrado.</p>
<div
id="investment-grid"
class="row row-cols-1 row-cols-md-3 g-4"
></div>
</div>
</div>
<div>
<button
class="btn btn-secondary create-investment"
style="position: fixed; bottom: 24px; right: 24px"
type="button"
data-bs-toggle="offcanvas"
data-bs-target="#offcanvasRight"
aria-controls="offcanvasRight"
>
+
</button>
</div>
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasRight"
aria-labelledby="offcanvasRightLabel"
>
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="offcanvasRightLabel">
Cadastro de Investimento
</h5>
<button
type="button"
id="offcanvas-close"
class="btn-close"
data-bs-dismiss="offcanvas"
aria-label="Close"
></button>
</div>
<div class="offcanvas-body">
<form>
<div class="mb-3">
<label for="name" class="form-label">Nome</label>
<input
type="name"
class="form-control"
id="name"
name="name"
placeholder="Ex: Tesouro Selic"
/>
</div>
<div class="mb-3">
<label for="value" class="form-label">Valor</label>
<input
type="number"
class="form-control"
id="value"
name="value"
step="0.01"
placeholder="100"
/>
</div>
<div class="mb-3">
<label for="interest" class="form-label">Taxa</label>
<input
type="text"
class="form-control"
id="interest"
name="interest"
placeholder="100% Selic"
/>
</div>
<div class="mb-3">
<label for="categoryId" class="form-label">Categoria</label>
<select
class="form-control"
id="categoryId"
name="categoryId"
></select>
</div>
<div class="mb-3">
<label for="broker" class="form-label">Corretora</label>
<input
type="text"
class="form-control"
id="broker"
name="broker"
placeholder="Corretora"
/>
</div>
<div class="mb-3">
<label for="createdAt" class="form-label">Data</label>
<input
type="date"
class="form-control"
id="createdAt"
name="createdAt"
/>
</div>
<div class="mb-3">
<input
type="submit"
class="form-control btn btn-secondary"
id="submit"
placeholder="100"
/>
</div>
</form>
</div>
</div>
<div class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Remover Investimento</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<p>Deseja remover o investimento?</p>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
Fechar
</button>
<button type="button" class="btn btn-primary">Confirmar</button>
</div>
</div>
</div>
</div>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN"
crossorigin="anonymous"
></script>
<script src="./js/index.js" type="module"></script>
</body>
</html>
/codes/expressjs/invest-app-prismajs-relation/public/js/index.js
import API from './services/api.js';
import { formatCurrency, formatDate } from './lib/format.js';
let removedHostId;
const form = document.querySelector('form');
const bsOffcanvas = new bootstrap.Offcanvas('.offcanvas');
const confirmModal = new bootstrap.Modal('.modal');
function InvestmentCard(investment) {
return `<div class="col" id="investment-${investment.id}">
<div class="card">
<div class="card-header">
<span class="investment-name">
${investment.name}
</span>
<span class="float-end">
<span class="icon-trash" >
<span
class="iconify"
data-icon="solar:trash-bin-minimalistic-broken"
>
</span>
</span>
<span class="icon-pencil">
<span
class="iconify"
data-icon="tabler:pencil"
>
</span>
</span>
</span>
</div>
<div class="card-body">
<div>
<span class="fw-bold">Valor:</span>
<span class="investment-value">
${formatCurrency(investment.value / 100)}
</span>
</div>
<div>
<span class="fw-bold">Taxa:</span>
<span class="investment-interest">
${investment.interest}
</span>
</div>
<div>
<span class="fw-bold">Data:</span>
<span class="investment-created-at">
${formatDate(investment.createdAt)}
</span>
</div>
<div>
<span class="fw-bold">Corretora:</span>
<span class="investment-broker">
${investment.broker.name}
</span>
</div>
<div>
<span class="fw-bold">Categoria:</span>
<span
class="badge investment-category"
style="background-color: ${investment.category.color}"
>
${investment.category.name}
</span>
</div>
</div>
</div>
</div>`;
}
function createInvestmentCard(investment) {
document.querySelector('.investments p').style.display = 'none';
const investmentContainer = document.querySelector(`#investment-grid`);
investmentContainer.insertAdjacentHTML(
'beforeend',
InvestmentCard(investment)
);
loadHandleConfirmModal(investment.id);
loadHandleUpdateInvestment(investment.id);
}
async function loadInvestmentCards() {
const investments = await API.read('/investments');
for (const investment of investments) {
createInvestmentCard(investment);
}
}
function updateInvestmentCard({
id,
name,
value,
createdAt,
category,
broker,
interest,
}) {
document.querySelector(`#investment-${id} .investment-name`).innerText = name;
document.querySelector(`#investment-${id} .investment-value`).innerText =
formatCurrency(value / 100);
document.querySelector(`#investment-${id} .investment-interest`).innerText =
interest;
document.querySelector(`#investment-${id} .investment-broker`).innerText =
broker.name;
document.querySelector(`#investment-${id} .investment-created-at`).innerText =
createdAt;
document.querySelector(
`#investment-${id} .investment-category`
).style.backgroundColor = category.color;
document.querySelector(`#investment-${id} .investment-category`).innerText =
category.name;
}
function loadHandleFormSubmit(type, id) {
form.onsubmit = async (event) => {
event.preventDefault();
const investment = Object.fromEntries(new FormData(form));
investment.value = Number(investment.value) * 100;
if (type === 'create') {
const createdInvestment = await API.create('/investments', investment);
createInvestmentCard(createdInvestment);
} else if (type === 'update') {
const updatedInvestment = await API.update(
`/investments/${id}`,
investment
);
updateInvestmentCard(updatedInvestment);
}
form.reset();
document.querySelector('#offcanvas-close').click();
};
}
function loadHandleCreateInvestment() {
const button = document.querySelector('.btn.create-investment');
button.onclick = () => {
form.reset();
bsOffcanvas.show();
loadHandleFormSubmit('create');
};
}
function loadHandleUpdateInvestment(id) {
const iconPencil = document.querySelector(`#investment-${id} .icon-pencil`);
iconPencil.onclick = async () => {
const investment = await API.read(`/investments/${id}`);
const { name, value, interest, createdAt, categoryId, broker } = investment;
document.querySelector('form #name').value = name;
document.querySelector('form #value').value = value / 100;
document.querySelector('form #interest').value = interest;
document.querySelector('form #categoryId').value = categoryId;
document.querySelector('form #createdAt').value = formatDate(
createdAt,
'ymd'
);
document.querySelector('form #broker').value = broker.name;
bsOffcanvas.show();
loadHandleFormSubmit('update', id);
};
}
function loadHandleConfirmModal(id) {
const iconTrash = document.querySelector(`#investment-${id} .icon-trash`);
iconTrash.onclick = () => {
removedHostId = id;
confirmModal.show();
};
}
function loadHandleRemoveInvestment() {
const confirmBtn = document.querySelector('.modal .btn-primary');
confirmBtn.onclick = () => {
API.remove(`/investments/${removedHostId}`);
document.querySelector(`#investment-${removedHostId}`).remove();
confirmModal.hide();
};
}
async function loadCategoriesSelect() {
const select = document.querySelector('#categoryId');
const categories = await API.read('/categories');
for (const category of categories) {
const option = `<option value="${category.id}">${category.name}</option>`;
select.insertAdjacentHTML('beforeend', option);
}
}
loadInvestmentCards();
loadHandleCreateInvestment();
loadCategoriesSelect();
loadHandleRemoveInvestment();
/codes/expressjs/invest-app-prismajs-relation/public/js/index.js
import API from './services/api.js';
import { formatCurrency, formatDate } from './lib/format.js';
let removedHostId;
const form = document.querySelector('form');
const bsOffcanvas = new bootstrap.Offcanvas('.offcanvas');
const confirmModal = new bootstrap.Modal('.modal');
function InvestmentCard(investment) {
return `<div class="col" id="investment-${investment.id}">
<div class="card">
<div class="card-header">
<span class="investment-name">
${investment.name}
</span>
<span class="float-end">
<span class="icon-trash" >
<span
class="iconify"
data-icon="solar:trash-bin-minimalistic-broken"
>
</span>
</span>
<span class="icon-pencil">
<span
class="iconify"
data-icon="tabler:pencil"
>
</span>
</span>
</span>
</div>
<div class="card-body">
<div>
<span class="fw-bold">Valor:</span>
<span class="investment-value">
${formatCurrency(investment.value / 100)}
</span>
</div>
<div>
<span class="fw-bold">Taxa:</span>
<span class="investment-interest">
${investment.interest}
</span>
</div>
<div>
<span class="fw-bold">Data:</span>
<span class="investment-created-at">
${formatDate(investment.createdAt)}
</span>
</div>
<div>
<span class="fw-bold">Corretora:</span>
<span class="investment-broker">
${investment.broker.name}
</span>
</div>
<div>
<span class="fw-bold">Categoria:</span>
<span
class="badge investment-category"
style="background-color: ${investment.category.color}"
>
${investment.category.name}
</span>
</div>
</div>
</div>
</div>`;
}
function createInvestmentCard(investment) {
document.querySelector('.investments p').style.display = 'none';
const investmentContainer = document.querySelector(`#investment-grid`);
investmentContainer.insertAdjacentHTML(
'beforeend',
InvestmentCard(investment)
);
loadHandleConfirmModal(investment.id);
loadHandleUpdateInvestment(investment.id);
}
async function loadInvestmentCards() {
const investments = await API.read('/investments');
for (const investment of investments) {
createInvestmentCard(investment);
}
}
function updateInvestmentCard({
id,
name,
value,
createdAt,
category,
broker,
interest,
}) {
document.querySelector(`#investment-${id} .investment-name`).innerText = name;
document.querySelector(`#investment-${id} .investment-value`).innerText =
formatCurrency(value / 100);
document.querySelector(`#investment-${id} .investment-interest`).innerText =
interest;
document.querySelector(`#investment-${id} .investment-broker`).innerText =
broker.name;
document.querySelector(`#investment-${id} .investment-created-at`).innerText =
createdAt;
document.querySelector(
`#investment-${id} .investment-category`
).style.backgroundColor = category.color;
document.querySelector(`#investment-${id} .investment-category`).innerText =
category.name;
}
function loadHandleFormSubmit(type, id) {
form.onsubmit = async (event) => {
event.preventDefault();
const investment = Object.fromEntries(new FormData(form));
investment.value = Number(investment.value) * 100;
if (type === 'create') {
const createdInvestment = await API.create('/investments', investment);
createInvestmentCard(createdInvestment);
} else if (type === 'update') {
const updatedInvestment = await API.update(
`/investments/${id}`,
investment
);
updateInvestmentCard(updatedInvestment);
}
form.reset();
document.querySelector('#offcanvas-close').click();
};
}
function loadHandleCreateInvestment() {
const button = document.querySelector('.btn.create-investment');
button.onclick = () => {
form.reset();
bsOffcanvas.show();
loadHandleFormSubmit('create');
};
}
function loadHandleUpdateInvestment(id) {
const iconPencil = document.querySelector(`#investment-${id} .icon-pencil`);
iconPencil.onclick = async () => {
const investment = await API.read(`/investments/${id}`);
const { name, value, interest, createdAt, categoryId, broker } = investment;
document.querySelector('form #name').value = name;
document.querySelector('form #value').value = value / 100;
document.querySelector('form #interest').value = interest;
document.querySelector('form #categoryId').value = categoryId;
document.querySelector('form #createdAt').value = formatDate(
createdAt,
'ymd'
);
document.querySelector('form #broker').value = broker.name;
bsOffcanvas.show();
loadHandleFormSubmit('update', id);
};
}
function loadHandleConfirmModal(id) {
const iconTrash = document.querySelector(`#investment-${id} .icon-trash`);
iconTrash.onclick = () => {
removedHostId = id;
confirmModal.show();
};
}
function loadHandleRemoveInvestment() {
const confirmBtn = document.querySelector('.modal .btn-primary');
confirmBtn.onclick = () => {
API.remove(`/investments/${removedHostId}`);
document.querySelector(`#investment-${removedHostId}`).remove();
confirmModal.hide();
};
}
async function loadCategoriesSelect() {
const select = document.querySelector('#categoryId');
const categories = await API.read('/categories');
for (const category of categories) {
const option = `<option value="${category.id}">${category.name}</option>`;
select.insertAdjacentHTML('beforeend', option);
}
}
loadInvestmentCards();
loadHandleCreateInvestment();
loadCategoriesSelect();
loadHandleRemoveInvestment();