novo repositorio
|
|
@ -0,0 +1,13 @@
|
|||
backoffice/node_modules/
|
||||
api/node_modules/
|
||||
books_viewer/build/
|
||||
books_viewer/.dart_tool/
|
||||
*.lock
|
||||
*.pdf
|
||||
*.zip
|
||||
*.mp4
|
||||
*.apk
|
||||
*.ipa
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
const mysql = require('mysql');
|
||||
|
||||
// Configuração da conexão com o banco de dados
|
||||
const dbConfig = {
|
||||
host: '54.94.231.207',
|
||||
user: 'root',
|
||||
password: 'maker2018**==',
|
||||
database: 'makerbook',
|
||||
multipleStatements: true,
|
||||
};
|
||||
|
||||
const db = mysql.createConnection(dbConfig);
|
||||
|
||||
// Conectando ao banco de dados
|
||||
db.connect(err => {
|
||||
if (err) {
|
||||
console.error('Erro ao conectar ao banco de dados: ' + err.stack);
|
||||
return;
|
||||
}
|
||||
console.log('Conectado ao banco de dados com sucesso. ID da conexão: ' + db.threadId);
|
||||
});
|
||||
|
||||
function execQuery(query, params, req, res, posFunction = null) {
|
||||
db.query(query, params, (err, results) => {
|
||||
if (err) {
|
||||
console.log("500 - " + new Date().toISOString() + " - " + req.originalUrl);
|
||||
res.status(500).json({
|
||||
status: 500,
|
||||
results: null,
|
||||
error: err
|
||||
});
|
||||
} else {
|
||||
if (posFunction != null) {
|
||||
posFunction(results, function(newResults) {
|
||||
console.log("200 - " + new Date().toISOString() + " - " + req.originalUrl);
|
||||
res.status(200).json({
|
||||
status: 200,
|
||||
results: newResults,
|
||||
error: err
|
||||
});
|
||||
})
|
||||
}
|
||||
else {
|
||||
console.log("200 - " + new Date().toISOString() + " - " + req.originalUrl);
|
||||
res.status(200).json({
|
||||
status: 200,
|
||||
results: results,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Exportar a conexão com o banco de dados
|
||||
module.exports = db;
|
||||
module.exports = execQuery;
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
const execQuery = require('../connector');
|
||||
const db = require('../connector');
|
||||
const express = require('express');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const axios = require('axios');
|
||||
const router = express.Router();
|
||||
|
||||
const routes = function () {
|
||||
|
||||
router.post('/login', async (req, res) => {
|
||||
const login = req.body[0];
|
||||
const query = 'CALL login (?, ?, ?, @success, @role, @userId, @userName, @validated); SELECT @success AS success, @role AS role, @userId as userId, @userName as userName, @validated as validated;';
|
||||
|
||||
try {
|
||||
// Faça uma solicitação GET para a API ipify para capturar o IP do cliente
|
||||
const response = await axios.get('https://api.ipify.org/?format=text');
|
||||
const clientIp = response.data;
|
||||
|
||||
// Aqui você pode continuar com sua lógica de login e executar a consulta.
|
||||
execQuery(query, [login.email, login.password, clientIp], req, res);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erro ao obter o IP do cliente:', error);
|
||||
execQuery(query, [login.email, login.password, req.socket.remoteAddress], req, res);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/loginBook', async (req, res) => {
|
||||
const login = req.body;
|
||||
const query = 'CALL loginBook (?, ?, @success, @userId, @courseId, @courseName, @ext); SELECT @success AS success, @userId as userId, @courseId as courseId, @courseName as courseName, @ext as ext;';
|
||||
|
||||
try {
|
||||
const response = await axios.get('https://api.ipify.org/?format=text');
|
||||
const clientIp = response.data;
|
||||
|
||||
execQuery(query, [login.accesskey, clientIp], req, res, (results, callback) => {
|
||||
// Caminho físico do arquivo
|
||||
const course = results[1][0];
|
||||
const bookFile = path.join('/var/www/html/maker_book/materials', `course_${course["courseId"]}${course["ext"]}`);
|
||||
let modifiedAt = null;
|
||||
|
||||
if (fs.existsSync(bookFile)) {
|
||||
const stats = fs.statSync(bookFile);
|
||||
modifiedAt = stats.mtime.toISOString(); // formato compatível com Flutter
|
||||
}
|
||||
|
||||
// results[1][0] é a SELECT final com os dados que o app espera
|
||||
if (results[1] && results[1][0]) {
|
||||
results[1][0].modifiedAt = modifiedAt;
|
||||
}
|
||||
callback(results); // continua fluxo padrão
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erro ao obter o IP do cliente:', error);
|
||||
execQuery(query, [login.accesskey, req.socket.remoteAddress], req, res);
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
module.exports = routes;
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
const execQuery = require('../connector');
|
||||
const db = require('../connector');
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const routes = function () {
|
||||
|
||||
router.get('/list', (req, res) => {
|
||||
execQuery('SELECT * FROM listbooks;', [], req, res);
|
||||
});
|
||||
|
||||
router.get('/listSerials/:idserialslote', (req, res) => {
|
||||
const idserialslote = req.params.idserialslote;
|
||||
execQuery('CALL serialsByLote (?);', [idserialslote], req, res);
|
||||
});
|
||||
|
||||
router.get('/get/:id', (req, res) => {
|
||||
const id = req.params.id;
|
||||
execQuery('CALL getBook (?);', [id], req, res);
|
||||
});
|
||||
|
||||
router.post('/create', (req, res) => {
|
||||
const form = req.body[0];
|
||||
const query = 'CALL generateSerialLote (?, ?, ?, ?);';
|
||||
execQuery(query, [form.description, form.amount, form.idcourse, form.createdBy], req, res);
|
||||
});
|
||||
|
||||
router.post('/update', (req, res) => {
|
||||
const form = req.body[0];
|
||||
const query = 'UPDATE serialslote SET description = ? WHERE id = ?;';
|
||||
execQuery(query, [form.description, form.id], req, res);
|
||||
});
|
||||
|
||||
router.delete('/delete/:id', (req, res) => {
|
||||
const id = req.params.id;
|
||||
execQuery('CALL deleteSerialLote (?);', [id], req, res);
|
||||
});
|
||||
|
||||
router.post('/validateBook', (req, res) => {
|
||||
const form = req.body[0];
|
||||
const query = 'CALL validateBook (?, ?);';
|
||||
execQuery(query, [form.idserial, form.userName], req, res);
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
module.exports = routes;
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
const execQuery = require('../connector');
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const fileUtils = require('../utils/fileUtils');
|
||||
const formUtils = require('../utils/formUtils');
|
||||
const formidable = require("formidable");
|
||||
|
||||
const routes = function () {
|
||||
|
||||
router.get('/list', (req, res) => {
|
||||
execQuery('SELECT * FROM listcourses;', [], req, res);
|
||||
});
|
||||
|
||||
router.get('/get/:id', (req, res) => {
|
||||
const id = req.params.id;
|
||||
execQuery('SELECT * FROM courses WHERE id = ?;', [id], req, res);
|
||||
});
|
||||
|
||||
router.post('/create', (req, res) => {
|
||||
let body = new formidable.IncomingForm();
|
||||
body.maxFileSize = 1024 * 1024 * 1024;
|
||||
body.parse(req, function (err, fields, files) {
|
||||
const pdf = files.pdf;
|
||||
const form = formUtils.normalizarFormulario(fields);
|
||||
const query = 'INSERT INTO courses (description, enabled, duration, ext) VALUES (?, ?, ?, ?);';
|
||||
execQuery(
|
||||
query,
|
||||
[form.description, form.enabled, form.duration, ".pdf"],
|
||||
req,
|
||||
res,
|
||||
async (results, callback) => {
|
||||
const courseId = results["insertId"];
|
||||
|
||||
if (pdf && pdf.length > 0) {
|
||||
await fileUtils.salvarPDF(pdf[0], "course_" + courseId);
|
||||
}
|
||||
|
||||
callback(results);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/update', (req, res) => {
|
||||
let body = new formidable.IncomingForm();
|
||||
body.maxFileSize = 1024 * 1024 * 1024;
|
||||
body.parse(req, function (err, fields, files) {
|
||||
const pdf = files.pdf;
|
||||
const form = formUtils.normalizarFormulario(fields);
|
||||
const query = 'UPDATE courses SET description = ?, enabled = ?, duration = ? WHERE id = ?;';
|
||||
execQuery(
|
||||
query,
|
||||
[form.description, form.enabled, form.duration, form.id],
|
||||
req,
|
||||
res,
|
||||
async (results, callback) => {
|
||||
const courseId = form.id;
|
||||
|
||||
if (pdf && pdf.length > 0) {
|
||||
await fileUtils.salvarPDF(pdf[0], "course_" + courseId);
|
||||
}
|
||||
|
||||
callback(results);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
router.delete('/delete/:id', (req, res) => {
|
||||
const id = req.params.id;
|
||||
execQuery('DELETE FROM courses WHERE id = ?;', [id], req, res, async (results, callback) => {
|
||||
const diretorioParaExcluir = './pdfs/course_' + id.toString() + ".pdf";
|
||||
await fileUtils.excluirArquivo(diretorioParaExcluir);
|
||||
|
||||
callback(results);
|
||||
});
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
module.exports = routes;
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
const execQuery = require('../connector');
|
||||
const db = require('../connector');
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const routes = function () {
|
||||
|
||||
router.get('/listTypes', (req, res) => {
|
||||
execQuery('SELECT * FROM usertype;', [], req, res);
|
||||
});
|
||||
|
||||
router.get('/list', (req, res) => {
|
||||
execQuery('SELECT * FROM listusers;', [], req, res);
|
||||
});
|
||||
|
||||
router.get('/listCourses/:id', (req, res) => {
|
||||
const id = req.params.id;
|
||||
execQuery('CALL userCourses (?);', [id], req, res);
|
||||
});
|
||||
|
||||
router.get('/get/:id', (req, res) => {
|
||||
const id = req.params.id;
|
||||
execQuery('SELECT * FROM users WHERE id = ?;', [id], req, res);
|
||||
});
|
||||
|
||||
router.post('/create', (req, res) => {
|
||||
const form = req.body[0];
|
||||
const query = 'INSERT INTO users (idusertype, name, email, password, enabled) VALUES (?, ?, ?, ?, ?);';
|
||||
execQuery(query, [form.idusertype, form.name, form.email, form.password, form.enabled], req, res);
|
||||
});
|
||||
|
||||
router.post('/update', (req, res) => {
|
||||
const form = req.body[0];
|
||||
const query = 'UPDATE users SET idusertype = ?, name = ?, email = ?, password = ?, enabled = ? WHERE id = ?;';
|
||||
execQuery(query, [form.idusertype, form.name, form.email, form.password, form.enabled, form.id], req, res);
|
||||
});
|
||||
|
||||
router.delete('/delete/:id', (req, res) => {
|
||||
const id = req.params.id;
|
||||
execQuery('DELETE FROM users WHERE id = ?;', [id], req, res);
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
module.exports = routes;
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "api",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node server.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.8",
|
||||
"express": "^4.19.2",
|
||||
"formidable": "^3.5.1",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql": "^2.18.1",
|
||||
"mysql2": "^3.9.4"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
const express = require('express');
|
||||
|
||||
const app = express();
|
||||
const port = 3035;
|
||||
|
||||
// Middleware para parsear o corpo das requisições JSON
|
||||
app.use(express.json());
|
||||
|
||||
// Importar os arquivos de roteamento
|
||||
const authController = require('./controllers/authController')();
|
||||
const booksController = require('./controllers/booksController')();
|
||||
const coursesController = require('./controllers/coursesController')();
|
||||
const usersController = require('./controllers/usersController')();
|
||||
|
||||
|
||||
// Usar os roteamentos
|
||||
app.use('/api/auth', authController);
|
||||
app.use('/api/books', booksController);
|
||||
app.use('/api/courses', coursesController);
|
||||
app.use('/api/users', usersController);
|
||||
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Servidor rodando em http://localhost:${port}`);
|
||||
});
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const pdf2html = require('pdf2html');
|
||||
const { exec } = require('child_process');
|
||||
|
||||
const diretorioArquivos = '/var/www/html/maker_book';
|
||||
//const pathToJar = '/var/www/logicworld_api/node_modules/pdf2html/vendor/tika-app-2.6.0.jar';
|
||||
|
||||
const salvarPDF = (arquivo, nome) => {
|
||||
console.log("Salvando arquivo...");
|
||||
const novoNome = `${nome}.pdf`;
|
||||
const diretorioDestino = path.join(diretorioArquivos, `/materials`);
|
||||
const novoCaminho = path.join(diretorioDestino, novoNome);
|
||||
|
||||
if (!fs.existsSync(diretorioDestino)){
|
||||
fs.mkdirSync(diretorioDestino, { recursive: true });
|
||||
}
|
||||
|
||||
if (arquivo.filepath) {
|
||||
try {
|
||||
fs.renameSync(arquivo.filepath, novoCaminho);
|
||||
console.log(`PDF salvo em ${novoCaminho}`);
|
||||
} catch (error) {
|
||||
console.error(`Erro ao salvar o PDF em ${novoCaminho}:`, error);
|
||||
}
|
||||
} else {
|
||||
console.error(`Tipo de arquivo não suportado ou caminho temporário ausente para ${novoCaminho}`);
|
||||
}
|
||||
console.log("PDF salvo com sucesso!");
|
||||
};
|
||||
|
||||
const exibirPDF = (arquivo, res, callback) => {
|
||||
const caminho = path.join(diretorioArquivos, 'materials', `${arquivo}.pdf`);
|
||||
console.log(`Carregando PDF de ${caminho}`);
|
||||
|
||||
fs.access(caminho, fs.constants.F_OK, (err) => {
|
||||
if (err) {
|
||||
console.error(`Erro ao acessar o arquivo: ${err}`);
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'application/pdf');
|
||||
res.setHeader('Content-Disposition', 'inline; filename=' + arquivo + '.pdf');
|
||||
fs.createReadStream(caminho).pipe(res).on('error', (streamErr) => {
|
||||
console.error(`Erro ao ler o arquivo: ${streamErr}`);
|
||||
callback(streamErr);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
async function processarPDF(arquivo) {
|
||||
try {
|
||||
const html = await exibirPDF(arquivo);
|
||||
console.log('HTML:', html);
|
||||
// Faça algo com o HTML aqui
|
||||
} catch (err) {
|
||||
console.error('Falha ao processar PDF:', err);
|
||||
// Trate o erro aqui
|
||||
}
|
||||
}
|
||||
|
||||
const salvarImagem = (imagem, nome, destino) => {
|
||||
console.log("Salvando imagem...");
|
||||
const novoNome = `${nome}.jpg`;
|
||||
const diretorioDestino = path.join(diretorioArquivos, destino);
|
||||
const novoCaminho = path.join(diretorioDestino, novoNome);
|
||||
|
||||
if (!fs.existsSync(diretorioDestino)){
|
||||
fs.mkdirSync(diretorioDestino, { recursive: true });
|
||||
}
|
||||
|
||||
if (imagem.filepath) {
|
||||
try {
|
||||
fs.renameSync(imagem.filepath, novoCaminho);
|
||||
console.log(`Imagem salva em ${novoCaminho}`);
|
||||
} catch (error) {
|
||||
console.error(`Erro ao salvar a imagem em ${novoCaminho}:`, error);
|
||||
}
|
||||
} else {
|
||||
console.error(`Tipo de arquivo não suportado ou caminho temporário ausente para ${novoCaminho}`);
|
||||
}
|
||||
console.log("Imagens salvas com sucesso!");
|
||||
};
|
||||
|
||||
const salvarImagens = (imagens, destino) => {
|
||||
console.log("Salvando imagens...");
|
||||
imagens.forEach((imagem, index) => {
|
||||
const novoNome = `${index}.jpg`;
|
||||
const diretorioDestino = path.join(diretorioArquivos, destino);
|
||||
const novoCaminho = path.join(diretorioDestino, novoNome);
|
||||
|
||||
if (!fs.existsSync(diretorioDestino)){
|
||||
fs.mkdirSync(diretorioDestino, { recursive: true });
|
||||
}
|
||||
|
||||
if (imagem.filepath) {
|
||||
try {
|
||||
fs.renameSync(imagem.filepath, novoCaminho);
|
||||
console.log(`Imagem salva em ${novoCaminho}`);
|
||||
} catch (error) {
|
||||
console.error(`Erro ao salvar a imagem em ${novoCaminho}:`, error);
|
||||
}
|
||||
} else {
|
||||
console.error(`Tipo de arquivo não suportado ou caminho temporário ausente para ${novoCaminho}`);
|
||||
}
|
||||
});
|
||||
console.log("Imagens salvas com sucesso!");
|
||||
};
|
||||
|
||||
const excluirArquivo = (caminho) => {
|
||||
console.log("Excluindo arquivo...");
|
||||
const caminhoCompleto = path.join(diretorioArquivos, caminho);
|
||||
if (fs.existsSync(caminhoCompleto)) {
|
||||
fs.unlinkSync(caminhoCompleto);
|
||||
} else {
|
||||
console.log("Arquivo não encontrado. " + caminhoCompleto);
|
||||
}
|
||||
}
|
||||
|
||||
const excluirDiretorioEArquivos = (caminho) => {
|
||||
const diretorio = path.join(diretorioArquivos, caminho);
|
||||
if (fs.existsSync(diretorio)) {
|
||||
fs.readdirSync(diretorio).forEach((arquivo) => {
|
||||
const caminhoCompleto = path.join(diretorio, arquivo);
|
||||
excluirArquivo(caminhoCompleto);
|
||||
});
|
||||
|
||||
fs.rmdirSync(diretorio);
|
||||
console.log(`Diretório ${diretorio} e seus arquivos foram excluídos.`);
|
||||
} else {
|
||||
console.log("Diretório não encontrado.");
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { salvarPDF, processarPDF, exibirPDF, salvarImagem, salvarImagens, excluirArquivo, excluirDiretorioEArquivos };
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
const normalizarFormulario = (form) => {
|
||||
// Percorra todas as chaves no objeto de formulário
|
||||
for (let key in form) {
|
||||
// Se o valor da chave for a string "null", substitua por null
|
||||
if (form[key] == "null") {
|
||||
form[key] = null;
|
||||
}
|
||||
else if (form[key] == true || form[key] == "true") {
|
||||
form[key] = 1;
|
||||
}
|
||||
else if (form[key] == false || form[key] == "false") {
|
||||
form[key] = 0;
|
||||
}
|
||||
}
|
||||
// Retorne o objeto de formulário modificado
|
||||
return form;
|
||||
};
|
||||
|
||||
module.exports = { normalizarFormulario };
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
*
|
||||
!.dockerignore
|
||||
!Dockerfile
|
||||
!dist/**
|
||||
!dist-debug/**
|
||||
!scripts/**
|
||||
!nginx.conf
|
||||
!version
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
# Only exists if Bazel was run
|
||||
/bazel-out
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events*.json
|
||||
speed-measure-plugin*.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"no-duplicate-heading": { "siblings_only": true},
|
||||
"line-length": { "line_length": 140 }
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"arrowParens": "avoid",
|
||||
"endOfLine": "lf",
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.pug",
|
||||
"options": {
|
||||
"attributeSeparator": "always",
|
||||
"parser": "pug"
|
||||
}
|
||||
}
|
||||
],
|
||||
"printWidth": 100,
|
||||
"proseWrap": "preserve",
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"validateAttributeSeparator": {
|
||||
"multiLineSeparator": ",\n ",
|
||||
"separator": ", "
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
[Glossary](/glossary)
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.3.0] - 2020-11-18
|
||||
|
||||
- [Changed] Updated to Angular 11
|
||||
- [Changed] Upgraded all dependencies
|
||||
|
||||
## [1.2.0] - 2020-09-18
|
||||
|
||||
- [Changed] New release process
|
||||
- [Changed] Upgraded all dependencies
|
||||
|
||||
## [1.1.1] - 2020-04-03
|
||||
|
||||
- [Changed] Updated dependencies in package.json
|
||||
- [Changed] TestBed.get -> TestBed.inject (Angular 9 deprecation)
|
||||
- [Changed] Only import HttpClientModule in appModule
|
||||
|
||||
## [1.1.0] - 2020-02-10
|
||||
|
||||
- [Added] Document how to use --max-old-space-size
|
||||
- [Added] Typings folder in `src/typings`
|
||||
- [Added] `.markdownlint.json`
|
||||
- [Added] @sbpro/ng - for SBPro Schematics
|
||||
- [Changed] Updated to Angular 9
|
||||
- [Changed] Updated other dependencies
|
||||
- [Changed] Docker run script to check for running container first.
|
||||
|
||||
## [1.0.0] - 2020-01-13
|
||||
|
||||
- Initial Release!
|
||||
|
||||
## Gloassary
|
||||
|
||||
- `[Added]` for new features.
|
||||
- `[Changed]` for changes in existing functionality.
|
||||
- `[Deprecated]` for soon-to-be removed features.
|
||||
- `[Removed]` for now removed features.
|
||||
- `[Fixed]` for any bug fixes.
|
||||
- `[Security]` in case of vulnerabilities.
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
FROM nginx:1.17.6
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
RUN echo "deb http://security.debian.org/debian-security stretch/updates main" > /etc/apt/sources.security.only.list
|
||||
RUN apt-get -y update -o Dir::Etc::SourceList=/etc/apt/sources.security.only.list -o Dir::Etc::Parts=/dev/null
|
||||
RUN apt-get -y upgrade -o Dir::Etc::SourceList=/etc/apt/sources.security.only.list -o Dir::Etc::Parts=/dev/nulld
|
||||
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
COPY scripts/docker/start-nginx.sh /usr/share/nginx/start-nginx.sh
|
||||
|
||||
COPY dist/sb-admin-angular /usr/share/nginx/html
|
||||
COPY version /usr/share/nginx/html/assets/version
|
||||
|
||||
ENTRYPOINT ["/usr/share/nginx/start-nginx.sh"]
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2020 Start Bootstrap LLC
|
||||
|
||||
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,100 @@
|
|||
# SB Admin Angular
|
||||
|
||||
SB Admin Angular is a free and open-sourced Bootstrap themed Angular 9 starter project.
|
||||
|
||||
It shares the same project structure and subset of tooling from our professional offering,
|
||||
[SB Admin Pro Angular](https://themes.startbootstrap.com/sb-admin-pro-angular/),
|
||||
so much of the [SB Admin Pro Angular Documentation](https://docs.startbootstrap.com/sb-admin-pro-angular/quickstart) is applicable.
|
||||
|
||||
In particular the documentation for [Structure](https://docs.startbootstrap.com/sb-admin-pro-angular/structure-root-level),
|
||||
and the documentation for [SBPro Schematics](https://docs.startbootstrap.com/sb-admin-pro-angular/development-general#sb-pro-schematics)
|
||||
|
||||
SB Admin Angular comes with a base implementation of navigation and layouts.
|
||||
|
||||
For professionally designed components (including an advanced SideNav), 100% code coverage,
|
||||
starter cypress tests and more, please consider our professional offering:
|
||||
[SB Admin Pro Angular](https://themes.startbootstrap.com/sb-admin-pro-angular/)
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
git clone git@github.com:startbootstrap/sb-admin-angular.git
|
||||
cd sb-admin-angular
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
`npm start` should open a browser window to <http://localhost:4200>
|
||||
|
||||
By default angular runs on port 4200. To change this port you can run:
|
||||
|
||||
```bash
|
||||
# This starts the development server on port 4205,
|
||||
# but you can use any port you'd like
|
||||
export PORT=4205 && npm start
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
### Unit Tests
|
||||
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
|
||||
### e2e
|
||||
|
||||
```bash
|
||||
npm run e2e
|
||||
```
|
||||
|
||||
## Production
|
||||
|
||||
SB Admin Angular come with a production ready Dockerfile and build scripts.
|
||||
|
||||
You can get Docker [here](https://www.docker.com/get-started)
|
||||
|
||||
```bash
|
||||
npm run docker:build
|
||||
npm run docker:run
|
||||
```
|
||||
|
||||
## Generate Code
|
||||
|
||||
```bash
|
||||
npm run generate:module -- --path src/modules --name Test
|
||||
npm run generate:component -- --path src/modules/test/containers --name Test
|
||||
npm run generate:component -- --path src/modules/test/components --name Test
|
||||
npm run generate:directive -- --path src/modules/test/directives --name Test
|
||||
npm run generate:service -- --path src/modules/test/services --name Test
|
||||
```
|
||||
|
||||
_Note: Creating a Component and a Container use the same command,
|
||||
the difference is just the paths and how they are used._
|
||||
|
||||
### MVCC
|
||||
|
||||
Containers and Components are both Angular Components, but used in different ways.
|
||||
|
||||
Containers should arrange Components.
|
||||
|
||||
Obviously this can become subjective, but MVCC is the paradigm that we subscribe to.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### npm start
|
||||
|
||||
If you receive memory issues adjust
|
||||
`max_old_space_size` in the `ng` command of the `package.json`:
|
||||
|
||||
```json
|
||||
"ng": "cross-env NODE_OPTIONS=--max_old_space_size=2048 ./node_modules/.bin/ng",
|
||||
```
|
||||
|
||||
You can adjust 2048 to any number you need.
|
||||
|
||||
For more information about why you may need `--max_old_space_size`
|
||||
see [this article](https://medium.com/@ashleydavis75/node-js-memory-limitations-30d3fe2664c0).
|
||||
|
||||
Keep in mind that this project only uses node to build the angular application.
|
||||
There is no production dependency on node.
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"maker-book": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "sb",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/maker-book",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": false,
|
||||
"assets": [
|
||||
"src/favicon.png",
|
||||
"src/assets",
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "node_modules/ngx-scanner-qrcode/wasm/",
|
||||
"output": "./assets/wasm/"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles/styles.scss",
|
||||
"src/styles/maker_book.scss"
|
||||
],
|
||||
"stylePreprocessorOptions": {
|
||||
"includePaths": ["src", "./node_modules"]
|
||||
},
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "maker-book:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "maker-book:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "maker-book:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": ["src/favicon.png", "src/assets"],
|
||||
"styles": [
|
||||
"src/styles/styles.scss",
|
||||
"src/styles/maker_book.scss"
|
||||
],
|
||||
"stylePreprocessorOptions": {
|
||||
"includePaths": ["src", "./node_modules"]
|
||||
},
|
||||
"scripts": []
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.spec.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "maker-book:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "maker-book:serve:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "maker-book"
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
> 0.5%
|
||||
last 2 versions
|
||||
Firefox ESR
|
||||
not dead
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
* @type { import("protractor").Config }
|
||||
*/
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: ['./src/**/*.e2e-spec.ts'],
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function() {},
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.json'),
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import { browser, logging } from 'protractor';
|
||||
|
||||
import { AppPage } from './app.po';
|
||||
|
||||
describe('workspace-project App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage();
|
||||
});
|
||||
|
||||
it('should display welcome message', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getTitleText()).toEqual('Dashboard');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Assert that there are no errors emitted from the browser
|
||||
const logs = await browser
|
||||
.manage()
|
||||
.logs()
|
||||
.get(logging.Type.BROWSER);
|
||||
expect(logs).not.toContain(
|
||||
jasmine.objectContaining({
|
||||
level: logging.Level.SEVERE,
|
||||
} as logging.Entry)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { browser, by, element } from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
navigateTo() {
|
||||
return browser.get(browser.baseUrl) as Promise<any>;
|
||||
}
|
||||
|
||||
getTitleText() {
|
||||
return element(by.css('app-root sb-dashboard-head h1')).getText() as Promise<string>;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types": ["jasmine", "jasminewd2", "node"]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma'),
|
||||
],
|
||||
client: {
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/sb-admin-angular'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true,
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true,
|
||||
});
|
||||
};
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
user nginx;
|
||||
worker_processes 1;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
keepalive_timeout 65;
|
||||
sendfile on;
|
||||
gzip on;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
location /_health {
|
||||
access_log off;
|
||||
return 200;
|
||||
}
|
||||
|
||||
location / {
|
||||
location ~ / {
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
try_files $uri /index.html =404;
|
||||
}
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
"name": "maker-book",
|
||||
"version": "1.3.0",
|
||||
"scripts": {
|
||||
"build": "npm run build:pug && npm run ng -- build --prod --build-optimizer=true --statsJson=true && npm run build:version",
|
||||
"build:debug": "npm run build:pug && npm run ng -- build --prod --source-map --build-optimizer=true --statsJson=true && npm run build:version",
|
||||
"build:pug": "node scripts/build-pug.js",
|
||||
"build:version": "node scripts/version.js",
|
||||
"bundle-report": "webpack-bundle-analyzer dist/maker-book/stats-es2015.json",
|
||||
"docker:build": "node scripts/docker/docker-build.js",
|
||||
"docker:run": "node scripts/docker/docker-run.js",
|
||||
"e2e": "npm run ng -- e2e",
|
||||
"generate:component": "./node_modules/.bin/ng generate @sbpro/ng:component",
|
||||
"generate:directive": "./node_modules/.bin/ng generate @sbpro/ng:directive",
|
||||
"generate:module": "./node_modules/.bin/ng generate @sbpro/ng:module",
|
||||
"generate:service": "./node_modules/.bin/ng generate @sbpro/ng:service",
|
||||
"lint:fix": "npm run ng -- lint --fix",
|
||||
"lint": "npm run ng -- lint",
|
||||
"ng": "cross-env NODE_OPTIONS=--max_old_space_size=2048 ./node_modules/.bin/ng",
|
||||
"serve": "./node_modules/.bin/static-server dist/maker-book",
|
||||
"start": "node scripts/start.js",
|
||||
"test": "npm run ng -- test"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "11.0.1",
|
||||
"@angular/common": "11.0.1",
|
||||
"@angular/compiler": "11.0.1",
|
||||
"@angular/core": "11.0.1",
|
||||
"@angular/forms": "11.0.1",
|
||||
"@angular/platform-browser": "11.0.1",
|
||||
"@angular/platform-browser-dynamic": "11.0.1",
|
||||
"@angular/router": "11.0.1",
|
||||
"@fortawesome/angular-fontawesome": "0.8.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.32",
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.1",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.1",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.1",
|
||||
"@ng-bootstrap/ng-bootstrap": "8.0.0",
|
||||
"@prettier/plugin-pug": "1.10.1",
|
||||
"chart.js": "2.9.4",
|
||||
"crypto-js": "^4.2.0",
|
||||
"ngx-scanner-qrcode": "^1.2.8",
|
||||
"object-hash": "2.0.3",
|
||||
"rxjs": "6.6.3",
|
||||
"tslib": "2.0.3",
|
||||
"uuid": "8.3.1",
|
||||
"webpack-bundle-analyzer": "4.1.0",
|
||||
"zone.js": "^0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "0.1100.2",
|
||||
"@angular/cli": "11.0.2",
|
||||
"@angular/compiler-cli": "11.0.1",
|
||||
"@angular/language-service": "11.0.1",
|
||||
"@inip/static-server": "1.0.1",
|
||||
"@sbpro/ng": "1.4.2",
|
||||
"@types/chart.js": "2.9.28",
|
||||
"@types/jasmine": "3.6.1",
|
||||
"@types/jasminewd2": "2.0.8",
|
||||
"@types/node": "14.14.8",
|
||||
"@types/object-hash": "1.3.4",
|
||||
"@types/uuid": "8.3.0",
|
||||
"bootstrap": "4.5.3",
|
||||
"chokidar": "3.4.3",
|
||||
"codelyzer": "6.0.1",
|
||||
"concurrently": "5.3.0",
|
||||
"cross-env": "7.0.2",
|
||||
"jasmine-core": "3.6.0",
|
||||
"jasmine-spec-reporter": "6.0.0",
|
||||
"karma": "5.2.3",
|
||||
"karma-chrome-launcher": "3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "3.0.3",
|
||||
"karma-jasmine": "4.0.1",
|
||||
"karma-jasmine-html-reporter": "1.5.4",
|
||||
"prettier": "2.1.2",
|
||||
"protractor": "7.0.0",
|
||||
"pug": "3.0.0",
|
||||
"pug-lint": "2.6.0",
|
||||
"shelljs": "0.8.4",
|
||||
"ts-node": "9.0.0",
|
||||
"tslint": "6.1.3",
|
||||
"tslint-plugin-prettier": "2.3.0",
|
||||
"typescript": "4.0.5"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
'use strict';
|
||||
const path = require('path');
|
||||
const sh = require('shelljs');
|
||||
const renderPug = require('./render-pug');
|
||||
|
||||
const srcPath = path.resolve(path.dirname(__filename), '../src');
|
||||
|
||||
sh.find(srcPath).forEach(_processFile);
|
||||
|
||||
function _processFile(filePath) {
|
||||
if (
|
||||
filePath.match(/\.pug$/)
|
||||
&& !filePath.match(/pug_include/)
|
||||
) {
|
||||
renderPug(filePath);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
const version = require('../version.js');
|
||||
const sh = require('shelljs');
|
||||
|
||||
const imageName = 'sb-admin-angular';
|
||||
|
||||
sh.exec(`docker build -t ${imageName}:latest -t ${imageName}:${version} .`);
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
const angularJSON = require('../../angular.json');
|
||||
const sh = require('shelljs');
|
||||
|
||||
const imageName = angularJSON.defaultProject;
|
||||
const isRunning = sh.exec(`docker ps -a -q -f name=${imageName}`, {silent: true}).stdout;
|
||||
const PORT = '4400';
|
||||
|
||||
if (isRunning) {
|
||||
sh.exec(`docker rm -f ${imageName}`);
|
||||
}
|
||||
|
||||
sh.exec(`docker run -d --name ${imageName} -p ${PORT}:80 ${imageName}:latest`);
|
||||
sh.exec(`docker ps`);
|
||||
|
||||
console.log(`\n\n### INFO: ${imageName} is running at:\n\n\thttp://localhost:${PORT}\n`);
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
nginx -g 'daemon off;'
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const chokidar = require('chokidar');
|
||||
const renderPug = require('./render-pug');
|
||||
|
||||
const watcher = chokidar.watch('src', {
|
||||
persistent: true,
|
||||
});
|
||||
|
||||
process.title = 'pug-watch';
|
||||
|
||||
let allFiles = {};
|
||||
|
||||
watcher.on('add', filePath => _processFile(filePath, 'add'));
|
||||
watcher.on('change', filePath => _processFile(filePath, 'change'));
|
||||
|
||||
function _processFile(filePath, watchEvent) {
|
||||
|
||||
if (filePath.match(/\.pug$/)) {
|
||||
return _handlePug(filePath, watchEvent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _handlePug(filePath, watchEvent) {
|
||||
|
||||
if (watchEvent === 'change') {
|
||||
if (filePath.match(/pug_include/)) {
|
||||
return _renderAllPug();
|
||||
}
|
||||
return renderPug(filePath);
|
||||
}
|
||||
if (!filePath.match(/pug_include/)) {
|
||||
allFiles[filePath] = true;
|
||||
return renderPug(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
function _renderAllPug() {
|
||||
console.log('### INFO: Rendering All');
|
||||
_.each(allFiles, (value, filePath) => {
|
||||
renderPug(filePath);
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
const fs = require('fs');
|
||||
const pug = require('pug');
|
||||
const prettier = require('prettier');
|
||||
|
||||
module.exports = function renderPug(filePath) {
|
||||
|
||||
console.log(`### INFO: Rendering ${filePath}`);
|
||||
const html = pug.renderFile(filePath, {
|
||||
doctype: 'html',
|
||||
filename: filePath,
|
||||
});
|
||||
|
||||
const prettified = prettier.format(html, {
|
||||
printWidth: 1000,
|
||||
tabWidth: 4,
|
||||
singleQuote: true,
|
||||
proseWrap: 'preserve',
|
||||
endOfLine: 'lf',
|
||||
parser: 'html'
|
||||
});
|
||||
|
||||
fs.writeFileSync(filePath.replace(/\.pug$/, '.html'), prettified);
|
||||
|
||||
};
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
const concurrently = require('concurrently');
|
||||
const port = process.env.PORT || 4200;
|
||||
|
||||
concurrently([
|
||||
{ command: 'node scripts/pug-watch.js', name: 'PUG_WATCH', prefixColor: 'bgGreen.bold' },
|
||||
{
|
||||
command: `npm run ng -- serve --port ${port} --open`,
|
||||
name: 'NG_SERVE',
|
||||
prefixColor: 'bgBlue.bold',
|
||||
}
|
||||
], {
|
||||
prefix: 'name',
|
||||
killOthers: ['failure', 'success'],
|
||||
}).then(success, failure);
|
||||
|
||||
function success() {
|
||||
console.log('Success');
|
||||
}
|
||||
|
||||
function failure() {
|
||||
console.log('Failure');
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const pjPath = path.resolve(path.dirname(__filename), '../package.json');
|
||||
const versionPath = path.resolve(path.dirname(__filename), '../version');
|
||||
const distVersionPath = path.resolve(path.dirname(__filename), '../dist/sb-admin-angular/assets/version');
|
||||
|
||||
const pj = require(pjPath);
|
||||
|
||||
console.log(`### INFO: Current Version: ${pj.version}`);
|
||||
fs.writeFileSync(versionPath, pj.version);
|
||||
fs.writeFileSync(distVersionPath, pj.version);
|
||||
|
||||
module.exports = pj.version;
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
redirectTo: '/auth2/login',
|
||||
},
|
||||
{
|
||||
path: 'charts',
|
||||
loadChildren: () =>
|
||||
import('modules/charts/charts-routing.module').then(m => m.ChartsRoutingModule),
|
||||
},
|
||||
{
|
||||
path: 'dashboard',
|
||||
loadChildren: () =>
|
||||
import('modules/dashboard/dashboard-routing.module').then(
|
||||
m => m.DashboardRoutingModule
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'auth',
|
||||
loadChildren: () =>
|
||||
import('modules/auth/auth-routing.module').then(m => m.AuthRoutingModule),
|
||||
},
|
||||
{
|
||||
path: 'error',
|
||||
loadChildren: () =>
|
||||
import('modules/error/error-routing.module').then(m => m.ErrorRoutingModule),
|
||||
},
|
||||
{
|
||||
path: 'tables',
|
||||
loadChildren: () =>
|
||||
import('modules/tables/tables-routing.module').then(m => m.TablesRoutingModule),
|
||||
},
|
||||
{
|
||||
path: 'version',
|
||||
loadChildren: () =>
|
||||
import('modules/utility/utility-routing.module').then(m => m.UtilityRoutingModule),
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
path: 'auth2',
|
||||
loadChildren: () =>
|
||||
import('pages/auth/auth-routing.module').then(m => m.AuthRoutingModule),
|
||||
},
|
||||
{
|
||||
path: 'users',
|
||||
loadChildren: () =>
|
||||
import('pages/users/users-routing.module').then(m => m.UsersRoutingModule),
|
||||
},
|
||||
{
|
||||
path: 'courses',
|
||||
loadChildren: () =>
|
||||
import('pages/courses/courses-routing.module').then(m => m.CoursesRoutingModule),
|
||||
},
|
||||
{
|
||||
path: 'classes',
|
||||
loadChildren: () =>
|
||||
import('pages/classes/classes-routing.module').then(m => m.ClassesRoutingModule),
|
||||
},
|
||||
{
|
||||
path: 'books',
|
||||
loadChildren: () =>
|
||||
import('pages/books/books-routing.module').then(m => m.BooksRoutingModule),
|
||||
},
|
||||
{
|
||||
path: 'student',
|
||||
loadChildren: () =>
|
||||
import('pages/doing-class/doing-class-routing.module').then(m => m.DoingClassRoutingModule),
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
path: '**',
|
||||
pathMatch: 'full',
|
||||
loadChildren: () =>
|
||||
import('modules/error/error-routing.module').then(m => m.ErrorRoutingModule),
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AppRoutingModule {}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<router-outlet></router-outlet>
|
||||
|
|
@ -0,0 +1 @@
|
|||
router-outlet
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [RouterTestingModule],
|
||||
declarations: [AppComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'sb-admin-angular'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app.title).toEqual('sb-admin-angular');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { ChildActivationEnd, Router } from '@angular/router';
|
||||
import { TranslationsService } from 'pages/general/services';
|
||||
import { filter } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'maker-book';
|
||||
|
||||
constructor(
|
||||
public router: Router,
|
||||
private titleService: Title,
|
||||
public translationsService: TranslationsService,
|
||||
) {
|
||||
this.router.events
|
||||
.pipe(filter(event => event instanceof ChildActivationEnd))
|
||||
.subscribe(event => {
|
||||
let snapshot = (event as ChildActivationEnd).snapshot;
|
||||
while (snapshot.firstChild !== null) {
|
||||
snapshot = snapshot.firstChild;
|
||||
}
|
||||
this.titleService.setTitle(snapshot.data.title);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { FaIconLibrary } from '@fortawesome/angular-fontawesome';
|
||||
import { faCamera, faEdit, faMinus, faPlus, faTrash, faWindowClose } from '@fortawesome/free-solid-svg-icons';
|
||||
import { TranslationsService } from 'pages/general/services';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
imports: [BrowserModule, AppRoutingModule, HttpClientModule],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: (translationService: TranslationsService) => () => translationService.changeLanguage(localStorage.getItem("locale") || 'en'), // Idioma padrão
|
||||
deps: [TranslationsService],
|
||||
multi: true
|
||||
}
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {
|
||||
constructor(library: FaIconLibrary) {
|
||||
library.addIcons(faPlus, faMinus, faTrash, faEdit, faCamera, faWindowClose);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"type_access_key": "Type your access key",
|
||||
"text_read_qrcode": "To access, use your camera to read the QR Code or enter the access key",
|
||||
"btn_read_qrcode": "READ QR CODE",
|
||||
"login_fail": "User and/or password wrong",
|
||||
"btn_login": "LOGIN",
|
||||
"btn_logout": "LOGOUT",
|
||||
"sure_leave": "Are you sure wanna leave?",
|
||||
"btn_yes": "YES",
|
||||
"btn_no": "NO",
|
||||
"text_what_name": "What is your name?",
|
||||
"text_type_name": "Type your name...",
|
||||
"btn_save": "SAVE",
|
||||
"btn_read_book": "READ THE BOOK",
|
||||
"btn_back": "Go Back",
|
||||
"lesson": "LESSON",
|
||||
"no_classes": "There are currently no classes available for this book.",
|
||||
"loading": "LOADING..."
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"type_access_key": "Digite sua chave de acesso",
|
||||
"text_read_qrcode": "Para acessar, utilize sua câmera para ler o QR Code ou digite a chave de acesso",
|
||||
"btn_read_qrcode": "LER QR CODE",
|
||||
"login_fail": "Usuário e/ou senha incorretos",
|
||||
"btn_login": "ENTRAR",
|
||||
"btn_logout": "SAIR",
|
||||
"sure_leave": "Tem certeza que deseja sair?",
|
||||
"btn_yes": "SIM",
|
||||
"btn_no": "NÃO",
|
||||
"text_what_name": "Qual é seu nome?",
|
||||
"text_type_name": "Digite seu nome...",
|
||||
"btn_save": "SALVAR",
|
||||
"btn_read_book": "LER O LIVRO",
|
||||
"btn_back": "Voltar",
|
||||
"lesson": "LIÇÃO",
|
||||
"no_classes": "No momento não existem aulas disponíveis para esse livro.",
|
||||
"loading": "CARREGANDO..."
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.0 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 0H2C0.9 0 0 0.9 0 2V14C0 15.1 0.9 16 2 16H14C15.1 16 16 15.1 16 14V2C16 0.9 15.1 0 14 0ZM12 9H4C3.45 9 3 8.55 3 8C3 7.45 3.45 7 4 7H12C12.55 7 13 7.45 13 8C13 8.55 12.55 9 12 9ZM8 13H4C3.45 13 3 12.55 3 12C3 11.45 3.45 11 4 11H8C8.55 11 9 11.45 9 12C9 12.55 8.55 13 8 13ZM12 5H4C3.45 5 3 4.55 3 4C3 3.45 3.45 3 4 3H12C12.55 3 13 3.45 13 4C13 4.55 12.55 5 12 5Z" fill="#B6166E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 495 B |
|
|
@ -0,0 +1,17 @@
|
|||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M30 4.125C27.0625 2.3125 23.6875 1.25 20 1.25V4.125H30Z" fill="#ED4C5C"/>
|
||||
<path d="M20 7H33.5C32.4375 5.9375 31.25 4.9375 30 4.125H20V7Z" fill="white"/>
|
||||
<path d="M20 9.875H35.8125C35.125 8.8125 34.375 7.875 33.5625 7H20V9.875Z" fill="#ED4C5C"/>
|
||||
<path d="M20 12.75H37.3125C36.875 11.75 36.375 10.75 35.8125 9.875H20V12.75Z" fill="white"/>
|
||||
<path d="M20 15.625H38.25C38 14.625 37.6875 13.6875 37.3125 12.75H20V15.625Z" fill="#ED4C5C"/>
|
||||
<path d="M20 18.5625H38.6875C38.625 17.5625 38.4375 16.625 38.25 15.6875H20V18.5625Z" fill="white"/>
|
||||
<path d="M38.6875 18.5625H20V20H1.25C1.25 20.5 1.25 20.9375 1.3125 21.4375H38.6875C38.75 20.9375 38.75 20.5 38.75 20C38.75 19.5 38.75 19 38.6875 18.5625Z" fill="#ED4C5C"/>
|
||||
<path d="M1.75 24.3125H38.25C38.5 23.375 38.625 22.4375 38.6875 21.4375H1.3125C1.375 22.375 1.5 23.375 1.75 24.3125Z" fill="white"/>
|
||||
<path d="M2.6875 27.1875H37.3125C37.6875 26.25 38 25.3125 38.25 24.3125H1.75C2 25.3125 2.3125 26.25 2.6875 27.1875Z" fill="#ED4C5C"/>
|
||||
<path d="M4.1875 30.0625H35.8125C36.375 29.125 36.875 28.1875 37.3125 27.1875H2.6875C3.125 28.1875 3.625 29.125 4.1875 30.0625Z" fill="white"/>
|
||||
<path d="M6.4375 32.9375H33.5625C34.375 32.0625 35.1875 31.0625 35.8125 30.0625H4.1875C4.8125 31.125 5.625 32.0625 6.4375 32.9375Z" fill="#ED4C5C"/>
|
||||
<path d="M9.9375 35.8125H30.0625C31.375 35 32.5 34 33.5625 32.9375H6.4375C7.5 34.0625 8.6875 35 9.9375 35.8125Z" fill="white"/>
|
||||
<path d="M20 38.75C23.6875 38.75 27.125 37.6875 30.0625 35.8125H9.9375C12.875 37.6875 16.3125 38.75 20 38.75Z" fill="#ED4C5C"/>
|
||||
<path d="M10 4.125C8.6875 4.9375 7.5 5.9375 6.4375 7C5.5625 7.875 4.8125 8.875 4.1875 9.875C3.625 10.8125 3.0625 11.75 2.6875 12.75C2.3125 13.6875 2 14.625 1.75 15.625C1.5 16.5625 1.375 17.5 1.3125 18.5C1.25 19 1.25 19.5 1.25 20H20V1.25C16.3125 1.25 12.9375 2.3125 10 4.125Z" fill="#428BC1"/>
|
||||
<path d="M15.625 1.875L15.9375 2.8125H16.875L16.125 3.4375L16.375 4.375L15.625 3.8125L14.875 4.375L15.125 3.4375L14.375 2.8125H15.3125L15.625 1.875ZM18.125 5.625L18.4375 6.5625H19.375L18.625 7.1875L18.875 8.125L18.125 7.5625L17.375 8.125L17.625 7.1875L16.875 6.5625H17.8125L18.125 5.625ZM13.125 5.625L13.4375 6.5625H14.375L13.625 7.1875L13.875 8.125L13.125 7.5625L12.375 8.125L12.625 7.1875L11.875 6.5625H12.8125L13.125 5.625ZM15.625 9.375L15.9375 10.3125H16.875L16.125 10.9375L16.375 11.875L15.625 11.3125L14.875 11.875L15.125 10.9375L14.375 10.3125H15.3125L15.625 9.375ZM10.625 9.375L10.9375 10.3125H11.875L11.125 10.9375L11.375 11.875L10.625 11.3125L9.875 11.875L10.125 10.9375L9.375 10.3125H10.3125L10.625 9.375ZM5.625 9.375L5.9375 10.3125H6.875L6.125 10.9375L6.375 11.875L5.625 11.3125L4.875 11.875L5.125 10.9375L4.375 10.3125H5.3125L5.625 9.375ZM18.125 13.125L18.4375 14.0625H19.375L18.625 14.6875L18.875 15.625L18.125 15.0625L17.375 15.625L17.625 14.6875L16.875 14.0625H17.8125L18.125 13.125ZM13.125 13.125L13.4375 14.0625H14.375L13.625 14.6875L13.875 15.625L13.125 15.0625L12.375 15.625L12.625 14.6875L11.875 14.0625H12.8125L13.125 13.125ZM8.125 13.125L8.4375 14.0625H9.375L8.625 14.6875L8.875 15.625L8.125 15.0625L7.375 15.625L7.625 14.6875L6.875 14.0625H7.8125L8.125 13.125ZM15.625 16.875L15.9375 17.8125H16.875L16.125 18.4375L16.375 19.375L15.625 18.8125L14.875 19.375L15.125 18.4375L14.375 17.8125H15.3125L15.625 16.875ZM10.625 16.875L10.9375 17.8125H11.875L11.125 18.4375L11.375 19.375L10.625 18.8125L9.875 19.375L10.125 18.4375L9.375 17.8125H10.3125L10.625 16.875ZM5.625 16.875L5.9375 17.8125H6.875L6.125 18.4375L6.375 19.375L5.625 18.8125L4.875 19.375L5.125 18.4375L4.375 17.8125H5.3125L5.625 16.875ZM7.375 8.125L8.125 7.5625L8.875 8.125L8.5625 7.1875L9.3125 6.5625H8.375L8.125 5.625L7.8125 6.5625H6.9375L7.6875 7.125L7.375 8.125ZM2.375 15.625L3.125 15.0625L3.875 15.625L3.5625 14.6875L4.3125 14.0625H3.4375L3.125 13.125L2.8125 14.0625H2.1875C2.1875 14.125 2.125 14.1875 2.125 14.25L2.625 14.625L2.375 15.625Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="100" height="80" viewBox="0 0 100 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_58_209)">
|
||||
<path d="M75.0111 15H25.0002C20.8782 14.9994 16.82 16.0181 13.1869 17.9652C9.55378 19.9124 6.45852 22.7277 4.17667 26.1605C1.89482 29.5933 0.497172 33.5371 0.108166 37.6407C-0.28084 41.7443 0.350863 45.8804 1.94704 49.6808C3.54321 53.4812 6.05432 56.828 9.25682 59.4232C12.4593 62.0183 16.2538 63.7814 20.3025 64.5554C24.3512 65.3293 28.5284 65.0902 32.4623 63.8592C36.3963 62.6283 39.9648 60.4437 42.8502 57.5H57.1502C60.0352 60.4434 63.6033 62.6278 67.5367 63.8588C71.4701 65.0898 75.6468 65.3293 79.6951 64.5559C83.7435 63.7824 87.5378 62.0201 90.7403 59.4258C93.9429 56.8314 96.4543 53.4855 98.0512 49.6859C99.648 45.8862 100.281 41.7507 99.8928 37.6475C99.505 33.5442 98.1087 29.6005 95.8282 26.1674C93.5477 22.7342 90.4538 19.9181 86.8219 17.9698C83.1899 16.0214 79.1326 15.0012 75.0111 15ZM38.7502 41.875C38.7502 42.3723 38.5526 42.8492 38.201 43.2008C37.8494 43.5525 37.3724 43.75 36.8752 43.75H28.7502V51.875C28.7502 52.3723 28.5526 52.8492 28.201 53.2008C27.8494 53.5525 27.3724 53.75 26.8752 53.75H23.1252C22.6279 53.75 22.151 53.5525 21.7993 53.2008C21.4477 52.8492 21.2502 52.3723 21.2502 51.875V43.75H13.1252C12.6279 43.75 12.151 43.5525 11.7993 43.2008C11.4477 42.8492 11.2502 42.3723 11.2502 41.875V38.125C11.2502 37.6277 11.4477 37.1508 11.7993 36.7992C12.151 36.4475 12.6279 36.25 13.1252 36.25H21.2502V28.125C21.2502 27.6277 21.4477 27.1508 21.7993 26.7992C22.151 26.4475 22.6279 26.25 23.1252 26.25H26.8752C27.3724 26.25 27.8494 26.4475 28.201 26.7992C28.5526 27.1508 28.7502 27.6277 28.7502 28.125V36.25H36.8752C37.3724 36.25 37.8494 36.4475 38.201 36.7992C38.5526 37.1508 38.7502 37.6277 38.7502 38.125V41.875ZM72.5002 53.75C71.264 53.75 70.0557 53.3834 69.0278 52.6967C68 52.0099 67.199 51.0338 66.7259 49.8918C66.2529 48.7497 66.1291 47.4931 66.3703 46.2807C66.6114 45.0683 67.2067 43.9547 68.0807 43.0806C68.9548 42.2065 70.0685 41.6112 71.2809 41.3701C72.4932 41.1289 73.7499 41.2527 74.8919 41.7258C76.034 42.1988 77.0101 42.9999 77.6969 44.0277C78.3836 45.0555 78.7502 46.2639 78.7502 47.5C78.7502 49.1576 78.0917 50.7473 76.9196 51.9194C75.7475 53.0915 74.1578 53.75 72.5002 53.75ZM82.5002 38.75C81.264 38.75 80.0557 38.3834 79.0279 37.6967C78 37.0099 77.199 36.0338 76.7259 34.8918C76.2529 33.7497 76.1291 32.4931 76.3703 31.2807C76.6114 30.0683 77.2067 28.9547 78.0807 28.0806C78.9548 27.2065 80.0685 26.6112 81.2809 26.3701C82.4932 26.1289 83.7499 26.2527 84.8919 26.7258C86.034 27.1988 87.0101 27.9999 87.6969 29.0277C88.3836 30.0555 88.7502 31.2639 88.7502 32.5C88.7502 34.1576 88.0917 35.7473 86.9196 36.9194C85.7475 38.0915 84.1578 38.75 82.5002 38.75Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_58_209">
|
||||
<rect width="100" height="80" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="20" height="16" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_297_214)">
|
||||
<path d="M15.0029 2.66666H5.00072C4.17632 2.66655 3.36468 2.87028 2.63806 3.25971C1.91144 3.64915 1.29239 4.21221 0.836018 4.89877C0.379647 5.58533 0.100118 6.37408 0.0223168 7.1948C-0.0554845 8.01552 0.0708561 8.84274 0.390091 9.60283C0.709325 10.3629 1.21155 11.0323 1.85205 11.5513C2.49255 12.0703 3.25145 12.4229 4.06118 12.5777C4.87092 12.7325 5.70637 12.6847 6.49315 12.4385C7.27994 12.1923 7.99364 11.7554 8.57072 11.1667H11.4307C12.0077 11.7553 12.7213 12.1922 13.508 12.4384C14.2947 12.6846 15.13 12.7325 15.9397 12.5778C16.7494 12.4232 17.5082 12.0707 18.1487 11.5518C18.7893 11.0329 19.2916 10.3638 19.6109 9.60384C19.9303 8.84391 20.0568 8.01681 19.9792 7.19616C19.9017 6.37551 19.6224 5.58677 19.1663 4.90014C18.7102 4.21351 18.0914 3.65029 17.3651 3.26062C16.6387 2.87095 15.8272 2.66691 15.0029 2.66666ZM7.75072 8.04166C7.75072 8.14112 7.71121 8.2365 7.64088 8.30683C7.57055 8.37716 7.47517 8.41666 7.37572 8.41666H5.75072V10.0417C5.75072 10.1411 5.71121 10.2365 5.64088 10.3068C5.57055 10.3772 5.47517 10.4167 5.37572 10.4167H4.62572C4.52626 10.4167 4.43088 10.3772 4.36055 10.3068C4.29022 10.2365 4.25072 10.1411 4.25072 10.0417V8.41666H2.62572C2.52626 8.41666 2.43088 8.37716 2.36055 8.30683C2.29022 8.2365 2.25072 8.14112 2.25072 8.04166V7.29166C2.25072 7.19221 2.29022 7.09683 2.36055 7.0265C2.43088 6.95617 2.52626 6.91666 2.62572 6.91666H4.25072V5.29166C4.25072 5.19221 4.29022 5.09683 4.36055 5.0265C4.43088 4.95617 4.52626 4.91666 4.62572 4.91666H5.37572C5.47517 4.91666 5.57055 4.95617 5.64088 5.0265C5.71121 5.09683 5.75072 5.19221 5.75072 5.29166V6.91666H7.37572C7.47517 6.91666 7.57055 6.95617 7.64088 7.0265C7.71121 7.09683 7.75072 7.19221 7.75072 7.29166V8.04166ZM14.5007 10.4167C14.2535 10.4167 14.0118 10.3434 13.8063 10.206C13.6007 10.0686 13.4405 9.87343 13.3459 9.64502C13.2513 9.41661 13.2265 9.16528 13.2747 8.9228C13.323 8.68032 13.442 8.4576 13.6168 8.28278C13.7916 8.10797 14.0144 7.98891 14.2569 7.94068C14.4993 7.89245 14.7507 7.91721 14.9791 8.01182C15.2075 8.10642 15.4027 8.26664 15.5401 8.4722C15.6774 8.67776 15.7507 8.91944 15.7507 9.16666C15.7507 9.49818 15.619 9.81613 15.3846 10.0505C15.1502 10.285 14.8322 10.4167 14.5007 10.4167ZM16.5007 7.41666C16.2535 7.41666 16.0118 7.34335 15.8063 7.206C15.6007 7.06865 15.4405 6.87343 15.3459 6.64502C15.2513 6.41661 15.2265 6.16528 15.2747 5.9228C15.323 5.68032 15.442 5.4576 15.6168 5.28278C15.7916 5.10797 16.0144 4.98891 16.2569 4.94068C16.4993 4.89245 16.7507 4.91721 16.9791 5.01181C17.2075 5.10642 17.4027 5.26664 17.5401 5.4722C17.6774 5.67776 17.7507 5.91944 17.7507 6.16666C17.7507 6.49818 17.619 6.81613 17.3846 7.05055C17.1502 7.28497 16.8322 7.41666 16.5007 7.41666Z" fill="#99C538"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_297_214">
|
||||
<rect width="20" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="800px" height="800px" viewBox="0 0 96.943 96.943"
|
||||
xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M61.168,83.92H11.364V13.025H61.17c1.104,0,2-0.896,2-2V3.66c0-1.104-0.896-2-2-2H2c-1.104,0-2,0.896-2,2v89.623
|
||||
c0,1.104,0.896,2,2,2h59.168c1.105,0,2-0.896,2-2V85.92C63.168,84.814,62.274,83.92,61.168,83.92z"/>
|
||||
<path d="M96.355,47.058l-26.922-26.92c-0.75-0.751-2.078-0.75-2.828,0l-6.387,6.388c-0.781,0.781-0.781,2.047,0,2.828
|
||||
l12.16,12.162H19.737c-1.104,0-2,0.896-2,2v9.912c0,1.104,0.896,2,2,2h52.644L60.221,67.59c-0.781,0.781-0.781,2.047,0,2.828
|
||||
l6.387,6.389c0.375,0.375,0.885,0.586,1.414,0.586c0.531,0,1.039-0.211,1.414-0.586l26.922-26.92
|
||||
c0.375-0.375,0.586-0.885,0.586-1.414C96.943,47.941,96.73,47.433,96.355,47.058z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="66" height="88" viewBox="0 0 66 88" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_58_205)">
|
||||
<path d="M12.7657 7.35136C10.2492 5.81448 7.08648 5.76381 4.51893 7.19936C1.95137 8.63491 0.353027 11.3371 0.353027 14.2758V73.7243C0.353027 76.6629 1.95137 79.3651 4.51893 80.8007C7.08648 82.2362 10.2492 82.1687 12.7657 80.6487L61.7363 50.9244C64.1678 49.4551 65.6471 46.8373 65.6471 44C65.6471 41.1627 64.1678 38.5618 61.7363 37.0756L12.7657 7.35136Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_58_205">
|
||||
<rect width="65.2941" height="86.4706" fill="white" transform="translate(0.353027 0.764648)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 663 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="12" height="16" viewBox="0 0 12 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.28125 1.21876C1.81875 0.934388 1.2375 0.925013 0.765625 1.19064C0.29375 1.45626 0 1.95626 0 2.50001V13.5C0 14.0438 0.29375 14.5438 0.765625 14.8094C1.2375 15.075 1.81875 15.0625 2.28125 14.7813L11.2812 9.28126C11.7281 9.00939 12 8.52501 12 8.00001C12 7.47501 11.7281 6.99376 11.2812 6.71876L2.28125 1.21876Z" fill="#B6166E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 441 B |
|
|
@ -0,0 +1,15 @@
|
|||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M38.4375 16.75C36.9375 7.9375 29.25 1.25 20 1.25C10.75 1.25 3.0625 7.9375 1.5625 16.75L20 7.5L38.4375 16.75ZM1.5625 23.25C3.0625 32.0625 10.75 38.75 20 38.75C29.25 38.75 36.9375 32.0625 38.4375 23.25L20 32.5L1.5625 23.25Z" fill="#699635"/>
|
||||
<path d="M20 7.5L1.5625 16.75C1.375 17.8125 1.25 18.875 1.25 20C1.25 21.125 1.375 22.1875 1.5625 23.25L20 32.5L38.4375 23.25C38.625 22.1875 38.75 21.125 38.75 20C38.75 18.875 38.625 17.8125 38.4375 16.75L20 7.5Z" fill="#FFE62E"/>
|
||||
<path d="M16.25 17.75C14.25 17.75 12.375 18.1875 10.6875 18.9375C10.625 19.3125 10.625 19.625 10.625 20C10.625 25.1875 14.8125 29.375 20 29.375C23.5 29.375 26.5625 27.4375 28.1875 24.625C25.875 20.5625 21.375 17.75 16.25 17.75Z" fill="#428BC1"/>
|
||||
<path d="M29.25 21.5C29.3125 21 29.375 20.5 29.375 20C29.375 14.8125 25.1875 10.625 20 10.625C16.3125 10.625 13.125 12.75 11.5625 15.875C13.0625 15.4375 14.625 15.1875 16.25 15.1875C21.5625 15.1875 26.25 17.6875 29.25 21.5Z" fill="#428BC1"/>
|
||||
<path d="M16.25 15.1875C14.625 15.1875 13.0625 15.4375 11.5625 15.875C11.125 16.8125 10.8125 17.8125 10.6875 18.9375C12.375 18.1875 14.25 17.75 16.25 17.75C21.375 17.75 25.875 20.5 28.1875 24.5625C28.75 23.625 29.0625 22.5625 29.25 21.5C26.25 17.6875 21.5625 15.1875 16.25 15.1875Z" fill="white"/>
|
||||
<path d="M13.75 20.625C14.0952 20.625 14.375 20.3452 14.375 20C14.375 19.6548 14.0952 19.375 13.75 19.375C13.4048 19.375 13.125 19.6548 13.125 20C13.125 20.3452 13.4048 20.625 13.75 20.625Z" fill="white"/>
|
||||
<path d="M16.25 24.375C16.5952 24.375 16.875 24.0952 16.875 23.75C16.875 23.4048 16.5952 23.125 16.25 23.125C15.9048 23.125 15.625 23.4048 15.625 23.75C15.625 24.0952 15.9048 24.375 16.25 24.375Z" fill="white"/>
|
||||
<path d="M20 24.375C20.3452 24.375 20.625 24.0952 20.625 23.75C20.625 23.4048 20.3452 23.125 20 23.125C19.6548 23.125 19.375 23.4048 19.375 23.75C19.375 24.0952 19.6548 24.375 20 24.375Z" fill="white"/>
|
||||
<path d="M20 26.875C20.3452 26.875 20.625 26.5952 20.625 26.25C20.625 25.9048 20.3452 25.625 20 25.625C19.6548 25.625 19.375 25.9048 19.375 26.25C19.375 26.5952 19.6548 26.875 20 26.875Z" fill="white"/>
|
||||
<path d="M25 24.375C25.3452 24.375 25.625 24.0952 25.625 23.75C25.625 23.4048 25.3452 23.125 25 23.125C24.6548 23.125 24.375 23.4048 24.375 23.75C24.375 24.0952 24.6548 24.375 25 24.375Z" fill="white"/>
|
||||
<path d="M25 26.875C25.3452 26.875 25.625 26.5952 25.625 26.25C25.625 25.9048 25.3452 25.625 25 25.625C24.6548 25.625 24.375 25.9048 24.375 26.25C24.375 26.5952 24.6548 26.875 25 26.875Z" fill="white"/>
|
||||
<path d="M22.5 25.625C22.8452 25.625 23.125 25.3452 23.125 25C23.125 24.6548 22.8452 24.375 22.5 24.375C22.1548 24.375 21.875 24.6548 21.875 25C21.875 25.3452 22.1548 25.625 22.5 25.625Z" fill="white"/>
|
||||
<path d="M13.75 23.125C14.0952 23.125 14.375 22.8452 14.375 22.5C14.375 22.1548 14.0952 21.875 13.75 21.875C13.4048 21.875 13.125 22.1548 13.125 22.5C13.125 22.8452 13.4048 23.125 13.75 23.125Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.2418 2.27813L4.87617 3.375H2.25C1.00898 3.375 0 4.38398 0 5.625V14.625C0 15.866 1.00898 16.875 2.25 16.875H15.75C16.991 16.875 18 15.866 18 14.625V5.625C18 4.38398 16.991 3.375 15.75 3.375H13.1238L12.7582 2.27813C12.5297 1.58906 11.8863 1.125 11.1586 1.125H6.84141C6.11367 1.125 5.47031 1.58906 5.2418 2.27813ZM9 6.75C9.89511 6.75 10.7536 7.10558 11.3865 7.73851C12.0194 8.37145 12.375 9.22989 12.375 10.125C12.375 11.0201 12.0194 11.8786 11.3865 12.5115C10.7536 13.1444 9.89511 13.5 9 13.5C8.10489 13.5 7.24645 13.1444 6.61351 12.5115C5.98058 11.8786 5.625 11.0201 5.625 10.125C5.625 9.22989 5.98058 8.37145 6.61351 7.73851C7.24645 7.10558 8.10489 6.75 9 6.75Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 793 B |
|
After Width: | Height: | Size: 23 KiB |
|
|
@ -0,0 +1,2 @@
|
|||
// This file will be replaced at build time
|
||||
x.x.x
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
export const environment = {
|
||||
production: true,
|
||||
api_endpoint: "https://makerbook.mooo.com/api",
|
||||
currentUser: localStorage.getItem("currentUser") ? JSON.parse(localStorage.getItem("currentUser") || "") : null,
|
||||
titleProject: "Maker Book",
|
||||
defaultImageError: "https://static.vecteezy.com/system/resources/previews/005/337/799/non_2x/icon-image-not-found-free-vector.jpg",
|
||||
translations: <any>{},
|
||||
secretKey: "4f3c9d8a7b6e5d4c3b2a1f0e9d8c7b6a5e4d3c2b1a0f9e8d7c6b5a4e3d2c1b0"
|
||||
};
|
||||
|
||||
export const GlobalFunctions = {
|
||||
validaForm(form: any, campos: string[], validacoes: any[]) : (boolean) {
|
||||
let campoInvalido: string = "";
|
||||
for (let i = 0; i < campos.length; i++) {
|
||||
let campo = campos[i];
|
||||
validacoes.forEach(val => {
|
||||
if (form[campo] == val) {
|
||||
campoInvalido = campo;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (campoInvalido != "") {
|
||||
alert("Preencha corretamente os campos para salvar! " + campoInvalido);
|
||||
}
|
||||
|
||||
return campoInvalido == "";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
api_endpoint: "http://localhost:3035/api",
|
||||
currentUser: localStorage.getItem("currentUser") ? JSON.parse(localStorage.getItem("currentUser") || "") : null,
|
||||
titleProject: "Maker Book",
|
||||
defaultImageError: "https://static.vecteezy.com/system/resources/previews/005/337/799/non_2x/icon-image-not-found-free-vector.jpg",
|
||||
translations: <any>{},
|
||||
secretKey: "4f3c9d8a7b6e5d4c3b2a1f0e9d8c7b6a5e4d3c2b1a0f9e8d7c6b5a4e3d2c1b0"
|
||||
};
|
||||
|
||||
/*
|
||||
* For easier debugging in development mode, you can import the following file
|
||||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
||||
*
|
||||
* This import should be commented out in production mode because it will have a negative impact
|
||||
* on performance if an error is thrown.
|
||||
*/
|
||||
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
|
||||
|
||||
export const GlobalFunctions = {
|
||||
validaForm(form: any, campos: string[], validacoes: any[]) : (boolean) {
|
||||
let campoInvalido: string = "";
|
||||
for (let i = 0; i < campos.length; i++) {
|
||||
let campo = campos[i];
|
||||
validacoes.forEach(val => {
|
||||
if (form[campo] == val) {
|
||||
campoInvalido = campo;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (campoInvalido != "") {
|
||||
alert("Preencha corretamente os campos para salvar! " + campoInvalido);
|
||||
}
|
||||
|
||||
return campoInvalido == "";
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Maker Book</title>
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.png" />
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
|
||||
|
||||
<!-- Bootstrap Bundle with Popper -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Inter Google Fonts -->
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.10.377/pdf.worker.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/* tslint:disable: ordered-imports*/
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
/* Third Party */
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { IconsModule } from '@modules/icons/icons.module';
|
||||
|
||||
const thirdParty = [IconsModule, NgbModule];
|
||||
|
||||
/* Containers */
|
||||
import * as appCommonContainers from './containers';
|
||||
|
||||
/* Components */
|
||||
import * as appCommonComponents from './components';
|
||||
|
||||
/* Guards */
|
||||
import * as appCommonGuards from './guards';
|
||||
|
||||
/* Services */
|
||||
import * as appCommonServices from './services';
|
||||
import * as authServices from '@modules/auth/services';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, RouterModule, ...thirdParty],
|
||||
providers: [...appCommonServices.services, ...authServices.services, ...appCommonGuards.guards],
|
||||
declarations: [...appCommonContainers.containers, ...appCommonComponents.components],
|
||||
exports: [...appCommonContainers.containers, ...appCommonComponents.components, ...thirdParty],
|
||||
})
|
||||
export class AppCommonModule {}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<div class="card text-white mb-4" [ngClass]="background">
|
||||
<ng-content select=".card-body"></ng-content>
|
||||
<div class="card-footer d-flex align-items-center justify-content-between">
|
||||
<a class="small text-white stretched-link" [routerLink]="link">View Details</a>
|
||||
<div class="small text-white"><fa-icon [icon]='["fas", "angle-right"]'></fa-icon></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
.card.text-white.mb-4(
|
||||
[ngClass]='background'
|
||||
)
|
||||
ng-content(select='.card-body')
|
||||
.card-footer.d-flex.align-items-center.justify-content-between
|
||||
a([routerLink]='link').small.text-white.stretched-link
|
||||
| View Details
|
||||
.small.text-white
|
||||
fa-icon([icon]='["fas", "angle-right"]')
|
||||
|
|
@ -0,0 +1 @@
|
|||
@import 'styles/variables.scss';
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import { Component, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
import { CardViewDetailsComponent } from './card-view-details.component';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<sb-card-view-details
|
||||
[someInput]="someInput"
|
||||
(someFunction)="someFunction($event)"
|
||||
></sb-card-view-details>
|
||||
`,
|
||||
})
|
||||
class TestHostComponent {
|
||||
// someInput = 1;
|
||||
// someFunction(event: Event) {}
|
||||
}
|
||||
|
||||
describe('CardViewDetailsComponent', () => {
|
||||
let fixture: ComponentFixture<TestHostComponent>;
|
||||
let hostComponent: TestHostComponent;
|
||||
let hostComponentDE: DebugElement;
|
||||
let hostComponentNE: Element;
|
||||
|
||||
let component: CardViewDetailsComponent;
|
||||
let componentDE: DebugElement;
|
||||
let componentNE: Element;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [TestHostComponent, CardViewDetailsComponent],
|
||||
imports: [NoopAnimationsModule],
|
||||
providers: [],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TestHostComponent);
|
||||
hostComponent = fixture.componentInstance;
|
||||
hostComponentDE = fixture.debugElement;
|
||||
hostComponentNE = hostComponentDE.nativeElement;
|
||||
|
||||
componentDE = hostComponentDE.children[0];
|
||||
component = componentDE.componentInstance;
|
||||
componentNE = componentDE.nativeElement;
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the component', () => {
|
||||
expect(hostComponentNE.querySelector('sb-card-view-details')).toEqual(jasmine.anything());
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'sb-card-view-details',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: './card-view-details.component.html',
|
||||
styleUrls: ['card-view-details.component.scss'],
|
||||
})
|
||||
export class CardViewDetailsComponent implements OnInit {
|
||||
@Input() background!: string;
|
||||
@Input() color!: string;
|
||||
@Input() link = '';
|
||||
|
||||
customClasses: string[] = [];
|
||||
|
||||
constructor() {}
|
||||
ngOnInit() {
|
||||
if (this.background) {
|
||||
this.customClasses.push(this.background);
|
||||
}
|
||||
if (this.color) {
|
||||
this.customClasses.push(this.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<div class="card mb-4" [ngClass]="customClasses"><ng-content select=".card-header"></ng-content><ng-content select=".card-body"></ng-content><ng-content select=".card-footer"></ng-content></div>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
.card.mb-4(
|
||||
[ngClass]='customClasses'
|
||||
)
|
||||
ng-content(select='.card-header')
|
||||
ng-content(select='.card-body')
|
||||
ng-content(select='.card-footer')
|
||||
|
|
@ -0,0 +1 @@
|
|||
@import 'styles/variables.scss';
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import { Component, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
import { CardComponent } from './card.component';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<sb-card [someInput]="someInput" (someFunction)="someFunction($event)"></sb-card>
|
||||
`,
|
||||
})
|
||||
class TestHostComponent {
|
||||
// someInput = 1;
|
||||
// someFunction(event: Event) {}
|
||||
}
|
||||
|
||||
describe('CardComponent', () => {
|
||||
let fixture: ComponentFixture<TestHostComponent>;
|
||||
let hostComponent: TestHostComponent;
|
||||
let hostComponentDE: DebugElement;
|
||||
let hostComponentNE: Element;
|
||||
|
||||
let component: CardComponent;
|
||||
let componentDE: DebugElement;
|
||||
let componentNE: Element;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [TestHostComponent, CardComponent],
|
||||
imports: [NoopAnimationsModule],
|
||||
providers: [],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TestHostComponent);
|
||||
hostComponent = fixture.componentInstance;
|
||||
hostComponentDE = fixture.debugElement;
|
||||
hostComponentNE = hostComponentDE.nativeElement;
|
||||
|
||||
componentDE = hostComponentDE.children[0];
|
||||
component = componentDE.componentInstance;
|
||||
componentNE = componentDE.nativeElement;
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the component', () => {
|
||||
expect(hostComponentNE.querySelector('sb-card')).toEqual(jasmine.anything());
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'sb-card',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: './card.component.html',
|
||||
styleUrls: ['card.component.scss'],
|
||||
})
|
||||
export class CardComponent implements OnInit {
|
||||
@Input() background!: string;
|
||||
@Input() color!: string;
|
||||
|
||||
customClasses: string[] = [];
|
||||
|
||||
constructor() {}
|
||||
ngOnInit() {
|
||||
if (this.background) {
|
||||
this.customClasses.push(this.background);
|
||||
}
|
||||
if (this.color) {
|
||||
this.customClasses.push(this.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { CardViewDetailsComponent } from './card-view-details/card-view-details.component';
|
||||
import { CardComponent } from './card/card.component';
|
||||
|
||||
export const components = [CardComponent, CardViewDetailsComponent];
|
||||
|
||||
export * from './card/card.component';
|
||||
export * from './card-view-details/card-view-details.component';
|
||||
|
|
@ -0,0 +1 @@
|
|||
export const containers = [];
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AppCommonGuard } from './app-common.guard';
|
||||
|
||||
describe('App Common Guards', () => {
|
||||
let appCommonGuard: AppCommonGuard;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [],
|
||||
providers: [AppCommonGuard],
|
||||
});
|
||||
appCommonGuard = TestBed.inject(AppCommonGuard);
|
||||
});
|
||||
|
||||
describe('canActivate', () => {
|
||||
it('should return an Observable<boolean>', () => {
|
||||
appCommonGuard.canActivate().subscribe(response => {
|
||||
expect(response).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CanActivate } from '@angular/router';
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class AppCommonGuard implements CanActivate {
|
||||
canActivate(): Observable<boolean> {
|
||||
return of(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { AppCommonGuard } from './app-common.guard';
|
||||
|
||||
export const guards = [AppCommonGuard];
|
||||
|
||||
export * from './app-common.guard';
|
||||
|
|
@ -0,0 +1 @@
|
|||
export {};
|
||||
|
|
@ -0,0 +1 @@
|
|||
export * from './app-common.model';
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AppCommonService } from './app-common.service';
|
||||
|
||||
describe('AppCommonService', () => {
|
||||
let appCommonService: AppCommonService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [AppCommonService],
|
||||
});
|
||||
appCommonService = TestBed.inject(AppCommonService);
|
||||
});
|
||||
|
||||
describe('getAppCommon$', () => {
|
||||
it('should return Observable<AppCommon>', () => {
|
||||
appCommonService.getAppCommon$().subscribe(response => {
|
||||
expect(response).toEqual({});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class AppCommonService {
|
||||
constructor() {}
|
||||
|
||||
getAppCommon$(): Observable<{}> {
|
||||
return of({});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { AppCommonService } from './app-common.service';
|
||||
|
||||
export const services = [AppCommonService];
|
||||
|
||||
export * from './app-common.service';
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/* tslint:disable: ordered-imports*/
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { SBRouteData } from '@modules/navigation/models';
|
||||
|
||||
/* Module */
|
||||
import { AuthModule } from './auth.module';
|
||||
|
||||
/* Containers */
|
||||
import * as authContainers from './containers';
|
||||
|
||||
/* Guards */
|
||||
import * as authGuards from './guards';
|
||||
|
||||
/* Routes */
|
||||
export const ROUTES: Routes = [
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
redirectTo: 'login',
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
canActivate: [],
|
||||
component: authContainers.LoginComponent,
|
||||
data: {
|
||||
title: 'Pages Login - SB Admin Angular',
|
||||
} as SBRouteData,
|
||||
},
|
||||
{
|
||||
path: 'register',
|
||||
canActivate: [],
|
||||
component: authContainers.RegisterComponent,
|
||||
data: {
|
||||
title: 'Pages Register - SB Admin Angular',
|
||||
} as SBRouteData,
|
||||
},
|
||||
{
|
||||
path: 'forgot-password',
|
||||
canActivate: [],
|
||||
component: authContainers.ForgotPasswordComponent,
|
||||
data: {
|
||||
title: 'Pages Forgot Password - SB Admin Angular',
|
||||
} as SBRouteData,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [AuthModule, RouterModule.forChild(ROUTES)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AuthRoutingModule {}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/* tslint:disable: ordered-imports*/
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
|
||||
|
||||
/* Modules */
|
||||
import { AppCommonModule } from '@common/app-common.module';
|
||||
import { NavigationModule } from '@modules/navigation/navigation.module';
|
||||
|
||||
/* Components */
|
||||
import * as authComponents from './components';
|
||||
|
||||
/* Containers */
|
||||
import * as authContainers from './containers';
|
||||
|
||||
/* Guards */
|
||||
import * as authGuards from './guards';
|
||||
|
||||
/* Services */
|
||||
import * as authServices from './services';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
AppCommonModule,
|
||||
NavigationModule,
|
||||
],
|
||||
providers: [...authServices.services, ...authGuards.guards],
|
||||
declarations: [...authContainers.containers, ...authComponents.components],
|
||||
exports: [...authContainers.containers, ...authComponents.components],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export const components = [];
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<sb-layout-auth
|
||||
><div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-5">
|
||||
<div class="card shadow-lg border-0 rounded-lg mt-5">
|
||||
<div class="card-header"><h3 class="text-center font-weight-light my-4">Password Recovery</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="small mb-3 text-muted">Enter your email address and we will send you a link to reset your password.</div>
|
||||
<form>
|
||||
<div class="form-group"><label class="small mb-1" for="inputEmailAddress">Email</label><input class="form-control py-4" id="inputEmailAddress" type="email" aria-describedby="emailHelp" placeholder="Enter email address" /></div>
|
||||
<div class="form-group d-flex align-items-center justify-content-between mt-4 mb-0"><a class="small" routerLink="/auth/login">Return to login</a><a class="btn btn-primary" routerLink="/auth/login">Reset Password</a></div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer text-center">
|
||||
<div class="small"><a routerLink="/auth/register">Need an account? Sign up!</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div></sb-layout-auth
|
||||
>
|
||||