瀏覽代碼

feat: :sparkles: feat (landing) estrutura inicial da landingpage

criacao da estrutura inicial da landingpage (ainda sem as funcoes de redirecionar ao sistema)

fase:dev | origin:escopo
Gustavo Zanatta 2 周之前
父節點
當前提交
6c09677315

+ 18 - 1
app/assets/css/main.css

@@ -147,7 +147,24 @@
   text-transform: uppercase;
 }
 
-/* ─────────────────────────────────────────────────────────────── */
+.reveal {
+  opacity: 0;
+  transform: translateY(28px);
+  transition: opacity 0.6s ease, transform 0.6s ease;
+  will-change: opacity, transform;
+}
+
+.reveal.is-visible {
+  opacity: 1;
+  transform: translateY(0);
+}
+
+.reveal-delay-1 { transition-delay: 0.1s; }
+.reveal-delay-2 { transition-delay: 0.2s; }
+.reveal-delay-3 { transition-delay: 0.3s; }
+.reveal-delay-4 { transition-delay: 0.4s; }
+.reveal-delay-5 { transition-delay: 0.5s; }
+.reveal-delay-6 { transition-delay: 0.6s; }
 
 ::selection,
 ::-moz-selection {

二進制
app/assets/images/imagem_fundo.png


File diff suppressed because it is too large
+ 6 - 0
app/assets/images/logo_desktop.svg


File diff suppressed because it is too large
+ 6 - 0
app/assets/images/logo_mobile.svg


+ 83 - 2
app/components/AppFooter.vue

@@ -1,5 +1,86 @@
 <template>
-  <footer class="h-[126px] text-center flex justify-center items-center">
-    <p class="font-medium text-xs md:text-sm text-black">© 2025 Serprati. Todos os direitos reservados.</p>
+  <footer class="bg-violet-dark text-white">
+    <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 lg:py-16">
+      <div class="grid grid-cols-1 sm:grid-cols-3 gap-10 lg:gap-16">
+
+        <div>
+          <img
+            class="h-9 mb-4 brightness-0 invert"
+            src="~/assets/images/logo_desktop.svg"
+            alt="Ser Prati"
+          />
+          <p class="text-sm leading-relaxed" style="color: rgba(254,254,254,0.65);">
+            Associação dos Servidores — cuidando de você e sua família.
+          </p>
+        </div>
+
+        <div>
+          <h4 class="text-xs font-semibold uppercase tracking-widest mb-5 text-neutral-light">
+            Links
+          </h4>
+          <ul class="space-y-3">
+            <li>
+              <button @click="scrollTo('quem-somos')" class="footer-link">Quem Somos</button>
+            </li>
+            <li>
+              <button @click="scrollTo('beneficios')" class="footer-link">Benefícios</button>
+            </li>
+            <li>
+              <span class="footer-link opacity-50 cursor-default select-none">Parceiros</span>
+            </li>
+            <li>
+              <button @click="scrollTo('associe-se')" class="footer-link">Associe-se</button>
+            </li>
+          </ul>
+        </div>
+
+        <div>
+          <h4 class="text-xs font-semibold uppercase tracking-widest mb-5 text-neutral-light">
+            Contato
+          </h4>
+          <ul class="space-y-3">
+            <li class="flex items-center gap-2" style="color: rgba(254,254,254,0.65);">
+              <span class="material-icons text-lg leading-none">email</span>
+              <span class="text-sm">contato@serprati.com.br</span>
+            </li>
+            <li class="flex items-center gap-2" style="color: rgba(254,254,254,0.65);">
+              <span class="material-icons text-lg leading-none">phone</span>
+              <span class="text-sm">(51) 3000-0000</span>
+            </li>
+            <li class="flex items-center gap-2" style="color: rgba(254,254,254,0.65);">
+              <span class="material-icons text-lg leading-none">location_on</span>
+              <span class="text-sm">Porto Alegre - RS</span>
+            </li>
+          </ul>
+        </div>
+
+      </div>
+    </div>
+
+    <div class="border-t" style="border-color: rgba(255,255,255,0.1);">
+      <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
+        <p class="text-center text-xs" style="color: rgba(254,254,254,0.4);">
+          © 2025 SER PRATI. Todos os direitos reservados.
+        </p>
+      </div>
+    </div>
   </footer>
 </template>
+
+<script setup lang="ts">
+const scrollTo = (id: string) => {
+  document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' })
+}
+</script>
+
+<style scoped>
+.footer-link {
+  font-size: 0.875rem;
+  color: rgba(254, 254, 254, 0.65);
+  transition: color 0.2s;
+  cursor: pointer;
+}
+.footer-link:hover {
+  color: #fefefe;
+}
+</style>

+ 72 - 1
app/components/AppHeader.vue

@@ -1,3 +1,74 @@
 <template>
-  <div></div>
+  <header class="fixed top-0 left-0 right-0 z-50 bg-white shadow-sm h-16">
+    <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-full flex items-center justify-between">
+
+      <button @click="scrollTo('quem-somos')" class="flex-shrink-0 focus:outline-none">
+        <img
+          class="hidden lg:block h-9"
+          src="~/assets/images/logo_desktop.svg"
+          alt="Ser Prati"
+        />
+        <img
+          class="block lg:hidden h-8"
+          src="~/assets/images/logo_mobile.svg"
+          alt="Ser Prati"
+        />
+      </button>
+
+      <nav class="hidden lg:flex items-center gap-8">
+        <button @click="scrollTo('quem-somos')" class="nav-link">Quem Somos</button>
+        <button @click="scrollTo('beneficios')" class="nav-link">Benefícios</button>
+        <span class="nav-link opacity-50 cursor-default select-none">Parceiros</span>
+        <button @click="scrollTo('associe-se')" class="nav-link">Associe-se</button>
+      </nav>
+
+      <div class="flex items-center gap-3 lg:gap-4">
+        <nav class="flex lg:hidden items-center gap-3">
+          <button @click="scrollTo('beneficios')" class="nav-link-mobile">Benefícios</button>
+          <button @click="scrollTo('associe-se')" class="nav-link-mobile">Associe-se</button>
+        </nav>
+
+        <button
+          @click="handleEntrar"
+          class="border border-violet-normal text-violet-normal text-xs lg:text-sm font-medium px-3 py-1.5 lg:px-5 lg:py-2 rounded-md hover:bg-violet-light transition-colors duration-200 cursor-pointer"
+        >
+          ENTRAR
+        </button>
+      </div>
+
+    </div>
+  </header>
 </template>
+
+<script setup lang="ts">
+const scrollTo = (id: string) => {
+  document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' })
+}
+
+const handleEntrar = () => {
+  console.log('redirecionando para pagina de entrar')
+}
+</script>
+
+<style scoped>
+.nav-link {
+  font-size: 0.875rem;
+  font-weight: 500;
+  color: #505050;
+  transition: color 0.2s;
+  cursor: pointer;
+}
+.nav-link:hover {
+  color: #661d75;
+}
+.nav-link-mobile {
+  font-size: 0.75rem;
+  font-weight: 500;
+  color: #505050;
+  transition: color 0.2s;
+  cursor: pointer;
+}
+.nav-link-mobile:hover {
+  color: #661d75;
+}
+</style>

+ 2 - 1
app/layouts/default.vue

@@ -1,6 +1,7 @@
 <template>
   <div>
-    <main>
+    <AppHeader />
+    <main class="pt-16">
       <slot />
     </main>
     <AppFooter />

+ 461 - 8
app/pages/index.vue

@@ -1,16 +1,469 @@
 <template>
   <div>
-    <div class="flex justify-center items-center h-[calc(100vh-126px)]">
-      <p class="text-text text-xl font-semibold">Serprati Landing Page</p>
-    </div>
+
+    <section
+      id="quem-somos"
+      class="relative min-h-screen flex flex-col bg-cover bg-center"
+      :style="{ backgroundImage: `url(${heroBg})` }"
+    >
+      <div class="absolute inset-0 bg-violet-normal/80"></div>
+
+      <div class="relative z-10 flex-1 flex items-center">
+        <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 w-full py-16 lg:py-24">
+          <div class="max-w-2xl">
+
+            <h1
+              class="reveal text-h2 text-white mb-6"
+              style="font-size: clamp(2rem, 5vw, 3.75rem);"
+            >
+              Ser PRATI é fazer parte de algo maior
+            </h1>
+
+            <p class="reveal reveal-delay-1 text-subtitle-1 text-white/85 mb-10 leading-relaxed">
+              Benefícios exclusivos, parceiros de qualidade e uma comunidade
+              que cuida de você e sua família.
+            </p>
+
+            <div class="reveal reveal-delay-2 flex flex-col sm:flex-row gap-4">
+              <button
+                @click="scrollTo('associe-se')"
+                class="btn-primary"
+              >
+                ASSOCIE-SE AGORA
+              </button>
+              <button
+                @click="scrollTo('beneficios')"
+                class="btn-outline"
+              >
+                CONHEÇA OS BENEFÍCIOS
+              </button>
+            </div>
+
+          </div>
+        </div>
+      </div>
+
+      <div class="relative z-10 bg-violet-dark/60 backdrop-blur-sm">
+        <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
+          <div class="grid grid-cols-3 gap-4 text-center">
+
+            <div class="reveal reveal-delay-1 flex flex-col items-center gap-1">
+              <span class="material-icons text-white/80 text-3xl lg:text-4xl">group</span>
+              <span class="text-h5 text-white font-bold" style="font-size: clamp(1.1rem, 2.5vw, 1.5rem);">1.000+</span>
+              <span class="text-caption text-white/70 lg:text-sm">Associados</span>
+            </div>
+
+            <div class="reveal reveal-delay-2 flex flex-col items-center gap-1">
+              <span class="material-icons text-white/80 text-3xl lg:text-4xl">handshake</span>
+              <span class="text-h5 text-white font-bold" style="font-size: clamp(1.1rem, 2.5vw, 1.5rem);">50+</span>
+              <span class="text-caption text-white/70 lg:text-sm">Parceiros</span>
+            </div>
+
+            <div class="reveal reveal-delay-3 flex flex-col items-center gap-1">
+              <span class="material-icons text-white/80 text-3xl lg:text-4xl">history</span>
+              <span class="text-h5 text-white font-bold" style="font-size: clamp(1.1rem, 2.5vw, 1.5rem);">30+</span>
+              <span class="text-caption text-white/70 lg:text-sm">Anos de História</span>
+            </div>
+
+          </div>
+        </div>
+      </div>
+    </section>
+
+    <section id="beneficios" class="bg-neutral-light py-16 lg:py-24">
+      <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
+
+        <div class="reveal text-center mb-12 lg:mb-16">
+          <h2 class="text-h4 text-text" style="font-size: clamp(1.5rem, 3vw, 2.125rem);">
+            Benefícios para você
+          </h2>
+        </div>
+
+        <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
+
+          <div
+            v-for="(benefit, index) in benefits"
+            :key="benefit.title"
+            :class="`reveal reveal-delay-${index + 1}`"
+            class="benefit-card"
+          >
+            <div class="benefit-icon-wrap">
+              <span class="material-icons text-violet-normal text-2xl">{{ benefit.icon }}</span>
+            </div>
+            <h3 class="text-subtitle-1 font-semibold text-text mt-4 mb-2">
+              {{ benefit.title }}
+            </h3>
+            <p class="text-body-2 text-text-2 leading-relaxed">
+              {{ benefit.description }}
+            </p>
+          </div>
+
+        </div>
+      </div>
+    </section>
+
+    <section id="associe-se" class="bg-violet-light py-16 lg:py-24">
+      <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
+
+        <div class="reveal text-center mb-10 lg:mb-12">
+          <h2 class="text-h4 text-text" style="font-size: clamp(1.5rem, 3vw, 2.125rem);">
+            Associe-se
+          </h2>
+        </div>
+
+        <div class="reveal reveal-delay-1 max-w-3xl mx-auto">
+
+          <div class="mb-0">
+            <button class="tab-active">
+              NOVA ASSOCIAÇÃO
+            </button>
+          </div>
+
+          <div class="bg-white rounded-b-2xl rounded-tr-2xl shadow-md p-6 lg:p-10">
+            <form @submit.prevent="handleSubmit" novalidate>
+
+              <div class="grid grid-cols-1 sm:grid-cols-2 gap-5 mb-6">
+
+                <div class="field-group">
+                  <label class="field-label">Nome Completo</label>
+                  <div class="field-input-wrap">
+                    <span class="material-icons field-icon">person</span>
+                    <input
+                      v-model="form.nomeCompleto"
+                      type="text"
+                      placeholder="Seu Nome"
+                      class="field-input"
+                    />
+                  </div>
+                </div>
+
+                <div class="field-group">
+                  <label class="field-label">CPF</label>
+                  <div class="field-input-wrap">
+                    <span class="material-icons field-icon">badge</span>
+                    <input
+                      :value="form.cpf"
+                      @input="handleCPF"
+                      type="text"
+                      placeholder="000.000.000-00"
+                      maxlength="14"
+                      class="field-input"
+                    />
+                  </div>
+                </div>
+
+                <div class="field-group">
+                  <label class="field-label">E-mail</label>
+                  <div class="field-input-wrap">
+                    <span class="material-icons field-icon">email</span>
+                    <input
+                      v-model="form.email"
+                      type="email"
+                      placeholder="nome@conta.com"
+                      class="field-input"
+                    />
+                  </div>
+                </div>
+
+                <div class="field-group">
+                  <label class="field-label">Telefone</label>
+                  <div class="field-input-wrap">
+                    <span class="material-icons field-icon">phone</span>
+                    <input
+                      v-model="form.telefone"
+                      type="tel"
+                      placeholder="00000000"
+                      class="field-input"
+                    />
+                  </div>
+                </div>
+
+                <div class="field-group">
+                  <label class="field-label">Cargo</label>
+                  <div class="field-input-wrap">
+                    <span class="material-icons field-icon">work</span>
+                    <input
+                      v-model="form.cargo"
+                      type="text"
+                      placeholder="Assistente Financeiro"
+                      class="field-input"
+                    />
+                  </div>
+                </div>
+
+                <div class="field-group">
+                  <label class="field-label">Setor</label>
+                  <div class="field-input-wrap">
+                    <span class="material-icons field-icon">business</span>
+                    <input
+                      v-model="form.setor"
+                      type="text"
+                      placeholder="Financeiro"
+                      class="field-input"
+                    />
+                  </div>
+                </div>
+
+              </div>
+
+              <div class="flex justify-center">
+                <button type="submit" class="btn-submit">
+                  ENVIAR SOLICITAÇÃO
+                </button>
+              </div>
+
+            </form>
+          </div>
+        </div>
+      </div>
+    </section>
+
+    <Transition name="toast">
+      <div
+        v-if="showSuccess"
+        class="fixed bottom-6 right-6 z-50 bg-violet-normal text-white px-5 py-4 rounded-xl shadow-xl flex items-center gap-3"
+      >
+        <span class="material-icons text-xl">check_circle</span>
+        <span class="text-sm font-medium">Solicitação enviada com sucesso!</span>
+      </div>
+    </Transition>
+
   </div>
 </template>
 
 <script setup lang="ts">
-useHead({
-  title: `Landing Page - Serprati`
+import heroBg from '~/assets/images/imagem_fundo.png'
+
+useHead({ title: 'Ser Prati — Associação dos Servidores' })
+definePageMeta({ layout: 'default' })
+
+const scrollTo = (id: string) => {
+  document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' })
+}
+
+const benefits = [
+  {
+    icon: 'local_offer',
+    title: 'Descontos Exclusivos',
+    description: 'Até 50% de desconto em parceiros selecionados.',
+  },
+  {
+    icon: 'health_and_safety',
+    title: 'Convênios Médicos',
+    description: 'Planos de saúde com condições especiais.',
+  },
+  {
+    icon: 'store',
+    title: 'Loja do Associado',
+    description: 'Produtos com preços diferenciados.',
+  },
+  {
+    icon: 'badge',
+    title: 'Carteirinha Digital',
+    description: 'Acesso rápido aos benefícios pelo celular.',
+  },
+  {
+    icon: 'notifications',
+    title: 'Notificações',
+    description: 'Fique por dentro das novidades e promoções.',
+  },
+  {
+    icon: 'family_restroom',
+    title: 'Dependentes',
+    description: 'Inclua sua família nos benefícios.',
+  },
+]
+
+const form = reactive({
+  nomeCompleto: '',
+  cpf: '',
+  email: '',
+  telefone: '',
+  cargo: '',
+  setor: '',
+})
+
+const formatCPF = (value: string): string => {
+  return value
+    .replace(/\D/g, '')
+    .replace(/(\d{3})(\d)/, '$1.$2')
+    .replace(/(\d{3})(\d)/, '$1.$2')
+    .replace(/(\d{3})(\d{1,2})$/, '$1-$2')
+    .slice(0, 14)
+}
+
+const handleCPF = (e: Event) => {
+  form.cpf = formatCPF((e.target as HTMLInputElement).value)
+}
+
+const showSuccess = ref(false)
+
+const handleSubmit = () => {
+  console.log({ ...form })
+  showSuccess.value = true
+  setTimeout(() => { showSuccess.value = false }, 4000)
+  Object.assign(form, {
+    nomeCompleto: '',
+    cpf: '',
+    email: '',
+    telefone: '',
+    cargo: '',
+    setor: '',
+  })
+}
+
+onMounted(() => {
+  const observer = new IntersectionObserver(
+    (entries) => {
+      entries.forEach((entry) => {
+        if (entry.isIntersecting) {
+          entry.target.classList.add('is-visible')
+          observer.unobserve(entry.target)
+        }
+      })
+    },
+    { threshold: 0.1, rootMargin: '0px 0px -40px 0px' }
+  )
+
+  document.querySelectorAll('.reveal').forEach((el) => observer.observe(el))
 })
-definePageMeta({
-  layout: 'default',
-});
 </script>
+
+<style scoped>
+.btn-primary {
+  background-color: #ffffff;
+  color: #661d75;
+  font-family: "Nunito", sans-serif;
+  font-weight: 600;
+  font-size: 0.875rem;
+  letter-spacing: 1px;
+  padding: 0.75rem 1.75rem;
+  border-radius: 0.5rem;
+  transition: background-color 0.2s, color 0.2s;
+  cursor: pointer;
+  white-space: nowrap;
+}
+.btn-primary:hover {
+  background-color: #f0e8f1;
+}
+
+.btn-outline {
+  background-color: transparent;
+  color: #ffffff;
+  font-family: "Nunito", sans-serif;
+  font-weight: 600;
+  font-size: 0.875rem;
+  letter-spacing: 1px;
+  padding: 0.75rem 1.75rem;
+  border-radius: 0.5rem;
+  border: 2px solid rgba(255, 255, 255, 0.75);
+  transition: background-color 0.2s, border-color 0.2s;
+  cursor: pointer;
+  white-space: nowrap;
+}
+.btn-outline:hover {
+  background-color: rgba(255, 255, 255, 0.1);
+  border-color: #ffffff;
+}
+
+.benefit-card {
+  background-color: #ffffff;
+  border-radius: 1rem;
+  padding: 1.75rem;
+  box-shadow: 0 2px 12px rgba(102, 29, 117, 0.07);
+  transition: box-shadow 0.2s, transform 0.2s;
+}
+.benefit-card:hover {
+  box-shadow: 0 6px 24px rgba(102, 29, 117, 0.13);
+  transform: translateY(-2px);
+}
+.benefit-icon-wrap {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 48px;
+  height: 48px;
+  background-color: #f0e8f1;
+  border-radius: 0.75rem;
+}
+
+.tab-active {
+  background-color: #661d75;
+  color: #ffffff;
+  font-family: "Nunito", sans-serif;
+  font-weight: 600;
+  font-size: 0.75rem;
+  letter-spacing: 1.25px;
+  padding: 0.625rem 1.5rem;
+  border-radius: 0.5rem 0.5rem 0 0;
+  cursor: default;
+}
+
+.field-group {
+  display: flex;
+  flex-direction: column;
+  gap: 0.375rem;
+}
+.field-label {
+  font-family: "Nunito", sans-serif;
+  font-size: 0.8125rem;
+  font-weight: 600;
+  color: #505050;
+}
+.field-input-wrap {
+  display: flex;
+  align-items: center;
+  gap: 0.5rem;
+  border: 1.5px solid #C0C0C0;
+  border-radius: 0.5rem;
+  padding: 0.5rem 0.75rem;
+  background-color: #fefefe;
+  transition: border-color 0.2s;
+}
+.field-input-wrap:focus-within {
+  border-color: #661d75;
+}
+.field-icon {
+  font-size: 1.125rem;
+  color: #b6b6b6;
+  flex-shrink: 0;
+}
+.field-input {
+  flex: 1;
+  border: none;
+  outline: none;
+  background: transparent;
+  font-family: "Nunito", sans-serif;
+  font-size: 0.875rem;
+  color: #161616;
+}
+.field-input::placeholder {
+  color: #b6b6b6;
+}
+
+.btn-submit {
+  background-color: #661d75;
+  color: #ffffff;
+  font-family: "Nunito", sans-serif;
+  font-weight: 600;
+  font-size: 0.875rem;
+  letter-spacing: 1.25px;
+  padding: 0.875rem 3rem;
+  border-radius: 0.5rem;
+  transition: background-color 0.2s;
+  cursor: pointer;
+}
+.btn-submit:hover {
+  background-color: #5c1a69;
+}
+
+.toast-enter-active,
+.toast-leave-active {
+  transition: opacity 0.4s ease, transform 0.4s ease;
+}
+.toast-enter-from,
+.toast-leave-to {
+  opacity: 0;
+  transform: translateY(12px);
+}
+</style>

+ 10 - 0
nuxt.config.ts

@@ -14,6 +14,16 @@ export default defineNuxtConfig({
   vite: {
     plugins: [tailwindcss()]
   },
+  app: {
+    head: {
+      link: [
+        {
+          rel: 'stylesheet',
+          href: 'https://fonts.googleapis.com/icon?family=Material+Icons'
+        }
+      ]
+    }
+  },
   runtimeConfig: {
     public: {
       studentPlatform: process.env.STUDENT_PLATFORM_URL || 'http://localhost:9000'

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