Initial commit

parents
Pipeline #1833 failed with stages
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
# See http://help.github.com/ignore-files/ for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"]
}
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html"
}
]
}
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
}
]
}
FROM node:22 as builder
WORKDIR /app
COPY . .
RUN npm install npm install --legacy-peer-deps
RUN npm run build || true
FROM nginx:alpine
COPY --from=builder /app/dist/login-page/browser /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
COPY mime.types /etc/nginx/mime.types
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
---
**Automatização do Aceite Técnico de Navios**
Uma aplicação Front-End desenvolvida com o framework **Angular (JavaScript)** para **facilitar** e **otimizar** o processo de aceite técnico de embarcações.
---
**COMANDOS DOCKER**
1. docker build -t login-page .
2. docker run -d -p 4200:80 --name login-container login-page
### 🎯 **Descrição**
Este projeto é uma **aplicação web** que permite aos usuários gerenciar completamente o processo de aceite técnico de navios. Com ele, você pode **criar**, **visualizar**, **editar** e **deletar** tarefas relacionadas ao processo de aceitação, proporcionando um **fluxo de trabalho eficiente** e um acompanhamento detalhado das atividades necessárias para a aprovação técnica das embarcações.
---
### 🚀 **Instalação**
1. **Clone o repositório**:
```bash
git clone https://github.com/Reirenan/Projeto_Angular_App.git
```
2. **Navegue até o diretório do projeto**:
```bash
cd Projeto_Angular_App
```
3. **Instale as dependências necessárias**:
```bash
npm install --legacy-peer-deps
```
4. **Inicie o servidor de desenvolvimento**:
```bash
ng serve
```
5. **Acesse a aplicação** no seu navegador:
🌐 [http://localhost:4200](http://localhost:4200)
---
### 🌱 **Passos para Criar uma Nova Branch no Git**
1. **Atualize o repositório local** (opcional, mas recomendado):
Antes de criar uma nova branch, é uma boa prática garantir que o repositório local esteja atualizado
com as mudanças mais recentes da branch principal (geralmente `main` ou `master`).
```bash
git checkout main
git pull origin main
```
2. **Crie uma nova branch**:
Escolha um nome para a nova branch e crie-a:
```bash
git checkout -b MinhaNovaBranch
```
3. **Verifique se está na nova branch**:
Após criar a branch, verifique se você está nela.
```bash
git branch
```
Isso deve mostrar um asterisco (`*`) ao lado da branch atual.
4. **Faça suas alterações**:
Realize as alterações necessárias nos arquivos do projeto.
5. **Adicione os arquivos modificados para o commit**:
Depois de fazer as alterações, adicione os arquivos que deseja incluir no commit:
```bash
git add .
```
Ou adicione arquivos específicos:
```bash
git add nome_do_arquivo
```
6. **Faça o commit das suas alterações**:
Agora, faça o commit das suas alterações com uma mensagem descritiva:
```bash
git commit -m 'Comentário do commit da minha nova branch'
```
7. **Envie a branch para o repositório remoto**:
Envie a nova branch para o repositório remoto, criando um link entre sua branch local e a remota:
```bash
git push --set-upstream origin MinhaNovaBranch
```
8. **Crie um Pull Request** (opcional, dependendo do fluxo de trabalho):
Se o repositório usa Pull Requests (PRs) para revisar e integrar o código, você pode criar um PR na interface do GitHub ou outro serviço de hospedagem de repositórios.
- No GitHub, vá até o repositório, e você verá a opção de criar um PR a partir da nova branch.
---
Esses passos cobrem desde a criação da branch até o envio para o repositório remoto. Dependendo do fluxo de trabalho do projeto, a criação de um Pull Request pode ser necessária para a revisão de código antes de realizar o merge com a branch principal.
Agora, a aplicação deve estar rodando localmente. Se precisar de mais informações ou orientações, estou à disposição! 😉
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"login-page": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/login-page",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "login-page:build:production"
},
"development": {
"buildTarget": "login-page:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "login-page:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
],
"scripts": []
}
}
}
}
},
"cli": {
"analytics": false
}
}
version: '3'
services:
hurr-ui:
build: .
ports:
- "4200:4200"
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, 'dist/hurr-ui')));
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'dist/hurr-ui', 'index.html'));
});
app.listen(8080, () => {
console.log('Servidor Angular rodando na porta 8080');
});
types {
text/html html htm shtml;
text/css css;
application/javascript js;
application/json json;
image/gif gif;
image/jpeg jpeg jpg;
application/xml xml;
text/xml xml;
image/png png;
}
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
\ No newline at end of file
This diff is collapsed.
{
"name": "login-page",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^17.2.0",
"@angular/cdk": "^17.3.8",
"@angular/common": "^17.2.0",
"@angular/compiler": "^17.2.0",
"@angular/core": "^17.2.0",
"@angular/forms": "^17.2.0",
"@angular/material": "^17.3.8",
"@angular/platform-browser": "^17.2.0",
"@angular/platform-browser-dynamic": "^17.2.0",
"@angular/router": "^17.2.0",
"chart.js": "^4.4.3",
"install": "^0.13.0",
"jwt-decode": "^4.0.0",
"ngx-currency": "^18.0.0",
"ngx-toastr": "^18.0.0",
"npm": "^11.2.0",
"primeng": "^17.17.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.2.3",
"@angular/cli": "^17.2.3",
"@angular/compiler-cli": "^17.2.0",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.1.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.3.2"
}
}
<ng-container *ngIf="token_valido">
<mat-toolbar class="mat-elevation-z3" style="background-color: #4169E1;">
<button mat-icon-button (click)="toggleCollapsed()">
<mat-icon>menu</mat-icon>
</button>
<h1>PORTO DO ITAQUÍ</h1>
<button mat-icon-button [matMenuTriggerFor]="menu" aria-label="Example icon-button with a menu" class="user-info">
<mat-icon>person</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item (click)="logout()">
<mat-icon>exit_to_app</mat-icon>
<span>Sair</span>
</button>
</mat-menu>
</mat-toolbar>
<mat-sidenav-container>
<mat-sidenav opened mode="side" [style.width]="sidenavWidth">
<app-custom-sidenav [collapsed]="collapsed"></app-custom-sidenav>
</mat-sidenav>
<mat-sidenav-content class="content" [style.margin-left]="sidenavWidth">
<router-outlet></router-outlet>
</mat-sidenav-content>
</mat-sidenav-container>
</ng-container>
<ng-container *ngIf="!token_valido">
<router-outlet></router-outlet>
</ng-container>
mat-toolbar {
position: relative;
z-index: 5;
color: #fff;
}
.content {
padding: 24px;
}
mat-sidenav-container {
height: calc(100vh - 60px);
}
h1 {
color: #fff;
margin: 0;
}
.user-info {
margin-left: auto;
display: flex;
align-items: center;
}
.example-spacer {
flex: 1 1 auto;
}
.user-info button {
margin-left: 10px;
}
.header-text h2 {
margin: 0;
font-size: 1rem;
line-height: 1.5rem;
}
.header-text p {
margin: 0;
font-size: 0.8rem;
}
.hide-header-text {
opacity: 0;
height: 0px;
}
import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have the 'login-page' title`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, login-page');
});
});
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { AuthService } from './auth.service';
import { JwtDecoderService } from './jwt-decoder.service';
import { Router, RouterLink, RouterOutlet } from '@angular/router';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatSidenavModule } from '@angular/material/sidenav';
import { CustomSidenavComponent } from './components/custom-sidenav/custom-sidenav.component';
import { CommonModule } from '@angular/common';
import { jwtDecode } from 'jwt-decode';
import {MatMenuModule} from '@angular/material/menu';
@Component({
selector: 'app-root',
standalone: true,
imports: [
RouterLink, RouterOutlet, CommonModule,
MatToolbarModule, MatButtonModule, MatIconModule,
MatSidenavModule, CustomSidenavComponent,MatIconModule,MatMenuModule
],
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
decodedToken: any;
collapsed = false;
sidenavWidth = '250px';
authTokenPresent = false;
token_valido = false;
constructor(
private jwtDecoderService: JwtDecoderService,
private authService: AuthService,
private cdr: ChangeDetectorRef,
private router:Router
) {}
ngOnInit() {
this.authService.authToken$.subscribe(isLoggedIn => {
this.authTokenPresent = isLoggedIn;
if (this.authTokenPresent) {
const token = sessionStorage.getItem('accessToken')!;
if (token) {
this.decodedToken = jwtDecode(token);
const isExpired = this.isTokenExpired(this.decodedToken);
if (isExpired) {
sessionStorage.removeItem('accessToken');
} else {
this.decodedToken = this.jwtDecoderService.decodeToken(token);
if (this.decodedToken.role != null) {
this.token_valido = true;
}
}
}
}
this.cdr.detectChanges();
});
}
logout() {
// Remove o token do armazenamento da sessão
sessionStorage.removeItem('accessToken');
// Atualiza o estado de autenticação para falso
this.authTokenPresent = false;
this.token_valido = false;
// Redireciona para a página de login ou outra rota
this.router.navigate(['/']);
// Detecta mudanças para atualizar o componente
this.cdr.detectChanges();
}
isTokenExpired(token: any): boolean {
if (!token || !token.exp) {
return true;
}
return token.exp < Date.now() / 1000;
}
toggleCollapsed() {
this.collapsed = !this.collapsed;
this.sidenavWidth = this.collapsed ? '65px' : '250px';
this.cdr.detectChanges();
}
}
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
import { provideAnimations } from '@angular/platform-browser/animations'
import { provideToastr } from 'ngx-toastr';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideClientHydration } from '@angular/platform-browser';
import { customInterceptor } from './custom.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes), provideHttpClient(withInterceptors([customInterceptor])),
provideAnimations(),
provideToastr(),
provideHttpClient(withFetch()), provideAnimationsAsync(),provideClientHydration(), provideAnimationsAsync(), provideHttpClient(withFetch())
]
};
import { Routes } from '@angular/router';
import { LoginComponent } from './pages/login/login.component';
import { SignUpComponent } from './pages/signup/signup.component';
import { UserComponent } from './pages/user/user.component';
import { AuthGuardService } from './services/auth-guard.service';
import { NaviosComponent } from './pages/crud-navios/navios/navios.component';
import { IsAdminService } from './services/is-admin.service';
import { DashboardComponent } from './pages/dashboard/dashboard.component';
import { AceitesComponent } from './pages/crud-aceites/aceites/aceites.component';
import { LoginSignupService } from './login-signup.service';
import { BercosComponent } from './pages/crud-bercos/bercos/bercos.component';
import { BlackListComponent } from './pages/crud-blackList/black-list/black-list.component';
export const routes: Routes = [
{
path: "",
component: LoginComponent,
canActivate: [LoginSignupService]
},
{
path: "signup",
component: SignUpComponent,
canActivate: [LoginSignupService]
},
{
path:'navio',
component: NaviosComponent,
canActivate: [AuthGuardService]
},
{
path:'dashboard',
component: DashboardComponent,
canActivate: [AuthGuardService,IsAdminService]
},
{
path:'aceite',
component: AceitesComponent,
canActivate: [AuthGuardService]
},
{
path:'berco',
component: BercosComponent,
canActivate: [AuthGuardService]
},
{
path:'black-list',
component: BlackListComponent,
canActivate:[AuthGuardService,IsAdminService]
},
{
path:'users',
component: UserComponent,
canActivate:[AuthGuardService,IsAdminService]
}
];
import { TestBed } from '@angular/core/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AuthService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private authTokenSubject = new BehaviorSubject<boolean>(this.checkAuthToken());
authToken$ = this.authTokenSubject.asObservable();
constructor() {}
private checkAuthToken(): boolean {
const authToken = sessionStorage.getItem('accessToken');
return !!authToken;
}
updateAuthTokenStatus(isLoggedIn: boolean) {
this.authTokenSubject.next(isLoggedIn);
}
}
<mat-nav-list>
@for (item of menuItems(); track item) {
@if(item.role === user.role) {
<a mat-list-item
[routerLink]="item.route"
routerLinkActive
#rla="routerLinkActive"
[activated]="rla.isActive"
>
<mat-icon matListItemIcon>{{item.icon}}</mat-icon>
<span matListItemTitle *ngIf="!sideNavCollapsed()">{{item.label}}</span>
</a>
} @else if(user.role === item.role || user.role === "COMPANY"){
<a mat-list-item
[routerLink]="item.route"
routerLinkActive
#rla="routerLinkActive"
[activated]="rla.isActive"
>
<mat-icon matListItemIcon>{{item.icon}}</mat-icon>
<span matListItemTitle *ngIf="!sideNavCollapsed()">{{item.label}}</span>
</a>
}
}
</mat-nav-list>
.header-text {
> h2 {
margin: 0;
font-size: 1rem;
line-height: 1.5rem;
}
> p {
margin: 0;
font-size: 0.8rem;
}
}
.hide-header-text {
opacity: 0;
height: 0px;
}
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CustomSidenavComponent } from './custom-sidenav.component';
describe('CustomSidenavComponent', () => {
let component: CustomSidenavComponent;
let fixture: ComponentFixture<CustomSidenavComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CustomSidenavComponent]
})
.compileComponents();
fixture = TestBed.createComponent(CustomSidenavComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { ChangeDetectorRef, Component, Input, OnInit, computed, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatListModule } from '@angular/material/list';
import { MatIconModule } from '@angular/material/icon';
import { RouterModule } from '@angular/router';
import { JwtDecoderService } from '../../jwt-decoder.service';
import { AuthService } from '../../auth.service';
import { jwtDecode } from 'jwt-decode';
export type MenuItem = {
icon: string;
label: string;
route?: string;
role?:string;
}
@Component({
selector: 'app-custom-sidenav',
standalone: true,
imports: [CommonModule, MatListModule, MatIconModule, RouterModule],
templateUrl: './custom-sidenav.component.html',
styleUrls: ['./custom-sidenav.component.scss']
})
export class CustomSidenavComponent implements OnInit{
user: any;
sidenavWidth = '250px';
authTokenPresent = false;
constructor(
private jwtDecoderService: JwtDecoderService,
private authService: AuthService,
private cdr: ChangeDetectorRef
) {}
ngOnInit() {
this.authService.authToken$.subscribe(isLoggedIn => {
this.authTokenPresent = isLoggedIn;
if (this.authTokenPresent) {
const token = sessionStorage.getItem('accessToken')!;
this.user = jwtDecode(token);
const isExpired =
this.user && this.user.exp? this.user.exp < Date.now()/1000:false
if(isExpired){
sessionStorage.removeItem('accessToken');
}
this.user = this.jwtDecoderService.decodeToken(token);
console.log(this.user);
}
this.cdr.detectChanges();
});
}
sideNavCollapsed = signal(false);
@Input() set collapsed(val: boolean) {
this.sideNavCollapsed.set(val);
}
menuItems = signal<MenuItem[]>([
{
icon: 'dashboard',
label: 'Dashboard',
route: 'dashboard',
role: 'COMPANY',
},
{
icon: 'directions_boat',
label: 'Navios',
route: 'navio',
role: 'CANDIDATE',
},
{
icon: 'calendar_today',
label: 'Aceites',
route: 'aceite',
role: 'CANDIDATE'
},
{
icon: 'dock',
label: 'Berços',
route: 'berco',
role: 'CANDIDATE'
},
{
icon:'block',
label:'Black List',
route:'black-list',
role:'COMPANY'
},
{
icon: 'groups',
label: 'Usuários',
route: 'users',
role: 'COMPANY'
}
]);
profilePicSize = computed(() => (this.sideNavCollapsed() ? '32' : '100'));
}
<main>
<section class="form-section">
<h2>{{ title }}</h2>
<ng-content></ng-content>
<div class="btn-wrapper">
<button
class="btn-primary"
(click)="submit()"
[disabled]="disablePrimaryBtn"
>
{{ primaryBtnText }}
</button>
<div class="divider">
<div></div>
<span>or</span>
<div></div>
</div>
<button class="btn-secondary" (click)="navigate()">{{ secondaryBtnText }}</button>
</div>
</section>
</main>
@import "../../../styles/variables.scss";
main {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 100%;
section {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.main-section {
gap: 8px;
flex-grow: 1;
background-color: #4169E1;
}
.form-section {
flex-grow: 0.2; /* Reduzir o crescimento flexível para tornar a seção menor */
padding: 0 20px; /* Reduzir o padding */
img {
margin-bottom: 20px; /* Reduzir a margem inferior da imagem */
width: 100%;
max-width: 250px; /* Tornar a imagem responsiva */
height: auto;
}
h2 {
font-size: 18px; /* Reduzir o tamanho da fonte */
font-weight: bold;
margin-bottom: 30px; /* Reduzir a margem inferior */
}
.btn-wrapper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 77%;
gap: 20px; /* Reduzir o espaçamento entre os botões */
.divider {
display: flex;
align-items: center;
justify-content: center;
gap: 10px; /* Reduzir o espaçamento entre os elementos do divisor */
width: 100%;
font-size: 12px; /* Reduzir o tamanho da fonte */
color: #4169E1;
font-weight: 400;
text-transform: uppercase;
div {
flex-grow: 1;
height: 1px; /* Reduzir a altura da linha */
background-color: #4169E1;
}
}
button {
width: 100%;
border-radius: 8px;
font-size: 14px; /* Reduzir o tamanho da fonte */
font-weight: 500;
border: none;
background: transparent;
padding: 10px 20px; /* Reduzir o padding */
cursor: pointer;
&:disabled {
opacity: 0.8;
}
&.btn-primary {
background-color: #4169E1;
color: white;
box-shadow: 0px 8px 12px rgba(148, 64, 255, 0.2);
}
&.btn-secondary {
border: 1px solid #4169E1;
color: #4169E1;
}
}
}
}
}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DefaultLoginLayoutComponent } from './default-login-layout.component';
describe('DefaultLoginLayoutComponent', () => {
let component: DefaultLoginLayoutComponent;
let fixture: ComponentFixture<DefaultLoginLayoutComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [DefaultLoginLayoutComponent]
})
.compileComponents();
fixture = TestBed.createComponent(DefaultLoginLayoutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-default-login-layout',
standalone: true,
imports: [],
templateUrl: './default-login-layout.component.html',
styleUrl: './default-login-layout.component.scss'
})
export class DefaultLoginLayoutComponent {
@Input() title: string = "";
@Input() primaryBtnText: string = "";
@Input() secondaryBtnText: string = "";
@Input() disablePrimaryBtn: boolean = true;
@Output("submit") onSubmit = new EventEmitter();
@Output("navigate") onNavigate = new EventEmitter();
submit(){
this.onSubmit.emit();
}
navigate(){
this.onNavigate.emit();
}
}
<div class="input-wrapper">
<label [for]="inputName">{{ label }}</label>
<div class="input-content">
<input
[type]="type"
[placeholder]="placeholder"
[value]="value"
(input)="onInput($event)"
/>
</div>
</div>
@import "../../../styles/variables.scss";
.input-wrapper {
color: $text-color;
font-size: 16px;
text-align: start;
label {
margin-bottom: 10px;
}
.input-content {
width: 100%;
border-radius: 8px;
display: flex;
.icon {
padding: 12px;
background-color: #4169E1;
border-radius: 0 8px 8px 0;
img {
height: 20px;
}
}
input {
border: none;
border-radius: 8px 8px 8px 8px;
background-color: #dcdcdc; // Nova cor aqui
padding: 12px 0 12px 20px;
width: 100%;
}
}
}
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PrimaryInputComponent } from './primary-input.component';
describe('PrimaryInputComponent', () => {
let component: PrimaryInputComponent;
let fixture: ComponentFixture<PrimaryInputComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PrimaryInputComponent]
})
.compileComponents();
fixture = TestBed.createComponent(PrimaryInputComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, Input, forwardRef } from '@angular/core';
import { ControlValueAccessor, FormGroup, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
type InputTypes = "text" | "email" | "password"
@Component({
selector: 'app-primary-input',
standalone: true,
imports: [
ReactiveFormsModule
],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => PrimaryInputComponent),
multi: true
}
],
templateUrl: './primary-input.component.html',
styleUrl: './primary-input.component.scss'
})
export class PrimaryInputComponent implements ControlValueAccessor {
@Input() type: InputTypes = "text";
@Input() placeholder: string = "";
@Input() label: string = "";
@Input() inputName: string = "";
value: string = ''
onChange: any = () => {}
onTouched: any = () => {}
onInput(event: Event){
const value = (event.target as HTMLInputElement).value
this.onChange(value)
}
writeValue(value: any): void {
this.value = value
}
registerOnChange(fn: any): void {
this.onChange = fn
}
registerOnTouched(fn: any): void {
this.onTouched = fn
}
setDisabledState(isDisabled: boolean): void {}
}
import { TestBed } from '@angular/core/testing';
import { CoreService } from './core.service';
describe('CoreService', () => {
let service: CoreService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CoreService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
@Injectable({
providedIn: 'root',
})
export class CoreService {
constructor(private _snackBar: MatSnackBar) {}
openSnackBar(message: string, action: string = 'ok') {
this._snackBar.open(message, action, {
duration: 10000,
verticalPosition: 'top',
});
}
openSnackBar1(message: string, action: string = 'ok') {
this._snackBar.open(message, action, {
duration: 10000,
verticalPosition: 'top',
});
}
}
\ No newline at end of file
import { TestBed } from '@angular/core/testing';
import { HttpInterceptorFn } from '@angular/common/http';
import { customInterceptor } from './custom.interceptor';
describe('customInterceptor', () => {
const interceptor: HttpInterceptorFn = (req, next) =>
TestBed.runInInjectionContext(() => customInterceptor(req, next));
beforeEach(() => {
TestBed.configureTestingModule({});
});
it('should be created', () => {
expect(interceptor).toBeTruthy();
});
});
import { HttpInterceptorFn } from '@angular/common/http';
import { jwtDecode } from 'jwt-decode';
export const customInterceptor: HttpInterceptorFn = (req, next) => {
const myToken = sessionStorage.getItem('accessToken');
if (myToken){
const decodedToken = jwtDecode(myToken);
const isExpired =
decodedToken && decodedToken.exp? decodedToken.exp < Date.now()/1000:false
if(isExpired){
sessionStorage.removeItem('accessToken');
}
const clonedRequest = req.clone({
setHeaders: {
Authorization: `Bearer ${myToken}`
}
});
return next(clonedRequest);
}
return next(req);
};
import { TestBed } from '@angular/core/testing';
import { JwtDecoderService } from './jwt-decoder.service';
describe('JwtDecoderService', () => {
let service: JwtDecoderService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(JwtDecoderService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class JwtDecoderService {
constructor() { }
public decodeToken(token: string) {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(
atob(base64)
.split('')
.map((c) => {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
})
.join('')
);
return JSON.parse(jsonPayload);
}
}
import { TestBed } from '@angular/core/testing';
import { LoginSignupService } from './login-signup.service';
describe('LoginSignupService', () => {
let service: LoginSignupService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(LoginSignupService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { JwtDecoderService } from './jwt-decoder.service';
import { ChangeDetectorRef } from '@angular/core';
import { Location } from '@angular/common';
@Injectable({
providedIn: 'root'
})
export class LoginSignupService {
private decodedToken: any;
constructor(
private jwtDecoderService: JwtDecoderService,
private location: Location
) {}
canActivate(
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
const authTokenPresent = sessionStorage.getItem('accessToken');
return authTokenPresent ? this.validToken(authTokenPresent) : true;
}
validToken(authTokenPresent:string){
this.decodedToken = this.jwtDecoderService.decodeToken(authTokenPresent);
return this.decodedToken ? false : true ;
}
}
<div mat-dialog-title>
<h1>{{'Solicitar Aceite' }}</h1>
</div>
<form [formGroup]="empForm" (ngSubmit)="onFormSubmit()">
<div mat-dialog-content class="content">
<div class="row">
<mat-form-field class="field full-width" appearance="outline">
<mat-label>IMO</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="imo">
<mat-error>
<ng-container *ngIf="empForm.get('imo')?.invalid">
<ng-container *ngIf="getErrorMessage('imo') === 'Esse imo não existe'">
{{ getErrorMessage('imo') }}, se desejar soliciatar a criação de um novo navio,
<a (click)="exibirFormNavio()" style="text-decoration: underline">clique aqui!</a>
</ng-container>
<ng-container *ngIf="getErrorMessage('imo') !== 'Esse imo não existe'">
{{ getErrorMessage('imo') }}
</ng-container>
</ng-container>
</mat-error>
</mat-form-field>
<mat-form-field class="field full-width" appearance="outline">
<mat-label>MMSI</mat-label>
<input matInput type="text" formControlName="mmsi" (keydown)="preventNegative($event)">
<mat-error *ngIf="empForm.get('mmsi')?.invalid">{{ getErrorMessage('mmsi') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field large" appearance="outline">
<mat-label>Nome</mat-label>
<input matInput type="text" formControlName="nome">
<mat-error *ngIf="empForm.get('nome')?.invalid">{{ getErrorMessage('nome') }}</mat-error>
</mat-form-field>
<mat-form-field class="field medium" appearance="outline">
<mat-label>LOA (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="loa">
<mat-error *ngIf="empForm.get('loa')?.invalid">{{ getErrorMessage('loa') }}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-label>Boca (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="boca">
<mat-error *ngIf="empForm.get('boca')?.invalid">{{ getErrorMessage('boca') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field medium" appearance="outline">
<mat-label>Calado de Entrada</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="calado_entrada">
<mat-error *ngIf="empForm.get('calado_entrada')?.invalid">{{ getErrorMessage('calado_entrada') }}</mat-error>
</mat-form-field>
<mat-form-field class="field medium" appearance="outline">
<mat-label>Calado de Saída</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="calado_saida">
<mat-error *ngIf="empForm.get('calado_saida')?.invalid">{{ getErrorMessage('calado_saida') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field large" appearance="outline">
<mat-label>DWT (Atual)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }"
formControlName="dwt">
<mat-error *ngIf="empForm.get('dwt')?.invalid">{{ getErrorMessage('dwt') }}</mat-error>
</mat-form-field>
<mat-form-field class="field medium" appearance="outline">
<mat-label>Pontal (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="pontal">
<mat-error *ngIf="empForm.get('pontal')?.invalid">{{ getErrorMessage('pontal') }}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-label>Ponte Mfold (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="ponte_mfold">
<mat-error *ngIf="empForm.get('ponte_mfold')?.invalid">{{ getErrorMessage('ponte_mfold') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field large" appearance="outline">
<mat-label>Mfold Quilha (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '',thousands: '.', decimal: ',' }" formControlName="mfold_quilha">
<mat-error *ngIf="empForm.get('mfold_quilha')?.invalid">{{ getErrorMessage('mfold_quilha') }}</mat-error>
</mat-form-field>
<mat-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label>
<mat-select formControlName="categoria">
<mat-option *ngFor="let cat of categoria" [value]="cat.id">
{{ cat.nome }}
</mat-option>
</mat-select>
<mat-error *ngIf="empForm.get('categoria')?.invalid">{{ getErrorMessage('categoria') }}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-label>Flag</mat-label>
<mat-select formControlName="flag">
<mat-select-trigger>
<img
*ngIf="selectedCountry"
[src]="selectedCountry.flagUrl"
width="20"
height="15"
alt="{{ selectedCountry.name }} flag"
style="margin-right: 8px"
/>
{{ selectedCountry?.name }}
</mat-select-trigger>
<mat-option
*ngFor="let country of countries"
[value]="country.id"
(onSelectionChange)="onCountrySelected(country)"
>
<img
[src]="country.flagUrl"
width="20"
height="15"
alt="{{ country.name }} flag"
style="margin-right: 8px"
/>
{{ country.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="empForm.get('flag')?.invalid">
{{ getErrorMessage("flag") }}
</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field full-width" appearance="outline">
<mat-label>Observação</mat-label>
<input matInput type="text" formControlName="obs">
<mat-error *ngIf="empForm.get('obs')?.invalid">{{ getErrorMessage('obs') }}</mat-error>
</mat-form-field>
</div>
<div class="row file-upload-container">
<mat-label style="color:cadetblue">Adicione o documente Q88 da embarcação</mat-label>
<input
type="file"
class="file-input"
(change)="onFileSelected($event)"
#fileUpload
style="display: none"
/>
<div class="file-upload-content">
<div class="file-info">
<span>{{ fileName || "No file uploaded yet." }}</span>
</div>
<div class="upload-button-wrapper">
<button
mat-mini-fab
color="primary"
class="upload-btn"
(click)="fileUpload.click()"
>
<mat-icon>attach_file</mat-icon>
</button>
</div>
</div>
<div class="uploaded-image-container" *ngIf="imageUrl">
<img [src]="imageUrl" alt="Uploaded Image" class="uploaded-image" />
<button
mat-mini-fab
color="warn"
class="remove-image-btn"
(click)="removeImage()"
>
<mat-icon>close</mat-icon>
</button>
</div>
</div>
</div>
<div mat-dialog-actions class="action">
<button mat-raised-button type="button" color="warn" [mat-dialog-close]="false">Cancelar</button>
<button mat-raised-button color="primary" type="submit">Salvar</button>
</div>
</form>
.content {
padding-top: 10px;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-bottom: 5px;
}
.file-upload {
display: flex;
align-items: center;
}
.upload-btn {
margin-left: 10px; /* Ajuste conforme necessário */
}
.uploaded-image {
margin-left: 10px; /* Ajuste conforme necessário */
max-height: 100px; /* Ajuste conforme necessário */
}
.field {
margin-bottom: 5px;
}
.field.full-width {
flex: 1 1 100%;
}
.field.large {
flex: 1 1 60%;
}
.field.medium {
flex: 1 1 40%;
}
.field.small {
flex: 1 1 20%;
}
.action {
display: flex;
justify-content: flex-end;
padding: 5px 0;
gap: 10px;
margin: 10px; /* Adiciona margem ao redor do container do botão */
}
@media (max-width: 600px) {
.field.large, .field.medium, .field.small {
flex: 1 1 100%;
}
}
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AceiteAddComponent } from './aceite-add.component';
describe('AceiteAddComponent', () => {
let component: AceiteAddComponent;
let fixture: ComponentFixture<AceiteAddComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AceiteAddComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AceiteAddComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
This diff is collapsed.
<div mat-dialog-title>
<h1>Editar Aceite</h1>
</div>
<form [formGroup]="empForm" (ngSubmit)="onFormSubmit()">
<div mat-dialog-content class="content">
<div class="row">
<mat-form-field class="field full-width" appearance="outline">
<mat-label>IMO</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="imo">
<mat-error>
<ng-container *ngIf="empForm.get('imo')?.invalid">
<ng-container *ngIf="getErrorMessage('imo') === 'Esse imo não existe'">
{{ getErrorMessage('imo') }}, se desejar soliciatar a criação de um novo navio,
<a (click)="exibirFormNavio()" style="text-decoration: underline">clique aqui!</a>
</ng-container>
<ng-container *ngIf="getErrorMessage('imo') !== 'Esse imo não existe'">
{{ getErrorMessage('imo') }}
</ng-container>
</ng-container>
</mat-error>
</mat-form-field>
<mat-form-field class="field full-width" appearance="outline">
<mat-label>MMSI</mat-label>
<input matInput type="text" formControlName="mmsi" (keydown)="preventNegative($event)">
<mat-error *ngIf="empForm.get('mmsi')?.invalid">{{ getErrorMessage('mmsi') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field large" appearance="outline">
<mat-label>Nome</mat-label>
<input matInput type="text" formControlName="nome">
<mat-error *ngIf="empForm.get('nome')?.invalid">{{ getErrorMessage('nome') }}</mat-error>
</mat-form-field>
<mat-form-field class="field medium" appearance="outline">
<mat-label>LOA (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '', thousands: '.', decimal: ',' }" formControlName="loa">
<mat-error *ngIf="empForm.get('loa')?.invalid">{{ getErrorMessage('loa') }}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-label>Boca (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '', thousands: '.', decimal: ',' }" formControlName="boca">
<mat-error *ngIf="empForm.get('boca')?.invalid">{{ getErrorMessage('boca') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field medium" appearance="outline">
<mat-label>Calado de Entrada</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '', thousands: '.', decimal: ',' }" formControlName="calado_entrada">
<mat-error *ngIf="empForm.get('calado_entrada')?.invalid">{{ getErrorMessage('calado_entrada') }}</mat-error>
</mat-form-field>
<mat-form-field class="field medium" appearance="outline">
<mat-label>Calado de Saída</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '', thousands: '.', decimal: ',' }" formControlName="calado_saida">
<mat-error *ngIf="empForm.get('calado_saida')?.invalid">{{ getErrorMessage('calado_saida') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field large" appearance="outline">
<mat-label>DWT (Atual)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '', thousands: '.', decimal: ',' }" formControlName="dwt">
<mat-error *ngIf="empForm.get('dwt')?.invalid">{{ getErrorMessage('dwt') }}</mat-error>
</mat-form-field>
<mat-form-field class="field medium" appearance="outline">
<mat-label>Pontal (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '', thousands: '.', decimal: ',' }" formControlName="pontal">
<mat-error *ngIf="empForm.get('pontal')?.invalid">{{ getErrorMessage('pontal') }}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-label>Ponte Mfold (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '', thousands: '.', decimal: ',' }" formControlName="ponte_mfold">
<mat-error *ngIf="empForm.get('ponte_mfold')?.invalid">{{ getErrorMessage('ponte_mfold') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field large" appearance="outline">
<mat-label>Mfold Quilha (m)</mat-label>
<input matInput type="text" [currencyMask]="{ prefix: '', thousands: '.', decimal: ',' }" formControlName="mfold_quilha">
<mat-error *ngIf="empForm.get('mfold_quilha')?.invalid">{{ getErrorMessage('mfold_quilha') }}</mat-error>
</mat-form-field>
<mat-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label>
<mat-select formControlName="categoria">
<mat-option *ngFor="let cat of categoria" [value]="cat.id">
{{ cat.nome }}
</mat-option>
</mat-select>
<mat-error *ngIf="empForm.get('categoria')?.invalid">{{ getErrorMessage('categoria') }}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-label>Flag</mat-label>
<mat-select formControlName="flag">
<mat-select-trigger>
<img
*ngIf="selectedCountry"
[src]="selectedCountry.flagUrl"
width="20"
height="15"
alt="{{ selectedCountry.name }} flag"
style="margin-right: 8px"
/>
{{ selectedCountry?.name }}
</mat-select-trigger>
<mat-option
*ngFor="let country of countries"
[value]="country.id"
(onSelectionChange)="onCountrySelected(country)"
>
<img
[src]="country.flagUrl"
width="20"
height="15"
alt="{{ country.name }} flag"
style="margin-right: 8px"
/>
{{ country.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="empForm.get('flag')?.invalid">
{{ getErrorMessage("flag") }}
</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field full-width" appearance="outline">
<mat-label>Observação</mat-label>
<input matInput type="text" formControlName="obs">
<mat-error *ngIf="empForm.get('obs')?.invalid">{{ getErrorMessage('obs') }}</mat-error>
</mat-form-field>
</div>
<!-- Sexta Linha: Status -->
<div class="row">
<mat-form-field class="field large" appearance="outline">
<mat-label>Status</mat-label>
<mat-select formControlName="status" (selectionChange)="onStatusChange($event.value)">
<mat-option value="Y">Aceito</mat-option>
<mat-option value="NE">Negado</mat-option>
<mat-option value="N">Em Processamento</mat-option>
</mat-select>
<mat-error *ngIf="empForm.get('status')?.invalid">{{ getErrorMessage('status') }}</mat-error>
</mat-form-field>
</div>
<div *ngIf="empForm.get('status')?.value === 'Y' || empForm.get('status')?.value === 'NE'" class="field large">
<mat-form-field class="field large" appearance="outline">
<mat-label>Comentários sobre o aceite:</mat-label>
<input matInput type="text" formControlName="restricoes">
<mat-error *ngIf="empForm.get('restricoes')?.invalid">
{{ getErrorMessage('restricoes') }}
</mat-error>
</mat-form-field>
<div class="row file-upload-container">
<mat-label style="color:cadetblue">Adicione o documente Q88 da embarcação</mat-label>
<input
type="file"
class="file-input"
(change)="onFileSelected($event)"
#fileUpload
style="display: none"
/>
<div class="file-upload-content">
<div class="file-info">
<span>{{ fileName || "No file uploaded yet." }}</span>
</div>
<div class="upload-button-wrapper">
<button
mat-mini-fab
color="primary"
class="upload-btn"
(click)="fileUpload.click()"
type="button"
>
<mat-icon>attach_file</mat-icon>
</button>
</div>
</div>
<div class="uploaded-image-container" *ngIf="imageUrl">
<img [src]="imageUrl" alt="Uploaded Image" class="uploaded-image" />
<button
mat-mini-fab
color="warn"
class="remove-image-btn"
(click)="removeImage()"
type="button"
>
<mat-icon>close</mat-icon>
</button>
</div>
</div>
</div>
</div>
<!-- Ações do Diálogo -->
<div mat-dialog-actions class="action">
<button mat-raised-button type="button" color="warn" [mat-dialog-close]="false">Cancelar</button>
<button mat-raised-button color="primary" type="submit">Salvar</button>
</div>
</form>
\ No newline at end of file
.content {
padding-top: 10px;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-bottom: 20px;
}
.field {
margin-bottom: 5px;
}
.field.full-width {
flex: 1 1 100%;
}
.field.large {
flex: 1 1 60%;
}
.field.medium {
flex: 1 1 40%;
}
.field.small {
flex: 1 1 20%;
}
.action {
display: flex;
justify-content: flex-end;
padding: 5px 0;
gap: 10px;
margin: 10px; /* Adiciona margem ao redor do container do botão */
}
@media (max-width: 600px) {
.field.large, .field.medium, .field.small {
flex: 1 1 100%;
}
}
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AceiteEditComponent } from './aceite-edit.component';
describe('AceiteEditComponent', () => {
let component: AceiteEditComponent;
let fixture: ComponentFixture<AceiteEditComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AceiteEditComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AceiteEditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
<div mat-dialog-title>
<h1>{{ "Buscar Aceite" }}</h1>
</div>
<form [formGroup]="empForm" (ngSubmit)="onFormSubmit()">
<div mat-dialog-content class="content">
<div class="row">
<!-- IMO -->
<mat-form-field class="field full-width" appearance="outline">
<mat-label>IMO</mat-label>
<input
matInput
type="number"
(keydown)="preventNegative($event)"
min="0"
formControlName="imo"
/>
<mat-error *ngIf="empForm.get('imo')?.invalid">{{
getErrorMessage("imo")
}}</mat-error>
</mat-form-field>
</div>
<!--
<div class="row">
<mat-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label>
<mat-select formControlName="categoria">
<mat-option *ngFor="let cat of categoria" [value]="cat">
{{ cat === 1 ? "Granel Sólido" : cat === 2 ? "Granel Líquido" : "Carga Geral" }}
</mat-option>
</mat-select>
<mat-error *ngIf="empForm.get('categoria')?.invalid">{{
getErrorMessage("categoria")
}}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-label>Flag</mat-label>
<mat-select formControlName="flag">
<mat-select-trigger>
<img
*ngIf="selectedCountry"
[src]="selectedCountry.flagUrl"
width="20"
height="15"
alt="{{ selectedCountry.name }} flag"
style="margin-right: 8px"
/>
{{ selectedCountry?.name }}
</mat-select-trigger>
<mat-option
*ngFor="let country of countries"
[value]="country.id"
(onSelectionChange)="onCountrySelected(country)"
>
<img
[src]="country.flagUrl"
width="20"
height="15"
alt="{{ country.name }} flag"
style="margin-right: 8px"
/>
{{ country.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="empForm.get('flag')?.invalid">
{{ getErrorMessage("flag") }}
</mat-error>
</mat-form-field>
</div>
-->
</div>
<div mat-dialog-actions class="action">
<button
mat-raised-button
type="button"
color="warn"
[mat-dialog-close]="false"
>
Cancelar
</button>
<button mat-raised-button color="primary" type="submit">Buscar</button>
</div>
</form>
\ No newline at end of file
.content {
padding-top: 10px;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-bottom: 20px;
}
.field {
margin-bottom: 5px;
}
.field.full-width {
flex: 1 1 100%;
}
.field.large {
flex: 1 1 60%;
}
.field.medium {
flex: 1 1 40%;
}
.field.small {
flex: 1 1 20%;
}
.action {
display: flex;
justify-content: flex-end;
padding: 5px 0;
gap: 10px;
margin: 10px; /* Adiciona margem ao redor do container do botão */
}
@media (max-width: 600px) {
.field.large, .field.medium, .field.small {
flex: 1 1 100%;
}
}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AceiteSearchComponent } from './aceite-search.component';
describe('AceiteSearchComponent', () => {
let component: AceiteSearchComponent;
let fixture: ComponentFixture<AceiteSearchComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AceiteSearchComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AceiteSearchComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
<app-add-search [title]="'Aceites'" [primaryButtonText]="'Solicitar Aceite'" [primaryButtonColor]="'primary'"
[primaryButtonAction]="openAddBercoForm.bind(this)" [secondaryButtonText]="'Buscar Aceite'"
[secondaryButtonColor]="'accent'" [secondaryButtonAction]="openSearchBercoForm.bind(this)">
</app-add-search>
<div class="main-body">
<div class="table-container" style="overflow-x: auto">
<table mat-table [dataSource]="dataSource" matSort>
<!-- ID Column -->
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Código</th>
<td mat-cell *matCellDef="let row">{{ row.id }}</td>
</ng-container>
<!-- IMO Column -->
<ng-container matColumnDef="imo">
<th mat-header-cell *matHeaderCellDef mat-sort-header>IMO</th>
<td mat-cell *matCellDef="let row">{{ row.imo || "Null" }}</td>
</ng-container>
<!-- MMSI Column -->
<ng-container matColumnDef="user">
<th mat-header-cell *matHeaderCellDef>Usuário Responsável</th>
<td mat-cell *matCellDef="let row">
{{ parseUserString(row.user)?.email || "Sem email" }}
</td>
</ng-container>
<!-- MMSI Column -->
<ng-container matColumnDef="nome">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Nome</th>
<td mat-cell *matCellDef="let row">{{ row.nome ?? 'Null' }}
</td>
</ng-container>
<ng-container matColumnDef="data_create">
<th mat-header-cell *matHeaderCellDef>Data de Criação</th>
<td mat-cell *matCellDef="let row">{{ row.data_create || "Null" }}</td>
</ng-container>
<ng-container matColumnDef="data_update">
<th mat-header-cell *matHeaderCellDef>Data de Edição</th>
<td mat-cell *matCellDef="let row">{{ row.data_update || "Null" }}</td>
</ng-container>
<ng-container matColumnDef="dataAccept">
<th mat-header-cell *matHeaderCellDef>Data de Edição</th>
<td mat-cell *matCellDef="let row">{{ row.dataAccept || "Null" }}</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef>Status</th>
<td mat-cell *matCellDef="let row">
{{ row.status == "Y" ? "Verificado" : "Não verificado" }}
</td>
</ng-container>
<!-- Vessel Column -->
<ng-container matColumnDef="vessel">
<th mat-header-cell *matHeaderCellDef>Vessel</th>
<td mat-cell *matCellDef="let row">{{ row.vessel || "Null" }}</td>
</ng-container>
<!-- Action Column -->
<!-- Action Column -->
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef>Action</th>
<td mat-cell *matCellDef="let row" style="white-space: nowrap">
<button mat-icon-button (click)="openEditForm(row)" *ngIf="user.role === 'COMPANY'">
<mat-icon style="color: coral">edit</mat-icon>
</button>
<button mat-icon-button color="primary" (click)="getBercoById(row)">
<mat-icon>visibility</mat-icon>
</button>
<button mat-icon-button color="warn" (click)="deleteBerco(row.id)" *ngIf="user.role === 'COMPANY'">
<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>
</div>
<!-- Add paginator reference #paginator="matPaginator" -->
<mat-paginator #paginator [length]="totalItems" [pageSize]="pageSize" (page)="loadData($event)">
</mat-paginator>
<button mat-button (click)="paginator.firstPage()" [disabled]="paginator.pageIndex === 0">
Primeira Página
</button>
<button mat-button (click)="paginator.lastPage()"
[disabled]="paginator.pageIndex === paginator.getNumberOfPages() - 1">
Última Página
</button>
</div>
\ No newline at end of file
.example-spacer {
flex: 1 1 auto;
}
.mat-toolbar {
padding-top: 5px;
margin: 0 auto;
max-width: 1348px;
background-color: #4169E1;
color: #fff;
}
.main-body {
padding-top: 20px;
margin: 0 auto;
max-width: 1348px;
mat-form-field {
width: 100%;
}
}
.mat-toolbar {
padding-top: 5px;
background-color: #4169E1;
color: #fff;
}
.action {
display: flex;
align-items: center;
}
@media screen and (max-width: 600px) {
.action button {
margin-right: 5px;
}
.table-container {
overflow-x: auto;
}
}
td.mat-cell {
white-space: nowrap;
}
td.mat-cell.action-buttons {
display: flex;
justify-content: flex-end;
align-items: center;
}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AceitesComponent } from './aceites.component';
describe('AceitesComponent', () => {
let component: AceitesComponent;
let fixture: ComponentFixture<AceitesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AceitesComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AceitesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatSort, MatSortModule } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { AceiteService } from '../../../services/aceites/aceite.service';
import { CoreService } from '../../../core/core.service';
import { VisualizacaoAceiteComponent } from '../visualizacao-aceite/visualizacao-aceite.component';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatSelectModule } from '@angular/material/select';
import { MatRadioModule } from '@angular/material/radio';
import { MatIconModule } from '@angular/material/icon';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatTableModule } from '@angular/material/table';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatPaginatorModule } from '@angular/material/paginator';
import { AceiteEditComponent } from '../aceite-edit/aceite-edit.component';
import { AceiteAddComponent } from '../aceite-add/aceite-add.component';
import { AddSearchComponent } from '../../../shared/addAndSearch/add-search/add-search.component';
import { AceiteSearchComponent } from '../aceite-search/aceite-search.component';
import { JwtDecoderService } from '../../../jwt-decoder.service';
import { AuthService } from '../../../auth.service';
import { jwtDecode } from 'jwt-decode';
interface AceiteData {
id: number;
imo: number;
codigo: string;
obs: string;
status: string;
bercos: any;
}
@Component({
selector: 'app-aceites',
standalone: true,
imports: [
MatPaginatorModule,
MatToolbarModule,
MatTableModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
MatButtonModule,
MatInputModule,
MatFormFieldModule,
MatDatepickerModule,
MatSelectModule,
MatRadioModule,
MatIconModule,
MatSnackBarModule,
MatSortModule,
AddSearchComponent,
],
templateUrl: './aceites.component.html',
styleUrls: ['./aceites.component.scss'],
})
export class AceitesComponent implements OnInit {
displayedColumns = [
'id',
'nome',
'user',
'imo',
'data_create',
'data_update',
'dataAccept',
'status',
'action',
];
dataSource = new MatTableDataSource<AceiteData>();
totalItems = 100;
pageSize = 10;
currentPage = 1;
user: any;
authTokenPresent = false;
@ViewChild(MatPaginator) paginator!: MatPaginator;
@ViewChild(MatSort) sort!: MatSort;
constructor(
private _dialog: MatDialog,
private _aceiteService: AceiteService,
private _coreService: CoreService,
private jwtDecoderService: JwtDecoderService,
private authService: AuthService,
private cdr: ChangeDetectorRef
) {}
ngOnInit(): void { this.validationToken();
}
ngAfterViewInit(): void {
this.loadData();
}
parseUserString(userString: string) {
const userRegex = /User\(id=(\d+), email=([^)]+)\)/;
const match = userRegex.exec(userString);
if (match) {
return {
id: parseInt(match[1], 10),
email: match[2],
};
}
return null;
}
loadData(event?: PageEvent): void {
if (typeof sessionStorage !== 'undefined') {
const storedData = sessionStorage.getItem('aceitesSearch');
if (storedData) {
const aceites = JSON.parse(storedData); // Converte o JSON em um objeto
console.log('Dados recuperados do sessionStorage:', aceites);
// Atualiza os dados na tabela
this.dataSource.data = aceites;
// Configurações do paginator e sort
if (this.paginator && this.sort) {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
// Atualiza a visualização
this.cdr.detectChanges();
// Remove dados do sessionStorage após o uso
sessionStorage.removeItem('aceitesSearch');
console.log('aceitesSearch removido do sessionStorage.');
return; // Retorna para evitar chamada à API
} else {
console.warn('Nenhum dado encontrado no sessionStorage.');
}
}
// Caso os dados não estejam no sessionStorage, chama a API
const pageIndex = event ? event.pageIndex + 1 : this.currentPage - 1;
this._aceiteService.getAceiteList(pageIndex, this.pageSize).subscribe({
next: (res: any) => {
const aceites = res._embedded.acceptResponseList;
console.log(aceites);
this.dataSource.data = aceites;
this.totalItems = res.page.totalElements;
console.log(res.page.totalElements);
},
error: (err: any) => {
console.error(err);
if (err.error.title) {
this._coreService.openSnackBar(err.error.title);
} else {
this._coreService.openSnackBar(err.error.message);
}
},
});
}
applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value;
this.dataSource.filter = filterValue.trim().toLowerCase();
if (this.dataSource.paginator) {
this.dataSource.paginator.firstPage();
}
}
deleteBerco(id: number) {
this._aceiteService.deleteEmployee(id).subscribe({
next: () => {
this._coreService.openSnackBar('Aceite deleted!', 'done');
this.loadData();
},
error: (err: any) => {
this._coreService.openSnackBar(err.error.title);
},
});
this.cdr.detectChanges();
}
openEditForm(data: any) {
this._dialog.open(AceiteEditComponent, {
data,
autoFocus: true, // Foca no primeiro campo interativo do formulário
disableClose: true, // Impede que o diálogo feche ao clicar fora
width: '600px', // Define a largura do diálogo
});
this.cdr.detectChanges();
}
openSearchBercoForm() {
this._dialog.open(AceiteSearchComponent, {
autoFocus: true, // Foca no primeiro campo interativo do formulário
disableClose: true, // Impede que o diálogo feche ao clicar fora
width: '600px' // Define a largura do diálogo
});
// Verifica se o sessionStorage está disponível
if (typeof sessionStorage !== 'undefined') {
const storedData = sessionStorage.getItem('aceitesSearch');
if (storedData) {
const dataSource = JSON.parse(storedData); // Converte o JSON em um objeto
console.log('Dados recuperados do sessionStorage:', dataSource);
this.dataSource.data = dataSource;
this.cdr.detectChanges();
// Atualiza a tabela com os dados
} else {
console.warn('Nenhum dado encontrado no sessionStorage.');
}
} else {
console.error('sessionStorage não está disponível.');
}
}
//
openAddBercoForm() {
this._dialog.open(AceiteAddComponent,{autoFocus: true, // Foca no primeiro campo interativo do formulário
disableClose: true, // Impede que o diálogo feche ao clicar fora
width: '600px', // Define a largura do diálogo});
});}
getBercoById(data: any) {
this._dialog.open(VisualizacaoAceiteComponent, {
data,
autoFocus: true, // Foca no primeiro campo interativo do formulário
width: '600px', // Define a largura do diálogo
});
}
validationToken() {
this.authService.authToken$.subscribe((isLoggedIn) => {
this.authTokenPresent = isLoggedIn;
if (this.authTokenPresent) {
const token = sessionStorage.getItem('accessToken')!;
this.user = jwtDecode(token);
const isExpired =
this.user && this.user.exp
? this.user.exp < Date.now() / 1000
: false;
if (isExpired) {
sessionStorage.removeItem('accessToken');
}
this.user = this.jwtDecoderService.decodeToken(token);
console.log(this.user);
}
this.cdr.detectChanges();
});
}
}
<h1 mat-dialog-title class="mat-dialog-title">Aceite</h1>
<mat-dialog-content>
<mat-tab-group class="mat-tab-group">
<mat-tab label="Informações">
<mat-card>
<mat-card-header>
<mat-card-title class="mat-card-title">Aceite</mat-card-title>
<mat-card-subtitle class="mat-card-subtitle">
Nome do Navio: {{ data.nome }}
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div class="row">
<p class="field full-width" appearance="outline">ID: {{ data.id }}</p>
<p class="field full-width" appearance="outline">
USUÁRIO: {{ parseUserString(data.user)?.email || "Sem email" }}
</p>
<p class="field full-width" appearance="outline">
IMO: {{ data.imo || "Sem informação" }}
</p>
<p class="field full-width" appearance="outline">
STATUS:
<ng-container [ngSwitch]="data.status">
<span *ngSwitchCase="'Y'" [ngStyle]="{ color: 'green' }">Navio Aceito</span>
<span *ngSwitchCase="'NE'" [ngStyle]="{ color: 'red' }">Aceite Negado</span>
<span *ngSwitchCase="'N'" [ngStyle]="{ color: 'orange' }">Em processamento</span>
</ng-container>
</p>
</div>
</mat-card-content>
</mat-card>
<ng-container *ngIf="data.path">
<mat-card-actions>
<button (click)="downloadNavioFunction()">Download Documento Q88</button>
</mat-card-actions>
</ng-container>
</mat-tab>
<mat-tab label="Status do Aceite">
<mat-card>
<mat-card-header>
<mat-card-title class="mat-card-title">Aceite</mat-card-title>
<mat-card-subtitle class="mat-card-subtitle">
Nome do Navio: {{ data.nome }}
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div class="row">
<ng-container [ngSwitch]="data.status">
<div *ngSwitchCase="'N'">
<p class="field full-width" appearance="outline">
Status: <span [ngStyle]="{ color: 'orange' }">Em processamento</span>
</p>
<p class="field full-width" appearance="outline">
O gerente está analisando a solicitação para verificar as especificações.
</p>
</div>
<div *ngSwitchCase="'Y'">
<p class="field full-width" appearance="outline">
Status: <span [ngStyle]="{ color: 'green' }">Navio Aceito</span>
</p>
@if(data.restricoes){
<p class="field full-width" appearance="outline">
{{data.restricoes}}
</p>
}@else{
<p class="field full-width" appearance="outline">
O navio é compatível com os seguintes berços:
</p>
<div *ngIf="data?.bercos?.length > 0">
<div *ngFor="let berco of data.bercos">
{{ berco.nome }}
</div>
</div>
}
</div>
<div *ngSwitchCase="'NE'">
<p class="field full-width" appearance="outline">
Status: <span [ngStyle]="{ color: 'red' }">Aceite Negado</span>
</p>
<p class="field full-width" appearance="outline">
{{data.restricoes}}
</p>
</div>
</ng-container>
</div>
</mat-card-content>
</mat-card>
</mat-tab>
</mat-tab-group>
</mat-dialog-content>
<mat-dialog-actions>
<!-- Botões de ação, se necessário -->
</mat-dialog-actions>
.card {
padding-top: 10px;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-bottom: 20px;
}
.field {
margin-bottom: 5px;
}
.field.full-width {
flex: 1 1 100%;
}
.field.large {
flex: 1 1 60%;
}
.field.medium {
flex: 1 1 40%;
}
.field.small {
flex: 1 1 20%;
}
.field.row-gap {
display: grid;
row-gap: 50px;
}
.action {
display: flex;
justify-content: flex-end;
padding: 5px 0;
gap: 10px;
margin: 10px; /* Adiciona margem ao redor do container do botão */
}
@media (max-width: 600px) {
.field.large, .field.medium, .field.small {
flex: 1 1 100%;
}
}
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { VisualizacaoAceiteComponent } from './visualizacao-aceite.component';
describe('VisualizacaoAceiteComponent', () => {
let component: VisualizacaoAceiteComponent;
let fixture: ComponentFixture<VisualizacaoAceiteComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [VisualizacaoAceiteComponent]
})
.compileComponents();
fixture = TestBed.createComponent(VisualizacaoAceiteComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatTabsModule } from '@angular/material/tabs';
import { MatDialogModule } from '@angular/material/dialog';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-visualizacao-aceite',
standalone: true,
imports: [
CommonModule,
MatButtonModule,
MatCardModule,
MatTabsModule,
MatDialogModule,
],
templateUrl: './visualizacao-aceite.component.html',
styleUrls: ['./visualizacao-aceite.component.scss'],
})
export class VisualizacaoAceiteComponent implements OnInit {
constructor(@Inject(MAT_DIALOG_DATA) public data: any,
private httpClient: HttpClient ,// Adicione isto
) {}
ngOnInit(): void {
console.log(this.data);
}
parseUserString(userString: string) {
const userRegex = /User\(id=(\d+), email=([^)]+)\)/;
const match = userRegex.exec(userString);
if (match) {
return {
id: parseInt(match[1], 10),
email: match[2],
};
}
return null;
}
downloadNavioFunction() {
this.httpClient.get('http://localhost:8080/api/download-file', {
params: { filename: this.data.path },
responseType: 'blob' // Adiciona 'blob' para lidar com arquivos binários
}).subscribe({
next: (response) => {
// Criação de um URL temporário para o arquivo
const blob = new Blob([response]);
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = this.data.path || 'download'; // Define o nome do arquivo
link.click();
// Libera o objeto URL após o download
window.URL.revokeObjectURL(link.href);
console.log('Download iniciado:', this.data.path);
},
error: (error) => {
console.error('Erro ao baixar o arquivo:', error);
}
});
}}
<div mat-dialog-title>
<h1>{{ 'Adicionar Berço' }}</h1>
</div>
<form [formGroup]="bercoForm" (ngSubmit)="onFormSubmit()">
<div mat-dialog-content class="content">
<div class="row">
<mat-form-field class="field full-width" appearance="outline">
<mat-label>Nome</mat-label>
<input matInput type="text" formControlName="nome">
<mat-error *ngIf="bercoForm.get('nome')?.invalid">{{ getErrorMessage('nome') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field full-medium" appearance="outline">
<mat-label>Comprimento Estrutural</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="compri_estrutural">
<mat-error *ngIf="bercoForm.get('compri_estrutural')?.invalid">{{ getErrorMessage('compri_estrutural') }}</mat-error>
</mat-form-field>
<mat-form-field class="field full-small" appearance="outline">
<mat-label>Comprimento Útil</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="compri_util">
<mat-error *ngIf="bercoForm.get('compri_util')?.invalid">{{ getErrorMessage('compri_util') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field full-medium" appearance="outline">
<mat-label>DWT</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="dwt">
<mat-error *ngIf="bercoForm.get('dwt')?.invalid">{{ getErrorMessage('dwt') }}</mat-error>
</mat-form-field>
<mat-form-field class="field full-small" appearance="outline">
<mat-label>Largura</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="largura">
<mat-error *ngIf="bercoForm.get('largura')?.invalid">{{ getErrorMessage('largura') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field full-medium" appearance="outline">
<mat-label>Profundidade</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="profundidade">
<mat-error *ngIf="bercoForm.get('profundidade')?.invalid">{{ getErrorMessage('profundidade') }}</mat-error>
</mat-form-field>
<mat-form-field class="field full-small" appearance="outline">
<mat-label>Calado Máximo</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="calado_max">
<mat-error *ngIf="bercoForm.get('calado_max')?.invalid">{{ getErrorMessage('calado_max') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field medium" appearance="outline">
<mat-label>Boca Máxima</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="boca_max">
<mat-error *ngIf="bercoForm.get('boca_max')?.invalid">{{ getErrorMessage('boca_max') }}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-label>LOA Máxima</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="loa_max">
<mat-error *ngIf="bercoForm.get('loa_max')?.invalid">{{ getErrorMessage('loa_max') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label>
<mat-select formControlName="categoria">
<mat-option *ngFor="let cat of categoria" [value]="cat.id">
{{ cat.nome }}
</mat-option>
</mat-select>
<mat-error *ngIf="bercoForm.get('categoria')?.invalid">{{ getErrorMessage('categoria') }}</mat-error>
</mat-form-field>
</div>
</div>
<div mat-dialog-actions class="action">
<button mat-raised-button type="button" color="warn" [mat-dialog-close]="false">Cancelar</button>
<button mat-raised-button color="primary" type="submit">Salvar</button>
</div>
</form>
.content {
padding-top: 10px;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 5px;
}
.file-upload {
display: flex;
align-items: center;
}
.upload-btn {
margin-left: 10px; /* Ajuste conforme necessário */
}
.uploaded-image {
margin-left: 10px; /* Ajuste conforme necessário */
max-height: 100px; /* Ajuste conforme necessário */
}
.field {
margin-bottom: 5px;
}
.field.full-width {
flex: 1 1 100%;
}
.field.large {
flex: 1 1 60%;
}
.field.medium {
flex: 1 1 40%;
}
.field.small {
flex: 1 1 20%;
}
.action {
display: flex;
justify-content: flex-end;
padding: 5px 0;
gap: 10px;
margin: 10px; /* Adiciona margem ao redor do container do botão */
}
@media (max-width: 600px) {
.field.large, .field.medium, .field.small {
flex: 1 1 100%;
}
}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BercoAddComponent } from './berco-add.component';
describe('BercoAddComponent', () => {
let component: BercoAddComponent;
let fixture: ComponentFixture<BercoAddComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [BercoAddComponent]
})
.compileComponents();
fixture = TestBed.createComponent(BercoAddComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, Inject, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { NavioService } from '../../../services/navio.service';
import { CoreService } from '../../../core/core.service';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatSelectModule } from '@angular/material/select';
import { MatRadioModule } from '@angular/material/radio';
import { MatIconModule } from '@angular/material/icon'; // Corrigido
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatDialogModule } from '@angular/material/dialog';
import { ToastrService } from 'ngx-toastr';
import { BercosComponent } from '../bercos/bercos.component';
import { BercosService } from '../../../services/bercos/bercos.service';
@Component({
selector: 'app-berco-add',
standalone: true,
imports: [
MatDialogModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
MatButtonModule,
MatInputModule,
MatFormFieldModule,
MatDatepickerModule,
MatSelectModule,
MatRadioModule,
MatIconModule,
MatSnackBarModule,
BercosComponent,
], templateUrl: './berco-add.component.html',
styleUrl: './berco-add.component.scss'
})
export class BercoAddComponent implements OnInit {
bercoForm: FormGroup;
categoria = [
{ id: 1, nome: 'Granel Sólido' },
{ id: 2, nome: 'Granel Líquido' },
{ id: 3, nome: 'Carga Geral' }
];
constructor(
private _fb: FormBuilder,
private _bercoService: BercosService,
private _dialogRef: MatDialogRef<BercoAddComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private toastService: ToastrService,
) {
this.bercoForm = this._fb.group({
nome: ['', Validators.required],
compri_estrutural: ['', Validators.required],
compri_util: ['', Validators.required],
dwt: ['', Validators.required],
largura: ['', Validators.required],
profundidade: ['', Validators.required],
calado_max: ['', Validators.required],
boca_max: ['', Validators.required],
loa_max: ['', Validators.required],
categoria:['', Validators.required]
});
}
preventNegative(event: KeyboardEvent): void {
if (event.key === '-' || event.key === '+' || event.key === 'e') {
event.preventDefault();
}
}
ngOnInit(): void {
if (this.data) {
this.bercoForm.patchValue(this.data.bercoForm);
}
}
getErrorMessage(formControlName: string): string {
if (this.bercoForm.get(formControlName)?.hasError('required')) {
return 'Campo obrigatório';
}
return '';
}
onFormSubmit() {
if (this.bercoForm.valid) {
console.log(this.bercoForm.value)
this._bercoService.addEmployee(this.bercoForm.value).subscribe({
next: (val: any) => {
this.toastService.success('Berço adicionado com sucesso');
this._dialogRef.close(true);
},
error: (err: any) => {
console.error(err);
}
});
}
}
}
\ No newline at end of file
<div mat-dialog-title>
<h1>{{ 'Editar Berço' }}</h1>
</div>
<form [formGroup]="bercoForm" (ngSubmit)="onFormSubmit()">
<div mat-dialog-content class="content">
<div class="row">
<mat-form-field class="field full-width" appearance="outline">
<mat-label>Nome</mat-label>
<input matInput type="text" formControlName="nome">
<mat-error *ngIf="bercoForm.get('nome')?.invalid">{{ getErrorMessage('nome') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field full-medium" appearance="outline">
<mat-label>Comprimento Estrutural</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="compri_estrutural">
<mat-error *ngIf="bercoForm.get('compri_estrutural')?.invalid">{{ getErrorMessage('compri_estrutural') }}</mat-error>
</mat-form-field>
<mat-form-field class="field full-small" appearance="outline">
<mat-label>Comprimento Útil</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="compri_util">
<mat-error *ngIf="bercoForm.get('compri_util')?.invalid">{{ getErrorMessage('compri_util') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field full-medium" appearance="outline">
<mat-label>DWT</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="dwt">
<mat-error *ngIf="bercoForm.get('dwt')?.invalid">{{ getErrorMessage('dwt') }}</mat-error>
</mat-form-field>
<mat-form-field class="field full-small" appearance="outline">
<mat-label>Largura</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="largura">
<mat-error *ngIf="bercoForm.get('largura')?.invalid">{{ getErrorMessage('largura') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field full-medium" appearance="outline">
<mat-label>Profundidade</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="profundidade">
<mat-error *ngIf="bercoForm.get('profundidade')?.invalid">{{ getErrorMessage('profundidade') }}</mat-error>
</mat-form-field>
<mat-form-field class="field full-small" appearance="outline">
<mat-label>Calado Máximo</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="calado_max">
<mat-error *ngIf="bercoForm.get('calado_max')?.invalid">{{ getErrorMessage('calado_max') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field medium" appearance="outline">
<mat-label>Boca Máxima</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="boca_max">
<mat-error *ngIf="bercoForm.get('boca_max')?.invalid">{{ getErrorMessage('boca_max') }}</mat-error>
</mat-form-field>
<mat-form-field class="field small" appearance="outline">
<mat-label>LOA Máxima</mat-label>
<input matInput type="number" (keydown)="preventNegative($event)" min="0" formControlName="loa_max">
<mat-error *ngIf="bercoForm.get('loa_max')?.invalid">{{ getErrorMessage('loa_max') }}</mat-error>
</mat-form-field>
</div>
<div class="row">
<mat-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label>
<mat-select formControlName="categoria">
<mat-option *ngFor="let cat of categoria" [value]="cat.id">
{{ cat.nome }}
</mat-option>
</mat-select>
<mat-error *ngIf="bercoForm.get('categoria')?.invalid">{{ getErrorMessage('categoria') }}</mat-error>
</mat-form-field>
</div>
</div>
<div mat-dialog-actions class="action">
<button mat-raised-button type="button" color="warn" [mat-dialog-close]="false">Cancelar</button>
<button mat-raised-button color="primary" type="submit">Salvar</button>
</div>
</form>
.content {
padding-top: 10px;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-bottom: 20px;
}
.field {
margin-bottom: 5px;
}
.field.full-width {
flex: 1 1 100%;
}
.field.large {
flex: 1 1 60%;
}
.field.medium {
flex: 1 1 40%;
}
.field.small {
flex: 1 1 20%;
}
.action {
display: flex;
justify-content: flex-end;
padding: 5px 0;
gap: 10px;
margin: 10px; /* Adiciona margem ao redor do container do botão */
}
@media (max-width: 600px) {
.field.large, .field.medium, .field.small {
flex: 1 1 100%;
}
}
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BercoEditComponent } from './berco-edit.component';
describe('BercoEditComponent', () => {
let component: BercoEditComponent;
let fixture: ComponentFixture<BercoEditComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [BercoEditComponent]
})
.compileComponents();
fixture = TestBed.createComponent(BercoEditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, Inject, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatSelectModule } from '@angular/material/select';
import { MatRadioModule } from '@angular/material/radio';
import { MatIconModule } from '@angular/material/icon';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatDialogModule } from '@angular/material/dialog';
import { ToastrService } from 'ngx-toastr';
import { BercosComponent } from '../bercos/bercos.component';
import { BercosService } from '../../../services/bercos/bercos.service';
@Component({
selector: 'app-berco-edit',
standalone: true,
imports: [
MatDialogModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
MatButtonModule,
MatInputModule,
MatFormFieldModule,
MatDatepickerModule,
MatSelectModule,
MatRadioModule,
MatIconModule,
MatSnackBarModule,
BercosComponent,
],
templateUrl: './berco-edit.component.html',
styleUrl: './berco-edit.component.scss'
})
export class BercoEditComponent implements OnInit {
bercoForm: FormGroup;
constructor(
private _fb: FormBuilder,
private _bercoService: BercosService,
private _dialogRef: MatDialogRef<BercoEditComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private toastService: ToastrService,
) {
this.bercoForm = this._fb.group({
nome: ['', Validators.required],
compri_estrutural: ['', Validators.required],
compri_util: ['', Validators.required],
dwt: ['', Validators.required],
largura: ['', Validators.required],
profundidade: ['', Validators.required],
calado_max: ['', Validators.required],
boca_max: ['', Validators.required],
loa_max: ['', Validators.required],
categoria:['', Validators.required]
});
}
categoria = [
{ id: 1, nome: 'Granel Sólido' },
{ id: 2, nome: 'Granel Líquido' },
{ id: 3, nome: 'Carga Geral' }
];
preventNegative(event: KeyboardEvent): void {
if (event.key === '-' || event.key === '+' || event.key === 'e') {
event.preventDefault();
}
}
ngOnInit(): void {
if (this.data) {
console.log(this.data)
this.bercoForm.patchValue(this.data);
}
}
getErrorMessage(formControlName: string): string {
if (this.bercoForm.get(formControlName)?.hasError('required')) {
return 'Campo obrigatório';
}
return '';
}
onFormSubmit() {
if (this.bercoForm.valid) {
if (this.data) {
this._bercoService.updateEmployee(this.data.id, this.bercoForm.value).subscribe({
next: (val: any) => {
this.toastService.success('Detalhes do berço atualizados!');
this._dialogRef.close(true);
},
error: (err: any) => {
console.error(err);
},
});
}
}
}
}
\ No newline at end of file
<div mat-dialog-title>
<h1>{{ "Buscar Berço" }}</h1>
</div>
<form [formGroup]="empForm" (ngSubmit)="onFormSubmit()">
<div mat-dialog-content class="content">
<div class="row">
<!-- Nome -->
<mat-form-field class="field full-width" appearance="outline">
<mat-label>Nome</mat-label>
<input matInput type="text" formControlName="nome" />
<mat-error *ngIf="empForm.get('nome')?.invalid">{{
getErrorMessage("nome")
}}</mat-error>
</mat-form-field>
<!-- Categoria -->
<mat-form-field class="field medium" appearance="outline">
<mat-label>Categoria</mat-label>
<mat-select formControlName="categoria">
<mat-option *ngFor="let cat of categoria" [value]="cat">
{{ cat === 1 ? "Granel Sólido" : "Granel Líquido" }}
</mat-option>
</mat-select>
<mat-error *ngIf="empForm.get('categoria')?.invalid">{{
getErrorMessage("categoria")
}}</mat-error>
</mat-form-field>
</div>
</div>
<div mat-dialog-actions class="action">
<button
mat-raised-button
type="button"
color="warn"
[mat-dialog-close]="false"
>
Cancelar
</button>
<button mat-raised-button color="primary" type="submit">Buscar</button>
</div>
</form>
.content {
padding-top: 10px;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-bottom: 20px;
}
.field {
margin-bottom: 5px;
}
.field.full-width {
flex: 1 1 100%;
}
.field.large {
flex: 1 1 60%;
}
.field.medium {
flex: 1 1 40%;
}
.field.small {
flex: 1 1 20%;
}
.action {
display: flex;
justify-content: flex-end;
padding: 5px 0;
gap: 10px;
margin: 10px; /* Adiciona margem ao redor do container do botão */
}
@media (max-width: 600px) {
.field.large, .field.medium, .field.small {
flex: 1 1 100%;
}
}
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BercoSearchComponent } from './berco-search.component';
describe('BercoSearchComponent', () => {
let component: BercoSearchComponent;
let fixture: ComponentFixture<BercoSearchComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [BercoSearchComponent]
})
.compileComponents();
fixture = TestBed.createComponent(BercoSearchComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
This diff is collapsed.
This diff is collapsed.
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BercosComponent } from './bercos.component';
describe('BercosComponent', () => {
let component: BercosComponent;
let fixture: ComponentFixture<BercosComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [BercosComponent]
})
.compileComponents();
fixture = TestBed.createComponent(BercosComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment