Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
42b044f
docs: update roadmap for Sprint 7 start
Jan 7, 2026
ececf2b
feat(admin): add Create Provider dialog
Jan 7, 2026
cd2be7f
feat(admin): complete Provider CRUD operations
Jan 7, 2026
9cd3688
docs: update Sprint 7 progress - Provider CRUD complete
Jan 7, 2026
e033488
feat(admin): implement Documents management feature
Jan 7, 2026
5e117c3
docs: update Sprint 7 progress - Documents complete (2/6 features)
Jan 7, 2026
bd0c46b
feat(admin): implement Service Catalogs CRUD (Categories + Services)
Jan 7, 2026
3317ace
feat(admin): implement Geographic Restrictions - AllowedCities UI
Jan 7, 2026
0e0d0d8
feat(admin): implement Dashboard Charts with MudBlazor
Jan 7, 2026
2a08284
test(admin): increase bUnit test coverage to 30 tests
Jan 7, 2026
2b5c8fb
docs: update roadmap - Sprint 7 completed
Jan 7, 2026
d611ab8
feat: improve type safety, validation, and error handling across Admi…
Jan 7, 2026
eff47d9
refactor: improve DTO semantics and UX consistency
Jan 7, 2026
7abe713
fix: implement IDisposable correctly in Dashboard component
Jan 7, 2026
a28c4ab
fix: correct build errors from CI/CD pipeline
Jan 7, 2026
d76b275
refactor: implement code review feedback for UX and error handling
Jan 7, 2026
b0e12e5
fix: update service Description in reducer and improve code quality
Jan 7, 2026
4b2920e
fix: correct build errors from previous commits
Jan 7, 2026
c76d157
fix: ensure test isolation in HangfireAuthorizationFilterTests
Jan 7, 2026
080556f
fix: remove literal backtick-n sequences from test file
Jan 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 95 additions & 3 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ Este documento consolida o planejamento estratégico e tático da plataforma MeA
- ✅ **14 Dez - 18 Dez**: Sprint 4 - Health Checks + Data Seeding + Code Review (CONCLUÍDO - MERGED!)
- ✅ **Sprint 5**: Tarefas completadas antecipadamente (NSubstitute→Moq, .slnx, UuidGenerator, Design Patterns, Bruno)
- ✅ **19 Dez - 30 Dez**: Sprint 5.5 - Refactor & Cleanup (CONCLUÍDO - Technical Debt Reduction)
- ✅ **30 Dez - 5 Jan 2026**: Sprint 6 - Blazor Admin Portal Setup (CONCLUÍDO - 5 Jan 2026)
- **6 Jan - 24 Jan 2026**: Sprint 7 - Blazor Admin Portal Features
- ✅ **30 Dez - 5 Jan 2026**: Sprint 6 - Blazor Admin Portal Setup (CONCLUÍDO - 5 Jan 2026, MERGED!)
- 🔄 **6 Jan - 24 Jan 2026**: Sprint 7 - Blazor Admin Portal Features (EM ANDAMENTO - Iniciado 6 Jan 2026)
- ⏳ **27 Jan - 14 Fev 2026**: Sprint 8 - Customer App (Web + Mobile)
- ⏳ **17 Fev - 7 Mar 2026**: Sprint 9 - BUFFER (Polishing, Risk Mitigation, Refactoring)
- 🎯 **31 de Março de 2026**: MVP Launch (Admin Portal + Customer App)
Expand All @@ -40,9 +40,101 @@ Este documento consolida o planejamento estratégico e tático da plataforma MeA

**📅 Hoje**: 6 de Janeiro de 2026

### 🔄 Sprint 7 - Blazor Admin Portal Features - EM ANDAMENTO (6 Jan - 24 Jan 2026)

**Branch**: `blazor-admin-portal-features`

**Objetivos**:
1. ✅ **CRUD Completo de Providers** (6-7 Jan 2026) - Create, Update, Delete, Verify
2. ✅ **Gestão de Documentos** (7 Jan 2026) - Upload, verificação, deletion workflow
3. ✅ **Gestão de Service Catalogs** (7 Jan 2026) - CRUD de categorias e serviços
4. ✅ **Gestão de Restrições Geográficas** (7 Jan 2026) - UI para AllowedCities com banco de dados
5. ✅ **Gráficos Dashboard** (7 Jan 2026) - MudCharts com providers por status e evolução temporal
6. ✅ **Testes** (7 Jan 2026) - Aumentar cobertura para 30 testes bUnit

**Progresso Atual**: 6/6 features completas ✅ **SPRINT 7 CONCLUÍDO!**

**Detalhamento - Provider CRUD** ✅:
- IProvidersApi enhanced: CreateProviderAsync, UpdateProviderAsync, DeleteProviderAsync, UpdateVerificationStatusAsync
- CreateProviderDialog: formulário completo com validação (ProviderType, Name, FantasyName, Document, Email, Phone, Description, Address)
- EditProviderDialog: edição simplificada (nome/telefone - aguardando DTO enriquecido do backend)
- VerifyProviderDialog: mudança de status de verificação (Verified, Rejected, Pending + optional notes)
- Providers.razor: action buttons (Edit, Delete, Verify) com MessageBox confirmation
- Result<T> error handling pattern em todas operações
- Portuguese labels + Snackbar notifications
- Build sucesso (19 warnings Sonar apenas)
- Commit: cd2be7f6 "feat(admin): complete Provider CRUD operations"

**Detalhamento - Documents Management** ✅:
- DocumentsState/Actions/Reducers/Effects: Fluxor pattern completo
- Documents.razor: página com provider selector e listagem de documentos
- MudDataGrid com status chips coloridos (Verified=Success, Rejected=Error, Pending=Warning, Uploaded=Info)
- ProviderSelectorDialog: seleção de provider da lista existente
- UploadDocumentDialog: MudFileUpload com tipos de documento (RG, CNH, CPF, CNPJ, Comprovante, Outros)
- RequestVerification action via IDocumentsApi.RequestDocumentVerificationAsync
- DeleteDocument com confirmação MessageBox
- Real-time status updates via Fluxor Dispatch
- Portuguese labels + Snackbar notifications
- Build sucesso (28 warnings Sonar apenas)
- Commit: e033488d "feat(admin): implement Documents management feature"

**Detalhamento - Service Catalogs** ✅:
- IServiceCatalogsApi enhanced: 10 métodos (Create, Update, Delete, Activate, Deactivate para Categories e Services)
- ServiceCatalogsState/Actions/Reducers/Effects: Fluxor pattern completo
- Categories.razor: full CRUD page com MudDataGrid, status chips, action buttons
- Services.razor: full CRUD page com category relationship e MudDataGrid
- CreateCategoryDialog, EditCategoryDialog: forms com Name, Description, DisplayOrder
- CreateServiceDialog, EditServiceDialog: forms com CategoryId (dropdown), Name, Description, DisplayOrder
- Activate/Deactivate toggles para ambos
- Delete confirmations com MessageBox
- Portuguese labels + Snackbar notifications
- Build sucesso (37 warnings Sonar/MudBlazor apenas)
- Commit: bd0c46b3 "feat(admin): implement Service Catalogs CRUD (Categories + Services)"

**Detalhamento - Geographic Restrictions** ✅:
- ILocationsApi já possuía CRUD completo (Create, Update, Delete, GetAll, GetById, GetByState)
- LocationsState/Actions/Reducers/Effects: Fluxor pattern completo
- AllowedCities.razor: full CRUD page com MudDataGrid
- CreateAllowedCityDialog: formulário com City, State, Country, Latitude, Longitude, ServiceRadiusKm, IsActive
- EditAllowedCityDialog: mesmo formulário para edição
- MudDataGrid com coordenadas em formato F6 (6 decimais), status chips (Ativa/Inativa)
- Toggle activation via MudSwitch (updates backend via UpdateAllowedCityAsync)
- Delete confirmation com MessageBox
- Portuguese labels + Snackbar notifications
- Build sucesso (42 warnings Sonar/MudBlazor apenas)
- Commit: 3317ace3 "feat(admin): implement Geographic Restrictions - AllowedCities UI"

**Detalhamento - Dashboard Charts** ✅:
- Dashboard.razor enhanced com 2 charts interativos (MudBlazor built-in charts)
- Provider Status Donut Chart: agrupa providers por VerificationStatus (Verified, Pending, Rejected)
- Provider Type Pie Chart: distribuição entre Individual (Pessoa Física) e Company (Empresa)
- Usa ProvidersState existente (sem novos endpoints de backend)
- OnAfterRender lifecycle hook para update de dados quando providers carregam
- UpdateChartData() método com GroupBy LINQ queries
- Portuguese labels para tipos de provider
- Empty state messages quando não há providers cadastrados
- MudChart components com Width="300px", Height="300px", LegendPosition.Bottom
- Build sucesso (43 warnings Sonar/MudBlazor apenas)
- Commit: 0e0d0d81 "feat(admin): implement Dashboard Charts with MudBlazor"

**Detalhamento - Testes bUnit** ✅:
- 30 testes bUnit criados (objetivo: 30+) - era 10, adicionados 20 novos
- CreateProviderDialogTests: 4 testes (form fields, submit button, provider type, MudForm)
- DocumentsPageTests: 5 testes (provider selector, upload button, loading, document list, error)
- CategoriesPageTests: 4 testes (load action, create button, list, loading)
- ServicesPageTests: 3 testes (load actions, create button, list)
- AllowedCitiesPageTests: 4 testes (load action, create button, list, loading)
- Todos seguem pattern: Mock IState/IDispatcher/IApi, AddMudServices, JSRuntimeMode.Loose
- Verificam rendering, state management, user interactions
- Namespaces corrigidos: Modules.*.DTOs
- Build sucesso (sem erros de compilação)
- Commit: 2a082840 "test(admin): increase bUnit test coverage to 30 tests"

---

### ✅ Sprint 6 - Blazor Admin Portal Setup - CONCLUÍDA (30 Dez 2025 - 5 Jan 2026)

**Branch**: `blazor-admin-portal-setup` (4 commits ahead, pronto para merge)
**Status**: MERGED to master (5 Jan 2026)

**Principais Conquistas**:
1. **Projeto Blazor WASM Configurado** ✅
Expand Down
66 changes: 66 additions & 0 deletions src/Client/MeAjudaAi.Client.Contracts/Api/IProvidersApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,72 @@ Task<Result<IReadOnlyList<ModuleProviderBasicDto>>> GetProvidersByStateAsync(
Task<Result<ModuleProviderDto?>> GetProviderByDocumentAsync(
string document,
CancellationToken cancellationToken = default);

/// <summary>
/// Cria um novo provider.
/// </summary>
/// <param name="request">Dados do provider a ser criado</param>
/// <param name="cancellationToken">Token de cancelamento da operação</param>
/// <returns>Provider criado com sucesso</returns>
/// <response code="201">Provider criado com sucesso</response>
/// <response code="400">Dados inválidos ou provider já existe</response>
/// <response code="401">Não autenticado</response>
/// <response code="403">Sem permissão para criar providers</response>
[Post("/api/v1/providers")]
Task<Result<ModuleProviderDto>> CreateProviderAsync(
[Body] CreateProviderRequestDto request,
CancellationToken cancellationToken = default);

/// <summary>
/// Atualiza os dados de um provider existente.
/// </summary>
/// <param name="id">ID do provider</param>
/// <param name="request">Dados atualizados do provider</param>
/// <param name="cancellationToken">Token de cancelamento da operação</param>
/// <returns>Provider atualizado</returns>
/// <response code="200">Provider atualizado com sucesso</response>
/// <response code="404">Provider não encontrado</response>
/// <response code="400">Dados inválidos</response>
/// <response code="401">Não autenticado</response>
/// <response code="403">Sem permissão para atualizar este provider</response>
[Put("/api/v1/providers/{id}")]
Task<Result<Unit>> UpdateProviderAsync(
Guid id,
[Body] UpdateProviderRequestDto request,
CancellationToken cancellationToken = default);

/// <summary>
/// Exclui um provider.
/// </summary>
/// <param name="id">ID do provider a ser excluído</param>
/// <param name="cancellationToken">Token de cancelamento da operação</param>
/// <returns>Resultado da operação</returns>
/// <response code="204">Provider excluído com sucesso</response>
/// <response code="404">Provider não encontrado</response>
/// <response code="401">Não autenticado</response>
/// <response code="403">Sem permissão para excluir providers</response>
[Delete("/api/v1/providers/{id}")]
Task<Result<Unit>> DeleteProviderAsync(
Guid id,
CancellationToken cancellationToken = default);

/// <summary>
/// Atualiza o status de verificação de um provider.
/// </summary>
/// <param name="id">ID do provider</param>
/// <param name="request">Novo status de verificação</param>
/// <param name="cancellationToken">Token de cancelamento da operação</param>
/// <returns>Resultado da operação</returns>
/// <response code="200">Status atualizado com sucesso</response>
/// <response code="404">Provider não encontrado</response>
/// <response code="400">Status inválido</response>
/// <response code="401">Não autenticado</response>
/// <response code="403">Sem permissão para verificar providers</response>
[Put("/api/v1/providers/{id}/verification-status")]
Task<Result<Unit>> UpdateVerificationStatusAsync(
Guid id,
[Body] UpdateVerificationStatusRequestDto request,
CancellationToken cancellationToken = default);
}

/// <summary>
Expand Down
108 changes: 108 additions & 0 deletions src/Client/MeAjudaAi.Client.Contracts/Api/IServiceCatalogsApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,112 @@ Task<Result<IReadOnlyList<ModuleServiceDto>>> GetServicesByCategoryAsync(
Guid categoryId,
[Query] bool activeOnly = true,
CancellationToken cancellationToken = default);

// ========== CATEGORIES WRITE OPERATIONS ==========

/// <summary>
/// Cria uma nova categoria de serviços.
/// </summary>
/// <param name="request">Dados da categoria (Name, Description, DisplayOrder)</param>
/// <param name="cancellationToken">Token de cancelamento</param>
[Post("/api/v1/service-catalogs/categories")]
Task<Result<ModuleServiceCategoryDto>> CreateCategoryAsync(
[Body] CreateServiceCatalogCategoryRequestDto request,
CancellationToken cancellationToken = default);

/// <summary>
/// Atualiza uma categoria existente.
/// </summary>
/// <param name="categoryId">ID da categoria</param>
/// <param name="request">Dados atualizados (Name, Description, DisplayOrder)</param>
/// <param name="cancellationToken">Token de cancelamento</param>
[Put("/api/v1/service-catalogs/categories/{categoryId}")]
Task<Result<Unit>> UpdateCategoryAsync(
Guid categoryId,
[Body] UpdateServiceCatalogCategoryRequestDto request,
CancellationToken cancellationToken = default);

/// <summary>
/// Deleta uma categoria.
/// </summary>
/// <param name="categoryId">ID da categoria</param>
/// <param name="cancellationToken">Token de cancelamento</param>
[Delete("/api/v1/service-catalogs/categories/{categoryId}")]
Task<Result<Unit>> DeleteCategoryAsync(
Guid categoryId,
CancellationToken cancellationToken = default);

/// <summary>
/// Ativa uma categoria.
/// </summary>
/// <param name="categoryId">ID da categoria</param>
/// <param name="cancellationToken">Token de cancelamento</param>
[Post("/api/v1/service-catalogs/categories/{categoryId}/activate")]
Task<Result<Unit>> ActivateCategoryAsync(
Guid categoryId,
CancellationToken cancellationToken = default);

/// <summary>
/// Desativa uma categoria.
/// </summary>
/// <param name="categoryId">ID da categoria</param>
/// <param name="cancellationToken">Token de cancelamento</param>
[Post("/api/v1/service-catalogs/categories/{categoryId}/deactivate")]
Task<Result<Unit>> DeactivateCategoryAsync(
Guid categoryId,
CancellationToken cancellationToken = default);

// ========== SERVICES WRITE OPERATIONS ==========

/// <summary>
/// Cria um novo serviço.
/// </summary>
/// <param name="request">Dados do serviço (CategoryId, Name, Description, DisplayOrder)</param>
/// <param name="cancellationToken">Token de cancelamento</param>
[Post("/api/v1/service-catalogs/services")]
Task<Result<ModuleServiceDto>> CreateServiceAsync(
[Body] CreateServiceRequestDto request,
CancellationToken cancellationToken = default);

/// <summary>
/// Atualiza um serviço existente.
/// </summary>
/// <param name="serviceId">ID do serviço</param>
/// <param name="request">Dados atualizados (Name, Description, DisplayOrder)</param>
/// <param name="cancellationToken">Token de cancelamento</param>
[Put("/api/v1/service-catalogs/services/{serviceId}")]
Task<Result<Unit>> UpdateServiceAsync(
Guid serviceId,
[Body] UpdateServiceRequestDto request,
CancellationToken cancellationToken = default);

/// <summary>
/// Deleta um serviço.
/// </summary>
/// <param name="serviceId">ID do serviço</param>
/// <param name="cancellationToken">Token de cancelamento</param>
[Delete("/api/v1/service-catalogs/services/{serviceId}")]
Task<Result<Unit>> DeleteServiceAsync(
Guid serviceId,
CancellationToken cancellationToken = default);

/// <summary>
/// Ativa um serviço.
/// </summary>
/// <param name="serviceId">ID do serviço</param>
/// <param name="cancellationToken">Token de cancelamento</param>
[Post("/api/v1/service-catalogs/services/{serviceId}/activate")]
Task<Result<Unit>> ActivateServiceAsync(
Guid serviceId,
CancellationToken cancellationToken = default);

/// <summary>
/// Desativa um serviço.
/// </summary>
/// <param name="serviceId">ID do serviço</param>
/// <param name="cancellationToken">Token de cancelamento</param>
[Post("/api/v1/service-catalogs/services/{serviceId}/deactivate")]
Task<Result<Unit>> DeactivateServiceAsync(
Guid serviceId,
CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace MeAjudaAi.Shared.Contracts.Modules.Providers.DTOs;

/// <summary>
/// Request DTO para criação de provider.
/// Usado pelo Admin Portal para adicionar novos providers ao sistema.
/// </summary>
public sealed record CreateProviderRequestDto(
string Name,
int Type,
BusinessProfileDto BusinessProfile,
string? Document = null);

/// <summary>
/// DTO para perfil de negócio do provider.
/// </summary>
public sealed record BusinessProfileDto(
string LegalName,
string? FantasyName,
string? Description,
ContactInfoDto ContactInfo,
PrimaryAddressDto PrimaryAddress);

/// <summary>
/// DTO para informações de contato.
/// </summary>
public sealed record ContactInfoDto(
string Email,
string? PhoneNumber,
string? Website);

/// <summary>
/// DTO para endereço primário.
/// </summary>
public sealed record PrimaryAddressDto(
string Street,
string? Number,
string? Complement,
string? Neighborhood,
string City,
string State,
string ZipCode,
string Country);
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace MeAjudaAi.Shared.Contracts.Modules.Providers.DTOs;

/// <summary>
/// Request DTO para atualização de provider.
/// Usado pelo Admin Portal para modificar dados de providers existentes.
/// </summary>
public sealed record UpdateProviderRequestDto(
string? Name = null,
string? Phone = null,
BusinessProfileUpdateDto? BusinessProfile = null);

/// <summary>
/// DTO para atualização de perfil de negócio.
/// </summary>
public sealed record BusinessProfileUpdateDto(
string? LegalName = null,
string? FantasyName = null,
string? Description = null,
ContactInfoUpdateDto? ContactInfo = null,
PrimaryAddressUpdateDto? PrimaryAddress = null);

/// <summary>
/// DTO para atualização de informações de contato.
/// </summary>
public sealed record ContactInfoUpdateDto(
string? Email = null,
string? PhoneNumber = null,
string? Website = null);

/// <summary>
/// DTO para atualização de endereço.
/// </summary>
public sealed record PrimaryAddressUpdateDto(
string? Street = null,
string? Number = null,
string? Complement = null,
string? Neighborhood = null,
string? City = null,
string? State = null,
string? ZipCode = null,
string? Country = null);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace MeAjudaAi.Shared.Contracts.Modules.Providers.DTOs;

/// <summary>
/// Request DTO para atualização do status de verificação de um provider.
/// </summary>
public sealed record UpdateVerificationStatusRequestDto(
string VerificationStatus,
string? Reason = null);
Loading
Loading