Dashboard-Automatizase/docs/stories/story-2-2-exibir-cards-instancias.md
Luis Erlacher 0152a2fda0 feat: add n8n API testing script for Google OAuth2 schema and existing credentials
- Implemented a bash script to test n8n API and retrieve credential schemas.
- Added types for API responses, Google Calendar, and WhatsApp instances.
- Configured Vitest for testing with React and added setup for testing-library.
2025-10-10 14:29:02 -03:00

8.8 KiB

Story 2.2: Exibir Cards de Instâncias WhatsApp com Status

Story Metadata

  • Epic: Epic 2 - WhatsApp Management via EvolutionAPI
  • Story ID: 2.2
  • Priority: P0 (Critical)
  • Effort Estimate: 2-3 hours
  • Status: Ready for Review
  • Assignee: James (Dev Agent)

User Story

Como usuário, Eu quero ver minhas instâncias WhatsApp como cards com status, Para que eu saiba quais estão conectadas ou desconectadas.

Acceptance Criteria

  1. Componente WhatsAppInstanceCard.tsx criado
  2. Dashboard exibe grid/lista de cards (um para cada instância em EVOLUTION_INSTANCE_NAMES)
  3. Cada card mostra: Nome da instância, Status badge (verde "Connected" ou vermelho "Disconnected")
  4. Layout dos cards segue design similar à imagem de referência da EvolutionAPI
  5. Cards são responsivos (stack em mobile, grid em desktop)
  6. Status é carregado via getAllInstancesStatus ao montar o componente
  7. Loading state exibido enquanto status carrega
  8. Erro de API exibe mensagem amigável no card

Technical Implementation Notes

WhatsApp Instance Card Component

Criar arquivo /components/WhatsAppInstanceCard.tsx:

'use client'

interface WhatsAppInstanceCardProps {
  instance: string
  status: 'connected' | 'disconnected' | 'error'
  error?: string
  onGenerateQR?: () => void
  onDisconnect?: () => void
}

export default function WhatsAppInstanceCard({
  instance,
  status,
  error,
  onGenerateQR,
  onDisconnect,
}: WhatsAppInstanceCardProps) {
  const statusConfig = {
    connected: {
      badge: 'Connected',
      color: 'bg-green-500',
      textColor: 'text-green-400',
    },
    disconnected: {
      badge: 'Disconnected',
      color: 'bg-red-500',
      textColor: 'text-red-400',
    },
    error: {
      badge: 'Error',
      color: 'bg-yellow-500',
      textColor: 'text-yellow-400',
    },
  }

  const config = statusConfig[status]

  return (
    <div className="bg-gray-800 border border-gray-700 rounded-lg p-4 hover:border-primary-500 transition-colors">
      {/* Header */}
      <div className="flex items-center justify-between mb-4">
        <h3 className="text-lg font-semibold text-white">{instance}</h3>
        <span
          className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${config.color} text-white`}
        >
          {config.badge}
        </span>
      </div>

      {/* Error Message (if any) */}
      {error && (
        <div className="mb-4 p-2 bg-red-900/20 border border-red-800 rounded text-sm text-red-400">
          {error}
        </div>
      )}

      {/* Actions - Will be implemented in next stories */}
      <div className="flex gap-2">
        {status === 'disconnected' && (
          <button
            onClick={onGenerateQR}
            className="flex-1 px-4 py-2 bg-primary-600 hover:bg-primary-700 text-white text-sm font-medium rounded-md transition-colors"
          >
            Gerar QR Code
          </button>
        )}
        {status === 'connected' && (
          <button
            onClick={onDisconnect}
            className="flex-1 px-4 py-2 bg-red-600 hover:bg-red-700 text-white text-sm font-medium rounded-md transition-colors"
          >
            Desconectar
          </button>
        )}
        {status === 'error' && (
          <button
            disabled
            className="flex-1 px-4 py-2 bg-gray-700 text-gray-400 text-sm font-medium rounded-md cursor-not-allowed"
          >
            Indisponível
          </button>
        )}
      </div>
    </div>
  )
}

Update Dashboard Page

Atualizar /app/dashboard/page.tsx:

'use client'

import { useEffect, useState } from 'react'
import { supabase } from '@/lib/supabase'
import { getAllInstancesStatus, type InstanceStatus } from '@/lib/evolutionapi'
import WhatsAppInstanceCard from '@/components/WhatsAppInstanceCard'

export default function DashboardPage() {
  const [userName, setUserName] = useState<string>('Usuário')
  const [instances, setInstances] = useState<InstanceStatus[]>([])
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    const getUser = async () => {
      const { data: { user } } = await supabase.auth.getUser()
      if (user?.email) {
        const name = user.email.split('@')[0]
        setUserName(name)
      }
    }

    const loadInstances = async () => {
      setLoading(true)
      try {
        const data = await getAllInstancesStatus()
        setInstances(data)
      } catch (error) {
        console.error('Error loading instances:', error)
      } finally {
        setLoading(false)
      }
    }

    getUser()
    loadInstances()
  }, [])

  return (
    <div className="space-y-6">
      {/* Welcome Section */}
      <div className="bg-gray-800 rounded-lg p-6 border border-gray-700">
        <h2 className="text-2xl font-bold text-white mb-2">
          Bem-vindo ao Portal AutomatizaSE, {userName}!
        </h2>
        <p className="text-gray-400">
          Gerencie suas integrações de WhatsApp e Google Calendar.
        </p>
      </div>

      {/* WhatsApp Instances Section */}
      <div>
        <h3 className="text-xl font-semibold text-white mb-4">Instâncias WhatsApp</h3>

        {loading ? (
          <div className="text-center py-8 text-gray-400">
            Carregando instâncias...
          </div>
        ) : instances.length === 0 ? (
          <div className="text-center py-8 text-gray-400">
            Nenhuma instância configurada
          </div>
        ) : (
          <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
            {instances.map((instance) => (
              <WhatsAppInstanceCard
                key={instance.instance}
                instance={instance.instance}
                status={instance.status}
                error={instance.error}
                onGenerateQR={() => {
                  // Will be implemented in Story 2.3
                  console.log('Generate QR for:', instance.instance)
                }}
                onDisconnect={() => {
                  // Will be implemented in Story 2.4
                  console.log('Disconnect:', instance.instance)
                }}
              />
            ))}
          </div>
        )}
      </div>

      {/* Google Calendar Section - Placeholder */}
      <div className="bg-gray-800 rounded-lg p-6 border border-gray-700">
        <h3 className="text-lg font-semibold text-white mb-2">
          Google Calendar
        </h3>
        <p className="text-gray-400 text-sm">
          Em breve: Autorize integração com Google Calendar
        </p>
      </div>
    </div>
  )
}

Testing Checklist

  • Componente WhatsAppInstanceCard criado
  • Cards renderizam corretamente no dashboard
  • Status badge exibe cores corretas (verde/vermelho/amarelo)
  • Layout responsivo funciona (grid em desktop, stack em mobile)
  • Loading state exibido durante carregamento
  • Erro de API exibe mensagem no card
  • Botões "Gerar QR Code" e "Desconectar" aparecem conforme status
  • Cards seguem design similar à EvolutionAPI

Dependencies

  • Blocks: Story 2.3, 2.4 (botões serão implementados nestas stories)
  • Blocked By: Story 2.1 (precisa de integração com EvolutionAPI)

Notes

  • Botões são placeholders nesta story - funcionalidade completa nas próximas stories
  • Design inspirado na interface da EvolutionAPI fornecida pelo usuário

Dev Agent Record

Agent Model Used

  • claude-sonnet-4-5-20250929

Completion Notes

  • Componente WhatsAppInstanceCard.tsx criado em /components com suporte a 3 estados (connected, disconnected, error)
  • Dashboard page atualizado para carregar e exibir cards de instâncias WhatsApp
  • Implementado loading state e tratamento de erros
  • Layout responsivo com grid (1 col mobile, 2 cols tablet, 3 cols desktop)
  • Botões com type="button" para acessibilidade
  • Re-export de InstanceStatus type adicionado em lib/evolutionapi.ts para facilitar imports
  • Código passa em validações TypeScript e Biome linter (sem erros no componente criado)
  • Nota: QRCodeModal e funcionalidade de geração de QR já foram implementados (possivelmente por Story 2.3), mas mantidos para compatibilidade

File List

Created:

  • /components/WhatsAppInstanceCard.tsx - Componente de card de instância WhatsApp

Modified:

  • /app/dashboard/page.tsx - Adicionado carregamento e exibição de cards de instâncias
  • /lib/evolutionapi.ts - Adicionado re-export de InstanceStatus type

Change Log

  • 2025-10-05: Implementação completa da Story 2.2
    • Criado componente WhatsAppInstanceCard com badges de status coloridos
    • Atualizado dashboard para exibir grid responsivo de cards
    • Adicionado loading state e mensagens de erro
    • Corrigido export de tipos TypeScript
    • Aplicadas correções de acessibilidade (button type)