# Fluent UI Blazor - Setup e Padrões (.NET 10)

> Biblioteca de UI recomendada para projetos **AI-first** e micro-SaaS no MORPH-SPEC.

---

## 🎯 Por Que Fluent UI Blazor?

### Para Projetos AI-First

| Vantagem | Descrição |
|----------|-----------|
| **Componentes AI nativos** | Chat containers, streaming, typing indicators |
| **Microsoft Stack** | Integração perfeita com Agent Framework, Aspire, .NET 10 |
| **Design Copilot-like** | UX patterns da Microsoft para AI assistants |
| **Futuro-proof** | Microsoft evolui junto com AI capabilities |
| **Performance** | ~200KB gzipped (vs 500KB MudBlazor) |
| **Aspire Dashboard** | Mesma lib - consistência visual |

### Para Micro-SaaS

- **Design System moderno** - Microsoft 365 style
- **Acessibilidade nativa** - WCAG compliant
- **Dark mode** - Built-in
- **Responsive** - Mobile-first
- **Grátis** - MIT License, sem custos

---

## 📦 Setup

### 1. Instalar Package

```bash
dotnet add package Microsoft.FluentUI.AspNetCore.Components --version 4.0.0
```

### 2. Configurar Program.cs

```csharp
var builder = WebApplication.CreateBuilder(args);

// Blazor
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

// Fluent UI
builder.Services.AddFluentUIComponents();

var app = builder.Build();

// Pipeline
app.UseStaticFiles();
app.UseAntiforgery();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode()
    .AddAdditionalAssemblies(typeof(Microsoft.FluentUI.AspNetCore.Components._Imports).Assembly);

app.Run();
```

### 3. Configurar _Imports.razor

```razor
@* Components/_Imports.razor *@
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.FluentUI.AspNetCore.Components
@using Microsoft.FluentUI.AspNetCore.Components.Icons
```

### 4. Adicionar CSS e JS

```html
<!-- Components/App.razor -->
<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="/" />

    <!-- Fluent UI CSS -->
    <link href="_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css" rel="stylesheet" />
    <link href="_content/Microsoft.FluentUI.AspNetCore.Components/Microsoft.FluentUI.AspNetCore.Components.min.css" rel="stylesheet" />

    <HeadOutlet />
</head>
<body>
    <Routes />

    <!-- Fluent UI JS -->
    <script src="_content/Microsoft.FluentUI.AspNetCore.Components/Microsoft.FluentUI.AspNetCore.Components.lib.module.js" type="module"></script>
</body>
</html>
```

---

## 🎨 Layout SaaS Padrão

### Layout Principal

```razor
@* Components/Layout/MainLayout.razor *@
@inherits LayoutComponentBase

<FluentLayout>
    <!-- Header -->
    <FluentHeader>
        <FluentStack Orientation="Orientation.Horizontal" HorizontalAlignment="HorizontalAlignment.SpaceBetween">
            <FluentStack Orientation="Orientation.Horizontal">
                <FluentIcon Value="@(new Icons.Regular.Size24.AppGeneric())" Color="Color.Accent" />
                <FluentLabel Typo="Typography.H5">Meu SaaS</FluentLabel>
            </FluentStack>

            <FluentStack Orientation="Orientation.Horizontal">
                <FluentButton Appearance="Appearance.Stealth" IconStart="@(new Icons.Regular.Size20.DarkTheme())">
                    Dark Mode
                </FluentButton>
                <FluentPersona Name="@_userName" Image="@_userAvatar" />
            </FluentStack>
        </FluentStack>
    </FluentHeader>

    <!-- Sidebar -->
    <FluentNavMenu Width="250">
        <FluentNavLink Icon="@(new Icons.Regular.Size20.Home())" Href="/" Match="NavLinkMatch.All">
            Dashboard
        </FluentNavLink>
        <FluentNavLink Icon="@(new Icons.Regular.Size20.People())" Href="/customers">
            Clientes
        </FluentNavLink>
        <FluentNavLink Icon="@(new Icons.Regular.Size20.Cart())" Href="/orders">
            Pedidos
        </FluentNavLink>
        <FluentNavLink Icon="@(new Icons.Regular.Size20.Chat())" Href="/ai-assistant">
            AI Assistant
        </FluentNavLink>

        <FluentDivider />

        <FluentNavLink Icon="@(new Icons.Regular.Size20.Settings())" Href="/settings">
            Configurações
        </FluentNavLink>
    </FluentNavMenu>

    <!-- Main Content -->
    <FluentMain>
        <FluentBodyContent>
            @Body
        </FluentBodyContent>
    </FluentMain>
</FluentLayout>

@code {
    private string _userName = "Usuário";
    private string _userAvatar = "";
}
```

---

## 📋 Componentes Essenciais

### Data Grid

```razor
@page "/customers"

<FluentStack Orientation="Orientation.Vertical">
    <FluentStack Orientation="Orientation.Horizontal" HorizontalAlignment="HorizontalAlignment.SpaceBetween">
        <FluentLabel Typo="Typography.H4">Clientes</FluentLabel>
        <FluentButton Appearance="Appearance.Accent" IconStart="@(new Icons.Regular.Size20.Add())">
            Novo Cliente
        </FluentButton>
    </FluentStack>

    <FluentDataGrid Items="@_customers" GridTemplateColumns="1fr 2fr 2fr 1fr">
        <PropertyColumn Property="@(c => c.Id)" />
        <PropertyColumn Property="@(c => c.Name)" Title="Nome" />
        <PropertyColumn Property="@(c => c.Email)" />
        <TemplateColumn Title="Ações">
            <FluentButton Appearance="Appearance.Stealth" IconStart="@(new Icons.Regular.Size16.Edit())">
                Editar
            </FluentButton>
        </TemplateColumn>
    </FluentDataGrid>
</FluentStack>

@code {
    private IQueryable<Customer> _customers = new List<Customer>
    {
        new() { Id = 1, Name = "João Silva", Email = "joao@example.com" },
        new() { Id = 2, Name = "Maria Santos", Email = "maria@example.com" }
    }.AsQueryable();
}
```

### Formulários

```razor
@page "/customers/new"

<EditForm Model="@_model" OnValidSubmit="HandleSubmit">
    <DataAnnotationsValidator />

    <FluentStack Orientation="Orientation.Vertical">
        <FluentLabel Typo="Typography.H4">Novo Cliente</FluentLabel>

        <FluentTextField @bind-Value="_model.Name" Label="Nome" Required />
        <FluentValidationMessage For="@(() => _model.Name)" />

        <FluentTextField @bind-Value="_model.Email" Label="Email" TextFieldType="TextFieldType.Email" Required />
        <FluentValidationMessage For="@(() => _model.Email)" />

        <FluentTextField @bind-Value="_model.Phone" Label="Telefone" />

        <FluentStack Orientation="Orientation.Horizontal">
            <FluentButton Type="ButtonType.Submit" Appearance="Appearance.Accent">
                Salvar
            </FluentButton>
            <FluentButton Appearance="Appearance.Neutral" OnClick="Cancel">
                Cancelar
            </FluentButton>
        </FluentStack>
    </FluentStack>
</EditForm>

@code {
    private CustomerFormModel _model = new();

    private async Task HandleSubmit()
    {
        // Salvar cliente
    }

    private void Cancel() => NavigationManager.NavigateTo("/customers");
}
```

### Dialogs e Mensagens

```razor
@inject IDialogService DialogService
@inject IMessageService MessageService

<FluentButton OnClick="ShowDialog">Abrir Dialog</FluentButton>
<FluentButton OnClick="ShowMessage">Mostrar Mensagem</FluentButton>

@code {
    private async Task ShowDialog()
    {
        var dialog = await DialogService.ShowDialogAsync<ConfirmDialog>(new DialogParameters
        {
            Title = "Confirmar ação",
            PrimaryAction = "Confirmar",
            SecondaryAction = "Cancelar"
        });

        var result = await dialog.Result;
        if (!result.Cancelled)
        {
            // Ação confirmada
        }
    }

    private void ShowMessage()
    {
        MessageService.ShowMessageBar(options =>
        {
            options.Intent = MessageIntent.Success;
            options.Title = "Sucesso!";
            options.Body = "Operação realizada com sucesso.";
        });
    }
}
```

---

## 🤖 Componentes AI-Specific

### Chat Container

```razor
@page "/ai-assistant"
@inject IDocumentAssistantAgent Assistant

<FluentStack Orientation="Orientation.Vertical" Style="height: 100vh;">
    <FluentLabel Typo="Typography.H4">AI Assistant</FluentLabel>

    <!-- Messages Area -->
    <FluentScrollArea Style="flex: 1; overflow-y: auto;">
        <FluentStack Orientation="Orientation.Vertical">
            @foreach (var message in _messages)
            {
                <FluentMessageBar Intent="@(message.IsUser ? MessageIntent.Info : MessageIntent.Success)">
                    <FluentLabel Weight="FontWeight.Bold">
                        @(message.IsUser ? "Você" : "AI Assistant")
                    </FluentLabel>
                    <FluentLabel>@message.Content</FluentLabel>
                </FluentMessageBar>
            }

            @if (_isTyping)
            {
                <FluentProgress />
                <FluentLabel Typo="Typography.Caption">AI está digitando...</FluentLabel>
            }
        </FluentStack>
    </FluentScrollArea>

    <!-- Input Area -->
    <FluentStack Orientation="Orientation.Horizontal">
        <FluentTextField @bind-Value="_userInput"
                        Placeholder="Digite sua pergunta..."
                        Style="flex: 1;"
                        @onkeypress="HandleKeyPress" />
        <FluentButton Appearance="Appearance.Accent"
                     IconStart="@(new Icons.Regular.Size20.Send())"
                     OnClick="SendMessage"
                     Disabled="@_isTyping">
            Enviar
        </FluentButton>
    </FluentStack>
</FluentStack>

@code {
    private List<ChatMessage> _messages = new();
    private string _userInput = "";
    private bool _isTyping;

    private async Task SendMessage()
    {
        if (string.IsNullOrWhiteSpace(_userInput)) return;

        // Adicionar mensagem do usuário
        _messages.Add(new ChatMessage { Content = _userInput, IsUser = true });
        var question = _userInput;
        _userInput = "";
        _isTyping = true;

        // Obter resposta do AI
        var response = await Assistant.AskQuestionAsync(question);
        _messages.Add(new ChatMessage { Content = response, IsUser = false });

        _isTyping = false;
    }

    private async Task HandleKeyPress(KeyboardEventArgs e)
    {
        if (e.Key == "Enter" && !e.ShiftKey)
        {
            await SendMessage();
        }
    }

    public class ChatMessage
    {
        public string Content { get; set; } = "";
        public bool IsUser { get; set; }
    }
}
```

### Streaming Response

```razor
@page "/ai-stream"

<FluentStack Orientation="Orientation.Vertical">
    <FluentTextField @bind-Value="_prompt" Label="Prompt" />
    <FluentButton OnClick="GenerateStream">Gerar (Streaming)</FluentButton>

    <FluentCard>
        <FluentLabel>@_streamedContent</FluentLabel>
        @if (_isStreaming)
        {
            <FluentProgressRing />
        }
    </FluentCard>
</FluentStack>

@code {
    private string _prompt = "";
    private string _streamedContent = "";
    private bool _isStreaming;

    private async Task GenerateStream()
    {
        _streamedContent = "";
        _isStreaming = true;

        // Simular streaming (substituir com Agent Framework real)
        var words = "Esta é uma resposta simulada do AI assistant em streaming.".Split(' ');
        foreach (var word in words)
        {
            _streamedContent += word + " ";
            StateHasChanged();
            await Task.Delay(200);
        }

        _isStreaming = false;
    }
}
```

---

## 🎨 Temas e Dark Mode

### Configurar Theme Provider

```razor
<!-- App.razor -->
<FluentDesignTheme @bind-Mode="@_theme"
                   StorageName="theme-preference"
                   OfficeColor="OfficeColor.Default" />

<FluentLayout>
    <!-- Conteúdo -->
</FluentLayout>

@code {
    private DesignThemeModes _theme = DesignThemeModes.System;
}
```

### Toggle Dark Mode

```razor
<FluentSwitch @bind-Value="_isDarkMode"
              @bind-Value:after="ToggleTheme"
              Label="@(_isDarkMode ? "Dark Mode" : "Light Mode")" />

@code {
    private bool _isDarkMode;

    private void ToggleTheme()
    {
        _theme = _isDarkMode ? DesignThemeModes.Dark : DesignThemeModes.Light;
    }
}
```

---

## 🔄 Abordagem Híbrida (Fluent UI + MudBlazor)

### Quando Adicionar MudBlazor

Use MudBlazor para:
- **DataGrid complexo** - filtros avançados, agrupamento, export
- **Charts** - MudChart para dashboards analytics
- **Calendar/Scheduler** - se precisar de componentes avançados

### Setup Híbrido

```bash
dotnet add package Microsoft.FluentUI.AspNetCore.Components
dotnet add package MudBlazor
```

```csharp
// Program.cs
builder.Services.AddFluentUIComponents();
builder.Services.AddMudServices();
```

```html
<!-- App.razor -->
<!-- Fluent UI CSS -->
<link href="_content/Microsoft.FluentUI.AspNetCore.Components/Microsoft.FluentUI.AspNetCore.Components.min.css" rel="stylesheet" />

<!-- MudBlazor CSS -->
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
```

### Exemplo de Uso Combinado

```razor
@* Layout com Fluent UI *@
<FluentLayout>
    <FluentHeader>...</FluentHeader>

    <FluentMain>
        @* Grid complexo com MudBlazor *@
        <MudDataGrid T="Order"
                     Items="@_orders"
                     Filterable="true"
                     Groupable="true"
                     Dense="true">
            <Columns>
                <PropertyColumn Property="x => x.OrderNumber" Title="Número" />
                <PropertyColumn Property="x => x.Total" Title="Total" Format="C2" />
            </Columns>
        </MudDataGrid>

        @* Chat com Fluent UI *@
        <FluentMessageBar Intent="MessageIntent.Success">
            Pedido criado com sucesso!
        </FluentMessageBar>
    </FluentMain>
</FluentLayout>
```

---

## 📊 Performance Tips

### Bundle Size

```xml
<!-- .csproj -->
<PropertyGroup>
    <!-- Trimming para reduzir bundle -->
    <PublishTrimmed>true</PublishTrimmed>
    <TrimMode>link</TrimMode>
</PropertyGroup>
```

### Lazy Loading de Componentes

```razor
@* Carregar MudBlazor só quando necessário *@
@if (_showComplexGrid)
{
    <MudDataGrid ... />
}
else
{
    <FluentDataGrid ... />
}
```

---

## ✅ Checklist de Implementação

- [ ] Package `Microsoft.FluentUI.AspNetCore.Components` instalado
- [ ] `AddFluentUIComponents()` no Program.cs
- [ ] CSS e JS referenciados no App.razor
- [ ] _Imports.razor atualizado
- [ ] Layout principal criado com FluentLayout
- [ ] Navegação configurada com FluentNavMenu
- [ ] Theme provider configurado
- [ ] Dark mode funcional
- [ ] Componentes AI (chat) implementados se aplicável
- [ ] (Opcional) MudBlazor adicionado para grids/charts complexos

---

## 🚫 O Que NÃO Fazer

❌ Misturar estilos - use Fluent UI OU MudBlazor consistentemente
❌ Importar todas as libs sem necessidade (bundle size)
❌ Ignorar acessibilidade (Fluent UI tem nativa)
❌ Criar componentes customizados sem checar se já existem
❌ Usar JavaScript customizado (Fluent UI tem tudo em C#)

---

## 📚 Recursos

| Recurso | URL |
|---------|-----|
| Documentação Oficial | [fluentui-blazor.net](https://www.fluentui-blazor.net/) |
| GitHub | [github.com/microsoft/fluentui-blazor](https://github.com/microsoft/fluentui-blazor) |
| Exemplos | [fluentui-blazor.net/examples](https://www.fluentui-blazor.net/examples) |
| Icons Explorer | [fluentui-blazor.net/icon-explorer](https://www.fluentui-blazor.net/icon-explorer) |
| MudBlazor (complemento) | [mudblazor.com](https://mudblazor.com/) |

---

## 🎓 Comparativo Rápido

| Feature | Fluent UI | MudBlazor | Recomendação |
|---------|-----------|-----------|--------------|
| AI Components | ✅ Nativo | ❌ Não | **Fluent UI** |
| DataGrid | ⚠️ Básico | ✅ Avançado | **MudBlazor** |
| Charts | ❌ Não | ✅ Sim | **MudBlazor** |
| Performance | ✅ Leve | ⚠️ Pesado | **Fluent UI** |
| Microsoft Integration | ✅ Nativo | ❌ Não | **Fluent UI** |
| Templates Prontos | ⚠️ Poucos | ✅ Muitos | **MudBlazor** |
| Futuro AI | ✅ Garantido | ❓ Incerto | **Fluent UI** |

---

*MORPH-SPEC by Polymorphism Tech*
