Modernizacao de componentes
This commit is contained in:
parent
e40e93280d
commit
71bc1b6b6e
BIN
public/logo.jpg
BIN
public/logo.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 5.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
|
|
@ -1,6 +1,10 @@
|
|||
import { Routes } from '@angular/router';
|
||||
import { Home } from './pages/home/home';
|
||||
import { Register } from './pages/register/register';
|
||||
import { LoginComponent } from './pages/login/login';
|
||||
|
||||
export const routes: Routes = [
|
||||
{ path: '', component: Home },
|
||||
{ path: "register", component: Register },
|
||||
{ path: "login", component: LoginComponent },
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
<button
|
||||
type="button"
|
||||
(click)="onClick()"
|
||||
[style.width.px]="width"
|
||||
[style.height.px]="height"
|
||||
[style.width]="width"
|
||||
[style.height]="height"
|
||||
[style.background]="background"
|
||||
[style.color]="color"
|
||||
[style.font-size.px]="fontSize"
|
||||
[style.font-size]="fontSize"
|
||||
[style.font-weight]="fontWeight"
|
||||
class="cta-button"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export class CtaButtonComponent {
|
|||
@Input() height: string = '55px';
|
||||
@Input() background: string = '#C91EB5';
|
||||
@Input() color: string = '#FFFFFF';
|
||||
@Input() fontSize: string = '18px';
|
||||
@Input() fontSize: string = '16.5px';
|
||||
@Input() fontWeight: string = '700';
|
||||
|
||||
@Output() clicked = new EventEmitter<void>();
|
||||
|
|
|
|||
|
|
@ -1,122 +1,79 @@
|
|||
/* ===================================== */
|
||||
/* FOOTER CONTAINER */
|
||||
/* FOOTER CONTAINER – VERSÃO MODERNA */
|
||||
/* ===================================== */
|
||||
|
||||
.footer-container {
|
||||
width: 100%;
|
||||
background: rgba(3, 15, 170, 0.93);
|
||||
padding: 10px 60px; /* ⬅️ TOPO/FUNDO DO FOOTER (antes 16px 60px) */
|
||||
/* Degradê com as cores da marca */
|
||||
background: linear-gradient(90deg, #030FAA 0%, #6066FF 45%, #C91EB5 100%);
|
||||
padding: 10px 32px; /* bem mais baixo que antes */
|
||||
box-sizing: border-box;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
|
||||
font-family: "Inter", sans-serif;
|
||||
color: #FFFFFF;
|
||||
|
||||
/* 💻 NOTEBOOKS 1200–1399px */
|
||||
@media (min-width: 1200px) and (max-width: 1399.98px) {
|
||||
padding: 6px 50px; /* ⬅️ TOPO/FUNDO NO NOTEBOOK (antes 8px 50px) */
|
||||
}
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.12);
|
||||
|
||||
/* Telas < 1200px — empilha esquerda e direita */
|
||||
/* Suave sombra pra destacar do conteúdo */
|
||||
box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.12);
|
||||
|
||||
/* Telas médias e abaixo – empilha conteúdo */
|
||||
@media (max-width: 1199.98px) {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 14px 30px; /* ⬅️ TOPO/FUNDO EM TELAS MÉDIAS (antes 18px 30px) */
|
||||
text-align: center;
|
||||
padding: 12px 20px;
|
||||
}
|
||||
|
||||
/* Tablets e celulares */
|
||||
@media (max-width: 768px) {
|
||||
padding: 14px 20px; /* ⬅️ TOPO/FUNDO EM MOBILE (antes 18px 20px) */
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===================================== */
|
||||
/* LADO ESQUERDO */
|
||||
/* LADO ESQUERDO (TEXTOS) */
|
||||
/* ===================================== */
|
||||
|
||||
.footer-left {
|
||||
margin-top: 100px; /* ⬅️ DISTÂNCIA DO TEXTO PRO TOPO NO DESKTOP (antes 100px) */
|
||||
|
||||
/* 💻 NOTEBOOKS 1200–1399px */
|
||||
@media (min-width: 1200px) and (max-width: 1399.98px) {
|
||||
margin-top: 80px; /* ⬅️ DISTÂNCIA NO NOTEBOOK (antes 100px) */
|
||||
}
|
||||
|
||||
/* Telas < 1200px */
|
||||
@media (max-width: 1199.98px) {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
margin-top: 16px;
|
||||
}
|
||||
margin: 0; /* remove aqueles 100px enormes de antes */
|
||||
}
|
||||
|
||||
.footer-left p {
|
||||
/* 🔁 PADRÃO PARA DESKTOPS E NOTEBOOKS: MESMO ESPAÇAMENTO HORIZONTAL */
|
||||
margin: 0 0 4px 120px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
|
||||
/* Telas < 1200px */
|
||||
@media (max-width: 1199.98px) {
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
margin: 0 0 2px 0; /* menos espaçamento vertical */
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
margin-left: 0;
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
font-size: 12px;
|
||||
font-size: 11.5px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===================================== */
|
||||
/* LADO DIREITO */
|
||||
/* LADO DIREITO (REDES + BOTÃO) */
|
||||
/* ===================================== */
|
||||
|
||||
.footer-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 100px; /* ⬅️ DISTÂNCIA DO BLOCO DIREITO PRO TOPO (antes 100px) */
|
||||
gap: 0px;
|
||||
align-items: flex-end;
|
||||
gap: 8px;
|
||||
|
||||
margin-right: 120px; /* espaçamento horizontal já ajustado */
|
||||
|
||||
/* 💻 NOTEBOOKS 1200–1399px */
|
||||
@media (min-width: 1200px) and (max-width: 1399.98px) {
|
||||
margin-top: 80px; /* ⬅️ MESMO IDEA DO ESQUERDO (antes 100px) */
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* Telas < 1200px */
|
||||
@media (max-width: 1199.98px) {
|
||||
margin-top: 20px;
|
||||
margin-right: 0;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
align-items: flex-start;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -125,21 +82,14 @@
|
|||
.social-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 0;
|
||||
|
||||
@media (min-width: 1200px) and (max-width: 1399.98px) {
|
||||
margin-top: 4px;
|
||||
}
|
||||
justify-content: flex-end;
|
||||
|
||||
@media (max-width: 1199.98px) {
|
||||
justify-content: center;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
justify-content: flex-start;
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +97,6 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 0;
|
||||
|
||||
@media (max-width: 480px) {
|
||||
gap: 6px;
|
||||
|
|
@ -155,42 +104,41 @@
|
|||
}
|
||||
|
||||
.social-label {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
|
||||
@media (max-width: 480px) {
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.social-icon i {
|
||||
font-size: 22px;
|
||||
font-size: 20px;
|
||||
color: #FFF;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
transition: transform 0.15s ease, opacity 0.15s ease;
|
||||
|
||||
@media (max-width: 480px) {
|
||||
font-size: 19px;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.social-icon i:hover {
|
||||
opacity: 0.7;
|
||||
opacity: 0.8;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Botão */
|
||||
.footer-button-wrapper {
|
||||
margin-top: 0;
|
||||
/* Botão Política de Privacidade */
|
||||
|
||||
@media (min-width: 1200px) and (max-width: 1399.98px) {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.footer-button-wrapper {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
@media (max-width: 1199.98px) {
|
||||
margin-top: 6px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
margin-top: 10px;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
<header class="header-container">
|
||||
<header
|
||||
class="header-container"
|
||||
[class.header-scrolled]="isScrolled">
|
||||
|
||||
<div class="header-top">
|
||||
|
||||
<!-- LOGO + TÍTULO (CLICÁVEIS) -->
|
||||
<a class="logo-area" routerLink="/"> <!-- ⬅️ AGORA É UM LINK ANGULAR -->
|
||||
<img src="logo.jpg" alt="Logo" class="logo">
|
||||
<img src="logo.png" alt="Logo" class="logo">
|
||||
|
||||
<div class="logo-text ms-2">
|
||||
<span class="line">Line</span><span class="gestao">Gestão</span>
|
||||
|
|
@ -21,15 +23,30 @@
|
|||
|
||||
<!-- BOTÕES -->
|
||||
<div class="btn-area">
|
||||
<button class="btn btn-cadastrar">Cadastre-se</button>
|
||||
<button class="btn btn-login">Login</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-cadastrar"
|
||||
[routerLink]="['/register']">
|
||||
Cadastre-se
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-login"
|
||||
[routerLink]="['/login']">
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<!-- FAIXA AZUL -->
|
||||
<div class="header-bar">
|
||||
<span class="header-bar-text">Somos a escolha certa para estar sempre conectado!</span>
|
||||
<!-- FAIXA AZUL – SÓ NA HOME -->
|
||||
<div class="header-bar" *ngIf="isHome">
|
||||
<span class="header-bar-text">
|
||||
Somos a escolha certa para estar sempre conectado!
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -2,13 +2,41 @@
|
|||
/* HEADER PRINCIPAL */
|
||||
/* ===================== */
|
||||
.header-container {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
/* mais transparente mesmo no topo */
|
||||
background: rgba(255, 255, 255, 0.75);
|
||||
font-family: 'Inter', sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
backdrop-filter: blur(6px);
|
||||
-webkit-backdrop-filter: blur(6px);
|
||||
|
||||
transition:
|
||||
background-color 0.25s ease,
|
||||
backdrop-filter 0.25s ease,
|
||||
-webkit-backdrop-filter 0.25s ease,
|
||||
box-shadow 0.25s ease,
|
||||
border-color 0.25s ease;
|
||||
}
|
||||
|
||||
/* HEADER “FOSCO” QUANDO SCROLLADO – BEM TRANSPARENTE */
|
||||
.header-container.header-scrolled {
|
||||
background: rgba(255, 255, 255, 0.22); /* quase vidro puro */
|
||||
backdrop-filter: blur(14px);
|
||||
-webkit-backdrop-filter: blur(14px);
|
||||
|
||||
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12);
|
||||
border-bottom: 1px solid rgba(201, 30, 181, 0.3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* ===================== */
|
||||
/* TOP AREA (LOGO + MENU + BOTÕES) */
|
||||
/* ===================== */
|
||||
|
|
@ -314,28 +342,39 @@
|
|||
/* ===================== */
|
||||
.header-bar {
|
||||
width: 100%;
|
||||
height: 38.41px;
|
||||
background: rgba(3, 15, 170, 0.93);
|
||||
height: 34px; /* um pouco mais baixa */
|
||||
background: linear-gradient(90deg, #030FAA 0%, #6066FF 45%, #C91EB5 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.25);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
|
||||
|
||||
@media (max-width: 480px) {
|
||||
height: 34px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-bar-text {
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
|
||||
/* NOTEBOOKS 1200–1399px */
|
||||
@media (min-width: 1200px) and (max-width: 1399.98px) {
|
||||
font-size: 15px;
|
||||
}
|
||||
/* “pill” para destacar o texto */
|
||||
padding: 4px 14px;
|
||||
border-radius: 999px;
|
||||
background: rgba(0, 0, 0, 0.12);
|
||||
backdrop-filter: blur(4px);
|
||||
-webkit-backdrop-filter: blur(4px);
|
||||
|
||||
@media (max-width: 480px) {
|
||||
font-size: 14px;
|
||||
font-size: 11.5px;
|
||||
padding: 3px 10px;
|
||||
letter-spacing: 0.06em;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,30 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { Component, HostListener } from '@angular/core';
|
||||
import { RouterLink, Router, NavigationEnd } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
standalone: true, // ⬅️ importante para usar `imports` aqui
|
||||
imports: [RouterLink], // ⬅️ habilita o uso de routerLink no template
|
||||
standalone: true,
|
||||
imports: [RouterLink, CommonModule], // ⬅️ CommonModule para usar *ngIf
|
||||
templateUrl: './header.html',
|
||||
styleUrl: './header.scss',
|
||||
styleUrls: ['./header.scss'],
|
||||
})
|
||||
export class Header { }
|
||||
export class Header {
|
||||
isScrolled = false;
|
||||
isHome = true; // valor inicial (ao abrir normalmente cai na home)
|
||||
|
||||
constructor(private router: Router) {
|
||||
// escuta mudanças de rota para saber se está na home
|
||||
this.router.events.subscribe((event) => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
const url = event.urlAfterRedirects || event.url;
|
||||
this.isHome = (url === '/' || url === '');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@HostListener('window:scroll', [])
|
||||
onWindowScroll() {
|
||||
this.isScrolled = window.scrollY > 10; // passou 10px de scroll, ativa o “fosco”
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,9 +50,13 @@
|
|||
|
||||
<div class="row justify-content-center button-section">
|
||||
<div class="col-auto">
|
||||
<app-cta-button label="COMEÇAR AGORA" (clicked)="iniciar()"></app-cta-button>
|
||||
<app-cta-button
|
||||
label="COMEÇAR AGORA"
|
||||
(clicked)="iniciar()">
|
||||
</app-cta-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,34 +1,29 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common'; // Importa CommonModule
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FeatureCardComponent } from '../../components/feature-card/feature-card';
|
||||
import { CtaButtonComponent } from '../../components/cta-button/cta-button';
|
||||
import { Router } from '@angular/router'; // ⬅️ IMPORT CERTO (ANGULAR)
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
// 1. Marca o componente como autônomo
|
||||
standalone: true,
|
||||
// 2. Importa o que o template precisa:
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule, // Para usar diretivas comuns (ngClass, ngIf, etc.) no seu home.html
|
||||
FeatureCardComponent, // Para usar o seletor <app-feature-card> no seu home.html
|
||||
CommonModule,
|
||||
FeatureCardComponent,
|
||||
CtaButtonComponent
|
||||
],
|
||||
templateUrl: './home.html',
|
||||
styleUrl: './home.scss',
|
||||
styleUrls: ['./home.scss'], // ⬅️ styleUrls (array)
|
||||
})
|
||||
export class Home {
|
||||
// Seus dados dos cards podem ser definidos aqui para uso no home.html:
|
||||
/*
|
||||
cardsData = [
|
||||
{ title: 'Monitoramento Completo', iconClass: 'bi bi-laptop', description: '...' },
|
||||
// ...
|
||||
];
|
||||
*/
|
||||
|
||||
// ⬅️ Aqui o Angular injeta o Router e guarda em this.router
|
||||
constructor(private router: Router) {}
|
||||
|
||||
iniciar(): void {
|
||||
console.log('Botão COMEÇAR AGORA clicado! Redirecionando ou abrindo modal...');
|
||||
// Aqui você adicionaria sua lógica de navegação, como:
|
||||
// this.router.navigate(['/cadastro']);
|
||||
// Ou uma chamada para abrir um modal de registro.
|
||||
// só pra debug, se quiser:
|
||||
// console.log('Botão COMEÇAR AGORA clicado! Redirecionando para /register...');
|
||||
|
||||
this.router.navigate(['/register']); // ⬅️ agora this.router não é mais undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
<div class="login-wrapper">
|
||||
<div class="login-card shadow-sm">
|
||||
|
||||
<!-- Título -->
|
||||
<div class="login-title mb-4">
|
||||
<h2 class="mb-0">Login</h2>
|
||||
</div>
|
||||
|
||||
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
|
||||
|
||||
<!-- Usuário -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Usuário</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
formControlName="username"
|
||||
placeholder="Usuário" />
|
||||
|
||||
<div class="text-danger small mt-1" *ngIf="hasError('username')">
|
||||
Informe o usuário.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Senha -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Senha</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
formControlName="password"
|
||||
placeholder="Senha" />
|
||||
|
||||
<div class="text-danger small mt-1" *ngIf="hasError('password')">
|
||||
A senha deve ter pelo menos 6 caracteres.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Botão Entrar -->
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary w-100 login-btn-submit"
|
||||
[disabled]="isSubmitting">
|
||||
{{ isSubmitting ? 'Entrando...' : 'ENTRAR' }}
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/* ========================= */
|
||||
/* TELA DE LOGIN */
|
||||
/* ========================= */
|
||||
|
||||
/* Wrapper para centralizar o card entre header e footer */
|
||||
.login-wrapper {
|
||||
min-height: calc(100vh - 60px);
|
||||
|
||||
display: flex;
|
||||
justify-content: center; /* login fica no centro */
|
||||
align-items: center;
|
||||
|
||||
padding-top: 32px;
|
||||
padding-right: 12px;
|
||||
padding-bottom: 100px; /* mesmo “respiro” do cadastro */
|
||||
padding-left: 12px;
|
||||
|
||||
background: url('../../../assets/wallpaper/registro_qualidade3.png')
|
||||
no-repeat right center;
|
||||
background-size: contain;
|
||||
background-color: #efefef;
|
||||
|
||||
@media (max-width: 992px) {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
min-height: calc(100vh - 40px);
|
||||
padding-top: 24px;
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Card principal – mesmo estilo “glass” do cadastro,
|
||||
mas SEM margin-left e centralizado pelo flex */
|
||||
.login-card {
|
||||
background-color: transparent;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #c91eb5;
|
||||
|
||||
max-width: 500px; /* aumenta a largura como no cadastro */
|
||||
width: 100%;
|
||||
min-height: 380px; /* um pouco menor que o cadastro, mas folgado */
|
||||
padding: 28px 24px; /* mesmo “respiro” interno */
|
||||
box-sizing: border-box;
|
||||
|
||||
backdrop-filter: blur(6px);
|
||||
-webkit-backdrop-filter: blur(6px);
|
||||
|
||||
.mb-3,
|
||||
.mb-4 {
|
||||
margin-bottom: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Título centralizado rosa */
|
||||
.login-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 1.25rem !important;
|
||||
|
||||
h2 {
|
||||
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
color: #c91eb5;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Labels – mesmo tamanho do cadastro */
|
||||
.form-label {
|
||||
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* Inputs iguais aos do cadastro (borda azul, maiores) */
|
||||
.form-control {
|
||||
height: 38px; /* antes 34px */
|
||||
border-radius: 8px;
|
||||
border: 2px solid #6066ff;
|
||||
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 14px; /* antes 13px */
|
||||
color: #000000;
|
||||
|
||||
&::placeholder {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* Botão ENTRAR – rosa sólido, mesmo estilo do cadastrar */
|
||||
.login-btn-submit {
|
||||
border-radius: 40px;
|
||||
border: none;
|
||||
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
padding: 9px 0;
|
||||
background-color: #c91eb5;
|
||||
color: #ffffff;
|
||||
|
||||
&:hover {
|
||||
filter: brightness(1.04);
|
||||
}
|
||||
}
|
||||
|
||||
/* Mensagens de erro */
|
||||
.text-danger.small {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {
|
||||
FormBuilder,
|
||||
FormGroup,
|
||||
Validators,
|
||||
ReactiveFormsModule
|
||||
} from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
standalone: true,
|
||||
imports: [CommonModule, ReactiveFormsModule],
|
||||
templateUrl: './login.html',
|
||||
styleUrls: ['./login.scss']
|
||||
})
|
||||
export class LoginComponent {
|
||||
|
||||
loginForm: FormGroup;
|
||||
isSubmitting = false;
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
this.loginForm = this.fb.group({
|
||||
username: ['', [Validators.required]], // “Usuário” do protótipo
|
||||
password: ['', [Validators.required, Validators.minLength(6)]]
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
if (this.loginForm.invalid) {
|
||||
this.loginForm.markAllAsTouched();
|
||||
return;
|
||||
}
|
||||
|
||||
this.isSubmitting = true;
|
||||
|
||||
// Simulação de envio – preparado para API futura
|
||||
console.log('Dados de login:', this.loginForm.value);
|
||||
|
||||
// FUTURO: aqui você injeta seu AuthService e chama a API .NET:
|
||||
/*
|
||||
this.authService.login(this.loginForm.value).subscribe({
|
||||
next: () => { ... },
|
||||
error: () => { ... },
|
||||
complete: () => this.isSubmitting = false
|
||||
});
|
||||
*/
|
||||
|
||||
setTimeout(() => {
|
||||
this.isSubmitting = false;
|
||||
alert('Login enviado (simulado). Depois conectamos na API .NET 😉');
|
||||
}, 800);
|
||||
}
|
||||
|
||||
hasError(field: string, error?: string): boolean {
|
||||
const control = this.loginForm.get(field);
|
||||
if (!control) return false;
|
||||
if (error) {
|
||||
return control.touched && control.hasError(error);
|
||||
}
|
||||
return control.touched && control.invalid;
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1,108 @@
|
|||
<p>register works!</p>
|
||||
<!-- src/app/pages/register.component.html -->
|
||||
|
||||
<div class="register-wrapper">
|
||||
<div class="register-card shadow-sm">
|
||||
|
||||
<!-- Barra / título -->
|
||||
<div class="register-title mb-4">
|
||||
<span class="register-icon">
|
||||
<i class="bi bi-box-arrow-in-right"></i> <!-- ícone bootstrap -->
|
||||
</span>
|
||||
<h2 class="mb-0">Cadastre-se</h2>
|
||||
</div>
|
||||
|
||||
|
||||
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
|
||||
|
||||
<!-- Nome completo -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nome Completo</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
formControlName="fullName"
|
||||
placeholder="Nome Completo" />
|
||||
|
||||
<div class="text-danger small mt-1" *ngIf="hasError('fullName')">
|
||||
Informe o seu nome completo (mínimo 3 caracteres).
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- E-mail -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">E-mail</label>
|
||||
<input
|
||||
type="email"
|
||||
class="form-control"
|
||||
formControlName="email"
|
||||
placeholder="E-mail" />
|
||||
|
||||
<div class="text-danger small mt-1" *ngIf="hasError('email', 'required')">
|
||||
E-mail é obrigatório.
|
||||
</div>
|
||||
<div class="text-danger small mt-1" *ngIf="hasError('email', 'email')">
|
||||
Informe um e-mail válido.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Telefone -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Telefone</label>
|
||||
<input
|
||||
type="tel"
|
||||
class="form-control"
|
||||
formControlName="phone"
|
||||
placeholder="Telefone" />
|
||||
|
||||
<div class="text-danger small mt-1" *ngIf="hasError('phone')">
|
||||
Informe um telefone válido.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Senha -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Senha</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
formControlName="password"
|
||||
placeholder="Senha" />
|
||||
|
||||
<div class="text-danger small mt-1" *ngIf="hasError('password')">
|
||||
A senha deve ter pelo menos 6 caracteres.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confirmar senha -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Confirme sua senha</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
formControlName="confirmPassword"
|
||||
placeholder="Confirme sua senha" />
|
||||
|
||||
<div class="text-danger small mt-1" *ngIf="registerForm.errors?.['passwordsMismatch'] && registerForm.touched">
|
||||
As senhas não conferem.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Botão Cadastrar (gradiente) -->
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary w-100 register-btn mb-3"
|
||||
[disabled]="isSubmitting">
|
||||
{{ isSubmitting ? 'Cadastrando...' : 'CADASTRAR' }}
|
||||
</button>
|
||||
|
||||
<!-- Botão para quem já tem conta -->
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-primary w-100 login-btn"
|
||||
routerLink="/login">
|
||||
JÁ TEM UMA CONTA? ENTRE
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,155 @@
|
|||
/* ========================= */
|
||||
/* TELA DE CADASTRO */
|
||||
/* ========================= */
|
||||
|
||||
/* Wrapper para centralizar o card entre header e footer */
|
||||
.register-wrapper {
|
||||
/* ocupa quase a tela toda, mas sem exagero */
|
||||
min-height: calc(100vh - 60px);
|
||||
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
/* respiro mais equilibrado */
|
||||
padding-top: 32px;
|
||||
padding-right: 12px;
|
||||
padding-bottom: 100px; /* ainda empurra o footer, mas menos que 140 */
|
||||
padding-left: 5vw;
|
||||
|
||||
background: url('../../../assets/wallpaper/registro_qualidade2.png')
|
||||
no-repeat right center;
|
||||
background-size: contain;
|
||||
background-color: #efefef;
|
||||
|
||||
/* responsivo: em telas menores, centraliza o card e reduz paddings */
|
||||
@media (max-width: 992px) {
|
||||
justify-content: center;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
min-height: calc(100vh - 40px);
|
||||
padding-top: 24px;
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Card principal – AGORA MAIS LARGO */
|
||||
.register-card {
|
||||
background-color: transparent;
|
||||
border-radius: 10px;
|
||||
border: 2px solid #c91eb5;
|
||||
|
||||
max-width: 500px; /* antes 340px ✅ aumenta a largura do card */
|
||||
width: 100%;
|
||||
min-height: 500px; /* leve ajuste pra não ficar “apertado” */
|
||||
padding: 28px 24px; /* um pouco mais de “respiro” interno */
|
||||
box-sizing: border-box;
|
||||
margin-left: 150px;
|
||||
|
||||
backdrop-filter: blur(6px);
|
||||
-webkit-backdrop-filter: blur(6px);
|
||||
|
||||
.mb-3,
|
||||
.mb-4 {
|
||||
margin-bottom: 0.9rem; /* mantém espaçamento confortável */
|
||||
}
|
||||
}
|
||||
|
||||
/* Título: ícone + texto lado a lado */
|
||||
.register-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
margin-bottom: 1.25rem !important;
|
||||
|
||||
h2 {
|
||||
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 32px;
|
||||
color: #c91eb5;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ícone ao lado do título */
|
||||
.register-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #c91eb5;
|
||||
|
||||
i {
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Labels */
|
||||
.form-label {
|
||||
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 14px; /* um pouco maior pra acompanhar o card */
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* Inputs com borda azul do protótipo – levemente maiores */
|
||||
.form-control {
|
||||
height: 38px; /* antes 34px */
|
||||
border-radius: 8px;
|
||||
border: 2px solid #6066ff;
|
||||
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 14px; /* antes 13px */
|
||||
color: #000000;
|
||||
|
||||
&::placeholder {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* Botão principal (CADASTRAR) – rosa sólido */
|
||||
.register-btn {
|
||||
border-radius: 40px;
|
||||
border: none;
|
||||
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 14px; /* um tic maior */
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
padding: 9px 0;
|
||||
background-color: #c91eb5;
|
||||
color: #ffffff;
|
||||
|
||||
&:hover {
|
||||
filter: brightness(1.04);
|
||||
}
|
||||
}
|
||||
|
||||
/* Botão "JÁ TEM UMA CONTA? ENTRE" – azul sólido */
|
||||
.login-btn {
|
||||
border-radius: 40px;
|
||||
border: none;
|
||||
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
padding: 8px 0;
|
||||
background-color: #030faa;
|
||||
color: #ffffff;
|
||||
|
||||
&:hover {
|
||||
filter: brightness(1.04);
|
||||
}
|
||||
}
|
||||
|
||||
/* Mensagens de erro */
|
||||
.text-danger.small {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
|
@ -1,11 +1,75 @@
|
|||
// src/app/pages/register.component.ts
|
||||
import { Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-register',
|
||||
imports: [],
|
||||
standalone: true,
|
||||
imports: [CommonModule, ReactiveFormsModule, RouterLink],
|
||||
templateUrl: './register.html',
|
||||
styleUrl: './register.scss',
|
||||
styleUrls: ['./register.scss'] // ou .scss
|
||||
})
|
||||
export class Register {
|
||||
|
||||
registerForm: FormGroup;
|
||||
isSubmitting = false;
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
this.registerForm = this.fb.group({
|
||||
fullName: ['', [Validators.required, Validators.minLength(3)]],
|
||||
email: ['', [Validators.required, Validators.email]],
|
||||
phone: ['', [Validators.required]],
|
||||
password: ['', [Validators.required, Validators.minLength(6)]],
|
||||
confirmPassword: ['', [Validators.required]]
|
||||
}, {
|
||||
validators: [this.passwordsMatchValidator]
|
||||
});
|
||||
}
|
||||
|
||||
// Validador para confirmar senha
|
||||
private passwordsMatchValidator(group: FormGroup) {
|
||||
const pass = group.get('password')?.value;
|
||||
const confirm = group.get('confirmPassword')?.value;
|
||||
return pass === confirm ? null : { passwordsMismatch: true };
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
if (this.registerForm.invalid) {
|
||||
this.registerForm.markAllAsTouched();
|
||||
return;
|
||||
}
|
||||
|
||||
this.isSubmitting = true;
|
||||
|
||||
// Por enquanto: apenas exibe os dados e "simula" o envio
|
||||
console.log('Dados de cadastro:', this.registerForm.value);
|
||||
|
||||
// TODO FUTURO: quando a API .NET estiver pronta,
|
||||
// você injeta um AuthService e envia para o backend:
|
||||
/*
|
||||
this.authService.register(this.registerForm.value).subscribe({
|
||||
next: () => { ... },
|
||||
error: () => { ... },
|
||||
complete: () => this.isSubmitting = false
|
||||
});
|
||||
*/
|
||||
|
||||
// Simulação de atraso só para UX (remova se quiser)
|
||||
setTimeout(() => {
|
||||
this.isSubmitting = false;
|
||||
this.registerForm.reset();
|
||||
alert('Cadastro realizado (simulado). Quando a API estiver pronta, isso irá de fato registrar o usuário.');
|
||||
}, 800);
|
||||
}
|
||||
|
||||
hasError(field: string, error?: string): boolean {
|
||||
const control = this.registerForm.get(field);
|
||||
if (!control) return false;
|
||||
if (error) {
|
||||
return control.touched && control.hasError(error);
|
||||
}
|
||||
return control.touched && control.invalid;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 4.9 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 MiB |
|
|
@ -7,6 +7,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@400;700&display=swap" rel="stylesheet">
|
||||
<!-- Dentro de index.html, na seção <head> -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
|
|
|
|||
Loading…
Reference in New Issue