saveinmed/website/README.md
2026-01-08 14:28:34 -03:00

9.2 KiB

SaveInMed Website

Status (pronto x faltando)

Pronto

  • Conteúdo descrito neste documento.

Faltando

  • Confirmar no código o estado real das funcionalidades e atualizar esta seção conforme necessário.

Site institucional do SaveInMed, desenvolvido com Fresh (Deno framework).

🎯 Propósito

Este é o site institucional e de marketing do SaveInMed, responsável por:

  • Apresentar a plataforma para novos usuários
  • Landing pages de produtos e serviços
  • Captura de leads
  • Informações sobre a empresa
  • Blog e conteúdo educacional
  • SEO otimizado para aquisição orgânica

🚀 Tecnologias

  • Fresh v2 - Framework web moderno para Deno
  • Deno 2 - Runtime JavaScript/TypeScript seguro
  • Preact - Biblioteca UI leve (3KB)
  • Tailwind CSS - Framework CSS utility-first
  • Islands Architecture - Hidratação parcial para performance máxima

📋 Funcionalidades

Landing Pages

  • Página inicial institucional
  • Páginas de produtos
  • Casos de uso
  • Preços e planos

Captura de Leads

  • Formulários de contato
  • Newsletter signup
  • Demonstração agendada
  • Download de materiais

Conteúdo

  • Blog posts
  • Artigos educacionais
  • FAQ
  • Documentação pública

SEO

  • Meta tags otimizadas
  • Open Graph
  • Schema.org markup
  • Sitemap XML
  • robots.txt

🏗️ Arquitetura (Fresh Islands)

website/
├── routes/                       # Rotas baseadas em arquivo
│   ├── index.tsx                 # Página inicial
│   ├── about.tsx                 # Sobre nós
│   ├── contact.tsx               # Contato
│   ├── blog/
│   │   ├── index.tsx             # Lista de posts
│   │   └── [slug].tsx            # Post individual
│   └── api/                      # API routes
│       └── newsletter.ts         # Endpoint de newsletter
├── islands/                      # Componentes interativos (client-side)
│   ├── FlowTicker.tsx            # Ticker animado
│   ├── LeadForm.tsx              # Formulário de captura
│   └── ContactForm.tsx           # Formulário de contato
├── components/                   # Componentes estáticos (SSR)
│   ├── Header.tsx
│   ├── Footer.tsx
│   └── Hero.tsx
├── static/                       # Arquivos estáticos
│   ├── styles.css
│   ├── logo.svg
│   └── images/
├── fresh.config.ts               # Configuração do Fresh
├── deno.json                     # Configuração do Deno
└── README.md

🔧 Configuração

Variáveis de Ambiente

Crie um arquivo .env na raiz do projeto:

# API Backend
API_URL=https://api.saveinmed.com

# Email (para formulários)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=noreply@saveinmed.com
SMTP_PASSWORD=your-password

# Analytics
GOOGLE_ANALYTICS_ID=G-XXXXXXXXXX

# Ambiente
DENO_ENV=development

Pré-requisitos

  • Deno 2.0 ou superior

🏃 Execução Local

# Executar em modo desenvolvimento
deno task dev

# Site estará disponível em http://localhost:8000

🏗️ Build e Produção

# Build para produção
deno task build

# Executar build de produção
deno task start

# Preview
deno task preview

🎨 Islands Architecture

Fresh utiliza a arquitetura de "islands" para hidratação parcial:

Componentes Estáticos (SSR)

Renderizados no servidor, sem JavaScript no cliente:

// components/Hero.tsx
export function Hero() {
  return (
    <section class="bg-gradient-to-r from-blue-600 to-purple-600 text-white py-20">
      <h1 class="text-5xl font-bold">SaveInMed</h1>
      <p class="text-xl mt-4">Marketplace B2B Farmacêutico</p>
    </section>
  );
}

Islands (Interativos)

Componentes que precisam de JavaScript no cliente:

// islands/LeadForm.tsx
import { useState } from "preact/hooks";

export default function LeadForm() {
  const [email, setEmail] = useState("");
  
  const handleSubmit = async (e: Event) => {
    e.preventDefault();
    await fetch("/api/newsletter", {
      method: "POST",
      body: JSON.stringify({ email }),
    });
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onInput={(e) => setEmail(e.currentTarget.value)}
        placeholder="Seu email"
      />
      <button type="submit">Inscrever</button>
    </form>
  );
}

🛣️ Roteamento

Fresh usa roteamento baseado em arquivos:

routes/
├── index.tsx          → /
├── about.tsx          → /about
├── contact.tsx        → /contact
├── blog/
│   ├── index.tsx      → /blog
│   └── [slug].tsx     → /blog/:slug
└── api/
    └── newsletter.ts  → /api/newsletter

📡 API Routes

// routes/api/newsletter.ts
import { Handlers } from "$fresh/server.ts";

export const handler: Handlers = {
  async POST(req) {
    const { email } = await req.json();
    
    // Salvar email no banco de dados
    await saveToNewsletter(email);
    
    return new Response(JSON.stringify({ success: true }), {
      headers: { "Content-Type": "application/json" },
    });
  },
};

🎨 Estilização

O projeto usa Tailwind CSS com configuração customizada:

// tailwind.config.ts
export default {
  theme: {
    extend: {
      colors: {
        primary: '#3b82f6',
        secondary: '#8b5cf6',
      },
    },
  },
};

🔍 SEO

Meta Tags

export default function BlogPost({ data }) {
  return (
    <>
      <Head>
        <title>{data.title} | SaveInMed Blog</title>
        <meta name="description" content={data.excerpt} />
        <meta property="og:title" content={data.title} />
        <meta property="og:description" content={data.excerpt} />
        <meta property="og:image" content={data.image} />
      </Head>
      <article>{/* ... */}</article>
    </>
  );
}

Sitemap

// routes/sitemap.xml.ts
export const handler: Handlers = {
  GET() {
    const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
      <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
        <url>
          <loc>https://saveinmed.com/</loc>
          <priority>1.0</priority>
        </url>
      </urlset>`;
    
    return new Response(sitemap, {
      headers: { "Content-Type": "application/xml" },
    });
  },
};

Performance

Fresh é otimizado para performance máxima:

  • Zero JavaScript por padrão: Apenas islands têm JS
  • Edge rendering: Deploy em edge para latência mínima
  • Streaming SSR: Renderização progressiva
  • Automatic code splitting: Por rota e island
  • Preact: Apenas 3KB de runtime

Lighthouse Score Esperado

  • Performance: 95-100
  • Accessibility: 95-100
  • Best Practices: 95-100
  • SEO: 95-100

🚀 Deploy

Deno Deploy (Recomendado)

# Instalar deployctl
deno install -Arf https://deno.land/x/deploy/deployctl.ts

# Deploy
deployctl deploy --project=saveinmed-website

Docker

# Build
docker build -t saveinmed-website:latest .

# Run
docker run -p 8000:8000 saveinmed-website:latest

Outras Plataformas

  • Cloudflare Pages: Suporte para Deno
  • Netlify: Via Deno runtime
  • Vercel: Via Deno runtime

📧 Formulários de Contato

// islands/ContactForm.tsx
export default function ContactForm() {
  const handleSubmit = async (e: Event) => {
    e.preventDefault();
    const formData = new FormData(e.target as HTMLFormElement);
    
    await fetch("/api/contact", {
      method: "POST",
      body: JSON.stringify(Object.fromEntries(formData)),
    });
    
    alert("Mensagem enviada!");
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input name="name" placeholder="Nome" required />
      <input name="email" type="email" placeholder="Email" required />
      <textarea name="message" placeholder="Mensagem" required />
      <button type="submit">Enviar</button>
    </form>
  );
}

🧪 Testes

# Executar testes
deno test

# Com coverage
deno test --coverage=coverage

# Gerar relatório de coverage
deno coverage coverage --lcov > coverage.lcov

Para matriz completa (com/sem banco e Playwright), veja: docs/TESTES.md

📊 Analytics

Integração com Google Analytics:

// components/Analytics.tsx
export function Analytics() {
  return (
    <script
      async
      src={`https://www.googletagmanager.com/gtag/js?id=${GA_ID}`}
    />
  );
}

🔗 Integração com Outros Componentes

  • Backend APIs: Links para signup e login
  • SaveInMed Frontend: Redirecionamento após captura de lead
  • Blog: Pode ser hospedado separadamente ou integrado

📝 Conteúdo

Estrutura de Blog Post

interface BlogPost {
  slug: string;
  title: string;
  excerpt: string;
  content: string;
  author: string;
  publishedAt: Date;
  tags: string[];
  image: string;
}

🌐 Internacionalização (i18n)

// lib/i18n.ts
const translations = {
  pt: {
    hero: {
      title: "Marketplace B2B Farmacêutico",
      cta: "Começar Agora",
    },
  },
  en: {
    hero: {
      title: "B2B Pharmaceutical Marketplace",
      cta: "Get Started",
    },
  },
};

📝 Licença

MIT