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
├── Onboarding/                        → Wizard de configuracao inicial
├── Help/                              → Ajuda in-app
├── Subscription/                      → Minha Assinatura (Admin)
└── Admin/                             → Tenants e uso de LLM

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 / Editar (dialogs)

Criacao e edicao de agentes sao feitas via dialogs, sem paginas dedicadas:

  • AgentCreateDialog.razor — Dialog para criacao de agente (nome, descricao, status)
  • AgentEditDialog.razor — Dialog para edicao de agente (mesmos campos, preenchidos com dados existentes)

Ambos usam MudModalForm e sao abertos via DialogHelper a partir da lista ou detalhe.

Detalhe (/agents/{id})

Arquivo: Pages/Agents/AgentDetail.razor

Layout com abas:

Aba Componente Descrição
Geral AgentGeneralTab Informações básicas
Personalidade e Regras AgentPromptTab Edição do system prompt
Roteiro de Atendimento AgentStepsTab Etapas do fluxo de atendimento com drag-drop para reordenação
Conhecimento AgentKnowledgeTab Knowledge blocks com drag-drop
FAQ AgentFaqTab Perguntas frequentes com drag-drop
Anexos AgentAttachmentsTab Arquivos anexados ao agente (upload, toggle, exclusão)
Follow Up AgentFollowUpTab Configuração de follow-up automático do agente

Header: DetailPageHeader com estatísticas (StatisticCard)

Ações no Header: - Playground — botão primário com texto e ícone (PrimaryAction) - Editar — botão de ícone direto (OnEdit) - Arquivar — dentro do menu de 3 pontos (OnArchive)

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, typing, delay, marca de leitura, assinatura (texto/posição/preview), mídia (análise de imagem/áudio), janela de reabertura (ReopenWindowMinutes), máximo de respostas IA por conversa, horários de disponibilidade da IA (ScheduleGrid)
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

Assinatura (/subscription)

Arquivo: Pages/Subscription/SubscriptionDetails.razor + .razor.cs

Visibilidade: Admin

Pagina dedicada para visualizacao do status da assinatura, creditos e uso de recursos.

Layout

[Header: "Minha Assinatura" + subtitulo]
[StatusCard — chip de status + periodo de vigencia + dias restantes]
[CreditosCard — saldo / franquia mensal + barra de progresso]
[Grid de ResourceCards]
  - Conexoes WhatsApp (instances)
  - Agentes
  - Usuarios
  - Base de Conhecimento
  - Etapas por Agente (maior count entre agentes)
  - FAQs (total do tenant)
  - Armazenamento (MB)

Componentes MudBlazor

  • MudChip para status (cores consistentes com TenantList)
  • MudProgressLinear para barras de uso (verde < 80%, amarelo >= 80%, vermelho >= 100%)
  • MudGrid / MudItem responsivo (xs=12, sm=6, md=4)
  • MudPaper com border padrao do projeto

Dados

  • Usa SubscriptionStateService.GetDetailsAsync() (cache in-memory, TTL 5 min)
  • Atualizações real-time de créditos via SignalR → SubscriptionStateService.OnChanged
  • Lógica de exibição delegada para SubscriptionDisplayHelper (métodos estáticos)
  • Loading state com MudProgressLinear indeterminate
  • Fallback: alert se nenhuma assinatura encontrada

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.

Tabela: Nome, Tenant (status), Assinatura (chip), Criado em, Acoes

Dialogs de Assinatura (SuperAdmin):

Dialog Arquivo Endpoint
SubscriptionDetailsDialog Pages/Admin/Components/SubscriptionDetailsDialog.razor GET /api/tenants/{tenantId}/subscription-details
SubscriptionHistoryDialog Pages/Admin/Components/SubscriptionHistoryDialog.razor GET /api/tenants/{tenantId}/subscription-history

O SubscriptionDetailsDialog exibe os mesmos dados da pagina /subscription (status, periodo, creditos, 7 cards de recursos) para o tenant selecionado. Mesmo DTO SubscriptionDetailsResponse, endpoint separado com auth SuperAdmin.

LLM Usage (/admin/llm-usage)

Arquivo: Pages/Admin/LlmUsage/Index.razor

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

Abas:

Aba Descrição
Visao Geral Dashboard principal com summary cards, tabelas por modelo/operação/tenant
Margem Dados de margem por tenant (custo LLM vs receita de créditos) - endpoint GET /api/admin/llm-usage/margin

Componentes:

Componente Descrição
LlmUsageSummaryCards Cards com totais (tokens, custo, requests, cache hit requests, cache read/write tokens)
LlmUsageFilterBar Filtros por período, provider, modelo
LlmUsageByModelTable Tabela de uso agrupado por modelo
LlmUsageByOperationTable Tabela de uso agrupado por tipo de operação + modelo
LlmUsageByTenantTable Tabela de uso agrupado por tenant (SuperAdmin)
MarginByTenantTable Tabela de margem por tenant (custo vs receita)

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 - Métricas de cache: cache hit requests, total cache read/write tokens - Aba "Margem" com dados de custo vs receita por tenant


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 Conexoes WhatsApp InstancesSection.razor Admin
3 Conversas ConversationsSection.razor Todos
4 Dashboard DashboardSection.razor Todos
5 Follow-Up FollowUpSection.razor Admin
6 Assinatura e Creditos SubscriptionSection.razor Todos

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
Conexoes
Agentes
Usuarios (Admin+)
Conversas
Ajuda
───────────
Minha Conta
Assinatura (Admin+)
───────────
Administracao (SuperAdmin)
  Tenants
  Uso de LLM

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