Frontend: Componentes Globais¶
Visão Geral¶
Componentes reutilizáveis em Ciba.Web/Components/. São utilizados em 2+ contextos diferentes no portal.
Padrão de organização:
- Componente: {Nome}.razor (markup)
- Code-behind: {Nome}.razor.cs (lógica)
- Estilos isolados: {Nome}.razor.css (quando necessário)
StateView\<TItem>¶
Gerencia estados de carregamento, erro, vazio e conteúdo.
Arquivo: Components/StateView.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
Loading |
bool |
Exibe indicador de carregamento |
Result |
Result<TItem>? |
Resultado da operação (sucesso ou erro) |
ChildContent |
RenderFragment<TItem> |
Conteúdo quando sucesso |
EmptyContent |
RenderFragment? |
Conteúdo quando lista vazia |
OnRetry |
EventCallback |
Callback para tentar novamente |
Estados¶
- Loading →
MudProgressLinearindeterminado - Error →
MudAlertcom mensagem + botão retry - Empty →
EmptyContentou mensagem padrão - Success →
ChildContentcom dados
Uso¶
<StateView TItem="List<AgentListResponse>"
Result="_result"
Loading="_loading"
OnRetry="LoadData">
<EmptyContent>
<EmptyState Icon="@Icons.Material.Outlined.SmartToy"
Message="Nenhum agente cadastrado"
ActionText="Criar agente"
OnAction="Create" />
</EmptyContent>
<ChildContent Context="agents">
<MudTable Items="agents">
@* colunas da tabela *@
</MudTable>
</ChildContent>
</StateView>
Code-behind¶
public partial class StateView<TItem> where TItem : class
{
private bool IsEmpty
{
get
{
if (Result?.Value is null) return true;
if (Result.Value is ICollection collection) return collection.Count == 0;
return false;
}
}
}
EmptyState¶
Visual para listas vazias com ação opcional.
Arquivo: Components/EmptyState.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
Icon |
string |
Ícone MudBlazor |
Message |
string |
Mensagem para usuário |
ActionText |
string? |
Texto do botão de ação |
OnAction |
EventCallback |
Callback do botão |
Uso¶
<EmptyState Icon="@Icons.Material.Outlined.Inbox"
Message="Nenhuma conversa encontrada"
ActionText="Iniciar conversa"
OnAction="OpenNewConversation" />
StatusChip¶
Chip colorido para status ativo/inativo.
Arquivo: Components/StatusChip.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
IsActive |
bool |
Se está ativo |
Uso¶
Renderização¶
- Ativo: Chip verde com "Ativo"
- Inativo: Chip cinza com "Inativo"
DetailPageHeader¶
Cabeçalho padrão para páginas de detalhe.
Arquivo: Components/DetailPageHeader.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
Title |
string |
Título da página |
IsActive |
bool |
Status para StatusChip |
ShowStatus |
bool |
Exibir StatusChip |
EditHref |
string? |
Link para edição (navegação) |
OnEdit |
EventCallback |
Callback para edição (dialog) |
OnDelete |
EventCallback |
Callback de exclusão |
Uso (com navegação)¶
<DetailPageHeader Title="@_agent.Name"
IsActive="_agent.IsActive"
ShowStatus="true"
EditHref="@($"/agents/{Id}/edit")"
OnDelete="ConfirmDelete" />
Uso (com dialog)¶
<DetailPageHeader Title="@_agent.Name"
IsActive="_agent.IsActive"
ShowStatus="true"
OnEdit="OpenEditDialog"
OnDelete="ConfirmDelete" />
Comportamento¶
- Se
OnEdittem delegate → botão abre dialog - Se
EditHrefdefinido → botão navega para página OnEdittem prioridade sobreEditHref
Renderização¶
TableActionButtons¶
Botões de ação para tabelas (editar/excluir).
Arquivo: Components/TableActionButtons.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
OnEdit |
EventCallback |
Callback de edição |
OnDelete |
EventCallback |
Callback de exclusão |
Uso¶
<TemplateColumn>
<CellTemplate>
<TableActionButtons OnEdit="@(() => Edit(context.Item))"
OnDelete="@(() => Delete(context.Item))" />
</CellTemplate>
</TemplateColumn>
StatisticCard¶
Card para exibir métricas.
Arquivo: Components/StatisticCard.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
Value |
object |
Valor da métrica |
Label |
string |
Descrição |
Color |
Color |
Cor MudBlazor |
Uso¶
<MudGrid>
<MudItem xs="6" md="3">
<StatisticCard Value="@_totalConversations" Label="Total de Conversas" Color="Color.Primary" />
</MudItem>
<MudItem xs="6" md="3">
<StatisticCard Value="@_todayConversations" Label="Conversas Hoje" Color="Color.Secondary" />
</MudItem>
</MudGrid>
FluentForm\<TModel>¶
Wrapper de formulário com validação FluentValidation integrada.
Arquivo: Components/FluentForm.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
Model |
TModel |
Modelo do formulário |
ChildContent |
RenderFragment |
Conteúdo do form |
OnValidSubmit |
EventCallback |
Submit quando válido |
OnInvalidSubmit |
EventCallback |
Submit quando inválido |
Propriedades¶
| Propriedade | Tipo | Descrição |
|---|---|---|
IsValid |
bool |
Se o form está válido |
IsSubmitting |
bool |
Se está submetendo |
Funcionamento¶
FluentForminjetaIValidator<TModel>do DI- Cada campo é validado por propriedade
FormButtonGroupdetecta o form viaCascadingParameter- No submit, valida todo o model antes de chamar
OnValidSubmit
Uso¶
<FluentForm TModel="CreateAgentRequest" Model="_request" OnValidSubmit="Save">
<MudTextField @bind-Value="_request.Name" Label="Nome" />
<MudTextField @bind-Value="_request.SystemPrompt" Label="Prompt" Lines="5" />
<FormButtonGroup CancelHref="/agents" SaveText="Criar" />
</FluentForm>
@code {
private CreateAgentRequest _request = new();
private async Task Save()
{
// Só é chamado se o form é válido
var result = await ApiClient.CreateAgentAsync(_request);
// ...
}
}
Validação por Campo¶
O form valida cada propriedade individualmente usando FluentValidation:
private async Task<IEnumerable<string>> ValidatePropertyAsync(object _, string propertyName)
{
var context = ValidationContext<TModel>.CreateWithOptions(
Model,
options => options.IncludeProperties(propertyName));
var result = await Validator.ValidateAsync(context);
return result.Errors
.Where(e => e.PropertyName == propertyName)
.Select(e => e.ErrorMessage);
}
MudModalForm\<TModel>¶
Dialog de formulário reutilizável com FluentForm integrado. Padroniza criação/edição de entidades em dialogs.
Arquivo: Components/MudModalForm.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
Model |
TModel |
Modelo do formulário |
Title |
string |
Título do dialog |
OnSubmit |
EventCallback |
Callback quando form é válido e submetido |
ChildContent |
RenderFragment |
Campos do formulário |
SaveText |
string |
Texto do botão (default: "Salvar") |
SaveColor |
Color |
Cor do botão (default: Color.Primary) |
Estrutura¶
O componente encapsula: - FluentForm com validação automática - MudDialog com TitleContent, DialogContent, DialogActions - Botão X no título para fechar - Botões Cancelar/Salvar com loading automático
Comportamento¶
- Botão X e Cancelar são desabilitados durante submit (
IsSubmitting) - Botão Salvar exibe spinner durante submit
- Validação via FluentValidation (busca
IValidator<TModel>no DI) - Fecha dialog automaticamente ao cancelar
Uso (Dialog de criação)¶
@* AgentCreateDialog.razor *@
@using Ciba.Shared.Features.Agents.Create
@using Ciba.Web.Components
<MudModalForm TModel="CreateAgentRequest"
Model="_model"
Title="Novo Agente"
OnSubmit="Save">
<MudTextField @bind-Value="_model.Name"
For="@(() => _model.Name)"
Label="Nome"
Variant="Variant.Outlined" />
<MudTextField @bind-Value="_model.Description"
For="@(() => _model.Description)"
Label="Descricao"
Variant="Variant.Outlined"
Lines="2" />
<MudSwitch @bind-Value="_model.IsActive"
Label="Ativo"
Color="Color.Primary" />
</MudModalForm>
// AgentCreateDialog.razor.cs
public partial class AgentCreateDialog
{
[Inject] private ApiClient Api { get; set; } = null!;
[Inject] private ISnackbar Snackbar { get; set; } = null!;
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
private readonly CreateAgentRequest _model = new();
private async Task Save()
{
var result = await Api.CreateAgentAsync(_model);
if (Snackbar.ShowIfError(result)) return;
Snackbar.Add("Agente criado com sucesso!", Severity.Success);
MudDialog.Close(DialogResult.Ok(result.Value!.Id));
}
}
Uso (Dialog de edição)¶
@* AgentEditDialog.razor *@
<MudModalForm TModel="UpdateAgentRequest"
Model="_model"
Title="Editar Agente"
OnSubmit="Save">
@* campos *@
</MudModalForm>
// AgentEditDialog.razor.cs
public partial class AgentEditDialog
{
[Parameter, EditorRequired] public AgentDetailResponse Agent { get; set; } = null!;
private readonly UpdateAgentRequest _model = new();
protected override void OnInitialized()
{
_model.Id = Agent.Id;
_model.Name = Agent.Name;
_model.Description = Agent.Description;
_model.IsActive = Agent.IsActive;
}
private async Task Save()
{
var result = await Api.UpdateAgentAsync(Agent.Id, _model);
if (Snackbar.ShowIfError(result)) return;
Snackbar.Add("Agente atualizado!", Severity.Success);
MudDialog.Close(DialogResult.Ok(true));
}
}
Abrindo o Dialog¶
// Na página/componente que abre o dialog
private async Task OpenCreateDialog()
{
var options = new DialogOptions
{
MaxWidth = MaxWidth.Small,
FullWidth = true,
BackdropClick = false,
CloseOnEscapeKey = false
};
var dialog = await DialogService.ShowAsync<AgentCreateDialog>(null, options);
var result = await dialog.Result;
if (!result!.Canceled)
{
await LoadData();
// Opcional: navegar para detalhe
// Navigation.NavigateTo($"/agents/{result.Data}");
}
}
private async Task OpenEditDialog(AgentDetailResponse agent)
{
var options = new DialogOptions
{
MaxWidth = MaxWidth.Small,
FullWidth = true,
BackdropClick = false,
CloseOnEscapeKey = false
};
var parameters = new DialogParameters<AgentEditDialog>
{
{ x => x.Agent, agent }
};
var dialog = await DialogService.ShowAsync<AgentEditDialog>(null, parameters, options);
var result = await dialog.Result;
if (!result!.Canceled)
{
await LoadData();
}
}
Vantagens sobre FluentForm em página¶
| Aspecto | Página | MudModalForm |
|---|---|---|
| Navegação | Navega para outra rota | Permanece na mesma página |
| Feedback | Snackbar após navegação | Snackbar imediato + reload de dados |
| UX | Mais passos (ir, editar, voltar) | Menos cliques |
| Contexto | Perde contexto da lista | Mantém contexto da lista |
FormButtonGroup¶
Botões padrão de formulário (Cancelar + Salvar).
Arquivo: Components/FormButtonGroup.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
CancelHref |
string? |
Link de cancelamento |
OnCancel |
EventCallback |
Callback de cancelamento |
OnSave |
EventCallback |
Callback de salvar (fora de FluentForm) |
SaveText |
string |
Texto do botão (default: "Salvar") |
Saving |
bool |
Loading manual (fora de FluentForm) |
IsValid |
bool |
Habilitar botão (fora de FluentForm) |
FullWidth |
bool |
Botão largura total |
Icon |
string? |
Ícone no botão |
LoadingText |
string? |
Texto durante loading |
HideCancel |
bool |
Ocultar botão cancelar |
Comportamento¶
- Dentro de FluentForm: Detecta form via
CascadingParametere chamaForm.SubmitAsync() - Fora de FluentForm: Usa
OnSavediretamente e controle manual viaSaving/IsValid
Uso (dentro de FluentForm)¶
<FluentForm TModel="CreateAgentRequest" Model="_request" OnValidSubmit="Save">
@* campos *@
<FormButtonGroup CancelHref="/agents" SaveText="Criar Agente" />
</FluentForm>
Uso (fora de FluentForm)¶
<FormButtonGroup CancelHref="/agents"
SaveText="Salvar"
Saving="_saving"
IsValid="_isFormValid"
OnSave="Save" />
ConnectionStatusIndicator¶
Indicador visual de status de conexão WhatsApp.
Arquivo: Components/ConnectionStatusIndicator.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
Status |
string |
Status da conexão |
Estados¶
| Status | Visual | Tooltip |
|---|---|---|
Connected |
Círculo verde | "Conectado" |
Connecting |
Círculo amarelo pulsante | "Conectando..." |
Disconnected |
Círculo cinza | "Desconectado" |
Failed |
Círculo vermelho | "Falha na conexão" |
Uso¶
ContactAvatar¶
Avatar de contato com fallback para inicial.
Arquivo: Components/ContactAvatar.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
ImageUrl |
string? |
URL da foto de perfil |
Name |
string? |
Nome do contato |
Phone |
string? |
Telefone (fallback para inicial) |
Size |
Size |
Tamanho MudBlazor |
Comportamento¶
- Se
ImageUrlválida → exibe imagem - Se imagem falha (
onerror) → exibe inicial - Se sem URL → exibe inicial baseada em
NameouPhone
Uso¶
<ContactAvatar ImageUrl="@contact.ProfilePictureUrl"
Name="@contact.Name"
Phone="@contact.Phone"
Size="Size.Medium" />
MessageStatusIcon¶
Ícone de status de mensagem (estilo WhatsApp).
Arquivo: Components/MessageStatusIcon.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
Status |
string |
Status da mensagem |
Estados¶
| Status | Ícone | Cor | Descrição |
|---|---|---|---|
pending/sending |
Relógio | Cinza | Enviando |
sent |
✓ | Cinza | Enviado |
delivered |
✓✓ | Cinza | Entregue |
read |
✓✓ | Azul | Lido |
failed |
Erro | Vermelho | Falha |
Uso¶
<div class="message-bubble">
<span>@message.Content</span>
<MessageStatusIcon Status="@message.Status" />
</div>
PromptVersionHistoryDialog¶
Dialog para visualizar histórico de versões de prompts, comparar versões side-by-side e restaurar versões anteriores.
Arquivo: Pages/Agents/Components/PromptVersionHistoryDialog.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
AgentId |
Guid |
ID do agente |
StepId |
Guid? |
ID do step (null = prompt do Agent) |
CurrentContent |
string? |
Conteudo atual do prompt/instrucao (exibido como linha "Atual") |
Funcionalidades¶
- Versao atual: Primeira linha da tabela com chip "Atual", sem botao restaurar, disponivel para comparacao
- Preview: Expandir conteúdo de uma versão inline
- Comparação: Selecionar duas versões para visualização side-by-side com
MudGrid2 colunas - Restaurar: Botão com confirmação via
DialogService.ShowMessageBox, aplica o conteúdo da versão selecionada
Comportamento¶
- Carrega versões via
ApiClient.GetAgentPromptVersionsAsyncouGetStepPromptVersionsAsyncconformeStepId - Restaurar chama
ApiClient.RestorePromptVersionAsynce fecha comDialogResult.Ok(true) - Erros exibidos via
Snackbar.ShowIfError(result) - Somente Admin pode restaurar (endpoint protegido por
AuthConstants.Roles.Admin)
Uso¶
// Abrir para Agent prompt
var parameters = new DialogParameters<PromptVersionHistoryDialog>
{
{ x => x.AgentId, agentId },
{ x => x.CurrentContent, agent.SystemPrompt }
};
await Dialog.ShowFormAsync<PromptVersionHistoryDialog>(
"Historico de Versoes", parameters, MaxWidth.Large);
// Abrir para Step instructions
var parameters = new DialogParameters<PromptVersionHistoryDialog>
{
{ x => x.AgentId, agentId },
{ x => x.StepId, stepId },
{ x => x.CurrentContent, step.Instructions }
};
await Dialog.ShowFormAsync<PromptVersionHistoryDialog>(
"Historico de Versoes", parameters, MaxWidth.Large);
Integração¶
- AgentPromptTab: Botão
Historyno cabeçalho ao lado do contador de palavras - AgentStepsTab: Botão
Historyno menu overflow ("...") de cada step
Componentes de Chat (Components/Chat/)¶
Componentes visuais de chat compartilhados entre Conversations e Playground.
ChatMode¶
Arquivo: Components/Chat/ChatMode.cs
Enum que controla o comportamento dos componentes de chat conforme o contexto.
| Valor | Descrição |
|---|---|
Conversation |
Modo padrão com reações, status de mensagem e avatar |
Playground |
Modo simplificado sem reações, status ou avatar |
MessageBubble¶
Bolha de mensagem estilo WhatsApp com suporte a texto, imagem, áudio, vídeo e documento.
Arquivo: Components/Chat/MessageBubble.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
Message |
MessageItemResponse |
Dados da mensagem |
IsUser |
bool |
Se é mensagem do usuário |
ContactPhone |
string |
Telefone do contato |
Mode |
ChatMode |
Modo de exibição (Conversation ou Playground) |
OnMediaPreview |
EventCallback<(string, string)> |
Preview de mídia |
DisplayLength |
int |
Limite de caracteres antes de "Ler mais" |
Comportamento por ChatMode¶
- Conversation: Exibe avatar, reações, status de mensagem (sent/delivered/read)
- Playground: Oculta avatar, reações e status de mensagem
MessageInput¶
Campo de entrada de mensagem com suporte a texto, anexos e gravação de voz.
Arquivo: Components/Chat/MessageInput.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
IsActive |
bool |
Se o input está ativo |
MessageText |
string |
Texto atual |
MessageTextChanged |
EventCallback<string> |
Callback de mudança de texto |
PendingAttachments |
List<AttachmentPreview> |
Anexos pendentes |
IsRecording |
bool |
Se está gravando áudio |
RecordingDuration |
int |
Duração da gravação em segundos |
OnSendMessage |
EventCallback |
Enviar mensagem |
OnStartRecording |
EventCallback |
Iniciar gravação |
OnStopRecording |
EventCallback |
Parar gravação |
OnCancelRecording |
EventCallback |
Cancelar gravação |
OnFilesSelected |
EventCallback<InputFileChangeEventArgs> |
Arquivos selecionados |
OnRemoveAttachment |
EventCallback<AttachmentPreview> |
Remover anexo |
AudioPlayer¶
Player de áudio estilo WhatsApp para mensagens de voz.
Arquivo: Components/Chat/AudioPlayer.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
MessageId |
Guid |
ID da mensagem |
MediaUrl |
string? |
URL do áudio |
MediaFileName |
string? |
Nome do arquivo |
DocumentPreview¶
Preview de documento com ícone por tipo de arquivo.
Arquivo: Components/Chat/DocumentPreview.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
MediaUrl |
string? |
URL do documento |
MediaFileName |
string? |
Nome do arquivo |
MediaMimeType |
string? |
MIME type |
ShowTimestamp |
bool |
Exibir timestamp |
IsUser |
bool |
Se é do usuário |
MessageTime |
string |
Hora da mensagem |
MessageStatus |
string? |
Status da mensagem |
Componentes de Dashboard (Components/Dashboard/)¶
Componentes visuais para o dashboard de relatórios. Usam ApexCharts para gráficos e MudBlazor para cards/filtros.
Nota: @using ApexCharts é adicionado por componente (não global em _Imports.razor) para evitar conflitos de namespace com MudBlazor (Size, Color, Mode).
SetupChecklist¶
Checklist de configuracao inicial exibido no Dashboard para administradores que ainda nao completaram o onboarding.
Arquivo: Components/Dashboard/SetupChecklist.razor + .razor.cs
| Parametro | Tipo | Descricao |
|---|---|---|
Status |
OnboardingStatusResponse? |
Status do onboarding com flags de progresso |
OnDismiss |
EventCallback |
Callback ao clicar "Ocultar checklist" |
Criterios exibidos:
| # | Criterio | Campo | Acao |
|---|---|---|---|
| 1 | Criar um agente | HasAgents |
Navega para /agents |
| 2 | Criar instancia WhatsApp | HasInstances |
Navega para /instances |
| 3 | Conectar WhatsApp | HasConnectedInstance |
Navega para /instances |
Comportamento:
- Chip indica progresso (ex: "2/3")
- Alerta de sucesso quando todos os 3 criterios sao atendidos
- Botao "Ocultar checklist" chama OnDismiss (que dispara POST /api/onboarding/dismiss)
WelcomeCard¶
Card de boas-vindas para usuarios comuns (nao-admin) no Dashboard.
Arquivo: Components/Dashboard/WelcomeCard.razor + .razor.cs
| Parametro | Tipo | Descricao |
|---|---|---|
UserName |
string |
Nome do usuario para saudacao |
OnDismiss |
EventCallback |
Callback ao clicar "Entendi" |
Comportamento:
- Exibe mensagem de boas-vindas personalizada
- 3 links rapidos: Dashboard, Conversas, Perfil
- Botao "Entendi" dispensa o card (armazenado em LocalStorage via welcomeCardDismissed)
DashboardDateFilter¶
Barra de filtros com DateRangePicker, botoes rapidos e seletor de agente.
Arquivo: Components/Dashboard/DashboardDateFilter.razor
| Parâmetro | Tipo | Descrição |
|---|---|---|
Agents |
List<AgentListResponse>? |
Lista de agentes para o select |
OnFilterChanged |
EventCallback<(DateTime, DateTime, Guid?)> |
Callback quando filtro muda |
Dispara OnFilterChanged automaticamente no OnInitializedAsync com os valores default (últimos 30 dias).
KpiCards¶
6 cards com métricas principais do período.
Arquivo: Components/Dashboard/KpiCards.razor
| Parâmetro | Tipo | Descrição |
|---|---|---|
Data |
KpiOverviewResponse? |
Dados dos KPIs |
ConversationsOverTimeChart¶
Gráfico de área com 3 séries: Total, Resolvidas por IA, Escaladas.
Arquivo: Components/Dashboard/ConversationsOverTimeChart.razor
| Parâmetro | Tipo | Descrição |
|---|---|---|
Data |
ConversationsOverTimeResponse? |
Série temporal |
StepFunnelChart¶
Gráfico de barras horizontal representando o funil de steps do agente.
Arquivo: Components/Dashboard/StepFunnelChart.razor
| Parâmetro | Tipo | Descrição |
|---|---|---|
Data |
StepFunnelResponse? |
Dados do funil |
PeakHoursHeatmap¶
Heatmap de horários de pico (dia da semana × hora do dia).
Arquivo: Components/Dashboard/PeakHoursHeatmap.razor
| Parâmetro | Tipo | Descrição |
|---|---|---|
Data |
PeakHoursResponse? |
Dados do heatmap (168 pontos) |
FollowUpEffectivenessCard¶
4 cards com métricas de efetividade de follow-ups automáticos.
Arquivo: Components/Dashboard/FollowUpEffectivenessCard.razor
| Parâmetro | Tipo | Descrição |
|---|---|---|
Data |
FollowUpEffectivenessResponse? |
Dados de follow-up |
ScheduleGrid¶
Grid de horários permitidos por dia da semana. Componente local reutilizado em Agents (follow-up) e Instances (horários de atendimento da IA).
Arquivo: Pages/Agents/Components/ScheduleGrid.razor
Parâmetros¶
| Parâmetro | Tipo | Descrição |
|---|---|---|
Value |
List<ScheduleDay>? |
Horários configurados |
ValueChanged |
EventCallback<List<ScheduleDay>?> |
Two-way binding |
NoScheduleText |
string? |
Texto exibido quando sem horário configurado |
Uso¶
<ScheduleGrid @bind-Value="_request.AllowedHours" />
<ScheduleGrid @bind-Value="_request.AiAvailabilityHours"
NoScheduleText="Sem restricao — IA atende 24 horas." />
Funcionalidades¶
- Botões rápidos: "Horário Comercial" (seg-sex 8h-18h), "Todos os Dias" (8h-22h), "Limpar"
- Toggle por dia da semana com time pickers para início/fim
- Emite
nullquando nenhum dia está habilitado
Critérios para Componentizar¶
| Critério | Ação |
|---|---|
| Usado em 2+ páginas diferentes | Components/ (global) |
| Usado apenas em 1 contexto | Pages/{Recurso}/Components/ (local) |
| Bloco grande (50+ linhas) | Componentizar |
| Lógica própria (estado, eventos) | Componentizar |
| Puramente visual e pequeno | Manter inline |
Boas Práticas¶
- Separação Razor/Code-behind: Sempre usar partial class
- Parâmetros tipados: Usar
[Parameter, EditorRequired]quando obrigatório - EventCallback: Preferir
EventCallbackaActionpara binding assíncrono - CascadingParameter: Usar para comunicação form → button
- Injeção de dependências: Via
[Inject]no code-behind