first commit

This commit is contained in:
Diego Freitas 2026-05-26 09:38:56 -03:00
commit 71c124cc68
151 changed files with 41061 additions and 0 deletions

51
.gitignore vendored Normal file
View File

@ -0,0 +1,51 @@
# These are some examples of commonly ignored file patterns.
# You should customize this list as applicable to your project.
# Learn more about .gitignore:
# https://www.atlassian.com/git/tutorials/saving-changes/gitignore
# Node artifact files
node_modules/
dist/
# Compiled Java class files
*.class
# Compiled Python bytecode
*.py[cod]
# Log files
*.log
# Package files
*.jar
# Maven
target/
dist/
# JetBrains IDE
.idea/
# Unit test reports
TEST*.xml
# Generated by MacOS
.DS_Store
# Generated by Windows
Thumbs.db
# Applications
*.app
*.war
# Large media files
*.mp4
*.tiff
*.avi
*.flv
*.mov
*.wmv
*desktop.ini

27
README.md Normal file
View File

@ -0,0 +1,27 @@
# ZendionInc
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.3.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

133
angular.json Normal file
View File

@ -0,0 +1,133 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"ZendionInc": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/ZendionInc",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css",
"src/styles.scss",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
"node_modules/jquery/dist/jquery.js",
"node_modules/popper.js/dist/umd/popper.js",
"node_modules/bootstrap/dist/js/bootstrap.js",
"node_modules/chart.js/dist/Chart.js"
]
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "ZendionInc:build"
},
"configurations": {
"production": {
"browserTarget": "ZendionInc:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "ZendionInc: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.ico",
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css",
"src/styles.scss"
],
"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": "ZendionInc:serve"
},
"configurations": {
"production": {
"devServerTarget": "ZendionInc:serve:production"
}
}
}
}
}
},
"defaultProject": "ZendionInc"
}

367
banco.sql Normal file
View File

@ -0,0 +1,367 @@
-- MySQL dump 10.13 Distrib 8.0.20, for Win64 (x86_64)
--
-- Host: 18.228.31.8 Database: zendioninc
-- ------------------------------------------------------
-- Server version 8.0.22-0ubuntu0.20.04.2
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `chaves`
--
DROP TABLE IF EXISTS `chaves`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `chaves` (
`idchaves` int NOT NULL AUTO_INCREMENT,
`licenca` varchar(150) DEFAULT NULL,
`data_criacao` datetime DEFAULT NULL,
`data_validade` datetime DEFAULT NULL,
`idusuarios` int DEFAULT NULL,
`usada` tinyint DEFAULT NULL,
`idsistemas` int DEFAULT NULL,
`insert_date` datetime DEFAULT CURRENT_TIMESTAMP,
`update_date` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idchaves`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `chaves`
--
LOCK TABLES `chaves` WRITE;
/*!40000 ALTER TABLE `chaves` DISABLE KEYS */;
/*!40000 ALTER TABLE `chaves` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `noticias`
--
DROP TABLE IF EXISTS `noticias`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `noticias` (
`idnoticias` int NOT NULL AUTO_INCREMENT,
`titulo` varchar(150) DEFAULT NULL,
`descricao` longtext,
`habilitado` tinyint DEFAULT NULL,
`link` varchar(200) DEFAULT NULL,
`insert_date` datetime DEFAULT CURRENT_TIMESTAMP,
`update_date` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idnoticias`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `noticias`
--
LOCK TABLES `noticias` WRITE;
/*!40000 ALTER TABLE `noticias` DISABLE KEYS */;
INSERT INTO `noticias` VALUES (1,'Teste noticia 1','Aqui entra o texto da noticia de teste 1',1,'https://www.google.com.br/','2021-03-05 23:23:01','2021-03-05 23:23:16');
/*!40000 ALTER TABLE `noticias` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `plataformas`
--
DROP TABLE IF EXISTS `plataformas`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `plataformas` (
`idplataformas` int NOT NULL AUTO_INCREMENT,
`descricao` varchar(150) DEFAULT NULL,
`habilitado` tinyint DEFAULT NULL,
`insert_date` datetime DEFAULT CURRENT_TIMESTAMP,
`update_date` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idplataformas`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `plataformas`
--
LOCK TABLES `plataformas` WRITE;
/*!40000 ALTER TABLE `plataformas` DISABLE KEYS */;
/*!40000 ALTER TABLE `plataformas` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `sistemas`
--
DROP TABLE IF EXISTS `sistemas`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `sistemas` (
`idsistemas` int NOT NULL AUTO_INCREMENT,
`nome` varchar(150) DEFAULT NULL,
`descricao` varchar(150) DEFAULT NULL,
`versaoatual` varchar(45) DEFAULT NULL,
`habilitado` tinyint DEFAULT NULL,
`idtiposdesistema` int DEFAULT NULL,
`idplataformas` int DEFAULT NULL,
`nome_install` varchar(150) DEFAULT NULL,
`notasdeatualizacao` longtext,
`idtiposlicenca` int DEFAULT NULL,
`insert_date` datetime DEFAULT CURRENT_TIMESTAMP,
`update_date` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idsistemas`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `sistemas`
--
LOCK TABLES `sistemas` WRITE;
/*!40000 ALTER TABLE `sistemas` DISABLE KEYS */;
INSERT INTO `sistemas` VALUES (9,'FMovel','Sistema de gestão de concecionaria','1.0.0.1',1,1,1,'fmovel_install.exe','Primeira versão',1,'2021-02-25 23:10:06','2021-03-04 00:30:07');
/*!40000 ALTER TABLE `sistemas` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `sistemasdetalhes`
--
DROP TABLE IF EXISTS `sistemasdetalhes`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `sistemasdetalhes` (
`idsistemasdetalhes` int NOT NULL AUTO_INCREMENT,
`idsistemas` int DEFAULT NULL,
`descricao` varchar(250) DEFAULT NULL,
`habilitado` tinyint DEFAULT NULL,
`insert_date` datetime DEFAULT CURRENT_TIMESTAMP,
`update_date` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idsistemasdetalhes`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `sistemasdetalhes`
--
LOCK TABLES `sistemasdetalhes` WRITE;
/*!40000 ALTER TABLE `sistemasdetalhes` DISABLE KEYS */;
/*!40000 ALTER TABLE `sistemasdetalhes` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `sistemasusuario`
--
DROP TABLE IF EXISTS `sistemasusuario`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `sistemasusuario` (
`idsistemasusuario` int NOT NULL AUTO_INCREMENT,
`idsistemas` int DEFAULT NULL,
`idusuarios` int DEFAULT NULL,
`data_download` datetime DEFAULT NULL,
`insert_date` datetime DEFAULT CURRENT_TIMESTAMP,
`update_date` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idsistemasusuario`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `sistemasusuario`
--
LOCK TABLES `sistemasusuario` WRITE;
/*!40000 ALTER TABLE `sistemasusuario` DISABLE KEYS */;
/*!40000 ALTER TABLE `sistemasusuario` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `slides`
--
DROP TABLE IF EXISTS `slides`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `slides` (
`idslides` int NOT NULL AUTO_INCREMENT,
`descricao` varchar(150) DEFAULT NULL,
`habilitado` tinyint DEFAULT NULL,
`link` varchar(250) DEFAULT NULL,
`insert_date` datetime DEFAULT CURRENT_TIMESTAMP,
`update_date` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idslides`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `slides`
--
LOCK TABLES `slides` WRITE;
/*!40000 ALTER TABLE `slides` DISABLE KEYS */;
INSERT INTO `slides` VALUES (1,'TI',1,'undefined','2021-03-05 23:21:52','2021-03-05 23:22:04'),(2,'Inovacao',1,'undefined','2021-03-05 23:22:25','2021-03-05 23:22:30');
/*!40000 ALTER TABLE `slides` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `tiposdesistema`
--
DROP TABLE IF EXISTS `tiposdesistema`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `tiposdesistema` (
`idtiposdesistema` int NOT NULL AUTO_INCREMENT,
`descricao` varchar(150) DEFAULT NULL,
`habilitado` tinyint DEFAULT NULL,
`insert_date` datetime DEFAULT CURRENT_TIMESTAMP,
`update_date` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idtiposdesistema`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `tiposdesistema`
--
LOCK TABLES `tiposdesistema` WRITE;
/*!40000 ALTER TABLE `tiposdesistema` DISABLE KEYS */;
/*!40000 ALTER TABLE `tiposdesistema` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `tiposdeusuario`
--
DROP TABLE IF EXISTS `tiposdeusuario`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `tiposdeusuario` (
`idtiposdeusuario` int NOT NULL AUTO_INCREMENT,
`descricao` varchar(45) DEFAULT NULL,
`habilitado` tinyint DEFAULT NULL,
`insert_date` datetime DEFAULT CURRENT_TIMESTAMP,
`update_date` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idtiposdeusuario`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `tiposdeusuario`
--
LOCK TABLES `tiposdeusuario` WRITE;
/*!40000 ALTER TABLE `tiposdeusuario` DISABLE KEYS */;
INSERT INTO `tiposdeusuario` VALUES (1,'Administrador',1,'2021-03-05 23:20:05',NULL),(2,'Cliente',1,'2021-03-05 23:20:05',NULL);
/*!40000 ALTER TABLE `tiposdeusuario` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `tiposlicenca`
--
DROP TABLE IF EXISTS `tiposlicenca`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `tiposlicenca` (
`idtiposlicenca` int NOT NULL AUTO_INCREMENT,
`descricao` varchar(150) DEFAULT NULL,
`habilitado` tinyint DEFAULT NULL,
`insert_date` datetime DEFAULT CURRENT_TIMESTAMP,
`update_date` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idtiposlicenca`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `tiposlicenca`
--
LOCK TABLES `tiposlicenca` WRITE;
/*!40000 ALTER TABLE `tiposlicenca` DISABLE KEYS */;
/*!40000 ALTER TABLE `tiposlicenca` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `trocasenha`
--
DROP TABLE IF EXISTS `trocasenha`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `trocasenha` (
`idtrocasenha` int NOT NULL AUTO_INCREMENT,
`idusuarios` int DEFAULT NULL,
`tag` varchar(250) DEFAULT NULL,
`usada` tinyint DEFAULT NULL,
`insert_date` datetime DEFAULT CURRENT_TIMESTAMP,
`update_date` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idtrocasenha`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `trocasenha`
--
LOCK TABLES `trocasenha` WRITE;
/*!40000 ALTER TABLE `trocasenha` DISABLE KEYS */;
/*!40000 ALTER TABLE `trocasenha` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `usuarios`
--
DROP TABLE IF EXISTS `usuarios`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `usuarios` (
`idusuarios` int NOT NULL AUTO_INCREMENT,
`nome` varchar(150) DEFAULT NULL,
`usuario` varchar(45) DEFAULT NULL,
`senha` varchar(45) DEFAULT NULL,
`data_cadastro` datetime DEFAULT NULL,
`email` varchar(45) DEFAULT NULL,
`idtiposdeusuario` int DEFAULT NULL,
`cpf` varchar(45) DEFAULT NULL,
`habilitado` tinyint DEFAULT NULL,
`insert_date` datetime DEFAULT CURRENT_TIMESTAMP,
`update_date` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`idusuarios`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `usuarios`
--
LOCK TABLES `usuarios` WRITE;
/*!40000 ALTER TABLE `usuarios` DISABLE KEYS */;
INSERT INTO `usuarios` VALUES (1,'Diego Freitas','ZENDION','Z210203!','2021-03-05 23:15:50','zendionmazinni@gmail.com',1,'425.855.768-41',1,'2021-03-05 23:15:50','2021-03-05 23:18:42');
/*!40000 ALTER TABLE `usuarios` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2021-03-05 20:27:35

12
browserslist Normal file
View File

@ -0,0 +1,12 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.

32
e2e/protractor.conf.js Normal file
View File

@ -0,0 +1,32 @@
// @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 } }));
}
};

23
e2e/src/app.e2e-spec.ts Normal file
View File

@ -0,0 +1,23 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to ZendionInc!');
});
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));
});
});

11
e2e/src/app.po.ts Normal file
View File

@ -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 h1')).getText() as Promise<string>;
}
}

13
e2e/tsconfig.json Normal file
View File

@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

32
karma.conf.js Normal file
View File

@ -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/ZendionInc'),
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
});
};

12078
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

59
package.json Normal file
View File

@ -0,0 +1,59 @@
{
"name": "zendion-inc",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~8.1.3",
"@angular/cdk": "~8.2.3",
"@angular/common": "~8.1.3",
"@angular/compiler": "~8.1.3",
"@angular/core": "~8.1.3",
"@angular/forms": "~8.1.3",
"@angular/material": "^8.2.3",
"@angular/platform-browser": "~8.1.3",
"@angular/platform-browser-dynamic": "~8.1.3",
"@angular/router": "~8.1.3",
"angular2-useful-swiper": "^8.0.1-beta.1",
"bootstrap": "^4.6.0",
"chart.js": "^2.7.3",
"hammerjs": "^2.0.8",
"jquery": "^3.6.0",
"ng5-slider": "^1.2.6",
"popper.js": "^1.16.1",
"primeng": "^8.1.0",
"rxjs": "~6.4.0",
"swiper": "^4.5.1",
"tslib": "^1.14.1",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.801.3",
"@angular/cli": "~8.1.3",
"@angular/compiler-cli": "~8.1.3",
"@angular/language-service": "~8.1.3",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "^2.0.8",
"@types/node": "~8.9.4",
"@types/swiper": "^4.4.11",
"codelyzer": "^5.2.2",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.5.4",
"protractor": "^5.4.4",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.4.3"
}
}

1
privacidade.html Normal file
View File

@ -0,0 +1 @@
<h2><span style="color: rgb(68, 68, 68);">Política Privacidade</span></h2><p><span style="color: rgb(68, 68, 68);">A sua privacidade é importante para nós. É política do Zendion Inc respeitar a sua privacidade em relação a qualquer informação sua que possamos coletar no site <a href="https://zendioninc.mooo.com/">Zendion Inc</a>, e outros sites que possuímos e operamos.</span></p><p><span style="color: rgb(68, 68, 68);">Solicitamos informações pessoais apenas quando realmente precisamos delas para lhe fornecer um serviço. Fazemo-lo por meios justos e legais, com o seu conhecimento e consentimento. Também informamos por que estamos coletando e como será usado.</span></p><p><span style="color: rgb(68, 68, 68);">Apenas retemos as informações coletadas pelo tempo necessário para fornecer o serviço solicitado. Quando armazenamos dados, protegemos dentro de meios comercialmente aceitáveis para evitar perdas e roubos, bem como acesso, divulgação, cópia, uso ou modificação não autorizados.</span></p><p><span style="color: rgb(68, 68, 68);">Não compartilhamos informações de identificação pessoal publicamente ou com terceiros, exceto quando exigido por lei.</span></p><p><span style="color: rgb(68, 68, 68);">O nosso site pode ter links para sites externos que não são operados por nós. Esteja ciente de que não temos controle sobre o conteúdo e práticas desses sites e não podemos aceitar responsabilidade por suas respectivas&nbsp;</span><a href="https://politicaprivacidade.com/" rel="noopener noreferrer" target="_blank" style="background-color: transparent; color: rgb(68, 68, 68);">políticas de privacidade</a><span style="color: rgb(68, 68, 68);">.</span></p><p><span style="color: rgb(68, 68, 68);">Você é livre para recusar a nossa solicitação de informações pessoais, entendendo que talvez não possamos fornecer alguns dos serviços desejados.</span></p><p><span style="color: rgb(68, 68, 68);">O uso continuado de nosso site será considerado como aceitação de nossas práticas em torno de privacidade e informações pessoais. Se você tiver alguma dúvida sobre como lidamos com dados do usuário e informações pessoais, entre em contacto connosco.</span></p><p><span style="color: rgb(68, 68, 68);"></span></p><h3><span style="color: rgb(68, 68, 68);">Compromisso do Usuário</span></h3><p><span style="color: rgb(68, 68, 68);">O usuário se compromete a fazer uso adequado dos conteúdos e da informação que o Zendion Inc oferece no site e com caráter enunciativo, mas não limitativo:</span></p><ul><li><span style="color: rgb(68, 68, 68);">A) Não se envolver em atividades que sejam ilegais ou contrárias à boa fé a à ordem pública;</span></li><li><span style="color: rgb(68, 68, 68);">B) Não difundir propaganda ou conteúdo de natureza racista, xenofóbica, </span><span style="color: rgb(33, 37, 41);"><a href='https://ondeapostar.com/betano-e-confiavel/' rel='nofollow' target='_BLANK' style='color:inherit !important; text-decoration: none !important; font-size: inherit !important;'>onde apostar</a></span><span style="color: rgb(68, 68, 68);"> ou azar, qualquer tipo de pornografia ilegal, de apologia ao terrorismo ou contra os direitos humanos;</span></li><li><span style="color: rgb(68, 68, 68);">C) Não causar danos aos sistemas físicos (hardwares) e lógicos (softwares) do Zendion Inc, de seus fornecedores ou terceiros, para introduzir ou disseminar vírus informáticos ou quaisquer outros sistemas de hardware ou software que sejam capazes de causar danos anteriormente mencionados.</span></li></ul><h3><span style="color: rgb(68, 68, 68);">Mais informações</span></h3><p><span style="color: rgb(68, 68, 68);">Esperemos que esteja esclarecido e, como mencionado anteriormente, se houver algo que você não tem certeza se precisa ou não, geralmente é mais seguro deixar os cookies ativados, caso interaja com um dos recursos que você usa em nosso site.</span></p><p><span style="color: rgb(68, 68, 68);">Esta política é efetiva a partir de&nbsp;</span><strong style="color: rgb(68, 68, 68);">5 December 2022 22:21</strong></p>

1
privacidade.txt Normal file
View File

@ -0,0 +1 @@
Política PrivacidadeA sua privacidade é importante para nós. É política do Zendion Inc respeitar a sua privacidade em relação a qualquer informação sua que possamos coletar no site Zendion Inc, e outros sites que possuímos e operamos.Solicitamos informações pessoais apenas quando realmente precisamos delas para lhe fornecer um serviço. Fazemo-lo por meios justos e legais, com o seu conhecimento e consentimento. Também informamos por que estamos coletando e como será usado.Apenas retemos as informações coletadas pelo tempo necessário para fornecer o serviço solicitado. Quando armazenamos dados, protegemos dentro de meios comercialmente aceitáveis para evitar perdas e roubos, bem como acesso, divulgação, cópia, uso ou modificação não autorizados.Não compartilhamos informações de identificação pessoal publicamente ou com terceiros, exceto quando exigido por lei.O nosso site pode ter links para sites externos que não são operados por nós. Esteja ciente de que não temos controle sobre o conteúdo e práticas desses sites e não podemos aceitar responsabilidade por suas respectivas políticas de privacidade.Você é livre para recusar a nossa solicitação de informações pessoais, entendendo que talvez não possamos fornecer alguns dos serviços desejados.O uso continuado de nosso site será considerado como aceitação de nossas práticas em torno de privacidade e informações pessoais. Se você tiver alguma dúvida sobre como lidamos com dados do usuário e informações pessoais, entre em contacto connosco.Compromisso do UsuárioO usuário se compromete a fazer uso adequado dos conteúdos e da informação que o Zendion Inc oferece no site e com caráter enunciativo, mas não limitativo:A) Não se envolver em atividades que sejam ilegais ou contrárias à boa fé a à ordem pública;B) Não difundir propaganda ou conteúdo de natureza racista, xenofóbica, onde apostar ou azar, qualquer tipo de pornografia ilegal, de apologia ao terrorismo ou contra os direitos humanos;C) Não causar danos aos sistemas físicos (hardwares) e lógicos (softwares) do Zendion Inc, de seus fornecedores ou terceiros, para introduzir ou disseminar vírus informáticos ou quaisquer outros sistemas de hardware ou software que sejam capazes de causar danos anteriormente mencionados.Mais informaçõesEsperemos que esteja esclarecido e, como mencionado anteriormente, se houver algo que você não tem certeza se precisa ou não, geralmente é mais seguro deixar os cookies ativados, caso interaja com um dos recursos que você usa em nosso site.Esta política é efetiva a partir de 5 December 2022 22:21

9
server/.env Normal file
View File

@ -0,0 +1,9 @@
SMTP_HOST=email-smtp.sa-east-1.amazonaws.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=AKIAWD3MQRM2CPUVNA4P
SMTP_PASS=BEfWvd2+A098qv5QlUSeUwyajG+ID8XSAwKslE3+qxRU
SMTP_FROM_EMAIL=contato@zendioninc.com.br
SMTP_FROM_NAME=Zendion, INC.

126
server/connector.js Normal file
View File

@ -0,0 +1,126 @@
var mysql = require('mysql');
//#region ZENDION INC
var MySQLConfig = {
connectionLimit : 10,
host : 'zendioninc.com.br',
port : '3306',
user : 'zendion',
password : 'Z210203!',
database : 'zendioninc',
debug : false,
multipleStatements : true
};
global.AbreConexaoMySQL = function AbreConexaoMySQL() {
global.mysqlConnection = mysql.createPool(MySQLConfig);
console.log("MySQL Conectado! ZENDION INC");
}
AbreConexaoMySQL();
global.ExecuteQuery = function ExecuteQuery(sqlQuery, req, res, params = [], callback = null) {
mysqlConnection.query(sqlQuery, params, function (error, results, fields) {
console.log(`${new Date().toUTCString()} - ${req.originalUrl} ${(error) ? ' 500 error' : ' 200 success'}`);
res.send(JSON.stringify({ "status": ((error) ? 500 : 200), "error": error, "response": results }))
});
}
//#endregion
//#region GCA Control
var ConfigMySQLGCA = {
connectionLimit : 10,
host : 'zendioninc.com.br',
port : '3306',
user : 'zendion',
password : 'Z210203!',
database : 'GCA',
debug : false,
multipleStatements : true
};
global.AbreConexaoGCAMySQL = function AbreConexaoGCAMySQL() {
global.ConexaoMySQL_GCA = mysql.createPool(ConfigMySQLGCA);
console.log("MySQL Conectado! GCA Control");
}
AbreConexaoGCAMySQL();
global.ExecuteQueryGCA = function ExecuteQueryGCA(sqlQuery, req, res, params = [], callback = null) {
ConexaoMySQL_GCA.query(sqlQuery, params, function (error, results, fields) {
console.log(`${new Date().toUTCString()} - ${req.originalUrl} ${(error) ? ' 500 error' : ' 200 success'}`);
res.send(JSON.stringify({ "status": ((error) ? 500 : 200), "error": error, "response": results }))
});
}
//#endregion
//#region PRIORIE
var ConfigMySQLPriorie = {
connectionLimit : 10,
host : 'zendioninc.com.br',
port : '3306',
user : 'zendion',
password : 'Z210203!',
database : 'priorie',
debug : false,
multipleStatements : true
};
global.AbreConexaoPriorieMySQL = function AbreConexaoPriorieMySQL() {
global.ConexaoMySQL_Priorie = mysql.createPool(ConfigMySQLPriorie);
console.log("MySQL Conectado! Priorie");
}
AbreConexaoPriorieMySQL();
global.ExecuteQueryPriorie = function ExecutarQuery(sqlQuery, req, res, callback = null) {
ConexaoMySQL_Priorie.query(sqlQuery, function(error, results, fields) {
let Status = 0;
if (!error) {
Status = 200;
console.log(req.originalUrl + " success 200");
}
else {
Status = 500;
console.log(req.originalUrl + " error 500");
results = [];
}
res.send(JSON.stringify({ "status": Status, "response": results, "error": error }));
try {
callback(Status, results['insertId']);
}
catch (e) {}
});
}
//#endregion
//#region AGROBASE
var ConfigMySQLAgro = {
connectionLimit : 10,
host : 'zendioninc.com.br',
port : '3306',
user : 'zendion',
password : 'Z210203!',
database : 'agrobase',
debug : false,
multipleStatements : true
};
global.AbreConexaoAgroMySQL = function AbreConexaoAgroMySQL() {
global.ConexaoMySQL_Agro = mysql.createPool(ConfigMySQLAgro);
console.log("MySQL Conectado! AgroBase");
}
AbreConexaoAgroMySQL();
global.ExecuteQueryAgro = function ExecuteQueryAgro(sqlQuery, req, res, params = [], callback = null) {
ConexaoMySQL_Agro.query(sqlQuery, params, function (error, results, fields) {
console.log(`${new Date().toUTCString()} - ${req.originalUrl} ${(error) ? ' 500 error' : ' 200 success'}`);
res.send(JSON.stringify({ "status": ((error) ? 500 : 200), "error": error, "response": results }))
});
}
//#endregion

View File

@ -0,0 +1,168 @@
const express = require('express');
const router = express.Router();
const formidable = require('formidable');
const unzipper = require('unzipper');
const fs = require('fs');
const path = require('path');
// Defina o caminho do arquivo .vsf
const vsfFilePath = path.join('/var/www/html/agrobase/downloads/version_files.vsf');
var routes = function () {
router.get('/getVsfData', (req, res) => {
fs.readFile(vsfFilePath, 'utf8', (err, data) => {
if (err) {
console.error("Erro ao ler o arquivo .vsf:", err);
return res.status(500).json({ message: "Erro ao ler o arquivo .vsf" });
}
// Tenta converter o conteúdo em JSON
try {
const jsonData = JSON.parse(data);
res.json(jsonData);
} catch (parseError) {
console.error("Erro ao analisar o arquivo .vsf:", parseError);
res.status(500).json({ message: "Erro ao analisar o conteúdo do arquivo .vsf" });
}
});
});
router.post('/uploadTrainFiles', (req, res) => {
let form = new formidable.IncomingForm();
form.maxFileSize = 600 * 1024 * 1024; // Limite de tamanho do arquivo
form.parse(req, async function (err, fields, files) {
if (err) {
console.error("Erro ao processar o arquivo:", err);
return res.status(500).send("Erro no processamento");
}
const nomeOperacao = fields.operacaoId;
const arquivoZipPath = files['imagens.zip'].path;
const dirDestino = '/var/www/html/agrobase/trainfiles/';
try {
// Certifique-se de que o diretório de destino existe
if (!fs.existsSync(dirDestino)) {
fs.mkdirSync(dirDestino, { recursive: true });
}
// Caminho completo do diretório para esta operação específica
const dirOperacao = path.join(dirDestino, nomeOperacao);
// Certifique-se de que o diretório da operação existe
if (!fs.existsSync(dirOperacao)) {
fs.mkdirSync(dirOperacao, { recursive: true });
}
// Abrir o arquivo zip e extrair as imagens com as subpastas
fs.createReadStream(arquivoZipPath)
.pipe(unzipper.Parse())
.on('entry', async function (entry) {
const fileName = entry.path;
const type = entry.type; // 'Directory' ou 'File'
if (type === 'File' && fileName.endsWith('.jpg')) {
// Caminho de destino da imagem extraída (mantém a subpasta, se houver)
const caminhoImagem = path.join(dirOperacao, fileName);
const dirImagem = path.dirname(caminhoImagem);
// Certifique-se de que a subpasta da imagem existe
if (!fs.existsSync(dirImagem)) {
fs.mkdirSync(dirImagem, { recursive: true });
}
// Salvar a imagem no diretório correto
entry.pipe(fs.createWriteStream(caminhoImagem));
} else {
entry.autodrain(); // Ignorar diretórios ou arquivos não suportados
}
})
.on('close', () => {
console.log("Arquivo descompactado com sucesso e imagens sincronizadas.");
res.send(true);
});
} catch (error) {
console.error("Erro ao descompactar o arquivo:", error);
res.status(500).send("Erro no processamento do arquivo ZIP");
}
});
});
router.post('/uploadVersionFiles', (req, res) => {
let form = new formidable.IncomingForm();
form.maxFileSize = 600 * 1024 * 1024; // Limite de tamanho do arquivo
form.parse(req, async function (err, fields, files) {
if (err) {
console.error("Erro ao processar o arquivo:", err);
return res.status(500).send("Erro no processamento");
}
const file = files.file;
const fileName = fields.fileName; // Nome do arquivo formatado
const destino = `/var/www/html/agrobase/downloads/${fileName}`;
fs.copyFile(file.path, destino, (err) => {
if (err) {
console.error("Erro ao salvar o arquivo:", err);
return res.status(500).send("Erro ao salvar o arquivo");
}
res.status(200).send({ filePath: destino });
});
});
});
router.post('/deleteVersionFile', (req, res) => {
const { filePath } = req.body;
if (!filePath) {
return res.status(400).send("Caminho do arquivo não fornecido.");
}
const downloadsDir = '/var/www/html/agrobase/downloads';
const fullPath = path.join(downloadsDir, path.basename(filePath));
fs.access(fullPath, fs.constants.F_OK, (err) => {
if (err) {
console.error("Arquivo não encontrado:", err);
return res.status(404).send("Arquivo não encontrado.");
}
fs.unlink(fullPath, (err) => {
if (err) {
console.error("Erro ao excluir o arquivo:", err);
return res.status(500).send("Erro ao excluir o arquivo.");
}
res.status(200).send({ message: "Arquivo excluído com sucesso." });
});
});
});
router.post('/updateVsfFile', (req, res) => {
const jsonData = req.body.data; // Dados JSON do Angular
fs.writeFile(vsfFilePath, jsonData, 'utf8', (err) => {
if (err) {
console.error("Erro ao atualizar o arquivo .vsf:", err);
return res.status(500).send("Erro ao salvar o arquivo .vsf");
}
res.status(200).send("Arquivo .vsf atualizado com sucesso");
});
});
router.post('/login', function (req, res) {
let usuario = req.body.usuario;
let senha = req.body.senha;
const sqlQuery = `SELECT
nome
FROM usuarios
WHERE usuario = '${usuario}' AND senha = '${senha}';`;
ExecuteQueryAgro(sqlQuery, req, res);
});
return router;
}
module.exports = routes;

View File

@ -0,0 +1,27 @@
const express = require('express');
const router = express.Router();
var routes = function () {
router.get('/backupDump/:idusuarios', (req, res) => {
const idusuarios = parseInt(req.params.idusuarios);
const fs = require('fs');
const shell = require('shelljs');
const Diretorio = '/var/www/html/sistemas/5/backups/usuario_' + idusuarios + '/';
if (!fs.existsSync(Diretorio)) {
fs.mkdirSync(Diretorio);
}
const Arquivo = Diretorio + 'dump.sql';
shell.exec('mysqldump -uroot -pZ210203! Allteeth > ' + Arquivo, function(sucesso) {
fs.readFile(Arquivo, (err, data) => {
console.log("/backupDump success 200");
res.send(JSON.stringify({"arquivo": Buffer.from(data).toString()}));
});
});
});
return router;
}
module.exports = routes;

View File

@ -0,0 +1,141 @@
const express = require('express');
const router = express.Router();
var routes = function () {
router.get('/getUsuarios', function (req, res, next) {
const sqlQuery = `SELECT * FROM usuarios;`;
connection.query(sqlQuery, function (error, results, fields) {
if (error) {
res.send(JSON.stringify({ "status": 500, "error": error, "response": null }))
console.log("/users/getUsuarios error 500");
}
else {
res.send(JSON.stringify({ "status": 200, "error": null, "response": results }))
console.log("/users/getUsuarios success 200");
}
});
});
router.get('/getUsuarioByID/:idusuarios', function (req, res, next) {
let idusuarios = req.params.idusuarios;
const sqlQuery = `SELECT * FROM usuarios WHERE idusuarios = ${idusuarios};`;
connection.query(sqlQuery, function (error, results, fields) {
if (error) {
res.send(JSON.stringify({ "status": 500, "error": error, "response": null }))
console.log("/users/getUsuarioByID error 500");
}
else {
res.send(JSON.stringify({ "status": 200, "error": null, "response": results }))
console.log("/users/getUsuarioByID success 200");
}
});
});
router.post('/authUser', function (req, res, next) {
let usuario = req.body.Usuario;
let senha = req.body.Senha;
const sqlQuery = `
SELECT
idusuarios AS IDUsuario,
nome AS Nome,
data_cadastramento AS DataCadastramento,
null AS Permissoes
FROM
usuarios
WHERE
usuario = '${usuario}'
AND
senha = '${senha}'
AND
habilitado = 1;`;
connection.query(sqlQuery, function (error, results, fields) {
if (error) {
res.send(null)
console.log("/users/authUser error 500");
}
else {
res.send(results[0]);
console.log("/users/authUser success 200");
}
});
});
router.post('/insertUsuarios', function (req, res, next) {
let idusuarios = req.body.idusuarios;
let usuario = req.body.usuario;
let senha = req.body.senha;
let habilitado = req.body.habilitado ? 1 : 0;
let nome = req.body.nome;
let data_cadastramento = req.body.data_cadastramento;
const sqlQuery = `INSERT INTO
usuarios (usuario, senha, habilitado, nome, data_cadastramento)
VALUES (UPPER('${usuario}'), UPPER('${senha}'), ${habilitado}, '${nome}', '${data_cadastramento}');`;
connection.query(sqlQuery, function (error, results, fields) {
if (error) {
res.send(JSON.stringify({ "status": 500, "error": error, "response": null }))
console.log("/users/insertUsuarios error 500");
}
else {
res.send(JSON.stringify({ "status": 200, "error": null, "response": results }))
console.log("/users/insertUsuarios success 200");
}
});
});
router.patch('/updateUsuarios', function (req, res, next) {
let idusuarios = req.body.idusuarios;
let usuario = req.body.usuario;
let senha = req.body.senha;
let habilitado = req.body.habilitado ? 1 : 0;
let nome = req.body.nome;
let data_cadastramento = req.body.data_cadastramento;
const sqlQuery = `
UPDATE
usuarios
SET
usuario = UPPER('${usuario}'),
senha = UPPER('${senha}'),
habilitado = ${habilitado},
nome = '${nome}',
data_cadastramento = '${data_cadastramento}'
WHERE
idusuarios = ${idusuarios};`;
connection.query(sqlQuery, function (error, results, fields) {
if (error) {
res.send(JSON.stringify({ "status": 500, "error": error, "response": null }))
console.log("/users/updateUsuarios error 500");
}
else {
res.send(JSON.stringify({ "status": 200, "error": null, "response": results }))
console.log("/users/updateUsuarios success 200");
}
});
});
router.delete('/deleteUsuarios/:idusuarios', function (req, res, next) {
let idusuarios = req.params.idusuarios;
const sqlQuery = `
DELETE FROM
usuarios
WHERE
idusuarios = ${idusuarios};`;
connection.query(sqlQuery, function (error, results, fields) {
if (error) {
res.send(JSON.stringify({ "status": 500, "error": error, "response": null }))
console.log("/users/deleteUsuarios error 500");
}
else {
res.send(JSON.stringify({ "status": 200, "error": null, "response": results }))
console.log("/users/deleteUsuarios success 200");
}
});
});
return router;
}
module.exports = routes;

View File

@ -0,0 +1,40 @@
require('../connector');
const express = require('express');
const router = express.Router();
const formidable = require('formidable');
var routes = function () {
router.post('/copy', (req, res) => {
let form = new formidable.IncomingForm();
form.maxFileSize = 600 * 1024 * 1024;
form.parse(req, function(err,fields,files){
const fs = require('fs');
var nome = fields.nome;
var pathImagem = files.arquivo.path;
var dir = '/var/www/html/' + fields.caminho;
fs.rename(pathImagem, dir + nome, function (err) {
console.log("arquivo copiado com sucesso");
res.send(true);
})
});
});
router.post('/editaConfig', (req, res) => {
var fs = require('fs');
var content = req.body.content;
var path = "/var/www/html/assets/Enderecos.txt";
fs.writeFile(path, content, function (erro) {
if (erro) {
throw erro;
}
else {
res.send(true);
}
});
});
return router;
}
module.exports = routes;

View File

@ -0,0 +1,105 @@
require('../connector');
const express = require('express');
const router = express.Router();
var routes = function () {
router.get('/getAllSorteios', function (req, res) {
ExecuteQuery(`SELECT idbingosorteios, descricao, rodada, date_format(data_sorteio, '%d/%m/%Y') as data_sorteio, n_max, participantes_max FROM bingosorteios;`, req, res);
});
router.get('/getSorteiosRealizar', function (req, res) {
ExecuteQuery(`SELECT * FROM bingosorteios where realizado = 0;`, req, res);
});
router.get('/getAllCartelas', function (req, res) {
ExecuteQuery(`SELECT * FROM bingocartelas;`, req, res);
});
router.get('/getCartelasSorteio/:idbingosorteios', function (req, res) {
const idbingosorteios = parseInt(req.params.idbingosorteios);
ExecuteQuery(`SELECT * FROM bingocartelas where idbingosorteios = ? order by canhoto asc;`, req, res, [idbingosorteios]);
});
router.get('/getCartelasVendidasSorteio/:idbingosorteios', function (req, res) {
const idbingosorteios = parseInt(req.params.idbingosorteios);
ExecuteQuery(`SELECT *, 1 as 'Acertos' FROM bingocartelas where idbingosorteios = ? and nome is not null order by canhoto asc;`, req, res, [idbingosorteios]);
});
router.post('/insertBingoSorteio', function (req, res) {
const descricao = req.body.descricao;
const rodada = parseInt(req.body.rodada);
const data_sorteio = req.body.data_sorteio;
const n_max = parseInt(req.body.n_max);
const participantes_max = parseInt(req.body.participantes_max);
ExecuteQuery(`insert into bingosorteios (descricao, rodada, data_sorteio, n_max, participantes_max) values (?, ?, ?, ?, ?);`, req, res, [descricao, rodada, data_sorteio, n_max, participantes_max]);
});
router.post('/updateBingoSorteio', function (req, res) {
const idbingosorteios = parseInt(req.body.idbingosorteios);
const descricao = req.body.descricao;
const rodada = parseInt(req.body.rodada);
const data_sorteio = req.body.data_sorteio;
const n_max = parseInt(req.body.n_max);
const participantes_max = parseInt(req.body.participantes_max);
const realizado = req.body.realizado ? 1 : 0;
ExecuteQuery(`update bingosorteios set descricao = ?, rodada = ?, data_sorteio = ?, n_max = ?, participantes_max = ?, realizado = ? where idbingosorteios = ?;`, req, res, [descricao, rodada, data_sorteio, n_max, participantes_max, realizado, idbingosorteios]);
});
router.delete('/deleteBingoSorteio/:idbingosorteios', function (req, res) {
const idbingosorteios = parseInt(req.params.idbingosorteios);
ExecuteQuery(`delete FROM bingosorteios where idbingosorteios = ${idbingosorteios};`, req, res);
});
router.post('/insertBingoCartela', function (req, res) {
const idbingosorteios = parseInt(req.body.idbingosorteios);
const Cartelas = req.body.Cartelas;
let SQLCartelas = `insert into bingocartelas (canhoto, idbingosorteios, numeros) values `;
for (let i = 0; i < Cartelas.length; i++) {
Cartelas[i]['numeros'] = Cartelas[i]['numeros'].toString().replace('passaros.png', 'LIVRE');
SQLCartelas += `(${Cartelas[i]['canhoto']},${idbingosorteios},'${Cartelas[i]['numeros']}'),`
}
SQLCartelas = SQLCartelas.substring(0, SQLCartelas.length - 1);
//console.log(SQLCartelas);
ExecuteQuery(SQLCartelas, req, res);
});
router.patch('/updateBingoCartela', function (req, res) {
const canhoto = req.body.canhoto;
const nome = req.body.nome;
const endereco = req.body.endereco;
const telefone = req.body.telefone;
const bingo = req.body.bingo ? 1 : 0;
const cinquina1 = req.body.cinquina1 ? 1 : 0;
const cinquina2 = req.body.cinquina2 ? 1 : 0;
const cinquina3 = req.body.cinquina3 ? 1 : 0;
ExecuteQuery(`update bingocartelas set nome = ?, endereco = ?, telefone = ?, bingo = ?, cinquina1 = ?, cinquina2 = ?, cinquina3 = ? where canhoto = ?;`, req, res, [nome, endereco, telefone, bingo, cinquina1, cinquina2, cinquina3, canhoto]);
});
router.post('/exportarExcel', function (req, res) {
const Cartelas = req.body.Cartelas;
const idbingosorteios = req.body.idbingosorteios;
let Texto2 = `Canhoto;Numeros;Nome;Endereco;Telefone;\r\n`;
for (let i = 0; i < Cartelas.length; i++) {
Texto2 += `${Cartelas[i]['canhoto']};${Cartelas[i]['numeros']};${Cartelas[i]['nome']};${Cartelas[i]['telefone']};\r\n`;
}
const fs = require('fs');
fs.writeFile('/var/www/html/Jogos/Bingo/Gerador/Cartelas/' + idbingosorteios + '.csv', Texto2, 'utf8', function (err) {
let Sucesso = false;
if (err) {
console.log("Erro ao salvar cartelas");
}
else {
console.log("Cartelas salvas com sucesso");
Sucesso = true;
}
res.send(Sucesso);
});
});
return router;
}
module.exports = routes;

View File

@ -0,0 +1,265 @@
const { MercadoPagoConfig, Preference, Payment } = require('mercadopago');
const express = require('express');
const router = express.Router();
//const client = new MercadoPagoConfig({ accessToken: 'TEST-7218616229800258-090908-d403b108f1f9c31e432f525c8abb14b3-115139428' });
const client = new MercadoPagoConfig({ accessToken: 'APP_USR-7218616229800258-090908-6281afa294bbb2de7f68e1e336539ba1-115139428' });
var routes = function () {
router.post('/buy', function (req, res) {
console.log(`${new Date().toUTCString()} - ${req.originalUrl} ${' called'}`);
const idprodutos = parseInt(req.body.idprodutos);
const idusuarios = parseInt(req.body.idusuarios);
const qtd = parseInt(req.body.qtd);
const sqlQuery = `SELECT
pdt.idprodutos,
pdt.valor,
sis.idsistemas,
sis.nome,
tpl.idtiposlicenca,
tpl.descricao,
(SELECT us.cpf from usuarios us WHERE us.idusuarios = ?) as cpf
FROM produtos pdt
INNER JOIN sistemas sis ON sis.idsistemas = pdt.idsistemas
INNER JOIN tiposlicenca tpl ON tpl.idtiposlicenca = pdt.idtiposlicenca
WHERE pdt.idprodutos = ?`;
mysqlConnection.query(sqlQuery, [idusuarios, idprodutos], function (error, results, fields) {
if (error) {
return res.status(500).send({ error: 'Erro ao consultar o banco de dados.' });
}
const produto = results[0];
if (produto['idtiposlicenca'] == 6) {
const sqlGerarChave = `CALL GerarInserirChave(?, ?, ?, @SerialKey, @idChave);`;
mysqlConnection.query(sqlGerarChave, [produto['cpf'], 30, produto['idsistemas']], function (chaveError, resChave) {
if (chaveError) {
return res.status(500).send({ error: 'Erro ao gerar chave.' });
}
else {
const serial = resChave[0]?.[0]['SerialKey']
res.status(200).send({url: null, chave: serial});
}
})
}
else {
const sqlGetVenda = `SELECT * FROM produtosvendas WHERE idprodutos = ? AND idusuarios = ? AND quantidade = ? AND status IS NULL;`;
mysqlConnection.query(sqlGetVenda, [produto.idprodutos, idusuarios, qtd], function (getError, getVenda) {
if (getError) {
return res.status(500).send({ error: 'Erro ao consultar venda no banco de dados.' });
}
if (getVenda.length == 0) {
const sqlCreate = `INSERT INTO produtosvendas (idprodutos, idusuarios, quantidade, valor) VALUES (?, ?, ?, ?)`;
mysqlConnection.query(sqlCreate, [produto.idprodutos, idusuarios, qtd, qtd * produto.valor], function (createError, venda) {
if (createError) {
return res.status(500).send({ error: 'Erro ao criar venda no banco de dados.' });
}
const preference = new Preference(client);
const pref = {
body: {
items: [
{
title: produto.nome + " " + produto.descricao,
quantity: qtd,
unit_price: produto.valor
}
],
notification_url: 'https://zendioninc.com.br/api/checkout/webhook',
external_reference: "" + venda.insertId
}
};
//console.log(pref);
preference.create(pref)
.then(response => {
//console.log(response);
const newPreferenceId = response.id; // ID da nova preferência criada
// Atualiza o campo preference_id no banco de dados
const updateQuery = `UPDATE produtosvendas SET preference_id = ? WHERE idprodutosvendas = ?`;
mysqlConnection.query(updateQuery, [newPreferenceId, venda.insertId], function (updateError) {
if (updateError) {
return res.status(500).send({ error: 'Erro ao atualizar o preference_id no banco de dados.' });
}
// Redireciona para o checkout com a nova preferência
res.status(200).send({url: response.init_point});
//res.redirect(response.init_point);
});
})
.catch(error => {
console.error('Erro ao criar a preferência:', error);
return res.status(500).send({ error: 'Erro ao criar a preferência de pagamento.' });
});
});
}
else {
// Se já existir, redireciona para o checkout utilizando o preference_id existente
const init_point = `https://www.mercadopago.com.br/checkout/v1/redirect?pref_id=${getVenda[0].preference_id}`;
res.status(200).send({url: init_point});
//return res.redirect(init_point);
}
});
}
});
});
router.post('/webhook', function (req, res) {
const paymentData = req.body;
//console.log(paymentData);
// Verifica o tipo de notificação recebida
if (paymentData.type === 'payment') {
const paymentId = paymentData.data.id;
//console.log(paymentId);
// Consultar o status do pagamento no Mercado Pago usando o ID
const payment = new Payment(client);
payment.get({
id: paymentId,
})
.then(response => {
//console.log(response);
const vendaId = response.external_reference;
const status = response.status;
if (vendaId == null) {
console.error('ID da venda nao informado!');
return res.status(500).send('ID da venda nao informado!');
}
const updateQuery = `UPDATE produtosvendas SET status = ? WHERE idprodutosvendas = ?;`;
mysqlConnection.query(updateQuery, [status, vendaId], function (error) {
if (error) {
console.error('Erro ao atualizar status do pagamento no banco de dados:', error);
return res.status(500).send('Erro ao atualizar banco de dados.');
}
if (status === 'approved') {
// Pagamento foi aprovado, agora você pode liberar a licença
console.log('Pagamento aprovado!');
const getQuery = `SELECT
pdt.idsistemas,
usu.idusuarios,
usu.cpf,
tpl.dias
FROM produtosvendas pdv
INNER JOIN usuarios usu ON usu.idusuarios = pdv.idusuarios
INNER JOIN produtos pdt ON pdt.idprodutos = pdv.idprodutos
INNER JOIN tiposlicenca tpl ON tpl.idtiposlicenca = pdt.idtiposlicenca
WHERE pdv.idprodutosvendas = ?;`;
mysqlConnection.query(getQuery, [vendaId], function (errGet, resGet) {
if (errGet) {
console.error('Erro ao consultar dados do usuario no banco de dados:', errGet);
return res.status(500).send('Erro ao consultar dados do usuario no banco de dados');
}
const dados = resGet[0];
const sqlGenerateSerial = `CALL GerarChave(?, ?, @serialKey); SELECT @serialKey AS serial;`;
mysqlConnection.query(sqlGenerateSerial, [dados.cpf, dados.dias], function (err, results) {
if (err) {
console.error('Erro ao gerar serial:', err);
return res.status(500).send('Erro ao gerar serial');
}
const serial = results[1][0].serial;
const sqlInsertSerial = `
INSERT INTO chaves (licenca, data_criacao, data_validade, idusuarios, usada, idsistemas)
VALUES (?, NOW(), DATE_ADD(NOW(), INTERVAL ? DAY), ?, 0, ?);
`;
mysqlConnection.query(sqlInsertSerial, [serial, dados.dias, dados.idusuarios, dados.idsistemas], function (errInsert) {
if (errInsert) {
console.error('Erro ao registrar serial:', errInsert);
return res.status(500).send('Erro ao registrar serial');
}
res.status(200).send('Licença liberada com sucesso!');
});
});
});
} else {
// Se o status for diferente de "approved", trate adequadamente (pendente, recusado, etc.)
console.log('Status do pagamento:', status);
res.status(200).send('Pagamento não aprovado.');
}
});
})
.catch(error => {
console.error('Erro ao consultar o pagamento:', error);
res.status(500).send('Erro ao consultar o pagamento.');
});
} else {
res.status(400).send('Tipo de notificação não suportado.');
}
});
router.get('/getProductsSell/:idsistemas', function (req, res) {
const idsistemas = parseInt(req.params.idsistemas);
ExecuteQuery(`SELECT
pdt.idprodutos,
pdt.valor,
tpl.descricao
FROM produtos pdt
INNER JOIN tiposlicenca tpl ON tpl.idtiposlicenca = pdt.idtiposlicenca
WHERE pdt.idsistemas = ${idsistemas} AND pdt.ativado = 1
ORDER BY tpl.dias ASC;;`, req, res);
});
router.get('/getAllProducts/:idsistemas', function (req, res) {
const idsistemas = parseInt(req.params.idsistemas);
ExecuteQuery(`SELECT
pdt.idprodutos,
pdt.valor,
pdt.ativado,
tpl.idtiposlicenca,
tpl.descricao,
(SELECT COUNT(1) FROM produtosvendas pdv WHERE pdv.idprodutos = pdt.idprodutos) AS vendas,
(SELECT COUNT(1) FROM produtosvendas pdv WHERE pdv.idprodutos = pdt.idprodutos AND pdv.status = 'approved') AS compensados
FROM produtos pdt
INNER JOIN tiposlicenca tpl ON tpl.idtiposlicenca = pdt.idtiposlicenca
WHERE pdt.idsistemas = ${idsistemas}
ORDER BY pdt.valor ASC;`, req, res);
});
router.post('/insertProduto', function (req, res, next) {
const idsistemas = parseInt(req.body.idsistemas);
const idtiposlicenca = parseInt(req.body.idtiposlicenca);
const valor = req.body.valor;
const ativado = req.body.ativado ? 1 : 0;
const sqlQuery = `INSERT INTO produtos (idsistemas, idtiposlicenca, valor, ativado)
VALUES (${idsistemas}, ${idtiposlicenca}, ${valor}, ${ativado});`;
ExecuteQuery(sqlQuery, req, res);
});
router.patch('/updateProduto', function (req, res, next) {
const idprodutos = parseInt(req.body.idprodutos);
const idsistemas = parseInt(req.body.idsistemas);
const idtiposlicenca = parseInt(req.body.idtiposlicenca);
const valor = req.body.valor;
const ativado = req.body.ativado ? 1 : 0;
const sqlQuery = `update produtos set
idtiposlicenca = ${idtiposlicenca},
valor = ${valor},
ativado = ${ativado}
where idprodutos = ${idprodutos};`;
ExecuteQuery(sqlQuery, req, res);
});
router.delete('/deleteProduto/:idprodutos', function (req, res, next) {
const idprodutos = req.params.idprodutos;
const sqlQuery = `DELETE FROM produtos WHERE idprodutos = ${idprodutos};`;
ExecuteQuery(sqlQuery, req, res);
});
return router;
}
module.exports = routes;

View File

@ -0,0 +1,100 @@
require('dotenv').config();
const express = require('express');
const router = express.Router();
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT || 587),
secure: process.env.SMTP_SECURE === 'true',
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
});
// Teste de conexão SMTP ao iniciar
transporter.verify(function (error, success) {
if (error) {
console.error('[SMTP] Falha na conexão:', error);
} else {
console.log('[SMTP] Servidor pronto para enviar emails');
}
});
async function EnviarEmailFunc(Para, Assunto, Conteudo, res) {
try {
if (!Para || !Assunto || !Conteudo) {
throw new Error('Para, Assunto e Conteudo são obrigatórios.');
}
const fromEmail = (process.env.SMTP_FROM_EMAIL || '').trim();
const fromName = (process.env.SMTP_FROM_NAME || 'Zendion, INC.').trim();
if (!fromEmail) {
throw new Error('SMTP_FROM_EMAIL não configurado no .env.');
}
const msg = {
to: Para,
from: {
name: fromName,
address: fromEmail
},
subject: Assunto,
text: Conteudo.replace(/<[^>]*>/g, ''),
html: Conteudo
};
const info = await transporter.sendMail(msg);
console.log('[EMAIL] Enviado:', info.messageId);
if (res) {
return res.status(200).send({
sucesso: true,
message: 'Email enviado com sucesso',
messageId: info.messageId
});
}
return {
sucesso: true,
messageId: info.messageId
};
} catch (error) {
console.error('[EMAIL] Erro ao enviar:', error);
if (res) {
return res.status(500).send({
sucesso: false,
message: 'Erro ao enviar o e-mail',
detalhe: error.message
});
}
return {
sucesso: false,
erro: error.message
};
}
}
router.post('/enviarEmail', function (req, res) {
const { Para, Assunto, Conteudo } = req.body;
if (!Para || !Assunto || !Conteudo) {
return res.status(400).send({
error: 'Os campos "Para", "Assunto" e "Conteudo" são obrigatórios.'
});
}
EnviarEmailFunc(Para, Assunto, Conteudo, res);
});
module.exports = {
router,
EnviarEmailFunc
};

View File

@ -0,0 +1,406 @@
require('../connector');
const express = require('express');
const router = express.Router();
const nodemailer = require('nodemailer');
const formidable = require('formidable');
const shell = require('shelljs');
var admin = require("firebase-admin");
var serviceAccount = require("./../gca-control-bcb9b-firebase-adminsdk-pf0od-2e672cfd0a.json");
var routes = function () {
//#region EMAIL
const Email = 'gcacontrol@gmail.com';
const Senha = 'gcaCtrl123';
var transporter = nodemailer.createTransport({
service: 'gmail',
//host: "email-ssl.com.br",
//port: 465,
//secure: true,
auth: {
user: Email,
pass: Senha,
//user: 'iyrconline@myrobot.com.br',
//pass: 'iyrcMyrobot@2255',
//user: 'myrobotschool@gmail.com',
//pass: 'myrobot2020**==',
}
});
function EnviarEmail(Dados, idemail) {
ConexaoMySQL_GCA.query(`SELECT
em.*
FROM emails em
where em.idemails = ${idemail};`, function (error, results, fields) {
if (!error && results.length > 0) {
let Mensagem = results[0]['texto'];
for (let i = 0; i < 10; i++) {
if (Dados['descricao'])
Mensagem = Mensagem.replace('>DISPOSITIVO<', Dados['descricao']);
if (Dados['nome'])
Mensagem = Mensagem.replace('>CLIENTE<', Dados['nome']);
if (Dados['Segredo'])
Mensagem = Mensagem.replace('>SEGREDO<', Dados['Segredo']);
}
var mailOptions = {
from: "GCA Control <gcacontrol@gmail.com>",
to: Dados['email'],
subject: results[0]['assunto'],
html: Mensagem
};
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
console.log(error);
}
else {
console.log('Email enviado para: ' + mailOptions.to + ' - ' + info.response);
ConexaoMySQL_GCA.query(`insert into emailsenviados (iddispositivos, idemails) values (${Dados['iddispositivos']}, ${idemail});`, function (error2, results2, fields2) {
});
}
/*
// envia sms
const Hash = "0cc6fcb95a28d506def8259e1e8dd90e";
const https = require('https');
https.get(`https://smsmobile.com.br/sms/shortcode/routes/sms.php?hash=${Hash}&mensagem=${encodeURI(Mensagem)}&numero=${Telefone}`, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
console.log("SMS enviado com sucesso para " + Dispositivo['telefone']);
});
}).on("error", (err) => {
console.log("Erro ao enviar SMS: " + err.message);
});
*/
});
}
});
}
router.get('/sendEmail/:iddispositivos/:idemail', (req, res) => {
const iddispositivos = parseInt(req.params.iddispositivos);
const idemail = parseInt(req.params.idemail);
ConexaoMySQL_GCA.query(`SELECT di.iddispositivos, di.descricao, di.alarme, cl.email, cl.telefone, cl.token FROM dispositivos di inner join clientes cl on cl.idclientes = di.idclientes WHERE di.iddispositivos = ${iddispositivos};`, function (error2, results2, fields2) {
EnviarEmail(results2[0], idemail);
console.log(req.originalUrl + "/ success 200");
res.send(true);
});
});
//#endregion
//#region DISPOSITIVOS
router.get('/getDispositivos', (req, res) => {
ConexaoMySQL_GCA.query(`select
di.iddispositivos,
di.qrcode,
di.idkits,
ki.descricao as 'Kit',
di.insert_date
from dispositivos di
left join clientes cl on cl.idclientes = di.idclientes
inner join kits ki on ki.idkits = di.idkits
where cl.idclientes is null;`, function (error, results, fields) {
if (error) throw error;
if (results) {
console.log(req.originalUrl + "/ success 200");
res.send(results);
}
else {
console.log(req.originalUrl + "/ success 500");
res.send([]);
}
});
});
router.get('/getDispositivosCliente/:segredo', (req, res) => {
const segredo = req.params.segredo;
ConexaoMySQL_GCA.query(`select
di.iddispositivos,
di.qrcode,
di.descricao,
di.idclientes,
cl.nome as 'Cliente',
di.idkits,
ki.descricao as 'Kit',
di.alarme,
di.statusalarme,
di.portao,
di.lampada,
di.statuslampada,
di.update_date
from dispositivos di
inner join clientes cl on cl.idclientes = di.idclientes
inner join kits ki on ki.idkits = di.idkits
where cl.segredo = '${segredo}';`, function (error, results, fields) {
if (error) throw error;
if (results) {
console.log(req.originalUrl + "/ success 200");
res.send(results);
}
else {
console.log(req.originalUrl + "/ success 500");
res.send([]);
}
});
});
router.get('/getStatus/:segredo/:iddispositivos', (req, res) => {
const segredo = req.params.segredo;
const iddispositivos = parseInt(req.params.iddispositivos);
ConexaoMySQL_GCA.query(`select
di.alarme,
di.statusalarme,
di.portao,
di.lampada,
di.statuslampada
FROM dispositivos di
inner join clientes cl on cl.idclientes = di.idclientes
where cl.segredo = '${segredo}' and di.iddispositivos = ${iddispositivos};`, function (error, results, fields) {
if (error) throw error;
if (results[0]) {
console.log(req.originalUrl + "/ success 200");
res.send(results[0]);
}
else {
console.log(req.originalUrl + "/ success 500");
res.send(false);
}
});
});
router.get('/setStatus/:segredo/:iddispositivos/:Atuador/:Estado', (req, res) => {
const segredo = req.params.segredo;
const iddispositivos = parseInt(req.params.iddispositivos);
const Atuador = parseInt(req.params.Atuador);
const Estado = parseInt(req.params.Estado);
ConexaoMySQL_GCA.query(`UPDATE dispositivos D
inner join clientes C on C.idclientes = D.idclientes
SET ${
(Atuador == 0) ? 'alarme' :
(Atuador == 1) ? 'statusalarme' :
(Atuador == 2) ? 'portao' :
(Atuador == 3) ? 'lampada' :
(Atuador == 4) ? 'statuslampada' :
''} = ${Estado}
WHERE D.iddispositivos = ${iddispositivos} and C.segredo = '${segredo}';`, function (error, results, fields) {
if (error) throw error;
console.log(req.originalUrl + "/ success 200");
let Resposta = shell.exec(`sudo mosquitto_pub -h gcacontrol.mooo.com -p 1883 -t "${segredo}" -m "${Atuador}:${Estado}" -u "tht" -P "123"`);
if (Atuador == 1 && Estado == 1) {
ConexaoMySQL_GCA.query(`select di.iddispositivos, di.descricao, di.alarme, cl.email, cl.telefone, cl.token from dispositivos di inner join clientes cl on cl.idclientes = di.idclientes where di.iddispositivos = ${iddispositivos};`, function (error2, results2, fields2) {
if (results2[0]['alarme'] == 1) {
EnviarEmail(results2[0], 1);
const DadosMensagem = {
title: "Alarme Acionado!",
message: "Seu alarme no dispositivo " + results2[0]['descricao'] + " foi acionado!"
};
EnviarNotificacao(DadosMensagem, results2[0]['token']);
}
res.send(true);
});
}
else {
res.send(true);
}
});
});
router.post('/vincularDispositivo', (req, res) => {
const descricao = req.body.descricao;
const segredo = req.body.segredo;
const iddispositivos = parseInt(req.body.iddispositivos);
ConexaoMySQL_GCA.query(`UPDATE dispositivos set
descricao = '${descricao}',
idclientes = (select idclientes from clientes where segredo = '${segredo}')
where iddispositivos = ${iddispositivos};`, function (error, results, fields) {
if (!error) {
console.log(req.originalUrl + "/ success 200");
res.send(true);
}
else {
console.log(error);
console.log(req.originalUrl + "/ error 500");
res.send(false);
}
});
});
//#endregion
//#region CLIENTES
router.post('/insertCliente', (req, res) => {
const nome = req.body.nome;
const cidade = req.body.cidade;
const telefone = req.body.telefone;
const email = req.body.email;
const cpf = req.body.cpf;
ConexaoMySQL_GCA.query(`select * from clientes where cpf = '${cpf}' or email = '${email}';`, function (errorS, resultsS, fieldsS) {
if (resultsS.length == 0) {
GeraSegredo(function(segredo) {
ConexaoMySQL_GCA.query(`insert into clientes (nome, cidade, telefone, email, segredo, cpf)
values ('${nome}', '${cidade}', '${telefone}', '${email}', '${segredo}', '${cpf}');`, function (error, results, fields) {
if (error) throw error;
if (!error) {
console.log(req.originalUrl + "/ success 200");
let Cliente = {
nome: nome,
email: email,
telefone: telefone,
Segredo: segredo
}
EnviarEmail(Cliente, 2);
res.redirect(`http://${req.headers.host.split(':')[0]}/cadastro.html?segredo=${segredo}`);
}
else {
console.log(req.originalUrl + "/ error 500");
res.redirect(`http://${req.headers.host.split(':')[0]}/cadastro.html?erro=0`);
}
});
});
}
else {
res.redirect(`http://${req.headers.host.split(':')[0]}/cadastro.html?erro=1`);
}
});
});
function GeraSegredo(callback) {
const caracteresPermitidos = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789!@$?_-";
let secret = "";
ConexaoMySQL_GCA.query(`select segredo from clientes;`, function (error, results, fields) {
for (let i = 0; i < 15; i++) {
secret += caracteresPermitidos[Math.floor(Math.random() * (caracteresPermitidos.length))];
}
let Aprovado = true;
for (let i = 0; i < results.length; i++) {
if (results[i]['segredo'] == secret)
Aprovado = false;
}
if (Aprovado)
callback(secret);
else
GeraSegredo(callback);
});
}
router.get('/setClienteToken/:segredo/:token', (req, res) => {
const segredo = req.params.segredo;
const token = req.params.token;
ConexaoMySQL_GCA.query(`UPDATE clientes SET
token = '${token}'
where segredo = '${segredo}';`, function (error, results, fields) {
if (error) throw error;
if (results) {
console.log(req.originalUrl + "/ success 200");
res.send(true);
}
else {
console.log(req.originalUrl + "/ success 500");
res.send(false);
}
});
});
router.post('/salvarConfigCliente', (req, res) => {
const Segredo = req.body.Segredo;
const NomeRede = req.body.NomeRede;
const SenhaRede = req.body.SenhaRede;
const Dispositivo = parseInt(req.body.Dispositivo);
ConexaoMySQL_GCA.query(`UPDATE clientes set
nomeRede = '${NomeRede}',
senhaRede = '${SenhaRede}',
indexDispositivo = ${Dispositivo}
where segredo = '${Segredo}';`, function (error, results, fields) {
if (!error) {
console.log(req.originalUrl + "/ success 200");
res.send(true);
}
else {
console.log(error);
console.log(req.originalUrl + "/ error 500");
res.send(false);
}
});
});
//#endregion
//#region CONFIGURACOES
router.get('/getConfig', (req, res) => {
ConexaoMySQL_GCA.query(`select * from configuracoes;`, function (error, results, fields) {
if (error) throw error;
if (results) {
console.log(req.originalUrl + "/ success 200");
res.send(results[0]);
}
else {
console.log(req.originalUrl + "/ success 500");
res.send([]);
}
});
});
//#endregion
//#region PUSH NOTIFICATION FIREBASE
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
projectId: "gca-control-bcb9b"
});
function EnviarNotificacao(DadosMensagem, registrationToken) {
// This registration token comes from the client FCM SDKs.
const message = {
data: DadosMensagem,
token: registrationToken
};
// Send a message to the device corresponding to the provided
// registration token.
admin.messaging().send(message).then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
}).catch((error) => {
console.log('Error sending message:', error);
});
}
router.post('/sendPushNotification', (req, res) => {
const titulo = req.body.titulo;
const mensagem = req.body.mensagem;
const segredo = req.body.segredo;
ConexaoMySQL_GCA.query(`SELECT token FROM clientes WHERE segredo = '${segredo}';`, function (error, results, fields) {
if (!error) {
const DadosMensagem = {
title: titulo,
message: mensagem
};
EnviarNotificacao(DadosMensagem, results[0]['token']);
console.log(req.originalUrl + "/ success 200");
res.send(true);
}
else {
console.log(error);
console.log(req.originalUrl + "/ error 500");
res.send(false);
}
});
});
//#endregion
return router;
}
module.exports = routes;

View File

@ -0,0 +1,80 @@
require('../connector');
const express = require('express');
const router = express.Router();
const formidable = require('formidable');
var routes = function () {
router.get('/getAllNoticias', function (req, res) {
ExecuteQuery(`SELECT * FROM noticias;`, req, res);
});
router.get('/getNoticiasEn', function (req, res, next) {
const sqlQuery = `select
idnoticias,
titulo,
descricao,
CASE WHEN habilitado = 1 THEN 'Sim' ELSE 'Não' END AS habilitado,
link
FROM noticias
WHERE habilitado = 1;`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getNoticiaByID/:idnoticias', function (req, res, next) {
const idnoticias = req.params.idnoticias;
const sqlQuery = `SELECT * FROM noticias WHERE idnoticias = ${idnoticias};`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/insertNoticias', function (req, res, next) {
const idnoticias = req.body.idnoticias;
const titulo = req.body.titulo;
const descricao = req.body.descricao;
const habilitado = req.body.habilitado ? 1 : 0;
const link = req.body.link;
const sqlQuery = `INSERT INTO noticias (titulo, descricao, habilitado, link)
VALUES ('${titulo}', '${descricao}', ${habilitado}, '${link}');`;
ExecuteQuery(sqlQuery, req, res);
});
router.patch('/updateNoticias', function (req, res, next) {
const idnoticias = req.body.idnoticias;
const titulo = req.body.titulo;
const descricao = req.body.descricao;
const habilitado = req.body.habilitado ? 1 : 0;
const link = req.body.link;
const sqlQuery = `update noticias set
titulo = '${titulo}',
descricao = '${descricao}',
habilitado = ${habilitado},
link = '${link}'
where idnoticias = ${idnoticias};`;
ExecuteQuery(sqlQuery, req, res);
});
router.delete('/deleteNoticias/:idnoticias', function (req, res, next) {
const idnoticias = req.params.idnoticias;
const sqlQuery = `DELETE FROM noticias WHERE idnoticias = ${idnoticias};`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/copy', (req, res) => {
let form = new formidable.IncomingForm();
form.maxFileSize = 600 * 1024 * 1024;
form.parse(req, function(err,fields,files){
const fs = require('fs');
var idnoticias = fields.idnoticias;
var pathImagem = files.imagem.path;
var dir = '/var/www/html/imagens/noticias/';
fs.rename(pathImagem, dir + "noticia_" + idnoticias + ".jpg", function (err) {
console.log("arquivo copiado com sucesso");
res.send(true);
});
});
});
return router;
}
module.exports = routes;

View File

@ -0,0 +1,24 @@
require('../connector');
const express = require('express');
const router = express.Router();
var routes = function () {
router.get('/getAllLogs', function (req, res) {
ExecuteQuery(`select * from vw_oriontard_logs where dataLog > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 3 MONTH);`, req, res);
});
router.post('/insertLog', function (req, res) {
const cpf = req.body.cpf;
const perfil = req.body.perfil;
const latitude = req.body.longitude;
const longitude = req.body.latitude;
const log = req.body.log;
ExecuteQuery(`insert into orionTardProLogs (cpf, perfil, latitude, longitude, log) values (?, ?, ?, ?, ?);`,
req, res, [cpf, perfil, latitude, longitude, log]);
});
return router;
}
module.exports = routes;

View File

@ -0,0 +1,585 @@
require('../connector');
const express = require('express');
const router = express.Router();
var routes = function () {
//#region LOGIN
router.post('/login', function(req, res) {
const usuario = req.body.usuario;
const senha = req.body.senha;
ExecuteQueryPriorie(`select * from usuarios where usuario = '${usuario}' and senha = '${senha}' and habilitado = 1;`, req, res);
});
//#endregion
//#region USUARIOS
router.post('/cadastrarUsuario', function(req, res) {
const idusuarios = parseInt(req.body.idusuarios);
const iddepartamentos = req.body.iddepartamentos;
const nome = req.body.nome;
const usuario = req.body.usuario;
const senha = req.body.senha;
const habilitado = req.body.habilitado ? 1 : 0;
const email = req.body.email;
ExecuteQueryPriorie(`insert into usuarios (iddepartamentos, nome, senha, usuario, habilitado, email) values
(${iddepartamentos}, '${nome}', '${senha}', '${usuario}', ${habilitado}, '${email}');`, req, res);
});
router.patch('/atualizarUsuario', function(req, res) {
const idusuarios = parseInt(req.body.idusuarios);
const iddepartamentos = parseInt(req.body.iddepartamentos);
const nome = req.body.nome;
const usuario = req.body.usuario;
const senha = req.body.senha;
const habilitado = req.body.habilitado ? 1 : 0;
const email = req.body.email;
ExecuteQueryPriorie(`update usuarios set
iddepartamentos = ${iddepartamentos},
nome = '${nome}',
senha = '${senha}',
usuario = '${usuario}',
habilitado = ${habilitado},
email = '${email}'
where idusuarios = ${idusuarios};`, req, res);
});
router.delete('/removerUsuario/:idusuarios', function(req, res) {
const idusuarios = parseInt(req.params.idusuarios);
ExecuteQueryPriorie(`delete from usuarios
where idusuarios = ${idusuarios};`, req, res);
});
router.get('/getAllUsuarios', function(req, res) {
ExecuteQueryPriorie(`select
us.* ,
dp.descricao as 'departamento'
from usuarios us
inner join departamentos dp on dp.iddepartamentos = us.iddepartamentos
order by nome asc;`, req, res);
});
router.get('/getUsuariosEnabled', function(req, res) {
ExecuteQueryPriorie(`select * from usuarios where habilitado = 1;`, req, res);
});
router.get('/getUsuario/:idusuarios', function(req, res) {
const idusuarios = parseInt(req.params.idusuarios);
ExecuteQueryPriorie(`select * from usuarios
where idusuarios = ${idusuarios};`, req, res);
});
//#endregion
//#region TAREFAS
router.post('/cadastrarTarefa', function(req, res) {
const idtarefas = parseInt(req.body.idtarefas);
const criadapor = parseInt(req.body.criadapor);
const descricao = fixApostrofe(req.body.descricao);
const idprojetos = parseInt(req.body.idprojetos);
const idtopicos = parseInt(req.body.idtopicos);
const idstatus = parseInt(req.body.idstatus);
const idtipotarefa = parseInt(req.body.idtipotarefa);
const previsaoinicio = req.body.previsaoinicio;
const iniciadoem = req.body.iniciadoem;
const previsaotermino = req.body.previsaotermino;
const concluidoem = req.body.concluidoem;
const prioridade = parseInt(req.body.prioridade);
const observacao = fixApostrofe(req.body.observacao);
ExecuteQueryPriorie(`insert into tarefas (criadapor, descricao, idprojetos, idtopicos, idstatus, idtipotarefa, ${(previsaoinicio) ? 'previsaoinicio,' : ''} ${(iniciadoem) ? 'iniciadoem,' : ''} ${(previsaotermino) ? 'previsaotermino,' : ''} ${(concluidoem) ? 'concluidoem,' : ''} prioridade, observacao) values
(${criadapor}, '${descricao}', ${idprojetos}, ${idtopicos}, ${idstatus}, ${idtipotarefa}, ${(previsaoinicio) ? "'" + previsaoinicio + "'," : ''} ${(iniciadoem) ? "'" + iniciadoem + "'," : ''} ${(previsaotermino) ? "'" + previsaotermino + "'," : ''} ${(concluidoem) ? "'" + concluidoem + "'," : ''} ${prioridade}, '${observacao}');`, req, res);
});
router.patch('/atualizarTarefa', function(req, res) {
const idtarefas = parseInt(req.body.idtarefas);
const criadapor = parseInt(req.body.criadapor);
const descricao = fixApostrofe(req.body.descricao);
const idprojetos = parseInt(req.body.idprojetos);
const idtopicos = parseInt(req.body.idtopicos);
const idstatus = parseInt(req.body.idstatus);
const idtipotarefa = parseInt(req.body.idtipotarefa);
const previsaoinicio = req.body.previsaoinicio;
const iniciadoem = req.body.iniciadoem;
const previsaotermino = req.body.previsaotermino;
const concluidoem = req.body.concluidoem;
const prioridade = parseInt(req.body.prioridade);
const observacao = fixApostrofe(req.body.observacao);
ExecuteQueryPriorie(`update tarefas set
criadapor = ${criadapor},
descricao = '${descricao}',
idprojetos = ${idprojetos},
idtopicos = ${idtopicos},
idstatus = ${idstatus},
idtipotarefa = ${idtipotarefa},
${(previsaoinicio) ? "previsaoinicio = '" + previsaoinicio + "',": ''}
${(iniciadoem) ? "iniciadoem = '" + iniciadoem + "',": ''}
${(previsaotermino) ? "previsaotermino = '" + previsaotermino + "',": ''}
${(concluidoem) ? "concluidoem = '" + concluidoem + "',": ''}
prioridade = ${prioridade},
observacao = '${observacao}'
where idtarefas = ${idtarefas};`, req, res, function(Status) {
if (Status == 200 && idstatus == 4) {
const SQLDados = `select
A.*,
us.nome as 'Gerente',
us.email as 'EmailGerente',
us2.nome
from (select distinct
ta.idtarefas,
date_format(ta.concluidoem, '%d/%m/%Y') as 'ConcluidaEm',
dm.idusuarios,
pr.descricao as 'Projeto',
tp.descricao as 'Topico',
ta.descricao as 'Tarefa'
from tarefas ta
inner join projetos pr on pr.idprojetos = ta.idprojetos
inner join topicos tp on tp.idtopicos = ta.idtopicos
inner join departamentosmembros dm on dm.iddepartamentos = pr.iddepartamentos
inner join usuarios us on us.iddepartamentos = dm.iddepartamentos and dm.gestor = 1
where ta.idtarefas = ${idtarefas}) A
inner join usuarios us on us.idusuarios = A.idusuarios
inner join projetosmembros pm on pm.idtarefas = A.idtarefas
inner join usuarios us2 on us2.idusuarios = pm.idusuarios;`;
connection.query(SQLDados, function(error, Dados, fields) {
if (Dados && Dados[0]['EmailGerente'] != "" && Dados[0]['EmailGerente'] != null) {
let Membros = "";
for (let i = 0; i < Dados.length; i++) {
if (i > 0)
Membros += ", ";
Membros += Dados[i]['nome'];
}
const Mensagem = `Olá ${Dados[0]['Gerente']}!<br>
Uma tarefa do projeto ${Dados[0]['Projeto']} foi marcada com concluída:<br><br>
Projeto: ${Dados[0]['Projeto']}<br>
Tópico: ${Dados[0]['Topico']}<br>
Tarefa: ${Dados[0]['Tarefa']}<br>
Data de Conclusão: ${Dados[0]['ConcluidaEm']}<br>
Membros: ${Membros}<br><br>
Acesse o sistema para conferir!<br><br>
<a href="http://priorie.mooo.com/login">Clique aqui para acessar o sistema</a>`;
EnviarEmail(Mensagem, "Tarefa Concluída", Dados[0]['EmailGerente']);
}
});
}
});
});
router.delete('/removerTarefa/:idtarefas', function(req, res) {
const idtarefas = parseInt(req.params.idtarefas);
ExecuteQueryPriorie(`delete from tarefas
where idtarefas = ${idtarefas};`, req, res);
});
router.get('/getAllTarefas', function(req, res) {
ExecuteQueryPriorie(`select * from tarefas;`, req, res);
});
router.get('/getTarefa/:idtarefas', function(req, res) {
const idtarefas = parseInt(req.params.idtarefas);
ExecuteQueryPriorie(`select * from tarefas
where idtarefas = ${idtarefas};`, req, res);
});
router.get('/getTarefasUsuario/:idusuarios/:iddepartamentos/:IdsStatus/:idprojetos/:idtopicos', function(req, res) {
const idusuarios = parseInt(req.params.idusuarios);
const iddepartamentos = parseInt(req.params.iddepartamentos);
const idprojetos = parseInt(req.params.idprojetos);
const idtopicos = parseInt(req.params.idtopicos);
const IdsStatus = req.params.IdsStatus;
let Condicao = `pr.iddepartamentos = ${iddepartamentos} /*${(idusuarios == 0) ? 'or' : 'and'}*/and ((`;
if (idusuarios == 0)
Condicao += `pm.idusuarios in (select distinct dm.idusuarios from departamentosmembros dm where dm.iddepartamentos = ${iddepartamentos})`;
else
Condicao += "pm.idusuarios = " + idusuarios;
Condicao += " and st.idstatus in " + IdsStatus;
if (idprojetos > 0)
Condicao += " and ta.idprojetos = " + idprojetos;
if (idtopicos > 0)
Condicao += " and ta.idtopicos = " + idtopicos;
if (IdsStatus.includes("100")) {
//Condicao += `) or (0 = (select count(*) from projetosmembros pm1 where pm1.idtarefas = ta.idtarefas)${(idprojetos > 0) ? ' and ta.idprojetos = ' + idprojetos : ''}))`;
Condicao += `) or (0 = (select count(*) from projetosmembros pm1 inner join tarefas ta1 on ta1.idtarefas = pm1.idtarefas inner join projetos pr1 on pr1.idprojetos = ta1.idprojetos where pm1.idtarefas = ta.idtarefas)${(idprojetos > 0) ? ' and ta.idprojetos = ' + idprojetos : ''}))`;
}
else
Condicao += "))";
let sqlQuery = `select
A.*,
GROUP_CONCAT(us.nome SEPARATOR ', ') as 'Membros',
GROUP_CONCAT(us.idusuarios SEPARATOR ',') as 'MembrosIDs'
from (
SELECT distinct
ta.idtarefas,
ta.descricao,
ta.criadapor,
(select us2.nome from usuarios us2 where us2.idusuarios = ta.criadapor) as 'criadapor_d',
ta.idprojetos,
pr.descricao as 'projeto',
pr.iddepartamentos,
ta.idtopicos,
tp.descricao as 'topico',
ta.idstatus,
ta.idstatus as 'idstatus_c',
st.descricao as 'status',
st.prioridade as 'prioridade_s',
st.color,
st.background,
ta.idtipotarefa,
tt.descricao as 'tipotarefa',
ta.previsaoinicio,
ta.iniciadoem,
ta.previsaotermino,
ta.concluidoem,
ta.prioridade,
pd.descricao as 'prioridade_d',
ta.observacao,
date_format(ta.insert_date, '%d/%m/%Y %H:%i') as 'insert_date',
(select count(*) from projetosmembros pm2 where pm2.idtarefas = ta.idtarefas) as 'QtdMembros'
FROM tarefas ta
inner join projetos pr on pr.idprojetos = ta.idprojetos
inner join topicos tp on tp.idtopicos = ta.idtopicos
inner join status st on st.idstatus = ta.idstatus
inner join tipotarefa tt on tt.idtipotarefa = ta.idtipotarefa
left join projetosmembros pm on pm.idtarefas = ta.idtarefas
inner join prioridades pd on pd.prioridade = ta.prioridade
where ${Condicao}) A
left join projetosmembros pm on pm.idtarefas = A.idtarefas
left join usuarios us on us.idusuarios = pm.idusuarios
group by A.idtarefas
order by A.prioridade_s desc, A.prioridade asc, A.idtipotarefa asc;`;
//console.log(sqlQuery);
ExecuteQueryPriorie(sqlQuery, req, res);
});
router.get('/getMembrosTarefa/:idtarefas', function(req, res) {
const idtarefas = parseInt(req.params.idtarefas);
ExecuteQueryPriorie(`SELECT
pm.idprojetosmembros,
pm.idusuarios,
pm.idtarefas,
us.nome
FROM projetosmembros pm
left join usuarios us on us.idusuarios = pm.idusuarios
where pm.idtarefas = ${idtarefas};`, req, res);
});
router.delete('/removerMembroTarefa/:idtarefas/:idusuarios', function(req, res) {
const idtarefas = parseInt(req.params.idtarefas);
const idusuarios = parseInt(req.params.idusuarios);
ExecuteQueryPriorie(`delete from projetosmembros
where idtarefas = ${idtarefas} and idusuarios = ${idusuarios};`, req, res);
});
router.post('/cadastrarTarefaMembros', function(req, res) {
const idtarefas = parseInt(req.body.idtarefas);
const idusuarios = parseInt(req.body.idusuarios);
ExecuteQueryPriorie(`insert into projetosmembros (idusuarios, idtarefas)
values (${idusuarios}, ${idtarefas});`, req, res, function(Status, idprojetosmembros) {
if (Status != 200) {
return;
}
const SQLDados = `select
pm.idusuarios,
us.nome,
us.email,
pr.descricao as 'Projeto',
tp.descricao as 'Topico',
ta.descricao as 'Tarefa',
date_format(ta.previsaoinicio, '%d/%m/%Y') as 'Inicio',
date_format(ta.previsaotermino, '%d/%m/%Y') as 'Previsao',
pd.descricao as 'Prioridade',
dp.descricao as 'Departamento'
from projetosmembros pm
inner join tarefas ta on ta.idtarefas = pm.idtarefas
inner join projetos pr on pr.idprojetos = ta.idprojetos
inner join topicos tp on tp.idtopicos = ta.idtopicos
inner join prioridades pd on pd.prioridade = ta.prioridade
inner join usuarios us on us.idusuarios = pm.idusuarios
inner join departamentos dp on dp.iddepartamentos = pr.iddepartamentos
where idprojetosmembros = ${idprojetosmembros};`;
connection.query(SQLDados, function(error, Dados, fields) {
if (Dados && Dados[0]['email'] != "" && Dados[0]['email'] != null) {
const Mensagem = `Olá ${Dados[0]['nome']}!<br>
Você tem uma nova tarefa atribuída no sistema Priorie para o departamento ${Dados[0]['Departamento']}:<br><br>
Projeto: ${Dados[0]['Projeto']}<br>
Tópico: ${Dados[0]['Topico']}<br>
Tarefa: ${Dados[0]['Tarefa']}<br>
Prioridade: ${Dados[0]['Prioridade']}<br>
Previsão de início: ${Dados[0]['Inicio']}<br>
Previsão de término: ${Dados[0]['Previsao']}<br><br>
Acesse o sistema para conferir!<br><br>
<a href="http://priorie.mooo.com/login">Clique aqui para acessar o sistema</a>`;
EnviarEmail(Mensagem, "Nova Tarefa Atribuída", Dados[0]['email']);
}
});
});
});
router.get('/getTarefasPorMembro/:IdsStatus/:idprojetos/:idtopicos', function(req, res) {
const IdsStatus = req.params.IdsStatus;
const idprojetos = parseInt(req.params.idprojetos);
const idtopicos = parseInt(req.params.idtopicos);
let Condicao = "";
if (idprojetos > 0)
Condicao += " and ta.idprojetos = " + idprojetos;
if (idtopicos > 0)
Condicao += " and ta.idtopicos = " + idtopicos;
ExecuteQueryPriorie(`SELECT
pm.idusuarios,
pr.iddepartamentos,
ta.idstatus,
count(*) as 'QtdeTarefas'
FROM projetosmembros pm
inner join tarefas ta on ta.idtarefas = pm.idtarefas
inner join projetos pr on pr.idprojetos = ta.idprojetos
where ta.idstatus in ${IdsStatus}${Condicao}
group by pr.iddepartamentos, pm.idusuarios, ta.idstatus;`, req, res);
});
//#endregion
//#region STATUS
router.get('/getAllStatus', function(req, res) {
ExecuteQueryPriorie(`select * from status;`, req, res);
});
//#endregion
//#region DEPARTAMENTOS
router.get('/getAllDepartamentos', function(req, res) {
ExecuteQueryPriorie(`SELECT * from departamentos order by descricao asc;`, req, res);
});
router.get('/getDepartamento/:iddepartamentos', function(req, res) {
const iddepartamentos = parseInt(req.params.iddepartamentos);
ExecuteQueryPriorie(`SELECT * from departamentos where iddepartamentos = ${iddepartamentos} order by descricao asc;`, req, res);
});
router.get('/getDepartamentosMembro/:idusuarios', function(req, res) {
const idusuarios = parseInt(req.params.idusuarios);
ExecuteQueryPriorie(`select * from departamentosmembros where idusuarios = ${idusuarios};`, req, res);
});
router.get('/getDepMembroDetalhe/:idusuarios', function(req, res) {
const idusuarios = parseInt(req.params.idusuarios);
ExecuteQueryPriorie(`SELECT
dm.iddepartamentosmembros,
dm.idusuarios,
us.nome,
us.iddepartamentos as 'iddepartamentos_u',
dm.iddepartamentos,
dp.descricao,
dm.gestor,
dm.permitecadastro
FROM departamentosmembros dm
inner join usuarios us on us.idusuarios = dm.idusuarios
inner join departamentos dp on dp.iddepartamentos = dm.iddepartamentos
where dm.idusuarios = ${idusuarios}
order by us.nome asc;`, req, res);
});
router.get('/getMemborsDepartamento/:iddepartamentos', function(req, res) {
const iddepartamentos = parseInt(req.params.iddepartamentos);
ExecuteQueryPriorie(`SELECT
dm.iddepartamentosmembros,
dm.idusuarios,
us.nome,
us.iddepartamentos as 'iddepartamentos_u',
dm.iddepartamentos,
dp.descricao,
dm.gestor,
dm.permitecadastro
FROM departamentosmembros dm
inner join usuarios us on us.idusuarios = dm.idusuarios
inner join departamentos dp on dp.iddepartamentos = dm.iddepartamentos
where dm.iddepartamentos = ${iddepartamentos}
order by us.nome asc;`, req, res);
});
router.post('/cadastrarDepMembro', function(req, res) {
const iddepartamentosmembros = parseInt(req.body.iddepartamentosmembros);
const idusuarios = parseInt(req.body.idusuarios);
const iddepartamentos = parseInt(req.body.iddepartamentos);
const gestor = req.body.gestor ? 1 : 0;
const permitecadastro = req.body.permitecadastro ? 1 : 0;
ExecuteQueryPriorie(`insert into departamentosmembros (idusuarios, iddepartamentos, gestor, permitecadastro)
values (${idusuarios}, ${iddepartamentos}, ${gestor}, ${permitecadastro});`, req, res);
});
router.patch('/atualizarDepMembro', function(req, res) {
const iddepartamentosmembros = parseInt(req.body.iddepartamentosmembros);
const idusuarios = parseInt(req.body.idusuarios);
const iddepartamentos = parseInt(req.body.iddepartamentos);
const gestor = req.body.gestor ? 1 : 0;
const permitecadastro = req.body.permitecadastro ? 1 : 0;
ExecuteQueryPriorie(`update departamentosmembros set
idusuarios = ${idusuarios},
iddepartamentos = ${iddepartamentos},
gestpr = ${gestor},
permitecadastro = ${permitecadastro}
where idtopicos = ${idtopicos};`, req, res);
});
router.delete('/removerDepMembro/:iddepartamentosmembros', function(req, res) {
const iddepartamentosmembros = parseInt(req.params.iddepartamentosmembros);
ExecuteQueryPriorie(`delete from departamentosmembros
where iddepartamentosmembros = ${iddepartamentosmembros};`, req, res);
});
//#endregion
//#region PROJETOS
router.post('/cadastrarProjeto', function(req, res) {
const idprojetos = parseInt(req.body.idprojetos);
const iddepartamentos = parseInt(req.body.iddepartamentos);
const descricao = fixApostrofe(req.body.descricao);
ExecuteQueryPriorie(`insert into projetos (iddepartamentos, descricao) values
(${iddepartamentos}, '${descricao}');`, req, res);
});
router.patch('/atualizarProjeto', function(req, res) {
const idprojetos = parseInt(req.body.idprojetos);
const iddepartamentos = parseInt(req.body.iddepartamentos);
const descricao = fixApostrofe(req.body.descricao);
ExecuteQueryPriorie(`update projetos set
iddepartamentos = ${iddepartamentos},
descricao = '${descricao}'
where idprojetos = ${idprojetos};`, req, res);
});
router.delete('/removerProjeto/:idprojetos', function(req, res) {
const idprojetos = parseInt(req.params.idprojetos);
ExecuteQueryPriorie(`delete from projetos
where idprojetos = ${idprojetos};`, req, res);
});
router.get('/getAllProjetos', function(req, res) {
ExecuteQueryPriorie(`select * from projetos;`, req, res);
});
router.get('/getProjetosDepartamento/:iddepartamentos', function(req, res) {
const iddepartamentos = parseInt(req.params.iddepartamentos);
ExecuteQueryPriorie(`select * from projetos where iddepartamentos = ${iddepartamentos} order by descricao asc;`, req, res);
});
router.get('/getAllTiposTarefas', function(req, res) {
ExecuteQueryPriorie(`select * from tipotarefa;`, req, res);
});
//#endregion
//#region TOPICOS
router.post('/cadastrarTopico', function(req, res) {
const idtopicos = parseInt(req.body.idtopicos);
const idprojetos = parseInt(req.body.idprojetos);
const descricao = fixApostrofe(req.body.descricao);
ExecuteQueryPriorie(`insert into topicos (idprojetos, descricao) values
(${idprojetos}, '${descricao}');`, req, res);
});
router.patch('/atualizarTopico', function(req, res) {
const idtopicos = parseInt(req.body.idtopicos);
const idprojetos = parseInt(req.body.idprojetos);
const descricao = fixApostrofe(req.body.descricao);
ExecuteQueryPriorie(`update topicos set
idprojetos = ${idprojetos},
descricao = '${descricao}'
where idtopicos = ${idtopicos};`, req, res);
});
router.delete('/removerTopico/:idtopicos', function(req, res) {
const idtopicos = parseInt(req.params.idtopicos);
ExecuteQueryPriorie(`delete from topicos
where idtopicos = ${idtopicos};`, req, res);
});
router.get('/getAllTopicos', function(req, res) {
ExecuteQueryPriorie(`select * from topicos;`, req, res);
});
router.get('/getTopico/:idtopicos', function(req, res) {
const idtopicos = parseInt(req.params.idtopicos);
ExecuteQueryPriorie(`select * from topicos where idtopicos = ${idtopicos} order by descricao asc;`, req, res);
});
router.get('/getAllTopicosTarefa/:idprojetos', function(req, res) {
const idprojetos = parseInt(req.params.idprojetos);
ExecuteQueryPriorie(`select * from topicos where idprojetos = ${idprojetos} order by descricao asc;`, req, res);
});
//#endregion
//#region PRIORIDADES
router.get('/getAllPrioridades', function(req, res) {
ExecuteQueryPriorie(`select * from prioridades;`, req, res);
});
//#endregion
//#region ENVIO DE NOTIFICACOES
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: "zendioninc.contato@gmail.com",
pass: "gmailzendioninc",
}
});
/*var transporter = nodemailer.createTransport({
host: "email-ssl.com.br",
port: 465,
secure: true,
auth: {
user: 'staff@myrobot.com.br',
pass: 'stAff66**'
}
});*/
function EnviarEmail(Mensagem, Assunto, Email) {
var mailOptions = {
from: '"Zendion, INC" <zendioninc.contato@gmail.com>',
to: Email,
subject: Assunto,
html: Mensagem
};
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
console.log("Erro ao enviar e-mail para: " + mailOptions.to);
console.log(error);
}
else {
console.log('Email enviado com sucesso para: ' + mailOptions.to + ' - ' + info.response);
}
});
}
//#endregion
//#region FUNCOES
function fixApostrofe(content) {
for (let i = 0; i < 20; i++) {
content = content.replace("'", '&apos;');
}
return content;
}
//#endregion
return router;
}
module.exports = routes;

View File

@ -0,0 +1,355 @@
const express = require('express');
const router = express.Router();
const formidable = require('formidable');
var routes = function () {
router.get('/getSistemas', function (req, res) {
const sqlQuery = `SELECT * FROM sistemas order by nome asc;`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getSistemasEn', function (req, res) {
const sqlQuery = `SELECT * FROM sistemas WHERE habilitado = 1 order by nome asc;`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getSistemaByID/:idsistemas', function (req, res) {
const idsistemas = parseInt(req.params.idsistemas);
const sqlQuery = `SELECT * FROM sistemas WHERE idsistemas = ${idsistemas};`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getSistemaByIDLista/:idsistemas', function (req, res) {
const idsistemas = parseInt(req.params.idsistemas);
const sqlQuery = `SELECT
s.*,
p.descricao as plataforma,
t.descricao as tiposdesistema,
l.descricao as tiposlicenca
FROM sistemas s
INNER JOIN plataformas p ON s.idplataformas = p.idplataformas
INNER JOIN tiposdesistema t ON t.idtiposdesistema = s.idtiposdesistema
INNER JOIN tiposlicenca l ON s.idtiposlicenca = l.idtiposlicenca
WHERE idsistemas = ${idsistemas}
order by s.nome asc;`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getSistemaVersao/:idsistemas', function (req, res) {
const idsistemas = parseInt(req.params.idsistemas);
const sqlQuery = `SELECT versaoatual as VersaoAtual, nome_install as NomeInstall FROM sistemas WHERE idsistemas = ${idsistemas};`;
//ExecuteQuery(sqlQuery, req, res);
mysqlConnection.query(sqlQuery, [], function (error, results, fields) {
console.log(`${new Date().toUTCString()} - ${req.originalUrl} ${(error) ? ' 500 error' : ' 200 success'}`);
res.send(results[0])
});
});
router.get('/getTiposdesistema', function (req, res) {
const sqlQuery = `SELECT * FROM tiposdesistema order by descricao asc;`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getTiposdesistemaByID/:idtiposdesistema', function (req, res) {
const idtiposdesistema = parseInt(req.params.idtiposdesistema);
const sqlQuery = `SELECT * FROM tiposdesistema WHERE idtiposdesistema = ${idtiposdesistema};`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getTiposdelicenca', function (req, res) {
const sqlQuery = `SELECT * FROM tiposlicenca order by descricao asc;`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getTiposdelicencaByID/:idtiposlicenca', function (req, res) {
const idtiposlicenca = parseInt(req.params.idtiposlicenca);
const sqlQuery = `SELECT * FROM tiposlicenca WHERE idtiposlicenca = ${idtiposlicenca};`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getPlataformas', function (req, res) {
const sqlQuery = `SELECT * FROM plataformas order by descricao asc;`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getPlataformaByID/:idplataformas', function (req, res) {
let idplataformas = parseInt(req.params.idplataformas);
const sqlQuery = `SELECT * FROM plataformas WHERE idplataformas = ${idplataformas};`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getDetalhesByID/:idsistemas', function (req, res) {
const idsistemas = parseInt(req.params.idsistemas);
const sqlQuery = `SELECT * FROM sistemasdetalhes WHERE idsistemas = ${idsistemas} order by ordem asc;`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getDetalhesByIDEn/:idsistemas', function (req, res) {
const idsistemas = parseInt(req.params.idsistemas);
const sqlQuery = `SELECT * FROM sistemasdetalhes WHERE idsistemas = ${idsistemas} AND habilitado = 1 order by ordem asc;`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/insertSistemas', function (req, res) {
const idsistemas = req.body.idsistemas;
const nome = req.body.nome;
const descricao = req.body.descricao;
const versaoatual = req.body.versaoatual;
const habilitado = req.body.habilitado ? 1 : 0;
const idtiposdesistema = req.body.idtiposdesistema;
const idplataformas = req.body.idplataformas;
const nome_install = req.body.nome_install;
const notasdeatualizacao = req.body.notasdeatualizacao;
const idtiposlicenca = req.body.idtiposlicenca;
const sqlQuery = `INSERT INTO sistemas (nome, descricao, versaoatual, habilitado, downloads, idtiposdesistema, idplataformas, nome_install, notasdeatualizacao, idtiposlicenca)
VALUES ('${nome}', '${descricao}', '${versaoatual}', ${habilitado}, 0, ${idtiposdesistema}, ${idplataformas}, '${nome_install}', '${notasdeatualizacao}', ${idtiposlicenca});`;
ExecuteQuery(sqlQuery, req, res);
});
router.patch('/updateSistemas', function (req, res) {
const idsistemas = parseInt(req.body.idsistemas);
const nome = req.body.nome;
const descricao = req.body.descricao;
const versaoatual = req.body.versaoatual;
const habilitado = req.body.habilitado ? 1 : 0;
const idtiposdesistema = req.body.idtiposdesistema;
const idplataformas = req.body.idplataformas;
const nome_install = req.body.nome_install;
const notasdeatualizacao = req.body.notasdeatualizacao;
const idtiposlicenca = req.body.idtiposlicenca;
const sqlQuery = `UPDATE sistemas SET
nome = '${nome}',
descricao = '${descricao}',
versaoatual = '${versaoatual}',
habilitado = ${habilitado},
idtiposdesistema = ${idtiposdesistema},
idplataformas = ${idplataformas},
nome_install = '${nome_install}',
notasdeatualizacao = '${notasdeatualizacao}',
idtiposlicenca = ${idtiposlicenca}
WHERE idsistemas = ${idsistemas};`;
ExecuteQuery(sqlQuery, req, res);
});
router.delete('/deleteSistemas/:idsistemas', function (req, res) {
const idsistemas = parseInt(req.params.idsistemas);
const sqlQuery = `DELETE FROM sistemas WHERE idsistemas = ${idsistemas};`;
ExecuteQuery(sqlQuery, req, res);
});
router.delete('/deleteSistemaUsuario/:idsistemas/:idusuarios', function (req, res) {
const idsistemas = parseInt(req.params.idsistemas);
const idusuarios = parseInt(req.params.idusuarios);
const sqlQuery = `DELETE FROM sistemasusuario WHERE idsistemas = ${idsistemas} AND idusuarios = ${idusuarios};`;
ExecuteQuery(sqlQuery, req, res);
});
router.patch('/downloadSistema', function (req, res) {
const idsistemas = parseInt(req.body.idsistemas);
const sqlQuery = `UPDATE sistemas SET
downloads = downloads + 1
WHERE idsistemas = ${idsistemas};`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/insertSistemaUsuario', function (req, res) {
const idsistemas = parseInt(req.body.idsistemas);
const idusuarios = parseInt(req.body.idusuarios);
const sqlQuery = `INSERT INTO sistemasusuario (idsistemas, idusuarios, data_download)
SELECT DISTINCT ${idsistemas} AS idsistemas, ${idusuarios} AS idusuarios, NOW() AS data_download
FROM sistemasusuario su WHERE (SELECT COUNT(*) qtdDown FROM sistemasusuario WHERE idsistemas = ${idsistemas} AND idusuarios = ${idusuarios}) = 0;`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/copy', (req, res) => {
let form = new formidable.IncomingForm();
form.maxFileSize = 600 * 1024 * 1024;
form.parse(req, function(err,fields,files){
const fs = require('fs');
var nome_install = fields.nome;
var versaoatual = fields.versaoatual;
var idsistemas = fields.idsistemas;
var pathArquivo = "";
var pathIcone = "";
try {
pathArquivo = files.arquivo.path;
}
catch(e) {
console.log("não copia arquivo");
}
try {
pathIcone = files.icone.path;
}
catch(e) {
console.log("não copia icone");
}
var dir = '/var/www/html/sistemas/';
if (!fs.existsSync(dir + idsistemas + "/")) {
fs.mkdirSync(dir + idsistemas + "/");
fs.mkdirSync(dir + idsistemas + "/detalhes/");
fs.mkdirSync(dir + idsistemas + "/versoes/");
fs.mkdirSync(dir + idsistemas + "/backups/");
fs.mkdirSync(dir + idsistemas + "/versoes/" + versaoatual + "/");
}
else if (!fs.existsSync(dir + idsistemas + "/versoes/" + versaoatual + "/")) {
fs.mkdirSync(dir + idsistemas + "/versoes/" + versaoatual + "/");
}
if (pathArquivo != "") {
fs.rename(pathArquivo, dir + idsistemas + "/versoes/" + versaoatual + "/" + nome_install, function (err) {
console.log("arquivo copiado com sucesso");
});
}
if (pathIcone != "") {
fs.rename(pathIcone, dir + idsistemas + "/icone.png", function (err) {
console.log("icone copiado com sucesso!");
});
}
setTimeout(() => {
res.send(true);
}, 200);
});
});
router.get('/getChavesSistema/:idsistemas/:idusuarios', function (req, res) {
const idsistemas = parseInt(req.params.idsistemas);
const idusuarios = parseInt(req.params.idusuarios);
const sqlQuery = `SELECT * FROM chaves
WHERE idsistemas = ${idsistemas} AND idusuarios = ${idusuarios};`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getChavesSistemaLista/:idsistemas/:idusuarios', function (req, res) {
const idsistemas = parseInt(req.params.idsistemas);
const idusuarios = parseInt(req.params.idusuarios);
const sqlQuery = `SELECT
idchaves,
licenca as Chave,
data_criacao as DataCriacao,
data_validade as DataValidade,
CASE WHEN data_validade < NOW() THEN 'Expirada' ELSE 'Válida' END AS Status,
CASE WHEN usada = 0 THEN 'Não' ELSE 'Sim' END AS Ativada
FROM chaves
WHERE idsistemas = ${idsistemas} AND idusuarios = ${idusuarios};`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/insertChave', function (req, res) {
let licenca = req.body.licenca;
let idusuarios = req.body.idusuarios;
let idsistemas = req.body.idsistemas;
let data_validade = req.body.data_validade;
let newdate = data_validade.toString();
newdate = newdate.substring(0, newdate.length - 1);
const sqlQuery = `INSERT INTO chaves (licenca, data_criacao, data_validade, idusuarios, usada, idsistemas)
VALUES ('${licenca}', NOW(), '${newdate}', ${idusuarios}, 0, ${idsistemas});`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/insertDetalhe', function (req, res) {
const idsistemasdetalhes = req.body.idsistemasdetalhes;
const idsistemas = req.body.idsistemas;
const descricao = req.body.descricao;
const ordem = parseInt(req.body.ordem);
const habilitado = req.body.habilitado ? 1 : 0;
const sqlQuery = `INSERT INTO sistemasdetalhes (idsistemas, descricao, ordem, habilitado)
VALUES (${idsistemas}, '${descricao}', ${ordem}, ${habilitado});`;
ExecuteQuery(sqlQuery, req, res);
});
router.patch('/updateDetalhe', function (req, res) {
const idsistemasdetalhes = req.body.idsistemasdetalhes;
const idsistemas = req.body.idsistemas;
const descricao = req.body.descricao;
const ordem = parseInt(req.body.ordem);
const habilitado = req.body.habilitado ? 1 : 0;
const sqlQuery = `UPDATE sistemasdetalhes SET
idsistemas = ${idsistemas},
descricao = '${descricao}',
ordem = ${ordem},
habilitado = ${habilitado}
WHERE idsistemasdetalhes = ${idsistemasdetalhes};`;
ExecuteQuery(sqlQuery, req, res);
});
router.delete('/deleteDetalhe/:idsistemasdetalhes/:idsistemas/:index', function (req, res) {
const idsistemasdetalhes = parseInt(req.params.idsistemasdetalhes);
const idsistemas = parseInt(req.params.idsistemas);
const index = parseInt(req.params.index);
const sqlQuery = `DELETE FROM sistemasdetalhes WHERE idsistemasdetalhes = ${idsistemasdetalhes};`;
ExecuteQuery(sqlQuery, req, res);
var Path = '/var/www/html/sistemas/' + idsistemas + "/detalhes/sistema_" + index + ".jpg";
const fs = require('fs');
if (fs.existsSync(Path)) {
fs.unlinkSync(Path);
}
});
router.post('/copyDetalhe', (req, res) => {
let form = new formidable.IncomingForm();
form.maxFileSize = 600 * 1024 * 1024;
form.parse(req, function(err,fields,files) {
const fs = require('fs');
var nome = fields.nome;
var idsistemas = fields.idsistemas;
var pathArquivo = files.arquivo.path;
var dir = '/var/www/html/sistemas/' + idsistemas + "/detalhes/";
fs.rename(pathArquivo, dir + nome, function (err) {
if (err)
console.log(err);
else
console.log("arquivo copiado com sucesso");
res.send(true);
});
});
});
router.get('/atualizarListaDetalhes/:idsistemas', (req, res) => {
const idsistemas = parseInt(req.params.idsistemas);
const fs = require('fs');
var dir = '/var/www/html/sistemas/' + idsistemas + "/detalhes/";
let i = 0;
fs.readdirSync(dir).forEach(file => {
fs.renameSync(dir + file, dir + file.split('_')[0] + "_" + i + '.jpg');
i++;
});
res.send(true);
});
router.get('/getSistemaVersoes/:idsistemas', function (req, res) {
const idsistemas = parseInt(req.params.idsistemas);
const fs = require('fs');
const dir = '/var/www/html/sistemas/' + idsistemas + "/versoes/";
if (!fs.existsSync(dir))
fs.mkdirSync(dir);
const versoes = fs.readdirSync(dir);
var lista = [];
versoes.forEach(versao => {
const file = fs.readdirSync(dir + versao)[0];
const arquivo = dir + versao + "/" + file;
const stats = fs.statSync(arquivo);
lista.push(arquivo + "¬" + stats.birthtime);
});
res.send(lista);
});
router.get('/getSistemaDownloads/:idsistemas', function (req, res) {
const idsistemas = parseInt(req.params.idsistemas);
const fs = require('fs');
const dir = '/var/www/html/sistemas/' + idsistemas + "/downloads/";
if (!fs.existsSync(dir))
fs.mkdirSync(dir);
const downloads = fs.readdirSync(dir);
var lista = [];
downloads.forEach(download => {
var stats = fs.statSync(dir + download);
lista.push(dir + download + "¬" + stats.birthtime);
});
res.send(lista);
});
return router;
}
module.exports = routes;

View File

@ -0,0 +1,75 @@
const express = require('express');
const router = express.Router();
const formidable = require('formidable');
var routes = function () {
router.get('/getAllSlides', function (req, res) {
const sqlQuery = `SELECT * FROM slides;`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getSlidesEn', function (req, res) {
const sqlQuery = `select
idslides,
descricao,
CASE WHEN habilitado = 1 THEN 'Sim' ELSE 'Não' END AS habilitado,
link
FROM slides
WHERE habilitado = 1;`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getSlideByID/:idslides', function (req, res) {
const idslides = parseInt(req.params.idslides);
const sqlQuery = `SELECT * FROM slides WHERE idslides = ${idslides};`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/insertSlides', function (req, res) {
const idslides = req.body.idslides;
const descricao = req.body.descricao;
const habilitado = req.body.habilitado ? 1 : 0;
const link = req.body.link;
const sqlQuery = `INSERT INTO slides (descricao, habilitado, link) VALUES ('${descricao}', ${habilitado}, '${link}');`;
ExecuteQuery(sqlQuery, req, res);
});
router.patch('/updateSlides', function (req, res) {
const idslides = parseInt(req.body.idslides);
const descricao = req.body.descricao;
const habilitado = req.body.habilitado ? 1 : 0;
const link = req.body.link;
const sqlQuery = `UPDATE slides SET
descricao = '${descricao}',
habilitado = ${habilitado},
link = '${link}'
WHERE idslides = ${idslides};`;
ExecuteQuery(sqlQuery, req, res);
});
router.delete('/deleteSlides/:idslides', function (req, res) {
const idslides = parseInt(req.params.idslides);
const sqlQuery = `DELETE FROM slides WHERE idslides = ${idslides};`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/copy', (req, res) => {
let form = new formidable.IncomingForm();
form.maxFileSize = 600 * 1024 * 1024;
form.parse(req, function(err,fields,files){
const fs = require('fs');
var idslides = fields.idslides;
var pathImagem = files.imagem.path;
var dir = '/var/www/html/imagens/slides/';
fs.rename(pathImagem, dir + "slide_" + idslides + ".jpg", function (err) {
console.log("arquivo copiado com sucesso");
res.send(true);
});
});
});
return router;
}
module.exports = routes;

View File

@ -0,0 +1,267 @@
const express = require('express');
const router = express.Router();
var routes = function () {
router.get('/ativarUsuario/:idusuarios', function (req, res) {
const idusuarios = parseInt(req.params.idusuarios);
if (!idusuarios || isNaN(idusuarios)) {
return res.status(400).send(RenderPaginaAtivacao({
sucesso: false,
titulo: 'Link inválido',
mensagem: 'O link de ativação utilizado é inválido ou está incompleto.',
textoBotao: 'Voltar ao site'
}));
}
const sqlQuery = `
UPDATE usuarios
SET habilitado = 1
WHERE idusuarios = ?;
`;
mysqlConnection.query(sqlQuery, [idusuarios], function (error, results) {
console.log(`${new Date().toUTCString()} - ${req.originalUrl} ${(error) ? '500 error' : '200 success'}`);
if (error) {
return res.status(500).send(RenderPaginaAtivacao({
sucesso: false,
titulo: 'Erro ao ativar conta',
mensagem: 'Não foi possível ativar sua conta no momento. Tente novamente mais tarde ou entre em contato com o suporte.',
textoBotao: 'Voltar ao site'
}));
}
if (!results || results.affectedRows === 0) {
return res.status(404).send(RenderPaginaAtivacao({
sucesso: false,
titulo: 'Conta não encontrada',
mensagem: 'Não encontramos uma conta associada a este link de ativação.',
textoBotao: 'Voltar ao site'
}));
}
return res.status(200).send(RenderPaginaAtivacao({
sucesso: true,
titulo: 'Conta ativada com sucesso!',
mensagem: 'Sua conta foi ativada e você já pode acessar a plataforma Zendion, INC.',
textoBotao: 'Entrar agora'
}));
});
});
function RenderPaginaAtivacao({ sucesso, titulo, mensagem, textoBotao }) {
const corPrincipal = sucesso ? '#071b63' : '#991b1b';
const corFundoIcone = sucesso ? '#dcfce7' : '#fee2e2';
const corIcone = sucesso ? '#15803d' : '#b91c1c';
const icone = sucesso ? '✓' : '!';
const linkBotao = sucesso ? 'https://zendioninc.com.br/login' : 'https://zendioninc.com.br/home';
return `
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${titulo}</title>
</head>
<body style="margin:0; padding:0; background:#f4f6fb; font-family:Arial, Helvetica, sans-serif; color:#1f2937;">
<div style="min-height:100vh; display:flex; align-items:center; justify-content:center; padding:32px 16px; box-sizing:border-box; background:linear-gradient(135deg, #f4f6fb 0%, #eef3ff 45%, #ffffff 100%);">
<div style="width:100%; max-width:560px; background:#ffffff; border-radius:22px; overflow:hidden; box-shadow:0 24px 70px rgba(15, 23, 42, 0.16); border:1px solid rgba(148, 163, 184, 0.22);">
<div style="background:#071b63; padding:28px 32px; text-align:center;">
<img src="https://zendioninc.com.br/assets/logo_escrito.png" alt="Zendion, INC." style="height:54px; max-width:240px; object-fit:contain; filter:brightness(0) invert(1);">
</div>
<div style="padding:42px 34px; text-align:center;">
<div style="width:72px; height:72px; margin:0 auto 22px auto; border-radius:999px; display:flex; align-items:center; justify-content:center; background:${corFundoIcone}; color:${corIcone}; font-size:38px; font-weight:900;">
${icone}
</div>
<h1 style="margin:0 0 14px 0; color:#0f172a; font-size:28px; line-height:36px; font-weight:800;">
${titulo}
</h1>
<p style="margin:0 0 28px 0; color:#64748b; font-size:16px; line-height:26px;">
${mensagem}
</p>
<a href="${linkBotao}" style="display:inline-block; background:${corPrincipal}; color:#ffffff; text-decoration:none; font-size:15px; font-weight:800; padding:14px 28px; border-radius:12px; box-shadow:0 12px 24px rgba(7, 27, 99, 0.22);">
${textoBotao}
</a>
</div>
<div style="background:#f8fafc; padding:18px 28px; text-align:center; border-top:1px solid #e5e7eb;">
<p style="margin:0; color:#94a3b8; font-size:12px; line-height:20px;">
Zendion, INC. Este é um processo automático de ativação de conta.
</p>
</div>
</div>
</div>
</body>
</html>
`;
}
router.get('/getUsuarios', function (req, res) {
const sqlQuery = `SELECT * FROM usuarios;`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getUsuarioByID/:idusuarios', function (req, res) {
const idusuarios = parseInt(req.params.idusuarios);
const sqlQuery = `SELECT * FROM usuarios WHERE idusuarios = ${idusuarios};`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getSistemasDoUsuario/:idusuarios', function (req, res) {
const idusuarios = parseInt(req.params.idusuarios);
const sqlQuery = `SELECT
s.idsistemas,
s.nome,
su.data_download,
su.idusuarios,
s.descricao,
(SELECT COUNT(1) FROM chaves cv WHERE cv.idsistemas = s.idsistemas AND cv.idusuarios = su.idusuarios AND cv.data_validade > CURRENT_DATE) AS chavesValidas,
CASE WHEN s.idtiposlicenca = 6 THEN 1 ELSE 0 END AS gratis
FROM sistemasusuario su
INNER JOIN sistemas s ON s.idsistemas = su.idsistemas
WHERE su.idusuarios = ${idusuarios};`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/authUser', function (req, res) {
let usuario = req.body.usuario;
let senha = req.body.senha;
const sqlQuery = `SELECT
idusuarios,
nome,
idtiposdeusuario,
habilitado
FROM usuarios
WHERE usuario = '${usuario}' AND senha = '${senha}';`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getTiposdeusuario', function (req, res) {
const sqlQuery = `SELECT * FROM tiposdeusuario;`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/insertUsuarios', function (req, res) {
let idusuarios = req.body.idusuarios;
let nome = req.body.nome;
let usuario = req.body.usuario;
let senha = req.body.senha;
let email = req.body.email;
let idtiposdeusuario = req.body.idtiposdeusuario;
let cpf = req.body.cpf;
let habilitado = req.body.habilitado ? 1 : 0;
const sqlQuery = `INSERT INTO usuarios (nome, usuario, senha, data_cadastro, cpf, habilitado, email, idtiposdeusuario)
VALUES ('${nome}', UPPER('${usuario}'), UPPER('${senha}'), now(), '${cpf}', ${habilitado}, '${email}', ${idtiposdeusuario});`;
ExecuteQuery(sqlQuery, req, res);
});
router.patch('/updateUsuarios', function (req, res) {
let idusuarios = req.body.idusuarios;
let nome = req.body.nome;
let usuario = req.body.usuario;
let senha = req.body.senha;
let email = req.body.email;
let idtiposdeusuario = req.body.idtiposdeusuario;
let cpf = req.body.cpf;
let habilitado = req.body.habilitado ? 1 : 0;
const sqlQuery = `UPDATE usuarios SET
nome = '${nome}',
usuario = UPPER('${usuario}'),
senha = UPPER('${senha}'),
cpf = '${cpf}',
habilitado = ${habilitado},
email = '${email}',
idtiposdeusuario = ${idtiposdeusuario}
WHERE idusuarios = ${idusuarios};`;
ExecuteQuery(sqlQuery, req, res);
});
router.delete('/deleteUsuarios/:idusuarios', function (req, res) {
let idusuarios = parseInt(req.params.idusuarios);
const sqlQuery = `DELETE FROM usuarios WHERE idusuarios = ${idusuarios};`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/setUsada', function (req, res) {
let chave = req.body.Chave;
const sqlQuery = `UPDATE chaves SET usada = 1 WHERE licenca = '${chave}';`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/validaLicenca', function (req, res) {
let usuario = req.body.Usuario;
let senha = req.body.Senha;
let chave = req.body.Chave;
const sqlQuery = `SELECT
u.nome AS Nome,
u.cpf AS CPF,
c.usada AS Usada,
c.idsistemas AS IDSistema
FROM chaves c
INNER JOIN usuarios u ON c.idusuarios = u.idusuarios
WHERE c.licenca = '${chave}' AND u.usuario = '${usuario}' AND u.senha = '${senha}' AND u.habilitado = 1 AND c.usada = 0;`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/insertTrocasenha', function (req, res) {
let idusuarios = parseInt(req.body.idusuarios);
let tag = req.body.tag;
const sqlQuery = `INSERT INTO trocasenha (idusuarios, tag, usada) VALUES (${idusuarios}, '${tag}', 0);`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getTrocasenha', function (req, res, next) {
const sqlQuery = `SELECT * FROM trocasenha WHERE usada = 0;`;
ExecuteQuery(sqlQuery, req, res);
});
router.get('/getTrocasenhaByTag/:tag', function (req, res, next) {
let tag = req.params.tag;
const sqlQuery = `SELECT * FROM trocasenha WHERE tag = '${tag}' AND usada = 0;`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/setExpirado', function (req, res, next) {
let tag = req.body.tag;
const sqlQuery = `UPDATE trocasenha SET usada = 1 WHERE tag = '${tag}';`;
ExecuteQuery(sqlQuery, req, res);
});
router.patch('/trocaSenha', function (req, res, next) {
const idusuarios = parseInt(req.body.idusuarios);
const senha = req.body.senha;
const sqlQuery = `UPDATE usuarios SET
senha = UPPER('${senha}')
WHERE idusuarios = ${idusuarios};`;
ExecuteQuery(sqlQuery, req, res);
});
router.post('/validaLicencaByChave/:idsistemas', function (req, res, next) {
const idsistemas = parseInt(req.params.idsistemas);
const chave = req.body.Chave;
const sqlQuery = `SELECT
u.nome AS Nome,
u.cpf AS CPF,
c.usada AS Usada,
c.idsistemas AS IDSistema
FROM chaves c
INNER JOIN usuarios u ON c.idusuarios = u.idusuarios
WHERE c.licenca = '${chave}' AND c.idsistemas = ${idsistemas} AND u.habilitado = 1 AND c.usada = 0;`;
ExecuteQuery(sqlQuery, req, res);
});
return router;
}
module.exports = routes;

10
server/forever.json Normal file
View File

@ -0,0 +1,10 @@
[
{
"uid": "zendioninc_api",
"append": true,
"watch": false,
"script": "server.js",
"sourceDir": "/var/www/html/server/",
"workingDir": "/var/www/html/server/"
}
]

View File

@ -0,0 +1,12 @@
{
"type": "service_account",
"project_id": "gca-control-bcb9b",
"private_key_id": "2e672cfd0a80e4d837abe0b0588540954677e4f8",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDKqU5nsJGM5P8C\n4DUsrNYIbIVQxZRxL1mdEvAtYuLg8S6sEjIaxUQElDazQ9W8X4fdU/oVL9oziCCG\nwlJoEKsaPMiLIi4aBzgyCxSjsMk1uJonl++TkEPxUQa4gy8fq/UhHXkBROS9nBy2\n7VWqvcN176oPmYwhg5qLxfhYsZWwwZLBtntpQNaGdA0Ul7h+nwp5qsrkst3Qx2Su\nqfvUOYr8pvvzb+P+eU1Qw5aPBjGQQXcZ8rE4d3qyTSJ+xaZwblPViQhHAch/bt8n\n2uKN/rFjl3QpumzDuiF7z7+dFqf4vnCfpHbar1jLK3HIS6RE+y5iTqBeREzRSLts\n2VAN1PP1AgMBAAECggEACW4nQMIcWmpLJy2Fg+qXULqIrz7BFCLlgF6MewwuLanu\nEkyMH3yc7Y/zsKlK1ZJmGSmKdPhuUUtiZMA/mJSSpaynkfkunIBIADoi79AEOOH6\nGGFkoPZLrGZpEeZsJ6+Mf0nmuCVQyzWWYiO5QrHvBGVOgZ3TQbmk6XVLmQhLGcz+\n6LdO225096syWBvAelcv8iSs2D7mEv920TT5Hj1ZwOuEnfOca38fiBpi359VrbMd\nqEDDO3B3qoy3mrWxWLW1NqTdavLLvJhee651xrFq4f5Zy99uuHVs9DD+L5Yo56BD\nRZBKJOmMHMtvpe3ePyYfr8d1E0ijMuWwIStnRHgFSQKBgQDuKgGwGq54wvg0ybgK\ngqtP/2gGHjXnS1ii5vUxy2DverCv1FcI5ll7/1LcgKQSwFBZGYcg+p7ZMTh31CCe\nQPvIEeFuo0pJIdqcjANKi1V9tQLq2BzJMJ/KXQpkr97Bz9PNaBy877Or/Nsf95Ao\n6rdaCteox5FU1vmFay2rfW/8XQKBgQDZ1qeIFMUFqKnMFKokk/bWnuSeCnwf6IGu\naMKQY0lVV1Y1nM7Ti0fLZRrEpYnSjvwpPYvno+onGH5yUSlP3owJdvaqPdLSAy54\nHrm/YdUUBx+f2LUdcC7hIaS+2b3rXlO5tT98wqbqYvFf5QSVfknjKpAEJ0ADYm7M\n4+bizLaceQKBgGdjcOwkCQ1/hgRUdUKKyxelRmExiDDqMoY1QMfnBCOx3TXXNhk0\n9i+v/Saf6ZoCRl8Hh/O3GHIsvdi3C5nnl+WYbYZ7+Gma+q3u0hbwOaKii6urOYPV\nSeitTy+6cTcto0KOKbuTSyokUHOmIrzAd+vsvQYV7G8qC5PxnRfPEmg5AoGAJShr\nXVgFn4h8pjYkK6cdpNBnuNmbAKpdbI2sGosFGEyUhQKheQ46CeTOIxsFnmgLdVFt\nSu734wFXmFJl1JzmkDq9qp5vFqm6RH0hPBcJDKiGaXLCuinHpVliy7dCoSJu76+O\nC878P+DuwWfEV2e6XMiG1l6HmcwxkDuTx74ZrCkCgYEAsa0RwkyCvQBShW7BtZot\nd6iziBqI+gqSq4KYraWvvMVrjA00IKk0B/uzofnK/aX98HJM4NzO9EAbW0ARpn5D\nDv7FwBRT+fWG8CW+XvT5LiKO7KYILimFm4cvzuv5gztLGexlfFBiUn1+t6fzI5Yf\neCafFoJuPEDGHoIOAqd5PME=\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-pf0od@gca-control-bcb9b.iam.gserviceaccount.com",
"client_id": "105026073302094660795",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-pf0od%40gca-control-bcb9b.iam.gserviceaccount.com"
}

View File

@ -0,0 +1,39 @@
{
"project_info": {
"project_number": "274810216572",
"project_id": "gca-control-bcb9b",
"storage_bucket": "gca-control-bcb9b.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:274810216572:android:0d9250a6adb98f84f4a9f9",
"android_client_info": {
"package_name": "com.ZendionInc.GCAControl"
}
},
"oauth_client": [
{
"client_id": "274810216572-6c1qof9nb2gns24604s1narajuhfluh4.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyDTKEi1eHsF50y1KIMHY9x04OXXOVqs7ig"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "274810216572-6c1qof9nb2gns24604s1narajuhfluh4.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}

2558
server/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

30
server/package.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "server",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@sendgrid/mail": "^8.1.3",
"body-parser": "^1.18.3",
"cors": "^2.8.4",
"dotenv": "^17.4.2",
"express": "^4.16.3",
"express-formidable": "^1.0.0",
"express-jwt": "^5.3.1",
"firebase-admin": "^10.0.1",
"formidable": "^1.2.1",
"jsonwebtoken": "^8.3.0",
"lodash": "^4.17.10",
"mercadopago": "^2.0.15",
"mysql": "^2.15.0",
"nodemailer": "^6.10.1",
"nodemon": "^3.1.14",
"shelljs": "^0.8.4",
"unzipper": "^0.12.3"
}
}

37
server/server.js Normal file
View File

@ -0,0 +1,37 @@
var express = require('express');
var http = require('http');
var app = express();
var cors = require('cors');
var bodyParser = require('body-parser');
var corsOptions = {
origin: '*',
optionsSuccessStatus: 200
}
app.use(cors(corsOptions));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
http.createServer(app).listen(1337, () => {
console.log(`API is listening on 1337`);
});
app.use('/users', require('./controllers/usuariosController')());
app.use('/sistemas', require('./controllers/sistemasController')());
app.use('/slides', require('./controllers/sliderController')());
app.use('/noticias', require('./controllers/noticiasController')());
app.use('/arquivos', require('./controllers/arquivosController')());
app.use('/bingo', require('./controllers/bingoController')());
app.use('/gca', require('./controllers/gcaController')());
app.use('/priorie', require('./controllers/priorieController')());
app.use('/email', require('./controllers/emailController').router);
app.use('/checkout', require('./controllers/checkoutController')());
app.use('/otp', require('./controllers/oriontardproController')());
// Allteeth
app.use('/api_5/sincronia', require('./controllers/allteeth/sincroniaController')());
app.use('/api_5/users', require('./controllers/allteeth/usuariosController')());
app.use('/agrobase', require('./controllers/agrobaseController')());

View File

@ -0,0 +1 @@
agrobase login

View File

@ -0,0 +1,19 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-agrobase-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class AgrobaseLoginComponent implements OnInit {
constructor(
private route: ActivatedRoute
) { }
ngOnInit() {
}
}

View File

@ -0,0 +1,115 @@
<!-- Título da Página -->
<h1>Gerenciamento de Arquivos de Versão</h1>
<!-- Botão para adicionar novo arquivo -->
<div class="actions">
<button mat-raised-button color="primary" (click)="abrirDialogoArquivo()">Adicionar Arquivo</button>
</div>
<!-- Tabela de Arquivos -->
<table mat-table [dataSource]="arquivos" class="mat-elevation-z8 table-container">
<!-- Coluna do Nome do Arquivo para Download -->
<ng-container matColumnDef="ArquivoDownload">
<th mat-header-cell *matHeaderCellDef>Nome do Arquivo para Download</th>
<td mat-cell *matCellDef="let arquivo">{{ arquivo.ArquivoDownload }}</td>
</ng-container>
<!-- Coluna do Nome do Arquivo -->
<ng-container matColumnDef="Arquivo">
<th mat-header-cell *matHeaderCellDef>Nome do Arquivo</th>
<td mat-cell *matCellDef="let arquivo">{{ arquivo.Arquivo }}</td>
</ng-container>
<!-- Coluna do Diretório -->
<ng-container matColumnDef="Diretorio">
<th mat-header-cell *matHeaderCellDef>Diretório</th>
<td mat-cell *matCellDef="let arquivo">{{ arquivo.Diretorio }}</td>
</ng-container>
<!-- Coluna da Extensão -->
<ng-container matColumnDef="Extensao">
<th mat-header-cell *matHeaderCellDef>Extensão</th>
<td mat-cell *matCellDef="let arquivo">{{ arquivo.Extensao }}</td>
</ng-container>
<!-- Coluna da Versão -->
<ng-container matColumnDef="Versao">
<th mat-header-cell *matHeaderCellDef>Versão</th>
<td mat-cell *matCellDef="let arquivo">{{ arquivo.Versao }}</td>
</ng-container>
<!-- Coluna do Tipo de Arquivo -->
<ng-container matColumnDef="TipoArquivo">
<th mat-header-cell *matHeaderCellDef>Tipo de Arquivo</th>
<td mat-cell *matCellDef="let arquivo">{{ arquivo.TipoArquivo | tipoArquivoNome }}</td>
</ng-container>
<!-- Coluna de Ações -->
<ng-container matColumnDef="acoes">
<th mat-header-cell *matHeaderCellDef>Ações</th>
<td mat-cell *matCellDef="let arquivo">
<button mat-icon-button color="primary" (click)="abrirDialogoArquivo(arquivo)">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button color="warn" (click)="excluirArquivo(arquivo)">
<mat-icon>delete</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<!-- Diálogo para Editar/Adicionar Arquivo -->
<ng-template #dialogoArquivo let-data>
<h2 mat-dialog-title>{{ data?.novo ? 'Adicionar Arquivo' : 'Editar Arquivo' }}</h2>
<form [formGroup]="arquivoForm" (ngSubmit)="onSubmit()">
<!-- Campo para o nome do arquivo -->
<mat-form-field appearance="fill">
<mat-label>Nome do Arquivo Local</mat-label>
<input matInput placeholder="Nome do Arquivo Local" formControlName="Arquivo" required />
</mat-form-field>
<!-- Campo para o diretório do arquivo -->
<mat-form-field appearance="fill">
<mat-label>Diretório</mat-label>
<input matInput placeholder="Diretório" formControlName="Diretorio" required />
</mat-form-field>
<!-- Campo para a extensão do arquivo -->
<mat-form-field appearance="fill">
<mat-label>Extensão</mat-label>
<input matInput placeholder="Extensão" formControlName="Extensao" required />
</mat-form-field>
<!-- Campo para a versão do arquivo -->
<mat-form-field appearance="fill">
<mat-label>Versão</mat-label>
<input matInput placeholder="Versão" formControlName="Versao" required />
</mat-form-field>
<!-- Campo para o tipo do arquivo (dropdown com opções) -->
<mat-form-field appearance="fill">
<mat-label>Tipo do Arquivo</mat-label>
<mat-select formControlName="TipoArquivo" required>
<mat-option *ngFor="let tipo of tiposArquivos" [value]="tipo">{{ tipo }}</mat-option>
</mat-select>
</mat-form-field>
<!-- Campo para o nome do arquivo -->
<mat-form-field appearance="fill">
<mat-label>Nome do Arquivo para Download</mat-label>
<input matInput placeholder="Nome do Arquivo para Download" formControlName="ArquivoDownload" required />
</mat-form-field>
<!-- Campo para upload do arquivo -->
<input type="file" (change)="onFileSelected($event)" style="margin-top: 16px;" />
<!-- Botões de ação para cancelar ou salvar -->
<div mat-dialog-actions style="justify-content: flex-end;">
<button mat-button (click)="onCancel()">Cancelar</button>
<button mat-raised-button color="primary" type="submit">{{ data?.novo ? 'Adicionar' : 'Salvar' }}</button>
</div>
</form>
</ng-template>

View File

@ -0,0 +1,50 @@
.agrobase-container {
background-color: #1A237E; /* Fundo azul escuro */
color: #E3F2FD; /* Texto branco */
padding: 20px;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
}
h1 {
color: #FFFFFF; /* Título branco */
text-align: center;
margin-bottom: 20px;
}
.table-container {
width: 100%; /* Largura total */
background-color: #283593; /* Azul médio para destaque */
color: #E3F2FD; /* Texto branco */
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
margin-top: 20px;
}
.mat-header-cell, .mat-cell {
color: #E3F2FD; /* Texto branco */
}
.mat-form-field {
color: #E3F2FD; /* Texto branco */
background-color: #283593; /* Azul médio para contraste */
border-radius: 4px;
}
.mat-dialog-container {
background-color: #1E88E5; /* Fundo mais claro para o diálogo */
color: #E3F2FD;
padding: 20px;
border-radius: 8px;
}
.mat-button {
color: #E3F2FD;
background-color: #42A5F5; /* Botão azul claro */
}
.mat-button:hover {
background-color: #64B5F6; /* Azul mais claro para hover */
}

View File

@ -0,0 +1,154 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { MatDialog, MatTableDataSource } from '@angular/material';
import { ActivatedRoute } from '@angular/router';
import { AgrobaseVersionFileModel, TiposArquivos } from 'src/app/models/agrobaseModel';
import { AgrobaseService } from 'src/app/services/agrobase.service';
@Component({
selector: 'app-agrobase-versionamento',
templateUrl: './versionamento.component.html',
styleUrls: ['./versionamento.component.scss']
})
export class AgrobaseVersionamentoComponent implements OnInit {
displayedColumns: string[] = ['ArquivoDownload', 'Arquivo', 'Diretorio', 'Extensao', 'Versao', 'TipoArquivo', 'acoes'];
arquivos: MatTableDataSource<AgrobaseVersionFileModel> = new MatTableDataSource<AgrobaseVersionFileModel>();
arquivoForm: FormGroup;
tiposArquivos = Object.values(TiposArquivos).filter(value => typeof value === 'string');
arquivoSelecionado: File | null = null;
@ViewChild('dialogoArquivo', null) dialogoArquivo;
constructor(
private agrobaseService: AgrobaseService,
private dialog: MatDialog,
private fb: FormBuilder,
) {
this.arquivoForm = this.fb.group({
id: [''],
Arquivo: [''],
Diretorio: [''],
Extensao: [''],
Versao: [''],
TipoArquivo: [''],
ArquivoDownload: [''],
});
}
ngOnInit() {
this.carregarArquivos();
}
carregarArquivos() {
this.agrobaseService.getArquivosVersao().subscribe(
(data) => {
this.arquivos.data = data;
},
(error) => {
console.error('Erro ao carregar dados de versão:', error);
}
);
}
abrirDialogoArquivo(arquivo?: AgrobaseVersionFileModel) {
const dialogRef = this.dialog.open(this.dialogoArquivo, {
data: arquivo ? { ...arquivo } : { novo: true }
});
if (arquivo) {
this.arquivoForm.patchValue(arquivo);
} else {
this.arquivoForm.reset();
}
dialogRef.afterClosed().subscribe(result => {
if (result) {
this.salvarArquivo(result);
}
});
}
onFileSelected(event: any) {
this.arquivoSelecionado = event.target.files[0] || null;
}
// Este método irá enviar o arquivo .vsf atualizado para o servidor
atualizarArquivoVsf() {
const jsonData = JSON.stringify(this.arquivos.data);
this.agrobaseService.atualizarVsfFile(jsonData).subscribe(
() => console.log("Arquivo .vsf atualizado com sucesso no servidor."),
(error) => console.error("Erro ao atualizar o arquivo .vsf:", error)
);
}
// Modifique o método salvarArquivo para incluir a atualização do .vsf após o upload do arquivo
salvarArquivo(dadosArquivo: AgrobaseVersionFileModel) {
if (this.arquivoSelecionado) {
this.agrobaseService.uploadFile(this.arquivoSelecionado, dadosArquivo.ArquivoDownload).subscribe(response => {
this.salvarNaTabela(dadosArquivo);
this.atualizarArquivoVsf(); // Chama a atualização do .vsf
});
} else {
this.salvarNaTabela(dadosArquivo);
this.atualizarArquivoVsf(); // Atualiza o .vsf mesmo sem upload de arquivo
}
}
salvarNaTabela(dadosArquivo: AgrobaseVersionFileModel) {
console.log(dadosArquivo.TipoArquivo);
dadosArquivo.TipoArquivo = TiposArquivos[dadosArquivo.TipoArquivo as unknown as keyof typeof TiposArquivos];
console.log(dadosArquivo.TipoArquivo);
let index = this.arquivos.data.findIndex(a =>
a.id === dadosArquivo.id ||
(a.Arquivo === dadosArquivo.Arquivo && a.Diretorio === dadosArquivo.Diretorio)
);
if (index >= 0) {
// Substitui o item existente com os dados atualizados
this.arquivos.data[index] = dadosArquivo;
} else {
// Adiciona um novo item caso não exista na lista
dadosArquivo.id = this.gerarIdUnico(); // Gera um ID único
this.arquivos.data = [...this.arquivos.data, dadosArquivo];
}
this.arquivos._updateChangeSubscription(); // Atualiza a tabela
}
// Função auxiliar para gerar IDs únicos
gerarIdUnico(): number {
return this.arquivos.data.length > 0 ? Math.max(...this.arquivos.data.map(a => a.id || 0)) + 1 : 1;
}
excluirArquivo(arquivo: AgrobaseVersionFileModel) {
if (confirm("Tem certeza que deseja excluir o arquivo " + arquivo.Arquivo + "?")) {
this.agrobaseService.deleteVersionFile(arquivo.ArquivoDownload).subscribe(
(data) => {
this.arquivos.data = this.arquivos.data.filter(a => a !== arquivo);
this.arquivos._updateChangeSubscription(); // Atualiza a tabela
this.atualizarArquivoVsf();
},
(error) => {
console.error('Erro ao carregar dados de versão:', error);
}
);
}
}
onSubmit() {
if (this.arquivoForm.valid) {
const dadosArquivo: AgrobaseVersionFileModel = this.arquivoForm.value;
this.salvarArquivo(dadosArquivo);
this.dialog.closeAll();
}
}
onCancel() {
this.dialog.closeAll();
}
}

View File

@ -0,0 +1,174 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { LoginComponent } from './login/login.component';
import { UsuariosService } from './services/usuarios.service';
import { DashboardComponent } from './dashboard/dashboard.component';
import { UsersComponent } from './users/users.component';
import { ClientComponent } from './client/client.component';
import { FuncoesGlobais } from 'src/environments/environment';
import { SistemasComponent } from './sistemas/sistemas.component';
import { SistemasService } from './services/sistemas.service';
import { CriarcontaComponent } from './criarconta/criarconta.component';
import { DetalhesSistemaComponent } from './detalhes-sistema/detalhes-sistema.component';
import { BaseSiteComponent } from './base-site/base-site.component';
import { ChavesComponent } from './chaves/chaves.component';
import { EditHomeComponent } from './edit-home/edit-home.component';
import { SliderComponent } from './slider/slider.component';
import { NoticiasComponent } from './noticias/noticias.component';
import { SliderService } from './services/slider.service';
import { NoticiasService } from './services/noticias.service';
import { GeralService } from './services/geral.service';
import { CadDetSistemaComponent } from './cad-det-sistema/cad-det-sistema.component';
import { EasyAVAComponent } from './easy-ava/easy-ava.component';
import { EasyAVAService } from './services/easy-ava.service';
import { BingoSortComponent } from './bingo-sort/bingo-sort.component';
import { BingoComponent } from './bingo/bingo.component';
import { BingoDashboardComponent } from './bingo-dashboard/bingo-dashboard.component';
import { BingoService } from './services/bingoService';
import { OrionDashboardComponent } from './orion-dashboard/orion-dashboard.component';
import { OrionService } from './services/orionService';
import { ProdutosComponent } from './produtos/produtos.component';
import { ProdutosService } from './services/produtosService';
import { AgrobaseLoginComponent } from './agrobase/login/login.component';
import { AgrobaseVersionamentoComponent } from './agrobase/versionamento/versionamento.component';
import { AgrobaseService } from './services/agrobase.service';
const routes: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{
path: '',
component: BaseSiteComponent,
children: [
{
path: 'home',
component: HomeComponent,
}, {
path: 'detalhes/:idsistemas',
component: DetalhesSistemaComponent,
},
]
}, {
path: 'login',
component: LoginComponent,
}, {
path: 'login/esqueci',
component: LoginComponent,
}, {
path: 'login/trocar/:tag',
component: LoginComponent,
}, {
path: 'criarconta',
component: CriarcontaComponent,
}, {
path: 'dashboard',
component: DashboardComponent,
children: [
{
path: 'usuarios/cadastro',
component: UsersComponent,
}, {
path: 'usuarios/editar/:idusuarios',
component: UsersComponent,
}, {
path: 'usuarios/visualizar',
component: UsersComponent,
}, {
path: 'sistemas/cadastro',
component: SistemasComponent,
}, {
path: 'sistemas/editar/:idsistemas',
component: SistemasComponent,
}, {
path: 'sistemas/visualizar',
component: SistemasComponent,
}, {
path: 'sistemas/detalhes/:idsistemas',
component: CadDetSistemaComponent,
}, {
path: 'sistemas/produtos/:idsistemas',
component: ProdutosComponent,
}, {
path: 'cliente',
component: ClientComponent,
}, {
path: 'cliente/chave/:idsistemas',
component: ChavesComponent,
}, {
path: 'admin/home',
component: EditHomeComponent,
}, {
path: 'admin/slides',
component: SliderComponent,
}, {
path: 'admin/slides/visualizar',
component: SliderComponent,
}, {
path: 'admin/slides/editar/:idslides',
component: SliderComponent,
}, {
path: 'admin/noticias',
component: NoticiasComponent,
}, {
path: 'admin/noticias/visualizar',
component: NoticiasComponent,
}, {
path: 'admin/noticias/editar/:idnoticias',
component: NoticiasComponent,
}
]
}, {
path: 'easyava',
component: EasyAVAComponent,
}, {
path: 'bingo',
//component: BingoComponent,
children: [
{
path: 'dashboard',
component: BingoDashboardComponent,
}, {
path: 'sorteio',
component: BingoSortComponent,
},
]
}, {
path: 'orion-dashboard',
component: OrionDashboardComponent
}, {
path: 'agrobase',
children: [
{
path: 'login',
component: AgrobaseLoginComponent,
}, {
path: 'versionamento',
component: AgrobaseVersionamentoComponent,
},
]
}
];
@NgModule({
imports: [
RouterModule.forRoot(routes),
],
exports: [
RouterModule,
],
providers: [
FuncoesGlobais,
UsuariosService,
SistemasService,
SliderService,
NoticiasService,
GeralService,
EasyAVAService,
BingoService,
OrionService,
ProdutosService,
AgrobaseService
]
})
export class AppRoutingModule { }

View File

@ -0,0 +1 @@
<router-outlet></router-outlet>

186
src/app/app.component.scss Normal file
View File

@ -0,0 +1,186 @@
.form-cabecalho {
border: 1px solid gray;
margin-right: 1em;
border-radius: 0.3em 0.3em 0 0;
background: gray;
padding-top: 0.4em;
padding-left: 2em;
height: 3em;
border-bottom: #d8d8d8;
}
.form-corpo {
border: 1px solid gray;
margin-right: 1em;
background: lightgrey;
padding: 1em;
color: #000;
}
.checkHabilitado {
width: 8em;
position: absolute;
right: 0;
margin-top: -2em;
}
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}
.quadro-Branco {
color: #000;
margin: 1em;
padding: 1em;
background: #fff;
border-radius: 1em;
}
.MyHR {
background: #eee;
height: 1px;
margin: auto;
margin-top: 1em;
margin-bottom: 1em;
}
.FundoCheio {
position: absolute;
height: 100vh;
width: 100vw;
top: 0;
left: 0;
background: #fff;
color: #000;
padding: 1em;
}
@mixin sp-layout {
@media screen and (max-width: 750px) {
@content;
}
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: radial-gradient(ellipse at bottom, #0d1d31 0%, #0c0d13 100%);
overflow: hidden;
}
@function random_range($min, $max) {
$rand: random();
$random_range: $min + floor($rand * (($max - $min) + 1));
@return $random_range;
}
.stars {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 120%;
transform: rotate(-45deg);
z-index: -10;
}
.star {
$star-count: 20;
--star-color: var(--primary-color);
--star-tail-length: 6em;
--star-tail-height: 2px;
--star-width: calc(var(--star-tail-length) / 6);
--fall-duration: 9s;
--tail-fade-duration: var(--fall-duration);
position: absolute;
top: var(--top-offset);
left: 0;
width: var(--star-tail-length);
height: var(--star-tail-height);
color: var(--star-color);
background: linear-gradient(45deg, currentColor, transparent);
border-radius: 50%;
filter: drop-shadow(0 0 6px currentColor);
transform: translate3d(104em, 0, 0);
animation: fall var(--fall-duration) var(--fall-delay) linear infinite, tail-fade var(--tail-fade-duration) var(--fall-delay) ease-out infinite;
@include sp-layout {
// For mobile performance, tail-fade animation will be removed QAQ
animation: fall var(--fall-duration) var(--fall-delay) linear infinite;
}
@for $i from 1 through $star-count {
&:nth-child(#{$i}) {
--star-tail-length: #{random_range(500em, 750em) / 100};
--top-offset: #{random_range(0vh, 10000vh) / 100};
--fall-duration: #{random_range(6000, 12000s) / 1000};
--fall-delay: #{random_range(0, 10000s) / 1000};
}
}
&::before, &::after {
position: absolute;
content: '';
top: 0;
left: calc(var(--star-width) / -2);
width: var(--star-width);
height: 100%;
background: linear-gradient(45deg, transparent, currentColor, transparent);
border-radius: inherit;
animation: blink 2s linear infinite;
}
&::before {
transform: rotate(45deg);
}
&::after {
transform: rotate(-45deg);
}
}
@keyframes fall {
to {
transform: translate3d(-30em, 0, 0);
}
}
@keyframes tail-fade {
0%, 50% {
width: var(--star-tail-length);
opacity: 1;
}
70%, 80% {
width: 0;
opacity: 0.4;
}
100% {
width: 0;
opacity: 0;
}
}
@keyframes blink {
50% {
opacity: 0.6;
}
}

22
src/app/app.component.ts Normal file
View File

@ -0,0 +1,22 @@
import { Title } from '@angular/platform-browser';
import { Component } from '@angular/core';
import { GeralService } from './services/geral.service';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
constructor(
private geralSerivice: GeralService,
private titleService: Title
) {
this.titleService.setTitle(environment.Titulo);
}
title = '"Zendion, Inc."';
}

109
src/app/app.module.ts Normal file
View File

@ -0,0 +1,109 @@
import { BrowserModule, Title } from '@angular/platform-browser';
import { LOCALE_ID, NgModule } from '@angular/core';
import { registerLocaleData } from '@angular/common';
import localePt from '@angular/common/locales/pt';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { Angular2UsefulSwiperModule } from 'angular2-useful-swiper';
import { LoginComponent } from './login/login.component';
import { HttpClientModule } from '@angular/common/http';
import { DashboardComponent } from './dashboard/dashboard.component';
import { UsersComponent } from './users/users.component';
import { ClientComponent } from './client/client.component';
import { SistemasComponent } from './sistemas/sistemas.component';
import { CriarcontaComponent } from './criarconta/criarconta.component';
import { DetalhesSistemaComponent } from './detalhes-sistema/detalhes-sistema.component';
import { BaseSiteComponent } from './base-site/base-site.component';
import { ChavesComponent } from './chaves/chaves.component';
import { EditHomeComponent } from './edit-home/edit-home.component';
import { SliderComponent } from './slider/slider.component';
import { NoticiasComponent } from './noticias/noticias.component';
import { CadDetSistemaComponent } from './cad-det-sistema/cad-det-sistema.component';
import { EasyAVAComponent } from './easy-ava/easy-ava.component';
import { BingoSortComponent } from './bingo-sort/bingo-sort.component';
import { BingoComponent } from './bingo/bingo.component';
import { BingoDashboardComponent } from './bingo-dashboard/bingo-dashboard.component';
import { OrionDashboardComponent } from './orion-dashboard/orion-dashboard.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatNativeDateModule } from '@angular/material/core';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatButtonModule } from '@angular/material/button';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatTabsModule } from '@angular/material/tabs';
import { MatTableModule } from '@angular/material/table';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatIconModule } from '@angular/material/icon';
import { MatDialogModule } from '@angular/material/dialog';
import { Ng5SliderModule } from 'ng5-slider';
import { ProdutosComponent } from './produtos/produtos.component';
import { AgrobaseLoginComponent } from './agrobase/login/login.component';
import { AgrobaseVersionamentoComponent } from './agrobase/versionamento/versionamento.component';
import { TipoArquivoNomePipe } from './pipes/tipo-arquivo-nome.pipe';
// Registrar a localidade pt-BR
registerLocaleData(localePt);
@NgModule({
declarations: [
AppComponent,
HomeComponent,
LoginComponent,
DashboardComponent,
UsersComponent,
ClientComponent,
SistemasComponent,
CriarcontaComponent,
DetalhesSistemaComponent,
BaseSiteComponent,
ChavesComponent,
EditHomeComponent,
SliderComponent,
NoticiasComponent,
CadDetSistemaComponent,
EasyAVAComponent,
BingoSortComponent,
BingoComponent,
BingoDashboardComponent,
OrionDashboardComponent,
ProdutosComponent,
AgrobaseLoginComponent,
AgrobaseVersionamentoComponent,
TipoArquivoNomePipe
],
imports: [
BrowserModule,
AppRoutingModule,
Angular2UsefulSwiperModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
BrowserAnimationsModule,
MatDatepickerModule,
MatNativeDateModule,
MatFormFieldModule,
MatInputModule,
MatSelectModule,
MatButtonModule,
MatSidenavModule,
MatTabsModule,
MatTableModule,
MatPaginatorModule,
MatIconModule,
MatDialogModule,
Ng5SliderModule
],
providers: [
Title,
{ provide: LOCALE_ID, useValue: 'pt-BR' }
],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -0,0 +1,78 @@
<div class="site-layout">
<header class="site-header">
<div class="header-content">
<a class="brand-link" [routerLink]="'/home'">
<img src="assets/logo_escrito.png" alt="Zendion, INC." class="logo logo-escrito">
<img src="assets/logo.png" alt="Zendion, INC." class="logo logo-favicon">
</a>
<nav class="header-nav">
<a [routerLink]="'/home'">Início</a>
<a href="/home#sistemas">Sistemas</a>
<a href="/home#noticias">Notícias</a>
</nav>
<div class="header-actions">
<button type="button" class="btn-header btn-header-secondary" (click)="btnCriarcontaClick()">
{{ btnSuperior }}
</button>
<button type="button" class="btn-header btn-header-primary" (click)="btnLoginClick()">
{{ btnLoginText }}
</button>
</div>
</div>
</header>
<main class="site-main">
<router-outlet></router-outlet>
</main>
<footer class="site-footer">
<div class="footer-content">
<div class="footer-section about">
<img src="assets/logo_escrito.png" alt="Zendion, INC." class="footer-logo">
<p>
A Zendion, INC. desenvolve soluções tecnológicas para simplificar processos,
conectar informações e apoiar operações digitais desde 2019.
</p>
</div>
<div class="footer-section">
<h4>Navegação</h4>
<a [routerLink]="'/home'">Início</a>
<a href="/home#sistemas">Sistemas</a>
<a href="/home#noticias">Notícias</a>
<a [routerLink]="'/login'">Área do cliente</a>
</div>
<div class="footer-section">
<h4>Contato</h4>
<p>{{ emailContato }}</p>
<p>{{ foneContato }}</p>
<p>Zendion, INC.</p>
</div>
<div class="footer-section">
<h4>Institucional</h4>
<p>
Tecnologia, sistemas e automação para empresas que querem operar com mais clareza.
</p>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2019 - {{ currentYear }} Zendion, INC. Todos os direitos reservados.</p>
</div>
</footer>
</div>

View File

@ -0,0 +1,242 @@
.site-layout {
min-height: 100vh;
display: flex;
flex-direction: column;
background: #f4f6fb;
}
.site-header {
position: sticky;
top: 0;
z-index: 1000;
background: rgba(7, 27, 99, 0.92);
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
box-shadow: 0 12px 34px rgba(15, 23, 42, 0.18);
backdrop-filter: blur(14px);
}
.header-content {
width: 100%;
max-width: 1220px;
min-height: 76px;
margin: 0 auto;
padding: 0 22px;
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: 26px;
box-sizing: border-box;
}
.brand-link {
display: inline-flex;
align-items: center;
text-decoration: none;
}
.logo {
display: block;
object-fit: contain;
}
.logo-escrito {
height: 46px;
}
.logo-favicon {
display: none;
height: 42px;
}
.header-nav {
display: flex;
align-items: center;
justify-content: center;
gap: 24px;
}
.header-nav a {
position: relative;
color: #dbeafe;
font-size: 14px;
font-weight: 700;
text-decoration: none;
transition: color 0.18s ease;
}
.header-nav a::after {
content: "";
position: absolute;
left: 0;
bottom: -7px;
width: 0;
height: 2px;
border-radius: 999px;
background: #ffffff;
transition: width 0.18s ease;
}
.header-nav a:hover {
color: #ffffff;
}
.header-nav a:hover::after {
width: 100%;
}
.header-actions {
display: flex;
align-items: center;
gap: 10px;
}
.btn-header {
min-height: 42px;
padding: 0 18px;
border-radius: 12px;
font-size: 13px;
font-weight: 800;
border: 1px solid transparent;
cursor: pointer;
transition: transform 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease, color 0.18s ease;
white-space: nowrap;
}
.btn-header:hover {
transform: translateY(-1px);
}
.btn-header-primary {
background: #ffffff;
color: #071b63;
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.16);
}
.btn-header-primary:hover {
background: #f8fafc;
}
.btn-header-secondary {
background: rgba(255, 255, 255, 0.10);
color: #ffffff;
border-color: rgba(255, 255, 255, 0.22);
}
.btn-header-secondary:hover {
background: rgba(255, 255, 255, 0.16);
}
.site-main {
flex: 1;
}
.site-footer {
margin-top: auto;
background:
radial-gradient(circle at top left, rgba(255, 255, 255, 0.10), transparent 30%),
linear-gradient(145deg, #071b63, #04113f);
color: #ffffff;
}
.footer-content {
width: 100%;
max-width: 1220px;
margin: 0 auto;
padding: 48px 22px 36px 22px;
display: grid;
grid-template-columns: 1.4fr 0.8fr 0.9fr 1fr;
gap: 34px;
box-sizing: border-box;
}
.footer-section h4 {
margin: 0 0 16px 0;
color: #ffffff;
font-size: 15px;
font-weight: 850;
}
.footer-section p,
.footer-section a {
display: block;
margin: 0 0 10px 0;
color: #cbd5e1;
font-size: 14px;
line-height: 23px;
text-decoration: none;
}
.footer-section a:hover {
color: #ffffff;
text-decoration: underline;
}
.footer-logo {
height: 42px;
max-width: 220px;
object-fit: contain;
display: block;
margin-bottom: 18px;
filter: brightness(0) invert(1);
}
.footer-bottom {
border-top: 1px solid rgba(255, 255, 255, 0.12);
padding: 18px 22px;
text-align: center;
}
.footer-bottom p {
margin: 0;
color: #94a3b8;
font-size: 12px;
line-height: 18px;
}
@media (max-width: 920px) {
.header-content {
grid-template-columns: auto auto;
justify-content: space-between;
}
.header-nav {
display: none;
}
.footer-content {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: 576px) {
.header-content {
min-height: 68px;
padding: 0 14px;
gap: 12px;
}
.logo-escrito {
display: none;
}
.logo-favicon {
display: block;
height: 40px;
}
.header-actions {
gap: 8px;
}
.btn-header {
min-height: 38px;
padding: 0 12px;
font-size: 12px;
border-radius: 10px;
}
.footer-content {
grid-template-columns: 1fr;
padding: 38px 20px 28px 20px;
}
}

View File

@ -0,0 +1,78 @@
import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-base-site',
templateUrl: './base-site.component.html',
styleUrls: ['./base-site.component.scss']
})
export class BaseSiteComponent implements OnInit {
btnLoginText: string = "Entrar";
btnSuperior: string = "Registrar";
emailContato: string = environment.emailContato;
foneContato: string = environment.telefoneContato;
currentYear: string = new Date().getFullYear().toString();
constructor(
private router: Router,
) { }
ngOnInit() {
this.CheckIsLogged();
this.router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
this.CheckIsLogged();
}
});
}
CheckIsLogged() {
if (localStorage.getItem("Session") == "1") {
this.btnLoginText = "Sair";
if (localStorage.getItem("Role") != "1") {
this.btnSuperior = "Meus sistemas";
} else {
this.btnSuperior = "Painel administrativo";
}
} else {
this.btnLoginText = "Entrar";
this.btnSuperior = "Registrar";
}
}
btnLoginClick() {
if (this.btnLoginText == "Entrar") {
this.router.navigate(['login']);
} else {
this.Logout();
}
}
Logout() {
localStorage.removeItem("Session");
localStorage.removeItem("UserName");
localStorage.removeItem("Role");
this.CheckIsLogged();
this.router.navigate(['home']);
}
btnCriarcontaClick() {
if (this.btnSuperior == "Registrar") {
this.router.navigate(['criarconta']);
return;
}
if (this.btnSuperior == "Painel administrativo") {
this.router.navigate(['dashboard/admin/home']);
return;
}
this.router.navigate(['dashboard/cliente']);
}
}

View File

@ -0,0 +1,112 @@
<div class="quadro-Branco">
<div class="col-md-12" style="margin: auto; margin-bottom: 1em;">
<div class="col-md-12">
<h3>Sorteios</h3>
<div style="float: right; margin-top: -2em;">
<i class="material-icons" style="cursor: pointer;" (click)="Voltar()">arrow_back</i>
</div>
</div>
<div class="row col-md-12 MyHR"></div>
<div class="row col-md-12">
<div class="col-md-7">
<label>Descricão</label>
<input type="text" class="form-control" [(ngModel)]="FormSorteio.descricao" [disabled]="!Editando && !Novo">
</div>
<div class="col-md-2">
<label>Rodada</label>
<input type="number" class="form-control" [(ngModel)]="FormSorteio.rodada" [disabled]="!Editando && !Novo">
</div>
<div class="col-md-3">
<label>Data</label>
<input type="date" class="form-control" [(ngModel)]="FormSorteio.data_sorteio" [disabled]="!Editando && !Novo">
</div>
<!--<div class="col-md-2">
<label>Bolas</label>
<input type="number" class="form-control" [(ngModel)]="FormSorteio.n_max" readonly>
</div>
<div class="col-md-2">
<label>Cartelas</label>
<input type="number" class="form-control" [(ngModel)]="FormSorteio.participantes_max" readonly>
</div>-->
</div>
<div class="row col-md-12" style="margin-top: 1em;" [hidden]="!Editando && !Novo">
<div class="row col-md-12">
<h3 style="cursor: pointer;" (click)="FormSorteio.VerCartelas = !FormSorteio.VerCartelas">Cartelas</h3>
</div>
<div class="col-md-12" *ngIf="FormSorteio.idbingosorteios == 0">
<iframe src="http://zendioninc.mooo.com/Jogos/Bingo/Gerador/" style="border: none; width: 100%; height: 30em; border-radius: 10px;"></iframe>
</div>
<div class="col-md-12" *ngIf="FormSorteio.idbingosorteios > 0" style="transition: 1s; overflow: auto;" [ngStyle]="FormSorteio.VerCartelas && {'height' : '30em'} || !FormSorteio.VerCartelas && {'height' : '0'}">
<div class="row col-md-12" *ngFor="let Cartela of FormSorteio.Cartelas; let i = index">
<div class="col-md-2">
<label>Canhoto</label>
<input type="text" class="form-control" [(ngModel)]="Cartela.canhoto" readonly [ngStyle]="Cartela.nome && {'background' : 'lightgreen'}">
</div>
<div class="col-md-3">
<label>Numeros</label>
<input type="text" class="form-control" [(ngModel)]="Cartela.numeros" readonly [ngStyle]="Cartela.nome && {'background' : 'lightgreen'}">
</div>
<div class="col-md-2">
<label>Nome</label>
<input type="text" class="form-control" [(ngModel)]="Cartela.nome" placeholder="Nome do comprador" [disabled]="!Cartela.Editando">
</div>
<div class="col-md-2">
<label>Telefone</label>
<input type="text" class="form-control" [(ngModel)]="Cartela.telefone" placeholder="(00) 00000-0000" [disabled]="!Cartela.Editando">
</div>
<div class="col-md-2">
<label>Endereco</label>
<input type="text" class="form-control" [(ngModel)]="Cartela.endereco" placeholder="Endereco do comprador" [disabled]="!Cartela.Editando">
</div>
<div class="col-md-1">
<label>Acão</label>
<button class="btn" (click)="SalvarCartela(Cartela)" [ngClass]="{'btn-primary' : !Cartela.Editando, 'btn-success': Cartela.Editando}">{{(Cartela.Editando) ? 'Salvar' : 'Editar'}}</button>
</div>
</div>
</div>
</div>
<div class="row col-md-12" style="margin-top: 1em;">
<div class="col-md-6">
<button class="btn btn-primary" (click)="NovoBingo()" style="margin-right: 1em;" [disabled]="Editando || Novo">Novo</button>
<button class="btn btn-success" (click)="SalvarBingo()" style="margin-right: 1em;" [disabled]="!Editando && !Novo">Salvar</button>
<button class="btn btn-danger" (click)="CancelarBingo()" style="margin-right: 1em;" [disabled]="!Editando && !Novo">Cancelar</button>
</div>
<!--<div class="col-md-3">
<button class="btn btn-success" (click)="SalvarSorteio()">Salvar Sorteio</button>
</div>-->
</div>
</div>
<div class="row col-md-12 MyHR"></div>
<div class="row col-md-12" style="margin: auto;">
<h3>Lista</h3>
<div class="col-md-12 form-corpo">
<table>
<tr>
<th>Descicão</th>
<th>Rodada</th>
<th>Data</th>
<th>Numeros</th>
<th>Cartelas</th>
<th style="width: 18em">Ação</th>
</tr>
<tr *ngFor="let Sorteio of ListaSorteios">
<td>{{Sorteio.descricao}}</td>
<td>{{Sorteio.rodada}}</td>
<td>{{Sorteio.data_sorteio}}</td>
<td>{{Sorteio.n_max}}</td>
<td>{{Sorteio.participantes_max}}</td>
<td style="width: 18em">
<button class="btn btn-success" (click)="ExportarCartelas(Sorteio)" style="margin-right: 0.5em" [disabled]="Editando || Novo">Cartelas</button>
<button class="btn btn-success" (click)="EditarSorteio(Sorteio)" style="margin-right: 0.5em" [disabled]="Editando || Novo">Editar</button>
<button class="btn btn-danger" (click)="RemoverSorteio(Sorteio)" [disabled]="Editando || Novo">Remover</button>
</td>
</tr>
</table>
</div>
</div>
</div>

View File

@ -0,0 +1,176 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { BingoCartelasModel, BingoSorteiosModel } from '../models/bingoModel';
import { BingoService } from '../services/bingoService';
@Component({
selector: 'app-bingo-dashboard',
templateUrl: './bingo-dashboard.component.html',
styleUrls: ['./bingo-dashboard.component.scss', './../app.component.scss']
})
export class BingoDashboardComponent implements OnInit {
FormSorteio: BingoSorteiosModel = new BingoSorteiosModel();
ListaSorteios: BingoSorteiosModel[] = new Array<BingoSorteiosModel>();
CartelaNumeros: number = 25;
AbreCartelas: boolean = false;
Novo: boolean = false;
Editando: boolean = false;
constructor(
private bingoService: BingoService,
private router: Router
) { }
ngOnInit() {
this.AtualizarDados();
}
AtualizarDados() {
localStorage.setItem("Cartelas", "");
localStorage.setItem("NMax", "0");
localStorage.setItem("NParticipantes", "0");
this.CancelarBingo();
this.bingoService.getAllSorteios().then(resSorteios => {
this.ListaSorteios = resSorteios;
this.GetCartelasSorteio(0);
});
}
GetCartelasSorteio(Index) {
if (Index < this.ListaSorteios.length) {
this.bingoService.getCartelasSorteio(this.ListaSorteios[Index].idbingosorteios).then(resCartelas => {
this.ListaSorteios[Index].Cartelas = resCartelas;
Index++;
this.GetCartelasSorteio(Index);
});
}
else {
//console.log(this.ListaSorteios);
}
}
NovoBingo() {
this.FormSorteio = new BingoSorteiosModel();
this.Novo = true;
}
SalvarBingo() {
if (this.FormSorteio.idbingosorteios > 0) {
this.SalvarSorteio();
}
else {
if (localStorage.getItem("Cartelas") == "") {
alert("Gere as cartelas para poder salvar");
return;
}
if (localStorage.getItem("NMax") == "0" || localStorage.getItem("NParticipantes") == "0") {
alert("Preencha corretamente os numeros de participantes e cartelas");
return;
}
this.FormSorteio.n_max = parseInt(localStorage.getItem("NMax"));
this.FormSorteio.participantes_max = parseInt(localStorage.getItem("NParticipantes"));
let CartelasGeradas: CasaCartela[] = JSON.parse(localStorage.getItem("Cartelas"));
this.FormSorteio.Cartelas = new Array<BingoCartelasModel>();
for (let i = 0; i < CartelasGeradas.length; i++) {
let Cartela: BingoCartelasModel = new BingoCartelasModel();
Cartela.canhoto = this.FormSorteio.Cartelas.length + 1;
for (let o = 0; o < this.CartelaNumeros; o++) {
if (Cartela.numeros != "")
Cartela.numeros += ",";
Cartela.numeros += CartelasGeradas[i][o].display;
}
this.FormSorteio.Cartelas.push(Cartela);
}
console.log(this.FormSorteio);
this.SalvarSorteio();
}
}
SalvarSorteio() {
this.bingoService.SalvarBingoSorteio(this.FormSorteio).then(resSalvar => {
if (resSalvar['status'] == 200) {
if (this.FormSorteio.idbingosorteios > 0) {
alert("Sorteio inserido com sucesso");
this.AtualizarDados();
}
else { // SALVAR AS CARTELAS SE ESTIVER EM MODO DE INSERT
this.FormSorteio.idbingosorteios = resSalvar['response']['insertId'];
this.bingoService.getAllCartelas().then(resAllCartelas => {
for (let i = 0; i < this.FormSorteio.Cartelas.length; i++) {
this.FormSorteio.Cartelas[i].canhoto += resAllCartelas.length;
}
this.bingoService.insertBingoCartela(this.FormSorteio).then(resCartelas => {
alert("Sorteio inserido com sucesso");
this.AtualizarDados();
});
});
}
}
else {
alert("Erro ao inserir Sorteio");
console.log(resSalvar['error']);
}
});
}
CancelarBingo() {
this.FormSorteio = new BingoSorteiosModel();
this.Novo = false;
this.Editando = false;
}
ExportarCartelas(Sorteio: BingoSorteiosModel) {
this.bingoService.exportarExcel(Sorteio).then(resExport => {
window.open('http://zendioninc.mooo.com/Jogos/Bingo/Gerador/Cartelas/' + Sorteio.idbingosorteios + '.csv', '_blank');
});
}
EditarSorteio(Sorteio: BingoSorteiosModel) {
this.FormSorteio = new BingoSorteiosModel();
this.FormSorteio.data_sorteio = Sorteio.data_sorteio.split("/")[2] + "-" + Sorteio.data_sorteio.split("/")[1] + "-" + Sorteio.data_sorteio.split("/")[0];
this.FormSorteio.descricao = Sorteio.descricao;
this.FormSorteio.idbingosorteios = Sorteio.idbingosorteios;
this.FormSorteio.n_max = Sorteio.n_max;
this.FormSorteio.participantes_max = Sorteio.participantes_max;
this.FormSorteio.rodada = Sorteio.rodada;
this.FormSorteio.Cartelas = Sorteio.Cartelas;
this.FormSorteio.VerCartelas = false;
this.Editando = true;
}
RemoverSorteio(Sorteio: BingoSorteiosModel) {
if (!confirm("Tem certeza que deseja remover esse sorteio?"))
return;
this.bingoService.deleteBingoSorteio(Sorteio.idbingosorteios).then(resDelete => {
alert("Removido com sucesso");
this.AtualizarDados();
});
}
SalvarCartela(Cartela: BingoCartelasModel) {
if (Cartela.Editando) {
this.bingoService.updateBingoCartela(Cartela).then(resUpdate => {
Cartela.Editando = !Cartela.Editando;
});
}
else {
Cartela.Editando = !Cartela.Editando;
}
}
Voltar() {
this.router.navigate(['dashboard/cliente']);
}
}
class CasaCartela {
display: string;
tipo: number;
}

View File

@ -0,0 +1,23 @@
<div class="FundoCheio" style="font-family: cursive;">
<div style="height: calc(100vh - 3em); width: calc(100vw - 2em); text-align: center;">
<div class="row col-md-12" *ngIf="!LiberaSorteio">
<div class="col-md-12" style="margin-top: 4em;">
<h2 class="Cabecalho">SELECIONE O SORTEIO</h2>
</div>
<div class="col-md-12">
<select class="comboBox" [(ngModel)]="SorteioSelecionado.idbingosorteios">
<option value="0" disabled>* Selecione um sorteio</option>
<option *ngFor="let Sorteio of ListaSorteios" [value]="Sorteio.idbingosorteios">{{Sorteio.descricao}} - Rodada {{Sorteio.rodada}}</option>
</select>
</div>
<div class="col-md-12">
<button class="btnSorteio" (click)="SelecionarSorteio()">SELECIONAR</button>
</div>
</div>
<iframe [hidden]="!LiberaSorteio" src="http://zendioninc.mooo.com/Jogos/Bingo/index.html" style="border: 1px solid #ddd; border-radius: 1em; height: 100%; width: 90%;"></iframe>
</div>
<div style="height: 2em;">
Cartelas faltando 1 numero para bater: {{CartelasParaBater.length}}
</div>
</div>

View File

@ -0,0 +1,32 @@
.Cabecalho {
color: forestgreen;
}
.btnSorteio {
background: #90ee90;
border-radius: 10em;
border: 2px solid forestgreen;
width: 10em;
height: 3em;
font-size: xx-large;
color: forestgreen;
transition: 0.5s;
margin-top: 2em;
}
.btnSorteio:hover {
background: #70c970;
color: #fff;
}
.comboBox {
outline: none;
padding-left: 1em;
height: 3em;
width: 25em;
font-size: larger;
border: 2px solid forestgreen;
border-radius: 10em;
text-align: center;
margin-top: 2em;
}

View File

@ -0,0 +1,176 @@
import { Component, OnInit } from '@angular/core';
import { BingoCartelasModel, BingoSorteiosModel } from '../models/bingoModel';
import { BingoService } from '../services/bingoService';
@Component({
selector: 'app-bingo-sort',
templateUrl: './bingo-sort.component.html',
styleUrls: ['./bingo-sort.component.scss', './../app.component.scss']
})
export class BingoSortComponent implements OnInit {
Sorteados: string = "";
Numeros: number[] = new Array<number>();
ListaSorteios: BingoSorteiosModel[] = new Array<BingoSorteiosModel>();
LiberaSorteio: boolean = false;
SorteioSelecionado: BingoSorteiosModel = new BingoSorteiosModel();
CartelasParaBater: BingoCartelasModel[] = new Array<BingoCartelasModel>();
Bingo: boolean = false;
Cinquina: number = 0;
CartelasSorteadas: BingoCartelasModel[] = new Array<BingoCartelasModel>();
constructor(
private bingoService: BingoService
) { }
ngOnInit() {
localStorage.setItem("NumSort", "");
this.Numeros = new Array<number>();
this.bingoService.getSorteiosRealizar().then(resSorteios => {
this.ListaSorteios = resSorteios;
});
}
SelecionarSorteio() {
for (let i = 0; i < this.ListaSorteios.length; i++) {
if (this.ListaSorteios[i].idbingosorteios == this.SorteioSelecionado.idbingosorteios) {
this.SorteioSelecionado = this.ListaSorteios[i];
break;
}
}
this.bingoService.getCartelasVendidasSorteio(this.SorteioSelecionado.idbingosorteios).then(resCartelas => {
this.SorteioSelecionado.Cartelas = resCartelas;
this.LiberaSorteio = true;
console.log(this.SorteioSelecionado);
this.LoopNumeracao();
});
}
LoopNumeracao() {
setTimeout(() => {
if (localStorage.getItem("NumSort") && localStorage.getItem("NumSort") != this.Sorteados) {
this.Sorteados = localStorage.getItem("NumSort");
const NumVar: string[] = localStorage.getItem("NumSort").split(',');
const NumeroAtual: number = parseInt(NumVar[NumVar.length - 1]);
this.Numeros.push(NumeroAtual);
this.VerificaCartelas(NumeroAtual);
}
else {
}
if (!this.Bingo)
this.LoopNumeracao();
}, 1000);
}
VerificaCartelas(NSorteado: number) {
for (let i = 0; i < this.SorteioSelecionado.Cartelas.length; i++) {
let Numeros: string[] = this.SorteioSelecionado.Cartelas[i].numeros.split(',');
for (let o = 0; o < Numeros.length; o++) {
if (Numeros[o] == NSorteado.toString()) {
this.SorteioSelecionado.Cartelas[i].Acertos++;
break;
}
}
if (this.Cinquina < 3 && this.Numeros.length > 3) {
this.ConfereCinquina();
}
// CINQUINA 1
if (this.Cinquina == 0 && !this.SorteioSelecionado.Cartelas[i].cinquina1 && this.SorteioSelecionado.Cartelas[i].Acertos == 5) {
this.SorteioSelecionado.Cartelas[i].cinquina1 = true;
this.CartelasSorteadas.push(this.SorteioSelecionado.Cartelas[i]);
}
// CINQUINA 2
if (this.Cinquina == 1 && !this.SorteioSelecionado.Cartelas[i].cinquina2 && this.SorteioSelecionado.Cartelas[i].Acertos == 10) {
this.SorteioSelecionado.Cartelas[i].cinquina2 = true;
this.CartelasSorteadas.push(this.SorteioSelecionado.Cartelas[i]);
}
// CINQUINA 3
if (this.Cinquina == 2 && !this.SorteioSelecionado.Cartelas[i].cinquina3 && this.SorteioSelecionado.Cartelas[i].Acertos == 15) {
this.SorteioSelecionado.Cartelas[i].cinquina3 = true;
this.CartelasSorteadas.push(this.SorteioSelecionado.Cartelas[i]);
}
// CARTELAS COM 1 NUMERO PARA BATER
if (this.SorteioSelecionado.Cartelas[i].Acertos == Numeros.length - 1) {
let Adicionada: boolean = false;
for (let o = 0; o < this.CartelasParaBater.length; o++) {
if (this.CartelasParaBater[o].idbingosorteios == this.SorteioSelecionado.Cartelas[i].idbingosorteios) {
Adicionada = true;
break;
}
}
if (!Adicionada)
this.CartelasParaBater.push(this.SorteioSelecionado.Cartelas[i]);
}
// CARTELA COM QUANTIDADE DE ACERTOS = QUANTIDADE DE NUMEROS, LOGO, BINGO!
if (this.SorteioSelecionado.Cartelas[i].Acertos == Numeros.length) {
this.SorteioSelecionado.Cartelas[i].bingo = true;
this.FinalizaSorteio(true, [this.SorteioSelecionado.Cartelas[i]]);
break;
}
}
if (this.CartelasSorteadas.length > 0)
this.FinalizaSorteio(false, this.CartelasSorteadas);
}
FinalizaSorteio(Bingo: boolean, Cartelas: BingoCartelasModel[]) {
if (Bingo) { // BINGO
this.bingoService.updateBingoCartela(Cartelas[0]).then(resUpdate => {
this.SorteioSelecionado.realizado = true;
this.SorteioSelecionado.data_sorteio = this.SorteioSelecionado.data_sorteio.replace("T", " ").replace("Z", "");
this.bingoService.SalvarBingoSorteio(this.SorteioSelecionado).then(resAtt => {
this.Bingo = true;
let Mensagem: string = `BINGO!!\r\nCartela: ${Cartelas[0].canhoto} - Ganhador: ${Cartelas[0].nome}\r\nParabens!`;
alert(Mensagem);
console.log("Cartela vencedora: " + Cartelas[0].canhoto);
console.log("Vencedor: " + Cartelas[0].nome);
});
});
}
else { // CINQUINAS
this.Cinquina++;
this.LoopUpdateCinquina(0);
}
}
LoopUpdateCinquina(Index: number) {
if (Index < this.CartelasSorteadas.length) {
this.bingoService.updateBingoCartela(this.CartelasSorteadas[Index]).then(resUpdate => {
Index++;
this.LoopUpdateCinquina(Index);
});
}
else {
let Mensagem: string = "CINQUINA " + this.Cinquina + " sorteada para as seguintes cartelas:\r\n";
for (let i = 0; i < this.CartelasSorteadas.length; i++) {
Mensagem += `Cartela: ${this.CartelasSorteadas[i].canhoto} - Ganhador: ${this.CartelasSorteadas[i].nome}\r\n`;
}
Mensagem += "Parabens!!";
alert(Mensagem);
this.CartelasSorteadas = new Array<BingoCartelasModel>();
}
}
ConfereCinquina() {
for (let i = 0; i < this.SorteioSelecionado.Cartelas.length; i++) {
let CartelaNumeros: string[] = this.SorteioSelecionado.Cartelas[i].numeros.split(",");
if (CartelaNumeros.length > 3) {
let NIguais: number = 0;
for (let o = 0; o < this.Numeros.length; o++) {
if (this.SorteioSelecionado.Cartelas[i].numeros.includes(this.Numeros[o].toString())) {
}
}
}
}
}
}

View File

@ -0,0 +1 @@
<p>bingo works!</p>

View File

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-bingo',
templateUrl: './bingo.component.html',
styleUrls: ['./bingo.component.scss']
})
export class BingoComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@ -0,0 +1,239 @@
<div class="details-admin-page">
<!-- LOADING -->
<div class="details-loader" *ngIf="!CarregadoSistema || !CarregadoLista">
<img src="assets/loader.gif" alt="Carregando">
<p>Carregando editor de detalhes...</p>
</div>
<ng-container *ngIf="CarregadoSistema && CarregadoLista">
<!-- HERO SISTEMA -->
<section class="system-hero">
<div class="system-hero-content">
<div class="system-logo-box">
<img
[src]="LinkProjeto + '/sistemas/' + Sistema.idsistemas + '/icone.png'"
alt="Ícone do sistema"
class="system-logo">
</div>
<div class="system-title">
<span class="eyebrow">Editor de detalhes</span>
<h1>{{ Sistema.nome }}</h1>
<p>{{ Sistema.descricao }}</p>
<div class="system-tags">
<span>Versão {{ Sistema.versaoatual }}</span>
<span>{{ Sistema.tiposdesistema }}</span>
<span>{{ ListaDetalhes?.length || 0 }} detalhe(s)</span>
</div>
</div>
</div>
</section>
<!-- RESUMO -->
<section class="summary-grid">
<div class="summary-card">
<div class="summary-icon">
<i class="material-icons">article</i>
</div>
<div>
<span>Total de detalhes</span>
<strong>{{ ListaDetalhes?.length || 0 }}</strong>
</div>
</div>
<div class="summary-card">
<div class="summary-icon active">
<i class="material-icons">visibility</i>
</div>
<div>
<span>Habilitados</span>
<strong>{{ TotalDetalhesHabilitados }}</strong>
</div>
</div>
<div class="summary-card">
<div class="summary-icon blocked">
<i class="material-icons">visibility_off</i>
</div>
<div>
<span>Desabilitados</span>
<strong>{{ TotalDetalhesDesabilitados }}</strong>
</div>
</div>
</section>
<!-- PAINEL PRINCIPAL -->
<section class="admin-card details-panel">
<div class="panel-header">
<div>
<span class="eyebrow">Conteúdo da página pública</span>
<h2>Detalhes do sistema</h2>
<p>
Edite os blocos que aparecem na página pública do sistema. Cada detalhe pode ter texto, imagem e status de publicação.
</p>
</div>
<button type="button" class="btn-main" (click)="NovoDetalhe()">
<i class="material-icons">note_add</i>
Novo detalhe
</button>
</div>
<!-- EMPTY -->
<div *ngIf="ListaDetalhes.length == 0" class="empty-state">
<div class="empty-icon">
<i class="material-icons">post_add</i>
</div>
<h3>Nenhum detalhe inserido</h3>
<p>
Clique em “Novo detalhe” para criar o primeiro bloco de apresentação deste sistema.
</p>
<button type="button" class="btn-main empty-btn" (click)="NovoDetalhe()">
<i class="material-icons">note_add</i>
Criar primeiro detalhe
</button>
</div>
<!-- LISTA DETALHES -->
<div class="details-list" *ngIf="ListaDetalhes.length > 0">
<article
class="detail-editor-card"
*ngFor="let Detalhe of ListaDetalhes; let i = index"
[ngClass]="Detalhe.habilitado ? 'enabled' : 'disabled'">
<div class="detail-card-header">
<div class="detail-index">
<span>#{{ i + 1 }}</span>
</div>
<div class="detail-status">
<span [ngClass]="Detalhe.habilitado ? 'status-enabled' : 'status-disabled'">
{{ Detalhe.habilitado ? 'Habilitado' : 'Desabilitado' }}
</span>
</div>
</div>
<div class="detail-card-body">
<div class="detail-editor">
<label>Descrição / HTML</label>
<textarea
[(ngModel)]="Detalhe.descricao"
class="form-control textarea-control"
placeholder="Digite o texto deste detalhe. Pode conter HTML simples, como <p>, <strong>, <br>...">
</textarea>
<div class="file-row">
<label class="file-label">
<i class="material-icons">image</i>
Selecionar imagem
<input type="file" (change)="onFileChanged($event, i)">
</label>
<small *ngIf="Detalhe.Arquivo">
{{ Detalhe.Arquivo.name }}
</small>
</div>
<div class="detail-actions">
<button type="button" class="btn-card remove" (click)="RemoverDetalhe(i)">
<i class="material-icons">delete</i>
Remover
</button>
<button
type="button"
class="btn-card"
(click)="HabilitarDetalhe(Detalhe)"
[ngClass]="Detalhe.habilitado ? 'disable' : 'enable'">
<i class="material-icons">
{{ Detalhe.habilitado ? 'visibility_off' : 'visibility' }}
</i>
{{ Detalhe.botao }}
</button>
</div>
</div>
<div class="detail-preview">
<span>Prévia da imagem</span>
<div class="preview-box">
<img
*ngIf="Detalhe.imagem"
[src]="Detalhe.imagem"
alt="Imagem do detalhe">
<div *ngIf="!Detalhe.imagem" class="preview-empty">
<i class="material-icons">image_not_supported</i>
<small>Nenhuma imagem selecionada</small>
</div>
</div>
</div>
</div>
</article>
</div>
</section>
<!-- PAINEL INFERIOR -->
<section class="secondary-grid">
<div class="admin-card platform-card">
<span class="eyebrow">Disponível para</span>
<img
[src]="LinkProjeto + '/imagens/plataformas/plataforma_' + Sistema.idplataformas + '.png'"
alt="Plataforma"
class="platform-icon">
<strong>{{ Sistema.plataforma }}</strong>
</div>
<div class="admin-card update-card">
<span class="eyebrow">Notas de atualização</span>
<small *ngIf="Sistema.update_date">
Atualizado em {{ Sistema.update_date | date:'dd/MM/yyyy' }}
</small>
<div [innerHTML]="Sistema.notasdeatualizacao" class="update-text"></div>
</div>
</section>
<!-- SAVE BAR -->
<section class="save-bar" *ngIf="ListaDetalhes.length > 0">
<div>
<strong>Pronto para salvar?</strong>
<span>As alterações serão aplicadas na página pública do sistema.</span>
</div>
<button type="button" class="btn-save" (click)="Salvar()" [disabled]="Salvando">
<i class="material-icons">{{ Salvando ? 'hourglass_top' : 'save' }}</i>
{{ Salvando ? 'Salvando...' : 'Salvar detalhes' }}
</button>
</section>
</ng-container>
</div>

View File

@ -0,0 +1,706 @@
.details-admin-page {
min-height: calc(100vh - 74px);
color: #0f172a;
}
/* LOADING */
.details-loader {
min-height: 360px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.details-loader img {
width: 72px;
height: 72px;
object-fit: contain;
margin-bottom: 16px;
}
.details-loader p {
margin: 0;
color: #64748b;
font-size: 15px;
font-weight: 800;
}
/* HERO */
.system-hero {
position: relative;
overflow: hidden;
margin-bottom: 22px;
padding: 32px;
border-radius: 24px;
background:
radial-gradient(circle at top right, rgba(255, 255, 255, 0.14), transparent 34%),
linear-gradient(145deg, #071b63, #12358f);
color: #ffffff;
box-shadow: 0 22px 55px rgba(15, 23, 42, 0.16);
}
.system-hero::before {
content: "";
position: absolute;
width: 320px;
height: 320px;
right: -120px;
top: -120px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.11);
}
.system-hero-content {
position: relative;
z-index: 1;
display: flex;
align-items: center;
gap: 24px;
}
.system-logo-box {
width: 96px;
height: 96px;
flex: 0 0 auto;
border-radius: 24px;
background: #ffffff;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 18px 42px rgba(0, 0, 0, 0.18);
}
.system-logo {
width: 62px;
height: 62px;
object-fit: contain;
}
.eyebrow {
display: inline-block;
margin-bottom: 10px;
color: #071b63;
font-size: 12px;
font-weight: 900;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.system-hero .eyebrow {
color: #bfdbfe;
}
.system-title h1 {
margin: 0 0 12px 0;
color: #ffffff;
font-size: 34px;
line-height: 42px;
font-weight: 900;
letter-spacing: -0.04em;
}
.system-title p {
max-width: 760px;
margin: 0;
color: #dbeafe;
font-size: 15px;
line-height: 25px;
}
.system-tags {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 18px;
}
.system-tags span {
display: inline-flex;
align-items: center;
min-height: 28px;
padding: 0 12px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.12);
color: #ffffff;
font-size: 12px;
font-weight: 800;
}
/* SUMMARY */
.summary-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px;
margin-bottom: 22px;
}
.summary-card {
display: flex;
align-items: center;
gap: 16px;
padding: 20px;
border-radius: 20px;
background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.22);
box-shadow: 0 16px 36px rgba(15, 23, 42, 0.08);
}
.summary-icon {
width: 48px;
height: 48px;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
background: #f1f5ff;
color: #071b63;
}
.summary-icon.active {
background: #dcfce7;
color: #166534;
}
.summary-icon.blocked {
background: #fee2e2;
color: #991b1b;
}
.summary-icon .material-icons {
font-size: 25px;
}
.summary-card span {
display: block;
margin-bottom: 4px;
color: #64748b;
font-size: 12px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.summary-card strong {
display: block;
color: #0f172a;
font-size: 22px;
line-height: 28px;
font-weight: 900;
}
/* CARDS */
.admin-card {
padding: 28px;
border-radius: 24px;
background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.22);
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
}
.details-panel {
margin-bottom: 22px;
}
.panel-header {
display: flex;
justify-content: space-between;
gap: 22px;
align-items: flex-start;
margin-bottom: 24px;
}
.panel-header h2 {
margin: 0 0 8px 0;
color: #0f172a;
font-size: 26px;
line-height: 34px;
font-weight: 900;
letter-spacing: -0.035em;
}
.panel-header p {
max-width: 760px;
margin: 0;
color: #64748b;
font-size: 14px;
line-height: 23px;
}
/* BUTTONS */
.btn-main,
.btn-card,
.btn-save {
border: none;
cursor: pointer;
font-weight: 900;
transition: transform 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease;
}
.btn-main {
min-height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 0 18px;
border-radius: 12px;
background: linear-gradient(135deg, #071b63, #12358f);
color: #ffffff;
box-shadow: 0 12px 24px rgba(7, 27, 99, 0.20);
}
.btn-main:hover,
.btn-card:hover,
.btn-save:hover {
transform: translateY(-1px);
}
.btn-main .material-icons,
.btn-card .material-icons,
.btn-save .material-icons {
font-size: 19px;
}
/* EMPTY */
.empty-state {
padding: 42px 20px;
text-align: center;
border-radius: 22px;
background: #f8fafc;
border: 1px dashed #cbd5e1;
}
.empty-icon {
width: 72px;
height: 72px;
margin: 0 auto 18px auto;
border-radius: 999px;
display: flex;
align-items: center;
justify-content: center;
background: #f1f5ff;
color: #071b63;
}
.empty-icon .material-icons {
font-size: 34px;
}
.empty-state h3 {
margin: 0 0 10px 0;
color: #0f172a;
font-size: 22px;
font-weight: 900;
}
.empty-state p {
max-width: 560px;
margin: 0 auto 22px auto;
color: #64748b;
font-size: 14px;
line-height: 23px;
}
/* DETAIL EDITOR */
.details-list {
display: grid;
gap: 20px;
}
.detail-editor-card {
position: relative;
overflow: hidden;
padding: 20px;
border-radius: 22px;
background: #f8fafc;
border: 1px solid #e5e7eb;
}
.detail-editor-card::before {
content: "";
position: absolute;
left: 0;
top: 0;
width: 7px;
height: 100%;
background: #94a3b8;
}
.detail-editor-card.enabled {
border-color: rgba(22, 163, 74, 0.26);
background: linear-gradient(90deg, rgba(220, 252, 231, 0.60), #f8fafc 32%);
}
.detail-editor-card.enabled::before {
background: #16a34a;
}
.detail-editor-card.disabled {
border-color: rgba(220, 38, 38, 0.22);
background: linear-gradient(90deg, rgba(254, 226, 226, 0.55), #f8fafc 32%);
}
.detail-editor-card.disabled::before {
background: #dc2626;
}
.detail-card-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
margin-bottom: 16px;
}
.detail-index span {
display: inline-flex;
align-items: center;
min-height: 30px;
padding: 0 12px;
border-radius: 999px;
background: #e0f2fe;
color: #075985;
font-size: 13px;
font-weight: 900;
}
.detail-status span {
display: inline-flex;
align-items: center;
min-height: 30px;
padding: 0 12px;
border-radius: 999px;
font-size: 12px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.status-enabled {
background: #dcfce7;
color: #166534;
}
.status-disabled {
background: #fee2e2;
color: #991b1b;
}
.detail-card-body {
display: grid;
grid-template-columns: 1fr 440px;
gap: 20px;
align-items: stretch;
}
.detail-editor label,
.detail-preview span {
display: block;
margin-bottom: 8px;
color: #1e293b;
font-size: 14px;
font-weight: 900;
}
.form-control {
width: 100%;
border-radius: 14px;
border: 1px solid #d7deea;
background: #ffffff;
color: #0f172a;
font-size: 14px;
outline: none;
transition: border-color 0.18s ease, box-shadow 0.18s ease;
box-sizing: border-box;
}
.form-control:focus {
border-color: #071b63;
box-shadow: 0 0 0 4px rgba(7, 27, 99, 0.10);
}
.textarea-control {
min-height: 230px;
padding: 14px;
resize: vertical;
line-height: 22px;
}
.file-row {
display: flex;
align-items: center;
gap: 12px;
margin-top: 14px;
flex-wrap: wrap;
}
.file-label {
min-height: 40px;
display: inline-flex !important;
align-items: center;
justify-content: center;
gap: 8px;
padding: 0 14px;
border-radius: 12px;
background: #f1f5ff;
color: #071b63 !important;
font-size: 13px !important;
font-weight: 900 !important;
cursor: pointer;
border: 1px solid #dbe6ff;
margin: 0 !important;
}
.file-label input {
display: none;
}
.file-label .material-icons {
font-size: 20px;
}
.file-row small {
color: #64748b;
font-size: 12px;
font-weight: 800;
}
.detail-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 16px;
}
.btn-card {
min-height: 40px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 7px;
padding: 0 14px;
border-radius: 12px;
color: #ffffff;
font-size: 12px;
}
.btn-card.remove {
background: #dc2626;
}
.btn-card.enable {
background: #15803d;
}
.btn-card.disable {
background: #b45309;
}
.detail-preview {
min-width: 0;
}
.preview-box {
height: 330px;
overflow: hidden;
border-radius: 18px;
background: #ffffff;
border: 1px solid #dbe6ff;
display: flex;
align-items: center;
justify-content: center;
}
.preview-box img {
width: 100%;
height: 100%;
object-fit: cover;
}
.preview-empty {
display: flex;
flex-direction: column;
align-items: center;
color: #94a3b8;
}
.preview-empty .material-icons {
font-size: 42px;
margin-bottom: 8px;
}
/* SECONDARY */
.secondary-grid {
display: grid;
grid-template-columns: 0.75fr 1.25fr;
gap: 22px;
margin-bottom: 22px;
}
.platform-card {
text-align: center;
}
.platform-icon {
display: block;
width: 56px;
height: 56px;
object-fit: contain;
margin: 10px auto 14px auto;
}
.platform-card strong {
color: #0f172a;
font-size: 18px;
font-weight: 900;
}
.update-card small {
display: block;
margin-bottom: 14px;
color: #64748b;
font-size: 13px;
font-weight: 800;
}
.update-text {
padding: 16px;
border-radius: 16px;
background: #f8fafc;
border: 1px solid #e5e7eb;
color: #475569;
font-size: 14px;
line-height: 23px;
}
/* SAVE BAR */
.save-bar {
position: sticky;
bottom: 18px;
z-index: 20;
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
padding: 18px 20px;
border-radius: 20px;
background: rgba(7, 27, 99, 0.96);
color: #ffffff;
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.24);
backdrop-filter: blur(12px);
}
.save-bar strong {
display: block;
font-size: 15px;
font-weight: 900;
}
.save-bar span {
display: block;
margin-top: 3px;
color: #bfdbfe;
font-size: 13px;
}
.btn-save {
min-height: 44px;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 0 18px;
border-radius: 12px;
background: #ffffff;
color: #071b63;
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.16);
}
.btn-save:disabled {
opacity: 0.7;
cursor: not-allowed;
}
/* RESPONSIVE */
@media (max-width: 1100px) {
.detail-card-body {
grid-template-columns: 1fr;
}
.secondary-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 760px) {
.system-hero-content {
flex-direction: column;
text-align: center;
}
.system-tags {
justify-content: center;
}
.summary-grid {
grid-template-columns: 1fr;
}
.panel-header {
flex-direction: column;
align-items: stretch;
}
.btn-main {
width: 100%;
}
.save-bar {
position: static;
flex-direction: column;
align-items: stretch;
}
.btn-save {
width: 100%;
justify-content: center;
}
}
@media (max-width: 520px) {
.system-hero,
.admin-card {
padding: 22px;
}
.system-title h1 {
font-size: 28px;
line-height: 36px;
}
.detail-editor-card {
padding: 16px;
}
.preview-box {
height: 230px;
}
.detail-actions {
flex-direction: column;
}
.btn-card {
width: 100%;
}
}

View File

@ -0,0 +1,223 @@
import { Component, OnInit } from '@angular/core';
import { SistemasDetalhes, SistemasLista } from '../models/sistemasModel';
import { SistemasService } from '../services/sistemas.service';
import { Router, ActivatedRoute } from '@angular/router';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-cad-det-sistema',
templateUrl: './cad-det-sistema.component.html',
styleUrls: ['./cad-det-sistema.component.scss', './../app.component.scss']
})
export class CadDetSistemaComponent implements OnInit {
ListaDetalhes: SistemasDetalhes[] = [];
Sistema: SistemasLista = new SistemasLista();
LinkProjeto: string = environment.LinkProjeto;
CarregadoLista: boolean = false;
CarregadoSistema: boolean = false;
IndexDetalhe: number = 0;
ItensNaLista: number = 0;
Salvando: boolean = false;
constructor(
private sistemasService: SistemasService,
private router: Router,
private route: ActivatedRoute,
) { }
ngOnInit() {
this.CarregadoLista = false;
this.CarregadoSistema = false;
this.IndexDetalhe = 0;
this.Salvando = false;
const idSistema = this.route.snapshot.params['idsistemas'];
this.sistemasService.getSistemaByIDLista(idSistema).then(resSistema => {
this.Sistema = resSistema;
this.CarregadoSistema = true;
});
this.sistemasService.getDetalhesByID(idSistema).then(resDetalhes => {
this.ListaDetalhes = resDetalhes || [];
this.ItensNaLista = this.ListaDetalhes.length;
if (this.ItensNaLista > 0) {
this.PreencheLista();
} else {
this.CarregadoLista = true;
}
});
}
get TotalDetalhesHabilitados(): number {
return (this.ListaDetalhes || []).filter(detalhe => detalhe.habilitado).length;
}
get TotalDetalhesDesabilitados(): number {
return (this.ListaDetalhes || []).filter(detalhe => !detalhe.habilitado).length;
}
PreencheLista() {
for (let i = 0; i < this.ItensNaLista; i++) {
const detalhe = this.ListaDetalhes[i];
detalhe.botao = detalhe.habilitado ? "Desabilitar" : "Habilitar";
const ordemImagem = detalhe.ordem != null && detalhe.ordem != undefined ? detalhe.ordem : i;
detalhe.imagem = this.LinkProjeto +
"/sistemas/" +
detalhe.idsistemas +
"/detalhes/detalhe_" +
ordemImagem +
".jpg";
}
this.CarregadoLista = true;
}
NovoDetalhe() {
const novo = new SistemasDetalhes();
novo.habilitado = false;
novo.idsistemas = this.Sistema.idsistemas;
novo.botao = "Habilitar";
novo.ordem = this.ListaDetalhes.length;
novo.descricao = "";
novo.imagem = "";
this.ListaDetalhes.push(novo);
}
onFileChanged(event, index: number) {
if (event.target.files && event.target.files[0]) {
this.ListaDetalhes[index].Arquivo = event.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(event.target.files[0]);
reader.onload = (event) => {
this.ListaDetalhes[index].imagem = <any>event.target['result'];
};
}
}
RemoverDetalhe(index: number) {
if (!confirm("Tem certeza que deseja remover esse item?")) {
return;
}
const detalhe = this.ListaDetalhes[index];
if (!detalhe.idsistemasdetalhes) {
this.ListaDetalhes.splice(index, 1);
this.ReordenarListaLocal();
return;
}
this.sistemasService.deleteSistemaDetalhe(detalhe.idsistemasdetalhes, this.Sistema.idsistemas, index).then(resDelete => {
if (resDelete['status'] == 200) {
this.ListaDetalhes.splice(index, 1);
this.ItensNaLista = this.ListaDetalhes.length;
this.ReordenarListaLocal();
} else {
alert("Erro ao remover detalhe");
console.log(resDelete);
}
});
}
ReordenarListaLocal() {
for (let i = 0; i < this.ListaDetalhes.length; i++) {
this.ListaDetalhes[i].ordem = i;
}
}
HabilitarDetalhe(Detalhe: SistemasDetalhes) {
Detalhe.habilitado = !Detalhe.habilitado;
Detalhe.botao = Detalhe.habilitado ? "Desabilitar" : "Habilitar";
}
Salvar() {
if (this.Salvando) {
return;
}
this.Salvando = true;
this.IndexDetalhe = 0;
this.SalvarProximo();
}
SalvarProximo() {
if (this.IndexDetalhe < this.ListaDetalhes.length) {
const Detalhe = new SistemasDetalhes();
Detalhe.idsistemasdetalhes = this.ListaDetalhes[this.IndexDetalhe].idsistemasdetalhes;
Detalhe.habilitado = this.ListaDetalhes[this.IndexDetalhe].habilitado;
Detalhe.descricao = this.ListaDetalhes[this.IndexDetalhe].descricao;
Detalhe.idsistemas = this.ListaDetalhes[this.IndexDetalhe].idsistemas;
Detalhe.ordem = this.IndexDetalhe;
if (this.IndexDetalhe < this.ItensNaLista) {
this.sistemasService.updateDetalhe(Detalhe).then(resUpdate => {
if (this.ListaDetalhes[this.IndexDetalhe].Arquivo != null) {
this.CopiarImagem(this.IndexDetalhe);
}
this.IndexDetalhe++;
this.SalvarProximo();
});
return;
}
this.sistemasService.insertDetalhe(Detalhe).then(resInsert => {
this.ListaDetalhes[this.IndexDetalhe].idsistemasdetalhes = resInsert['response'].insertId;
if (this.ListaDetalhes[this.IndexDetalhe].Arquivo != null) {
this.CopiarImagem(this.IndexDetalhe);
}
this.IndexDetalhe++;
this.SalvarProximo();
});
return;
}
this.sistemasService.atualizarListaDetalhes(this.Sistema.idsistemas).then(resAtualizacao => {
console.log("Atualizacao do indice de detalhes:", resAtualizacao);
alert("Detalhes cadastrados com sucesso!");
this.IndexDetalhe = 0;
this.Salvando = false;
this.ngOnInit();
});
}
CopiarImagem(index: number) {
const req = new XMLHttpRequest();
const formData = new FormData();
formData.append("nome", "detalhe_" + this.ListaDetalhes[index].ordem + ".jpg");
formData.append("arquivo", this.ListaDetalhes[index].Arquivo);
formData.append("idsistemas", this.ListaDetalhes[index].idsistemas.toString());
req.open("POST", environment.ApiUrl + "/sistemas/copyDetalhe");
req.send(formData);
req.onreadystatechange = function () {
if (req.readyState === 4) {
console.log("Imagem copiada com sucesso");
}
};
}
}

View File

@ -0,0 +1,195 @@
<div class="key-page">
<!-- HERO DO SISTEMA -->
<section class="key-hero">
<div class="key-hero-content">
<div class="system-logo-box" (click)="NavegarSistema()">
<img [src]="LinkImagem" alt="Sistema Imagem" class="system-logo">
</div>
<div class="system-title">
<span class="eyebrow">Licenças e chaves</span>
<h1>{{ Sistema.nome }}</h1>
<p>
Gerencie suas chaves de acesso, compre licenças e acompanhe a validade deste sistema.
</p>
<div class="system-tags">
<span>Versão {{ Sistema.versaoatual }}</span>
<span>Lançamento {{ Sistema.insert_date | date:'dd/MM/yyyy' }}</span>
</div>
</div>
</div>
</section>
<!-- RESUMO -->
<section class="summary-grid">
<div class="summary-card">
<div class="summary-icon">
<i class="material-icons">vpn_key</i>
</div>
<div>
<span>Chaves geradas</span>
<strong>{{ ListaDeChaves?.length || 0 }}</strong>
</div>
</div>
<div class="summary-card">
<div class="summary-icon">
<i class="material-icons">shopping_cart</i>
</div>
<div>
<span>Opções de licença</span>
<strong>{{ Produtos?.length || 0 }}</strong>
</div>
</div>
<div class="summary-card">
<div class="summary-icon">
<i class="material-icons">verified_user</i>
</div>
<div>
<span>Status</span>
<strong>Disponível</strong>
</div>
</div>
</section>
<!-- LICENÇAS -->
<section class="panel-card license-section">
<div class="panel-header">
<div>
<span class="eyebrow">Comprar licença</span>
<h2>Opções disponíveis</h2>
</div>
</div>
<div *ngIf="Produtos != null && Produtos.length > 0" class="license-grid">
<article *ngFor="let Produto of Produtos" class="license-card">
<div class="license-card-header">
<div class="license-icon">
<i class="material-icons">workspace_premium</i>
</div>
<span>Licença</span>
</div>
<h3>{{ Produto.descricao }}</h3>
<div class="license-price">
<small>R$</small>
<strong>{{ Produto.valor | number:'1.2-2' }}</strong>
</div>
<p>
Libere uma chave de acesso para utilização do sistema conforme o tipo de licença selecionado.
</p>
<button type="button" class="btn-license" (click)="comprarLicenca(Produto)">
Comprar licença
</button>
</article>
</div>
<div *ngIf="Produtos != null && Produtos.length == 0" class="empty-state small">
<div class="empty-icon">
<i class="material-icons">info</i>
</div>
<h3>Nenhuma licença disponível</h3>
<p>
Este sistema não possui versões disponíveis para compra de licenças no momento.
</p>
</div>
</section>
<!-- CHAVE DE AVALIAÇÃO -->
<section *ngIf="ListaDeChaves != null && ListaDeChaves.length == 0" class="trial-section">
<div class="trial-content">
<div>
<span class="eyebrow">Avaliação</span>
<h2>Gerar chave de avaliação</h2>
<p>
Você ainda não possui chaves para este sistema. Gere uma chave de avaliação para testar o acesso.
</p>
</div>
<button type="button" class="btn-trial" (click)="btnGerarClick()">
<i class="material-icons">add_box</i>
Gerar chave de avaliação
</button>
</div>
</section>
<!-- CHAVES -->
<section class="panel-card keys-section">
<div class="panel-header">
<div>
<span class="eyebrow">Acessos</span>
<h2>Chaves do sistema</h2>
</div>
</div>
<div *ngIf="ListaDeChaves != null && ListaDeChaves.length > 0" class="keys-list">
<article
class="key-card"
*ngFor="let Chave of ListaDeChaves"
[ngClass]="getKeyCardClass(Chave)">
<div class="key-status-ribbon">
<span>{{ getKeyStatusLabel(Chave) }}</span>
</div>
<div class="key-main">
<span class="key-label">Chave de acesso</span>
<div class="key-value" title="Clique para copiar" (click)="copyToClipboard(Chave.Chave)">
<input type="text" readonly [value]="Chave.Chave">
<i class="material-icons">content_copy</i>
</div>
</div>
<div class="key-meta">
<div class="meta-item">
<span>Criação</span>
<strong>{{ Chave.DataCriacao | date:'dd/MM/yyyy' }}</strong>
</div>
<div class="meta-item">
<span>Validade</span>
<strong>{{ Chave.DataValidade | date:'dd/MM/yyyy' }}</strong>
</div>
<div class="meta-item">
<span>Status</span>
<strong>{{ Chave.Status }}</strong>
</div>
<div class="meta-item">
<span>Ativada</span>
<strong>{{ Chave.Ativada }}</strong>
</div>
</div>
</article>
</div>
<div *ngIf="ListaDeChaves != null && ListaDeChaves.length == 0" class="empty-state">
<div class="empty-icon">
<i class="material-icons">vpn_key_off</i>
</div>
<h3>Nenhuma chave gerada</h3>
<p>
Gere uma chave de avaliação ou compre uma licença para liberar o acesso ao sistema.
</p>
</div>
</section>
</div>

View File

@ -0,0 +1,672 @@
.key-page {
min-height: calc(100vh - 74px);
color: #0f172a;
}
/* HERO */
.key-hero {
position: relative;
overflow: hidden;
margin-bottom: 22px;
padding: 32px;
border-radius: 24px;
background:
radial-gradient(circle at top right, rgba(255, 255, 255, 0.14), transparent 34%),
linear-gradient(145deg, #071b63, #12358f);
color: #ffffff;
box-shadow: 0 22px 55px rgba(15, 23, 42, 0.16);
}
.key-hero::before {
content: "";
position: absolute;
width: 320px;
height: 320px;
right: -120px;
top: -120px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.11);
}
.key-hero-content {
position: relative;
z-index: 1;
display: flex;
align-items: center;
gap: 24px;
}
.system-logo-box {
width: 96px;
height: 96px;
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
border-radius: 24px;
background: #ffffff;
cursor: pointer;
box-shadow: 0 18px 42px rgba(0, 0, 0, 0.18);
}
.system-logo {
width: 62px;
height: 62px;
object-fit: contain;
}
.system-title {
min-width: 0;
}
.eyebrow {
display: inline-block;
margin-bottom: 10px;
color: #071b63;
font-size: 12px;
font-weight: 900;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.key-hero .eyebrow,
.trial-section .eyebrow {
color: #bfdbfe;
}
.system-title h1 {
margin: 0 0 12px 0;
color: #ffffff;
font-size: 34px;
line-height: 42px;
font-weight: 900;
letter-spacing: -0.04em;
}
.system-title p {
max-width: 680px;
margin: 0;
color: #dbeafe;
font-size: 15px;
line-height: 25px;
}
.system-tags {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 18px;
}
.system-tags span {
display: inline-flex;
align-items: center;
min-height: 28px;
padding: 0 12px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.12);
color: #ffffff;
font-size: 12px;
font-weight: 800;
}
/* SUMMARY */
.summary-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px;
margin-bottom: 22px;
}
.summary-card {
display: flex;
align-items: center;
gap: 16px;
padding: 20px;
border-radius: 20px;
background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.22);
box-shadow: 0 16px 36px rgba(15, 23, 42, 0.08);
}
.summary-icon {
width: 48px;
height: 48px;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
background: #f1f5ff;
color: #071b63;
}
.summary-icon .material-icons {
font-size: 25px;
}
.summary-card span {
display: block;
margin-bottom: 4px;
color: #64748b;
font-size: 12px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.summary-card strong {
display: block;
color: #0f172a;
font-size: 22px;
line-height: 28px;
font-weight: 900;
}
/* PANELS */
.panel-card {
margin-bottom: 22px;
padding: 28px;
border-radius: 24px;
background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.22);
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
}
.panel-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24px;
}
.panel-header h2 {
margin: 0;
color: #0f172a;
font-size: 26px;
line-height: 34px;
font-weight: 900;
letter-spacing: -0.035em;
}
/* LICENSES */
.license-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 18px;
}
.license-card {
padding: 22px;
border-radius: 22px;
background: #f8fafc;
border: 1px solid #e5e7eb;
transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.license-card:hover {
transform: translateY(-3px);
border-color: rgba(7, 27, 99, 0.22);
box-shadow: 0 18px 42px rgba(15, 23, 42, 0.12);
}
.license-card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 18px;
}
.license-icon {
width: 46px;
height: 46px;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
background: #f1f5ff;
color: #071b63;
}
.license-card-header span {
color: #64748b;
font-size: 12px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.license-card h3 {
margin: 0 0 16px 0;
color: #0f172a;
font-size: 20px;
line-height: 27px;
font-weight: 900;
}
.license-price {
display: flex;
align-items: flex-start;
gap: 6px;
margin-bottom: 14px;
}
.license-price small {
margin-top: 6px;
color: #071b63;
font-size: 14px;
font-weight: 900;
}
.license-price strong {
color: #071b63;
font-size: 34px;
line-height: 38px;
font-weight: 900;
letter-spacing: -0.04em;
}
.license-card p {
min-height: 68px;
margin: 0 0 18px 0;
color: #64748b;
font-size: 14px;
line-height: 23px;
}
.btn-license {
width: 100%;
min-height: 44px;
border: none;
border-radius: 12px;
background: linear-gradient(135deg, #071b63, #12358f);
color: #ffffff;
font-size: 13px;
font-weight: 900;
cursor: pointer;
box-shadow: 0 12px 24px rgba(7, 27, 99, 0.20);
transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.btn-license:hover {
transform: translateY(-1px);
box-shadow: 0 16px 30px rgba(7, 27, 99, 0.26);
}
/* TRIAL */
.trial-section {
margin-bottom: 22px;
padding: 28px;
border-radius: 24px;
background:
radial-gradient(circle at top right, rgba(255, 255, 255, 0.12), transparent 34%),
linear-gradient(145deg, #15803d, #166534);
color: #ffffff;
box-shadow: 0 18px 45px rgba(21, 128, 61, 0.16);
}
.trial-content {
display: grid;
grid-template-columns: 1fr auto;
align-items: center;
gap: 22px;
}
.trial-content h2 {
margin: 0 0 8px 0;
color: #ffffff;
font-size: 26px;
line-height: 34px;
font-weight: 900;
}
.trial-content p {
max-width: 680px;
margin: 0;
color: #dcfce7;
font-size: 14px;
line-height: 23px;
}
.btn-trial {
min-height: 46px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 0 18px;
border: none;
border-radius: 12px;
background: #ffffff;
color: #166534;
font-size: 13px;
font-weight: 900;
cursor: pointer;
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.16);
transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.btn-trial:hover {
transform: translateY(-1px);
}
.btn-trial .material-icons {
font-size: 20px;
}
/* KEYS */
.keys-list {
display: grid;
gap: 16px;
}
.key-card {
padding: 20px;
border-radius: 22px;
background: #f8fafc;
border: 1px solid #e5e7eb;
}
.key-main {
margin-bottom: 16px;
}
.key-label {
display: block;
margin-bottom: 8px;
color: #64748b;
font-size: 12px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.key-value {
display: grid;
grid-template-columns: 1fr auto;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: 14px;
background: #ffffff;
border: 1px solid #dbe6ff;
cursor: pointer;
transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
.key-value:hover {
border-color: rgba(7, 27, 99, 0.28);
box-shadow: 0 0 0 4px rgba(7, 27, 99, 0.08);
}
.key-value input {
width: 100%;
min-width: 0;
border: none;
outline: none;
background: transparent;
color: #0f172a;
font-size: 15px;
font-weight: 800;
cursor: pointer;
}
.key-value .material-icons {
color: #071b63;
font-size: 22px;
}
.key-meta {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
}
.key-card {
position: relative;
overflow: hidden;
padding: 20px;
border-radius: 22px;
background: #f8fafc;
border: 1px solid #e5e7eb;
transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.key-card::before {
content: "";
position: absolute;
left: 0;
top: 0;
width: 7px;
height: 100%;
background: #94a3b8;
}
.key-card:hover {
transform: translateY(-2px);
box-shadow: 0 18px 42px rgba(15, 23, 42, 0.12);
}
.key-status-ribbon {
display: flex;
justify-content: flex-end;
margin-bottom: 14px;
}
.key-status-ribbon span {
display: inline-flex;
align-items: center;
min-height: 28px;
padding: 0 12px;
border-radius: 999px;
font-size: 12px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 0.06em;
}
/* Verde: válida e ativada */
.key-card-active {
background: linear-gradient(90deg, rgba(220, 252, 231, 0.75), #f8fafc 34%);
border-color: rgba(22, 163, 74, 0.28);
}
.key-card-active::before {
background: #16a34a;
}
.key-card-active .key-status-ribbon span {
background: #dcfce7;
color: #166534;
}
/* Amarelo: válida, mas não ativada */
.key-card-pending {
background: linear-gradient(90deg, rgba(254, 249, 195, 0.82), #f8fafc 34%);
border-color: rgba(202, 138, 4, 0.28);
}
.key-card-pending::before {
background: #ca8a04;
}
.key-card-pending .key-status-ribbon span {
background: #fef9c3;
color: #854d0e;
}
/* Vermelho: expirada/vencida/inválida */
.key-card-expired {
background: linear-gradient(90deg, rgba(254, 226, 226, 0.82), #f8fafc 34%);
border-color: rgba(220, 38, 38, 0.28);
}
.key-card-expired::before {
background: #dc2626;
}
.key-card-expired .key-status-ribbon span {
background: #fee2e2;
color: #991b1b;
}
/* Neutra */
.key-card-neutral {
background: linear-gradient(90deg, rgba(226, 232, 240, 0.72), #f8fafc 34%);
border-color: rgba(100, 116, 139, 0.22);
}
.key-card-neutral::before {
background: #64748b;
}
.key-card-neutral .key-status-ribbon span {
background: #e2e8f0;
color: #334155;
}
.meta-item {
padding: 14px;
border-radius: 16px;
background: #ffffff;
border: 1px solid #e5e7eb;
}
.meta-item span {
display: block;
margin-bottom: 5px;
color: #64748b;
font-size: 11px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.meta-item strong {
display: block;
color: #0f172a;
font-size: 14px;
line-height: 21px;
font-weight: 900;
}
/* EMPTY */
.empty-state {
padding: 42px 20px;
text-align: center;
border-radius: 22px;
background: #f8fafc;
border: 1px dashed #cbd5e1;
}
.empty-state.small {
padding: 30px 20px;
}
.empty-icon {
width: 66px;
height: 66px;
margin: 0 auto 18px auto;
border-radius: 999px;
display: flex;
align-items: center;
justify-content: center;
background: #f1f5ff;
color: #071b63;
}
.empty-icon .material-icons {
font-size: 32px;
}
.empty-state h3 {
margin: 0 0 10px 0;
color: #0f172a;
font-size: 22px;
font-weight: 900;
}
.empty-state p {
max-width: 520px;
margin: 0 auto;
color: #64748b;
font-size: 14px;
line-height: 23px;
}
/* RESPONSIVE */
@media (max-width: 1040px) {
.license-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.key-meta {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 720px) {
.key-hero-content {
flex-direction: column;
text-align: center;
}
.system-tags {
justify-content: center;
}
.summary-grid,
.license-grid {
grid-template-columns: 1fr;
}
.trial-content {
grid-template-columns: 1fr;
}
.btn-trial {
width: 100%;
}
.panel-card,
.trial-section,
.key-hero {
padding: 22px;
}
.panel-header h2,
.trial-content h2 {
font-size: 22px;
line-height: 30px;
}
.system-title h1 {
font-size: 28px;
line-height: 36px;
}
}
@media (max-width: 480px) {
.key-meta {
grid-template-columns: 1fr;
}
.key-value {
grid-template-columns: 1fr;
}
.key-value .material-icons {
justify-self: end;
}
.license-price strong {
font-size: 30px;
}
}

View File

@ -0,0 +1,320 @@
import { Component, OnInit } from '@angular/core';
import { SistemasService } from '../services/sistemas.service';
import { Chaves, Sistemas, ListChaves } from '../models/sistemasModel';
import { ActivatedRoute, Router } from '@angular/router';
import { environment } from 'src/environments/environment';
import { UsuariosService } from '../services/usuarios.service';
import { Usuarios } from '../models/usuariosModel';
import { ProdutosService } from '../services/produtosService';
import { ProdutosModel } from '../models/produtosModel';
@Component({
selector: 'app-chaves',
templateUrl: './chaves.component.html',
styleUrls: ['./chaves.component.scss', './../app.component.scss']
})
export class ChavesComponent implements OnInit {
FormChaves: Chaves = new Chaves();
Sistema: Sistemas = new Sistemas();
ListaDeChaves: ListChaves[];
Usuario: Usuarios = new Usuarios();
UserID: number = parseInt(localStorage.getItem("UserID"));
idsistemas: number = this.route.snapshot.params['idsistemas'];
LinkImagem: string;
Produtos: ProdutosModel[] = [];
constructor(
private sistemasService: SistemasService,
private route: ActivatedRoute,
private usuariosService: UsuariosService,
private router: Router,
private produtosService: ProdutosService
) { }
ngOnInit() {
this.sistemasService.getSistemaByID(this.idsistemas).then(resSistema => {
this.Sistema = resSistema;
this.LinkImagem = environment.LinkProjeto + "/sistemas/" + this.idsistemas + "/icone.png";
});
this.usuariosService.getUsuarioByID(this.UserID).then(resUsuario => {
this.Usuario = resUsuario;
});
this.produtosService.getProdutosSell(this.idsistemas).then(resProdutos => {
this.Produtos = resProdutos;
});
this.AtualizaChaves();
}
AtualizaChaves() {
this.sistemasService.getChavesSistemaLista(this.idsistemas, this.UserID).then(resChave => {
this.ListaDeChaves = resChave;
})
}
btnGerarClick() {
let Mensagem: string;
if (this.ListaDeChaves.length == 0) {
Mensagem = "A primeira chave do sistema é gerada gratuitamente com validade de 30 dias a partir de sua data de geração! Deseja gerar sua chave de avaliação agora?";
}
else {
//this.Pagamento = true;
//this.GerarChave(30);
if (confirm("Deseja solicitar uma nova chave de sistema?")) {
this.produtosService.buySistema(this.idsistemas, this.UserID, 1).then(res => {
//console.log(res);
alert("Você será redirecionado para a tela de pagamentos do Mercado Pago.\rQuando o pagamento for confirmado, a licença será gerada automaticamente e já aparecerá na sua lista de chaves!");
const paymentUrl = res['url'];
window.open(paymentUrl, '_blank');
});
}
return;
}
if (confirm(Mensagem)) {
this.GerarChave(30);
}
else {
return;
}
//this.GerarChave(30);
}
DataValidade(Dias: number) {
let DataAtual = new Date();
DataAtual.setDate(DataAtual.getDate() + Dias);
let DataFix =
DataAtual.toLocaleDateString()[0] +
DataAtual.toLocaleDateString()[1] +
DataAtual.toLocaleDateString()[3] +
DataAtual.toLocaleDateString()[4] +
DataAtual.toLocaleDateString()[6] +
DataAtual.toLocaleDateString()[7] +
DataAtual.toLocaleDateString()[8] +
DataAtual.toLocaleDateString()[9];
return DataFix;
}
GerarChave(Dias: number) {
let SerialKey: string;
let CPF = this.CryptText(this.Usuario.cpf);
let Validade = this.CryptText(this.DataValidade(Dias));
SerialKey =
Validade[0].toString() +
CPF[13].toString() +
CPF[0].toString() +
Validade[1].toString() +
CPF[12].toString() +
CPF[1].toString() +
Validade[2].toString() +
CPF[11].toString() +
CPF[2].toString() +
Validade[3].toString() +
CPF[10].toString() +
CPF[3].toString() +
Validade[4].toString() +
CPF[9].toString() +
CPF[4].toString() +
Validade[5].toString() +
CPF[8].toString() +
CPF[5].toString() +
Validade[6].toString() +
CPF[7].toString() +
CPF[6].toString() +
Validade[7].toString();
let DatValidade = new Date();
DatValidade.setDate(DatValidade.getDate() + Dias);
this.FormChaves.licenca = SerialKey;
this.FormChaves.idusuarios = this.UserID;
this.FormChaves.idsistemas = this.idsistemas;
this.FormChaves.data_validade = DatValidade;
this.sistemasService.insertChave(this.FormChaves).then(resInsert => {
if (resInsert['status'] == 200) {
alert("Chave de licença gerada com sucesso!");
this.AtualizaChaves();
}
else {
alert("Erro ao gerar chave! Por favor tente novamente mais tarde!");
}
})
}
CryptText(Texto: string)
{
let RandNumber: number;
let NewChar: string;
let FinalString: string = "";
let SwitchChar: string = "";
for (let i = 0; i <= Texto.length; i++)
{
RandNumber = Math.floor(Math.random() * 2);
SwitchChar = Texto.substring(i, 1)[Texto.substring(i, 1).length - 1];
if (SwitchChar != undefined) {
switch (SwitchChar)
{
case "0":
if (RandNumber == 0)
NewChar = "q";
else
NewChar = "3";
break;
case "1":
if (RandNumber == 0)
NewChar = "w";
else
NewChar = "5";
break;
case "2":
if (RandNumber == 0)
NewChar = "r";
else
NewChar = "4";
break;
case "3":
if (RandNumber == 0)
NewChar = "D";
else
NewChar = "6";
break;
case "4":
if (RandNumber == 0)
NewChar = "H";
else
NewChar = "9";
break;
case "5":
if (RandNumber == 0)
NewChar = "b";
else
NewChar = "7";
break;
case "6":
if (RandNumber == 0)
NewChar = "U";
else
NewChar = "8";
break;
case "7":
if (RandNumber == 0)
NewChar = "k";
else
NewChar = "1";
break;
case "8":
if (RandNumber == 0)
NewChar = "I";
else
NewChar = "0";
break;
case "9":
if (RandNumber == 0)
NewChar = "p";
else
NewChar = "2";
break;
default:
NewChar = "-";
break;
}
FinalString += NewChar;
}
}
return FinalString;
}
comprarLicenca(Produto: ProdutosModel) {
if (confirm("Deseja solicitar uma nova chave de sistema na versão " + Produto.descricao + "?")) {
this.produtosService.buySistema(Produto.idprodutos, this.UserID, 1).then(res => {
//console.log(res);
if (res['chave'] != null) {
alert("Chave gerada com sucesso!");
this.AtualizaChaves();
}
else {
alert("Você será redirecionado para a tela de pagamentos do Mercado Pago.\rQuando o pagamento for confirmado, a licença será gerada automaticamente e já aparecerá na sua lista de chaves!");
const paymentUrl = res['url'];
window.open(paymentUrl, '_blank');
}
});
}
}
copyToClipboard(chave: string) {
navigator.clipboard.writeText(chave).then(() => {
alert("Chave copiada para a área de transferência!");
});
}
NavegarSistema() {
const url = this.router.serializeUrl(
this.router.createUrlTree(['/detalhes/' + this.idsistemas])
);
window.open(url, '_blank');
}
getKeyCardClass(Chave: any): string {
const hoje = new Date();
hoje.setHours(0, 0, 0, 0);
const validade = new Date(Chave.DataValidade);
validade.setHours(0, 0, 0, 0);
const status = (Chave.Status || '').toString().toLowerCase();
const ativada = (Chave.Ativada || '').toString().toLowerCase();
const expirou = validade < hoje;
const estaAtivada =
ativada === 'sim' ||
ativada === 'true' ||
ativada === '1' ||
ativada === 'ativada';
const estaValida =
!expirou &&
(
status.includes('válida') ||
status.includes('valid') ||
status.includes('ativa') ||
status.includes('vigente') ||
status === 'ok'
);
//console.log("status: " + status + ", ativada: " + ativada + ", expirou: " + expirou + ", estaAtivada: " + estaAtivada + ", estaValida: " + estaValida)
if (expirou || status.includes('expir') || status.includes('venc')) {
return 'key-card-expired';
}
if (estaValida && estaAtivada) {
return 'key-card-active';
}
if (estaValida && !estaAtivada) {
return 'key-card-pending';
}
return 'key-card-neutral';
}
getKeyStatusLabel(Chave: any): string {
const cardClass = this.getKeyCardClass(Chave);
if (cardClass === 'key-card-expired') {
return 'Expirada';
}
if (cardClass === 'key-card-active') {
return 'Válida e ativada';
}
if (cardClass === 'key-card-pending') {
return 'Válida, aguardando ativação';
}
return 'Status indefinido';
}
}

View File

@ -0,0 +1,157 @@
<div class="client-page">
<!-- HERO -->
<section class="client-hero">
<div class="client-hero-text">
<span class="eyebrow">Área do cliente</span>
<h1>Bem-vindo, {{ NomeUsuario }}!</h1>
<p>
Gerencie seus sistemas, acompanhe suas licenças e gere chaves de acesso em um só lugar.
</p>
</div>
<div class="client-hero-card">
<span>Sistemas ativos</span>
<strong>{{ SistemasDoUsuario?.length || 0 }}</strong>
<small>Disponíveis na sua conta</small>
</div>
</section>
<!-- SUMMARY -->
<section class="summary-grid">
<div class="summary-card">
<div class="summary-icon">
<i class="material-icons">apps</i>
</div>
<div>
<span>Total de sistemas</span>
<strong>{{ SistemasDoUsuario?.length || 0 }}</strong>
</div>
</div>
<div class="summary-card">
<div class="summary-icon">
<i class="material-icons">vpn_key</i>
</div>
<div>
<span>Chaves válidas</span>
<strong>{{ TotalChavesValidas }}</strong>
</div>
</div>
<div class="summary-card">
<div class="summary-icon">
<i class="material-icons">verified_user</i>
</div>
<div>
<span>Status</span>
<strong>Ativo</strong>
</div>
</div>
</section>
<!-- SISTEMAS -->
<section class="systems-panel" *ngIf="JanelaModo.Visualizar">
<div class="panel-header">
<div>
<span class="eyebrow">Meus sistemas</span>
<h2>Soluções vinculadas à sua conta</h2>
</div>
</div>
<div *ngIf="SistemasDoUsuario && SistemasDoUsuario.length > 0" class="client-systems-grid">
<article class="client-system-card" *ngFor="let Sistema of SistemasDoUsuario">
<div class="system-card-top">
<div class="system-logo-box" (click)="NavegarSistema(Sistema.idsistemas)">
<img
[src]="LinkImagem(Sistema.idsistemas)"
alt="Ícone do sistema"
class="system-logo">
</div>
<div class="system-title-block">
<h3>{{ Sistema.nome }}</h3>
<span class="system-status">
{{ Sistema.gratis ? 'Gratuito' : 'Licenciado' }}
</span>
</div>
</div>
<p class="system-description">
{{ Sistema.descricao }}
</p>
<div class="system-meta-grid">
<div class="meta-item">
<span>Data download</span>
<strong>{{ Sistema.data_download | date:'dd/MM/yyyy' }}</strong>
</div>
<div class="meta-item">
<span>Chaves válidas</span>
<strong>{{ Sistema.chavesValidas }}</strong>
</div>
</div>
<div class="system-actions">
<button
*ngIf="!Sistema.gratis"
type="button"
class="btn-card btn-key"
(click)="GerarChave(Sistema)">
<i class="material-icons">add</i>
Gerar chave
</button>
<button
type="button"
class="btn-card btn-details"
(click)="NavegarSistema(Sistema.idsistemas)">
<i class="material-icons">open_in_new</i>
Detalhes
</button>
<button
type="button"
class="btn-card btn-remove"
(click)="RemoverSistema(Sistema)">
<i class="material-icons">delete</i>
Remover
</button>
</div>
</article>
</div>
<div *ngIf="!SistemasDoUsuario || SistemasDoUsuario.length == 0" class="empty-state">
<div class="empty-icon">
<i class="material-icons">inventory_2</i>
</div>
<h3>Nenhum sistema vinculado ainda</h3>
<p>
Você ainda não possui sistemas na sua conta. Acesse a página inicial para conhecer as soluções disponíveis.
</p>
<button type="button" class="btn-empty" (click)="router.navigate(['/home'])">
Conhecer sistemas
</button>
</div>
</section>
</div>

View File

@ -0,0 +1,452 @@
.client-page {
min-height: calc(100vh - 74px);
color: #0f172a;
}
/* HERO */
.client-hero {
position: relative;
overflow: hidden;
display: grid;
grid-template-columns: 1fr 230px;
align-items: center;
gap: 24px;
margin-bottom: 22px;
padding: 32px;
border-radius: 24px;
background:
radial-gradient(circle at top right, rgba(255, 255, 255, 0.14), transparent 34%),
linear-gradient(145deg, #071b63, #12358f);
color: #ffffff;
box-shadow: 0 22px 55px rgba(15, 23, 42, 0.16);
}
.client-hero::before {
content: "";
position: absolute;
width: 320px;
height: 320px;
right: -120px;
top: -120px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.11);
}
.client-hero-text {
position: relative;
z-index: 1;
}
.eyebrow {
display: inline-block;
margin-bottom: 10px;
color: #071b63;
font-size: 12px;
font-weight: 900;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.client-hero .eyebrow {
color: #bfdbfe;
}
.client-hero h1 {
margin: 0 0 12px 0;
color: #ffffff;
font-size: 34px;
line-height: 42px;
font-weight: 900;
letter-spacing: -0.04em;
}
.client-hero p {
max-width: 660px;
margin: 0;
color: #dbeafe;
font-size: 15px;
line-height: 25px;
}
.client-hero-card {
position: relative;
z-index: 1;
padding: 22px;
border-radius: 22px;
background: rgba(255, 255, 255, 0.12);
border: 1px solid rgba(255, 255, 255, 0.18);
backdrop-filter: blur(10px);
}
.client-hero-card span,
.client-hero-card small {
display: block;
color: #bfdbfe;
font-size: 12px;
font-weight: 800;
}
.client-hero-card strong {
display: block;
margin: 8px 0;
color: #ffffff;
font-size: 42px;
line-height: 46px;
font-weight: 900;
}
/* SUMMARY */
.summary-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px;
margin-bottom: 22px;
}
.summary-card {
display: flex;
align-items: center;
gap: 16px;
padding: 20px;
border-radius: 20px;
background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.22);
box-shadow: 0 16px 36px rgba(15, 23, 42, 0.08);
}
.summary-icon {
width: 48px;
height: 48px;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
background: #f1f5ff;
color: #071b63;
}
.summary-icon .material-icons {
font-size: 25px;
}
.summary-card span {
display: block;
margin-bottom: 4px;
color: #64748b;
font-size: 12px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.summary-card strong {
display: block;
color: #0f172a;
font-size: 22px;
line-height: 28px;
font-weight: 900;
}
/* PANEL */
.systems-panel {
padding: 28px;
border-radius: 24px;
background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.22);
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
}
.panel-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24px;
}
.panel-header h2 {
margin: 0;
color: #0f172a;
font-size: 26px;
line-height: 34px;
font-weight: 900;
letter-spacing: -0.035em;
}
/* SYSTEM CARDS */
.client-systems-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 18px;
}
.client-system-card {
display: flex;
flex-direction: column;
min-height: 280px;
padding: 22px;
border-radius: 22px;
background: #f8fafc;
border: 1px solid #e5e7eb;
transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.client-system-card:hover {
transform: translateY(-3px);
border-color: rgba(7, 27, 99, 0.22);
box-shadow: 0 18px 42px rgba(15, 23, 42, 0.12);
}
.system-card-top {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 18px;
}
.system-logo-box {
width: 70px;
height: 70px;
flex: 0 0 auto;
border-radius: 18px;
display: flex;
align-items: center;
justify-content: center;
background: #ffffff;
border: 1px solid #dbe6ff;
cursor: pointer;
}
.system-logo {
width: 46px;
height: 46px;
object-fit: contain;
}
.system-title-block {
min-width: 0;
}
.system-title-block h3 {
margin: 0 0 8px 0;
color: #0f172a;
font-size: 20px;
line-height: 27px;
font-weight: 900;
letter-spacing: -0.025em;
}
.system-status {
display: inline-flex;
align-items: center;
min-height: 24px;
padding: 0 10px;
border-radius: 999px;
background: #e0f2fe;
color: #075985;
font-size: 12px;
font-weight: 900;
}
.system-description {
flex: 1;
margin: 0 0 18px 0;
color: #64748b;
font-size: 14px;
line-height: 23px;
}
.system-meta-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-bottom: 18px;
}
.meta-item {
padding: 14px;
border-radius: 16px;
background: #ffffff;
border: 1px solid #e5e7eb;
}
.meta-item span {
display: block;
margin-bottom: 5px;
color: #64748b;
font-size: 11px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.meta-item strong {
display: block;
color: #0f172a;
font-size: 15px;
line-height: 22px;
font-weight: 900;
}
.system-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.btn-card {
min-height: 40px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 7px;
padding: 0 14px;
border: none;
border-radius: 12px;
color: #ffffff;
font-size: 12px;
font-weight: 900;
cursor: pointer;
transition: transform 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease;
}
.btn-card .material-icons {
font-size: 18px;
}
.btn-card:hover {
transform: translateY(-1px);
}
.btn-key {
background: #15803d;
box-shadow: 0 10px 20px rgba(21, 128, 61, 0.20);
}
.btn-details {
background: #071b63;
box-shadow: 0 10px 20px rgba(7, 27, 99, 0.20);
}
.btn-remove {
background: #dc2626;
box-shadow: 0 10px 20px rgba(220, 38, 38, 0.18);
}
/* EMPTY */
.empty-state {
padding: 42px 20px;
text-align: center;
border-radius: 22px;
background: #f8fafc;
border: 1px dashed #cbd5e1;
}
.empty-icon {
width: 72px;
height: 72px;
margin: 0 auto 18px auto;
border-radius: 999px;
display: flex;
align-items: center;
justify-content: center;
background: #f1f5ff;
color: #071b63;
}
.empty-icon .material-icons {
font-size: 34px;
}
.empty-state h3 {
margin: 0 0 10px 0;
color: #0f172a;
font-size: 22px;
font-weight: 900;
}
.empty-state p {
max-width: 520px;
margin: 0 auto 22px auto;
color: #64748b;
font-size: 14px;
line-height: 23px;
}
.btn-empty {
min-height: 44px;
padding: 0 18px;
border: none;
border-radius: 12px;
background: #071b63;
color: #ffffff;
font-size: 13px;
font-weight: 900;
cursor: pointer;
}
/* RESPONSIVE */
@media (max-width: 980px) {
.client-hero {
grid-template-columns: 1fr;
}
.client-hero-card {
max-width: 260px;
}
.client-systems-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 720px) {
.summary-grid {
grid-template-columns: 1fr;
}
.systems-panel {
padding: 20px;
}
.panel-header h2 {
font-size: 22px;
line-height: 30px;
}
.system-meta-grid {
grid-template-columns: 1fr;
}
.system-actions {
flex-direction: column;
}
.btn-card {
width: 100%;
}
}
@media (max-width: 480px) {
.client-hero {
padding: 24px;
}
.client-hero h1 {
font-size: 28px;
line-height: 36px;
}
.client-system-card {
padding: 18px;
}
.system-card-top {
align-items: flex-start;
}
}

View File

@ -0,0 +1,70 @@
import { Component, OnInit } from '@angular/core';
import { UsuariosService } from '../services/usuarios.service';
import { SistemasDoUsuario } from '../models/usuariosModel';
import { Modos } from '../models/variaveisModel';
import { Router } from '@angular/router';
import { SistemasService } from '../services/sistemas.service';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-client',
templateUrl: './client.component.html',
styleUrls: ['./client.component.scss', './../app.component.scss']
})
export class ClientComponent implements OnInit {
NomeUsuario: string = localStorage.getItem("UserName") || "Usuário";
idusuarios: number = parseInt(localStorage.getItem("UserID") || "0");
SistemasDoUsuario: SistemasDoUsuario[] = [];
JanelaModo: Modos = new Modos();
constructor(
private usuariosService: UsuariosService,
private sistemasService: SistemasService,
public router: Router
) { }
ngOnInit() {
this.JanelaModo.Visualizar = true;
this.CarregarSistemas();
}
get TotalChavesValidas(): number {
if (!this.SistemasDoUsuario) {
return 0;
}
return this.SistemasDoUsuario.reduce((total, sistema) => {
return total + Number(sistema["chavesValidas"] || 0);
}, 0);
}
CarregarSistemas() {
this.usuariosService.getSistemasDoUsuario(this.idusuarios).then(resSistemas => {
this.SistemasDoUsuario = resSistemas || [];
});
}
GerarChave(Sistema: SistemasDoUsuario) {
this.router.navigate(['dashboard/cliente/chave/' + Sistema.idsistemas]);
}
RemoverSistema(Sistema: SistemasDoUsuario) {
if (confirm("Tem certeza que deseja remover esse sistema de sua lista?")) {
this.sistemasService.deleteSistemaUsuario(Sistema.idsistemas, Sistema.idusuarios).then(resDelete => {
this.SistemasDoUsuario = this.SistemasDoUsuario.filter(item => item.idsistemas !== Sistema.idsistemas);
alert("Sistema removido com sucesso!");
});
}
}
LinkImagem(idsistemas: number): string {
return environment.LinkProjeto + "/sistemas/" + idsistemas + "/icone.png";
}
NavegarSistema(idsistemas: number) {
this.router.navigate(['detalhes/' + idsistemas]);
}
}

View File

@ -0,0 +1,137 @@
<div class="register-page">
<div class="register-shell">
<div class="register-brand-panel">
<div class="brand-content">
<img src="assets/logo_escrito.png" alt="Zendion, INC." class="brand-logo">
<h1>Crie sua conta</h1>
<p>
Acesse os sistemas da Zendion, INC. com segurança e acompanhe suas soluções em um só lugar.
</p>
<div class="brand-badges">
<span>Conta segura</span>
<span>Ativação por e-mail</span>
<span>Acesso rápido</span>
</div>
</div>
</div>
<div class="register-card">
<div class="register-header">
<span class="eyebrow">Cadastro</span>
<h2>{{ TituloJanela }}</h2>
<p *ngIf="!Sucesso">
Preencha seus dados para criar sua conta. Depois enviaremos um link de ativação para seu e-mail.
</p>
</div>
<div class="register-body">
<div *ngIf="!Sucesso" class="register-form">
<div class="form-group">
<label for="nome">Nome completo</label>
<input
id="nome"
type="text"
class="form-control"
placeholder="Digite seu nome"
[(ngModel)]="FormUsuario.nome"
(change)="MensagemForm = ''">
</div>
<div class="form-group">
<label for="email">E-mail</label>
<input
id="email"
type="email"
class="form-control"
placeholder="seuemail@exemplo.com"
[(ngModel)]="FormUsuario.email"
(change)="MensagemForm = ''">
</div>
<div class="form-group">
<label for="cpf">CPF</label>
<input
id="cpf"
type="text"
class="form-control"
placeholder="000.000.000-00"
[(ngModel)]="FormUsuario.cpf"
maxlength="14"
(keyup)="MascaraCPF($event)"
(change)="MensagemForm = ''">
<small class="field-hint" *ngIf="FormUsuario.cpf && !CPFValido">
Digite um CPF válido para liberar o cadastro.
</small>
</div>
<div class="form-group">
<label for="usuario">Usuário</label>
<input
id="usuario"
type="text"
class="form-control"
placeholder="Escolha um nome de usuário"
[(ngModel)]="FormUsuario.usuario"
(change)="MensagemForm = ''">
</div>
<div class="form-group">
<label for="senha">Senha</label>
<input
id="senha"
type="password"
class="form-control"
placeholder="Crie uma senha"
[(ngModel)]="FormUsuario.senha"
(change)="MensagemForm = ''">
</div>
<div *ngIf="MensagemForm != ''" class="register-message">
<small>{{ MensagemForm }}</small>
</div>
<button
[disabled]="!CPFValido"
type="submit"
class="btn-register"
(click)="btnCriarcontaClick()">
{{ TextoBotao }}
</button>
<p class="terms-text">
Ao criar sua conta, você confirma que seus dados estão corretos e aceita receber o e-mail de ativação.
</p>
</div>
<div *ngIf="Sucesso" class="success-box">
<div class="success-icon"></div>
<h3>Cadastro criado com sucesso</h3>
<p>
Enviamos um link de ativação para o seu e-mail. Verifique sua caixa de entrada e também a pasta de spam.
</p>
<button type="button" class="btn-register" (click)="btnCriarcontaClick()">
{{ TextoBotao }}
</button>
</div>
<div class="register-footer">
<a [routerLink]="'/home'">Voltar ao site</a>
<span></span>
<a [routerLink]="'/login'">Já tenho conta</a>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,386 @@
.register-page {
min-height: calc(100vh - 80px);
display: flex;
align-items: center;
justify-content: center;
padding: 48px 18px;
background:
radial-gradient(circle at top left, rgba(7, 27, 99, 0.14), transparent 34%),
linear-gradient(135deg, #f4f6fb 0%, #eef3ff 45%, #ffffff 100%);
}
.register-shell {
width: 100%;
max-width: 1040px;
display: grid;
grid-template-columns: 0.95fr 1.05fr;
background: #ffffff;
border-radius: 24px;
overflow: hidden;
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.16);
border: 1px solid rgba(148, 163, 184, 0.22);
}
.register-brand-panel {
position: relative;
min-height: 620px;
display: flex;
align-items: flex-end;
padding: 42px;
background:
linear-gradient(145deg, rgba(7, 27, 99, 0.96), rgba(18, 47, 132, 0.94)),
url('/assets/logo_escrito.png');
color: #ffffff;
overflow: hidden;
}
.register-brand-panel::before {
content: "";
position: absolute;
width: 340px;
height: 340px;
right: -120px;
top: -100px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.12);
}
.register-brand-panel::after {
content: "";
position: absolute;
width: 220px;
height: 220px;
left: -80px;
bottom: -70px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.08);
}
.brand-content {
position: relative;
z-index: 1;
}
.brand-logo {
height: 54px;
max-width: 240px;
object-fit: contain;
display: block;
margin-bottom: 34px;
filter: brightness(0) invert(1);
}
.brand-content h1 {
margin: 0 0 14px 0;
font-size: 34px;
line-height: 42px;
font-weight: 800;
letter-spacing: -0.03em;
}
.brand-content p {
max-width: 390px;
margin: 0;
color: #dbeafe;
font-size: 16px;
line-height: 27px;
}
.brand-badges {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 28px;
}
.brand-badges span {
display: inline-flex;
align-items: center;
padding: 8px 12px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.13);
color: #ffffff;
font-size: 12px;
font-weight: 700;
backdrop-filter: blur(8px);
}
.register-card {
padding: 44px 48px;
}
.register-header {
margin-bottom: 28px;
}
.eyebrow {
display: inline-block;
margin-bottom: 10px;
color: #071b63;
font-size: 12px;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.register-header h2 {
margin: 0 0 10px 0;
color: #0f172a;
font-size: 30px;
line-height: 38px;
font-weight: 800;
letter-spacing: -0.03em;
}
.register-header p {
margin: 0;
color: #64748b;
font-size: 15px;
line-height: 24px;
}
.register-body {
width: 100%;
}
.register-form {
display: grid;
gap: 16px;
}
.form-group {
text-align: left;
}
.form-group label {
display: block;
margin-bottom: 7px;
color: #1e293b;
font-size: 14px;
font-weight: 700;
}
.form-control {
width: 100%;
height: 46px;
padding: 0 14px;
border-radius: 12px;
border: 1px solid #d7deea;
background: #ffffff;
color: #0f172a;
font-size: 15px;
outline: none;
transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
box-sizing: border-box;
}
.form-control::placeholder {
color: #94a3b8;
}
.form-control:focus {
border-color: #071b63;
box-shadow: 0 0 0 4px rgba(7, 27, 99, 0.10);
background-color: #ffffff;
}
.field-hint {
display: block;
margin-top: 7px;
color: #b45309;
font-size: 12px;
line-height: 18px;
}
.btn-register {
width: 100%;
height: 48px;
margin-top: 6px;
border: none;
border-radius: 12px;
background: linear-gradient(135deg, #071b63, #12358f);
color: #ffffff;
font-size: 15px;
font-weight: 800;
cursor: pointer;
box-shadow: 0 12px 24px rgba(7, 27, 99, 0.22);
transition: transform 0.18s ease, box-shadow 0.18s ease, opacity 0.18s ease;
}
.btn-register:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 16px 30px rgba(7, 27, 99, 0.28);
}
.btn-register:active:not(:disabled) {
transform: translateY(0);
}
.btn-register:disabled {
opacity: 0.55;
cursor: not-allowed;
box-shadow: none;
}
.terms-text {
margin: 0;
color: #64748b;
font-size: 12px;
line-height: 19px;
text-align: center;
}
.register-footer {
display: grid;
grid-template-columns: 1fr auto 1fr;
align-items: center;
gap: 16px;
margin-top: 26px;
padding-top: 22px;
border-top: 1px solid #e5e7eb;
}
.register-footer span {
width: 4px;
height: 4px;
border-radius: 999px;
background: #cbd5e1;
}
.register-footer a {
color: #071b63;
font-size: 14px;
font-weight: 700;
text-decoration: none;
}
.register-footer a:first-child {
text-align: left;
}
.register-footer a:last-child {
text-align: right;
}
.register-footer a:hover {
text-decoration: underline;
}
.register-message {
padding: 12px 14px;
border-radius: 12px;
background: #fef2f2;
border: 1px solid #fecaca;
text-align: left;
}
.register-message small {
display: block;
color: #b91c1c;
font-size: 13px;
line-height: 20px;
font-weight: 600;
}
.success-box {
text-align: center;
padding: 18px 0 4px 0;
}
.success-icon {
width: 64px;
height: 64px;
margin: 0 auto 18px auto;
border-radius: 999px;
display: flex;
align-items: center;
justify-content: center;
background: #dcfce7;
color: #15803d;
font-size: 34px;
font-weight: 900;
}
.success-box h3 {
margin: 0 0 10px 0;
color: #0f172a;
font-size: 24px;
line-height: 32px;
font-weight: 800;
}
.success-box p {
margin: 0 0 24px 0;
color: #64748b;
font-size: 15px;
line-height: 24px;
}
@media (max-width: 860px) {
.register-shell {
grid-template-columns: 1fr;
max-width: 560px;
}
.register-brand-panel {
min-height: auto;
padding: 30px;
align-items: flex-start;
}
.brand-logo {
height: 44px;
margin-bottom: 22px;
}
.brand-content h1 {
font-size: 28px;
line-height: 36px;
}
.register-card {
padding: 34px 26px;
}
}
@media (max-width: 480px) {
.register-page {
padding: 22px 12px;
}
.register-shell {
border-radius: 18px;
}
.register-brand-panel {
padding: 24px;
}
.brand-badges {
display: none;
}
.register-card {
padding: 28px 20px;
}
.register-header h2 {
font-size: 25px;
line-height: 32px;
}
.register-footer {
grid-template-columns: 1fr;
gap: 10px;
}
.register-footer span {
display: none;
}
.register-footer a,
.register-footer a:first-child,
.register-footer a:last-child {
text-align: center;
}
}

View File

@ -0,0 +1,341 @@
import { Component, OnInit } from '@angular/core';
import { Usuarios, Email } from '../models/usuariosModel';
import { UsuariosService } from '../services/usuarios.service';
import { environment } from 'src/environments/environment';
import { Router } from '@angular/router';
@Component({
selector: 'app-criarconta',
templateUrl: './criarconta.component.html',
styleUrls: ['./criarconta.component.scss']
})
export class CriarcontaComponent implements OnInit {
FormUsuario: Usuarios = new Usuarios();
ListUsuarios: Usuarios[];
MensagemForm: String;
TituloJanela: String = "Cadastre-se";
TextoBotao: String = "Criar conta";
FormEmail: Email = new Email();
Sucesso: boolean = false;
CPFValido: boolean = false;
constructor(
private usuariosSerivce: UsuariosService,
private router: Router,
) { }
ngOnInit() {
this.FormUsuario.idtiposdeusuario = 2;
this.FormUsuario.habilitado = false;
this.ListUsuarios = [];
this.usuariosSerivce.getUsuarios().then(resUsuarios => {
this.ListUsuarios = resUsuarios || [];
});
}
EnviarEmail(link: string) {
const nomeUsuario = this.FormUsuario.nome || 'usuário';
const text = `
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ativação de conta Zendion, INC.</title>
</head>
<body style="margin:0; padding:0; background-color:#f4f6fb; font-family:Arial, Helvetica, sans-serif; color:#1f2937;">
<table width="100%" cellpadding="0" cellspacing="0" style="background-color:#f4f6fb; padding:32px 12px;">
<tr>
<td align="center">
<table width="100%" cellpadding="0" cellspacing="0" style="max-width:620px; background-color:#ffffff; border-radius:14px; overflow:hidden; box-shadow:0 6px 24px rgba(15, 23, 42, 0.08);">
<!-- Header -->
<tr>
<td style="background-color:#071b63; padding:26px 32px; text-align:center;">
<img src="https://zendioninc.com.br/assets/logo_escrito.png" alt="Zendion, INC." style="height:54px; max-width:220px; display:inline-block;">
</td>
</tr>
<!-- Title -->
<tr>
<td style="padding:34px 34px 10px 34px; text-align:left;">
<h1 style="margin:0; color:#111827; font-size:24px; line-height:32px; font-weight:700;">
Olá, ${nomeUsuario}!
</h1>
</td>
</tr>
<!-- Content -->
<tr>
<td style="padding:8px 34px 0 34px;">
<p style="margin:0 0 18px 0; font-size:16px; line-height:26px; color:#374151;">
Sua conta na <strong>Zendion, INC.</strong> foi criada com sucesso.
</p>
<p style="margin:0 0 24px 0; font-size:16px; line-height:26px; color:#374151;">
Para ativar sua conta e começar a usar o sistema, clique no botão abaixo:
</p>
</td>
</tr>
<!-- Button -->
<tr>
<td align="center" style="padding:8px 34px 30px 34px;">
<a href="${link}" target="_blank" style="display:inline-block; background-color:#071b63; color:#ffffff; text-decoration:none; font-size:16px; font-weight:700; padding:14px 28px; border-radius:8px;">
Ativar minha conta
</a>
</td>
</tr>
<!-- Fallback link -->
<tr>
<td style="padding:0 34px 30px 34px;">
<p style="margin:0 0 8px 0; font-size:13px; line-height:21px; color:#6b7280;">
Se o botão não funcionar, copie e cole este link no navegador:
</p>
<p style="margin:0; font-size:13px; line-height:21px; word-break:break-all;">
<a href="${link}" target="_blank" style="color:#071b63; text-decoration:underline;">
${link}
</a>
</p>
</td>
</tr>
<!-- Security note -->
<tr>
<td style="padding:0 34px 32px 34px;">
<table width="100%" cellpadding="0" cellspacing="0" style="background-color:#f8fafc; border-left:4px solid #071b63; border-radius:8px;">
<tr>
<td style="padding:16px 18px;">
<p style="margin:0; font-size:14px; line-height:22px; color:#4b5563;">
Se você não criou uma conta na Zendion, INC., ignore este e-mail.
</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- Signature -->
<tr>
<td style="padding:0 34px 32px 34px;">
<p style="margin:0; font-size:15px; line-height:24px; color:#374151;">
Atenciosamente,<br>
<strong>Equipe Zendion, INC.</strong>
</p>
</td>
</tr>
<!-- Footer -->
<tr>
<td style="background-color:#071b63; padding:22px 34px; text-align:center;">
<p style="margin:0 0 8px 0; font-size:13px; line-height:20px; color:#ffffff;">
Zendion, INC.
</p>
<p style="margin:0; font-size:12px; line-height:20px; color:#cbd5e1;">
+55 19 99871-7560<br>
contato@zendioninc.com.br<br>
<a href="https://zendioninc.com.br" target="_blank" style="color:#ffffff; text-decoration:none;">
zendioninc.com.br
</a>
</p>
</td>
</tr>
</table>
<!-- Legal bottom -->
<table width="100%" cellpadding="0" cellspacing="0" style="max-width:620px;">
<tr>
<td style="padding:18px 12px 0 12px; text-align:center;">
<p style="margin:0; font-size:11px; line-height:18px; color:#9ca3af;">
Este é um e-mail automático. Por favor, não responda esta mensagem.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
`;
this.FormEmail.Assunto = 'Ative sua conta na Zendion, INC.';
this.FormEmail.Conteudo = text;
this.FormEmail.Para = this.FormUsuario.email;
this.usuariosSerivce.EnviarEmail(this.FormEmail).then(response => {
this.Sucesso = true;
this.TextoBotao = "Voltar ao site";
if (response != null && response["sucesso"] == true) {
this.TituloJanela = "Um link de ativação foi enviado para seu e-mail. Verifique sua caixa de entrada para ativar sua conta!";
} else {
this.TituloJanela = "Ocorreu um erro ao enviar o link de ativação para seu endereço de e-mail. Verifique suas informações ou entre em contato com o administrador do sistema.";
}
});
}
btnCriarcontaClick() {
if (this.TextoBotao == "Voltar ao site") {
this.router.navigate(['home']);
return;
}
this.MensagemForm = "";
if (!this.FormUsuario.nome || this.FormUsuario.nome.trim() == "") {
this.MensagemForm = "O campo nome é obrigatório.";
return;
}
if (!this.FormUsuario.email || this.FormUsuario.email.trim() == "") {
this.MensagemForm = "O campo e-mail é obrigatório.";
return;
}
if (!this.FormUsuario.cpf || this.FormUsuario.cpf.trim() == "") {
this.MensagemForm = "O campo CPF é obrigatório.";
return;
}
if (!this.CPFValido) {
this.MensagemForm = "Digite um CPF válido para continuar.";
return;
}
if (!this.FormUsuario.usuario || this.FormUsuario.usuario.trim() == "") {
this.MensagemForm = "O campo usuário é obrigatório.";
return;
}
if (!this.FormUsuario.senha || this.FormUsuario.senha.trim() == "") {
this.MensagemForm = "O campo senha é obrigatório.";
return;
}
if (this.ListUsuarios && this.ListUsuarios.length > 0) {
for (let i = 0; i < this.ListUsuarios.length; i++) {
if (this.ListUsuarios[i].usuario == this.FormUsuario.usuario) {
this.MensagemForm = "Nome de usuário indisponível.";
return;
}
if (this.ListUsuarios[i].email == this.FormUsuario.email) {
this.MensagemForm = "Este e-mail já está cadastrado.";
return;
}
if (this.ListUsuarios[i].cpf == this.FormUsuario.cpf) {
this.MensagemForm = "Este CPF já está cadastrado.";
return;
}
}
}
this.usuariosSerivce.insertUsuarios(this.FormUsuario).then(resInsert => {
if (resInsert['status'] == 200) {
this.EnviarEmail(environment.ApiUrl + "/users/ativarUsuario/" + resInsert['response'].insertId);
} else {
this.MensagemForm = "Erro ao criar usuário.";
}
});
}
MascaraCPF(event: KeyboardEvent): void {
if (!((event.keyCode >= 48 && event.keyCode <= 58) || (event.keyCode >= 96 && event.keyCode <= 106))) {
if (event.keyCode == 8 || (event.keyCode >= 37 && event.keyCode <= 40) || event.keyCode == 46) {
return;
}
this.FormUsuario.cpf = this.FormUsuario.cpf.substr(0, this.FormUsuario.cpf.length - 1);
}
if (this.FormUsuario.cpf.length == 3 || this.FormUsuario.cpf.length == 7)
this.FormUsuario.cpf += ".";
if (this.FormUsuario.cpf.length == 11)
this.FormUsuario.cpf += "-";
if (this.FormUsuario.cpf.length == 14)
this.ValidaCPF();
}
ValidaCPF() {
const cpf =
this.FormUsuario.cpf[0] +
this.FormUsuario.cpf[1] +
this.FormUsuario.cpf[2] +
this.FormUsuario.cpf[4] +
this.FormUsuario.cpf[5] +
this.FormUsuario.cpf[6] +
this.FormUsuario.cpf[8] +
this.FormUsuario.cpf[9] +
this.FormUsuario.cpf[10] +
this.FormUsuario.cpf[12] +
this.FormUsuario.cpf[13];
if (cpf) {
let numbers, digits, sum, i, result, equalDigits;
equalDigits = 1;
if (cpf.length < 11) {
this.CPFValido = false;
return null;
}
for (i = 0; i < cpf.length - 1; i++) {
if (cpf.charAt(i) !== cpf.charAt(i + 1)) {
equalDigits = 0;
break;
}
}
if (!equalDigits) {
numbers = cpf.substring(0, 9);
digits = cpf.substring(9);
sum = 0;
for (i = 10; i > 1; i--) {
sum += numbers.charAt(10 - i) * i;
}
result = sum % 11 < 2 ? 0 : 11 - (sum % 11);
if (result !== Number(digits.charAt(0))) {
this.CPFValido = false;
this.MensagemForm = "CPF digitado é inválido!";
return { cpfNotValid: true };
}
numbers = cpf.substring(0, 10);
sum = 0;
for (i = 11; i > 1; i--) {
sum += numbers.charAt(11 - i) * i;
}
result = sum % 11 < 2 ? 0 : 11 - (sum % 11);
if (result !== Number(digits.charAt(1))) {
this.CPFValido = false;
this.MensagemForm = "CPF digitado é inválido!";
return { cpfNotValid: true };
}
this.CPFValido = true;
this.MensagemForm = "";
return null;
} else {
this.CPFValido = false;
this.MensagemForm = "CPF digitado é inválido!";
return { cpfNotValid: true };
}
}
this.MensagemForm = "";
this.CPFValido = true;
return null;
}
}

View File

@ -0,0 +1,79 @@
<div class="dashboard-layout">
<!-- HEADER -->
<header class="dashboard-header">
<div class="header-left">
<a [routerLink]="'/home'" class="brand-link">
<img src="assets/logo_escrito.png" class="logo logo-escrito" alt="Zendion, INC.">
<img src="assets/logo.png" class="logo logo-favicon" alt="Zendion, INC.">
</a>
<div class="header-divider"></div>
<div class="header-title">
<span>Painel</span>
<strong>Zendion, INC.</strong>
</div>
</div>
<div class="header-right">
<div class="user-pill">
<i class="material-icons">account_circle</i>
<span>{{UserName || 'Usuário'}}</span>
</div>
<button type="button" class="logout-btn" (click)="Sair()" title="Sair">
<i class="material-icons">exit_to_app</i>
<span>Sair</span>
</button>
</div>
</header>
<!-- SIDEBAR -->
<aside *ngIf="BotoesMenuLateral.length > 0" class="dashboard-sidebar">
<div class="sidebar-title">
<span>Menu</span>
</div>
<nav class="sidebar-nav">
<div *ngFor="let Botao of BotoesMenuLateral; let i = index" class="menu-item">
<button class="dropdown-btn" type="button" (click)="DropMenuItem(Botao.index)">
<span class="menu-main">
<i class="material-icons menu-icon">{{Botao.icon}}</i>
<span>{{Botao.texto}}</span>
</span>
<i *ngIf="Botao.havesub" class="material-icons dropdown-icon">
{{Botao.dropicon}}
</i>
</button>
<div *ngIf="Botao.havesub" class="dropdown-container">
<a
*ngFor="let Sub of Botao.sub"
[routerLink]="Sub.link"
routerLinkActive="active-submenu"
class="submenu-item">
<i class="material-icons submenu-icon">{{Sub.icon}}</i>
<span>{{Sub.texto}}</span>
</a>
</div>
</div>
</nav>
</aside>
<!-- CONTENT -->
<main
class="dashboard-main"
[ngClass]="BotoesMenuLateral.length > 0 ? 'with-sidebar' : 'without-sidebar'">
<div class="dashboard-content-card">
<router-outlet></router-outlet>
</div>
</main>
</div>

View File

@ -0,0 +1,385 @@
.dashboard-layout {
min-height: 100vh;
background:
radial-gradient(circle at top left, rgba(7, 27, 99, 0.08), transparent 34%),
linear-gradient(135deg, #f4f6fb 0%, #eef3ff 50%, #ffffff 100%);
color: #0f172a;
}
/* =========================
HEADER
========================= */
.dashboard-header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 74px;
z-index: 1000;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
background: rgba(7, 27, 99, 0.95);
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
box-shadow: 0 14px 38px rgba(15, 23, 42, 0.22);
backdrop-filter: blur(14px);
box-sizing: border-box;
}
.header-left,
.header-right {
display: flex;
align-items: center;
}
.header-left {
gap: 18px;
}
.header-right {
gap: 12px;
}
.brand-link {
display: inline-flex;
align-items: center;
text-decoration: none;
}
.logo {
object-fit: contain;
display: block;
}
.logo-escrito {
height: 44px;
}
.logo-favicon {
display: none;
height: 40px;
}
.header-divider {
width: 1px;
height: 34px;
background: rgba(255, 255, 255, 0.18);
}
.header-title {
display: flex;
flex-direction: column;
line-height: 1.15;
}
.header-title span {
color: #bfdbfe;
font-size: 12px;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.header-title strong {
color: #ffffff;
font-size: 15px;
font-weight: 900;
}
.user-pill {
min-height: 38px;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 0 14px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.10);
color: #ffffff;
border: 1px solid rgba(255, 255, 255, 0.16);
}
.user-pill .material-icons {
font-size: 21px;
}
.user-pill span {
font-size: 13px;
font-weight: 800;
max-width: 160px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.logout-btn {
min-height: 38px;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 0 14px;
border: none;
border-radius: 12px;
background: #ffffff;
color: #071b63;
font-size: 13px;
font-weight: 900;
cursor: pointer;
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.16);
transition: transform 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease;
}
.logout-btn .material-icons {
font-size: 20px;
}
.logout-btn:hover {
transform: translateY(-1px);
background: #f8fafc;
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.20);
}
/* =========================
SIDEBAR
========================= */
.dashboard-sidebar {
position: fixed;
top: 74px;
left: 0;
bottom: 0;
width: 270px;
z-index: 900;
padding: 22px 14px;
background:
radial-gradient(circle at top left, rgba(255, 255, 255, 0.10), transparent 34%),
linear-gradient(180deg, #071b63 0%, #04113f 100%);
border-right: 1px solid rgba(255, 255, 255, 0.10);
box-shadow: 14px 0 34px rgba(15, 23, 42, 0.12);
overflow-y: auto;
box-sizing: border-box;
}
.sidebar-title {
padding: 0 12px 16px 12px;
}
.sidebar-title span {
color: #bfdbfe;
font-size: 12px;
font-weight: 900;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.sidebar-nav {
display: grid;
gap: 8px;
}
.menu-item {
width: 100%;
}
.dropdown-btn {
width: 100%;
min-height: 46px;
padding: 0 12px;
border: none;
border-radius: 14px;
background: transparent;
color: #dbeafe;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
text-align: left;
transition: background-color 0.18s ease, color 0.18s ease, transform 0.18s ease;
}
.dropdown-btn:hover {
background: rgba(255, 255, 255, 0.10);
color: #ffffff;
}
.menu-main {
display: inline-flex;
align-items: center;
gap: 12px;
min-width: 0;
}
.menu-main span {
font-size: 14px;
font-weight: 800;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.menu-icon {
font-size: 22px;
color: #bfdbfe;
flex: 0 0 auto;
}
.dropdown-icon {
font-size: 22px;
color: #bfdbfe;
flex: 0 0 auto;
}
.dropdown-container {
display: block;
margin: 6px 0 10px 0;
padding: 6px;
border-radius: 16px;
background: rgba(2, 6, 23, 0.22);
border: 1px solid rgba(255, 255, 255, 0.08);
}
.submenu-item {
min-height: 40px;
padding: 0 10px;
display: flex;
align-items: center;
gap: 10px;
border-radius: 12px;
color: #cbd5e1;
text-decoration: none;
transition: background-color 0.18s ease, color 0.18s ease, transform 0.18s ease;
}
.submenu-item span {
font-size: 13px;
font-weight: 750;
}
.submenu-icon {
font-size: 19px;
color: #93c5fd;
}
.submenu-item:hover,
.active-submenu {
background: rgba(255, 255, 255, 0.12);
color: #ffffff;
text-decoration: none;
}
.submenu-item:hover {
transform: translateX(2px);
}
/* =========================
CONTENT
========================= */
.dashboard-main {
min-height: 100vh;
padding-top: 74px;
box-sizing: border-box;
}
.dashboard-main.with-sidebar {
padding-left: 270px;
}
.dashboard-main.without-sidebar {
padding-left: 0;
}
.dashboard-content-card {
min-height: calc(100vh - 74px);
padding: 28px;
box-sizing: border-box;
}
/* =========================
RESPONSIVE
========================= */
@media (max-width: 900px) {
.dashboard-sidebar {
width: 230px;
}
.dashboard-main.with-sidebar {
padding-left: 230px;
}
.header-title {
display: none;
}
}
@media (max-width: 768px) {
.dashboard-header {
height: 68px;
padding: 0 14px;
}
.dashboard-sidebar {
position: relative;
top: 68px;
width: 100%;
height: auto;
min-height: auto;
padding: 12px;
display: block;
border-right: none;
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}
.dashboard-main,
.dashboard-main.with-sidebar,
.dashboard-main.without-sidebar {
padding-left: 0;
padding-top: 68px;
}
.dashboard-main.with-sidebar {
padding-top: 68px;
}
.dashboard-content-card {
padding: 18px 14px;
}
.logo-escrito {
display: none;
}
.logo-favicon {
display: block;
}
.user-pill {
display: none;
}
.logout-btn span {
display: none;
}
.logout-btn {
width: 40px;
padding: 0;
justify-content: center;
}
}
@media (max-width: 480px) {
.dashboard-content-card {
padding: 14px 10px;
}
.dropdown-btn {
min-height: 44px;
}
.menu-main span {
font-size: 13px;
}
}

View File

@ -0,0 +1,272 @@
import { Component, OnInit } from '@angular/core';
import { MenuItem } from '../models/dashboardModel';
import { Router } from '@angular/router';
import { FuncoesGlobais } from 'src/environments/environment';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss']
})
export class DashboardComponent implements OnInit {
BotoesMenuLateral: MenuItem[];
UserName: string = localStorage.getItem("UserName") || "";
constructor(
private router: Router,
private FuncoesGlobais: FuncoesGlobais,
) { }
ngOnInit() {
if (localStorage.getItem("Role") == "1") {
this.PreencherMenuItensAdmin();
}
else if (localStorage.getItem("Role") == "2") {
this.PreencherMenuItensCliente();
}
else {
this.router.navigate(['/login']);
}
}
PreencherMenuItensAdmin() {
this.BotoesMenuLateral = [
{
index: 0,
texto: "Página inicial",
link: "dashboard/admin/home",
havesub: false,
dropicon: "arrow_drop_down",
icon: "home",
issub: false,
sub: null,
},
{
index: 1,
texto: "Usuários",
link: "",
havesub: true,
dropicon: "arrow_drop_down",
icon: "person",
issub: false,
sub: [
{
index: 0,
texto: "Cadastrar",
link: "usuarios/cadastro",
icon: "person_add",
havesub: false,
dropicon: "",
issub: true,
sub: null,
},
{
index: 1,
texto: "Visualizar",
link: "usuarios/visualizar",
icon: "pageview",
havesub: false,
dropicon: "",
issub: true,
sub: null,
},
],
},
{
index: 2,
texto: "Sistemas",
link: "",
havesub: true,
dropicon: "arrow_drop_down",
icon: "extension",
issub: false,
sub: [
{
index: 0,
texto: "Cadastro",
link: "sistemas/cadastro",
icon: "add_to_queue",
havesub: false,
dropicon: "",
issub: true,
sub: null,
},
{
index: 1,
texto: "Visualizar",
link: "sistemas/visualizar",
icon: "pageview",
havesub: null,
dropicon: "",
issub: true,
sub: null,
},
],
},
{
index: 3,
texto: "Ambiente cliente",
link: "/dashboard/cliente",
havesub: false,
dropicon: "arrow_drop_down",
icon: "home",
issub: false,
sub: null,
},
{
index: 4,
texto: "Bingo",
link: "",
havesub: true,
dropicon: "arrow_drop_down",
icon: "extension",
issub: false,
sub: [
{
index: 0,
texto: "Dashboard",
link: "/bingo/dashboard",
icon: "add_to_queue",
havesub: false,
dropicon: "",
issub: true,
sub: null,
},
{
index: 1,
texto: "Sorteio",
link: "/bingo/sorteio",
icon: "pageview",
havesub: null,
dropicon: "",
issub: true,
sub: null,
},
],
},
{
index: 5,
texto: "OrionTard Pro",
link: "/orion-dashboard",
havesub: false,
dropicon: "arrow_drop_down",
icon: "home",
issub: false,
sub: null,
},
{
index: 6,
texto: "AgroBase",
link: "",
havesub: true,
dropicon: "arrow_drop_down",
icon: "agriculture",
issub: false,
sub: [
{
index: 0,
texto: "Versionamento",
link: "/agrobase/versionamento",
icon: "update",
havesub: false,
dropicon: "",
issub: true,
sub: null,
},
],
},
];
this.CriaEventoClickSubItens();
}
PreencherMenuItensCliente() {
this.BotoesMenuLateral = [
/*{
index: 0,
texto: "Página inicial",
link: "/home",
havesub: false,
dropicon: "arrow_drop_down",
icon: "home",
issub: false,
sub: null,
},
{
index: 1,
texto: "Meus Sistemas",
link: "/dashboard/cliente",
havesub: false,
dropicon: "arrow_drop_down",
icon: "extension",
issub: false,
sub: [
{
index: 0,
texto: "Machining Price Simulator",
link: "sistemas/MPS",
icon: "assessment",
havesub: false,
dropicon: "",
issub: true,
sub: null,
},
{
index: 1,
texto: "Casco-Tubo",
link: "sistemas/Casco-Tubo",
icon: "assessment",
havesub: false,
dropicon: "",
issub: true,
sub: null,
},
],
},*/
];
this.CriaEventoClickSubItens();
}
CriaEventoClickSubItens() {
setTimeout(() => {
var dropdown = document.getElementsByClassName("dropdown-btn");
for (let i = 0; i < dropdown.length; i++) {
if (this.BotoesMenuLateral[i].havesub) {
dropdown[i].addEventListener("click", function() {
this.classList.toggle("active");
var dropdownContent = this.nextElementSibling;
if (dropdownContent.style.display === "block") {
dropdownContent.style.display = "none";
} else {
dropdownContent.style.display = "block";
}
});
}
}
}, 100);
}
DropMenuItem(indexBtnMenu) {
if (this.BotoesMenuLateral[indexBtnMenu].havesub) {
if (this.BotoesMenuLateral[indexBtnMenu].dropicon == "arrow_drop_down")
this.BotoesMenuLateral[indexBtnMenu].dropicon = "arrow_drop_up";
else
this.BotoesMenuLateral[indexBtnMenu].dropicon = "arrow_drop_down";
}
else {
this.router.navigate([this.BotoesMenuLateral[indexBtnMenu].link]);
}
}
Sair() {
localStorage.removeItem("Session");
localStorage.removeItem("UserName");
localStorage.removeItem("UserID");
localStorage.removeItem("Role");
localStorage.removeItem("LastRouteBeforeLogin");
this.router.navigate(['/home']);
}
}

View File

@ -0,0 +1,197 @@
<div class="details-page">
<!-- LOADING -->
<div class="details-loader" *ngIf="!Carregado">
<img src="assets/loader.gif" class="loader" alt="Carregando">
<p>Carregando detalhes do sistema...</p>
</div>
<!-- CONTENT -->
<div class="details-wrapper" *ngIf="Carregado">
<!-- HERO -->
<section class="system-hero">
<div class="system-hero-glow"></div>
<div class="system-hero-content">
<div class="system-icon-card">
<img
[src]="LinkProjeto + '/sistemas/' + idsistemas + '/icone.png'"
class="system-icon"
alt="Ícone do sistema">
</div>
<div class="system-hero-text">
<span class="eyebrow">Sistema Zendion</span>
<h1>{{ Sistema.nome }}</h1>
<p>{{ Sistema.descricao }}</p>
<div class="system-badges">
<span>{{ Sistema.tiposdesistema }}</span>
<span>{{ Sistema.tiposlicenca }}</span>
<span>{{ Sistema.plataforma }}</span>
</div>
</div>
<div class="system-action-card">
<small>Versão atual</small>
<strong>{{ Sistema.versaoatual }}</strong>
<button type="button" class="btn-main-action" (click)="btnBaixarClick()">
{{ btnBaixar }}
</button>
<p>
{{ Sistema.plataforma }} • {{ Sistema.versaoatual }}
</p>
</div>
</div>
</section>
<!-- INFO CARDS -->
<section class="info-grid">
<div class="info-card">
<span>Versão</span>
<strong>{{ Sistema.versaoatual }}</strong>
</div>
<div class="info-card">
<span>Lançamento</span>
<strong>{{ Sistema.insert_date | date:'dd/MM/yyyy' }}</strong>
</div>
<div class="info-card">
<span>Categoria</span>
<strong>{{ Sistema.tiposdesistema }}</strong>
</div>
<div class="info-card">
<span>Licença</span>
<strong>{{ Sistema.tiposlicenca }}</strong>
</div>
</section>
<!-- DETAILS -->
<section class="details-sections" *ngIf="Detalhes && Detalhes.length > 0">
<div class="section-header">
<span class="eyebrow">Detalhes</span>
<h2>Conheça melhor o sistema</h2>
<p>Recursos, telas e informações importantes sobre esta solução.</p>
</div>
<article
class="detail-card"
*ngFor="let Detalhe of Detalhes; let i = index"
[ngClass]="i % 2 == 0 ? 'normal' : 'reverse'">
<div class="detail-text">
<div [innerHTML]="Detalhe.descricao"></div>
</div>
<div class="detail-image-wrapper">
<img
[src]="LinkProjeto + '/sistemas/' + idsistemas + '/detalhes/detalhe_' + Detalhe.ordem + '.jpg'"
class="detail-image"
alt="Detalhe do sistema"
(click)="ImageClick($event)">
</div>
</article>
</section>
<!-- PLATFORM + UPDATE -->
<section class="secondary-grid">
<div class="panel-card availability-section">
<span class="panel-label">Disponível para</span>
<img
[src]="LinkProjeto + '/imagens/plataformas/plataforma_' + Sistema.idplataformas + '.png'"
class="platform-icon"
alt="Plataforma">
<strong>{{ Sistema.plataforma }}</strong>
</div>
<div class="panel-card update-notes">
<span class="panel-label">Notas de atualização</span>
<small *ngIf="Sistema.update_date">
Atualizado em {{ Sistema.update_date | date:'dd/MM/yyyy' }}
</small>
<div [innerHTML]="Sistema.notasdeatualizacao" class="update-text"></div>
</div>
</section>
<!-- VERSIONS -->
<section *ngIf="Sistema.idplataformas != 6 && Adiquirido" class="panel-card list-panel">
<div class="list-header">
<div>
<span class="panel-label">Histórico</span>
<h3>Versões anteriores</h3>
</div>
</div>
<ul class="resource-list">
<li *ngFor="let versao of Sistema.versoes">
<div>
<strong>{{ versao.split('versoes/')[1].split('/')[0] }}</strong>
<small>{{ versao.split('¬')[1] | date:'dd/MM/yyyy HH:mm' }}</small>
</div>
<a [href]="versao.split('¬')[0]" target="_blank">
Baixar
</a>
</li>
</ul>
</section>
<!-- DOWNLOADS -->
<section *ngIf="Adiquirido" class="panel-card list-panel">
<div class="list-header">
<div>
<span class="panel-label">Arquivos</span>
<h3>Materiais para download</h3>
</div>
</div>
<ul class="resource-list">
<li *ngFor="let download of Sistema.mdownloads">
<div>
<strong>{{ download.split('downloads/')[1].split('¬')[0] }}</strong>
<small>{{ download.split('¬')[1] | date:'dd/MM/yyyy HH:mm' }}</small>
</div>
<a [href]="download.split('¬')[0]" target="_blank">
Abrir
</a>
</li>
</ul>
</section>
<!-- FINAL CTA -->
<section class="final-cta">
<div>
<span class="eyebrow">Pronto para continuar?</span>
<h2>{{ Sistema.nome }}</h2>
<p>{{ Sistema.plataforma }} • {{ Sistema.versaoatual }}</p>
</div>
<button type="button" class="btn-main-action" (click)="btnBaixarClick()">
{{ btnBaixar }}
</button>
</section>
</div>
<!-- MODAL -->
<div id="myModal" class="image-modal" (click)="btnFecharModal()">
<span class="close" (click)="btnFecharModal()">&times;</span>
<img class="modal-content" id="img01">
<div id="caption"></div>
</div>
</div>

View File

@ -0,0 +1,628 @@
.details-page {
min-height: 100vh;
background:
radial-gradient(circle at top left, rgba(7, 27, 99, 0.14), transparent 32%),
linear-gradient(135deg, #f4f6fb 0%, #eef3ff 45%, #ffffff 100%);
color: #0f172a;
}
.details-loader {
min-height: 360px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.loader {
width: 72px;
height: 72px;
object-fit: contain;
margin-bottom: 16px;
}
.details-loader p {
color: #64748b;
font-size: 15px;
font-weight: 700;
}
.details-wrapper {
width: 100%;
max-width: 1180px;
margin: 0 auto;
padding: 44px 22px 64px 22px;
box-sizing: border-box;
}
/* HERO */
.system-hero {
position: relative;
overflow: hidden;
border-radius: 28px;
background:
radial-gradient(circle at top right, rgba(255,255,255,0.14), transparent 34%),
linear-gradient(145deg, #071b63, #12358f);
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.18);
color: #ffffff;
}
.system-hero-glow {
position: absolute;
width: 360px;
height: 360px;
right: -130px;
top: -130px;
border-radius: 999px;
background: rgba(255,255,255,0.12);
}
.system-hero-content {
position: relative;
z-index: 1;
display: grid;
grid-template-columns: auto 1fr 260px;
align-items: center;
gap: 30px;
padding: 42px;
}
.system-icon-card {
width: 126px;
height: 126px;
border-radius: 28px;
background: #ffffff;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 18px 42px rgba(0, 0, 0, 0.18);
}
.system-icon {
width: 82px;
height: 82px;
object-fit: contain;
}
.eyebrow {
display: inline-block;
margin-bottom: 12px;
color: #bfdbfe;
font-size: 12px;
font-weight: 800;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.system-hero-text h1 {
margin: 0 0 14px 0;
font-size: 42px;
line-height: 50px;
font-weight: 900;
letter-spacing: -0.045em;
}
.system-hero-text p {
max-width: 600px;
margin: 0;
color: #dbeafe;
font-size: 16px;
line-height: 27px;
}
.system-badges {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 22px;
}
.system-badges span {
padding: 8px 12px;
border-radius: 999px;
background: rgba(255,255,255,0.12);
color: #ffffff;
font-size: 12px;
font-weight: 800;
}
.system-action-card {
padding: 22px;
border-radius: 22px;
background: rgba(255,255,255,0.12);
border: 1px solid rgba(255,255,255,0.18);
backdrop-filter: blur(10px);
}
.system-action-card small {
display: block;
color: #bfdbfe;
font-size: 12px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.system-action-card strong {
display: block;
margin: 8px 0 18px 0;
color: #ffffff;
font-size: 24px;
font-weight: 900;
}
.system-action-card p {
margin: 14px 0 0 0;
color: #dbeafe;
font-size: 12px;
line-height: 18px;
}
.btn-main-action {
width: 100%;
min-height: 48px;
border: none;
border-radius: 14px;
background: #ffffff;
color: #071b63;
font-size: 15px;
font-weight: 900;
cursor: pointer;
box-shadow: 0 14px 26px rgba(0,0,0,0.16);
transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.btn-main-action:hover {
transform: translateY(-1px);
box-shadow: 0 18px 34px rgba(0,0,0,0.20);
}
/* INFO */
.info-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 16px;
margin-top: 24px;
}
.info-card {
padding: 20px;
border-radius: 20px;
background: #ffffff;
box-shadow: 0 16px 36px rgba(15, 23, 42, 0.08);
border: 1px solid rgba(148, 163, 184, 0.22);
}
.info-card span {
display: block;
margin-bottom: 8px;
color: #64748b;
font-size: 12px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.info-card strong {
display: block;
color: #0f172a;
font-size: 17px;
line-height: 24px;
font-weight: 900;
}
/* SECTION HEADER */
.section-header {
text-align: center;
margin: 54px 0 34px 0;
}
.section-header .eyebrow {
color: #071b63;
}
.section-header h2 {
margin: 0 0 10px 0;
color: #0f172a;
font-size: 34px;
line-height: 42px;
font-weight: 900;
letter-spacing: -0.035em;
}
.section-header p {
max-width: 620px;
margin: 0 auto;
color: #64748b;
font-size: 16px;
line-height: 26px;
}
/* DETAILS CARDS */
.details-sections {
margin-top: 12px;
}
.detail-card {
display: grid;
grid-template-columns: 1fr 1fr;
overflow: hidden;
margin-bottom: 26px;
border-radius: 24px;
background: #ffffff;
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
border: 1px solid rgba(148, 163, 184, 0.22);
}
.detail-card.reverse .detail-text {
order: 2;
}
.detail-card.reverse .detail-image-wrapper {
order: 1;
}
.detail-text {
padding: 38px;
display: flex;
align-items: center;
color: #475569;
font-size: 16px;
line-height: 27px;
}
.detail-text h1,
.detail-text h2,
.detail-text h3,
.detail-text h4 {
color: #0f172a;
font-weight: 900;
letter-spacing: -0.025em;
}
.detail-text p:last-child {
margin-bottom: 0;
}
.detail-image-wrapper {
min-height: 340px;
background: #e5e7eb;
}
.detail-image {
width: 100%;
height: 100%;
min-height: 340px;
object-fit: cover;
display: block;
cursor: zoom-in;
transition: transform 0.22s ease;
}
.detail-image:hover {
transform: scale(1.025);
}
/* SECONDARY PANELS */
.secondary-grid {
display: grid;
grid-template-columns: 0.75fr 1.25fr;
gap: 22px;
margin-top: 42px;
}
.panel-card {
border-radius: 24px;
background: #ffffff;
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
border: 1px solid rgba(148, 163, 184, 0.22);
}
.availability-section {
padding: 30px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.panel-label {
display: block;
margin-bottom: 14px;
color: #071b63;
font-size: 12px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 0.10em;
}
.platform-icon {
width: 54px;
height: 54px;
object-fit: contain;
margin: 8px 0 16px 0;
}
.availability-section strong {
color: #0f172a;
font-size: 18px;
font-weight: 900;
}
.update-notes {
padding: 30px;
}
.update-notes small {
display: block;
color: #64748b;
font-size: 13px;
margin-bottom: 14px;
}
.update-text {
color: #475569;
font-size: 15px;
line-height: 25px;
padding: 18px;
border-radius: 16px;
background: #f8fafc;
border: 1px solid #e5e7eb;
}
/* LISTS */
.list-panel {
margin-top: 24px;
padding: 30px;
}
.list-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 18px;
}
.list-header h3 {
margin: 0;
color: #0f172a;
font-size: 24px;
line-height: 32px;
font-weight: 900;
}
.resource-list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 12px;
}
.resource-list li {
display: flex;
justify-content: space-between;
gap: 18px;
align-items: center;
padding: 16px;
border-radius: 16px;
background: #f8fafc;
border: 1px solid #e5e7eb;
}
.resource-list strong {
display: block;
color: #0f172a;
font-size: 14px;
line-height: 21px;
font-weight: 900;
word-break: break-word;
}
.resource-list small {
display: block;
margin-top: 4px;
color: #64748b;
font-size: 12px;
}
.resource-list a {
flex: 0 0 auto;
padding: 10px 14px;
border-radius: 12px;
background: #071b63;
color: #ffffff;
font-size: 13px;
font-weight: 900;
text-decoration: none;
}
.resource-list a:hover {
background: #12358f;
}
/* FINAL CTA */
.final-cta {
margin-top: 34px;
padding: 32px;
border-radius: 24px;
display: grid;
grid-template-columns: 1fr 220px;
align-items: center;
gap: 24px;
background:
radial-gradient(circle at top right, rgba(255,255,255,0.14), transparent 34%),
linear-gradient(145deg, #071b63, #12358f);
color: #ffffff;
box-shadow: 0 22px 55px rgba(15, 23, 42, 0.16);
}
.final-cta h2 {
margin: 0 0 8px 0;
color: #ffffff;
font-size: 28px;
line-height: 36px;
font-weight: 900;
}
.final-cta p {
margin: 0;
color: #dbeafe;
font-size: 14px;
}
.final-cta .btn-main-action {
background: #ffffff;
color: #071b63;
}
/* MODAL */
.image-modal {
display: none;
position: fixed;
z-index: 9999;
inset: 0;
overflow: auto;
background-color: rgba(2, 6, 23, 0.92);
padding: 56px 18px;
box-sizing: border-box;
}
.modal-content {
margin: auto;
display: block;
width: auto;
max-width: 92%;
max-height: 82vh;
border-radius: 16px;
box-shadow: 0 24px 70px rgba(0,0,0,0.45);
}
#caption {
margin: 16px auto 0 auto;
width: 80%;
max-width: 760px;
text-align: center;
color: #cbd5e1;
font-size: 14px;
line-height: 22px;
}
.close {
position: fixed;
top: 18px;
right: 30px;
color: #ffffff;
font-size: 42px;
font-weight: 900;
cursor: pointer;
z-index: 10000;
}
.close:hover {
color: #cbd5e1;
}
/* RESPONSIVE */
@media (max-width: 980px) {
.system-hero-content {
grid-template-columns: auto 1fr;
}
.system-action-card {
grid-column: 1 / -1;
}
.info-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.detail-card {
grid-template-columns: 1fr;
}
.detail-card.reverse .detail-text,
.detail-card.reverse .detail-image-wrapper {
order: unset;
}
.secondary-grid {
grid-template-columns: 1fr;
}
.final-cta {
grid-template-columns: 1fr;
}
}
@media (max-width: 620px) {
.details-wrapper {
padding: 28px 14px 46px 14px;
}
.system-hero-content {
grid-template-columns: 1fr;
padding: 28px;
text-align: center;
}
.system-icon-card {
margin: 0 auto;
}
.system-hero-text h1 {
font-size: 32px;
line-height: 40px;
}
.system-badges {
justify-content: center;
}
.info-grid {
grid-template-columns: 1fr;
}
.section-header h2 {
font-size: 28px;
line-height: 36px;
}
.detail-text {
padding: 26px;
}
.detail-image,
.detail-image-wrapper {
min-height: 240px;
}
.resource-list li {
align-items: flex-start;
flex-direction: column;
}
.resource-list a {
width: 100%;
text-align: center;
}
.final-cta {
padding: 26px;
text-align: center;
}
.modal-content {
width: 100%;
max-width: 100%;
}
}

View File

@ -0,0 +1,202 @@
import { Component, OnInit } from '@angular/core';
import { Sistemas, Download, SistemasDetalhes, SistemasLista } from '../models/sistemasModel';
import { SistemasService } from '../services/sistemas.service';
import { UsuariosService } from '../services/usuarios.service';
import { ActivatedRoute, Router } from '@angular/router';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-detalhes-sistema',
templateUrl: './detalhes-sistema.component.html',
styleUrls: ['./detalhes-sistema.component.scss']
})
export class DetalhesSistemaComponent implements OnInit {
Sistema: SistemasLista = new SistemasLista();
idsistemas: number = parseInt(this.route.snapshot.params['idsistemas']);
idusuarios: number = parseInt(localStorage.getItem("UserID") || "0");
FormDownload: Download = new Download();
Logado: boolean = false;
Adiquirido: boolean = false;
btnBaixar: string = "Adquirir";
Detalhes: SistemasDetalhes[] = [];
LinkProjeto: string = environment.LinkProjeto;
Carregado: boolean = false;
x86: boolean = false;
x64: boolean = true;
constructor(
private sistemasService: SistemasService,
private usuariosService: UsuariosService,
private route: ActivatedRoute,
private router: Router
) { }
ngOnInit() {
this.FormDownload.idsistemas = this.idsistemas;
this.FormDownload.idusuarios = this.idusuarios;
this.sistemasService.getSistemaByIDLista(this.idsistemas).then(resSistema => {
this.Sistema = resSistema;
if (!this.Sistema.versoes) {
this.Sistema.versoes = [];
}
if (!this.Sistema.mdownloads) {
this.Sistema.mdownloads = [];
}
this.CarregarVersoesEDownloads();
})
.catch(error => {
console.error('[DetalhesSistema] Erro ao carregar sistema:', error);
this.Carregado = true;
});
}
CarregarVersoesEDownloads() {
this.sistemasService.getSistemaVersoes(this.idsistemas).then(resVersoes => {
this.Sistema.versoes = [];
if (resVersoes) {
resVersoes.forEach(versao => {
this.Sistema.versoes.push(versao.replace("/var/www/html", this.LinkProjeto));
});
}
this.CarregarDownloads();
})
.catch(error => {
this.CarregarDownloads();
});
}
CarregarDownloads() {
this.sistemasService.getSistemaDownloads(this.idsistemas).then(resDownloads => {
this.Sistema.mdownloads = [];
if (resDownloads) {
resDownloads.forEach(download => {
this.Sistema.mdownloads.push(download.replace("/var/www/html", this.LinkProjeto));
});
}
this.CarregaDetalhesSistema();
})
.catch(error => {
this.CarregaDetalhesSistema();
});
}
CarregaDetalhesSistema() {
if (localStorage.getItem("Session") == "1") {
this.Logado = true;
this.usuariosService.getSistemasDoUsuario(this.idusuarios).then(resSistemas => {
if (resSistemas) {
resSistemas.forEach(sistema => {
if (sistema.idsistemas == this.Sistema.idsistemas) {
this.Adiquirido = true;
this.btnBaixar = (this.Sistema.idtiposdesistema == 6) ? "Acessar" : "Baixar";
}
});
}
this.CarregarDetalhes();
})
.catch(error => {
this.CarregarDetalhes();
});
return;
}
this.CarregarDetalhes();
}
CarregarDetalhes() {
this.sistemasService.getDetalhesByIDEn(this.idsistemas).then(resDetalhes => {
this.Detalhes = resDetalhes || [];
this.Carregado = true;
})
.catch(error => {
console.error('[DetalhesSistema] Erro ao carregar detalhes:', error);
this.Detalhes = [];
this.Carregado = true;
});
}
btnBaixarClick() {
if (localStorage.getItem("Session") == "1") {
this.Baixar();
return;
}
localStorage.setItem("LastRouteBeforeLogin", "/detalhes/" + this.idsistemas);
this.router.navigate(['/criarconta']);
}
Baixar() {
if (this.btnBaixar == "Adquirir") {
this.sistemasService.downloadSistema(this.FormDownload).then(resDownload => {
this.sistemasService.insertSistemaUsuario(this.FormDownload).then(resInsertSistema => {
if (this.Sistema.idplataformas == 6) {
this.btnBaixar = "Acessar";
} else {
this.btnBaixar = "Baixar";
}
this.Adiquirido = true;
alert("Acesso liberado! Clique em " + this.btnBaixar + " para continuar.");
});
});
return;
}
if (this.btnBaixar == "Acessar") {
window.open(this.Sistema.nome_install, '_blank');
return;
}
if (this.btnBaixar == "Baixar") {
window.open(
environment.LinkProjeto + "/sistemas/" + this.idsistemas + "/versoes/" + this.Sistema.versaoatual + "/" + this.Sistema.nome_install,
'_blank'
);
}
}
ImageClick(event) {
const modal = document.getElementById("myModal");
const modalImg = document.getElementById("img01") as HTMLImageElement;
const captionText = document.getElementById("caption");
const target = event.target as HTMLImageElement;
if (!modal || !modalImg || !captionText || !target) {
return;
}
modal.style.display = "block";
modalImg.src = target.currentSrc || target.src;
captionText.innerHTML = target.alt || "";
}
btnFecharModal() {
const modal = document.getElementById("myModal");
if (modal) {
modal.style.display = "none";
}
}
}

View File

@ -0,0 +1,12 @@
<div class="Corpo">
<h2>EasyAVA</h2>
<label>Cole a pergunta na área abaixo</label> <br>
<textarea [(ngModel)]="Questao" style="width: 50%; height: 300px;"></textarea> <br> <br>
<button (click)="Procurar()" class="button" type="button">Pesquisar resposta</button> <br> <br>
<span style="font-size: 30px;">A resposta correta é:</span> <span style="font-size: 30px; color: green;"> {{Resposta}}</span>
<br> <br>
<iframe src="http://myrobotlab.com.br"></iframe>
</div>

View File

@ -0,0 +1,6 @@
.Corpo {
text-align: center;
margin: auto;
color: #fff;
margin-top: 2em;
}

View File

@ -0,0 +1,28 @@
import { Component, OnInit } from '@angular/core';
import { EasyAVAService } from '../services/easy-ava.service';
@Component({
selector: 'app-easy-ava',
templateUrl: './easy-ava.component.html',
styleUrls: ['./easy-ava.component.scss']
})
export class EasyAVAComponent implements OnInit {
Questao: string = "";
Resposta: string = "";
constructor(
private easyAvaService: EasyAVAService
) { }
ngOnInit() {
}
Procurar() {
this.easyAvaService.ProcurarResposta().then(resResposta => {
console.log(resResposta);
})
}
}

View File

@ -0,0 +1,273 @@
<div class="edit-home-page">
<!-- HEADER -->
<section class="admin-hero">
<div>
<span class="eyebrow">Backoffice</span>
<h1>Painel administrativo</h1>
<p>
Gerencie as configurações principais da página inicial, identidade visual,
endereços do projeto, slider e conteúdo público.
</p>
</div>
</section>
<!-- CONFIGURAÇÕES GERAIS -->
<section class="admin-card">
<div class="panel-header">
<div>
<span class="eyebrow">Configurações</span>
<h2>Cabeçalho e endereços</h2>
</div>
</div>
<div class="settings-grid">
<!-- TÍTULO -->
<div class="setting-card">
<div class="setting-info">
<div class="setting-icon">
<i class="material-icons">title</i>
</div>
<div>
<h3>Título do site</h3>
<p>Nome exibido nas configurações principais da aplicação.</p>
</div>
</div>
<div class="setting-control">
<input
class="form-control"
[(ngModel)]="Titulo"
placeholder="Título do site">
<button
type="button"
class="btn-main"
[disabled]="Titulo == TituloAnterior"
(click)="Editar('Título')">
<i class="material-icons">save</i>
Salvar título
</button>
</div>
</div>
<!-- URL PROJETO -->
<div class="setting-card">
<div class="setting-info">
<div class="setting-icon">
<i class="material-icons">public</i>
</div>
<div>
<h3>URL do projeto</h3>
<p>Endereço público utilizado pelo site para montar links e recursos.</p>
</div>
</div>
<div class="setting-control">
<input
class="form-control"
[(ngModel)]="ProjetoUrl"
placeholder="https://zendioninc.com.br">
<label class="confirm-check">
<input type="checkbox" [(ngModel)]="ProjetoUrlCheck">
<span>Confirmar alteração sensível</span>
</label>
<button
type="button"
class="btn-main"
[disabled]="ProjetoUrl == ProjetoUrlAnterior || !ProjetoUrlCheck"
(click)="Editar('URL do projeto')">
<i class="material-icons">save</i>
Salvar URL do projeto
</button>
</div>
</div>
<!-- URL API -->
<div class="setting-card">
<div class="setting-info">
<div class="setting-icon">
<i class="material-icons">api</i>
</div>
<div>
<h3>URL da API</h3>
<p>Endereço usado pelo frontend para se comunicar com o servidor.</p>
</div>
</div>
<div class="setting-control">
<input
class="form-control"
[(ngModel)]="APIUrl"
placeholder="https://api.zendioninc.com.br">
<label class="confirm-check">
<input type="checkbox" [(ngModel)]="APIUrlCheck">
<span>Confirmar alteração sensível</span>
</label>
<button
type="button"
class="btn-main"
[disabled]="APIUrl == APIUrlAnterior || !APIUrlCheck"
(click)="Editar('URL da API')">
<i class="material-icons">save</i>
Salvar URL da API
</button>
</div>
</div>
</div>
</section>
<!-- IDENTIDADE VISUAL -->
<section class="admin-card">
<div class="panel-header">
<div>
<span class="eyebrow">Identidade visual</span>
<h2>Arquivos do site</h2>
</div>
</div>
<div class="assets-grid">
<!-- LOGO -->
<article class="asset-card">
<div class="asset-preview logo-preview">
<img [src]="CaminhoLogo" alt="Logo">
</div>
<div class="asset-body">
<h3>Logo escrita</h3>
<p>Arquivo principal exibido no cabeçalho e rodapé do site.</p>
<label class="file-label">
<i class="material-icons">upload_file</i>
Selecionar PNG
<input type="file" (change)="onFileChangedLogo($event)" accept=".png">
</label>
<button
type="button"
class="btn-main asset-btn"
[disabled]="!Logo"
(click)="CopiarArquivos('logo_escrito.png', Logo, 'assets/')">
<i class="material-icons">save</i>
Editar logo
</button>
</div>
</article>
<!-- FAVICON -->
<article class="asset-card">
<div class="asset-preview favicon-preview">
<img [src]="CaminhoFavicon" alt="Favicon">
</div>
<div class="asset-body">
<h3>Favicon</h3>
<p>Ícone exibido na aba do navegador.</p>
<label class="file-label">
<i class="material-icons">upload_file</i>
Selecionar ICO
<input type="file" (change)="onFileChangedFavicon($event)" accept=".ico">
</label>
<button
type="button"
class="btn-main asset-btn"
[disabled]="!Favicon"
(click)="CopiarArquivos('favicon.ico', Favicon, '')">
<i class="material-icons">save</i>
Editar favicon
</button>
</div>
</article>
<!-- BACKGROUND -->
<article class="asset-card">
<div class="asset-preview background-preview">
<img [src]="CaminhoBackground" alt="Background">
</div>
<div class="asset-body">
<h3>Background</h3>
<p>Imagem de fundo usada no site.</p>
<label class="file-label">
<i class="material-icons">upload_file</i>
Selecionar PNG
<input type="file" (change)="onFileChangedBackground($event)" accept=".png,.jpg,.jpeg">
</label>
<button
type="button"
class="btn-main asset-btn"
[disabled]="!Background"
(click)="CopiarArquivos('background.jpg', Background, 'assets/')">
<i class="material-icons">save</i>
Editar background
</button>
</div>
</article>
</div>
</section>
<!-- ATALHOS -->
<section class="shortcut-grid">
<article class="admin-card shortcut-card">
<div class="shortcut-icon">
<i class="material-icons">collections</i>
</div>
<div>
<span class="eyebrow">Slider</span>
<h2>Slides da página inicial</h2>
<p>Cadastre, edite e visualize os banners exibidos na home.</p>
</div>
<div class="shortcut-actions">
<button type="button" class="btn-secondary" (click)="Cadastrar('slides')">
Cadastrar slides
</button>
<button type="button" class="btn-main form-submit" (click)="Visualizar('slides')">
Visualizar slides
</button>
</div>
</article>
<article class="admin-card shortcut-card">
<div class="shortcut-icon">
<i class="material-icons">article</i>
</div>
<div>
<span class="eyebrow">Conteúdo</span>
<h2>Notícias da página inicial</h2>
<p>Gerencie os conteúdos publicados na área de notícias do site.</p>
</div>
<div class="shortcut-actions">
<button type="button" class="btn-secondary" (click)="Cadastrar('noticias')">
Cadastrar notícias
</button>
<button type="button" class="btn-main form-submit" (click)="Visualizar('noticias')">
Visualizar notícias
</button>
</div>
</article>
</section>
</div>

View File

@ -0,0 +1,363 @@
.edit-home-page {
min-height: calc(100vh - 74px);
color: #0f172a;
}
/* HERO */
.admin-hero {
margin-bottom: 22px;
padding: 32px;
border-radius: 24px;
background:
radial-gradient(circle at top right, rgba(255, 255, 255, 0.14), transparent 34%),
linear-gradient(145deg, #071b63, #12358f);
color: #ffffff;
box-shadow: 0 22px 55px rgba(15, 23, 42, 0.16);
}
.eyebrow {
display: inline-block;
margin-bottom: 10px;
color: #071b63;
font-size: 12px;
font-weight: 900;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.admin-hero .eyebrow {
color: #bfdbfe;
}
.admin-hero h1 {
margin: 0 0 10px 0;
color: #ffffff;
font-size: 34px;
line-height: 42px;
font-weight: 900;
letter-spacing: -0.04em;
}
.admin-hero p {
max-width: 760px;
margin: 0;
color: #dbeafe;
font-size: 15px;
line-height: 25px;
}
/* BASE CARDS */
.admin-card {
margin-bottom: 22px;
padding: 28px;
border-radius: 24px;
background: #ffffff;
border: 1px solid rgba(148, 163, 184, 0.22);
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
}
.panel-header {
display: flex;
justify-content: space-between;
gap: 18px;
margin-bottom: 24px;
}
.panel-header h2,
.shortcut-card h2 {
margin: 0;
color: #0f172a;
font-size: 26px;
line-height: 34px;
font-weight: 900;
letter-spacing: -0.035em;
}
/* BUTTONS */
.btn-main,
.btn-secondary {
min-height: 44px;
border: none;
border-radius: 12px;
padding: 0 18px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
font-size: 13px;
font-weight: 900;
cursor: pointer;
transition: transform 0.18s ease, box-shadow 0.18s ease, opacity 0.18s ease;
}
.btn-main {
background: linear-gradient(135deg, #071b63, #12358f);
color: #ffffff;
box-shadow: 0 12px 24px rgba(7, 27, 99, 0.20);
}
.btn-secondary {
background: #f1f5f9;
color: #334155;
}
.btn-main:hover:not(:disabled),
.btn-secondary:hover:not(:disabled) {
transform: translateY(-1px);
}
.btn-main:disabled,
.btn-secondary:disabled {
opacity: 0.55;
cursor: not-allowed;
box-shadow: none;
}
/* SETTINGS */
.settings-grid {
display: grid;
gap: 18px;
}
.setting-card {
display: grid;
grid-template-columns: 1fr 420px;
gap: 22px;
align-items: center;
padding: 20px;
border-radius: 20px;
background: #f8fafc;
border: 1px solid #e5e7eb;
}
.setting-info {
display: flex;
align-items: flex-start;
gap: 14px;
}
.setting-icon {
width: 46px;
height: 46px;
flex: 0 0 auto;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
background: #f1f5ff;
color: #071b63;
}
.setting-icon .material-icons {
font-size: 24px;
}
.setting-info h3,
.asset-body h3 {
margin: 0 0 6px 0;
color: #0f172a;
font-size: 18px;
line-height: 25px;
font-weight: 900;
}
.setting-info p,
.asset-body p,
.shortcut-card p {
margin: 0;
color: #64748b;
font-size: 14px;
line-height: 23px;
}
.setting-control {
display: grid;
gap: 10px;
}
.form-control {
width: 100%;
height: 46px;
padding: 0 14px;
border-radius: 12px;
border: 1px solid #d7deea;
background: #ffffff;
color: #0f172a;
font-size: 15px;
outline: none;
transition: border-color 0.18s ease, box-shadow 0.18s ease;
box-sizing: border-box;
}
.form-control:focus {
border-color: #071b63;
box-shadow: 0 0 0 4px rgba(7, 27, 99, 0.10);
}
.confirm-check {
display: flex;
align-items: center;
gap: 8px;
color: #64748b;
font-size: 13px;
font-weight: 800;
cursor: pointer;
}
.confirm-check input {
width: 18px;
height: 18px;
}
/* ASSETS */
.assets-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 18px;
}
.asset-card {
overflow: hidden;
border-radius: 22px;
background: #f8fafc;
border: 1px solid #e5e7eb;
}
.asset-preview {
height: 150px;
display: flex;
align-items: center;
justify-content: center;
background:
radial-gradient(circle at top right, rgba(7, 27, 99, 0.12), transparent 34%),
#ffffff;
border-bottom: 1px solid #e5e7eb;
}
.asset-preview img {
max-width: 76%;
max-height: 90px;
object-fit: contain;
}
.favicon-preview img {
max-width: 64px;
max-height: 64px;
}
.background-preview img {
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
object-fit: cover;
}
.asset-body {
padding: 20px;
}
.file-label {
min-height: 42px;
margin-top: 16px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 0 14px;
border-radius: 12px;
background: #f1f5ff;
color: #071b63;
font-size: 13px;
font-weight: 900;
cursor: pointer;
border: 1px solid #dbe6ff;
}
.file-label input {
display: none;
}
.file-label .material-icons {
font-size: 20px;
}
.asset-btn {
width: 100%;
margin-top: 14px;
}
/* SHORTCUTS */
.shortcut-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 22px;
}
.shortcut-card {
display: grid;
gap: 16px;
}
.shortcut-icon {
width: 54px;
height: 54px;
border-radius: 18px;
background: #f1f5ff;
color: #071b63;
display: flex;
align-items: center;
justify-content: center;
}
.shortcut-icon .material-icons {
font-size: 28px;
}
.shortcut-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
/* RESPONSIVE */
@media (max-width: 1100px) {
.setting-card {
grid-template-columns: 1fr;
}
.assets-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 760px) {
.admin-hero,
.admin-card {
padding: 22px;
}
.admin-hero h1 {
font-size: 28px;
line-height: 36px;
}
.shortcut-grid {
grid-template-columns: 1fr;
}
.shortcut-actions {
flex-direction: column;
}
.shortcut-actions button {
width: 100%;
}
}

View File

@ -0,0 +1,173 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { environment, FuncoesGlobais } from 'src/environments/environment';
import { GeralService } from '../services/geral.service';
@Component({
selector: 'app-edit-home',
templateUrl: './edit-home.component.html',
styleUrls: ['./edit-home.component.scss', './../app.component.scss']
})
export class EditHomeComponent implements OnInit {
LinkProjeto: string = environment.LinkProjeto;
Logo: File = null;
Favicon: File = null;
Background: File = null;
CaminhoLogo: string = environment.LinkProjeto + "/assets/logo_escrito.png";
CaminhoFavicon: string = "/favicon.ico";
CaminhoBackground: string = "/assets/background.jpg";
TituloAnterior: string = "";
Titulo: string = "";
APIUrl: string = "";
APIUrlAnterior: string = "";
APIUrlCheck: boolean = false;
ProjetoUrl: string = "";
ProjetoUrlAnterior: string = "";
ProjetoUrlCheck: boolean = false;
StringConfig: string = "";
constructor(
private router: Router,
private geralService: GeralService,
private funcoesGlobais: FuncoesGlobais,
) { }
ngOnInit() {
this.funcoesGlobais.CheckAdminRoute();
this.geralService.readEnderecos().then(resGeral => {
this.StringConfig = resGeral || "";
const partes = this.StringConfig.split(';');
this.ProjetoUrlAnterior = partes[0].split('=')[1] || "";
this.ProjetoUrl = this.ProjetoUrlAnterior;
this.APIUrlAnterior = partes[1].split('=')[1] || "";
this.APIUrl = this.APIUrlAnterior;
this.TituloAnterior = partes[2].split('=')[1] || "";
this.Titulo = this.TituloAnterior;
});
}
Visualizar(Janela: string) {
this.router.navigate(['/dashboard/admin/' + Janela + '/visualizar']);
}
Cadastrar(Janela: string) {
this.router.navigate(['/dashboard/admin/' + Janela]);
}
onFileChangedLogo(event) {
this.CarregarPreviewArquivo(event, 'logo');
}
onFileChangedFavicon(event) {
this.CarregarPreviewArquivo(event, 'favicon');
}
onFileChangedBackground(event) {
this.CarregarPreviewArquivo(event, 'background');
}
CarregarPreviewArquivo(event, tipo: string) {
if (event.target.files && event.target.files[0]) {
const arquivo = event.target.files[0];
if (tipo === 'logo') {
this.Logo = arquivo;
}
if (tipo === 'favicon') {
this.Favicon = arquivo;
}
if (tipo === 'background') {
this.Background = arquivo;
}
const reader = new FileReader();
reader.readAsDataURL(arquivo);
reader.onload = (event) => {
const result = event.target['result'].toString();
if (tipo === 'logo') {
this.CaminhoLogo = result;
}
if (tipo === 'favicon') {
this.CaminhoFavicon = result;
}
if (tipo === 'background') {
this.CaminhoBackground = result;
}
};
}
}
CopiarArquivos(Nome: string, Arquivo: File, Caminho: string) {
if (!Arquivo) {
alert("Selecione um arquivo antes de salvar.");
return;
}
const req = new XMLHttpRequest();
const formData = new FormData();
formData.append("nome", Nome);
formData.append("arquivo", Arquivo);
formData.append("caminho", Caminho);
req.open("POST", environment.ApiUrl + "/arquivos/copy");
req.send(formData);
req.onreadystatechange = () => {
if (req.readyState === 4) {
alert(Nome + " alterado com sucesso!");
if (Nome === 'logo_escrito.png') {
this.Logo = null;
}
if (Nome === 'favicon.ico') {
this.Favicon = null;
}
if (Nome === 'background.jpg') {
this.Background = null;
}
}
};
}
Editar(Item: string) {
this.StringConfig = `Dominio=${this.ProjetoUrl};API=${this.APIUrl};Título=${this.Titulo}`;
const form = {
content: this.StringConfig
};
this.geralService.writeEnderecos(form).then(resEdit => {
if (resEdit == true) {
alert(Item + " alterado com sucesso!");
this.ProjetoUrlAnterior = this.ProjetoUrl;
this.APIUrlAnterior = this.APIUrl;
this.TituloAnterior = this.Titulo;
this.ProjetoUrlCheck = false;
this.APIUrlCheck = false;
}
});
}
}

View File

@ -0,0 +1,121 @@
<div class="home-page">
<!-- HERO / SLIDER -->
<section class="hero-section">
<div class="hero-background-glow"></div>
<div class="hero-content">
<div class="hero-text">
<span class="eyebrow">Zendion, INC.</span>
<h1>
Soluções digitais para negócios mais inteligentes.
</h1>
<p>
Sistemas, automações e tecnologia sob medida para simplificar processos,
conectar informações e impulsionar operações.
</p>
<div class="hero-actions">
<a href="/home#sistemas" class="btn-hero-primary">
Conhecer sistemas
</a>
<a href="/home#noticias" class="btn-hero-secondary">
Ver novidades
</a>
</div>
</div>
<div class="hero-slider-card">
<swiper [config]="config" class="hero-swiper">
<div class="swiper-wrapper">
<div class="swiper-slide" *ngFor="let Slide of ListaDeSlides; let i = index">
<img
[src]="LinkProjeto + '/imagens/slides/slide_' + Slide.idslides + '.jpg'"
class="slider-image"
alt="Slide Zendion">
</div>
</div>
<div class="swiper-pagination"></div>
<div class="swiper-button-next" id="slideNext"></div>
<div class="swiper-button-prev" id="slidePreview"></div>
</swiper>
</div>
</div>
</section>
<!-- SISTEMAS -->
<section class="systems-section" id="sistemas">
<div class="section-header">
<span class="eyebrow">Nossas soluções</span>
<h2>Sistemas</h2>
<p>
Conheça os sistemas disponíveis na plataforma Zendion.
</p>
</div>
<div class="systems-grid">
<div class="system-card" *ngFor="let Sistema of ListaDeSistemas; let i = index;">
<div class="system-icon-wrapper">
<img
[src]="LinkProjeto + '/sistemas/' + Sistema.idsistemas + '/icone.png'"
class="system-icon"
alt="Ícone do sistema">
</div>
<h3>{{ Sistema.nome }}</h3>
<p>{{ Sistema.descricao }}</p>
<button
type="button"
class="system-btn"
(click)="AbrirDetalhesSistema(Sistema)">
Ver detalhes
</button>
</div>
</div>
</section>
<!-- NOTÍCIAS -->
<section class="news-section" id="noticias" *ngIf="ListaDeNoticias && ListaDeNoticias.length > 0">
<div class="section-header">
<span class="eyebrow">Atualizações</span>
<h2>Notícias</h2>
<p>
Acompanhe novidades, comunicados e informações da Zendion.
</p>
</div>
<div class="news-list">
<article
class="news-card"
*ngFor="let Noticia of ListaDeNoticias; let i = index"
[ngClass]="i % 2 == 0 ? 'normal' : 'reverse'">
<div class="news-image-wrapper">
<img
[src]="LinkProjeto + '/imagens/noticias/noticia_' + Noticia.idnoticias + '.jpg'"
class="news-image"
alt="Imagem da notícia">
</div>
<div class="news-content">
<span class="news-label">Notícia</span>
<h3>{{ Noticia.titulo }}</h3>
<div [innerHTML]="Noticia.descricao" class="news-description"></div>
</div>
</article>
</div>
</section>
</div>

View File

@ -0,0 +1,478 @@
.home-page {
min-height: 100vh;
background:
radial-gradient(circle at top left, rgba(7, 27, 99, 0.18), transparent 30%),
linear-gradient(135deg, #f4f6fb 0%, #eef3ff 45%, #ffffff 100%);
color: #0f172a;
}
/* =========================
HERO
========================= */
.hero-section {
position: relative;
overflow: hidden;
padding: 64px 22px 52px 22px;
}
.hero-background-glow {
position: absolute;
width: 420px;
height: 420px;
right: -140px;
top: -140px;
border-radius: 999px;
background: rgba(7, 27, 99, 0.16);
filter: blur(8px);
}
.hero-content {
position: relative;
z-index: 1;
width: 100%;
max-width: 1180px;
margin: 0 auto;
display: grid;
grid-template-columns: 0.9fr 1.1fr;
align-items: center;
gap: 42px;
}
.hero-text {
padding: 18px 0;
}
.eyebrow {
display: inline-block;
margin-bottom: 12px;
color: #071b63;
font-size: 12px;
font-weight: 800;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.hero-text h1 {
margin: 0 0 18px 0;
max-width: 520px;
color: #0f172a;
font-size: 48px;
line-height: 58px;
font-weight: 900;
letter-spacing: -0.045em;
}
.hero-text p {
margin: 0;
max-width: 520px;
color: #475569;
font-size: 17px;
line-height: 29px;
}
.hero-actions {
display: flex;
flex-wrap: wrap;
gap: 14px;
margin-top: 30px;
}
.btn-hero-primary,
.btn-hero-secondary {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 46px;
padding: 0 22px;
border-radius: 12px;
font-size: 14px;
font-weight: 800;
text-decoration: none;
transition: transform 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease;
}
.btn-hero-primary {
background: linear-gradient(135deg, #071b63, #12358f);
color: #ffffff;
box-shadow: 0 14px 28px rgba(7, 27, 99, 0.24);
}
.btn-hero-secondary {
background: #ffffff;
color: #071b63;
border: 1px solid #d7deea;
}
.btn-hero-primary:hover,
.btn-hero-secondary:hover {
transform: translateY(-1px);
text-decoration: none;
}
.hero-slider-card {
position: relative;
background: #ffffff;
border-radius: 24px;
padding: 14px;
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.16);
border: 1px solid rgba(148, 163, 184, 0.22);
overflow: hidden;
}
.hero-swiper {
width: 100%;
height: 430px;
border-radius: 18px;
overflow: hidden;
display: block;
}
.slider-image {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* Swiper buttons */
:host ::ng-deep .swiper-button-next,
:host ::ng-deep .swiper-button-prev {
color: #ffffff;
width: 42px;
height: 42px;
border-radius: 999px;
background: rgba(7, 27, 99, 0.78);
backdrop-filter: blur(6px);
}
:host ::ng-deep .swiper-button-next::after,
:host ::ng-deep .swiper-button-prev::after {
font-size: 16px;
font-weight: 800;
}
:host ::ng-deep .swiper-pagination-bullet {
background: #ffffff;
opacity: 0.65;
}
:host ::ng-deep .swiper-pagination-bullet-active {
background: #071b63;
opacity: 1;
}
/* =========================
SECTIONS
========================= */
.systems-section,
.news-section {
width: 100%;
max-width: 1180px;
margin: 0 auto;
padding: 48px 22px;
}
.section-header {
text-align: center;
margin-bottom: 34px;
}
.section-header h2 {
margin: 0 0 10px 0;
color: #0f172a;
font-size: 34px;
line-height: 42px;
font-weight: 900;
letter-spacing: -0.035em;
}
.section-header p {
margin: 0 auto;
max-width: 620px;
color: #64748b;
font-size: 16px;
line-height: 26px;
}
/* =========================
SISTEMAS
========================= */
.systems-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 22px;
}
.system-card {
position: relative;
overflow: hidden;
min-height: 310px;
display: flex;
flex-direction: column;
align-items: flex-start;
padding: 28px;
border-radius: 22px;
background: #ffffff;
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
border: 1px solid rgba(148, 163, 184, 0.22);
transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.system-card::before {
content: "";
position: absolute;
width: 180px;
height: 180px;
right: -80px;
top: -80px;
border-radius: 999px;
background: rgba(7, 27, 99, 0.08);
}
.system-card:hover {
transform: translateY(-4px);
box-shadow: 0 24px 60px rgba(15, 23, 42, 0.16);
border-color: rgba(7, 27, 99, 0.22);
}
.system-icon-wrapper {
width: 74px;
height: 74px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 22px;
border-radius: 18px;
background: #f1f5ff;
border: 1px solid #dbe6ff;
}
.system-icon {
width: 48px;
height: 48px;
object-fit: contain;
}
.system-card h3 {
margin: 0 0 12px 0;
color: #0f172a;
font-size: 21px;
line-height: 28px;
font-weight: 850;
}
.system-card p {
flex: 1;
margin: 0 0 24px 0;
color: #64748b;
font-size: 14px;
line-height: 23px;
}
.system-btn {
width: 100%;
min-height: 44px;
border: none;
border-radius: 12px;
background: linear-gradient(135deg, #071b63, #12358f);
color: #ffffff;
font-size: 14px;
font-weight: 800;
cursor: pointer;
box-shadow: 0 12px 24px rgba(7, 27, 99, 0.20);
transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.system-btn:hover {
transform: translateY(-1px);
box-shadow: 0 16px 30px rgba(7, 27, 99, 0.26);
}
/* =========================
NOTÍCIAS
========================= */
.news-list {
display: grid;
gap: 28px;
}
.news-card {
display: grid;
grid-template-columns: 0.92fr 1.08fr;
align-items: stretch;
gap: 0;
overflow: hidden;
border-radius: 24px;
background: #ffffff;
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
border: 1px solid rgba(148, 163, 184, 0.22);
}
.news-card.reverse {
grid-template-columns: 1.08fr 0.92fr;
}
.news-card.reverse .news-image-wrapper {
order: 2;
}
.news-card.reverse .news-content {
order: 1;
}
.news-image-wrapper {
min-height: 320px;
background: #e5e7eb;
}
.news-image {
width: 100%;
height: 100%;
min-height: 320px;
object-fit: cover;
display: block;
}
.news-content {
padding: 36px;
display: flex;
flex-direction: column;
justify-content: center;
}
.news-label {
display: inline-block;
width: fit-content;
margin-bottom: 14px;
padding: 7px 11px;
border-radius: 999px;
background: #f1f5ff;
color: #071b63;
font-size: 12px;
line-height: 16px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.news-content h3 {
margin: 0 0 16px 0;
color: #0f172a;
font-size: 28px;
line-height: 36px;
font-weight: 900;
letter-spacing: -0.035em;
}
.news-description {
color: #475569;
font-size: 15px;
line-height: 25px;
}
.news-description p {
margin-top: 0;
}
.news-description p:last-child {
margin-bottom: 0;
}
/* =========================
RESPONSIVO
========================= */
@media (max-width: 980px) {
.hero-content {
grid-template-columns: 1fr;
}
.hero-text {
text-align: center;
}
.hero-text h1,
.hero-text p {
margin-left: auto;
margin-right: auto;
}
.hero-actions {
justify-content: center;
}
.systems-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.news-card,
.news-card.reverse {
grid-template-columns: 1fr;
}
.news-card.reverse .news-image-wrapper,
.news-card.reverse .news-content {
order: unset;
}
}
@media (max-width: 680px) {
.hero-section {
padding: 42px 14px 34px 14px;
}
.hero-text h1 {
font-size: 34px;
line-height: 42px;
}
.hero-text p {
font-size: 15px;
line-height: 25px;
}
.hero-swiper {
height: 280px;
}
.systems-section,
.news-section {
padding: 38px 14px;
}
.systems-grid {
grid-template-columns: 1fr;
}
.section-header h2 {
font-size: 28px;
line-height: 36px;
}
.news-content {
padding: 26px;
}
.news-content h3 {
font-size: 24px;
line-height: 32px;
}
.news-image,
.news-image-wrapper {
min-height: 230px;
}
.hero-actions {
flex-direction: column;
}
.btn-hero-primary,
.btn-hero-secondary {
width: 100%;
}
}

View File

@ -0,0 +1,67 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Sistemas } from '../models/sistemasModel';
import { SistemasService } from '../services/sistemas.service';
import { environment } from 'src/environments/environment';
import { Slides } from '../models/sliderModel';
import { SliderService } from '../services/slider.service';
import { Noticias } from '../models/noticiasModel';
import { NoticiasService } from '../services/noticias.service';
import { SwiperOptions } from 'swiper';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
config: SwiperOptions = {
pagination: {
el: '.swiper-pagination',
clickable: true
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
},
spaceBetween: 24,
loop: true,
speed: 700,
autoplay: {
delay: 8000,
disableOnInteraction: false
}
};
ListaDeSlides: Slides[] = [];
ListaDeSistemas: Sistemas[] = [];
ListaDeNoticias: Noticias[] = [];
LinkProjeto: string = environment.LinkProjeto;
constructor(
private sistemasService: SistemasService,
private router: Router,
private sliderService: SliderService,
private noticiasService: NoticiasService
) { }
ngOnInit() {
this.sliderService.getSlidesEn().then(resSlides => {
this.ListaDeSlides = resSlides || [];
});
this.sistemasService.getSistemasEn().then(resSistemas => {
this.ListaDeSistemas = resSistemas || [];
});
this.noticiasService.getNoticiasEn().then(resNoticias => {
this.ListaDeNoticias = resNoticias || [];
});
}
AbrirDetalhesSistema(Sistema: Sistemas) {
this.router.navigate(['/detalhes/' + Sistema.idsistemas]);
}
}

View File

@ -0,0 +1,160 @@
<div class="login-page">
<div class="login-shell">
<div class="login-brand-panel">
<div class="brand-content">
<img src="assets/logo_escrito.png" alt="Zendion, INC." class="brand-logo">
<h1>Bem-vindo de volta</h1>
<p>
Acesse sua conta para gerenciar seus sistemas, licenças e recursos da Zendion, INC.
</p>
<div class="brand-badges">
<span>Acesso seguro</span>
<span>Gestão centralizada</span>
<span>Suporte Zendion</span>
</div>
</div>
</div>
<div class="login-card">
<div class="login-header">
<span class="eyebrow">
<ng-container *ngIf="JanelaModo.Visualizar">Login</ng-container>
<ng-container *ngIf="JanelaModo.Cadastro">Recuperação</ng-container>
<ng-container *ngIf="JanelaModo.Editar">Nova senha</ng-container>
</span>
<h2>{{ TituloJanela }}</h2>
<p *ngIf="JanelaModo.Visualizar">
Entre com seu usuário e senha para acessar sua conta.
</p>
<p *ngIf="JanelaModo.Cadastro">
Informe seu usuário e e-mail para receber as instruções de redefinição de senha.
</p>
<p *ngIf="JanelaModo.Editar">
Crie uma nova senha para sua conta. Escolha uma senha segura e fácil de lembrar.
</p>
</div>
<div class="login-body">
<!-- Modo login -->
<div *ngIf="JanelaModo.Visualizar" class="login-form">
<div class="form-group">
<label for="usuario">Usuário</label>
<input
id="usuario"
type="text"
class="form-control"
placeholder="Digite seu usuário"
[(ngModel)]="LoginModel.usuario"
[ngClass]="LoginError ? 'Danger' : 'Normal'">
</div>
<div class="form-group">
<label for="senha">Senha</label>
<input
id="senha"
type="password"
class="form-control"
placeholder="Digite sua senha"
[(ngModel)]="LoginModel.senha"
[ngClass]="LoginError ? 'Danger' : 'Normal'">
</div>
</div>
<!-- Modo esqueci senha -->
<div *ngIf="JanelaModo.Cadastro" class="login-form">
<div class="form-group">
<label for="usuario-recuperacao">Usuário</label>
<input
id="usuario-recuperacao"
type="text"
class="form-control"
placeholder="Digite seu usuário"
[(ngModel)]="LoginModel.usuario"
[ngClass]="LoginError ? 'Danger' : 'Normal'">
</div>
<div class="form-group">
<label for="email-recuperacao">E-mail</label>
<input
id="email-recuperacao"
type="email"
class="form-control"
placeholder="seuemail@exemplo.com"
[(ngModel)]="LoginModel.email"
[ngClass]="LoginError ? 'Danger' : 'Normal'">
</div>
</div>
<!-- Modo trocar senha -->
<div *ngIf="JanelaModo.Editar" class="login-form">
<div class="form-group">
<label for="nova-senha">Nova senha</label>
<input
id="nova-senha"
type="password"
class="form-control"
placeholder="Digite sua nova senha"
[(ngModel)]="LoginModel.senha"
[ngClass]="LoginError ? 'Danger' : 'Normal'">
</div>
<div class="form-group">
<label for="confirmar-senha">Confirmar senha</label>
<input
id="confirmar-senha"
type="password"
class="form-control"
placeholder="Confirme sua nova senha"
[(ngModel)]="LoginModel.novasenha"
[ngClass]="LoginError ? 'Danger' : 'Normal'">
</div>
<small class="field-hint">
Use uma senha que você não utiliza em outros serviços.
</small>
</div>
<div *ngIf="MenssagemForm != ''" class="login-message">
<small>{{ MenssagemForm }}</small>
</div>
<button
type="submit"
class="btn-login"
(click)="btnEntrarClick()"
[disabled]="DesabilitaBotao">
{{ BotaoText }}
</button>
<div class="login-footer">
<a *ngIf="JanelaModo.Visualizar" [routerLink]="'/login/esqueci'">
Esqueci a senha
</a>
<a *ngIf="JanelaModo.Visualizar" [routerLink]="'/criarconta'">
Criar conta
</a>
<a *ngIf="!JanelaModo.Visualizar" [routerLink]="'/login'">
Voltar ao login
</a>
<a [routerLink]="'/home'">
Voltar ao site
</a>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,337 @@
.login-page {
min-height: calc(100vh - 80px);
display: flex;
align-items: center;
justify-content: center;
padding: 48px 18px;
background:
radial-gradient(circle at top left, rgba(7, 27, 99, 0.14), transparent 34%),
linear-gradient(135deg, #f4f6fb 0%, #eef3ff 45%, #ffffff 100%);
}
.login-shell {
width: 100%;
max-width: 980px;
display: grid;
grid-template-columns: 0.95fr 1.05fr;
background: #ffffff;
border-radius: 24px;
overflow: hidden;
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.16);
border: 1px solid rgba(148, 163, 184, 0.22);
}
.login-brand-panel {
position: relative;
min-height: 560px;
display: flex;
align-items: flex-end;
padding: 42px;
background:
linear-gradient(145deg, rgba(7, 27, 99, 0.96), rgba(18, 47, 132, 0.94));
color: #ffffff;
overflow: hidden;
}
.login-brand-panel::before {
content: "";
position: absolute;
width: 340px;
height: 340px;
right: -120px;
top: -100px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.12);
}
.login-brand-panel::after {
content: "";
position: absolute;
width: 220px;
height: 220px;
left: -80px;
bottom: -70px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.08);
}
.brand-content {
position: relative;
z-index: 1;
}
.brand-logo {
height: 54px;
max-width: 240px;
object-fit: contain;
display: block;
margin-bottom: 34px;
filter: brightness(0) invert(1);
}
.brand-content h1 {
margin: 0 0 14px 0;
font-size: 34px;
line-height: 42px;
font-weight: 800;
letter-spacing: -0.03em;
}
.brand-content p {
max-width: 390px;
margin: 0;
color: #dbeafe;
font-size: 16px;
line-height: 27px;
}
.brand-badges {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 28px;
}
.brand-badges span {
display: inline-flex;
align-items: center;
padding: 8px 12px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.13);
color: #ffffff;
font-size: 12px;
font-weight: 700;
backdrop-filter: blur(8px);
}
.login-card {
display: flex;
flex-direction: column;
justify-content: center;
padding: 44px 48px;
}
.login-header {
margin-bottom: 28px;
}
.eyebrow {
display: inline-block;
margin-bottom: 10px;
color: #071b63;
font-size: 12px;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.login-header h2 {
margin: 0 0 10px 0;
color: #0f172a;
font-size: 30px;
line-height: 38px;
font-weight: 800;
letter-spacing: -0.03em;
}
.login-header p {
margin: 0;
color: #64748b;
font-size: 15px;
line-height: 24px;
}
.login-body {
width: 100%;
}
.login-form {
display: grid;
gap: 16px;
}
.form-group {
text-align: left;
}
.form-group label {
display: block;
margin-bottom: 7px;
color: #1e293b;
font-size: 14px;
font-weight: 700;
}
.form-control {
width: 100%;
height: 46px;
padding: 0 14px;
border-radius: 12px;
border: 1px solid #d7deea;
background: #ffffff;
color: #0f172a;
font-size: 15px;
outline: none;
transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
box-sizing: border-box;
}
.form-control::placeholder {
color: #94a3b8;
}
.form-control:focus {
border-color: #071b63;
box-shadow: 0 0 0 4px rgba(7, 27, 99, 0.10);
background-color: #ffffff;
}
.field-hint {
display: block;
margin-top: 2px;
color: #64748b;
font-size: 12px;
line-height: 18px;
text-align: left;
}
.btn-login {
width: 100%;
height: 48px;
margin-top: 22px;
border: none;
border-radius: 12px;
background: linear-gradient(135deg, #071b63, #12358f);
color: #ffffff;
font-size: 15px;
font-weight: 800;
cursor: pointer;
box-shadow: 0 12px 24px rgba(7, 27, 99, 0.22);
transition: transform 0.18s ease, box-shadow 0.18s ease, opacity 0.18s ease;
}
.btn-login:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 16px 30px rgba(7, 27, 99, 0.28);
}
.btn-login:active:not(:disabled) {
transform: translateY(0);
}
.btn-login:disabled {
opacity: 0.55;
cursor: not-allowed;
box-shadow: none;
}
.login-footer {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px 16px;
margin-top: 26px;
padding-top: 22px;
border-top: 1px solid #e5e7eb;
}
.login-footer a {
color: #071b63;
font-size: 14px;
font-weight: 700;
text-decoration: none;
}
.login-footer a:hover {
text-decoration: underline;
}
.login-message {
margin-top: 18px;
padding: 12px 14px;
border-radius: 12px;
background: #fef2f2;
border: 1px solid #fecaca;
text-align: left;
}
.login-message small {
display: block;
color: #b91c1c;
font-size: 13px;
line-height: 20px;
font-weight: 600;
}
/* Estados de erro herdados do seu TS */
.Danger {
border-color: #ef4444 !important;
background-color: #fff7f7;
}
.Danger:focus {
border-color: #ef4444 !important;
box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.12);
}
.Normal {
border-color: #d7deea;
}
@media (max-width: 860px) {
.login-shell {
grid-template-columns: 1fr;
max-width: 560px;
}
.login-brand-panel {
min-height: auto;
padding: 30px;
align-items: flex-start;
}
.brand-logo {
height: 44px;
margin-bottom: 22px;
}
.brand-content h1 {
font-size: 28px;
line-height: 36px;
}
.login-card {
padding: 34px 26px;
}
}
@media (max-width: 480px) {
.login-page {
padding: 22px 12px;
}
.login-shell {
border-radius: 18px;
}
.login-brand-panel {
padding: 24px;
}
.brand-badges {
display: none;
}
.login-card {
padding: 28px 20px;
}
.login-header h2 {
font-size: 25px;
line-height: 32px;
}
.login-footer {
grid-template-columns: 1fr;
text-align: center;
}
}

View File

@ -0,0 +1,359 @@
import { Component, OnInit } from '@angular/core';
import { UsuariosService } from '../services/usuarios.service';
import { Login, Usuarios, Email, RecuperarSenha } from '../models/usuariosModel';
import { Router, ActivatedRoute } from '@angular/router';
import { Modos } from '../models/variaveisModel';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
LoginModel: Login = new Login;
LoginError = false;
MenssagemForm: string;
BotaoText: string;
TituloJanela: string;
JanelaModo: Modos = new Modos();
ListaDeUsuarios: Usuarios[];
FormEmail: Email = new Email();
FormTrocaSenha: RecuperarSenha = new RecuperarSenha();
DesabilitaBotao: boolean = false;
ListaTrocaSenha: RecuperarSenha[];
LinkProjeto: string = environment.LinkProjeto;
constructor(
private usuariosSerive: UsuariosService,
private router: Router,
private route: ActivatedRoute,
) { }
ngOnInit() {
if (this.router.url.includes('esqueci')) {
this.usuariosSerive.getUsuarios().then(resUsuarios => {
this.ListaDeUsuarios = resUsuarios;
})
this.usuariosSerive.getTrocasenha().then(resTrocasenha => {
this.ListaTrocaSenha = resTrocasenha;
})
this.BotaoText = "Enviar e-mail";
this.TituloJanela = "Esqueci a senha";
this.JanelaModo.Cadastro = true;
}
else if (this.router.url.includes('trocar')) {
this.usuariosSerive.getTrocasenhaByTag(this.route.snapshot.params['tag']).then(resTag => {
if (resTag.length > 0) {
this.FormTrocaSenha = resTag[0];
this.usuariosSerive.setExpirado(resTag[0]).then(resExpirado => {
console.log("certo");
})
}
else {
this.MenssagemForm = "Este link de redefinição de senha expirou";
this.DesabilitaBotao = true;
}
})
this.BotaoText = "Alterar senha";
this.TituloJanela = "Digite a nova senha";
this.JanelaModo.Editar = true;
}
else {
this.BotaoText = "Entrar";
this.TituloJanela = "Login";
this.JanelaModo.Visualizar = true;
}
}
btnEntrarClick() {
if (this.BotaoText == "Voltar") {
this.router.navigate(['login']);
return;
}
if (this.JanelaModo.Visualizar) { // entrar
this.usuariosSerive.AuthUser(this.LoginModel).then(resLogin => {
if (resLogin['response'].length > 0) {
if (resLogin['response'][0]['habilitado'] == 0) {
this.MenssagemForm = "Usuário ainda não ativado! Acesse a caixa de entrada de seu e-mail e ative sua conta para entrar!";
this.LoginError = true;
return;
}
localStorage.setItem("UserID", resLogin['response'][0]['idusuarios']);
localStorage.setItem("UserName", resLogin['response'][0]['nome']);
localStorage.setItem("Role", resLogin['response'][0]['idtiposdeusuario']);
localStorage.setItem("Session", "1");
if (localStorage.getItem("Role") == "1" || localStorage.getItem("Role") == "2") {
if (localStorage.getItem("LastRouteBeforeLogin")) {
this.router.navigate([localStorage.getItem("LastRouteBeforeLogin")]);
localStorage.removeItem("LastRouteBeforeLogin");
}
else {
this.router.navigate(['dashboard/cliente']);
}
}
}
else {
this.MenssagemForm = "Usuário ou senha incorretos";
this.LoginError = true;
}
})
}
else if (this.JanelaModo.Cadastro) { // enviar email
let EmailCorreto = false;
let UsuarioCorreto = false;
let idusuariosU: number;
let idusuariosE: number;
let nomeUsuario: string;
for (let i = 0; i < this.ListaDeUsuarios.length; i++) {
if (this.LoginModel.email == this.ListaDeUsuarios[i].email) {
idusuariosU = this.ListaDeUsuarios[i].idusuarios;
EmailCorreto = true;
nomeUsuario = this.ListaDeUsuarios[i].nome;
}
if (this.LoginModel.usuario == this.ListaDeUsuarios[i].usuario) {
idusuariosE = this.ListaDeUsuarios[i].idusuarios;
UsuarioCorreto = true;
}
}
if (EmailCorreto && UsuarioCorreto && (idusuariosE == idusuariosU)) {
for (let i = 0; i < this.ListaTrocaSenha.length; i++) {
if (this.ListaTrocaSenha[i].idusuarios == idusuariosE) {
this.MenssagemForm = "Este usuário já tem um link de redefinição de senha pendente";
this.BotaoText = "Voltar";
return;
}
}
this.FormTrocaSenha.idusuarios = idusuariosE;
this.FormEmail.Para = this.LoginModel.email;
this.FormEmail.Assunto = "Redefinição de senha Zendion, Inc.";
this.FormEmail.Conteudo = this.MensagemEmail(nomeUsuario);
this.usuariosSerive.insertTrocasenha(this.FormTrocaSenha).then(resTrocasenha => {
if (resTrocasenha['status'] == 200) {
this.usuariosSerive.EnviarEmail(this.FormEmail).then(resEmail => {
if (resEmail != null && resEmail['sucesso'] == true) {
this.MenssagemForm = "Link de redefinição de senha enviado com sucesso!";
this.BotaoText = "Voltar";
}
else {
this.MenssagemForm = "Erro ao enviar e-mail";
this.LoginError = true;
this.BotaoText = "Voltar";
}
})
}
else {
this.MenssagemForm = "Erro ao gerar link de redefinição de senha";
this.LoginError = true;
this.BotaoText = "Voltar";
}
})
}
else {
this.LoginError = true;
if (!EmailCorreto)
this.MenssagemForm = "E-mail incorreto";
if (!UsuarioCorreto)
this.MenssagemForm = "Usuário incorreto";
if (!EmailCorreto && !UsuarioCorreto)
this.MenssagemForm = "Usuário e E-mail incorretos";
if (idusuariosE != idusuariosU)
this.MenssagemForm = "Usuário ou E-mail incorreto";
}
}
else if (this.JanelaModo.Editar) { // inserir nova senha
if (this.LoginModel.senha != this.LoginModel.novasenha) {
this.LoginError = true;
this.MenssagemForm = "As senhas digitadas não são iguais";
return;
}
let formUsuario: Usuarios = new Usuarios();
formUsuario.idusuarios = this.FormTrocaSenha.idusuarios;
formUsuario.senha = this.LoginModel.senha;
this.usuariosSerive.trocaSenha(formUsuario).then(resUpdate => {
if (resUpdate['status'] == 200) {
this.MenssagemForm = "Senha alterada com sucesso";
}
else {
this.MenssagemForm = "Erro ao alterar senha";
}
this.BotaoText = "Voltar";
})
}
}
MensagemEmail(nomeUsuario: string) {
let Mensagem: string;
const nome = nomeUsuario || 'usuário';
// Melhor que Math.random para token simples no frontend
const array = new Uint32Array(4);
window.crypto.getRandomValues(array);
const Tag = Array.from(array)
.map(x => x.toString(16))
.join('');
this.FormTrocaSenha.tag = Tag;
const linkTrocaSenha = `${this.LinkProjeto}/login/trocar/${Tag}`;
Mensagem = `
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Redefinição de senha Zendion, INC.</title>
</head>
<body style="margin:0; padding:0; background-color:#f4f6fb; font-family:Arial, Helvetica, sans-serif; color:#1f2937;">
<table width="100%" cellpadding="0" cellspacing="0" style="background-color:#f4f6fb; padding:32px 12px;">
<tr>
<td align="center">
<table width="100%" cellpadding="0" cellspacing="0" style="max-width:620px; background-color:#ffffff; border-radius:14px; overflow:hidden; box-shadow:0 6px 24px rgba(15, 23, 42, 0.08);">
<!-- Header -->
<tr>
<td style="background-color:#071b63; padding:26px 32px; text-align:center;">
<img src="${this.LinkProjeto}/assets/logo_escrito.png" alt="Zendion, INC." style="height:54px; max-width:220px; display:inline-block;">
</td>
</tr>
<!-- Title -->
<tr>
<td style="padding:34px 34px 10px 34px;">
<h1 style="margin:0; color:#111827; font-size:24px; line-height:32px; font-weight:700;">
Redefinição de senha
</h1>
</td>
</tr>
<!-- Greeting -->
<tr>
<td style="padding:8px 34px 0 34px;">
<p style="margin:0 0 18px 0; font-size:16px; line-height:26px; color:#374151;">
Olá, <strong>${nome}</strong>!
</p>
<p style="margin:0 0 18px 0; font-size:16px; line-height:26px; color:#374151;">
Recebemos uma solicitação para redefinir a senha da sua conta na <strong>Zendion, INC.</strong>.
</p>
<p style="margin:0 0 24px 0; font-size:16px; line-height:26px; color:#374151;">
Para criar uma nova senha, clique no botão abaixo:
</p>
</td>
</tr>
<!-- Button -->
<tr>
<td align="center" style="padding:8px 34px 30px 34px;">
<a href="${linkTrocaSenha}" target="_blank" style="display:inline-block; background-color:#071b63; color:#ffffff; text-decoration:none; font-size:16px; font-weight:700; padding:14px 28px; border-radius:8px;">
Redefinir minha senha
</a>
</td>
</tr>
<!-- Expiration notice -->
<tr>
<td style="padding:0 34px 28px 34px;">
<table width="100%" cellpadding="0" cellspacing="0" style="background-color:#fff7ed; border-left:4px solid #f97316; border-radius:8px;">
<tr>
<td style="padding:16px 18px;">
<p style="margin:0; font-size:14px; line-height:22px; color:#7c2d12;">
Este link é válido por <strong>24 horas</strong>. Após esse período, será necessário solicitar uma nova redefinição de senha.
</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- Fallback link -->
<tr>
<td style="padding:0 34px 30px 34px;">
<p style="margin:0 0 8px 0; font-size:13px; line-height:21px; color:#6b7280;">
Se o botão não funcionar, copie e cole este link no navegador:
</p>
<p style="margin:0; font-size:13px; line-height:21px; word-break:break-all;">
<a href="${linkTrocaSenha}" target="_blank" style="color:#071b63; text-decoration:underline;">
${linkTrocaSenha}
</a>
</p>
</td>
</tr>
<!-- Security note -->
<tr>
<td style="padding:0 34px 32px 34px;">
<table width="100%" cellpadding="0" cellspacing="0" style="background-color:#f8fafc; border-left:4px solid #071b63; border-radius:8px;">
<tr>
<td style="padding:16px 18px;">
<p style="margin:0; font-size:14px; line-height:22px; color:#4b5563;">
Se você não solicitou a redefinição de senha, ignore este e-mail. Sua senha atual continuará a mesma.
</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- Signature -->
<tr>
<td style="padding:0 34px 32px 34px;">
<p style="margin:0; font-size:15px; line-height:24px; color:#374151;">
Atenciosamente,<br>
<strong>Equipe Zendion, INC.</strong>
</p>
</td>
</tr>
<!-- Footer -->
<tr>
<td style="background-color:#071b63; padding:22px 34px; text-align:center;">
<p style="margin:0 0 8px 0; font-size:13px; line-height:20px; color:#ffffff;">
Zendion, INC.
</p>
<p style="margin:0; font-size:12px; line-height:20px; color:#cbd5e1;">
${environment.telefoneContato}<br>
${environment.emailContato}<br>
<a href="${this.LinkProjeto}" target="_blank" style="color:#ffffff; text-decoration:none;">
${this.LinkProjeto}
</a>
</p>
</td>
</tr>
</table>
<!-- Legal bottom -->
<table width="100%" cellpadding="0" cellspacing="0" style="max-width:620px;">
<tr>
<td style="padding:18px 12px 0 12px; text-align:center;">
<p style="margin:0; font-size:11px; line-height:18px; color:#9ca3af;">
Este é um e-mail automático. Por favor, não responda esta mensagem.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
`;
return Mensagem;
}
}

View File

@ -0,0 +1,27 @@
export class AgrobaseVersionFileModel {
id: number;
Diretorio: string;
Arquivo: string;
Extensao: string;
Versao: string;
ArquivoDownload: string;
TipoArquivo: TiposArquivos;
}
export enum TiposArquivos {
ModeloIA_StreetDetector = 0,
ModeloIA_WeedDetector = 1,
ParametrosModulo_ATU = 2,
ParametrosModulo_MVD = 3,
ParametrosModulo_SEN = 4,
ParametrosModulo_KNT = 5,
PinoutModulo_ATU = 6,
PinoutModulo_SEN = 7,
Script_StreetDetector = 8,
Script_WeedDetector = 9,
Script_GreenDetector = 10,
Script_MapLoad = 11,
Script_MapFollow = 12,
Script_GpsViewer = 13,
ArquivoModelo3D = 14,
}

View File

@ -0,0 +1,27 @@
export class BingoSorteiosModel {
idbingosorteios: number = 0;
descricao: string = "";
rodada: number = 1;
data_sorteio: string;
n_max: number = 75;
participantes_max: number = 0;
realizado: boolean = false;
Cartelas: BingoCartelasModel[];
VerCartelas: boolean = false;
}
export class BingoCartelasModel {
canhoto: number = 0;
idbingosorteios: number = 0;
numeros: string = "";
nome: string = "";
endereco: string = "";
telefone: string = "";
bingo: boolean = false;
cinquina1: boolean = false;
cinquina2: boolean = false;
cinquina3: boolean = false;
Valida: boolean = true;
Editando: boolean = false;
Acertos: number = 1;
}

View File

@ -0,0 +1,10 @@
export class MenuItem {
index: number;
texto: string;
link: string;
havesub: boolean;
dropicon: string;
icon: string;
issub: boolean;
sub: MenuItem[];
}

View File

@ -0,0 +1,15 @@
export class Noticias {
idnoticias: number;
titulo: string;
descricao: string;
habilitado: boolean;
link: string;
}
export class NoticiasList {
idnoticias: number;
titulo: string;
descricao: string;
habilitado: string;
link: string;
}

View File

@ -0,0 +1,92 @@
export class OrionLogsModel {
dataLog: Date;
nome: string;
coordenadas: string;
perfil: string;
log: string;
logObj: OrionLogsDetalhesModel;
}
export class OrionLogsDetalhesModel {
data: Date;
firmwareVersao: number;
robotNs: number;
modelo: string;
versao: string;
cpf: string;
usuario: string;
perfil: string;
camera: string;
servico: OrionLogServicoModel;
odometroTotal: number;
odometroConectado: number;
odometroMotores: number;
odometroMotoresParcial: number;
latitude: number;
longitude: number;
altitude: number;
tempoTotal: number;
tempoConectado: number;
sensores: OrionLogSensorModel[];
portaCOM: string;
baudRate: number;
conectado: boolean;
distancia: number;
HoraInicio: string;
HoraFim: string;
Duracao: string;
Coordenadas: string;
Localizacao: string;
}
export class OrionLogServicoModel {
Nome: string;
Cidade: string;
Responsavel: string;
Servico: string;
Path: string;
DataCadastro: Date;
}
export class OrionLogSensorModel {
sensor: number;
descricao: string;
minimo: number;
maximo: number;
media: number;
moda: number;
mediana: number;
lista: number[];
}
export class GeoCoordenadasResponseModel {
data: GeoCoordenadasModel[];
}
export class GeoCoordenadasModel {
latitude: number;
longitude: number;
type: string;
distance: number;
name: string;
number: string;
postal_code: string;
street: string;
confidence: number;
region: string;
region_code: string;
county: string;
lacality: string;
administrative_area: string;
neighborhood: string;
country: string;
country_code: string;
continent: string;
label: string;
}
export class LogAgrupado {
key: string;
value: OrionLogsDetalhesModel[];
}

View File

@ -0,0 +1,10 @@
export class ProdutosModel {
idprodutos: number;
idsistemas: number;
idtiposlicenca: number;
valor: number;
ativado: boolean;
descricao: string;
vendas: number;
compensados: number;
}

View File

@ -0,0 +1,90 @@
export class Sistemas {
idsistemas: number;
nome: string;
descricao: string;
versaoatual: string;
habilitado: boolean;
downloads: number;
idtiposdesistema: number;
idplataformas: number;
nome_install: string;
notasdeatualizacao: string;
idtiposlicenca: number;
insert_date: Date;
}
export class SistemasLista {
idsistemas: number;
nome: string;
descricao: string;
versaoatual: string;
habilitado: boolean;
downloads: number;
idtiposdesistema: number;
tiposdesistema: string;
idplataformas: number;
plataforma: string;
nome_install: string;
notasdeatualizacao: string;
idtiposlicenca: number;
licenca: string;
custo: number;
versoes: string[];
mdownloads: string[];
tiposlicenca: string;
insert_date: Date;
update_date: Date;
}
export class TiposDeLicenca {
idtiposlicenca: number;
descricao: string;
}
export class TiposDeSistema {
idtiposdesistema: number;
descricao: string;
}
export class Plataformas {
idplataformas: number;
descricao: string;
}
export class Download {
idsistemas: number;
idusuarios: number;
}
export class Chaves {
idchaves: number;
licenca: string;
data_criacao: Date;
data_validade: Date;
idusuarios: number;
usada: boolean;
idsistemas: number;
}
export class ListChaves {
idchaves: number;
Chave: string;
DataCriacao: Date;
DataValidade: Date;
Status: string;
Ativada: string;
}
export class SistemasDetalhes {
idsistemasdetalhes: number;
idsistemas: number;
descricao: string;
ordem: number;
habilitado: boolean;
imagem: string;
botao: string;
Arquivo: File;
}

View File

@ -0,0 +1,13 @@
export class Slides {
idslides: number;
descricao: string;
habilitado: boolean;
link: string;
}
export class SlidesList {
idslides: number;
descricao: string;
habilitado: string;
link: string;
}

View File

@ -0,0 +1,46 @@
export class Usuarios {
idusuarios: number;
nome: string;
usuario: string;
senha: string;
data_cadastro: Date;
cpf: string;
habilitado: boolean;
email: string;
idtiposdeusuario: number;
}
export class Login {
usuario: string;
senha: string;
email: string;
novasenha: string;
}
export class TiposDeUsuario {
idtiposdeusuario: string;
descricao: string;
}
export class Email {
Para: string;
Assunto: string;
Conteudo: string;
}
export class SistemasDoUsuario {
idsistemas: number;
nome: string;
descricao: string;
data_download: Date;
idusuarios: number;
licensasValidas: number;
gratis: boolean;
}
export class RecuperarSenha {
idtrocasenha: number;
idusuarios: number;
tag: string;
usada: boolean;
}

View File

@ -0,0 +1,10 @@
export class Variaveis {
LinkAPI: string;
LinkProjeto: string;
}
export class Modos {
Cadastro: boolean = false;
Editar: boolean = false;
Visualizar: boolean = false;
}

View File

@ -0,0 +1,237 @@
<div class="news-admin-page">
<!-- HEADER -->
<section class="admin-hero">
<div>
<span class="eyebrow">Backoffice</span>
<h1>{{ TituloJanela }}</h1>
<p>
Gerencie as notícias exibidas na página inicial do site.
</p>
</div>
<div class="admin-hero-actions" *ngIf="JanelaModo.Visualizar">
<button type="button" class="btn-main" (click)="NovaNoticia()">
<i class="material-icons">post_add</i>
Nova notícia
</button>
</div>
</section>
<!-- FORM CADASTRO / EDIÇÃO -->
<section class="admin-card form-panel" *ngIf="JanelaModo.Cadastro || JanelaModo.Editar">
<div class="panel-header">
<div>
<span class="eyebrow">Conteúdo editorial</span>
<h2>{{ JanelaModo.Cadastro ? 'Cadastrar notícia' : 'Editar notícia' }}</h2>
</div>
<label class="status-toggle">
<input type="checkbox" [(ngModel)]="FormNoticias.habilitado">
<span class="toggle-box">
<i class="material-icons">{{ FormNoticias.habilitado ? 'check_circle' : 'block' }}</i>
{{ FormNoticias.habilitado ? 'Habilitada' : 'Desabilitada' }}
</span>
</label>
</div>
<div class="form-grid">
<div class="form-group full">
<label>Título</label>
<input
type="text"
class="form-control"
placeholder="Título da notícia"
[(ngModel)]="FormNoticias.titulo">
</div>
<div class="form-group full">
<label>Descrição / conteúdo</label>
<textarea
[(ngModel)]="FormNoticias.descricao"
class="form-control textarea-control"
placeholder="Digite o conteúdo da notícia. Pode conter HTML simples, como <p>, <strong>, <br>...">
</textarea>
</div>
<div class="form-group full">
<label>Link</label>
<input
type="text"
class="form-control"
placeholder="Link opcional relacionado à notícia"
[(ngModel)]="FormNoticias.link">
</div>
<div class="form-group full">
<label>Imagem</label>
<label class="file-label">
<i class="material-icons">upload_file</i>
Selecionar imagem
<input type="file" (change)="onFileChangedIcone($event)" accept=".jpg,.jpeg,.png">
</label>
<small class="field-hint">
Recomendado: imagem horizontal em boa resolução, preferencialmente JPG.
</small>
</div>
</div>
<div class="news-preview-section" *ngIf="CaminhoImagem != null">
<span>Prévia da notícia</span>
<article class="news-preview-card">
<div class="news-preview-image">
<img [src]="CaminhoImagem" alt="Prévia da notícia">
</div>
<div class="news-preview-content">
<small>{{ FormNoticias.habilitado ? 'Publicada' : 'Oculta' }}</small>
<h3>{{ FormNoticias.titulo || 'Título da notícia' }}</h3>
<div
class="news-preview-description"
[innerHTML]="FormNoticias.descricao || 'A descrição da notícia aparecerá aqui.'">
</div>
</div>
</article>
</div>
<div *ngIf="MensagemForm" class="form-message">
<i class="material-icons">info</i>
<span>{{ MensagemForm }}</span>
</div>
<div class="form-actions">
<button type="button" class="btn-secondary" (click)="VoltarLista()">
Cancelar
</button>
<button type="submit" class="btn-main form-submit" (click)="btnCadastrarClick()">
<i class="material-icons">{{ JanelaModo.Cadastro ? 'save' : 'edit' }}</i>
{{ TextoBotao }}
</button>
</div>
</section>
<!-- VISUALIZAR -->
<section class="admin-list" *ngIf="JanelaModo.Visualizar">
<div class="summary-grid">
<div class="summary-card">
<div class="summary-icon">
<i class="material-icons">article</i>
</div>
<div>
<span>Total de notícias</span>
<strong>{{ ListaNoticias?.length || 0 }}</strong>
</div>
</div>
<div class="summary-card">
<div class="summary-icon active">
<i class="material-icons">visibility</i>
</div>
<div>
<span>Habilitadas</span>
<strong>{{ TotalHabilitadas }}</strong>
</div>
</div>
<div class="summary-card">
<div class="summary-icon blocked">
<i class="material-icons">visibility_off</i>
</div>
<div>
<span>Desabilitadas</span>
<strong>{{ TotalDesabilitadas }}</strong>
</div>
</div>
</div>
<div class="admin-card news-panel">
<div class="panel-header">
<div>
<span class="eyebrow">Notícias</span>
<h2>Notícias cadastradas</h2>
</div>
</div>
<div *ngIf="ListaNoticias && ListaNoticias.length > 0" class="news-grid">
<article
class="news-card"
*ngFor="let Noticia of ListaNoticias"
[ngClass]="Noticia.habilitado ? 'news-enabled' : 'news-disabled'">
<div class="news-image-wrapper">
<img
[src]="LinkImagemNoticia(Noticia)"
alt="Imagem da notícia"
class="news-image">
</div>
<div class="news-card-body">
<div class="news-card-header">
<span class="id-pill">#{{ Noticia.idnoticias }}</span>
<span class="status-badge" [ngClass]="Noticia.habilitado ? 'enabled' : 'disabled'">
{{ Noticia.habilitado ? 'Habilitada' : 'Desabilitada' }}
</span>
</div>
<h3>{{ Noticia.titulo || 'Notícia sem título' }}</h3>
<div class="news-description" [innerHTML]="Noticia.descricao"></div>
<div class="news-link" *ngIf="Noticia.link">
<i class="material-icons">link</i>
<span>{{ Noticia.link }}</span>
</div>
<div class="news-actions">
<button type="button" class="btn-card edit" (click)="EditarNoticia(Noticia)">
<i class="material-icons">edit</i>
Editar
</button>
<button type="button" class="btn-card remove" (click)="RemoverNoticia(Noticia)">
<i class="material-icons">delete</i>
Remover
</button>
</div>
</div>
</article>
</div>
<div *ngIf="!ListaNoticias || ListaNoticias.length == 0" class="empty-state">
<div class="empty-icon">
<i class="material-icons">article</i>
</div>
<h3>Nenhuma notícia cadastrada</h3>
<p>
Cadastre a primeira notícia para exibir conteúdo na página inicial.
</p>
<button type="button" class="btn-main empty-btn" (click)="NovaNoticia()">
<i class="material-icons">post_add</i>
Criar primeira notícia
</button>
</div>
</div>
</section>
</div>

Some files were not shown because too many files have changed in this diff Show More