Pular para conteúdo

Frontend: Páginas

Visão Geral

Páginas do portal em Ciba.Web/Pages/. Organizadas por recurso.

Estrutura:

Pages/
├── Dashboard.razor                    → Pagina inicial com checklist/welcome
├── Login.razor                        → Autenticacao
├── Profile.razor                      → Minha conta (alterar senha)
├── Agents/                            → Gestao de agentes
├── Instances/                         → Instancias WhatsApp
├── Conversations/                     → Chat e conversas
├── AiModels/                          → Modelos de IA
├── Onboarding/                        → Wizard de configuracao inicial
├── Help/                              → Ajuda in-app
└── Admin/                             → Tenants e configuracoes

Padrão de organização: - Página: {Nome}.razor (markup) - Code-behind: {Nome}.razor.cs (lógica) - Componentes locais: Components/ dentro da pasta do recurso


Dashboard (/)

Arquivo: Pages/Dashboard.razor + Pages/Dashboard.razor.cs

Dashboard com relatórios visuais, KPIs e listagem de instâncias/agentes.

Layout

[SetupChecklist (admin, onboarding incompleto) OU WelcomeCard (usuario comum)]
[DashboardDateFilter — filtro de período + agente]
[KpiCards — 6 cards com métricas principais]
[ConversationsOverTimeChart — gráfico de área full width]
[StepFunnelChart | PeakHoursHeatmap — 50/50 grid]
[FollowUpEffectivenessCard — 4 cards full width]
--- divider ---
[Instâncias WhatsApp — cards com status]
[Agentes — cards com métricas do dia]

Componentes de Dashboard (Components/Dashboard/)

Componente Tipo Descrição
SetupChecklist MudBlazor Checklist de configuracao inicial (admin) com 3 criterios
WelcomeCard MudBlazor Card de boas-vindas para usuarios comuns com dismiss
DashboardDateFilter MudBlazor DateRangePicker + botões rápidos (7d/30d/90d) + seletor de agente
KpiCards MudBlazor 6 MudCards: Total, Ativas, IA, Escaladas, Tempo Médio, Taxa IA
ConversationsOverTimeChart ApexCharts Gráfico de área com 3 séries (Total, IA, Escalado)
StepFunnelChart ApexCharts Gráfico de barras horizontal (funil de steps)
PeakHoursHeatmap ApexCharts Heatmap 7 dias × 24 horas
FollowUpEffectivenessCard MudBlazor 4 cards com métricas de follow-up

Filtros

  • Período: DateRangePicker com botões rápidos (7 dias, 30 dias, 90 dias)
  • Agente: Select opcional para filtrar por agente específico
  • Default: últimos 30 dias, todos os agentes

Carregamento

  • Instâncias e agentes carregados no OnInitializedAsync
  • 5 endpoints de relatório carregados em paralelo via Task.WhenAll quando filtro muda
  • DashboardDateFilter dispara OnFilterChanged automaticamente no OnInitializedAsync

Chart Library

  • Blazor-ApexCharts v3.* — @using ApexCharts adicionado por componente (não global, conflita com MudBlazor nos tipos Size, Color, Mode)
  • Scripts JS carregados em wwwroot/index.html

Dados Exibidos

KPIs: - Total de conversas no período - Conversas ativas - Resolvidas por IA - Escaladas para humano - Tempo médio de resolução (horas) - Taxa de resolução por IA (%)

Por Instância: - Nome - Status de conexão (Connected, Disconnected, etc.) - Agente vinculado - Conversas do dia

Por Agente: - Nome - Status (Ativo/Inativo) - Conversas do dia


Login (/login)

Arquivo: Pages/Login.razor

Página de autenticação com layout próprio (EmptyLayout).

Estrutura

  • Lado esquerdo: Branding CIBA + lista de features
  • Lado direito: Form de login

Form

<FluentForm TModel="LoginRequest" Model="_model" OnValidSubmit="HandleLogin">
    <MudTextField @bind-Value="_model.Email" Label="Email" />
    <MudTextField @bind-Value="_model.Password" InputType="InputType.Password" />
    <FormButtonGroup SaveText="Entrar" HideCancel="true" />
</FluentForm>

Comportamento

  1. Valida credenciais via AuthService
  2. Armazena JWT no LocalStorage
  3. Se mustChangePassword=true, redireciona para /profile
  4. Caso contrário, redireciona para Dashboard

Minha Conta (/profile)

Arquivo: Pages/Profile.razor

Página para alteração de senha do próprio usuário.

Campos: - Senha atual - Nova senha (mínimo 8 caracteres, 1 maiúscula, 1 minúscula, 1 número)

Comportamento: - Se mustChangePassword=true, exibe alerta e exige alteração - Após sucesso, limpa a flag e redireciona para /


Agentes

Lista (/agents)

Arquivo: Pages/Agents/AgentList.razor

Coluna Descrição
Nome Link para detalhe
Status StatusChip (Ativo/Inativo)
Conversas Hoje Contador

Componentes: StateView, EmptyState, StatusChip

Criar (/agents/create)

Arquivo: Pages/Agents/AgentCreate.razor

Form com campos: - Nome (obrigatório) - Descrição - System Prompt (textarea) - Status (toggle)

Editar (/agents/{id}/edit)

Arquivo: Pages/Agents/AgentEdit.razor

Mesmo form do criar, preenchido com dados existentes.

Detalhe (/agents/{id})

Arquivo: Pages/Agents/AgentDetail.razor

Layout com abas:

Aba Componente Descrição
Geral AgentGeneralTab Informações básicas
Prompt AgentPromptTab Edição do system prompt
Conhecimento AgentKnowledgeTab Knowledge blocks com drag-drop
FAQ AgentFaqTab Perguntas frequentes com drag-drop
Comportamento IA AgentAiSettingsTab Tom, idioma e emojis

Header: DetailPageHeader com estatísticas (StatisticCard)

Ações no Header: - Playground (abre AgentPlaygroundDialog via DialogHelper) - Editar - Arquivar

Playground (AgentPlaygroundTab / AgentPlaygroundDialog)

Ambiente de teste para conversar diretamente com o agente. Usa os mesmos componentes visuais do Chat (Components/Chat/) com Mode=ChatMode.Playground.

Funcionalidades: - Envio de texto, imagens (Claude Vision) e áudio (Whisper transcrição) - Múltiplas conversas com persistência em localStorage - Exportação de chat em JSON - Contador de tokens (input/output)

Armazenamento: localStorage via PlaygroundStorageService (sem persistência em banco)

Dialog: AgentPlaygroundDialog encapsula AgentPlaygroundTab em um MudDialog


Instâncias WhatsApp

Lista (/instances)

Arquivo: Pages/Instances/InstanceList.razor

Coluna Descrição
Nome Link para detalhe
Status Chip colorido
Agente Link ou "Não vinculado"

Criar (dialog)

Arquivo: Pages/Instances/Components/InstanceCreateDialog.razor

Dialog com campos: - Nome (obrigatório) - Agente (select opcional)

Detalhe (/instances/{id})

Arquivo: Pages/Instances/InstanceDetail.razor

Abas:

Aba Componente Descrição
Geral InstanceGeneralTab Info da instância
Configurações InstanceConfigTab Agente, modelo, assinatura, typing, delay
Whitelist InstanceWhitelistTab Números permitidos
Blacklist InstanceBlacklistTab Números bloqueados

Ações no Header: - Conectar/Desconectar WhatsApp (abre QrCodeDialog) - Arquivar

Diálogos

  • InstanceCreateDialog: Criação de instância
  • QrCodeDialog: Exibe QR code para conexão
  • InstanceWhitelistEntryDialog: Adicionar/editar whitelist
  • InstanceBlacklistEntryDialog: Adicionar/editar blacklist

Conversas

Layout (/conversations)

Arquivo: Pages/Conversations/ConversationsLayout.razor

Layout split-pane responsivo: - Desktop: Sidebar (350px) + Chat - Mobile: Uma view por vez

Rotas suportadas: - /conversations — Lista de conversas - /conversations/{conversationId} — Conversa específica - /conversations/instance/{instanceId} — Filtro por instância

Arquivo: Pages/Conversations/ConversationSidebar.razor

  • Filtro por instância
  • Busca por nome/telefone
  • Lista de conversas com:
  • Avatar do contato
  • Nome/telefone
  • Preview da última mensagem
  • Badge de não lidas
  • Timestamp

Chat

Arquivo: Pages/Conversations/ConversationChat.razor

Componentes locais: - ChatHeader — Info do contato + ações

Componentes compartilhados (Components/Chat/): - MessageBubble — Bolha de mensagem (com Mode=ChatMode.Conversation) - MessageInput — Campo de envio com texto, anexos e gravação - AudioPlayer — Player de áudio - DocumentPreview — Preview de documento

Funcionalidades: - Scroll infinito (paginação) - Real-time via SignalR - Envio de texto e mídia - Reações com emoji - Status de mensagem (sent, delivered, read)

Diálogos

  • StartConversationDialog: Iniciar nova conversa
  • ResolveConversationDialog: Resolver/fechar conversa
  • MediaLightboxDialog: Visualizar mídia em tela cheia

Modelos de IA

Lista (/ai-models)

Arquivo: Pages/AiModels/AiModelList.razor

Coluna Descrição
Nome Nome do modelo
Provider OpenAI, Anthropic, etc.
Model ID ID da API

Criar (/ai-models/create)

Arquivo: Pages/AiModels/AiModelCreate.razor

Editar (/ai-models/{id}/edit)

Arquivo: Pages/AiModels/AiModelEdit.razor


Admin (SuperAdmin)

Usuarios (/admin/users)

Arquivo: Pages/Admin/UsersList.razor

Gerenciamento de usuários (Admin e SuperAdmin).

Tabela: - Nome - Email - Role - Status - Último acesso - Tenant (apenas SuperAdmin)

Ações: - Criar usuário - Editar usuário - Resetar senha - Ativar/Desativar

Tenants

Lista: /admin/tenants Criar: /admin/tenants/create Editar: /admin/tenants/{id}/edit

Gerenciamento de tenants para multi-tenancy.

LLM Usage (/admin/llm-usage)

Arquivo: Pages/Admin/LlmUsage/Index.razor

Dashboard de monitoramento de uso de LLM (tokens e custos).

Componentes:

Componente Descrição
LlmUsageSummaryCards Cards com totais (tokens, custo, requests)
LlmUsageFilterBar Filtros por período, provider, modelo
LlmUsageByModelTable Tabela de uso agrupado por modelo
LlmUsageByOperationTable Tabela de uso agrupado por tipo de operação
LlmUsageByTenantTable Tabela de uso agrupado por tenant (SuperAdmin)

Funcionalidades: - Filtro por período (data inicial/final) - Filtro por provider (Anthropic, OpenAI) - Filtro por modelo específico - Visualização de custo estimado em USD - Agregação por modelo, operação e tenant

System Settings (/admin/settings)

Arquivo: Pages/Admin/SystemSettings.razor

Configurações globais do sistema: - Mensagem de erro padrão - Mensagem de boas-vindas padrão - Mensagem de rate limit - Mensagem fora do horário


Onboarding (/onboarding)

Arquivo: Pages/Onboarding/OnboardingWizard.razor + .razor.cs

Wizard de configuracao inicial para administradores na primeira utilizacao. Usa EmptyLayout (sem menu lateral).

Layout

[MudContainer MaxWidth.Small, centralizado, altura total do viewport]
    [MudPaper com elevacao]
        Indicador de passo: "Passo X de 3"
        Conteudo do passo ativo
        Botoes de navegacao (Anterior / Proximo / Pular)

Passos

Passo Indice Descricao
Boas-vindas 0 Apresentacao do sistema com lista de features e botoes "Comecar" / "Pular"
Criar Agente 1 Formulario com nome e system prompt do agente
Criar Instancia 2 Formulario com nome da instancia (pre-vinculada ao agente criado)
QR Code 3 Exibicao do QR code com polling automatico de status (5s)
Sucesso 4 Tela de conclusao com links rapidos para agente e dashboard

Comportamento

  • Pular: Chama POST /api/onboarding/complete e redireciona para Dashboard
  • Finalizar: Mesmo fluxo do pular, apos completar todos os passos
  • Polling QR: Timer a cada 5s verifica status de conexao; auto-avanca quando conectado
  • Disposal: Timer limpo via IDisposable
  • Erros: Snackbar.ShowIfError(result) para feedback de erros

Dependencias

  • ApiClient (criar agente, instancia, conectar, status)
  • NavigationManager (redirecionamentos)
  • ISnackbar (feedback)

Help (/help)

Arquivo: Pages/Help/HelpIndex.razor + .razor.cs

Pagina de ajuda in-app com documentacao das funcionalidades do sistema.

Layout

[MudGrid]
    [Sidebar xs=12 md=3]
        MudNavMenu com links para secoes
    [Conteudo xs=12 md=9]
        Secao ativa renderizada via switch

Secoes

# Secao Componente Visibilidade
0 Primeiros Passos GettingStartedSection.razor Todos
1 Agentes AgentsSection.razor Admin
2 Canais WhatsApp InstancesSection.razor Admin
3 Conversas ConversationsSection.razor Todos
4 Dashboard DashboardSection.razor Todos
5 Follow-Up FollowUpSection.razor Admin

Comportamento

  • Navegacao client-side (sem mudanca de rota, troca de secao via estado)
  • Filtro por role: secoes admin-only ocultadas para usuarios comuns
  • Secoes sao componentes estaticos (sem chamadas a API)
  • Todo conteudo em portugues

Secoes em Pages/Help/Sections/

Cada secao e um componente Razor puro (sem code-behind) envolto em MudPaper:

Componente Conteudo
GettingStartedSection O que e Ciba, 3 cards conceituais (Agente, Instancia, Conversa), timeline de setup
AgentsSection Guia de criacao, abas detalhadas (MudExpansionPanels), boas praticas de prompt
InstancesSection Timeline de criacao, tabela de configuracoes, whitelist/blacklist
ConversationsSection Tabela de status, modos IA/Manual, acoes disponiveis
DashboardSection Filtros, metricas (KPIs, graficos, heatmap, follow-up)
FollowUpSection Timeline de fluxo, tabela de configuracoes, dicas

Padroes de Pagina

Página de Lista

@page "/recurso"

<MudText Typo="Typo.h4">Título</MudText>

<MudButton Href="/recurso/create">Novo</MudButton>

<StateView TItem="List<ItemResponse>" Result="_result" Loading="_loading" OnRetry="Load">
    <EmptyContent>
        <EmptyState Message="Nenhum item" ActionText="Criar" OnAction="Create" />
    </EmptyContent>
    <ChildContent Context="items">
        <MudTable Items="items">
            @* colunas *@
        </MudTable>
    </ChildContent>
</StateView>

Página de Detalhe

@page "/recurso/{Id:guid}"

<StateView TItem="ItemResponse" Result="_result" Loading="_loading" OnRetry="Load">
    <ChildContent Context="item">
        <DetailPageHeader Title="@item.Name" EditHref="..." OnDelete="Delete" />

        <MudTabs>
            @* abas de conteúdo *@
        </MudTabs>
    </ChildContent>
</StateView>

Página de Form (Create/Edit)

@page "/recurso/create"

<MudText Typo="Typo.h4">Novo Item</MudText>

<FluentForm TModel="CreateRequest" Model="_model" OnValidSubmit="Save">
    <MudTextField @bind-Value="_model.Name" Label="Nome" />
    @* campos *@
    <FormButtonGroup CancelHref="/recurso" SaveText="Criar" />
</FluentForm>

Code-Behind Padrão

public partial class RecursoList
{
    [Inject] private ApiClient ApiClient { get; set; } = null!;
    [Inject] private ISnackbar Snackbar { get; set; } = null!;
    [Inject] private NavigationManager Navigation { get; set; } = null!;

    private bool _loading = true;
    private Result<List<ItemResponse>>? _result;

    protected override async Task OnInitializedAsync() => await Load();

    private async Task Load()
    {
        _loading = true;
        _result = await ApiClient.GetItemsAsync();
        _loading = false;
    }

    private async Task Delete(ItemResponse item)
    {
        var result = await ApiClient.DeleteItemAsync(item.Id);
        if (Snackbar.ShowIfError(result)) return;

        Snackbar.Add("Item excluído", Severity.Success);
        await Load();
    }
}

Menu lateral (NavMenu):

Dashboard
Canais
Agentes
Usuarios (Admin+)
Conversas
Ajuda
Minha Conta
───────────
Tenants (SuperAdmin)
Uso de LLM (SuperAdmin)
Configuracoes (SuperAdmin)

Breadcrumbs: Implícitos via estrutura de URLs


Responsividade

  • Mobile (<960px): Layout single-column
  • Tablet (960-1264px): Sidebar reduzida
  • Desktop (>1264px): Layout completo

Conversas tem comportamento especial: - Mobile: alterna entre lista e chat - Desktop: split-pane permanente