DBE S04: cadastro e listagem de instrumentos musicais
This commit is contained in:
commit
bcf1a6d218
17 files changed
+2152
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules/
|
||||||
|
.env
|
||||||
|
npm-debug.log*
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Anderson Valadares Pinto
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
# DBE-S04 - Cadastro e Listagem de Instrumentos Musicais
|
||||||
|
|
||||||
|
**Aluno:** Anderson Valadares Pinto (20251PL.TSIAD0018) [SLS]
|
||||||
|
|
||||||
|
Aplicação web desenvolvida para a disciplina de **Desenvolvimento Back-End (DBE)** - Semana 04. O projeto demonstra o cadastro e a listagem de instrumentos musicais em uma loja, utilizando **Node.js**, **Express**, **EJS**, **MongoDB** (com Mongoose) e estilização com **Bootstrap 5**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Tecnologias Utilizadas
|
||||||
|
|
||||||
|
- **Node.js**: Ambiente de execução JavaScript no servidor com suporte a **ES Modules** (`"type": "module"`).
|
||||||
|
- **Express.js**: Framework minimalista para criação de servidores HTTP e roteamento dinâmico.
|
||||||
|
- **EJS (Embedded JavaScript)**: Mecanismo de templates para renderização dinâmica no lado do servidor (SSR).
|
||||||
|
- **MongoDB + Mongoose**: Banco de dados orientado a documentos e modelagem dos instrumentos.
|
||||||
|
- **Bootstrap 5 & Bootstrap Icons (CDN)**: Framework CSS responsivo e ícones para uma interface moderna e acessível.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎸 Instrumentos de Teste
|
||||||
|
|
||||||
|
O script `seed.js` cadastra seis instrumentos, atendendo às orientações da atividade:
|
||||||
|
|
||||||
|
| Nome | Fabricante | Preço | Tipo |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Guitarra Elétrica Stratocaster | Fender | R$ 5.490,00 | Cordas |
|
||||||
|
| Guitarra Acústica Folk | Yamaha | R$ 2.190,00 | Cordas |
|
||||||
|
| Baixo Elétrico SR300 | Ibanez | R$ 3.890,00 | Cordas |
|
||||||
|
| Bateria Acústica Roadshow | Pearl | R$ 7.290,00 | Percussão |
|
||||||
|
| Teclado Digital Juno-DS61 | Roland | R$ 4.590,00 | Teclas |
|
||||||
|
| Saxofone Alto YAS-280 | Yamaha | R$ 6.290,00 | Sopro |
|
||||||
|
|
||||||
|
Há três instrumentos do tipo **Cordas**, preços diferentes e nomes que permitem testar uma pesquisa por "Guitarra", "Yamaha" ou "Elétrica" posteriormente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 Rotas e Endpoints
|
||||||
|
|
||||||
|
| Método | Rota | Descrição |
|
||||||
|
|---|---|---|
|
||||||
|
| `GET` | `/` | Redireciona para a listagem de instrumentos. |
|
||||||
|
| `GET` | `/instrumentos` | Lista todos os instrumentos cadastrados, com nome, fabricante, tipo e preço. |
|
||||||
|
| `GET` | `/instrumentos/add` | Exibe o formulário de cadastro de um novo instrumento. |
|
||||||
|
| `POST` | `/instrumentos` | Recebe os dados do formulário, grava no MongoDB e exibe a confirmação. |
|
||||||
|
|
||||||
|
### Exemplos de URLs para Testes:
|
||||||
|
- `http://localhost:3000/instrumentos` — Listagem completa do estoque
|
||||||
|
- `http://localhost:3000/instrumentos/add` — Formulário de cadastro
|
||||||
|
- `http://localhost:3000/naoexiste` — Resposta de página não encontrada (404)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📂 Estrutura do Projeto
|
||||||
|
|
||||||
|
```
|
||||||
|
DBE-S04/
|
||||||
|
├── models/
|
||||||
|
│ └── Instrumento.js # Schema e validações do instrumento
|
||||||
|
├── routes/
|
||||||
|
│ └── instrumento.js # Rotas de cadastro e listagem
|
||||||
|
├── views/
|
||||||
|
│ ├── instrumento/
|
||||||
|
│ │ ├── add.ejs # Formulário de cadastro
|
||||||
|
│ │ ├── addok.ejs # Confirmação do cadastro
|
||||||
|
│ │ └── lst.ejs # Listagem tabular com loop EJS
|
||||||
|
│ └── erro.ejs # Páginas de erro 404 e 500
|
||||||
|
├── .gitignore
|
||||||
|
├── app.js # Servidor Express, EJS e conexão com o MongoDB
|
||||||
|
├── seed.js # Popula o banco com instrumentos de teste
|
||||||
|
├── package.json # Configurações do projeto e dependências
|
||||||
|
└── README.md # Documentação do projeto
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Como Executar o Projeto Localmente
|
||||||
|
|
||||||
|
Pré-requisitos: **Node.js 18+** e **MongoDB** escutando em `127.0.0.1:27017`.
|
||||||
|
|
||||||
|
1. **Clone o repositório:**
|
||||||
|
```bash
|
||||||
|
git clone https://gitea.tsiad.com.br/anderson/DBE-S04.git
|
||||||
|
cd DBE-S04
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Instale as dependências:**
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Popule o banco com os instrumentos de teste (opcional):**
|
||||||
|
```bash
|
||||||
|
npm run seed
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Inicie a aplicação:**
|
||||||
|
- Modo padrão:
|
||||||
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
- Modo de desenvolvimento (watch mode):
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Acesse no navegador:**
|
||||||
|
[http://localhost:3000/instrumentos](http://localhost:3000/instrumentos)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Configuração
|
||||||
|
|
||||||
|
As variáveis de ambiente são opcionais:
|
||||||
|
|
||||||
|
| Variável | Padrão |
|
||||||
|
|---|---|
|
||||||
|
| `PORT` | `3000` |
|
||||||
|
| `MONGODB_URI` | `mongodb://127.0.0.1:27017/loja_instrumentos` |
|
||||||
|
|
||||||
|
Exemplo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MONGODB_URI="mongodb://127.0.0.1:27017/loja_instrumentos" PORT=3000 npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗃️ Modelo `Instrumento`
|
||||||
|
|
||||||
|
| Campo | Tipo | Regras |
|
||||||
|
|---|---|---|
|
||||||
|
| `nome` | String | obrigatório, até 120 caracteres |
|
||||||
|
| `fabricante` | String | obrigatório, até 120 caracteres |
|
||||||
|
| `preco` | Number | obrigatório, maior ou igual a zero |
|
||||||
|
| `tipo` | String | obrigatório, um dos tipos da lista |
|
||||||
|
|
||||||
|
Tipos aceitos: Cordas, Percussão, Teclas, Sopro, Áudio e Acessórios.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📸 Telas
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
A listagem também funciona em telas pequenas (a tabela rola na horizontal):
|
||||||
|
[docs/listagem-mobile.png](docs/listagem-mobile.png).
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
// Anderson Valadares Pinto (20251PL.TSIAD0018) [SLS]
|
||||||
|
|
||||||
|
import path from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
import express from 'express';
|
||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
|
import instrumentoRoutes from './routes/instrumento.js';
|
||||||
|
|
||||||
|
// Configuração para resolução de caminhos com ES Modules
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const PORT = Number(process.env.PORT) || 3000;
|
||||||
|
const MONGODB_URI =
|
||||||
|
process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/loja_instrumentos';
|
||||||
|
|
||||||
|
// Configuração do mecanismo de visualização EJS
|
||||||
|
app.set('view engine', 'ejs');
|
||||||
|
app.set('views', path.join(__dirname, 'views'));
|
||||||
|
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
// Funções de formatação disponíveis em todos os templates.
|
||||||
|
app.locals.formatarPreco = (valor) =>
|
||||||
|
new Intl.NumberFormat('pt-BR', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'BRL',
|
||||||
|
}).format(Number(valor) || 0);
|
||||||
|
|
||||||
|
app.use('/', instrumentoRoutes);
|
||||||
|
|
||||||
|
// Página não encontrada.
|
||||||
|
app.use((req, res) => {
|
||||||
|
res.status(404).render('erro', {
|
||||||
|
titulo: 'Página Não Encontrada',
|
||||||
|
codigo: 404,
|
||||||
|
mensagem: 'O endereço que você tentou abrir não existe.',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Qualquer erro inesperado cai aqui, em vez de derrubar o servidor.
|
||||||
|
app.use((erro, req, res, next) => {
|
||||||
|
console.error(erro);
|
||||||
|
res.status(500).render('erro', {
|
||||||
|
titulo: 'Erro Interno',
|
||||||
|
codigo: 500,
|
||||||
|
mensagem: 'Algo deu errado ao processar a solicitação. Tente novamente.',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function iniciar() {
|
||||||
|
await mongoose.connect(MONGODB_URI);
|
||||||
|
console.log(`Conectado ao MongoDB em ${MONGODB_URI}`);
|
||||||
|
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(
|
||||||
|
`Servidor rodando com sucesso na porta ${PORT}: http://localhost:${PORT}/instrumentos`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
iniciar().catch((erro) => {
|
||||||
|
console.error('Não foi possível iniciar a aplicação:', erro.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 93 KiB |
@@ -0,0 +1,52 @@
|
|||||||
|
// Anderson Valadares Pinto (20251PL.TSIAD0018) [SLS]
|
||||||
|
|
||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
|
// Tipos aceitos no formulário. A lista também alimenta o <select> de add.ejs.
|
||||||
|
export const TIPOS = [
|
||||||
|
'Cordas',
|
||||||
|
'Percussão',
|
||||||
|
'Teclas',
|
||||||
|
'Sopro',
|
||||||
|
'Áudio',
|
||||||
|
'Acessórios',
|
||||||
|
];
|
||||||
|
|
||||||
|
const instrumentoSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
nome: {
|
||||||
|
type: String,
|
||||||
|
required: [true, 'Informe o nome do instrumento.'],
|
||||||
|
trim: true,
|
||||||
|
maxlength: [120, 'O nome pode ter no máximo 120 caracteres.'],
|
||||||
|
},
|
||||||
|
fabricante: {
|
||||||
|
type: String,
|
||||||
|
required: [true, 'Informe o fabricante.'],
|
||||||
|
trim: true,
|
||||||
|
maxlength: [120, 'O fabricante pode ter no máximo 120 caracteres.'],
|
||||||
|
},
|
||||||
|
preco: {
|
||||||
|
type: Number,
|
||||||
|
required: [true, 'Informe o preço.'],
|
||||||
|
min: [0, 'O preço não pode ser negativo.'],
|
||||||
|
},
|
||||||
|
tipo: {
|
||||||
|
type: String,
|
||||||
|
required: [true, 'Informe o tipo do instrumento.'],
|
||||||
|
trim: true,
|
||||||
|
enum: {
|
||||||
|
values: TIPOS,
|
||||||
|
message: 'Escolha um tipo da lista.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
versionKey: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const Instrumento = mongoose.model('Instrumento', instrumentoSchema);
|
||||||
|
|
||||||
|
export default Instrumento;
|
||||||
Generated
+1157
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "cadastro-instrumentos",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Cadastro e Listagem de Instrumentos Musicais com Express, EJS e MongoDB - DBE Semana 04",
|
||||||
|
"main": "app.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node app.js",
|
||||||
|
"dev": "node --watch app.js",
|
||||||
|
"seed": "node seed.js"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://gitea.tsiad.com.br/anderson/DBE-S04.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"express",
|
||||||
|
"ejs",
|
||||||
|
"mongodb",
|
||||||
|
"instrumentos",
|
||||||
|
"dbe"
|
||||||
|
],
|
||||||
|
"author": "Anderson <[email protected]>",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ejs": "^3.1.10",
|
||||||
|
"express": "^4.21.2",
|
||||||
|
"mongoose": "^8.19.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
// Anderson Valadares Pinto (20251PL.TSIAD0018) [SLS]
|
||||||
|
|
||||||
|
import express from 'express';
|
||||||
|
|
||||||
|
import Instrumento, { TIPOS } from '../models/Instrumento.js';
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
// "/" só manda para a listagem.
|
||||||
|
router.get('/', (req, res) => res.redirect('/instrumentos'));
|
||||||
|
|
||||||
|
// Listagem: todos os instrumentos, do mais recente para o mais antigo.
|
||||||
|
router.get('/instrumentos', async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const instrumentos = await Instrumento.find().sort({ createdAt: -1 }).lean();
|
||||||
|
res.render('instrumento/lst', { titulo: 'Instrumentos', instrumentos });
|
||||||
|
} catch (erro) {
|
||||||
|
next(erro);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Formulário de cadastro.
|
||||||
|
router.get('/instrumentos/add', (req, res) => {
|
||||||
|
res.render('instrumento/add', {
|
||||||
|
titulo: 'Cadastrar Instrumento',
|
||||||
|
tipos: TIPOS,
|
||||||
|
valores: {},
|
||||||
|
erro: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Recebe o formulário, grava no MongoDB e mostra a confirmação.
|
||||||
|
router.post('/instrumentos', async (req, res, next) => {
|
||||||
|
const valores = {
|
||||||
|
nome: String(req.body.nome ?? '').trim(),
|
||||||
|
fabricante: String(req.body.fabricante ?? '').trim(),
|
||||||
|
preco: String(req.body.preco ?? '').replace(',', '.').trim(),
|
||||||
|
tipo: String(req.body.tipo ?? '').trim(),
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const instrumento = await Instrumento.create({
|
||||||
|
nome: valores.nome,
|
||||||
|
fabricante: valores.fabricante,
|
||||||
|
preco: Number(valores.preco),
|
||||||
|
tipo: valores.tipo,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(201).render('instrumento/addok', {
|
||||||
|
titulo: 'Instrumento Cadastrado',
|
||||||
|
instrumento: instrumento.toObject(),
|
||||||
|
});
|
||||||
|
} catch (erro) {
|
||||||
|
// ValidationError cobre campos vazios e tipo inválido; CastError aparece
|
||||||
|
// quando o preço chega como texto que não vira número.
|
||||||
|
if (erro.name === 'ValidationError' || erro.name === 'CastError') {
|
||||||
|
// O Mongoose embrulha o erro de conversão do preço num ValidationError,
|
||||||
|
// então olhamos o primeiro erro interno para dar uma mensagem clara.
|
||||||
|
const primeiro =
|
||||||
|
erro.name === 'CastError' ? erro : Object.values(erro.errors || {})[0];
|
||||||
|
const mensagem =
|
||||||
|
primeiro?.name === 'CastError'
|
||||||
|
? 'Informe o preço como um número.'
|
||||||
|
: primeiro?.message || 'Confira os dados informados.';
|
||||||
|
return res.status(400).render('instrumento/add', {
|
||||||
|
titulo: 'Cadastrar Instrumento',
|
||||||
|
tipos: TIPOS,
|
||||||
|
valores,
|
||||||
|
erro: mensagem,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next(erro);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
// Anderson Valadares Pinto (20251PL.TSIAD0018) [SLS]
|
||||||
|
//
|
||||||
|
// Popula o banco com instrumentos para testar a listagem.
|
||||||
|
// Uso: npm run seed (insere o que ainda não existe)
|
||||||
|
// npm run seed -- --reset (apaga tudo e insere de novo)
|
||||||
|
|
||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
|
import Instrumento from './models/Instrumento.js';
|
||||||
|
|
||||||
|
const MONGODB_URI =
|
||||||
|
process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/loja_instrumentos';
|
||||||
|
|
||||||
|
// Três cordas (dois do mesmo tipo, como pede o enunciado), preços variados
|
||||||
|
// e nomes que permitem testar a busca depois.
|
||||||
|
const instrumentos = [
|
||||||
|
{
|
||||||
|
nome: 'Guitarra Elétrica Stratocaster',
|
||||||
|
fabricante: 'Fender',
|
||||||
|
preco: 5490.0,
|
||||||
|
tipo: 'Cordas',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'Guitarra Acústica Folk',
|
||||||
|
fabricante: 'Yamaha',
|
||||||
|
preco: 2190.0,
|
||||||
|
tipo: 'Cordas',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'Baixo Elétrico SR300',
|
||||||
|
fabricante: 'Ibanez',
|
||||||
|
preco: 3890.0,
|
||||||
|
tipo: 'Cordas',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'Bateria Acústica Roadshow',
|
||||||
|
fabricante: 'Pearl',
|
||||||
|
preco: 7290.0,
|
||||||
|
tipo: 'Percussão',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'Teclado Digital Juno-DS61',
|
||||||
|
fabricante: 'Roland',
|
||||||
|
preco: 4590.0,
|
||||||
|
tipo: 'Teclas',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: 'Saxofone Alto YAS-280',
|
||||||
|
fabricante: 'Yamaha',
|
||||||
|
preco: 6290.0,
|
||||||
|
tipo: 'Sopro',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function executar() {
|
||||||
|
await mongoose.connect(MONGODB_URI);
|
||||||
|
|
||||||
|
if (process.argv.includes('--reset')) {
|
||||||
|
const removidos = await Instrumento.deleteMany({});
|
||||||
|
console.log(`${removidos.deletedCount} instrumento(s) removido(s).`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let inseridos = 0;
|
||||||
|
for (const instrumento of instrumentos) {
|
||||||
|
// upsert por nome: rodar o seed duas vezes não duplica o estoque.
|
||||||
|
const resultado = await Instrumento.updateOne(
|
||||||
|
{ nome: instrumento.nome },
|
||||||
|
{ $setOnInsert: instrumento },
|
||||||
|
{ upsert: true }
|
||||||
|
);
|
||||||
|
if (resultado.upsertedCount) inseridos += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = await Instrumento.countDocuments();
|
||||||
|
console.log(`${inseridos} instrumento(s) inserido(s); ${total} no total.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
executar()
|
||||||
|
.catch((erro) => {
|
||||||
|
console.error('Falha ao popular o banco:', erro.message);
|
||||||
|
process.exitCode = 1;
|
||||||
|
})
|
||||||
|
.finally(() => mongoose.disconnect());
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
<!-- Anderson Valadares Pinto (20251PL.TSIAD0018) [SLS] -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="pt-BR">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><%= titulo %> | Loja de Instrumentos</title>
|
||||||
|
<!-- Bootstrap 5 CSS via CDN -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<!-- Bootstrap Icons -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Barra de Navegação -->
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark shadow-sm">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand fw-bold" href="/instrumentos">
|
||||||
|
<i class="bi bi-music-note-beamed text-warning me-2"></i>Loja de Instrumentos
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navMenu">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navMenu">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/instrumentos">
|
||||||
|
<i class="bi bi-list-columns-reverse me-1"></i>Instrumentos
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/instrumentos/add">
|
||||||
|
<i class="bi bi-plus-circle me-1"></i>Cadastrar
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Conteúdo Principal -->
|
||||||
|
<main class="container my-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="card border-danger shadow-sm rounded-4 text-center p-4 p-md-5">
|
||||||
|
<div class="text-danger mb-3" style="font-size: 4rem;">
|
||||||
|
<i class="bi bi-exclamation-triangle-fill"></i>
|
||||||
|
</div>
|
||||||
|
<h1 class="h3 fw-bold text-danger mb-2"><%= titulo %></h1>
|
||||||
|
<p class="text-secondary mb-4 lead"><%= mensagem %></p>
|
||||||
|
<div class="d-flex flex-wrap justify-content-center gap-2">
|
||||||
|
<a href="/instrumentos" class="btn btn-primary px-4 shadow-sm">
|
||||||
|
<i class="bi bi-list-columns-reverse me-1"></i>Ir para a Listagem
|
||||||
|
</a>
|
||||||
|
<a href="/instrumentos/add" class="btn btn-outline-secondary px-4">
|
||||||
|
<i class="bi bi-plus-lg me-1"></i>Cadastrar Instrumento
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Rodapé -->
|
||||||
|
<footer class="bg-dark text-white-50 text-center py-4 border-top">
|
||||||
|
<div class="container">
|
||||||
|
<p class="mb-0">Desenvolvimento Back-End (DBE) • Semana 04 • Cadastro e Listagem de Instrumentos com Express, EJS e MongoDB</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
<!-- Anderson Valadares Pinto (20251PL.TSIAD0018) [SLS] -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="pt-BR">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><%= titulo %> | Loja de Instrumentos</title>
|
||||||
|
<!-- Bootstrap 5 CSS via CDN -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<!-- Bootstrap Icons -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Barra de Navegação -->
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark shadow-sm">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand fw-bold" href="/instrumentos">
|
||||||
|
<i class="bi bi-music-note-beamed text-warning me-2"></i>Loja de Instrumentos
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navMenu">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navMenu">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/instrumentos">
|
||||||
|
<i class="bi bi-list-columns-reverse me-1"></i>Instrumentos
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="/instrumentos/add">
|
||||||
|
<i class="bi bi-plus-circle me-1"></i>Cadastrar
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Conteúdo Principal -->
|
||||||
|
<main class="container my-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="d-flex flex-wrap justify-content-between align-items-center mb-4 gap-2">
|
||||||
|
<div>
|
||||||
|
<h1 class="h2 fw-bold mb-1">
|
||||||
|
<i class="bi bi-plus-square-fill text-primary me-2"></i><%= titulo %>
|
||||||
|
</h1>
|
||||||
|
<p class="text-muted mb-0">Preencha os dados para adicionar o instrumento ao estoque.</p>
|
||||||
|
</div>
|
||||||
|
<a href="/instrumentos" class="btn btn-outline-secondary">
|
||||||
|
<i class="bi bi-arrow-left me-1"></i>Voltar à Listagem
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border-0 shadow-sm rounded-4">
|
||||||
|
<div class="card-body p-4 p-md-5">
|
||||||
|
<% if (erro) { %>
|
||||||
|
<div class="alert alert-danger d-flex align-items-start gap-2" role="alert">
|
||||||
|
<i class="bi bi-exclamation-triangle-fill fs-5"></i>
|
||||||
|
<div>
|
||||||
|
<strong>Não foi possível cadastrar.</strong><br>
|
||||||
|
<%= erro %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<form method="POST" action="/instrumentos">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="nome" class="form-label fw-semibold">Nome</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="nome"
|
||||||
|
name="nome"
|
||||||
|
required
|
||||||
|
maxlength="120"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="Ex.: Guitarra Elétrica Stratocaster"
|
||||||
|
value="<%= valores.nome || '' %>"
|
||||||
|
<%= erro ? '' : 'autofocus' %>
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="fabricante" class="form-label fw-semibold">Fabricante</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="fabricante"
|
||||||
|
name="fabricante"
|
||||||
|
required
|
||||||
|
maxlength="120"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="Ex.: Fender"
|
||||||
|
value="<%= valores.fabricante || '' %>"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="tipo" class="form-label fw-semibold">Tipo</label>
|
||||||
|
<select class="form-select" id="tipo" name="tipo" required>
|
||||||
|
<option value="">Selecione...</option>
|
||||||
|
<% tipos.forEach((tipo) => { %>
|
||||||
|
<option value="<%= tipo %>" <%= valores.tipo === tipo ? 'selected' : '' %>><%= tipo %></option>
|
||||||
|
<% }) %>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="preco" class="form-label fw-semibold">Preço (R$)</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="form-control"
|
||||||
|
id="preco"
|
||||||
|
name="preco"
|
||||||
|
required
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
inputmode="decimal"
|
||||||
|
placeholder="0.00"
|
||||||
|
value="<%= valores.preco || '' %>"
|
||||||
|
>
|
||||||
|
<div class="form-text">
|
||||||
|
Use ponto para os centavos — por exemplo, <code>1499.90</code>.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex flex-wrap gap-2 pt-4">
|
||||||
|
<button type="submit" class="btn btn-primary px-4 shadow-sm">
|
||||||
|
<i class="bi bi-check2-circle me-1"></i>Cadastrar Instrumento
|
||||||
|
</button>
|
||||||
|
<a href="/instrumentos" class="btn btn-outline-secondary px-4">
|
||||||
|
<i class="bi bi-x-lg me-1"></i>Cancelar
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Rodapé -->
|
||||||
|
<footer class="bg-dark text-white-50 text-center py-4 border-top">
|
||||||
|
<div class="container">
|
||||||
|
<p class="mb-0">Desenvolvimento Back-End (DBE) • Semana 04 • Cadastro e Listagem de Instrumentos com Express, EJS e MongoDB</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
<!-- Anderson Valadares Pinto (20251PL.TSIAD0018) [SLS] -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="pt-BR">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><%= titulo %> | Loja de Instrumentos</title>
|
||||||
|
<!-- Bootstrap 5 CSS via CDN -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<!-- Bootstrap Icons -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Barra de Navegação -->
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark shadow-sm">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand fw-bold" href="/instrumentos">
|
||||||
|
<i class="bi bi-music-note-beamed text-warning me-2"></i>Loja de Instrumentos
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navMenu">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navMenu">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/instrumentos">
|
||||||
|
<i class="bi bi-list-columns-reverse me-1"></i>Instrumentos
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="/instrumentos/add">
|
||||||
|
<i class="bi bi-plus-circle me-1"></i>Cadastrar
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Conteúdo Principal -->
|
||||||
|
<main class="container my-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card border-0 shadow-sm rounded-4 overflow-hidden">
|
||||||
|
<div class="card-header bg-success text-white py-3 px-4 d-flex justify-content-between align-items-center">
|
||||||
|
<span class="badge bg-light text-success fs-6 px-3 py-2 rounded-pill">
|
||||||
|
<i class="bi bi-check2-circle me-1"></i>Cadastro realizado
|
||||||
|
</span>
|
||||||
|
<span class="text-white-50"><i class="bi bi-music-note-list me-1"></i>Ficha do Instrumento</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-4 p-md-5 text-center">
|
||||||
|
<div class="text-success mb-3" style="font-size: 4rem;">
|
||||||
|
<i class="bi bi-check-circle-fill"></i>
|
||||||
|
</div>
|
||||||
|
<h1 class="h3 fw-bold text-dark mb-2"><%= titulo %></h1>
|
||||||
|
<p class="lead text-secondary mb-4">
|
||||||
|
<strong><%= instrumento.nome %></strong> já está no estoque da loja.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="list-group list-group-flush text-start mb-4">
|
||||||
|
<div class="list-group-item d-flex justify-content-between align-items-center px-0 py-3">
|
||||||
|
<span class="text-secondary fw-semibold">
|
||||||
|
<i class="bi bi-music-note text-primary me-2"></i>Nome:
|
||||||
|
</span>
|
||||||
|
<span class="fw-bold text-dark"><%= instrumento.nome %></span>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item d-flex justify-content-between align-items-center px-0 py-3">
|
||||||
|
<span class="text-secondary fw-semibold">
|
||||||
|
<i class="bi bi-building text-muted me-2"></i>Fabricante:
|
||||||
|
</span>
|
||||||
|
<span class="fw-semibold text-dark"><%= instrumento.fabricante %></span>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item d-flex justify-content-between align-items-center px-0 py-3">
|
||||||
|
<span class="text-secondary fw-semibold">
|
||||||
|
<i class="bi bi-tag-fill text-primary me-2"></i>Tipo:
|
||||||
|
</span>
|
||||||
|
<span class="badge bg-primary-subtle text-primary-emphasis rounded-pill px-3 py-2 fs-6">
|
||||||
|
<%= instrumento.tipo %>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item d-flex justify-content-between align-items-center px-0 py-3">
|
||||||
|
<span class="text-secondary fw-semibold">
|
||||||
|
<i class="bi bi-currency-dollar text-success me-2"></i>Preço:
|
||||||
|
</span>
|
||||||
|
<span class="fw-bold text-success fs-5"><%= formatarPreco(instrumento.preco) %></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex flex-wrap justify-content-center gap-2">
|
||||||
|
<a href="/instrumentos/add" class="btn btn-primary px-4 shadow-sm">
|
||||||
|
<i class="bi bi-plus-lg me-1"></i>Cadastrar outro
|
||||||
|
</a>
|
||||||
|
<a href="/instrumentos" class="btn btn-outline-secondary px-4">
|
||||||
|
<i class="bi bi-arrow-left me-1"></i>Voltar à Listagem
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Rodapé -->
|
||||||
|
<footer class="bg-dark text-white-50 text-center py-4 border-top">
|
||||||
|
<div class="container">
|
||||||
|
<p class="mb-0">Desenvolvimento Back-End (DBE) • Semana 04 • Cadastro e Listagem de Instrumentos com Express, EJS e MongoDB</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
<!-- Anderson Valadares Pinto (20251PL.TSIAD0018) [SLS] -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="pt-BR">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><%= titulo %> | Loja de Instrumentos</title>
|
||||||
|
<!-- Bootstrap 5 CSS via CDN -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<!-- Bootstrap Icons -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Barra de Navegação -->
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark shadow-sm">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand fw-bold" href="/instrumentos">
|
||||||
|
<i class="bi bi-music-note-beamed text-warning me-2"></i>Loja de Instrumentos
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navMenu">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navMenu">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="/instrumentos">
|
||||||
|
<i class="bi bi-list-columns-reverse me-1"></i>Instrumentos
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/instrumentos/add">
|
||||||
|
<i class="bi bi-plus-circle me-1"></i>Cadastrar
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Conteúdo Principal -->
|
||||||
|
<main class="container my-5">
|
||||||
|
<div class="d-flex flex-wrap justify-content-between align-items-center mb-4 gap-2">
|
||||||
|
<div>
|
||||||
|
<h1 class="h2 fw-bold mb-1">
|
||||||
|
<i class="bi bi-music-note-list text-primary me-2"></i><%= titulo %>
|
||||||
|
</h1>
|
||||||
|
<p class="text-muted mb-0">
|
||||||
|
Estoque da loja cadastrado no MongoDB.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<a href="/instrumentos/add" class="btn btn-primary shadow-sm">
|
||||||
|
<i class="bi bi-plus-lg me-1"></i>Cadastrar Instrumento
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tabela de Instrumentos -->
|
||||||
|
<div class="card border-0 shadow-sm rounded-3 overflow-hidden">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-hover mb-0 align-middle">
|
||||||
|
<thead class="table-dark">
|
||||||
|
<tr>
|
||||||
|
<th scope="col" class="text-center" style="width: 70px;">#</th>
|
||||||
|
<th scope="col">Nome</th>
|
||||||
|
<th scope="col">Fabricante</th>
|
||||||
|
<th scope="col">Tipo</th>
|
||||||
|
<th scope="col" class="text-end">Preço</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% if (instrumentos && instrumentos.length > 0) { %>
|
||||||
|
<% for (let i = 0; i < instrumentos.length; i++) { %>
|
||||||
|
<% const instrumento = instrumentos[i]; %>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge bg-secondary rounded-pill px-2 py-1">
|
||||||
|
<%= i + 1 %>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="fw-bold text-dark">
|
||||||
|
<i class="bi bi-music-note text-primary me-2"></i><%= instrumento.nome %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<i class="bi bi-building me-1 text-muted"></i><%= instrumento.fabricante %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge bg-primary-subtle text-primary-emphasis rounded-pill px-3 py-2">
|
||||||
|
<%= instrumento.tipo %>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-end fw-semibold text-success">
|
||||||
|
<%= formatarPreco(instrumento.preco) %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% } %>
|
||||||
|
<% } else { %>
|
||||||
|
<tr>
|
||||||
|
<td colspan="5" class="text-center py-5">
|
||||||
|
<div class="text-muted">
|
||||||
|
<i class="bi bi-inbox fs-1 d-block mb-2"></i>
|
||||||
|
<p class="lead mb-3">Nenhum instrumento cadastrado no estoque no momento.</p>
|
||||||
|
<a href="/instrumentos/add" class="btn btn-primary">
|
||||||
|
<i class="bi bi-plus-lg me-1"></i>Cadastrar o primeiro instrumento
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% } %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-muted text-end mt-3 mb-0">
|
||||||
|
Quer incluir outro item?
|
||||||
|
<a href="/instrumentos/add" class="fw-semibold">Cadastrar novo instrumento</a>
|
||||||
|
</p>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Rodapé -->
|
||||||
|
<footer class="bg-dark text-white-50 text-center py-4 border-top">
|
||||||
|
<div class="container">
|
||||||
|
<p class="mb-0">Desenvolvimento Back-End (DBE) • Semana 04 • Cadastro e Listagem de Instrumentos com Express, EJS e MongoDB</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in new issue
Block a user