diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 92fc1d4..a25bd5f 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -3,10 +3,11 @@ import { Home } from './pages/home/home'; import { Register } from './pages/register/register'; import { LoginComponent } from './pages/login/login'; import { Geral } from './pages/geral/geral'; +import { authGuard } from './guards/auth.guard'; export const routes: Routes = [ { path: '', component: Home }, { path: "register", component: Register }, { path: "login", component: LoginComponent }, - { path: "geral", component: Geral }, + { path: 'geral', component: Geral, canActivate: [authGuard] }, ]; diff --git a/src/app/components/feature-card/feature-card.html b/src/app/components/feature-card/feature-card.html index d1f2c5e..79dd997 100644 --- a/src/app/components/feature-card/feature-card.html +++ b/src/app/components/feature-card/feature-card.html @@ -1,21 +1,31 @@
- -
- - -

- - {{ title }} -

+
+ + - + +
+ + +

+ {{ title }} +

+
+ +

+ +
-
\ No newline at end of file +
diff --git a/src/app/components/feature-card/feature-card.scss b/src/app/components/feature-card/feature-card.scss index 4eb5754..68e69fa 100644 --- a/src/app/components/feature-card/feature-card.scss +++ b/src/app/components/feature-card/feature-card.scss @@ -1,136 +1,250 @@ -.feature-card-container { - padding: 10px; +:host { + --brand: #E33DCF; + --text: #111214; + --muted: rgba(17, 18, 20, 0.70); + --radius-xl: 22px; + --radius-lg: 16px; + + display: block; } -/* CARD BASE — mantém o design exato do Figma em desktops */ +/* ✅ CARD PRINCIPAL */ .feature-card { - border: 1px solid #000000; - background-color: #FFFFFF; - padding: 30px; - border-radius: 5px; + position: relative; + overflow: hidden; + width: 365px; + min-height: 175px; + padding: 18px 18px 16px 18px; + border-radius: 18px; cursor: default; - /* DESKTOP (layout original) */ - width: 365px; - height: 169px; + background: rgba(255, 255, 255, 0.82); + border: 1px solid rgba(227, 61, 207, 0.18); + backdrop-filter: blur(10px); + box-shadow: 0 18px 38px rgba(17, 18, 20, 0.10); - overflow: hidden; display: flex; flex-direction: column; - justify-content: flex-start; + gap: 10px; - transition: background-color 0.2s ease-in-out; + transform: translateZ(0); + transition: + transform 180ms ease, + border-color 180ms ease, + box-shadow 180ms ease, + background 180ms ease; + + /* borda interna sutil */ + &::before { + content: ''; + position: absolute; + inset: 1px; + border-radius: 17px; + pointer-events: none; + border: 1px solid rgba(255, 255, 255, 0.65); + opacity: 0.75; + } + + /* efeito “radial” suave no fundo */ + &::after { + content: ''; + position: absolute; + inset: -80px; + pointer-events: none; + background: + radial-gradient(380px 200px at 20% 10%, rgba(227, 61, 207, 0.16), transparent 60%), + radial-gradient(320px 180px at 85% 70%, rgba(227, 61, 207, 0.10), transparent 60%); + opacity: 0.85; + } /* ================================ 📱 RESPONSIVIDADE ================================ */ - @media (min-width: 1200px) and (max-width: 1399.98px) { width: 330px; - height: auto; - padding: 24px; + min-height: 170px; } - - /* Telas MUITO pequenas (≤ 360px) */ - @media (max-width: 360px) { - width: 95%; - height: auto; - padding: 20px; - } - - /* Celulares (≤ 480px) */ - @media (max-width: 480px) { - width: 95%; - height: auto; /* Permite texto quebrar */ - } - - /* Tablets pequenos (≤ 768px) */ - @media (max-width: 768px) { - width: 300px; - height: auto; - } - - /* Tablets grandes e monitores pequenos (≤ 1024px) */ @media (max-width: 1024px) { width: 330px; - height: auto; + min-height: 170px; + } + + @media (max-width: 768px) { + width: 300px; + min-height: 170px; + } + + @media (max-width: 480px) { + width: 95%; + min-height: 170px; + } + + @media (max-width: 360px) { + width: 95%; + min-height: 165px; + padding: 16px; } } -/* HOVER */ +/* ✅ HOVER PREMIUM (sem “cinza chapado”) */ .feature-card.hover-state { - background-color: #E1E1E1; + transform: translateY(-4px); + border-color: rgba(227, 61, 207, 0.32); + box-shadow: 0 26px 52px rgba(17, 18, 20, 0.14); + background: rgba(255, 255, 255, 0.90); } -/* ------------------------- - TÍTULO DO CARD --------------------------- */ -.card-title { - font-family: 'Inter', sans-serif; - font-size: 20px; - font-weight: 700; - color: #E33DCF; +/* brilho animado (fica bem SaaS) */ +.shine { + position: absolute; + inset: -40%; + pointer-events: none; + transform: translateX(-120%) rotate(12deg); + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.55), transparent); + opacity: 0.0; + transition: opacity 200ms ease; + z-index: 1; +} - margin-top: -15px; - margin-bottom: 10px; +.feature-card.hover-state .shine { + opacity: 1; + animation: shine 1.25s ease-in-out 1; +} + +/* header */ +.card-head { + position: relative; + z-index: 2; display: flex; align-items: center; - justify-content: center; - - @media (min-width: 1200px) and (max-width: 1399.98px) { - font-size: 18px; - margin-top: -10px; - } - - - /* Mobile */ - @media (max-width: 480px) { - font-size: 18px; - margin-top: -5px; - } + gap: 12px; + justify-content: center; /* seu layout atual é centralizado */ } -/* ÍCONE */ +/* ícone no estilo do mock */ +.icon-wrap { + width: 42px; + height: 42px; + border-radius: 14px; + + display: grid; + place-items: center; + + background: rgba(227, 61, 207, 0.10); + border: 1px solid rgba(227, 61, 207, 0.22); + + transition: transform 180ms ease, box-shadow 180ms ease, background 180ms ease; +} + +.feature-card.hover-state .icon-wrap { + transform: translateY(-1px) scale(1.03); + box-shadow: 0 12px 24px rgba(227, 61, 207, 0.18); + background: rgba(227, 61, 207, 0.14); +} + +/* ícone */ .card-icon { - margin-right: 10px; - font-size: 24px; - - @media (min-width: 1200px) and (max-width: 1399.98px) { - font-size: 22px; - } - - - /* Mobile */ - @media (max-width: 480px) { - font-size: 20px; - } + font-size: 18px; + color: var(--brand); } -/* ------------------------- - DESCRIÇÃO DO CARD --------------------------- */ -.card-description { +/* título */ +.card-title { + position: relative; + z-index: 2; + + margin: 0; font-family: 'Inter', sans-serif; - font-size: 17px; - color: #000000; - line-height: 1; - padding-left: 15px; + font-size: 18px; + font-weight: 950; + color: var(--text); + letter-spacing: -0.2px; - @media (min-width: 1200px) and (max-width: 1399.98px) { - font-size: 16px; - line-height: 1.2; - } + transition: transform 180ms ease, color 180ms ease; +} +.feature-card.hover-state .card-title { + transform: translateY(-1px); +} - /* Mobile */ +/* descrição */ +.card-description { + position: relative; + z-index: 2; + + margin: 0; + padding: 0 6px; + + font-family: 'Inter', sans-serif; + font-size: 14px; + line-height: 1.35; + color: var(--muted); + + /* deixa mais “SaaS” e menos apertado */ @media (max-width: 480px) { - font-size: 15px; - padding-left: 8px; + font-size: 13.5px; + padding: 0 2px; } } .card-description strong { - font-weight: 700; + font-weight: 900; + color: #111214; +} + +/* linha decorativa inferior */ +.card-accent { + position: relative; + z-index: 2; + + margin-top: auto; + height: 6px; + border-radius: 999px; + background: rgba(227, 61, 207, 0.12); + border: 1px solid rgba(227, 61, 207, 0.18); + overflow: hidden; + + &::after { + content: ''; + position: absolute; + inset: 0; + transform: translateX(-60%); + background: linear-gradient(90deg, transparent, rgba(227, 61, 207, 0.28), transparent); + opacity: 0.0; + transition: opacity 180ms ease; + } +} + +.feature-card.hover-state .card-accent::after { + opacity: 1; + animation: sweep 1.2s ease-in-out 1; +} + +/* acessibilidade: reduz animações */ +@media (prefers-reduced-motion: reduce) { + .feature-card, + .icon-wrap, + .card-title { + transition: none !important; + } + .feature-card.hover-state { + transform: none; + } + .feature-card.hover-state .shine, + .feature-card.hover-state .card-accent::after { + animation: none !important; + } +} + +/* animações */ +@keyframes shine { + 0% { transform: translateX(-120%) rotate(12deg); } + 100% { transform: translateX(120%) rotate(12deg); } +} + +@keyframes sweep { + 0% { transform: translateX(-60%); } + 100% { transform: translateX(60%); } } diff --git a/src/app/components/feature-card/feature-card.ts b/src/app/components/feature-card/feature-card.ts index 860269d..21289f0 100644 --- a/src/app/components/feature-card/feature-card.ts +++ b/src/app/components/feature-card/feature-card.ts @@ -1,24 +1,23 @@ import { Component, Input, OnInit } from '@angular/core'; -import { CommonModule } from '@angular/common'; // <-- Importe aqui! +import { CommonModule } from '@angular/common'; @Component({ selector: 'app-feature-card', - standalone: true, // <-- DEVE ser standalone - imports: [CommonModule], // <-- PRECISA do CommonModule para usar [ngClass] + standalone: true, + imports: [CommonModule], templateUrl: './feature-card.html', styleUrls: ['./feature-card.scss'] }) export class FeatureCardComponent implements OnInit { - - // ... o restante da lógica ... + @Input() title: string = ''; @Input() description: string = ''; - @Input() iconClass: string = ''; + @Input() iconClass: string = ''; @Input() textAlign: 'left' | 'center' = 'left'; - isHovered: boolean = false; + isHovered: boolean = false; - constructor() { } + constructor() {} - ngOnInit(): void { } -} \ No newline at end of file + ngOnInit(): void {} +} diff --git a/src/app/components/header/header.html b/src/app/components/header/header.html index 6cc84e4..64460f7 100644 --- a/src/app/components/header/header.html +++ b/src/app/components/header/header.html @@ -1,52 +1,115 @@
- + [class.header-scrolled]="isScrolled" +>
+ + -
- LineGestão -
- - - -
- +
Somos a escolha certa para estar sempre conectado!
- -
+ + + + + + diff --git a/src/app/components/header/header.scss b/src/app/components/header/header.scss index e887fcb..23d097b 100644 --- a/src/app/components/header/header.scss +++ b/src/app/components/header/header.scss @@ -1,380 +1,420 @@ -/* ===================== */ -/* HEADER PRINCIPAL */ -/* ===================== */ -.header-container { - position: sticky; - top: 0; - z-index: 1000; +:host { + --brand: #E33DCF; + --brand-2: rgba(227, 61, 207, 0.18); + --brand-3: rgba(227, 61, 207, 0.10); - width: 100%; - /* mais transparente mesmo no topo */ - background: rgba(255, 255, 255, 0.75); - font-family: 'Inter', sans-serif; - display: flex; - flex-direction: column; + --text: #0b0b0f; + --muted: rgba(0, 0, 0, 0.66); - backdrop-filter: blur(6px); - -webkit-backdrop-filter: blur(6px); + --border: rgba(0, 0, 0, 0.08); - 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; + --shadow: 0 18px 45px rgba(0, 0, 0, 0.10); + --shadow-soft: 0 10px 26px rgba(0, 0, 0, 0.08); } -/* 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); +/* =============================== */ +/* PÁGINA / FUNDO GLOBAL */ +/* =============================== */ +.home-page { + position: relative; + min-height: 100vh; + background: #efefef; - box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12); - border-bottom: 1px solid rgba(201, 30, 181, 0.3); + /* 🔑 evita o background “atrapalhar” o conteúdo */ + isolation: isolate; } +/* BACKGROUND GLOBAL (viewport inteiro) */ +.page-bg { + position: fixed; + inset: 0; + z-index: -1; /* 🔑 sempre atrás */ + pointer-events: none; + .grid { + position: absolute; + inset: -40px; + opacity: 0.30; + background: + radial-gradient(circle at 25% 25%, var(--brand-2), transparent 45%), + radial-gradient(circle at 80% 30%, var(--brand-3), transparent 55%), + radial-gradient(circle at 45% 90%, var(--brand-2), transparent 50%), + repeating-linear-gradient( + 90deg, + rgba(0, 0, 0, 0.05) 0px, + rgba(0, 0, 0, 0.05) 1px, + transparent 1px, + transparent 46px + ), + repeating-linear-gradient( + 0deg, + rgba(0, 0, 0, 0.04) 0px, + rgba(0, 0, 0, 0.04) 1px, + transparent 1px, + transparent 46px + ); + /* ❌ SEM blur */ + transform: translateZ(0); + animation: gridMove 12s ease-in-out infinite; + } + .blob { + position: absolute; + width: 520px; + height: 520px; + border-radius: 50%; + background: radial-gradient(circle at 30% 30%, var(--brand), transparent 62%); + opacity: 0.16; /* um pouco mais leve sem blur */ + /* ❌ SEM blur */ + transform: translateZ(0); + animation: floaty 9s ease-in-out infinite; + } -/* ===================== */ -/* TOP AREA (LOGO + MENU + BOTÕES) */ -/* ===================== */ -.header-top { - width: 100%; - height: 72px; - padding: 0 32px; + .blob-1 { + top: -180px; + left: -180px; + } - display: flex; + .blob-2 { + bottom: -220px; + right: -220px; + opacity: 0.12; + animation-duration: 11s; + } +} + +/* =============================== */ +/* HERO */ +/* =============================== */ +.hero { + position: relative; + overflow: hidden; + padding: clamp(48px, 5vw, 70px) 0 20px; +} + +.hero-content { + text-align: center; + max-width: 980px; + margin: 0 auto; +} + +/* =============================== */ +/* KICKER */ +/* =============================== */ +.kicker { + display: inline-flex; align-items: center; - justify-content: space-between; /* Alinha logo / menu / botões */ - gap: 40px; + gap: 10px; - @media (max-width: 900px) { - gap: 20px; - } + padding: 10px 14px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.65); + border: 1px solid var(--border); + box-shadow: var(--shadow-soft); - @media (max-width: 768px) { - padding: 0 20px; - } + color: var(--muted); + font-family: 'Poppins', sans-serif; + font-size: 14px; - @media (max-width: 600px) { - flex-direction: column; - height: auto; - gap: 12px; - padding: 12px 0; - } -} - -/* ===================== */ -/* LOGO */ -/* ===================== */ - -.logo { - width: 44px; - height: 44px; - - /* NOTEBOOKS 1200–1399px */ - @media (min-width: 1200px) and (max-width: 1399.98px) { - width: 38px; - height: 38px; - } - - @media (max-width: 1280px) { - width: 38px; - height: 38px; - } - - @media (max-width: 1024px) { - width: 28px; - height: 28px; - } - - @media (max-width: 480px) { - width: 26px; - height: 26px; - } -} - -.logo-area { - display: flex; - align-items: center; - gap: 8px; - text-decoration: none; /* ⬅️ tira sublinhado */ - color: inherit; /* ⬅️ usa as cores definidas nos spans */ - cursor: pointer; /* ⬅️ deixa com “carinha” de botão/link */ -} - -/* TEXTO DA LOGO */ -.logo-text .line, -.logo-text .gestao { - font-weight: 600; - font-size: 32px; /* desktop grande */ - - /* NOTEBOOKS 1200–1399px */ - @media (min-width: 1200px) and (max-width: 1399.98px) { - font-size: 26px; - } - - @media (max-width: 1200px) { - font-size: 19px; - } - - @media (max-width: 1024px) { - font-size: 18px; - } - - @media (max-width: 900px) { - font-size: 17px; - } - - @media (max-width: 768px) { + i { + color: var(--brand); font-size: 16px; } - - @media (max-width: 480px) { - font-size: 15px; - } } -.logo-text .line { color: #030FAA; } -.logo-text .gestao { color: #000000; } +/* =============================== */ +/* TÍTULO PRINCIPAL */ +/* =============================== */ +.main-title { + font-family: 'Inter', sans-serif; + font-size: clamp(28px, 4.6vw, 54px); + line-height: 1.08; + margin: 22px auto 22px; -/* ===================== */ -/* MENU */ -/* ===================== */ -.menu { display: flex; + flex-direction: column; align-items: center; - gap: 24px; + width: fit-content; - @media (max-width: 1200px) { - gap: 18px; + .first-line, + .second-line { + color: var(--brand); + font-weight: 600; + display: block; + text-align: center; + letter-spacing: -0.02em; } - @media (max-width: 1024px) { - gap: 14px; + .second-line strong { + background: linear-gradient(90deg, var(--brand), rgba(227, 61, 207, 0.55)); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + position: relative; } - @media (max-width: 900px) { - gap: 12px; - } - - @media (max-width: 768px) { - gap: 10px; - } - - @media (max-width: 600px) { - flex-wrap: wrap; - justify-content: center; - gap: 10px; - width: 100%; + .second-line strong::after { + content: ""; + position: absolute; + left: -6px; + right: -6px; + bottom: -6px; + height: 10px; + border-radius: 999px; + background: rgba(227, 61, 207, 0.18); + z-index: -1; + transform: scaleX(0); + transform-origin: left; + animation: underlineGrow 900ms ease forwards; + animation-delay: 700ms; } } -.menu-item { - font-size: 16px; - font-weight: 600; - color: #000 !important; - text-decoration: none !important; - - /* NOTEBOOKS 1200–1399px */ - @media (min-width: 1200px) and (max-width: 1399.98px) { - font-size: 14.5px; - } - - @media (max-width: 1200px) { - font-size: 14px; - } - - @media (max-width: 1024px) { - font-size: 13.5px; - } - - @media (max-width: 900px) { - font-size: 13px; - } - - @media (max-width: 768px) { - font-size: 12.5px; - } - - @media (max-width: 600px) { - font-size: 12px; - } +/* deslocamento em telas grandes */ +@media (min-width: 1200px) { + .main-title .first-line { transform: translateX(-70px); } + .main-title .second-line { transform: translateX(100px) translateY(-6px); } } -.menu-item:hover { - color: #030FAA !important; +@media (max-width: 1199.98px) { + .main-title .first-line, + .main-title .second-line { transform: none; } } -/* ===================== */ -/* BOTÕES */ -/* ===================== */ -.btn-area { - display: flex; - align-items: center; - gap: 12px; - margin-left: 0 !important; +/* =============================== */ +/* PARÁGRAFO PRINCIPAL */ +/* =============================== */ +.main-paragraph { + width: min(980px, 92%); + margin: 0 auto 26px; - @media (max-width: 900px) { - gap: 10px; - } + font-family: 'Poppins', sans-serif; + font-size: clamp(15px, 1.55vw, 20px); + color: var(--text); + line-height: 1.45; + font-weight: 400; + text-align: center; - @media (max-width: 600px) { - width: 100%; - justify-content: center; - } + strong { font-weight: 700; } } -/* --- Botão Cadastre-se --- */ -.btn-cadastrar { - width: 164px; - height: 41px; - background: #E1E1E1; - border-radius: 8px; - color: #000; - font-size: 16px; - font-weight: 600; - border: none; - cursor: pointer; - transition: background-color 0.15s ease, transform 0.08s ease; +.brand-name { color: var(--brand); } - @media (min-width: 1200px) and (max-width: 1399.98px) { - width: 140px; - height: 36px; - font-size: 14px; - } - - @media (max-width: 1024px) { - width: 140px; - height: 36px; - font-size: 14px; - } - - @media (max-width: 900px) { - width: 135px; - height: 36px; - font-size: 14px; - } - - @media (max-width: 768px) { - width: 120px; - height: 34px; - font-size: 13px; - } - - @media (max-width: 600px) { - width: 45%; - } +.highlight { + color: var(--text); + position: relative; + padding: 0 2px; } -.btn-cadastrar:hover { - background: #d7d7d7; +.highlight::after { + content: ""; + position: absolute; + left: -2px; + right: -2px; + bottom: 2px; + height: 10px; + background: rgba(227, 61, 207, 0.12); + border-radius: 999px; + z-index: -1; } -.btn-cadastrar:active { - background: rgba(225, 225, 225, 0.7) !important; - transform: scale(0.98); -} - -/* --- Botão Login --- */ -.btn-login { - width: 164px; - height: 41px; - background: #E33DCF; - border-radius: 8px; - color: #fff; - font-size: 16px; - font-weight: 600; - border: none; - cursor: pointer; - transition: background-color 0.15s ease, transform 0.08s ease; - - @media (min-width: 1200px) and (max-width: 1399.98px) { - width: 140px; - height: 36px; - font-size: 14px; - } - - @media (max-width: 1024px) { - width: 150px; - height: 38px; - font-size: 15px; - } - - @media (max-width: 900px) { - width: 135px; - height: 36px; - font-size: 14px; - } - - @media (max-width: 768px) { - width: 120px; - height: 34px; - font-size: 13px; - } - - @media (max-width: 600px) { - width: 45%; - } -} - -/* garante que o texto SEMPRE fique branco */ -.btn-login, -.btn-login:hover, -.btn-login:active, -.btn-login:focus { - color: #fff !important; -} - -.btn-login:hover { - background: #d72bd0; -} - -.btn-login:active { - background: rgba(227, 61, 207, 0.8) !important; - transform: scale(0.98); -} - -/* ===================== */ -/* FAIXA AZUL INFERIOR */ -/* ===================== */ -.header-bar { - width: 100%; - height: 34px; /* um pouco mais baixa */ - background: linear-gradient(90deg, #030FAA 0%, #6066FF 45%, #C91EB5 100%); +/* =============================== */ +/* AÇÕES / LINKS */ +/* =============================== */ +.hero-actions { + margin-top: 10px; 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: 30px; - } + gap: 14px; + flex-wrap: wrap; } -.header-bar-text { - color: #ffffff; - font-size: 13px; +.secondary-link { + display: inline-flex; + align-items: center; + gap: 8px; + + color: rgba(0, 0, 0, 0.72); + font-family: 'Poppins', sans-serif; font-weight: 600; - letter-spacing: 0.08em; - text-transform: uppercase; + text-decoration: none; - /* “pill” para destacar o texto */ - padding: 4px 14px; + padding: 12px 14px; + border-radius: 12px; + border: 1px solid var(--border); + background: rgba(255, 255, 255, 0.55); + box-shadow: var(--shadow-soft); + + transition: transform 0.22s ease, box-shadow 0.22s ease; +} + +.secondary-link:hover { + transform: translateY(-2px); + box-shadow: var(--shadow); +} + +.hero-badges { + margin-top: 18px; + display: flex; + justify-content: center; + gap: 10px; + flex-wrap: wrap; +} + +.badge-pill { + display: inline-flex; + align-items: center; + gap: 8px; + + padding: 9px 12px; 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: 11.5px; - padding: 3px 10px; - letter-spacing: 0.06em; - text-align: center; + background: rgba(255, 255, 255, 0.70); + border: 1px solid var(--border); + box-shadow: var(--shadow-soft); + + font-family: 'Poppins', sans-serif; + font-size: 13px; + color: rgba(0, 0, 0, 0.75); + + i { color: var(--brand); } +} + +/* =============================== */ +/* BENEFÍCIOS */ +/* =============================== */ +.hero-benefits { + margin: 20px auto 0; + width: min(820px, 96%); + + display: grid; + gap: 10px; + + .benefit { + display: flex; + gap: 10px; + align-items: flex-start; + + padding: 12px 14px; + border-radius: 16px; + + background: rgba(255, 255, 255, 0.58); + border: 1px solid var(--border); + box-shadow: var(--shadow-soft); + + i { + color: var(--brand); + font-size: 18px; + margin-top: 1px; + } + + span { + font-family: 'Poppins', sans-serif; + color: rgba(0, 0, 0, 0.78); + font-size: 14px; + line-height: 1.35; + text-align: left; + } + } + + @media (min-width: 820px) { + grid-template-columns: repeat(3, 1fr); } } +/* =============================== */ +/* FEATURES */ +/* =============================== */ +.section-head { + text-align: center; + margin: 0 auto 24px; + max-width: 820px; +} + +.section-title { + font-family: 'Inter', sans-serif; + font-size: clamp(20px, 2.4vw, 30px); + color: rgba(0, 0, 0, 0.88); + margin-bottom: 8px; + letter-spacing: -0.02em; +} + +.section-subtitle { + font-family: 'Poppins', sans-serif; + color: rgba(0, 0, 0, 0.64); + font-size: 15px; + margin: 0 auto; +} + +.feature-cards-row { margin-top: 18px; } + +.feature-item { + border-radius: 18px; + padding: 10px; + background: transparent; + transition: transform 0.25s ease, filter 0.25s ease; +} + +.feature-item:hover { + transform: translateY(-6px); + filter: drop-shadow(0 14px 22px rgba(0, 0, 0, 0.10)); +} + +.button-section { + margin-top: 18px; + padding-bottom: 30px; +} + +/* =============================== */ +/* REVEAL (scroll) */ +/* =============================== */ +.reveal { + opacity: 0; + transform: translateY(18px) scale(0.98); + transition: + opacity 650ms ease, + transform 650ms ease; + transition-delay: var(--delay, 0ms); +} + +.reveal.is-visible { + opacity: 1; + transform: translateY(0) scale(1); +} + +/* =============================== */ +/* ANIMAÇÕES */ +/* =============================== */ +@keyframes floaty { + 0%, 100% { transform: translate(0, 0) scale(1); } + 50% { transform: translate(14px, 18px) scale(1.03); } +} + +@keyframes gridMove { + 0%, 100% { transform: translate(0, 0); } + 50% { transform: translate(-10px, 10px); } +} + +@keyframes underlineGrow { + from { transform: scaleX(0); } + to { transform: scaleX(1); } +} + +/* =============================== */ +/* Acessibilidade */ +/* =============================== */ +@media (prefers-reduced-motion: reduce) { + .reveal, + .page-bg .grid, + .page-bg .blob, + .main-title .second-line strong::after { + animation: none !important; + transition: none !important; + } + + .reveal { + opacity: 1; + transform: none; + } +} diff --git a/src/app/components/header/header.ts b/src/app/components/header/header.ts index ca0177c..a1027e4 100644 --- a/src/app/components/header/header.ts +++ b/src/app/components/header/header.ts @@ -1,30 +1,61 @@ -import { Component, HostListener } from '@angular/core'; +import { Component, HostListener, Inject } from '@angular/core'; import { RouterLink, Router, NavigationEnd } from '@angular/router'; -import { CommonModule } from '@angular/common'; +import { CommonModule, isPlatformBrowser } from '@angular/common'; +import { PLATFORM_ID } from '@angular/core'; @Component({ selector: 'app-header', standalone: true, - imports: [RouterLink, CommonModule], // ⬅️ CommonModule para usar *ngIf + imports: [RouterLink, CommonModule], templateUrl: './header.html', styleUrls: ['./header.scss'], }) export class Header { isScrolled = false; - isHome = true; // valor inicial (ao abrir normalmente cai na home) + isHome = true; - constructor(private router: Router) { - // escuta mudanças de rota para saber se está na home + // ✅ menu hamburguer + menuOpen = false; + + // ✅ define quando mostrar header “logado” + isLoggedHeader = false; + + constructor( + private router: Router, + @Inject(PLATFORM_ID) private platformId: object + ) { this.router.events.subscribe((event) => { if (event instanceof NavigationEnd) { const url = event.urlAfterRedirects || event.url; + this.isHome = (url === '/' || url === ''); + + // ✅ considera header logado quando está em /geral + this.isLoggedHeader = url.startsWith('/geral'); + + // ✅ ao trocar de rota, fecha o menu + this.menuOpen = false; } }); } + toggleMenu() { + this.menuOpen = !this.menuOpen; + } + + closeMenu() { + this.menuOpen = false; + } + @HostListener('window:scroll', []) onWindowScroll() { - this.isScrolled = window.scrollY > 10; // passou 10px de scroll, ativa o “fosco” + if (!isPlatformBrowser(this.platformId)) return; + this.isScrolled = window.scrollY > 10; + } + + @HostListener('document:keydown.escape', []) + onEsc() { + if (!isPlatformBrowser(this.platformId)) return; + this.closeMenu(); } } diff --git a/src/app/guards/auth.guard.ts b/src/app/guards/auth.guard.ts new file mode 100644 index 0000000..ed76d3e --- /dev/null +++ b/src/app/guards/auth.guard.ts @@ -0,0 +1,21 @@ +import { inject, PLATFORM_ID } from '@angular/core'; +import { CanActivateFn, Router } from '@angular/router'; +import { isPlatformBrowser } from '@angular/common'; + +export const authGuard: CanActivateFn = () => { + const router = inject(Router); + const platformId = inject(PLATFORM_ID); + + // SSR: não existe localStorage. Bloqueia e manda pro login. + if (!isPlatformBrowser(platformId)) { + return router.parseUrl('/login'); + } + + const token = localStorage.getItem('token'); + + if (!token) { + return router.parseUrl('/login'); + } + + return true; +}; diff --git a/src/app/pages/geral/geral.html b/src/app/pages/geral/geral.html index 7f35267..465ae03 100644 --- a/src/app/pages/geral/geral.html +++ b/src/app/pages/geral/geral.html @@ -1 +1,265 @@ -

geral works!

+ +
+ +
+ +
+ + + + + + + +
+ +
+ + +
+
+ +
+
+ + Gestão centralizada +
+ +
Geral
+ Tabela de linhas e dados de telefonia +
+ +
+ + + +
+
+ + +
+ +
+ + + + + + + +
+ +
+ Itens por página + +
+ +
+
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ITÉM + + {{ sortKey==='item' ? (sortDir==='asc' ? '▲' : '▼') : '' }} + + + CONTA + + {{ sortKey==='conta' ? (sortDir==='asc' ? '▲' : '▼') : '' }} + + + LINHA + + {{ sortKey==='linha' ? (sortDir==='asc' ? '▲' : '▼') : '' }} + + + CHIP + + {{ sortKey==='chip' ? (sortDir==='asc' ? '▲' : '▼') : '' }} + + + CLIENTE + + {{ sortKey==='cliente' ? (sortDir==='asc' ? '▲' : '▼') : '' }} + + + USUÁRIO + + {{ sortKey==='usuario' ? (sortDir==='asc' ? '▲' : '▼') : '' }} + + + PLANO + + {{ sortKey==='plano' ? (sortDir==='asc' ? '▲' : '▼') : '' }} + + + CONTRATO + + {{ sortKey==='contrato' ? (sortDir==='asc' ? '▲' : '▼') : '' }} + + AÇÕES
{{ r.item }}{{ r.conta }}{{ r.linha }}{{ r.chip }}{{ r.cliente }}{{ r.usuario }}{{ r.plano }}{{ r.contrato }} + + + + + +
+ + Nenhum registro encontrado. +
+ +
+
+
+ + + + +
+
+
diff --git a/src/app/pages/geral/geral.scss b/src/app/pages/geral/geral.scss index e69de29..1d12bea 100644 --- a/src/app/pages/geral/geral.scss +++ b/src/app/pages/geral/geral.scss @@ -0,0 +1,400 @@ +:host { + --brand: #E33DCF; + --blue: #030FAA; + + --text: #111214; + --muted: rgba(17, 18, 20, 0.70); + + --radius-xl: 22px; + --radius-lg: 16px; + + display: block; +} + +.geral-page { + min-height: calc(100vh - 69.2px); + padding: 24px 12px 90px; + display: flex; + justify-content: center; + + font-family: 'Inter', sans-serif; + + position: relative; + overflow: hidden; + + background: + radial-gradient(900px 420px at 20% 10%, rgba(227, 61, 207, 0.14), transparent 60%), + radial-gradient(820px 380px at 80% 30%, rgba(227, 61, 207, 0.08), transparent 60%), + linear-gradient(180deg, #ffffff 0%, #f5f5f7 70%); + + &::after { + content: ''; + position: absolute; + inset: 0; + pointer-events: none; + background: rgba(255, 255, 255, 0.25); + } +} + +/* blobs fixos */ +.page-blob { + position: fixed; + pointer-events: none; + border-radius: 999px; + filter: blur(34px); + opacity: 0.55; + z-index: 0; + + background: radial-gradient(circle at 30% 30%, rgba(227,61,207,0.55), rgba(227,61,207,0.06)); + animation: floaty 10s ease-in-out infinite; + + &.blob-1 { width: 420px; height: 420px; top: -140px; left: -140px; } + &.blob-2 { width: 520px; height: 520px; top: -220px; right: -240px; animation-duration: 12s; } + &.blob-3 { width: 360px; height: 360px; bottom: -180px; left: 25%; animation-duration: 14s; } + &.blob-4 { width: 520px; height: 520px; bottom: -260px; right: -260px; animation-duration: 16s; opacity: .45; } + + @media (max-width: 992px) { opacity: 0.35; } +} + +.container-geral { + width: 100%; + max-width: 1180px; + position: relative; + z-index: 1; +} + +/* CARD GLASS */ +.geral-card { + border-radius: var(--radius-xl); + overflow: hidden; + + background: rgba(255, 255, 255, 0.82); + border: 1px solid rgba(227, 61, 207, 0.16); + backdrop-filter: blur(12px); + box-shadow: 0 22px 46px rgba(17, 18, 20, 0.10); + + /* borda interna */ + position: relative; + &::before { + content: ''; + position: absolute; + inset: 1px; + border-radius: calc(var(--radius-xl) - 1px); + pointer-events: none; + border: 1px solid rgba(255, 255, 255, 0.65); + opacity: 0.75; + } +} + +/* HEADER */ +.geral-header { + padding: 16px 16px 14px 16px; + border-bottom: 1px solid rgba(17, 18, 20, 0.06); + background: linear-gradient(180deg, rgba(227,61,207,0.08), rgba(255,255,255,0.10)); +} + +.header-row-top { + display: grid; + grid-template-columns: 1fr auto; + align-items: center; + gap: 12px; + + @media (max-width: 768px) { + grid-template-columns: 1fr; + text-align: center; + } +} + +.header-title { + display: flex; + flex-direction: column; + align-items: flex-start; + + @media (max-width: 768px) { + align-items: center; + } +} + +.title-badge { + display: inline-flex; + align-items: center; + gap: 10px; + + padding: 8px 12px; + border-radius: 999px; + + background: rgba(255, 255, 255, 0.78); + border: 1px solid rgba(227, 61, 207, 0.22); + backdrop-filter: blur(10px); + + color: var(--text); + font-size: 13px; + font-weight: 800; + + i { color: var(--brand); } +} + +.title { + font-size: 26px; + font-weight: 950; + letter-spacing: -0.3px; + color: var(--text); + margin-top: 10px; + + @media (max-width: 768px) { font-size: 24px; } +} + +.subtitle { + color: rgba(17, 18, 20, 0.65); + font-weight: 700; +} + +.header-actions { + justify-content: flex-end; + + @media (max-width: 768px) { + justify-content: center; + } +} + +/* BOTÕES */ +.btn-brand { + background-color: var(--brand); + border-color: var(--brand); + font-weight: 900; + border-radius: 12px; + + transition: transform 180ms ease, box-shadow 180ms ease, filter 180ms ease; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 14px 24px rgba(227, 61, 207, 0.22); + filter: brightness(0.97); + } +} + +.btn-glass { + border-radius: 12px; + font-weight: 900; + + background: rgba(255, 255, 255, 0.72); + border: 1px solid rgba(3, 15, 170, 0.25); + color: var(--blue); + + transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 14px 24px rgba(17, 18, 20, 0.10); + border-color: rgba(227, 61, 207, 0.30); + } +} + +/* CONTROLES */ +.controls { + display: flex; + gap: 12px; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + + @media (max-width: 768px) { + justify-content: center; + } +} + +/* Busca */ +.search-group { + max-width: 340px; + border-radius: 14px; + overflow: hidden; + + .input-group-text { + background: rgba(255, 255, 255, 0.78); + border: 1px solid rgba(17, 18, 20, 0.10); + color: var(--brand); + } + + .form-control { + border: 1px solid rgba(17, 18, 20, 0.10); + background: rgba(255, 255, 255, 0.78); + } + + .form-control:focus { + border-color: rgba(227, 61, 207, 0.35); + box-shadow: 0 0 0 0.2rem rgba(227, 61, 207, 0.12); + } + + .btn-clear { + border: 1px solid rgba(17, 18, 20, 0.10); + background: rgba(255, 255, 255, 0.78); + } +} + +/* select */ +.select-glass { + border-radius: 12px; + background: rgba(255, 255, 255, 0.78); + border: 1px solid rgba(17, 18, 20, 0.10); +} + +/* BODY */ +.geral-body { + padding: 0; +} + +/* Wrapper com altura e scroll */ +.table-wrap { + max-height: 58vh; + overflow: auto; +} + +/* TABELA MODERNA */ +.table-geral { + font-size: 0.875rem; + + th, td { + white-space: nowrap; + padding: 10px 12px; + color: var(--text); + font-weight: 700; + } + + thead th { + position: sticky; + top: 0; + z-index: 2; + + background: rgba(255, 255, 255, 0.92); + backdrop-filter: blur(10px); + + border-bottom: 2px solid rgba(227, 61, 207, 0.15); + + color: rgba(17, 18, 20, 0.75); + font-weight: 950; + letter-spacing: 0.06em; + text-transform: uppercase; + } + + th.sortable { + cursor: pointer; + user-select: none; + transition: color 180ms ease; + + &:hover { color: var(--brand); } + } + + .sort-caret { + font-size: 11px; + margin-left: 6px; + opacity: 0.75; + color: rgba(17, 18, 20, 0.60); + } + + /* destaque da linha */ + .td-highlight { + color: var(--blue); + font-weight: 950; + } + + /* linhas mais “clean” */ + tbody tr { + border-top: 1px solid rgba(17, 18, 20, 0.06); + transition: background-color 180ms ease, transform 180ms ease; + } + + tbody tr:hover { + background-color: rgba(227, 61, 207, 0.06); + } + + td.actions { + min-width: 160px; + } +} + +/* botões ícone */ +.btn-icon { + padding: 6px 10px; + border-radius: 12px; + transition: transform 150ms ease, box-shadow 150ms ease; + + &:hover { + transform: translateY(-1px); + box-shadow: 0 12px 22px rgba(17, 18, 20, 0.10); + } +} + +.empty-state { + background: rgba(255, 255, 255, 0.35); +} + +/* FOOTER */ +.geral-footer { + padding: 14px 16px; + border-top: 1px solid rgba(17, 18, 20, 0.06); + + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + flex-wrap: wrap; + + @media (max-width: 768px) { + justify-content: center; + text-align: center; + } +} + +/* Paginação moderna */ +.pagination-modern .page-link { + color: var(--blue); + font-weight: 900; + border-radius: 12px; + border: 1px solid rgba(17, 18, 20, 0.10); + background: rgba(255, 255, 255, 0.78); + transition: transform 150ms ease, border-color 150ms ease, box-shadow 150ms ease, color 150ms ease; + + i { font-size: 12px; } +} + +.pagination-modern .page-link:hover { + transform: translateY(-1px); + border-color: rgba(227, 61, 207, 0.28); + color: var(--brand); + box-shadow: 0 12px 22px rgba(17, 18, 20, 0.08); +} + +.pagination-modern .page-item.active .page-link { + background-color: rgba(3, 15, 170, 0.92); + border-color: rgba(3, 15, 170, 0.92); + color: #fff; +} + +/* =============================== */ +/* ✅ ANIMAÇÕES SSR-SAFE */ +/* =============================== */ +[data-animate] { opacity: 1; transform: none; } + +.js-animate [data-animate] { + opacity: 0; + transform: translateY(14px); + transition: opacity 600ms ease, transform 600ms ease; + will-change: opacity, transform; +} + +.js-animate [data-animate].is-visible { + opacity: 1; + transform: translateY(0); +} + +@media (prefers-reduced-motion: reduce) { + .page-blob { animation: none; } + .js-animate [data-animate] { transition: none; transform: none; opacity: 1; } +} + +/* animações */ +@keyframes floaty { + 0% { transform: translate(0, 0) scale(1); } + 50% { transform: translate(18px, 10px) scale(1.03); } + 100% { transform: translate(0, 0) scale(1); } +} diff --git a/src/app/pages/geral/geral.ts b/src/app/pages/geral/geral.ts index 452ad36..887a27f 100644 --- a/src/app/pages/geral/geral.ts +++ b/src/app/pages/geral/geral.ts @@ -1,11 +1,188 @@ -import { Component } from '@angular/core'; +import { Component, ElementRef, ViewChild, Inject, PLATFORM_ID, AfterViewInit } from '@angular/core'; +import { isPlatformBrowser, CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; + +type SortDir = 'asc' | 'desc'; + +type LineRow = { + id: number; + item: string; + conta: string; + linha: string; + chip: string; + cliente: string; + usuario: string; + plano: string; + contrato: string; +}; @Component({ - selector: 'app-geral', - imports: [], + standalone: true, + imports: [CommonModule, FormsModule], templateUrl: './geral.html', - styleUrl: './geral.scss', + styleUrls: ['./geral.scss'] }) -export class Geral { +export class Geral implements AfterViewInit { + toastMessage = ''; + @ViewChild('successToast') successToast!: ElementRef; + constructor(@Inject(PLATFORM_ID) private platformId: object) {} + + async ngAfterViewInit() { + if (!isPlatformBrowser(this.platformId)) return; + + // ✅ animações (igual Home) + this.initAnimations(); + + const msg = (history.state && history.state.toastMessage) ? String(history.state.toastMessage) : ''; + if (!msg) return; + + this.toastMessage = msg; + await this.showToast(msg); + + const newState = { ...history.state }; + delete newState.toastMessage; + history.replaceState(newState, '', location.href); + } + + private initAnimations() { + document.documentElement.classList.add('js-animate'); + + setTimeout(() => { + const items = Array.from(document.querySelectorAll('[data-animate]')); + if (!items.length) return; + + const reduceMotion = + window.matchMedia && + window.matchMedia('(prefers-reduced-motion: reduce)').matches; + + if (reduceMotion || !('IntersectionObserver' in window)) { + items.forEach(i => i.classList.add('is-visible')); + return; + } + + const io = new IntersectionObserver((entries) => { + for (const entry of entries) { + if (entry.isIntersecting) { + (entry.target as HTMLElement).classList.add('is-visible'); + io.unobserve(entry.target); + } + } + }, { threshold: 0.12 }); + + items.forEach(el => io.observe(el)); + }, 0); + } + + private async showToast(message: string) { + this.toastMessage = message; + const bs = await import('bootstrap'); + const toast = new bs.Toast(this.successToast.nativeElement, { autohide: true, delay: 1500 }); + toast.show(); + } + + // ========= TABELA (mock por enquanto) ========= + rows: LineRow[] = [ + { id: 1, item: '001', conta: 'Empresa A', linha: '71999990001', chip: 'ICCID-0001', cliente: 'Cliente A', usuario: 'João', plano: 'Pós 30GB', contrato: 'CT-2025-001' }, + { id: 2, item: '002', conta: 'Empresa B', linha: '71999990002', chip: 'ICCID-0002', cliente: 'Cliente B', usuario: 'Maria', plano: 'Pós Ilimitado', contrato: 'CT-2025-002' }, + { id: 3, item: '003', conta: 'Empresa A', linha: '71999990003', chip: 'ICCID-0003', cliente: 'Cliente A', usuario: 'Carlos', plano: 'Controle 20GB', contrato: 'CT-2025-003' }, + ]; + + searchTerm = ''; + sortKey: keyof LineRow = 'item'; + sortDir: SortDir = 'asc'; + + page = 1; + pageSize = 10; + + onSearch() { this.page = 1; } + clearSearch() { this.searchTerm = ''; this.page = 1; } + + setSort(key: keyof LineRow) { + if (this.sortKey === key) { + this.sortDir = this.sortDir === 'asc' ? 'desc' : 'asc'; + return; + } + this.sortKey = key; + this.sortDir = 'asc'; + } + + onPageSizeChange() { this.page = 1; } + + goToPage(p: number) { + const target = Math.max(1, Math.min(this.totalPages, p)); + this.page = target; + } + + trackById(_: number, row: LineRow) { return row.id; } + + get filteredRows(): LineRow[] { + const term = this.searchTerm.trim().toLowerCase(); + if (!term) return this.rows; + + return this.rows.filter(r => { + const blob = [r.item, r.conta, r.linha, r.chip, r.cliente, r.usuario, r.plano, r.contrato].join(' ').toLowerCase(); + return blob.includes(term); + }); + } + + get sortedRows(): LineRow[] { + const copy = [...this.filteredRows]; + const key = this.sortKey; + const dir = this.sortDir; + + copy.sort((a, b) => { + const av = String(a[key] ?? '').toLowerCase(); + const bv = String(b[key] ?? '').toLowerCase(); + if (av < bv) return dir === 'asc' ? -1 : 1; + if (av > bv) return dir === 'asc' ? 1 : -1; + return 0; + }); + + return copy; + } + + get totalPages(): number { + const total = Math.ceil(this.sortedRows.length / this.pageSize); + return total > 0 ? total : 1; + } + + get pagedRows(): LineRow[] { + const start = (this.page - 1) * this.pageSize; + const end = start + this.pageSize; + return this.sortedRows.slice(start, end); + } + + get filteredCount(): number { return this.sortedRows.length; } + + get pageStart(): number { + if (this.filteredCount === 0) return 0; + return (this.page - 1) * this.pageSize + 1; + } + + get pageEnd(): number { + if (this.filteredCount === 0) return 0; + return Math.min(this.page * this.pageSize, this.filteredCount); + } + + get pageNumbers(): number[] { + const total = this.totalPages; + const current = this.page; + + const max = 5; + let start = Math.max(1, current - 2); + let end = Math.min(total, start + (max - 1)); + start = Math.max(1, end - (max - 1)); + + const pages: number[] = []; + for (let i = start; i <= end; i++) pages.push(i); + return pages; + } + + // ========= BOTÕES (placeholders) ========= + async onImportExcel() { await this.showToast('Importação via Excel será implementada.'); } + async onCadastrarLinha() { await this.showToast('Cadastro de linha será implementado.'); } + async onDetalhes(row: LineRow) { await this.showToast(`Detalhes: linha ${row.linha}`); } + async onFinanceiro(row: LineRow) { await this.showToast(`Financeiro: linha ${row.linha} (em implementação)`); } + async onRemover(row: LineRow) { await this.showToast(`Remover: linha ${row.linha} (em implementação)`); } } diff --git a/src/app/pages/home/home.html b/src/app/pages/home/home.html index fdd9d69..e2d92b3 100644 --- a/src/app/pages/home/home.html +++ b/src/app/pages/home/home.html @@ -1,62 +1,201 @@ -
-

- Gerencie suas linhas móveis - com inteligência e praticidade -

+
-

- LineGestão é a solução completa para empresas que - desejam controlar suas linhas móveis com eficiência e segurança. - Com recursos como gerenciamento de clientes, - importação de dados via Excel e - monitoramento estratégico de contratos e linhas, - você simplifica processos, - reduz erros e - ganha mais controle sobre seus recursos corporativos. -

-
+ + + + + -
-
- -
- -
+ +
+
-
- -
+
-
- -
+ +
-
+
+ + SaaS para gestão de linhas corporativas +
+ +
+

+ Gerencie suas linhas móveis + com inteligência e praticidade +

+ +

+ LineGestão é a solução completa para empresas que + desejam controlar suas linhas móveis com eficiência e segurança. + Com recursos como gerenciamento de clientes, + importação de dados via Excel e + monitoramento estratégico de contratos e linhas, + você simplifica processos, + reduz erros e + ganha mais controle sobre seus recursos corporativos. +

+ +
+ + + + +
+ +
+
+ + +
+
+
+ + Visão Geral +
+ +
+
+ Linhas ativas + 128 + controle +
+ +
+ Contratos + 12 + organizado +
+ +
+ Clientes + 34 + centralizado +
+ +
+
+
+
Linha 55XX9XXXXXXXX
+
Status: Ativa • Operadora: Vivo
+
+
OK
+
+
+
+
-
-
- -
+ + +
+
+
+ +
+ Setup rápido + comece em minutos +
+
+ +
+ +
+ Excel → Sistema + importação inteligente +
+
+ +
+ +
+ Mais segurança + menos erro manual +
+
+
+
+
+
-
+ +
+
+
+

+ Tudo o que você precisa para gestão de linhas +

+

+ Um painel simples, bonito e direto ao ponto — feito para empresa que quer controle. +

+
+ + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ + +
+
+ +
+
+ + Menos planilha, mais controle. +
+
+ + Mais agilidade no dia a dia. +
+
+ + Mais segurança na gestão. +
+
+ +
+
+ +
diff --git a/src/app/pages/home/home.scss b/src/app/pages/home/home.scss index 4797418..e7dcede 100644 --- a/src/app/pages/home/home.scss +++ b/src/app/pages/home/home.scss @@ -1,136 +1,544 @@ -/* =============================== */ -/* CONTAINER PRINCIPAL */ -/* =============================== */ -.hero-text-section { - text-align: center; - padding-top: 50px; - margin: 0 auto; +:host { + --brand: #E33DCF; + --brand-soft: rgba(227, 61, 207, 0.14); + --brand-soft-2: rgba(227, 61, 207, 0.08); + + --text: #111214; + --muted: rgba(17, 18, 20, 0.70); + + --radius-xl: 22px; + --radius-lg: 16px; + + display: block; +} + +/* ✅ FUNDO GLOBAL (vale para a Home TODA até o footer) */ +.home-page { + position: relative; + min-height: 100vh; + overflow: hidden; + + background: + radial-gradient(900px 420px at 20% 10%, var(--brand-soft), transparent 60%), + radial-gradient(820px 380px at 80% 30%, var(--brand-soft-2), transparent 60%), + linear-gradient(180deg, #ffffff 0%, #f5f5f7 70%); + + /* névoa suave pra dar sensação de blur */ + &::after { + content: ''; + position: absolute; + inset: 0; + pointer-events: none; + background: rgba(255, 255, 255, 0.25); + } +} + +/* ✅ BLOBS FIXOS (continuam no scroll) */ +.page-blob { + position: fixed; + pointer-events: none; + border-radius: 999px; + filter: blur(34px); + opacity: 0.55; + z-index: 0; + + background: radial-gradient(circle at 30% 30%, rgba(227,61,207,0.55), rgba(227,61,207,0.06)); + animation: floaty 10s ease-in-out infinite; + + &.blob-1 { width: 420px; height: 420px; top: -140px; left: -140px; } + &.blob-2 { width: 520px; height: 520px; top: -220px; right: -240px; animation-duration: 12s; } + &.blob-3 { width: 360px; height: 360px; bottom: -180px; left: 25%; animation-duration: 14s; } + &.blob-4 { width: 520px; height: 520px; bottom: -260px; right: -260px; animation-duration: 16s; opacity: .45; } + + @media (max-width: 992px) { + opacity: 0.35; + } +} + +/* ✅ garante que o conteúdo fique acima do fundo */ +.hero, +.features-section, +.container { + position: relative; + z-index: 1; } /* =============================== */ -/* TÍTULO PRINCIPAL */ +/* HERO */ /* =============================== */ +.hero { + padding: 56px 0 18px 0; +} + +.hero-inner { + display: grid; + grid-template-columns: 1.1fr 0.9fr; + gap: 36px; + align-items: center; + + @media (max-width: 992px) { + grid-template-columns: 1fr; + gap: 26px; + } +} + +/* texto centralizado */ +.hero-copy { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +/* badge */ +.hero-badge { + display: inline-flex; + align-items: center; + gap: 10px; + + width: fit-content; + padding: 10px 14px; + border-radius: 999px; + + background: rgba(255, 255, 255, 0.78); + border: 1px solid rgba(227, 61, 207, 0.22); + backdrop-filter: blur(10px); + + color: var(--text); + font-family: 'Inter', sans-serif; + font-size: 14px; + font-weight: 700; + + i { color: var(--brand); font-size: 16px; } +} + +.hero-text-section { + width: 100%; + margin-top: 12px; +} + +/* título */ .main-title { font-family: 'Inter', sans-serif; - font-size: 48px; - line-height: 1.2; - margin-bottom: 30px; + font-size: 52px; + line-height: 1.05; + margin: 18px 0 18px 0; display: flex; flex-direction: column; align-items: center; - width: fit-content; - margin: 0 auto 80px auto; - - /* ========== RESPONSIVO ========== */ - - /* até 1400px diminui um pouco */ - @media (max-width: 1400px) { - font-size: 40px; - } - - /* tablets e notebooks pequenos */ - @media (max-width: 1024px) { - font-size: 36px; - } - - /* tablets menores */ - @media (max-width: 768px) { - font-size: 32px; - margin-bottom: 50px; - } - - /* celulares */ - @media (max-width: 480px) { - font-size: 28px; - margin-bottom: 40px; - } + @media (max-width: 1400px) { font-size: 44px; } + @media (max-width: 1024px) { font-size: 38px; } + @media (max-width: 768px) { font-size: 32px; } + @media (max-width: 480px) { font-size: 28px; } } -/* Primeira linha */ -.main-title .first-line { - color: #E33DCF; - font-weight: 500; - display: block; - text-align: center; -} - -/* Segunda linha */ +.main-title .first-line, .main-title .second-line { - color: #E33DCF; - font-weight: 500; + font-weight: 650; display: block; + color: var(--text); text-align: center; } -/* -------- Efeito deslocado só em telas grandes -------- */ -@media (min-width: 1200px) { - .main-title .first-line { - transform: translateX(-70px); - } - - .main-title .second-line { - transform: translateX(100px) translateY(-6px); - } +.main-title strong { + color: var(--brand); + position: relative; } -/* Em telas menores que 1200px o título fica plenamente centralizado */ -@media (max-width: 1199.98px) { - .main-title .first-line, - .main-title .second-line { - transform: none; /* garante que não herda nenhum translate */ - } +.main-title strong::after { + content: ''; + position: absolute; + left: 0; + bottom: 6px; + width: 100%; + height: 10px; + border-radius: 999px; + background: rgba(227, 61, 207, 0.18); + z-index: -1; } -/* =============================== */ -/* PARÁGRAFO PRINCIPAL */ -/* =============================== */ +/* parágrafo */ .main-paragraph { - width: 985px; - height: 132px; - margin: 0 auto 50px auto; + width: min(980px, 100%); + margin: 0 auto 16px auto; font-family: 'Poppins', sans-serif; - font-size: 24px; - color: #000000; - line-height: 1; - font-weight: 400; + font-size: 20px; + color: var(--muted); + line-height: 1.45; + + @media (max-width: 1400px) { font-size: 19px; } + @media (max-width: 1024px) { font-size: 18px; } + @media (max-width: 768px) { font-size: 16px; } +} + +.main-paragraph .brand-name { color: var(--text); } +.main-paragraph .highlight { color: var(--text); font-weight: 800; } +.main-paragraph strong { font-weight: 800; } + +/* botões */ +.hero-actions { + display: flex; + gap: 14px; + align-items: center; + justify-content: center; + margin-top: 16px; + flex-wrap: wrap; +} + +.cta-secondary { + height: 44px; + padding: 0 14px; + border-radius: 12px; + background: rgba(255, 255, 255, 0.72); + border: 1px solid rgba(17, 18, 20, 0.10); + color: var(--text); + font-weight: 800; + display: inline-flex; + align-items: center; + gap: 10px; + transition: transform 180ms ease, border-color 180ms ease, box-shadow 180ms ease; + + i { color: var(--brand); } + + &:hover { + transform: translateY(-2px); + border-color: rgba(227, 61, 207, 0.28); + box-shadow: 0 12px 24px rgba(17, 18, 20, 0.10); + } +} + +/* =============================== */ +/* MOCK (direita) */ +/* =============================== */ +.hero-mock { + display: flex; + justify-content: flex-end; + + @media (max-width: 992px) { + justify-content: center; + } +} + +.mock-card { + width: min(460px, 100%); + border-radius: var(--radius-xl); + background: rgba(255, 255, 255, 0.78); + border: 1px solid rgba(227, 61, 207, 0.14); + backdrop-filter: blur(12px); + box-shadow: 0 22px 46px rgba(17, 18, 20, 0.10); + overflow: hidden; + + transform: perspective(900px) rotateY(-6deg) rotateX(2deg); + transition: transform 200ms ease; + + &:hover { + transform: perspective(900px) rotateY(-2deg) rotateX(1deg) translateY(-2px); + } +} + +.mock-top { + display: flex; + align-items: center; + gap: 8px; + padding: 14px 16px; + background: linear-gradient(180deg, rgba(227, 61, 207, 0.10), rgba(255, 255, 255, 0.20)); + + .dot { + width: 10px; + height: 10px; + border-radius: 999px; + background: rgba(17, 18, 20, 0.12); + } + + .mock-title { + margin-left: 6px; + font-weight: 950; + font-family: 'Inter', sans-serif; + color: var(--text); + } +} + +.mock-grid { + padding: 16px; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; + + @media (max-width: 380px) { grid-template-columns: 1fr; } +} + +.mock-kpi { + border-radius: var(--radius-lg); + background: #fff; + border: 1px solid rgba(17, 18, 20, 0.08); + padding: 12px; + + .kpi-label { + display: block; + font-size: 12px; + color: rgba(17, 18, 20, 0.65); + font-family: 'Inter', sans-serif; + font-weight: 800; + } + + .kpi-value { + display: block; + font-size: 26px; + font-family: 'Inter', sans-serif; + font-weight: 950; + color: var(--text); + margin-top: 2px; + } + + .kpi-tag { + display: inline-flex; + align-items: center; + gap: 6px; + margin-top: 8px; + padding: 6px 10px; + border-radius: 999px; + border: 1px solid rgba(227, 61, 207, 0.20); + background: rgba(227, 61, 207, 0.07); + color: var(--text); + font-size: 12px; + font-weight: 900; + + i { color: var(--brand); } + } +} + +.mock-line { + grid-column: 1 / -1; + border-radius: var(--radius-lg); + background: #fff; + border: 1px solid rgba(17, 18, 20, 0.08); + padding: 12px; + + display: grid; + grid-template-columns: auto 1fr auto; + gap: 12px; + align-items: center; + + .line-icon { + width: 40px; + height: 40px; + border-radius: 12px; + background: rgba(227, 61, 207, 0.10); + display: grid; + place-items: center; + + i { color: var(--brand); font-size: 18px; } + } + + .line-title { + font-weight: 950; + font-family: 'Inter', sans-serif; + color: var(--text); + font-size: 14px; + } + + .line-sub { + color: rgba(17, 18, 20, 0.65); + font-size: 12px; + margin-top: 2px; + font-family: 'Inter', sans-serif; + } + + .line-pill { + padding: 8px 10px; + border-radius: 999px; + font-weight: 950; + font-size: 12px; + background: rgba(227, 61, 207, 0.10); + border: 1px solid rgba(227, 61, 207, 0.22); + color: var(--text); + } +} + +/* =============================== */ +/* MÉTRICAS CENTRALIZADAS (MEIO) */ +/* =============================== */ +.hero-metrics-wide { + margin-top: 18px; + display: flex; + justify-content: center; +} + +.hero-metrics { + display: flex; + gap: 14px; + flex-wrap: wrap; + justify-content: center; +} + +.metric { + display: inline-flex; + align-items: center; + gap: 10px; + padding: 12px 14px; + border-radius: 14px; + background: rgba(255, 255, 255, 0.78); + border: 1px solid rgba(227, 61, 207, 0.16); + backdrop-filter: blur(10px); + + i { color: var(--brand); font-size: 18px; } + + .metric-title { + display: block; + font-weight: 900; + color: var(--text); + font-family: 'Inter', sans-serif; + font-size: 14px; + line-height: 1.1; + } + + .metric-sub { + display: block; + font-family: 'Inter', sans-serif; + font-size: 12px; + color: rgba(17, 18, 20, 0.65); + } +} + +/* =============================== */ +/* FEATURES */ +/* =============================== */ +.features-section { + padding: 18px 0 60px 0; + background: transparent; +} + +.section-head { text-align: center; + margin-bottom: 24px; +} - /* ========== RESPONSIVO ========== */ +.section-title { + font-family: 'Inter', sans-serif; + font-weight: 950; + color: var(--text); + font-size: 30px; - @media (max-width: 1400px) { - width: 65%; - height: auto; - font-size: 20px; + @media (max-width: 768px) { font-size: 24px; } +} + +.section-title .brand { color: var(--brand); } + +.section-subtitle { + margin-top: 10px; + color: var(--muted); + font-family: 'Poppins', sans-serif; +} + +/* ✅ AQUI: 3 CARDS CENTRALIZADOS LADO A LADO NO NOTEBOOK */ +.feature-cards-row { + display: flex; + justify-content: center; + align-items: stretch; + gap: 26px; + flex-wrap: nowrap; /* ✅ força ficar em 1 linha */ + + /* notebook / telas médias */ + @media (max-width: 1199.98px) { + gap: 18px; } - /* Tablets */ - @media (max-width: 1024px) { - width: 70%; - height: auto; - font-size: 20px; - line-height: 1.2; - } - - /* Tablets pequenos */ - @media (max-width: 768px) { - width: 80%; - font-size: 18px; - line-height: 1.25; - margin-bottom: 40px; - } - - /* Celulares */ - @media (max-width: 480px) { - width: 90%; - font-size: 16px; - line-height: 1.3; - margin-bottom: 30px; + /* quando a tela ficar pequena de verdade, aí quebra */ + @media (max-width: 992px) { + flex-wrap: wrap; } } -.main-paragraph strong { - font-weight: 700; +/* Garante que o col-auto do bootstrap não atrapalhe */ +.feature-item { + display: flex; + justify-content: center; + align-items: stretch; + margin-bottom: 0 !important; +} + +/* melhora o respiro no botão */ +.button-section { + margin-top: 18px; +} + +/* faixa de valores */ +.value-strip { + margin-top: 26px; + padding: 14px 16px; + border-radius: var(--radius-xl); + background: rgba(255, 255, 255, 0.80); + border: 1px solid rgba(227, 61, 207, 0.14); + backdrop-filter: blur(10px); + + display: flex; + justify-content: center; + gap: 18px; + flex-wrap: wrap; + + .value { + display: inline-flex; + align-items: center; + gap: 10px; + + i { color: var(--brand); font-size: 18px; } + span { color: var(--text); font-family: 'Inter', sans-serif; } + } +} + +/* =============================== */ +/* ✅ ANIMAÇÕES SSR-SAFE */ +/* =============================== */ +[data-animate] { opacity: 1; transform: none; } + +.js-animate [data-animate] { + opacity: 0; + transform: translateY(14px); + transition: opacity 600ms ease, transform 600ms ease; + will-change: opacity, transform; +} + +.js-animate [data-animate].is-visible { + opacity: 1; + transform: translateY(0); +} + +/* brilho suave no botão */ +:host ::ng-deep app-cta-button .btn, +:host ::ng-deep app-cta-button button { + position: relative; + overflow: hidden; + transform: translateZ(0); +} + +:host ::ng-deep app-cta-button .btn::after, +:host ::ng-deep app-cta-button button::after { + content: ''; + position: absolute; + inset: 0; + transform: translateX(-120%) rotate(12deg); + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.55), transparent); + animation: shine 3.4s ease-in-out infinite; +} + +@media (prefers-reduced-motion: reduce) { + .page-blob { animation: none; } + .js-animate [data-animate] { transition: none; transform: none; opacity: 1; } + :host ::ng-deep app-cta-button .btn::after, + :host ::ng-deep app-cta-button button::after { animation: none; } +} + +@keyframes floaty { + 0% { transform: translate(0, 0) scale(1); } + 50% { transform: translate(18px, 10px) scale(1.03); } + 100% { transform: translate(0, 0) scale(1); } +} + +@keyframes shine { + 0%, 65% { transform: translateX(-120%) rotate(12deg); opacity: 0.0; } + 75% { opacity: 1; } + 100% { transform: translateX(120%) rotate(12deg); opacity: 0.0; } } diff --git a/src/app/pages/home/home.ts b/src/app/pages/home/home.ts index 9540652..d263c11 100644 --- a/src/app/pages/home/home.ts +++ b/src/app/pages/home/home.ts @@ -1,29 +1,68 @@ -import { Component } from '@angular/core'; -import { CommonModule } from '@angular/common'; +import { Component, AfterViewInit, Inject, PLATFORM_ID } from '@angular/core'; +import { CommonModule, isPlatformBrowser } 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) +import { Router } from '@angular/router'; @Component({ selector: 'app-home', standalone: true, - imports: [ - CommonModule, - FeatureCardComponent, - CtaButtonComponent - ], + imports: [CommonModule, FeatureCardComponent, CtaButtonComponent], templateUrl: './home.html', - styleUrls: ['./home.scss'], // ⬅️ styleUrls (array) + styleUrls: ['./home.scss'], }) -export class Home { +export class Home implements AfterViewInit { + private readonly isBrowser: boolean; - // ⬅️ Aqui o Angular injeta o Router e guarda em this.router - constructor(private router: Router) {} + constructor( + private router: Router, + @Inject(PLATFORM_ID) platformId: Object + ) { + this.isBrowser = isPlatformBrowser(platformId); + } iniciar(): void { - // só pra debug, se quiser: - // console.log('Botão COMEÇAR AGORA clicado! Redirecionando para /register...'); + this.router.navigate(['/register']); + } - this.router.navigate(['/register']); // ⬅️ agora this.router não é mais undefined + scrollToFeatures(): void { + if (!this.isBrowser) return; + document.getElementById('features')?.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + + ngAfterViewInit(): void { + if (!this.isBrowser) return; + + document.documentElement.classList.add('js-animate'); + + setTimeout(() => { + const items = Array.from(document.querySelectorAll('[data-animate]')); + if (!items.length) return; + + const reduceMotion = + window.matchMedia && + window.matchMedia('(prefers-reduced-motion: reduce)').matches; + + if (reduceMotion) { + items.forEach(i => i.classList.add('is-visible')); + return; + } + + if (!('IntersectionObserver' in window)) { + items.forEach(i => i.classList.add('is-visible')); + return; + } + + const io = new IntersectionObserver((entries) => { + for (const entry of entries) { + if (entry.isIntersecting) { + (entry.target as HTMLElement).classList.add('is-visible'); + io.unobserve(entry.target); + } + } + }, { threshold: 0.12 }); + + items.forEach(el => io.observe(el)); + }, 0); } } diff --git a/src/app/pages/login/login.ts b/src/app/pages/login/login.ts index 616e55b..53af01b 100644 --- a/src/app/pages/login/login.ts +++ b/src/app/pages/login/login.ts @@ -77,15 +77,15 @@ export class LoginComponent { }; this.authService.login(payload).subscribe({ - next: async (res) => { + next: (res) => { this.isSubmitting = false; const nome = this.getNameFromToken(res.token); - await this.showToast(`Bem-vindo, ${nome}!`); - setTimeout(() => { - this.router.navigate(['/geral']); - }, 900); + // ✅ Vai para /geral já levando a mensagem do toast + this.router.navigate(['/geral'], { + state: { toastMessage: `Bem-vindo, ${nome}!` } + }); }, error: (err) => { this.isSubmitting = false;