Conversation
…tion, profile management, document handling, and tier system.
…figuration, Docker Compose, and Aspire AppHost integration.
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughRoadmap and Keycloak infra updated; provider self-registration/onboarding added (tiers, documents, services); DTOs, commands, handlers, EF migrations and DB permissions updated; extensive frontend pages/hooks/components and API clients added; Testcontainers RabbitMQ integrated into test infra; multiple validators, rate limits, and auth flows introduced. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Browser
participant Frontend as Customer UI
participant API as API Server
participant Keycloak as Keycloak
participant DB as Database
participant Storage as Blob Storage
Frontend->>API: POST /api/v1/providers/become (RegisterProviderRequest)
activate API
API->>Keycloak: Validate token / claims (optional)
Keycloak-->>API: token/claims
API->>DB: Check provider by userId
DB-->>API: Not found / Existing
API->>DB: Create Provider, Document metadata (FileName/FileUrl), emit events
DB-->>API: OK
API-->>Frontend: 201 Created {ProviderDto}
deactivate API
Frontend->>API: GET /api/v1/providers/me/documents/sas (request SAS)
API->>Storage: Request SAS URL
Storage-->>API: SAS URL
API-->>Frontend: SAS URL
Frontend->>Storage: PUT file to SAS URL
Storage-->>Frontend: 200 OK
Frontend->>API: POST /api/v1/providers/me/documents (metadata with FileUrl)
API->>DB: Insert/Update Document (FileName/FileUrl)
DB-->>API: OK
API-->>Frontend: 200 {ProviderDto with Documents}
sequenceDiagram
participant Browser as Client
participant Keycloak as Keycloak
participant API as API Server
participant DB as Database
Browser->>Keycloak: OAuth signin (idp_hint=google/facebook)
Keycloak-->>Browser: Redirect + tokens
Browser->>API: GET /api/v1/providers/me/status (Bearer token)
API->>Keycloak: (optional) introspect/validate token
API->>DB: Query provider by userId
DB-->>API: ProviderStatusDto
API-->>Browser: 200 {ProviderStatusDto}
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 20
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/Modules/Providers/Tests/Unit/Application/Validators/AddDocumentRequestValidatorTests.cs (1)
46-48:⚠️ Potential issue | 🟠 MajorTranslate validation messages to Portuguese to align with language policy.
The
AddDocumentRequestValidatoruses English messages ("Document number is required", etc.), but the codebase language policy requires user-facing validation messages in Portuguese. All other validators in the codebase follow this pattern (e.g.,"Rua é obrigatória","Email é obrigatório"). Update the four.WithMessage()calls in the validator to Portuguese, and update the corresponding test assertion at line 47.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Providers/Tests/Unit/Application/Validators/AddDocumentRequestValidatorTests.cs` around lines 46 - 48, The test assertion and validator messages are using English; update them to Portuguese: change the assertion result.ShouldHaveValidationErrorFor(x => x.Number).WithErrorMessage("Document number is required") to use the Portuguese message, and update the four .WithMessage(...) calls inside the AddDocumentRequestValidator (the rules that reference Number and the other three fields) to their Portuguese equivalents (e.g., "Número do documento é obrigatório" or the project's established phrasing like "Número é obrigatório", and translate the other three messages consistently with existing validators such as "Rua é obrigatória" and "Email é obrigatório"). Ensure the test string matches the new Portuguese message used in AddDocumentRequestValidator so the test asserts the translated message.infrastructure/compose/standalone/keycloak-only.yml (1)
20-26:⚠️ Potential issue | 🟡 MinorRemove duplicate KC_HOSTNAME_STRICT_HTTPS entry.
There are two identical
KC_HOSTNAME_STRICT_HTTPS: falseentries; keep only one to avoid confusion.🧹 Suggested cleanup
- KC_HOSTNAME_STRICT_HTTPS: false🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/compose/standalone/keycloak-only.yml` around lines 20 - 26, The environment block contains a duplicate KC_HOSTNAME_STRICT_HTTPS entry; remove one of the two KC_HOSTNAME_STRICT_HTTPS: false lines so only a single KC_HOSTNAME_STRICT_HTTPS variable remains alongside KC_HOSTNAME_STRICT, KC_HTTP_ENABLED, KEYCLOAK_ADMIN and KEYCLOAK_ADMIN_PASSWORD to avoid redundancy and confusion.docs/roadmap.md (1)
10-45:⚠️ Potential issue | 🟡 MinorResolve conflicting MVP dates.
The roadmap lists multiple MVP targets (e.g., 14 Março 2026 vs 28 Março 2026). Please pick a single MVP launch date and update all references to match.
Also applies to: 2132-2173
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/roadmap.md` around lines 10 - 45, Conflicting MVP dates exist (the "MVP Target: 14/Março/2026" text and the "🎯 **28 Março 2026**: MVP Launch" entry); pick one canonical launch date (e.g., 28 Março 2026) and update every occurrence to match — specifically replace the "MVP Target: 14/Março/2026" token and any other mentions (including the "🎯 MVP Launch" entry and the repeated entries referenced by "Also applies to: 2132-2173") so the roadmap consistently shows the single chosen date.
🧹 Nitpick comments (11)
src/Shared/Authorization/Core/EPermission.cs (1)
126-144: New permission entries are well-structured and follow existing conventions.The
Display(Name)values are consistent with themodule:actionpattern used throughout. XML documentation is in Portuguese per language policy. Appending to the end of the enum preserves existing ordinal values for backward compatibility.One observation:
UsersRegisterandProvidersRegisterare documented as public/unauthenticated endpoints. If these permissions are never checked at runtime (since the endpoints areAllowAnonymous), consider adding a brief comment clarifying they exist for documentation/auditing purposes to avoid confusion.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Shared/Authorization/Core/EPermission.cs` around lines 126 - 144, Add a short clarifying comment near the UsersRegister and ProvidersRegister enum members to state these permissions correspond to public AllowAnonymous endpoints and are included only for documentation/auditing/consistency (they are not enforced at runtime); update the comments adjacent to the UsersRegister and ProvidersRegister members in the EPermission enum so reviewers and future maintainers understand these entries are intentionally present despite not being checked at authorization time.src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs (1)
64-65: Internal error messages returned directly to the client.
userResult.Error.MessageandproviderResult.Error.Messageare forwarded verbatim to the caller. If these contain internal details (stack traces, DB constraint names, Keycloak error details), this leaks implementation information. Consider returning a generic user-facing message in Portuguese and logging the details server-side.Also applies to: 95-96
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs` around lines 64 - 65, Replace returning internal error strings from userResult.Error.Message and providerResult.Error.Message with fixed, user-friendly Portuguese responses (e.g., "Ocorreu um erro ao registrar o usuário." and "Ocorreu um erro ao registrar o provedor.") and return those with Results.BadRequest; simultaneously log the full error details server-side (e.g., log userResult.Error and providerResult.Error with the existing ILogger or logger instance) so the internal message/stack is not exposed to clients; apply the same change for the checks around variables userResult and providerResult (the failure branches currently returning their .Error.Message).src/Modules/Providers/Application/Handlers/RegisterProviderCommandHandler.cs (1)
26-32: Silent idempotency on existing provider — consider logging or returning a distinct status.When a provider already exists for the
UserId, the handler silently returns the existing DTO (line 29-32) without any log or status distinction. The caller (and any monitoring) has no way to differentiate a new registration from a duplicate attempt. At minimum, add an info-level log.Proposed improvement
var existingProvider = await _providerRepository.GetByUserIdAsync(command.UserId, cancellationToken); if (existingProvider is not null) { + _logger.LogInformation("Provider already exists for user {UserId}, returning existing profile", command.UserId); return ProviderMapper.ToDto(existingProvider); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Providers/Application/Handlers/RegisterProviderCommandHandler.cs` around lines 26 - 32, The handler's idempotent path in Handle(RegisterProviderCommand, CancellationToken) returns ProviderMapper.ToDto(existingProvider) when _providerRepository.GetByUserIdAsync(...) finds an existing provider but emits no log or status; update this branch to log an info-level message (e.g., using the existing logger instance) indicating a duplicate registration attempt for the given command.UserId and, optionally, return a distinct result status or wrapper value (e.g., Result indicating AlreadyExists) so callers/monitoring can distinguish new vs duplicate registrations instead of silently returning the DTO.src/Modules/Providers/Domain/Entities/Provider.cs (1)
505-533: Consider whetherPromoteTiershould gate on provider status.The method allows tier changes on any non-deleted provider (including
PendingBasicInfo,Rejected,Suspended). If a provider is rejected or suspended, should they still be able to upgrade their tier via a Stripe webhook? This may be intentional (the XML docs say "Não há restrição de progressão"), but accepting payment for a rejected/suspended provider could create a confusing billing state.If this is deliberate, no action needed — just flagging for awareness.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Providers/Domain/Entities/Provider.cs` around lines 505 - 533, PromoteTier currently allows tier changes for any non-deleted provider; add a guard that prevents tier updates for undesirable statuses (e.g., PendingBasicInfo, Rejected, Suspended) or explicitly document why it's allowed. Locate the PromoteTier method and either (a) add a check against the provider Status property (e.g., if Status == ProviderStatus.Rejected || Status == ProviderStatus.Suspended || Status == ProviderStatus.PendingBasicInfo) and throw a ProviderDomainException, or (b) add a clear XML doc comment clarifying that tier changes are allowed regardless of Status; ensure the ProviderTierUpdatedDomainEvent and MarkAsUpdated() behavior remains consistent after the change.src/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsx (1)
71-100: Raw enum values displayed in Portuguese UI.
providerStatus.tier(e.g."Standard") andproviderStatus.status(e.g."PendingBasicInfo") render as-is. In a Portuguese-language UI, these should be mapped to localized labels (e.g. "Padrão", "Pendente"). Consider a small display-name helper similar toToDisplayNameused in the Admin web (ProviderConstants.cs).Also, while loading (
providerStatusisundefined), the fallback "Quero trabalhar" link flashes briefly before resolving. You could use theisLoadingstate from the hook to suppress the section entirely during the initial fetch.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsx` around lines 71 - 100, The UI is rendering raw enum values (providerStatus.tier and providerStatus.status) and flashing the fallback link while providerStatus is undefined; map those enums to Portuguese display labels (use or add a small helper like ToDisplayName/ProviderConstants mapping for tier and status) and replace direct usages of providerStatus.tier and providerStatus.status inside DropdownMenuLabel and the status span with the mapped display strings; additionally, suppress rendering the entire provider block while the hook is loading by checking the hook's isLoading before showing either the providerStatus branch or the fallback DropdownMenuItem (i.e., use isLoading to skip rendering until fetch completes).src/Web/MeAjudaAi.Web.Customer/hooks/use-provider-status.ts (1)
8-13:ApiResponse<T>interface is duplicated across hooks.This same shape exists in
use-register-provider.ts(asApiSuccessResponse<T>). Consider extracting a shared type to a common location (e.g.,@/types/api.ts) to keep them in sync.Based on learnings: prefer reuse of shared contracts to keep Web aligned with backend/shared code.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-provider-status.ts` around lines 8 - 13, The ApiResponse<T> interface defined in use-provider-status.ts duplicates the ApiSuccessResponse<T> type in use-register-provider.ts; extract a single shared type (e.g., ApiResponse<T>) into a new shared file like `@/types/api.ts` and update both hooks to import and use that shared type instead of their local declarations (refer to ApiResponse<T>, ApiSuccessResponse<T>, use-provider-status.ts and use-register-provider.ts to locate the duplicated definitions).src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx (1)
16-22: Document number validation allows non-digit characters.The
documentNumberfield only checks length (11–14), but doesn't enforce digits-only. A user could submit"abcdefghijk"and pass client validation. Consider adding a.regex(/^\d+$/, "Apenas números são permitidos")constraint.Proposed fix
- documentNumber: z.string().min(11, "Documento inválido").max(14, "Documento inválido"), + documentNumber: z.string().regex(/^\d{11,14}$/, "Documento inválido (apenas números, 11 a 14 dígitos)"),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx` around lines 16 - 22, The documentNumber validation in the formSchema allows non-digit characters because it only enforces length; update the documentNumber validator inside formSchema (in src/Web/.../page.tsx) to require digits-only by adding a regex constraint (e.g., append .regex(/^\d+$/, "Apenas números são permitidos") to the existing z.string().min(...).max(...)) so only numeric CPF/CNPJ values pass client validation.src/Modules/Providers/API/Endpoints/ProvidersModuleEndpoints.cs (1)
30-46: XML doc doesn't list the three new endpoints.The
<remarks>block enumerates every endpoint but omitsPOST /register,POST /me/documents, andGET /me/status. Keeping it in sync avoids confusion for future maintainers.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Providers/API/Endpoints/ProvidersModuleEndpoints.cs` around lines 30 - 46, Update the XML <remarks> block in ProvidersModuleEndpoints.cs to include the three new endpoints that are missing from the list: add entries for "POST /register", "POST /me/documents", and "GET /me/status" alongside the existing entries so the documentation enumerates every route exposed by the Providers module; modify the <remarks> text near the existing endpoints list to insert those three lines in the same style/format as the other bullet points.src/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.ts (2)
23-30: Request fires with"Bearer undefined"when session has no token.If
sessionis null oraccessTokenis missing, the header becomesAuthorization: Bearer undefined. SinceuseMutationis user-triggered, the user is likely authenticated, but a defensive guard would surface a clearer error.🛡️ Suggested guard
mutationFn: async (data: RegisterProviderRequest): Promise<ProviderDto> => { + if (!session?.accessToken) { + throw new Error("User is not authenticated"); + } const config = client.getConfig();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.ts` around lines 23 - 30, The fetch call in use-register-provider (inside the useRegisterProvider hook) may send Authorization: "Bearer undefined" when session or session.accessToken is missing; add a defensive guard before calling fetch: verify session and session.accessToken (or token variable used) and if missing either throw a clear Error (e.g., "Missing access token") or return a rejected Promise so useMutation surfaces the error, and only include the Authorization header when a valid token exists (or fail-fast instead of sending "Bearer undefined").
8-12: Duplicate response wrapper — reuseApiResponse<T>from@/types/api.ts.A local
ApiSuccessResponse<T>is defined here, but the codebase already hasApiResponse<T>intypes/api.ts(used byuse-provider-status.ts). Consolidate to avoid drift between the two definitions.♻️ Proposed fix
-import { RegisterProviderRequest, ProviderDto } from "@/types/provider"; +import { RegisterProviderRequest, ProviderDto } from "@/types/provider"; +import { ApiResponse } from "@/types/api"; - -interface ApiSuccessResponse<T> { - data: T; - isSuccess: boolean; - // ... -}Then at Line 37:
- const json = await response.json() as ApiSuccessResponse<ProviderDto>; + const json = await response.json() as ApiResponse<ProviderDto>;Based on learnings: "prefer reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.ts` around lines 8 - 12, Replace the local ApiSuccessResponse<T> declaration with the shared ApiResponse<T> type: remove the local interface in use-register-provider.ts, add an import for ApiResponse from "@/types/api" and update all references in this file (e.g., any usages in functions/hooks) to use ApiResponse<T> instead of ApiSuccessResponse<T> so the hook aligns with the existing shared contract used by use-provider-status.ts.src/Modules/Providers/API/Endpoints/Public/RegisterProviderEndpoint.cs (1)
16-24: Registration returns200 OKunconditionally — consider201 Createdfor new providers.The bootstrapper-level endpoint (
ProviderRegistrationEndpoints.cs) returnsResults.Created(...)for new registrations, while this endpoint always returns200 OKviaHandle(result). If the handler returns the existing provider on duplicate registration (confirmed byRegisterProviderCommandHandler), returning 200 for idempotent re-registrations is fine, but a201on actual creation would give the client a clearer signal.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Providers/API/Endpoints/Public/RegisterProviderEndpoint.cs` around lines 16 - 24, The endpoint always returns 200 OK via RegisterProviderAsync/Handle(result) even when a new provider was created; update the endpoint logic in Map/RegisterProviderAsync to detect when the handler indicates creation (use the result from RegisterProviderCommandHandler/RegisterProviderAsync) and return a 201 Created response (e.g., Results.Created with the resource location or route name "RegisterProvider" and the created ProviderDto) while still returning 200 OK for idempotent/duplicate registrations; ensure Response<ProviderDto> produced metadata matches both cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/roadmap.md`:
- Around line 41-43: Update the inconsistent Sprint 8B/8C entries so naming and
status match the schedule: change the line containing "🔄 **19 Fev - 4 Mar
2026**: Sprint 8B - Authentication & Onboarding Flow (EM ANDAMENTO)" to reflect
the correct name and status (either rename to "Mobile App" if that is the
canonical label or change status from "(EM ANDAMENTO)" to "(PLANEJADO)" to match
the PR date), and ensure the adjacent "⏳ **5-18 Mar 2026**: Sprint 8C - Mobile
App (React Native + Expo)" entry uses the complementary label; apply the same
alignment to the other occurrences referenced (sections around 1507-1556 and
2168-2170) so all sprint names and statuses are consistent across the document.
In `@infrastructure/compose/environments/development.yml`:
- Around line 57-58: Remove the duplicate KC_HOSTNAME_STRICT_HTTPS YAML key so
it's declared only once; locate the environment block where
KC_HOSTNAME_STRICT_HTTPS is defined (both entries currently set to false) and
delete the redundant line, leaving a single KC_HOSTNAME_STRICT_HTTPS: false
entry to avoid ambiguity.
In `@infrastructure/keycloak/realms/meajudaai-realm.dev.json`:
- Around line 96-141: The realm JSON includes an Instagram identity provider
(alias "instagram", providerId "instagram") but Keycloak 26+ disables that
broker by default; either remove the Instagram provider block from the realm
JSON or enable the broker by adding --features=instagram-broker to your Keycloak
startup args in all compose/startup manifests (keycloak-only.yml,
development.yml, testing.yml, base/keycloak.yml) so the provider becomes
functional; choose one approach and apply consistently across the deployment
manifests and the realm file.
In
`@src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs`:
- Around line 47-49: The phone sanitization currently only strips spaces, dashes
and parentheses for request.PhoneNumber and can leave characters like '+' that
break usernames; update the sanitization in ProviderRegistrationEndpoints (where
phone and username are built) to use a regex-based cleanup (e.g., replace all
non-digit characters via Regex.Replace(request.PhoneNumber, @"\D", "")) so only
digits remain, ensure the System.Text.RegularExpressions namespace is imported,
and then build username as $"provider_{phone}" from the cleaned digits.
- Line 56: The temporary password generated as Guid.NewGuid().ToString("N") in
ProviderRegistrationEndpoints (the registration payload) fails
CreateUserCommandValidator's ValidationConstants.Patterns.Password; change the
registration logic in ProviderRegistrationEndpoints.cs to either omit the
temporary password and let Keycloak/social handle credentials, or generate a
compliant temporary password (e.g., include at least one uppercase, one
lowercase, one digit and length >=8) before sending to CreateUserCommand (ensure
the same change is applied where Password is set in the registration payload so
CreateUserCommand validation passes).
- Around line 61-96: After creating the user (CreateUserCommand via
commandDispatcher.SendAsync returning userResult), add a compensation step: if
the subsequent CreateProviderCommand (createProviderCommand / providerResult)
fails, explicitly remove the previously created Keycloak user to avoid orphaned
identities; call the existing Keycloak/delete user API or dispatch a
DeleteUserCommand (using the userResult.Value!.Id or userResult.Value.Username)
in the providerResult.IsFailure branch, await that call, log success/failure,
and surface a clear error to the client; ensure this cleanup is idempotent and
any cleanup failures are logged but do not mask the original provider creation
error.
In
`@src/Modules/Providers/API/Endpoints/Public/Me/GetMyProviderStatusEndpoint.cs`:
- Around line 22-30: The OpenAPI metadata for the endpoint mapped by
Map(...).MapGet("me/status", GetMyStatusAsync) is missing a 400 response; the
GetMyStatusAsync handler returns BadRequest(...) for invalid requests, so update
the endpoint fluent chain to include a 400 response by adding
.Produces(StatusCodes.Status400BadRequest) (or
.Produces<Response<YourErrorDto>>(StatusCodes.Status400BadRequest) if you want a
typed response) alongside the existing 200 and 404 declarations so Swagger
correctly documents BadRequest responses.
In `@src/Modules/Providers/API/Endpoints/Public/Me/UploadMyDocumentEndpoint.cs`:
- Around line 55-56: The current handler in UploadMyDocumentEndpoint.cs returns
raw providerResult.Error.Message on failure; change the BadRequest to return a
localized, user-facing message instead (either a hardcoded Portuguese string
matching the endpoint style or a ValidationMessages constant). Locate the
failure check around providerResult (the if (providerResult.IsFailure) branch)
and replace BadRequest(providerResult.Error.Message) with
BadRequest(ValidationMessages.YourAppropriateConstant) or BadRequest("Não foi
possível enviar o documento. Verifique os dados e tente novamente."), keeping
the original flow and HTTP status but ensuring consistent localization.
- Around line 28-34: The endpoint mapped by Map -> app.MapPost("me/documents",
UploadMyDocumentAsync) currently calls RequireAuthorization() with no
parameters, allowing any authenticated user; update this to enforce the
provider-* role by applying an authorization policy or role requirement (e.g.,
add a policy that checks roles starting with "provider-" similar to the existing
SelfOrAdmin policy and call RequireAuthorization("ProviderPolicy") or use
RequireAuthorization with the appropriate role/ policy name), or if the generic
authenticated behavior is intentional, update the endpoint documentation to
remove the "provider-*" requirement; change should reference
UploadMyDocumentAsync and the RequireAuthorization call.
In `@src/Modules/Providers/API/Endpoints/Public/RegisterProviderEndpoint.cs`:
- Around line 63-69: Rename the endpoint-local record RegisterProviderRequest to
a distinct name (e.g., RegisterProviderApiRequest) to avoid collision with
Application.DTOs.Requests.RegisterProviderRequest; update the record declaration
and all references in this file (the endpoint handler method signature,
parameter names, model binding usages, and any local variable references) to use
RegisterProviderApiRequest so the endpoint DTO clearly differs from the
application-layer DTO.
In `@src/Modules/Providers/Application/DTOs/Requests/RegisterProviderRequest.cs`:
- Around line 14-45: The RegisterProviderRequest DTO is missing the
DocumentNumber property so incoming JSON with documentNumber will be dropped;
add a public string DocumentNumber { get; init; } = string.Empty; to the
RegisterProviderRequest record so the deserialized payload provides the value
expected by RegisterProviderCommand and the Document entity; ensure the property
name matches casing/serialization conventions (DocumentNumber) and that any
mapping code (e.g., where RegisterProviderRequest is mapped to
RegisterProviderCommand) forwards this value.
In
`@src/Modules/Providers/Application/Handlers/RegisterProviderCommandHandler.cs`:
- Around line 72-75: In the catch block inside RegisterProviderCommandHandler
replace returning the exception message to consumers: stop concatenating
ex.Message into the Error returned by Result<ProviderDto>.Failure (the Error
with code "Provider.Registration") and instead return a fixed, generic
Portuguese user-facing message (e.g., "Erro inesperado ao registrar prestador.")
while logging the full exception server-side via your logger (e.g., ILogger or
processLogger) using LogError/LogCritical and include ex and contextual data;
ensure only the generic message is sent in the Error constructor and that the
exception details remain in the server logs for diagnostics.
- Around line 38-41: The current RegisterProviderCommandHandler creates an
Address with non-semantic placeholders via new Address("Pending", "0",
"Pending", "Pending", "Pending", "00000000") which will persist as valid but
meaningless data; change this by either (A) using clear sentinel values that
match expected formats (e.g., state="XX", zip="00000-000") before creating
Address so downstream filters/clients can detect placeholders, or (B) refactor
BusinessProfile to accept a nullable Address and update
RegisterProviderCommandHandler to pass null during initial registration and only
create/populate Address when the wizard completes; update any consumers of
BusinessProfile.Address to handle null/sentinel accordingly (look for Address
constructor usage and BusinessProfile.Address property).
In
`@src/Modules/Providers/Tests/Unit/API/Endpoints/UpdateMyProviderProfileEndpointTests.cs`:
- Around line 50-53: The test constructs a ProviderDto with Guid.NewGuid() which
doesn't match the locally defined providerId used later in the assertion; update
the ProviderDto instantiation in UpdateMyProviderProfileEndpointTests to use the
existing providerId variable instead of Guid.NewGuid() so the returned
ProviderDto.Id matches the providerId asserted in the Verify (check the
ProviderDto constructor call and the providerId variable referenced in the
Verify c.ProviderId == providerId).
In
`@src/Modules/Providers/Tests/Unit/API/Endpoints/UploadMyDocumentEndpointTests.cs`:
- Around line 121-152: The test currently asserts
result.Should().BeOfType<BadRequest<Result<ProviderDto>>> but the endpoint
pipeline uses BaseEndpoint.Handle which wraps failures in Response<object>, so
update the assertion to expect BadRequest<Response<object>>; locate the test
method UploadDocumentAsync_WhenCommandFails_ShouldReturnBadRequest (and the
helper UploadDocumentMethod()) and change the expected type from
BadRequest<Result<ProviderDto>> to BadRequest<Response<object>> (and adjust any
casts or variable names accordingly) so the unit test matches the Handle()
return contract.
- Around line 46-92: The test method
UploadDocumentAsync_WithValidRequest_ShouldUploadAndReturnCreated misstates the
expected response: the endpoint returns Ok<Result<ProviderDto>> (200) because
UploadMyDocumentEndpoint calls Handle(result) without a createdRoute; either
rename the test to UploadDocumentAsync_WithValidRequest_ShouldUploadAndReturnOk
(update the method symbol in the test) to match the current behavior, or change
the endpoint's Handle invocation in UploadMyDocumentEndpoint (pass a
createdRoute string like "UploadMyDocument" and appropriate routeValues such as
the new document/provider id) so Handle(result, "UploadMyDocument", routeValues)
produces a 201 Created; pick one and update corresponding assertions/route setup
accordingly.
In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/page.tsx`:
- Around line 43-48: Replace raw enum outputs with localized labels by creating
mapping objects (e.g., statusLabels: Record<EProviderStatus,string> and
tierLabels: Record<EProviderTier,string>) that map EProviderStatus and
EProviderTier values to Portuguese strings, then use
statusLabels[providerStatus.status] and tierLabels[providerStatus.tier] in the
JSX instead of {providerStatus.status} and {providerStatus.tier}; update the
span that currently uses providerStatus.status and any other places rendering
providerStatus.tier to reference these helpers so users see friendly Portuguese
labels.
- Around line 8-10: ProviderProfilePage currently only destructures data and
isLoading from useProviderStatus, so transient API failures render the "not a
provider" view; destructure error from useProviderStatus (e.g., const { data:
providerStatus, isLoading, error } = useProviderStatus()) and add an early error
branch before the existing !providerStatus check that renders an error message
("Erro ao carregar status") with a retry action (e.g., Button that calls
window.location.reload() or revalidate) so network/API errors show a proper
error state instead of the "Você ainda não é um prestador" screen.
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-provider-status.ts`:
- Around line 46-52: The retry predicate in use-provider-status's retry function
is checking for numeric codes inside error.message but the thrown error is
created from response.statusText (e.g., "Unauthorized"), so update the error
creation (where you currently use response.statusText) to attach the numeric
status (e.g., set error.status = response.status or include the status in the
error message) and then change the retry predicate in retry to check the numeric
status (e.g., error.status === 401 || error.status === 403) instead of looking
for "401"/"403" in the message; this ensures 401/403 responses are not retried
and keeps the existing 404 handling intact.
In `@src/Web/MeAjudaAi.Web.Customer/types/provider.ts`:
- Around line 1-55: Update the frontend enums to match backend definitions:
modify EProviderType to include Cooperative and Freelancer (so values become
Individual, Company, Cooperative, Freelancer, and any other backend values),
change EProviderStatus to replace PendingDocuments with
PendingDocumentVerification, remove PendingVerification and Deleted, and keep
statuses that match backend (ensure the set matches backend Enum exactly), and
update EVerificationStatus to change UnderReview -> InProgress and Revoked ->
Suspended; verify EProviderTier remains unchanged. Locate and edit the
declarations for EProviderType, EProviderStatus, EVerificationStatus (and
confirm ProviderStatusDto/ProviderDto types still reference the updated enum
names) so the frontend enum string values exactly match the backend Enum
definitions.
---
Outside diff comments:
In `@docs/roadmap.md`:
- Around line 10-45: Conflicting MVP dates exist (the "MVP Target:
14/Março/2026" text and the "🎯 **28 Março 2026**: MVP Launch" entry); pick one
canonical launch date (e.g., 28 Março 2026) and update every occurrence to match
— specifically replace the "MVP Target: 14/Março/2026" token and any other
mentions (including the "🎯 MVP Launch" entry and the repeated entries
referenced by "Also applies to: 2132-2173") so the roadmap consistently shows
the single chosen date.
In `@infrastructure/compose/standalone/keycloak-only.yml`:
- Around line 20-26: The environment block contains a duplicate
KC_HOSTNAME_STRICT_HTTPS entry; remove one of the two KC_HOSTNAME_STRICT_HTTPS:
false lines so only a single KC_HOSTNAME_STRICT_HTTPS variable remains alongside
KC_HOSTNAME_STRICT, KC_HTTP_ENABLED, KEYCLOAK_ADMIN and KEYCLOAK_ADMIN_PASSWORD
to avoid redundancy and confusion.
In
`@src/Modules/Providers/Tests/Unit/Application/Validators/AddDocumentRequestValidatorTests.cs`:
- Around line 46-48: The test assertion and validator messages are using
English; update them to Portuguese: change the assertion
result.ShouldHaveValidationErrorFor(x => x.Number).WithErrorMessage("Document
number is required") to use the Portuguese message, and update the four
.WithMessage(...) calls inside the AddDocumentRequestValidator (the rules that
reference Number and the other three fields) to their Portuguese equivalents
(e.g., "Número do documento é obrigatório" or the project's established phrasing
like "Número é obrigatório", and translate the other three messages consistently
with existing validators such as "Rua é obrigatória" and "Email é obrigatório").
Ensure the test string matches the new Portuguese message used in
AddDocumentRequestValidator so the test asserts the translated message.
---
Nitpick comments:
In
`@src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs`:
- Around line 64-65: Replace returning internal error strings from
userResult.Error.Message and providerResult.Error.Message with fixed,
user-friendly Portuguese responses (e.g., "Ocorreu um erro ao registrar o
usuário." and "Ocorreu um erro ao registrar o provedor.") and return those with
Results.BadRequest; simultaneously log the full error details server-side (e.g.,
log userResult.Error and providerResult.Error with the existing ILogger or
logger instance) so the internal message/stack is not exposed to clients; apply
the same change for the checks around variables userResult and providerResult
(the failure branches currently returning their .Error.Message).
In `@src/Modules/Providers/API/Endpoints/ProvidersModuleEndpoints.cs`:
- Around line 30-46: Update the XML <remarks> block in
ProvidersModuleEndpoints.cs to include the three new endpoints that are missing
from the list: add entries for "POST /register", "POST /me/documents", and "GET
/me/status" alongside the existing entries so the documentation enumerates every
route exposed by the Providers module; modify the <remarks> text near the
existing endpoints list to insert those three lines in the same style/format as
the other bullet points.
In `@src/Modules/Providers/API/Endpoints/Public/RegisterProviderEndpoint.cs`:
- Around line 16-24: The endpoint always returns 200 OK via
RegisterProviderAsync/Handle(result) even when a new provider was created;
update the endpoint logic in Map/RegisterProviderAsync to detect when the
handler indicates creation (use the result from
RegisterProviderCommandHandler/RegisterProviderAsync) and return a 201 Created
response (e.g., Results.Created with the resource location or route name
"RegisterProvider" and the created ProviderDto) while still returning 200 OK for
idempotent/duplicate registrations; ensure Response<ProviderDto> produced
metadata matches both cases.
In
`@src/Modules/Providers/Application/Handlers/RegisterProviderCommandHandler.cs`:
- Around line 26-32: The handler's idempotent path in
Handle(RegisterProviderCommand, CancellationToken) returns
ProviderMapper.ToDto(existingProvider) when
_providerRepository.GetByUserIdAsync(...) finds an existing provider but emits
no log or status; update this branch to log an info-level message (e.g., using
the existing logger instance) indicating a duplicate registration attempt for
the given command.UserId and, optionally, return a distinct result status or
wrapper value (e.g., Result indicating AlreadyExists) so callers/monitoring can
distinguish new vs duplicate registrations instead of silently returning the
DTO.
In `@src/Modules/Providers/Domain/Entities/Provider.cs`:
- Around line 505-533: PromoteTier currently allows tier changes for any
non-deleted provider; add a guard that prevents tier updates for undesirable
statuses (e.g., PendingBasicInfo, Rejected, Suspended) or explicitly document
why it's allowed. Locate the PromoteTier method and either (a) add a check
against the provider Status property (e.g., if Status == ProviderStatus.Rejected
|| Status == ProviderStatus.Suspended || Status ==
ProviderStatus.PendingBasicInfo) and throw a ProviderDomainException, or (b) add
a clear XML doc comment clarifying that tier changes are allowed regardless of
Status; ensure the ProviderTierUpdatedDomainEvent and MarkAsUpdated() behavior
remains consistent after the change.
In `@src/Shared/Authorization/Core/EPermission.cs`:
- Around line 126-144: Add a short clarifying comment near the UsersRegister and
ProvidersRegister enum members to state these permissions correspond to public
AllowAnonymous endpoints and are included only for
documentation/auditing/consistency (they are not enforced at runtime); update
the comments adjacent to the UsersRegister and ProvidersRegister members in the
EPermission enum so reviewers and future maintainers understand these entries
are intentionally present despite not being checked at authorization time.
In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx`:
- Around line 16-22: The documentNumber validation in the formSchema allows
non-digit characters because it only enforces length; update the documentNumber
validator inside formSchema (in src/Web/.../page.tsx) to require digits-only by
adding a regex constraint (e.g., append .regex(/^\d+$/, "Apenas números são
permitidos") to the existing z.string().min(...).max(...)) so only numeric
CPF/CNPJ values pass client validation.
In `@src/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsx`:
- Around line 71-100: The UI is rendering raw enum values (providerStatus.tier
and providerStatus.status) and flashing the fallback link while providerStatus
is undefined; map those enums to Portuguese display labels (use or add a small
helper like ToDisplayName/ProviderConstants mapping for tier and status) and
replace direct usages of providerStatus.tier and providerStatus.status inside
DropdownMenuLabel and the status span with the mapped display strings;
additionally, suppress rendering the entire provider block while the hook is
loading by checking the hook's isLoading before showing either the
providerStatus branch or the fallback DropdownMenuItem (i.e., use isLoading to
skip rendering until fetch completes).
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-provider-status.ts`:
- Around line 8-13: The ApiResponse<T> interface defined in
use-provider-status.ts duplicates the ApiSuccessResponse<T> type in
use-register-provider.ts; extract a single shared type (e.g., ApiResponse<T>)
into a new shared file like `@/types/api.ts` and update both hooks to import and
use that shared type instead of their local declarations (refer to
ApiResponse<T>, ApiSuccessResponse<T>, use-provider-status.ts and
use-register-provider.ts to locate the duplicated definitions).
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.ts`:
- Around line 23-30: The fetch call in use-register-provider (inside the
useRegisterProvider hook) may send Authorization: "Bearer undefined" when
session or session.accessToken is missing; add a defensive guard before calling
fetch: verify session and session.accessToken (or token variable used) and if
missing either throw a clear Error (e.g., "Missing access token") or return a
rejected Promise so useMutation surfaces the error, and only include the
Authorization header when a valid token exists (or fail-fast instead of sending
"Bearer undefined").
- Around line 8-12: Replace the local ApiSuccessResponse<T> declaration with the
shared ApiResponse<T> type: remove the local interface in
use-register-provider.ts, add an import for ApiResponse from "@/types/api" and
update all references in this file (e.g., any usages in functions/hooks) to use
ApiResponse<T> instead of ApiSuccessResponse<T> so the hook aligns with the
existing shared contract used by use-provider-status.ts.
src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs
Outdated
Show resolved
Hide resolved
src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs
Outdated
Show resolved
Hide resolved
src/Modules/Providers/Tests/Unit/API/Endpoints/UploadMyDocumentEndpointTests.cs
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/page.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/page.tsx
Outdated
Show resolved
Hide resolved
…ges, backend API, and a comprehensive development environment.
There was a problem hiding this comment.
Actionable comments posted: 18
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/Modules/Providers/Application/Validators/AddDocumentRequestValidator.cs (1)
25-27:⚠️ Potential issue | 🟡 Minor
DocumentTypevalidation message is still in English — inconsistent with the localization applied in this same PR.The literal portion of the message on line 27 must be Portuguese per the codebase language policy (user-facing validation messages → Portuguese). Based on learnings, the project enforces Portuguese for all user-facing API/validation messages.
🌐 Proposed fix
- .WithMessage($"DocumentType must be a valid document type. {EnumExtensions.GetValidValuesDescription<EDocumentType>()}"); + .WithMessage($"DocumentType deve ser um tipo de documento válido. {EnumExtensions.GetValidValuesDescription<EDocumentType>()}");Based on learnings: "User-facing validation messages and API responses shown to the frontend must be in Portuguese."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Providers/Application/Validators/AddDocumentRequestValidator.cs` around lines 25 - 27, The validation message for DocumentType is currently English; update the literal text in the RuleFor(...) chain that uses .WithMessage(...) so the user-facing portion is in Portuguese while preserving the dynamic enum description from EnumExtensions.GetValidValuesDescription<EDocumentType>(); specifically modify the .WithMessage call that follows RuleFor(x => x.DocumentType).Must(BeValidDocumentType) to a Portuguese string (e.g., "DocumentType deve ser um tipo de documento válido.") concatenated with EnumExtensions.GetValidValuesDescription<EDocumentType>() so only the static text is translated and the enum helper remains intact.src/Modules/Providers/Tests/Unit/Application/Validators/AddDocumentRequestValidatorTests.cs (1)
127-128:⚠️ Potential issue | 🟡 MinorTranslate the
DocumentTypevalidation message to Portuguese in the validator.The validation message in
AddDocumentRequestValidator.cs(line 27) is in English while all other validation messages in this validator are in Portuguese. Per the codebase language policy, user-facing validation messages must be in Portuguese.Update
AddDocumentRequestValidator.csline 27 to translate the message to Portuguese, for example:.WithMessage($"DocumentType deve ser um tipo de documento válido. Valores válidos de EDocumentType: {EnumExtensions.GetValidValuesDescription<EDocumentType>()}");Once the validator is updated, update the test assertion at line 128 to match the Portuguese message.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Providers/Tests/Unit/Application/Validators/AddDocumentRequestValidatorTests.cs` around lines 127 - 128, Translate the DocumentType validation message in AddDocumentRequestValidator (update the message at the rule that currently uses the English text) to Portuguese, e.g. use .WithMessage($"DocumentType deve ser um tipo de documento válido. Valores válidos de EDocumentType: {EnumExtensions.GetValidValuesDescription<EDocumentType>()}"); then update the unit test assertion in AddDocumentRequestValidatorTests (the .ShouldHaveValidationErrorFor(x => x.DocumentType) expectation) to assert the new Portuguese message instead of the English text so the test matches the validator change.
🧹 Nitpick comments (5)
infrastructure/compose/environments/development.yml (1)
59-64: OAuth env vars silently pass empty strings when unset.
${VAR}and${VAR:-}are equivalent in Docker Compose — both resolve to empty string if the variable is not defined. Since the realm JSON reads these values directly to configure identity providerclientId/clientSecret, an unset variable produces a silently misconfigured broker at startup (login via that provider will fail with no Keycloak startup warning).For a dev environment where OAuth is genuinely optional, document them clearly in
.env.exampleas optional (which the AI summary says is already being done in that file). If they are required for the feature under test, prefer:?to fail fast.♻️ Explicit optional pattern
- GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} - GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} - FACEBOOK_APP_ID: ${FACEBOOK_APP_ID} - FACEBOOK_APP_SECRET: ${FACEBOOK_APP_SECRET} - INSTAGRAM_CLIENT_ID: ${INSTAGRAM_CLIENT_ID} - INSTAGRAM_CLIENT_SECRET: ${INSTAGRAM_CLIENT_SECRET} + GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-} + GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-} + FACEBOOK_APP_ID: ${FACEBOOK_APP_ID:-} + FACEBOOK_APP_SECRET: ${FACEBOOK_APP_SECRET:-} + INSTAGRAM_CLIENT_ID: ${INSTAGRAM_CLIENT_ID:-} + INSTAGRAM_CLIENT_SECRET: ${INSTAGRAM_CLIENT_SECRET:-}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/compose/environments/development.yml` around lines 59 - 64, The environment variables GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, FACEBOOK_APP_ID, FACEBOOK_APP_SECRET, INSTAGRAM_CLIENT_ID and INSTAGRAM_CLIENT_SECRET currently silently resolve to empty strings when unset; update the development compose env entries to either (a) use the fail-fast expansion pattern (VAR:?error) for any variables required for tests so the container fails on missing values, or (b) explicitly mark them as optional in `.env.example` and keep plain expansion if OAuth is optional for dev. Locate the variables in the compose file (the keys listed above) and apply the chosen pattern consistently so missing credentials either error immediately or are clearly documented as optional.src/Modules/Providers/API/Endpoints/Public/Me/GetMyProviderStatusEndpoint.cs (1)
46-47:result.Error.Messagemay expose non-localized internal error text.If the query handler returns an English or technical error message, it will be forwarded verbatim to the client. Consider returning a generic Portuguese message instead, consistent with line 50's pattern.
Proposed fix
if (result.IsFailure) - return BadRequest(result.Error.Message); + return BadRequest("Não foi possível consultar o status do prestador. Tente novamente.");Based on learnings, user-facing validation messages and API responses shown to the frontend must be in Portuguese.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Providers/API/Endpoints/Public/Me/GetMyProviderStatusEndpoint.cs` around lines 46 - 47, The BadRequest currently returns raw result.Error.Message which may expose internal/non-localized text; update the failure branch in GetMyProviderStatusEndpoint (the check on result.IsFailure and the BadRequest return) to return a generic Portuguese message instead (matching the pattern used around line 50) rather than forwarding result.Error.Message — keep result.Error for logging if needed but send a fixed Portuguese error string to the client.src/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsx (1)
14-18: Consolidate duplicatelucide-reactimports.
Briefcasecan be added to the existing line 14 import.♻️ Proposed fix
-import { User, LogOut } from "lucide-react"; +import { User, LogOut, Briefcase } from "lucide-react"; import Link from "next/link"; import { useProviderStatus } from "@/hooks/use-provider-status"; import { EProviderStatus, PROVIDER_STATUS_LABELS, PROVIDER_TIER_LABELS } from "@/types/provider"; -import { Briefcase } from "lucide-react";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsx` around lines 14 - 18, Consolidate duplicate lucide-react imports in user-menu.tsx by removing the second import of Briefcase and adding Briefcase to the first import that currently imports User and LogOut; update the import line that references User and LogOut to include Briefcase so there is only a single lucide-react import statement.src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx (1)
96-117:EProviderType.CooperativeandEProviderType.Freelancerare not selectable in the UI.The form schema uses
z.nativeEnum(EProviderType)which allows all 4 types, but the selector only renders buttons forIndividualandCompany. If addingCooperative/Freelancersupport is on the roadmap, consider tracking it as a TODO.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx` around lines 96 - 117, The provider-type selector only renders EProviderType.Individual and EProviderType.Company, leaving EProviderType.Cooperative and EProviderType.Freelancer unreachable; update the UI in the render that uses field (inside FormControl/FormItem) to include two additional Button controls for EProviderType.Cooperative and EProviderType.Freelancer with the same pattern (variant={field.value === EProviderType.Cooperative ? "primary" : "outline"} and onClick={() => field.onChange(EProviderType.Cooperative)} and likewise for Freelancer), or if those types are intentionally not supported yet add a clear TODO comment near the EProviderType usage indicating planned support and ensure the form schema/validation remains consistent with the UI.src/Web/MeAjudaAi.Web.Customer/types/api.ts (1)
1-6: Updatedatafield toT | nullto match backend response semantics.The backend
Response<T>definesDataasT?(nullable), but the frontendApiResponse<T>declaresdata: T(non-nullable). This type mismatch means the TypeScript interface doesn't reflect that the backend can return null data (e.g.,new Response<object>(null, 400, message)). Update todata: T | nullto align the frontend type contract with the actual backend behavior.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/types/api.ts` around lines 1 - 6, Update the ApiResponse<T> interface so its data property allows null values to match the backend Response<T> semantics: change the declaration of data on the ApiResponse<T> interface to be nullable (T | null) and update any usages that assume non-null data to handle the null case; locate the ApiResponse<T> interface definition in src/Web/MeAjudaAi.Web.Customer/types/api.ts and adjust the data type accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/roadmap.md`:
- Line 10: The document has inconsistent MVP dates: the header shows "MVP
Target: 28/Março/2026" while three body occurrences still read "14 de Março de
2026"; pick the canonical date (either keep 28/Março/2026 or revert header to 14
de Março de 2026) and update all instances to match—specifically search for and
change the phrases "Data de MVP atualizada para 14 de Março de 2026...", "**MVP
Launch Target**: 14 de Março de 2026 🎯", and "PRONTO PARA LAUNCH EM 14 DE MARÇO
DE 2026" to the chosen canonical date so the header and body are consistent.
- Around line 78-94: The document now contains a direct contradiction: the
"Migração Admin Portal: Blazor WASM → React?" block (heading text) declares
"Manter Blazor" while Sprint 8D ("Admin Portal Migration: Blazor WASM → React")
remains listed as planned in multiple places (phase-2 summary and the "### ⏳
Sprint 8D - Admin Portal Migration (Novo)" section and Sumário Executivo).
Resolve by removing or deprecating Sprint 8D references: either delete the
Sprint 8D entries from the phase-2 summary, sprint detail, and Sumário
Executivo, or add a clear deprecation note in each Sprint 8D occurrence
referencing the new decision (e.g., mark "Sprint 8D" as Deprecated/Cancelled and
point to "Migração Admin Portal: Blazor WASM → React?" decision), and update any
status icons/text to match "Manter Blazor". Ensure all occurrences of "Sprint
8D" and the exact heading "Migração Admin Portal: Blazor WASM → React?" are
updated so the document is consistent.
- Line 5049: The markdown list entry on the line containing "Documentação:
<https://viacep.com.br/>- **BrasilApi CEP**" is missing a newline between two
items; split that fused line into two list items by inserting a newline (or add
a line break) so the ViaCEP link and the "**BrasilApi CEP** - Lookup de CEP
(fallback)" entry are on separate lines, restoring a valid bullet list entry.
In `@infrastructure/compose/environments/development.yml`:
- Line 65: The docker-compose command currently forces Keycloak to start with
"--features=instagram-broker" (see command: ["start-dev", "--import-realm",
"--features=instagram-broker"]) which is deprecated; update this by either
removing the "--features=instagram-broker" flag and documenting the dependency
on the pinned image "26.0.2" (so future Keycloak bumps are tracked) or add a
guarded approach: keep the pinned image for development but add a clear comment
in the command/compose file about deprecation and a migration note to replace
Instagram broker usage in the imported realm (--import-realm) with a generic
OIDC/OAuth2 client configuration for Instagram if Keycloak removes the broker;
ensure the compose change references "start-dev" and the import realm behavior
so reviewers know to update realm JSON and CI when upgrading Keycloak.
- Line 68: The docker-compose mapping "8443:8443" exposes HTTPS but start-dev
does not enable TLS given KC_HTTP_ENABLED: true and no KC_HTTPS_CERTIFICATE_*
variables; either remove the "8443:8443" port mapping from the Keycloak service
to avoid a misleading binding, or add the required TLS environment variables
(the KC_HTTPS_CERTIFICATE_* family — e.g., cert/key/chain vars used by Keycloak)
and ensure KC_HTTP_ENABLED is set appropriately so HTTPS actually starts; update
the service env block accordingly and remove the port mapping if you choose not
to configure TLS.
In `@infrastructure/compose/standalone/keycloak-only.yml`:
- Around line 24-25: There is a duplicate environment variable entry
KC_HOSTNAME_STRICT_HTTPS in the compose YAML; remove the redundant declaration
so KC_HOSTNAME_STRICT_HTTPS: false appears only once in the Keycloak service
environment block, ensuring no duplicated keys remain (search for
KC_HOSTNAME_STRICT_HTTPS to locate both occurrences).
In
`@src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs`:
- Line 56: The Password field in ProviderRegistrationEndpoints (currently set to
the hardcoded string "TempPassword123!") is a security risk; replace it with a
per-registration generated compliant temporary password (or mark the account
credential-less for social/login-only flows). Implement a helper like
GenerateTemporaryPassword() and call it when constructing the Keycloak user
payload instead of the static literal; ensure the generated value satisfies
Keycloak password policy (length, upper/lower/digit/special) and set the account
to require password change or to be credential-less depending on the
social-login flow. Update any relevant code in ProviderRegistrationEndpoints
where Password is assigned and add tests or validation to confirm generated
passwords meet policy.
- Around line 99-113: The compensation catch is silently swallowing failures
when attempting to delete the created Keycloak user; update the endpoint to
accept or resolve an ILogger (inject or add as a parameter) and in the catch
block log the exception with context (include userResult.Value!.Id and that
DeleteUserCommand via commandDispatcher.SendAsync failed) so compensation
failures are recorded; keep the existing try/catch/return flow but replace the
empty catch with a logged error using the ILogger instance.
In `@src/Modules/Providers/API/Endpoints/Public/RegisterProviderEndpoint.cs`:
- Around line 23-25: Remove the incorrect 200 OK response annotation from the
endpoint's OpenAPI metadata: in RegisterProviderEndpoint (the chained .Produces
calls that currently include
.Produces<Response<ProviderDto>>(StatusCodes.Status200OK)), delete that
.Produces(...) entry so the endpoint only declares 201 Created and 400
BadRequest (matching the handler which returns Results.Created(...) or an
error). Ensure only the
.Produces<Response<ProviderDto>>(StatusCodes.Status201Created) and
.Produces<Response<object>>(StatusCodes.Status400BadRequest) calls remain.
In
`@src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs`:
- Around line 63-65: The catch in RegisterProviderCommandHandler currently
returns DomainException.Message to the API (Result<ProviderDto>.Failure(new
Error(ex.Message, 400))), which leaks internal English domain text; change this
to return a generic Portuguese error message (e.g., "Operação inválida" or
"Requisição inválida") in the Error sent to the client and move the detailed
ex.Message into a server-side log; update the catch(DomainException ex) block to
call your logging utility (or ILogger) to log ex and then return
Result<ProviderDto>.Failure(new Error("<mensagem genérica em Português>", 400))
so clients see Portuguese text while internals keep the detailed exception in
logs.
- Around line 63-65: Add the missing using for DomainException (add using
MeAjudaAi.Shared.Exceptions;) and do not expose ex.Message to the client;
replace the return in the DomainException catch (the
Result<ProviderDto>.Failure(new Error(...)) call) to use a generic Portuguese
error message consistent with the pattern used on line 70 (e.g. "Erro ao
processar a requisição" or the exact generic message used elsewhere) and keep
the 400 status code.
In
`@src/Modules/Providers/Tests/Unit/API/Endpoints/UpdateMyProviderProfileEndpointTests.cs`:
- Around line 49-50: Remove the duplicated comment line "// Setup Query to
return ProviderId" in the test file UpdateMyProviderProfileEndpointTests (keep a
single, clear comment where the query is being set up) so there's only one
explanatory comment near the query setup; locate the duplicate in the test class
UpdateMyProviderProfileEndpointTests and delete the extra comment to avoid
redundancy.
In
`@src/Modules/Providers/Tests/Unit/Application/Validators/AddDocumentRequestValidatorTests.cs`:
- Around line 46-47: The test file AddDocumentRequestValidatorTests contains a
duplicated inline comment "// Assert" in the test method(s); remove the
redundant comment so only one "// Assert" remains. Locate the duplicate in the
AddDocumentRequestValidatorTests class (search for "// Assert" occurrences) and
delete the extra line, keeping a single assertion comment above the Assert
block.
In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx`:
- Around line 16-25: The formSchema currently allows any 11–14 digit
documentNumber regardless of type; update formSchema to enforce exact lengths by
replacing the current min/max on documentNumber with a digits-only check (keep
the .regex(/^\d+$/,"Apenas números são permitidos")) and add a .superRefine on
formSchema that reads the type (EProviderType) and documentNumber and calls
ctx.addIssue when the length mismatches (if type === EProviderType.Individual
require length === 11, if type === EProviderType.Company require length === 14),
targeting the "documentNumber" path with appropriate messages like "CPF deve ter
11 dígitos" / "CNPJ deve ter 14 dígitos".
In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/page.tsx`:
- Around line 84-94: The step-2 rendering currently treats any
non-PendingBasicInfo status as "Pendente", causing Active and
PendingDocumentVerification providers to appear dimmed; update the logic around
providerStatus.status (used in the container className, the badge styling, and
the conditional that renders the <Button> vs <p>) to explicitly handle at least
three states: EProviderStatus.PendingBasicInfo (step 2 locked/disabled "Em
breve"), the state where address is the current pending step (if you have an
EProviderStatus.PendingAddress value handle it here to make step 2
active/editable), and EProviderStatus.PendingDocumentVerification or
EProviderStatus.Active (treat as completed/accessible for step 2 and render the
enabled UI instead of the "Pendente" paragraph). Modify the ternary checks
referencing providerStatus.status and EProviderStatus to branch on these
explicit enums so Active providers no longer see step 2 as dimmed.
- Around line 1-4: This page is missing the "use client" directive causing
client-side hooks like useProviderStatus (which internally uses useQuery and
useSession) to run in a Server Component; add the top-of-file directive "use
client" as the first line in this file
(src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/page.tsx) so the
component that imports and calls useProviderStatus, and uses Button, Link,
EProviderStatus, PROVIDER_STATUS_LABELS and PROVIDER_TIER_LABELS, is treated as
a Client Component and the hooks run correctly.
In `@src/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsx`:
- Around line 69-104: The first DropdownMenuSeparator is rendered
unconditionally and causes two adjacent separators when isLoadingProvider is
true and no provider content is shown; update the JSX around the first
DropdownMenuSeparator to render it only when providerStatus is truthy (or when
not isLoadingProvider and providerStatus exists) so separators flank actual
content, referencing the isLoadingProvider and providerStatus checks and the
DropdownMenuSeparator/DropdownMenuLabel/DropdownMenuItem blocks to locate the
spot to gate.
In `@src/Web/MeAjudaAi.Web.Customer/types/provider.ts`:
- Around line 1-6: The EProviderType enum is missing the backend's undefined
value; update the EProviderType enum by adding a new member None = "None"
alongside Individual, Company, Cooperative, and Freelancer so the frontend
matches the backend's provider type definitions and can represent the
default/undefined state; ensure any code that constructs or compares
EProviderType (e.g., type checks, switch statements) can handle
EProviderType.None appropriately.
---
Outside diff comments:
In `@src/Modules/Providers/Application/Validators/AddDocumentRequestValidator.cs`:
- Around line 25-27: The validation message for DocumentType is currently
English; update the literal text in the RuleFor(...) chain that uses
.WithMessage(...) so the user-facing portion is in Portuguese while preserving
the dynamic enum description from
EnumExtensions.GetValidValuesDescription<EDocumentType>(); specifically modify
the .WithMessage call that follows RuleFor(x =>
x.DocumentType).Must(BeValidDocumentType) to a Portuguese string (e.g.,
"DocumentType deve ser um tipo de documento válido.") concatenated with
EnumExtensions.GetValidValuesDescription<EDocumentType>() so only the static
text is translated and the enum helper remains intact.
In
`@src/Modules/Providers/Tests/Unit/Application/Validators/AddDocumentRequestValidatorTests.cs`:
- Around line 127-128: Translate the DocumentType validation message in
AddDocumentRequestValidator (update the message at the rule that currently uses
the English text) to Portuguese, e.g. use .WithMessage($"DocumentType deve ser
um tipo de documento válido. Valores válidos de EDocumentType:
{EnumExtensions.GetValidValuesDescription<EDocumentType>()}"); then update the
unit test assertion in AddDocumentRequestValidatorTests (the
.ShouldHaveValidationErrorFor(x => x.DocumentType) expectation) to assert the
new Portuguese message instead of the English text so the test matches the
validator change.
---
Duplicate comments:
In `@docs/roadmap.md`:
- Around line 41-43: Sprint 8B still shows the wrong name and a status date
mismatch: find the Sprint 8B entry in docs/roadmap.md (the table row that
currently reads "Mobile App (React Native + Expo)" and the heading
"Authentication & Onboarding Flow") and change the table row to match the
heading by renaming it to "Authentication & Onboarding Flow"; also align the
status "(EM ANDAMENTO)" with the sprint start date by either shifting the sprint
start to 18 Fev or removing/preponing the status so it does not appear before
the sprint's listed start (ensure the start date and the status date are
consistent).
In `@infrastructure/compose/standalone/keycloak-only.yml`:
- Line 36: Duplicate host port mapping "8443:8443" in keycloak-only.yml can
conflict with development.yml; remove or change the host-side port binding for
the Keycloak service by editing the port mapping string "8443:8443" in the
keycloak service section—either remove the host port (leave only container
port), change to a unique host port (e.g., "8444:8443"), or document that it
must not be run alongside development.yml; ensure the change is applied to the
ports entry for the Keycloak service in standalone/keycloak-only.yml.
- Around line 27-32: The OAuth env-vars (GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET,
FACEBOOK_APP_ID, FACEBOOK_APP_SECRET, INSTAGRAM_CLIENT_ID,
INSTAGRAM_CLIENT_SECRET) in the compose file are silently set to empty when
unset; update the compose service environment entries to enforce presence (e.g.,
use the shell-style mandatory expansion or add runtime validation) so the
container fails fast instead of starting misconfigured identity
brokers—specifically replace each ${VAR} with a mandatory check (e.g., ${VAR:?}
or equivalent) or add a startup validation script that checks these variables
and logs/exit if any of GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, FACEBOOK_APP_ID,
FACEBOOK_APP_SECRET, INSTAGRAM_CLIENT_ID, INSTAGRAM_CLIENT_SECRET are missing.
- Line 33: The compose command includes the deprecated flag
"--features=instagram-broker" (in the command array
["start-dev","--import-realm","--features=instagram-broker"]); remove that
deprecated feature flag or replace it with the correct, supported feature name
used elsewhere (mirror the fix applied in development.yml) so the command
becomes start-dev with only valid flags (e.g., ["start-dev","--import-realm"] or
the updated feature token); ensure you update the same command occurrence in
keycloak-only.yml.
---
Nitpick comments:
In `@infrastructure/compose/environments/development.yml`:
- Around line 59-64: The environment variables GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET, FACEBOOK_APP_ID, FACEBOOK_APP_SECRET, INSTAGRAM_CLIENT_ID
and INSTAGRAM_CLIENT_SECRET currently silently resolve to empty strings when
unset; update the development compose env entries to either (a) use the
fail-fast expansion pattern (VAR:?error) for any variables required for tests so
the container fails on missing values, or (b) explicitly mark them as optional
in `.env.example` and keep plain expansion if OAuth is optional for dev. Locate
the variables in the compose file (the keys listed above) and apply the chosen
pattern consistently so missing credentials either error immediately or are
clearly documented as optional.
In
`@src/Modules/Providers/API/Endpoints/Public/Me/GetMyProviderStatusEndpoint.cs`:
- Around line 46-47: The BadRequest currently returns raw result.Error.Message
which may expose internal/non-localized text; update the failure branch in
GetMyProviderStatusEndpoint (the check on result.IsFailure and the BadRequest
return) to return a generic Portuguese message instead (matching the pattern
used around line 50) rather than forwarding result.Error.Message — keep
result.Error for logging if needed but send a fixed Portuguese error string to
the client.
In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx`:
- Around line 96-117: The provider-type selector only renders
EProviderType.Individual and EProviderType.Company, leaving
EProviderType.Cooperative and EProviderType.Freelancer unreachable; update the
UI in the render that uses field (inside FormControl/FormItem) to include two
additional Button controls for EProviderType.Cooperative and
EProviderType.Freelancer with the same pattern (variant={field.value ===
EProviderType.Cooperative ? "primary" : "outline"} and onClick={() =>
field.onChange(EProviderType.Cooperative)} and likewise for Freelancer), or if
those types are intentionally not supported yet add a clear TODO comment near
the EProviderType usage indicating planned support and ensure the form
schema/validation remains consistent with the UI.
In `@src/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsx`:
- Around line 14-18: Consolidate duplicate lucide-react imports in user-menu.tsx
by removing the second import of Briefcase and adding Briefcase to the first
import that currently imports User and LogOut; update the import line that
references User and LogOut to include Briefcase so there is only a single
lucide-react import statement.
In `@src/Web/MeAjudaAi.Web.Customer/types/api.ts`:
- Around line 1-6: Update the ApiResponse<T> interface so its data property
allows null values to match the backend Response<T> semantics: change the
declaration of data on the ApiResponse<T> interface to be nullable (T | null)
and update any usages that assume non-null data to handle the null case; locate
the ApiResponse<T> interface definition in
src/Web/MeAjudaAi.Web.Customer/types/api.ts and adjust the data type
accordingly.
src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/page.tsx
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/page.tsx
Outdated
Show resolved
Hide resolved
…guration and implement provider registration flow.
…dleware for protected routes.
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
infrastructure/compose/standalone/keycloak-only.yml (1)
1-14:⚠️ Potential issue | 🟡 MinorStale header comments no longer reflect the file's actual behaviour.
Three parts of the header are now misleading:
- Line 2 — "Minimal setup with embedded H2 database" contradicts the new
KC_DB: postgresconfig (lines 21-26).- Lines 4-5, 9-10 — Instruct callers to
export KEYCLOAK_ADMIN_PASSWORD/KEYCLOAK_ADMINbefore running, but both are now hardcoded in the file body (lines 32-33), making these instructions incorrect.- Lines 6, 11 — Reference
KEYCLOAK_PORTas an optional variable, but the port is now fixed at8080(line 43).📝 Proposed header update
-# Standalone Keycloak for development -# Minimal setup with embedded H2 database for quick testing -# -# REQUIRED: Set KEYCLOAK_ADMIN_PASSWORD before running -# OPTIONAL: Set KEYCLOAK_ADMIN (defaults to 'admin', consider using a custom username) -# OPTIONAL: Set KEYCLOAK_PORT (defaults to 8081 to avoid conflicts with development.yml) -# -# Usage: -# export KEYCLOAK_ADMIN_PASSWORD=$(openssl rand -base64 32) -# export KEYCLOAK_ADMIN="meajudaai_admin" # Recommended: avoid 'admin' -# export KEYCLOAK_PORT=8081 # Avoid port conflicts -# docker compose -f standalone/keycloak-only.yml up -d -# -# Or use .env file with secure credentials +# Standalone Keycloak for development +# Uses the shared PostgreSQL instance (host.docker.internal:5432) — Aspire or another +# compose stack must already be running. +# +# REQUIRED: Provide social OAuth credentials in a .env file or as environment variables: +# GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET +# FACEBOOK_APP_ID, FACEBOOK_APP_SECRET +# INSTAGRAM_CLIENT_ID, INSTAGRAM_CLIENT_SECRET +# DB_PASSWORD (defaults to 'postgres') +# +# Usage: +# docker compose -f standalone/keycloak-only.yml up -d🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/compose/standalone/keycloak-only.yml` around lines 1 - 14, The header comments are stale and contradict the compose file: update the top-of-file documentation to state that Keycloak is configured to use Postgres (KC_DB: postgres), that KEYCLOAK_ADMIN and KEYCLOAK_ADMIN_PASSWORD are currently hardcoded in the compose file (remove instructions to export them or note they can be overridden via environment or an .env file), and that the service port is fixed to 8080 (not an optional KEYCLOAK_PORT); edit the header to accurately describe KC_DB, KC_* credentials handling, and the fixed port behavior so readers aren’t misled by the previous H2/variable instructions.
🧹 Nitpick comments (10)
src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx (1)
5-5:AvatarPropsstill extendsComponentProps<"img">but all inherited props are silently discarded.The interface advertises the full set of native
<img>props (e.g.onError,loading,style,data-*), but the component only destructures five named props and the rest are dropped. This creates a misleading contract: callers can pass anyimgattribute without a type error, yet it has no effect at runtime. Since the...restspread was removed in this PR, consider narrowing the interface to exactly what is consumed:♻️ Suggested narrowing
-export interface AvatarProps extends Omit<ComponentProps<"img">, "src"> { +export interface AvatarProps { src?: string | null; alt: string; size?: "sm" | "md" | "lg" | "xl"; fallback?: string; + className?: string; }Also applies to: 26-32
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx` at line 5, AvatarProps currently extends ComponentProps<"img"> but the component only destructures five specific props (the named props in the Avatar component) while the rest are dropped, creating a misleading contract; either narrow AvatarProps to list only the exact props the component consumes or restore and forward the remaining native img attributes by reintroducing the ...rest spread and applying it to the rendered element (update the Avatar component signature and JSX accordingly). Locate AvatarProps and the Avatar functional component to either replace the extends ComponentProps<"img"> with an explicit prop list (e.g., url, alt, size, fallback, className — whatever five props are used) or add back ...rest in the parameter destructure and spread {...rest} on the rendered img/container so native img props like onError, loading, style, data-* are preserved.infrastructure/compose/standalone/keycloak-only.yml (1)
43-44: Hardcoded port8080removes configurability and risks conflicts.Port 8080 is a common default for many local services (e.g., Spring Boot, Tomcat, other Docker containers). The previous
"${KEYCLOAK_PORT:-8081}:8080"defaulted to 8081 specifically to avoid this collision. Restoring the variable keeps the standalone compose usable alongside other stacks without editing the file:♻️ Proposed change
- - "8080:8080" + - "${KEYCLOAK_PORT:-8080}:8080"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/compose/standalone/keycloak-only.yml` around lines 43 - 44, Replace the hardcoded port mapping "- \"8080:8080\"" with an environment-driven mapping that uses the KEYCLOAK_PORT variable (e.g., "- \"${KEYCLOAK_PORT:-8081}:8080\"") so the compose file keeps the previous default of 8081 to avoid port conflicts while allowing overrides via KEYCLOAK_PORT; update any comments if needed to reflect the change.src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx (2)
16-45: CPF/CNPJ length enforcement added — but Cooperative and Freelancer types have no specific validation.The
superRefinecorrectly enforces 11 digits for Individual (CPF) and 14 for Company (CNPJ). However, Cooperative (typically CNPJ) and Freelancer (typically CPF) fall through to just the basemin(11).max(14)check, accepting any length in that range. Consider extending the refinement:Suggested extension
} else if (data.type === EProviderType.Company) { // CNPJ must be 14 digits if (data.documentNumber.length !== 14) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "CNPJ deve ter 14 dígitos", path: ["documentNumber"], }); } + } else if (data.type === EProviderType.Cooperative) { + if (data.documentNumber.length !== 14) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "CNPJ deve ter 14 dígitos", + path: ["documentNumber"], + }); + } + } else if (data.type === EProviderType.Freelancer) { + if (data.documentNumber.length !== 11) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "CPF deve ter 11 dígitos", + path: ["documentNumber"], + }); + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx` around lines 16 - 45, The superRefine in formSchema only checks EProviderType.Individual and EProviderType.Company, leaving EProviderType.Cooperative and EProviderType.Freelancer validated only by the generic min/max; update the superRefine in page.tsx to explicitly handle EProviderType.Cooperative (require documentNumber.length === 14, CNPJ) and EProviderType.Freelancer (require documentNumber.length === 11, CPF), adding ctx.addIssue calls identical to the existing messages for Company/Individual so those types get the correct length validation.
63-73: Email is pre-filled from session but never rendered — user cannot review or correct it.The
useEffect, but there's no<FormField>for it in the JSX. The email will be silently submitted without the user seeing it. If this is intentional (email sourced exclusively from the IdP), a comment clarifying that would help. If users should be able to edit it, add a visible form field.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx` around lines 63 - 73, The email value is populated in the useEffect using session.user.email via form.setValue but there is no visible FormField for "email", so users cannot see or edit it; either add a visible FormField bound to the same form key "email" in the JSX (matching the schema/defaults) so the populated value is rendered and editable, or if the email must be immutable, add a clear inline comment near the useEffect and/or render a read-only FormField for "email" to make intent explicit (refer to useEffect, form.setValue("email"), form.getValues("email"), and the form schema).src/Web/MeAjudaAi.Web.Customer/types/provider.ts (2)
32-53: Consider addingVERIFICATION_STATUS_LABELSfor consistency.You have label maps for type, status, and tier, but
EVerificationStatushas no correspondingVERIFICATION_STATUS_LABELS. If verification status is (or will be) shown to users, the same pattern should be applied to avoid rendering raw enum strings.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/types/provider.ts` around lines 32 - 53, Add a new mapping constant VERIFICATION_STATUS_LABELS: Record<EVerificationStatus, string> to mirror the existing PROVIDER_*_LABELS so verification enums render user-friendly text; locate the enums usage around PROVIDER_STATUS_LABELS/PROVIDER_TYPE_LABELS and define VERIFICATION_STATUS_LABELS with keys like [EVerificationStatus.Unverified], [EVerificationStatus.Pending], [EVerificationStatus.Verified], etc., mapping each EVerificationStatus value to the appropriate Portuguese label and export it for consumption where verification status is displayed.
70-79:ProviderDtois missing several backend fields — acceptable for now but worth tracking.The backend
ProviderMapper.ToDtomaps ~17 fields (includingBusinessProfile,Documents,Qualifications,Services,CreatedAt,SuspensionReason,RejectionReason, etc.), while the frontend DTO only declares 7. The "outros campos omitidos" comment acknowledges this. Just ensure this gets expanded before any UI needs those fields, to avoid silentundefinedaccess.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/types/provider.ts` around lines 70 - 79, ProviderDto currently only lists 7 fields while ProviderMapper.ToDto returns ~17; update ProviderDto to include the missing backend fields (e.g., BusinessProfile, Documents, Qualifications, Services, CreatedAt, SuspensionReason, RejectionReason and any other properties emitted by ProviderMapper.ToDto), mark fields optional if they may be absent, and import or define the correct types for each field so the frontend type matches the backend mapper; also add a short TODO/TICKET comment referencing ProviderMapper.ToDto so future changes remain synchronized.src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/page.tsx (1)
114-120: Step 3 is permanently locked regardless of provider status.For
ActiveorPendingDocumentVerificationproviders, step 3 ("Serviços e Docs") is still rendered as "Bloqueado" with 50% opacity. If this is intentional for this sprint, consider adding a brief comment noting the planned behavior; otherwise, this step should react to status like step 2 does.src/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsx (2)
83-87:SuspendedandRejectedstatuses share the same amber colour as pending states.All non-
Activestatuses map totext-amber-600, but terminal-failure states (Suspended,Rejected) conventionally warrant a destructive red to communicate they require action. This is purely a UX consideration.✨ Proposed improvement — differentiate negative terminal states
- <span className={providerStatus.status === EProviderStatus.Active ? "text-green-600 font-medium" : "text-amber-600"}> + <span className={ + providerStatus.status === EProviderStatus.Active + ? "text-green-600 font-medium" + : (providerStatus.status === EProviderStatus.Suspended || providerStatus.status === EProviderStatus.Rejected) + ? "text-red-600 font-medium" + : "text-amber-600" + }>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsx` around lines 83 - 87, The status color currently uses a single conditional on providerStatus.status === EProviderStatus.Active and falls back to "text-amber-600" for all non-active values; change the logic so terminal-failure states (EProviderStatus.Suspended and EProviderStatus.Rejected) render with a destructive red class (e.g., "text-red-600 font-medium") while other non-active states keep the amber class; update the JSX around providerStatus.status (or extract a small helper like getStatusClass(status)) so the span's className picks "text-green-600 font-medium" for Active, "text-red-600 font-medium" for Suspended/Rejected, and "text-amber-600" for the remaining statuses, leaving PROVIDER_STATUS_LABELS unchanged.
24-25: Duplicate comment — remove one instance.Lines 24 and 25 carry the identical comment
// Loading state - prevent flash of unauthenticated UI.✨ Proposed fix
// Fail-safe: Show buttons by default (avoids infinite loading if JS fails) // If authenticated, we show the avatar. - // Loading state - prevent flash of unauthenticated UI // Loading state - prevent flash of unauthenticated UI if (status === "loading") {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsx` around lines 24 - 25, In the UserMenu React component in user-menu.tsx there is a duplicated inline comment "// Loading state - prevent flash of unauthenticated UI" on two consecutive lines; remove the redundant line so only one instance of that comment remains (keep a single comment immediately before the loading-state logic or return statement where it currently sits).src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs (1)
45-45: Usetypeofinstead of a hardcoded string for the logger category.If the class is renamed or moved, the string silently drifts out of sync.
♻️ Proposed fix
- var logger = loggerFactory.CreateLogger("MeAjudaAi.ApiService.Endpoints.ProviderRegistrationEndpoints"); + var logger = loggerFactory.CreateLogger(typeof(ProviderRegistrationEndpoints).FullName!);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs` at line 45, Replace the hardcoded category string passed to loggerFactory.CreateLogger with a type-based category to avoid drift when the class is renamed; specifically, use the ProviderRegistrationEndpoints type (e.g., pass typeof(ProviderRegistrationEndpoints).FullName or use a generic ILogger<T>/CreateLogger<T> variant) in the call to loggerFactory.CreateLogger so the logger category is tied to the ProviderRegistrationEndpoints type instead of a literal string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/roadmap.md`:
- Around line 2173-2174: The note "Atualizado para 14 de Março de 2026."
directly contradicts the updated heading "**MVP Launch Target**: 28 de Março de
2026 🎯"; update or remove the stale note so both lines agree — either change
the "Atualizado para 14 de Março de 2026." text to "Atualizado para 28 de Março
de 2026." or delete the note entirely; make the edit where the strings "**MVP
Launch Target**: 28 de Março de 2026 🎯" and "Atualizado para 14 de Março de
2026." appear.
In `@infrastructure/compose/standalone/keycloak-only.yml`:
- Around line 32-33: Replace the hardcoded weak credentials by using
environment-variable interpolation for KEYCLOAK_ADMIN and
KEYCLOAK_ADMIN_PASSWORD (similar to the existing DB_PASSWORD pattern): reference
KEYCLOAK_ADMIN and KEYCLOAK_ADMIN_PASSWORD in the compose file and change them
to read from env vars with either a safe non-trivial default or a required/guard
pattern (e.g., use ${KEYCLOAK_ADMIN:?} style or a strong default) so the compose
no longer embeds "admin" / "admin123" in source control and will fail or prompt
when not provided.
- Around line 35-40: The compose YAML forces all social OAuth vars to be present
using the :? expansion (GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, FACEBOOK_APP_ID,
FACEBOOK_APP_SECRET, INSTAGRAM_CLIENT_ID, INSTAGRAM_CLIENT_SECRET), which aborts
docker compose for the standalone quick-test scenario; update those expansions
to use a permissive default (:-) so they become optional (e.g., change
${GOOGLE_CLIENT_ID:?GOOGLE_CLIENT_ID is required} → ${GOOGLE_CLIENT_ID:-}) and
ensure the realm import or provider setup code that consumes these env vars
skips configuring a provider when its values are empty (or document providing
placeholder values in a separate .env.standalone).
In
`@src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs`:
- Around line 55-63: CreateUserCommand is currently constructed with LastName:
string.Empty which fails CreateUserCommandValidator (.NotEmpty(), min length and
pattern). In ProviderRegistrationEndpoints change the LastName assignment to a
non-empty placeholder (for example use request.Name or request.Name.Trim() as
LastName) so the CreateUserCommand passes validation for providers, or
alternatively update CreateUserCommandValidator to allow empty LastName when
Roles contains UserRoles.ProviderStandard; modify either the CreateUserCommand
construction or the validator logic referencing CreateUserCommandValidator,
CreateUserCommand, ProviderRegistrationEndpoints and UserRoles.ProviderStandard
accordingly.
- Around line 21-34: The public RegisterProvider route lacks rate limiting:
update the MapProviderRegistrationEndpoints pipeline by attaching a named
rate-limit policy to the "/register" endpoint (the fluent chain that calls
RegisterProviderAsync and currently uses AllowAnonymous) using
.RequireRateLimiting("provider-registration"); then register a corresponding
policy in the app startup (e.g., in Program.cs) with
builder.Services.AddRateLimiter and addPolicy "provider-registration"
(partitioning by IP and appropriate PermitLimit/Window/QueueLimit) and ensure
options.RejectionStatusCode is set to 429 so excessive anonymous registration
attempts are throttled.
In
`@src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs`:
- Around line 23-27: In RegisterProviderCommandHandler, make the registration
idempotent by catching DbUpdateException specifically around the
providerRepository.AddAsync(...) call: add a catch (DbUpdateException ex) when
(ex.InnerException?.Message.Contains("ix_providers_user_id") ?? false) that
calls providerRepository.GetByUserIdAsync(command.UserId, cancellationToken) and
returns Result<ProviderDto>.Success(existingProvider.ToDto()); keep the generic
exception handling for other errors but replace the current generic 500 path for
unique-index violations with this lookup-and-success flow to match the earlier
existence check using providerRepository.GetByUserIdAsync.
In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx`:
- Around line 153-165: The document label currently uses form.watch("type") ===
EProviderType.Individual to show "CPF" and defaults to "CNPJ", which mislabels
Freelancers; update the condition in the FormField render (the block using
FormField, form.watch("type"), and EProviderType) to treat
EProviderType.Freelancer the same as EProviderType.Individual (e.g., check for
type === EProviderType.Individual || type === EProviderType.Freelancer or use an
inclusion check) so the FormLabel shows "CPF" for both Individual and Freelancer
and "CNPJ" for business types.
In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/page.tsx`:
- Around line 101-111: The "Preencher Endereço" Button rendered when
providerStatus.status === EProviderStatus.PendingBasicInfo is missing a
navigation handler; add either an onClick that calls the app router (e.g.,
router.push('/endereco') or the correct address form route) or wrap the Button
in a Link component so it actually navigates to the address form; update the
Button usage in page.tsx where providerStatus and
EProviderStatus.PendingBasicInfo are checked, ensuring the chosen route
string/href matches your address form route and preserving size/className props.
In `@src/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsx`:
- Around line 79-86: Guard against undefined label lookups by providing nullish
fallbacks: replace direct index usage of
PROVIDER_TIER_LABELS[providerStatus.tier] and
PROVIDER_STATUS_LABELS[providerStatus.status] with expressions that fall back to
a safe string (e.g. "Unknown" or "—") when undefined, e.g. use
(PROVIDER_TIER_LABELS[providerStatus.tier] ?? "Unknown") and
(PROVIDER_STATUS_LABELS[providerStatus.status] ?? "Unknown"); keep the existing
status class conditional using EProviderStatus.Active and providerStatus.status
unchanged so styling still depends on the enum value.
- Around line 69-103: The DropdownMenuSeparator currently only renders when
providerStatus is truthy; update the conditional around the separator so it
renders whenever isLoadingProvider is false (i.e., show the separator for both
the provider and non-provider paths). Locate the separator usage near the JSX
that references isLoadingProvider and providerStatus in the user-menu component
(symbols: isLoadingProvider, providerStatus, DropdownMenuSeparator) and move or
change its conditional to check !isLoadingProvider instead of providerStatus so
"Quero trabalhar" is preceded by the separator when not loading.
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx`:
- Around line 54-60: Update Next's image config to allow external OAuth avatar
hosts by adding an images.remotePatterns array in next.config.ts including
entries for lh3.googleusercontent.com and avatars.githubusercontent.com (and
other provider hostnames as needed) so next/image calls in the Avatar component
won't throw; then modify the Avatar component (types: AvatarProps, component
function where Image is used) to collect and forward the remaining native img
props by including ...rest in the destructuring and passing {...rest} to the
Image component so props like onError, loading, and style are not dropped.
---
Outside diff comments:
In `@infrastructure/compose/standalone/keycloak-only.yml`:
- Around line 1-14: The header comments are stale and contradict the compose
file: update the top-of-file documentation to state that Keycloak is configured
to use Postgres (KC_DB: postgres), that KEYCLOAK_ADMIN and
KEYCLOAK_ADMIN_PASSWORD are currently hardcoded in the compose file (remove
instructions to export them or note they can be overridden via environment or an
.env file), and that the service port is fixed to 8080 (not an optional
KEYCLOAK_PORT); edit the header to accurately describe KC_DB, KC_* credentials
handling, and the fixed port behavior so readers aren’t misled by the previous
H2/variable instructions.
---
Duplicate comments:
In `@docs/roadmap.md`:
- Around line 41-43: Multiple sections still show inconsistent Sprint
names/statuses: normalize all occurrences so Sprint 8B uses the canonical title
"Sprint 8B - Authentication & Onboarding Flow" with status "EM ANDAMENTO" and
remove any "Mobile App" labeling, and ensure Sprint 8D is marked "CANCELADO"
everywhere (not "PLANEJADO"); search for and update all mentions of "Sprint 8B",
"Sprint 8D", "Mobile App (React Native)", and any "Planejado/⏳" state in the
sprint table and Sumário Executivo to match the canonical entries already
present in the roadmap header.
- Around line 59-75: Update the timing wording to remove the contradiction
between "pós-MVP" and "Antes de iniciar o Provider App (Sprint 8C+)" by choosing
one clear placement; for example, change the Status line that currently reads "⏳
Planejado pós-MVP" or the sentence "Antes de iniciar o Provider App (Sprint
8C+)" so they both indicate post‑MVP (e.g., "Planejado para depois do MVP" and
"Antes de iniciar o Provider App (Sprint 8C+), i.e., scheduled after MVP") or
explicitly state a sprint range (e.g., "Planned for after MVP — target: Sprint
9+"); ensure you update the phrases "pós-MVP" and "Sprint 8C+" so they
consistently reflect the chosen timing.
In
`@src/Modules/Providers/API/Endpoints/Public/Me/GetMyProviderStatusEndpoint.cs`:
- Around line 22-31: The OpenAPI metadata on GetMyProviderStatusEndpoint.Map
already includes the missing 400 response, so no code change is required to the
Map method or GetMyStatusAsync handler; mark the PR approval and remove the
duplicate reviewer/comment marker ([duplicate_comment]/[approve_code_changes])
from the review thread to avoid confusion.
In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/page.tsx`:
- Around line 1-9: No further changes required—this file correctly includes the
"use client" directive, destructures error from useProviderStatus, renders a
retry/error screen, and uses PROVIDER_STATUS_LABELS and PROVIDER_TIER_LABELS for
localization; you can proceed to merge as-is (no modifications needed to
ProviderProfilePage, useProviderStatus, or the referenced label constants).
In `@src/Web/MeAjudaAi.Web.Customer/types/provider.ts`:
- Around line 1-30: Enums EProviderType, EProviderStatus, EProviderTier, and
EVerificationStatus already match the backend definitions; no code change
required—keep the current exported enums (EProviderType, EProviderStatus,
EProviderTier, EVerificationStatus) as-is and run the related DTO/unit tests to
confirm parity.
---
Nitpick comments:
In `@infrastructure/compose/standalone/keycloak-only.yml`:
- Around line 43-44: Replace the hardcoded port mapping "- \"8080:8080\"" with
an environment-driven mapping that uses the KEYCLOAK_PORT variable (e.g., "-
\"${KEYCLOAK_PORT:-8081}:8080\"") so the compose file keeps the previous default
of 8081 to avoid port conflicts while allowing overrides via KEYCLOAK_PORT;
update any comments if needed to reflect the change.
In
`@src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs`:
- Line 45: Replace the hardcoded category string passed to
loggerFactory.CreateLogger with a type-based category to avoid drift when the
class is renamed; specifically, use the ProviderRegistrationEndpoints type
(e.g., pass typeof(ProviderRegistrationEndpoints).FullName or use a generic
ILogger<T>/CreateLogger<T> variant) in the call to loggerFactory.CreateLogger so
the logger category is tied to the ProviderRegistrationEndpoints type instead of
a literal string.
In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx`:
- Around line 16-45: The superRefine in formSchema only checks
EProviderType.Individual and EProviderType.Company, leaving
EProviderType.Cooperative and EProviderType.Freelancer validated only by the
generic min/max; update the superRefine in page.tsx to explicitly handle
EProviderType.Cooperative (require documentNumber.length === 14, CNPJ) and
EProviderType.Freelancer (require documentNumber.length === 11, CPF), adding
ctx.addIssue calls identical to the existing messages for Company/Individual so
those types get the correct length validation.
- Around line 63-73: The email value is populated in the useEffect using
session.user.email via form.setValue but there is no visible FormField for
"email", so users cannot see or edit it; either add a visible FormField bound to
the same form key "email" in the JSX (matching the schema/defaults) so the
populated value is rendered and editable, or if the email must be immutable, add
a clear inline comment near the useEffect and/or render a read-only FormField
for "email" to make intent explicit (refer to useEffect, form.setValue("email"),
form.getValues("email"), and the form schema).
In `@src/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsx`:
- Around line 83-87: The status color currently uses a single conditional on
providerStatus.status === EProviderStatus.Active and falls back to
"text-amber-600" for all non-active values; change the logic so terminal-failure
states (EProviderStatus.Suspended and EProviderStatus.Rejected) render with a
destructive red class (e.g., "text-red-600 font-medium") while other non-active
states keep the amber class; update the JSX around providerStatus.status (or
extract a small helper like getStatusClass(status)) so the span's className
picks "text-green-600 font-medium" for Active, "text-red-600 font-medium" for
Suspended/Rejected, and "text-amber-600" for the remaining statuses, leaving
PROVIDER_STATUS_LABELS unchanged.
- Around line 24-25: In the UserMenu React component in user-menu.tsx there is a
duplicated inline comment "// Loading state - prevent flash of unauthenticated
UI" on two consecutive lines; remove the redundant line so only one instance of
that comment remains (keep a single comment immediately before the loading-state
logic or return statement where it currently sits).
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx`:
- Line 5: AvatarProps currently extends ComponentProps<"img"> but the component
only destructures five specific props (the named props in the Avatar component)
while the rest are dropped, creating a misleading contract; either narrow
AvatarProps to list only the exact props the component consumes or restore and
forward the remaining native img attributes by reintroducing the ...rest spread
and applying it to the rendered element (update the Avatar component signature
and JSX accordingly). Locate AvatarProps and the Avatar functional component to
either replace the extends ComponentProps<"img"> with an explicit prop list
(e.g., url, alt, size, fallback, className — whatever five props are used) or
add back ...rest in the parameter destructure and spread {...rest} on the
rendered img/container so native img props like onError, loading, style, data-*
are preserved.
In `@src/Web/MeAjudaAi.Web.Customer/types/provider.ts`:
- Around line 32-53: Add a new mapping constant VERIFICATION_STATUS_LABELS:
Record<EVerificationStatus, string> to mirror the existing PROVIDER_*_LABELS so
verification enums render user-friendly text; locate the enums usage around
PROVIDER_STATUS_LABELS/PROVIDER_TYPE_LABELS and define
VERIFICATION_STATUS_LABELS with keys like [EVerificationStatus.Unverified],
[EVerificationStatus.Pending], [EVerificationStatus.Verified], etc., mapping
each EVerificationStatus value to the appropriate Portuguese label and export it
for consumption where verification status is displayed.
- Around line 70-79: ProviderDto currently only lists 7 fields while
ProviderMapper.ToDto returns ~17; update ProviderDto to include the missing
backend fields (e.g., BusinessProfile, Documents, Qualifications, Services,
CreatedAt, SuspensionReason, RejectionReason and any other properties emitted by
ProviderMapper.ToDto), mark fields optional if they may be absent, and import or
define the correct types for each field so the frontend type matches the backend
mapper; also add a short TODO/TICKET comment referencing ProviderMapper.ToDto so
future changes remain synchronized.
src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs
Outdated
Show resolved
Hide resolved
src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/page.tsx
Outdated
Show resolved
Hide resolved
…luding basic info and address steps.
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts (1)
73-137:⚠️ Potential issue | 🟠 MajorRemove unnecessary
@ts-ignoredirectives or add missing properties to backend DTO.Most properties accessed in this mapper (businessProfile, contactInfo, primaryAddress, fantasyName, legalName, email, phoneNumber, city, state, status, verificationStatus, documents, qualifications) already exist in the generated
MeAjudaAiModulesProvidersApplicationDtosProviderDtotype and don't require suppression. However, four properties are genuinely missing from the generated type and should be added to the backend DTO:services,averageRating,reviewCount, andtier. Either update the backend ProviderDto to include these fields and regenerate types, or remove the@ts-ignorecomments for properties that are already properly typed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts` around lines 73 - 137, The mapper mapApiProviderToProvider is using many unnecessary `@ts-ignore` directives and is missing four actual DTO fields (services, averageRating, reviewCount, tier) on MeAjudaAiModulesProvidersApplicationDtosProviderDto; fix by either (A) updating the backend ProviderDto to include services, averageRating, reviewCount and tier and then regenerate the TypeScript DTOs so mapApiProviderToProvider can rely on strong types, or (B) if the generated DTO already includes the referenced properties (businessProfile, contactInfo, primaryAddress, fantasyName, legalName, email, phoneNumber, city, state, status, verificationStatus, documents, qualifications), remove the redundant `@ts-ignore` lines in mapApiProviderToProvider and update the mapper signatures/field accesses to match the generated types (ensuring services is typed as an array and tier/averageRating/reviewCount have appropriate types) so TypeScript type checking passes without ignores.
🧹 Nitpick comments (8)
src/Web/MeAjudaAi.Web.Customer/hooks/use-my-provider-profile.ts (1)
18-22:Content-Type: application/jsonis unnecessary on a GET request with no body.This won't cause issues but adds noise. Consider removing it for clarity.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-my-provider-profile.ts` around lines 18 - 22, The GET request in the use-my-provider-profile hook is sending an unnecessary "Content-Type: application/json" header; remove "Content-Type" from the headers object passed to fetch(`${baseUrl}/api/v1/providers/me`, ...) (or conditionally add Content-Type only when a request body exists) so the const response = await fetch(...) call only includes Authorization and any required headers for GET.src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts (1)
17-47: Consider wrappingfetchAddressinuseCallback.
fetchAddressgets a new reference on every render since it's a plainasyncarrow function. If a consumer passes it as a dependency (e.g., in auseEffectdep array), it would re-trigger unnecessarily. Wrapping inuseCallbackwith an empty dependency array stabilizes the reference.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts` around lines 17 - 47, fetchAddress is recreated each render causing unstable references; wrap the async function in React's useCallback to stabilize it. Update the definition of fetchAddress (the async function that validates/cleans cep, calls fetch, sets setIsLoading/setError and returns ViaCepResponse|null) to use useCallback(() => { ... }, []) so its identity is stable for consumers (e.g., effects or prop deps); ensure any state setters used (setIsLoading, setError) are referenced safely and include the same logic inside the callback.src/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.ts (1)
13-14: Base URL resolution and fetch pattern duplicated across all provider hooks.The
client.getConfig().baseUrl || process.env.NEXT_PUBLIC_API_URL || "http://localhost:7002"pattern plus manualfetchwith bearer token headers is repeated verbatim inuse-register-provider,use-update-provider-profile, anduse-my-provider-profile. Extract a shared authenticated-fetch helper to reduce duplication and ensure consistent error handling (e.g., this hook doesn't checkerror.detail, but the update hook does).Also applies to: 20-27, 29-31
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.ts` around lines 13 - 14, Extract the repeated baseUrl resolution and bearer-token fetch logic into a shared helper (e.g., create an exported function authFetch or useAuthenticatedFetch) and replace the duplicate code in useRegisterProvider (use-register-provider.ts), useUpdateProviderProfile (use-update-provider-profile.ts) and useMyProviderProfile (use-my-provider-profile.ts) to call that helper; the helper should compute baseUrl once (using client.getConfig().baseUrl || process.env.NEXT_PUBLIC_API_URL || "http://localhost:7002"), attach Authorization: `Bearer ${token}` and Content-Type headers, perform fetch, parse JSON, and normalize error handling so callers consistently check error.detail (or return a standardized { ok, data, error } shape) rather than each hook duplicating parsing/validation.src/Web/MeAjudaAi.Web.Customer/hooks/use-update-provider-profile.ts (1)
8-31: Inline request type duplicates shared DTO structure.
UpdateProviderProfileRequestis defined inline here with nestedbusinessProfile,contactInfo, andprimaryAddressshapes that overlap withBusinessProfileDto,ContactInfoDto, andAddressDtofrom@/types/api/provider. This introduces drift risk (e.g.,logoUrlhere isn't in the shared types). Consider composing from the shared types.Example
-interface UpdateProviderProfileRequest { - name: string; - businessProfile: { - legalName: string; - ... - }; -} +import { BusinessProfileDto } from "@/types/api/provider"; + +interface UpdateProviderProfileRequest { + name: string; + businessProfile: BusinessProfileDto; +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-update-provider-profile.ts` around lines 8 - 31, The inline UpdateProviderProfileRequest duplicates nested DTO shapes; replace it by importing and composing the shared types (BusinessProfileDto, ContactInfoDto, AddressDto) from "@/types/api/provider" and use them in the UpdateProviderProfileRequest signature (e.g., name: string; businessProfile: BusinessProfileDto) ensuring you reconcile optional fields like logoUrl to match the shared DTOs (or add a clear comment if an extra optional field is intentionally required) and remove the duplicated nested definitions (update any usages of UpdateProviderProfileRequest to the composed type).src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts (1)
136-157: Label constants look correct for pt-BR UI.The label maps cover all enum members. One minor note:
Standard(Line 153) is kept in English while other tiers are translated — this appears intentional per the comment on Line 154.Based on learnings: prefer reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code. Consider whether these enums and label maps could be auto-generated or sourced from a shared contract to prevent future drift.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts` around lines 136 - 157, PROVIDER_* label maps are fine but to avoid drift you should import and reuse the canonical enums from Shared.Contracts instead of relying on local enum definitions; update the module to reference the shared EProviderType, EProviderStatus and EProviderTier types and ensure PROVIDER_TYPE_LABELS, PROVIDER_STATUS_LABELS and PROVIDER_TIER_LABELS are keyed by those imported enums (or generate the maps from a shared contract/utility at build time), keeping the existing translated strings (note the intentional "Standard" comment) and add a short TODO comment to indicate auto-generation is preferred.src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/endereco/page.tsx (2)
52-64: CEP auto-fill doesn't clear stale validation errors.
form.setValuecalls (lines 57-60) don't pass{ shouldValidate: true }, so if the user previously attempted submission and has validation errors onstreet,neighborhood,city, orstate, those error messages will persist even after successful auto-fill.Suggested fix
- form.setValue("street", data.logradouro); - form.setValue("neighborhood", data.bairro); - form.setValue("city", data.localidade); - form.setValue("state", data.uf); + form.setValue("street", data.logradouro, { shouldValidate: true }); + form.setValue("neighborhood", data.bairro, { shouldValidate: true }); + form.setValue("city", data.localidade, { shouldValidate: true }); + form.setValue("state", data.uf, { shouldValidate: true });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/endereco/page.tsx` around lines 52 - 64, The CEP autofill in handleCepBlur does not clear prior validation errors because form.setValue calls for "street", "neighborhood", "city", and "state" are missing the validation option; update each form.setValue call inside handleCepBlur to include the options object { shouldValidate: true } (e.g., form.setValue("street", data.logradouro, { shouldValidate: true })) so that stale errors are re-validated/cleared after autofill, leaving form.setFocus("number") as-is.
36-50:formin the useEffect dependency array may trigger unnecessary resets.While
useFormreturns a stable object reference in practice, includingformin the dependency array is a common source of lint warnings and can cause unexpected re-runs if the reference ever changes (e.g., due to component re-mount patterns). Consider usingform.resetdirectly or extracting it:Suggested fix
+ const { reset } = form; + useEffect(() => { if (profile?.businessProfile?.primaryAddress) { const addr = profile.businessProfile.primaryAddress; - form.reset({ + reset({ zipCode: addr.zipCode || "", ... }); } - }, [profile, form]); + }, [profile, reset]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/endereco/page.tsx` around lines 36 - 50, The useEffect currently depends on the entire form object which can cause unnecessary re-runs; extract the reset method from the form (e.g., const { reset } = form or destructure the useForm return) and call reset(...) inside useEffect, then update the dependency array to only include profile (or profile.businessProfile.primaryAddress) and reset — e.g., useEffect(..., [profile, reset]) so the effect only runs when the profile or reset changes rather than when the whole form reference changes. Ensure you reference the existing symbols: useEffect, profile.businessProfile.primaryAddress, and form.reset (now reset).src/Web/MeAjudaAi.Web.Customer/components/ui/verified-badge.tsx (1)
6-6: Type says required but runtime guards fornull/undefined.
statusis typed asEVerificationStatus(non-optional), yet line 13 explicitly checks=== undefined || === null. Either mark the prop optional (status?: EVerificationStatus) to match the defensive guard, or drop the guard if callers are expected to always provide it. Current mismatch lets TypeScript-unaware callers passundefinedwithout a type error.Suggested fix (make optional to match guard)
interface VerifiedBadgeProps { - status: EVerificationStatus; + status?: EVerificationStatus; className?: string;Also applies to: 13-13
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/ui/verified-badge.tsx` at line 6, The prop declaration for status is typed as non-optional (status: EVerificationStatus) but the component contains runtime guards checking for undefined/null; make the types align by updating the prop to be optional (status?: EVerificationStatus) in the VerifiedBadge/props interface so TypeScript reflects that callers may omit or pass undefined, and keep the existing null/undefined checks, or alternatively remove the runtime guard and keep status required—pick the optional change to match current guards and update the prop signature accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Validators/AddDocumentRequestValidatorTests.cs`:
- Line 127: The test fails because AddDocumentRequestValidator produces a
mixed-language message (Portuguese prefix + English enum suffix); update the
validator (AddDocumentRequestValidator) to format the enum validation message
entirely in Portuguese (translate or replace the helper text that currently
emits "Valid EDocumentType values: ...") and ensure it lists the EDocumentType
values in Portuguese if applicable; then update the test assertion in
AddDocumentRequestValidatorTests.cs to expect the fully Portuguese message
instead of the mixed string (replace the .WithErrorMessage(...) expected text to
the corrected Portuguese-only message that AddDocumentRequestValidator now
returns).
In
`@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/endereco/page.tsx`:
- Around line 17-21: Destructure and check the error returned by
useMyProviderProfile in ProviderAddressPage (e.g., const { data: profile, error,
isLoading: isLoadingProfile } = useMyProviderProfile()) and add an error branch
that renders a visible error state (matching the profile page pattern) instead
of rendering the form when error is present; also update onSubmit (the function
that currently early-returns when !profile) to guard against silent no-ops by
preventing submission and surface the error to the user if profile is missing,
and apply the same error-destructure + error-branch pattern to the other
identical hook usage later in the file (the block referenced around 106-112).
- Around line 93-95: Update the local UpdateProviderProfileRequest interface in
use-update-provider-profile.ts to exactly mirror the generated API type
MeAjudaAiModulesProvidersApplicationDtosRequestsUpdateProviderProfileRequest:
make all top-level fields optional, make nested BusinessProfileDto fields
optional as in the generated contract, and remove the non-existent logoUrl
property; then remove the `@ts-ignore` by letting updateProfile accept the
corrected UpdateProviderProfileRequest so TypeScript validates calls correctly.
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-my-provider-profile.ts`:
- Around line 37-43: The current fallback "return json.data || json" masks
legitimate null ApiResponse.data values; in the useMyProviderProfile hook (the
json handling block) change the logic to explicitly check for the presence of
the data field and return its value (which may be null) instead of falling back
to the wrapper object — e.g., if ('data' in json) return json.data; otherwise
return json; keep the existing Result<T> handling for json.value as-is.
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.ts`:
- Around line 42-44: The onSuccess handler in the useRegisterProvider hook only
invalidates ["providerStatus"] so myProviderProfile stays stale after
registration; update the onSuccess in use-register-provider.ts (the mutation
success callback where queryClient.invalidateQueries is called) to also
invalidate the ["myProviderProfile"] queryKey (i.e., call
queryClient.invalidateQueries for ["myProviderProfile"] alongside
["providerStatus"]) so the fresh profile is refetched immediately after
registration.
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-update-provider-profile.ts`:
- Around line 60-68: The response handling in use-update-provider-profile.ts
incorrectly uses "json.data || json", causing an ApiResponse<ProviderDto> to be
returned when data is null; update the code to explicitly extract the
ProviderDto using nullish coalescing and any Result wrapper (e.g., return
json.data ?? json.value ?? /* fallback */ undefined or throw) and remove the
`@ts-ignore` so types are preserved; additionally factor this behavior into a
shared helper (e.g., normalizeApiResponse<T>(json: ApiResponse<T> | T)) and call
it from the update function to ensure consistent, type-safe normalization across
hooks.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Around line 60-63: Replace the magic numbers in the object literal with the
corresponding enum members: use EProviderStatus.<appropriateActiveMember>
instead of 2, EVerificationStatus.<appropriateVerifiedMember> instead of 1, and
EProviderTier.<appropriateTierMember> instead of 0; also ensure the file imports
the enums (EProviderType, EProviderStatus, EVerificationStatus, EProviderTier
and ProviderServiceDto) from '@/types/api/provider' so the mapped object in the
mapper function uses the named enum constants rather than numeric literals.
In `@src/Web/MeAjudaAi.Web.Customer/lib/schemas/auth.ts`:
- Around line 13-32: The superRefine block in the schema only enforces document
length for EProviderType.Individual (CPF 11) and EProviderType.Company (CNPJ
14), leaving EProviderType.Freelancer and EProviderType.Cooperative to rely on
the generic .min(11).max(14) rule; update the superRefine in auth.ts to handle
those types explicitly (e.g., validate EProviderType.Freelancer as CPF length 11
and EProviderType.Cooperative as either CPF or CNPJ per your business rule) by
adding branches for EProviderType.Freelancer and EProviderType.Cooperative that
call ctx.addIssue on invalid lengths for documentNumber, or if the current
permissive behavior is intended, add a clear inline comment in the superRefine
mentioning that Freelancer and Cooperative intentionally allow 11–14 digit
documents.
In `@src/Web/MeAjudaAi.Web.Customer/middleware.ts`:
- Around line 11-15: Middleware currently only protects exact "/prestador" via
isProviderDashboard and thus misses the new provider self-service routes under
"/cadastro", allowing unauthenticated access; update the isProtected logic
(referencing pathname, isProviderDashboard, isProfile, isClientDashboard, and
isProtected) to include the cadastro routes (e.g.,
pathname.startsWith("/cadastro") or specific "/cadastro/prestador*" checks) so
those pages are covered by authentication middleware or alternatively enforce
redirect() in the corresponding layout.
In `@src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts`:
- Around line 85-89: The current DocumentDto interface is missing and misnamed
fields compared to the backend; replace the simple { type: string; url: string;
status: string } with a shape that matches the backend DTO: include Id: string,
ProviderId: string, DocumentType: EDocumentType (or a string union matching the
backend enum), FileName: string, FileUrl: string, Status: EDocumentStatus (or
matching enum), UploadedAt: string | Date, VerifiedAt?: string | Date,
RejectionReason?: string, and OcrData?: string; update the DocumentDto
definition and any consumers to use these exact property names/types (and import
or declare EDocumentType/EDocumentStatus) so serialization/deserialization
aligns with the API.
---
Outside diff comments:
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Around line 73-137: The mapper mapApiProviderToProvider is using many
unnecessary `@ts-ignore` directives and is missing four actual DTO fields
(services, averageRating, reviewCount, tier) on
MeAjudaAiModulesProvidersApplicationDtosProviderDto; fix by either (A) updating
the backend ProviderDto to include services, averageRating, reviewCount and tier
and then regenerate the TypeScript DTOs so mapApiProviderToProvider can rely on
strong types, or (B) if the generated DTO already includes the referenced
properties (businessProfile, contactInfo, primaryAddress, fantasyName,
legalName, email, phoneNumber, city, state, status, verificationStatus,
documents, qualifications), remove the redundant `@ts-ignore` lines in
mapApiProviderToProvider and update the mapper signatures/field accesses to
match the generated types (ensuring services is typed as an array and
tier/averageRating/reviewCount have appropriate types) so TypeScript type
checking passes without ignores.
---
Duplicate comments:
In `@src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts`:
- Around line 56-83: Enums EProviderType, EProviderStatus, EVerificationStatus,
and EProviderTier now match backend numeric definitions so no code changes are
needed; leave the enum declarations (EProviderType, EProviderStatus,
EVerificationStatus, EProviderTier) as-is and proceed to approve/merge the
change.
---
Nitpick comments:
In
`@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/endereco/page.tsx`:
- Around line 52-64: The CEP autofill in handleCepBlur does not clear prior
validation errors because form.setValue calls for "street", "neighborhood",
"city", and "state" are missing the validation option; update each form.setValue
call inside handleCepBlur to include the options object { shouldValidate: true }
(e.g., form.setValue("street", data.logradouro, { shouldValidate: true })) so
that stale errors are re-validated/cleared after autofill, leaving
form.setFocus("number") as-is.
- Around line 36-50: The useEffect currently depends on the entire form object
which can cause unnecessary re-runs; extract the reset method from the form
(e.g., const { reset } = form or destructure the useForm return) and call
reset(...) inside useEffect, then update the dependency array to only include
profile (or profile.businessProfile.primaryAddress) and reset — e.g.,
useEffect(..., [profile, reset]) so the effect only runs when the profile or
reset changes rather than when the whole form reference changes. Ensure you
reference the existing symbols: useEffect,
profile.businessProfile.primaryAddress, and form.reset (now reset).
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/verified-badge.tsx`:
- Line 6: The prop declaration for status is typed as non-optional (status:
EVerificationStatus) but the component contains runtime guards checking for
undefined/null; make the types align by updating the prop to be optional
(status?: EVerificationStatus) in the VerifiedBadge/props interface so
TypeScript reflects that callers may omit or pass undefined, and keep the
existing null/undefined checks, or alternatively remove the runtime guard and
keep status required—pick the optional change to match current guards and update
the prop signature accordingly.
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-my-provider-profile.ts`:
- Around line 18-22: The GET request in the use-my-provider-profile hook is
sending an unnecessary "Content-Type: application/json" header; remove
"Content-Type" from the headers object passed to
fetch(`${baseUrl}/api/v1/providers/me`, ...) (or conditionally add Content-Type
only when a request body exists) so the const response = await fetch(...) call
only includes Authorization and any required headers for GET.
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.ts`:
- Around line 13-14: Extract the repeated baseUrl resolution and bearer-token
fetch logic into a shared helper (e.g., create an exported function authFetch or
useAuthenticatedFetch) and replace the duplicate code in useRegisterProvider
(use-register-provider.ts), useUpdateProviderProfile
(use-update-provider-profile.ts) and useMyProviderProfile
(use-my-provider-profile.ts) to call that helper; the helper should compute
baseUrl once (using client.getConfig().baseUrl ||
process.env.NEXT_PUBLIC_API_URL || "http://localhost:7002"), attach
Authorization: `Bearer ${token}` and Content-Type headers, perform fetch, parse
JSON, and normalize error handling so callers consistently check error.detail
(or return a standardized { ok, data, error } shape) rather than each hook
duplicating parsing/validation.
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-update-provider-profile.ts`:
- Around line 8-31: The inline UpdateProviderProfileRequest duplicates nested
DTO shapes; replace it by importing and composing the shared types
(BusinessProfileDto, ContactInfoDto, AddressDto) from "@/types/api/provider" and
use them in the UpdateProviderProfileRequest signature (e.g., name: string;
businessProfile: BusinessProfileDto) ensuring you reconcile optional fields like
logoUrl to match the shared DTOs (or add a clear comment if an extra optional
field is intentionally required) and remove the duplicated nested definitions
(update any usages of UpdateProviderProfileRequest to the composed type).
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts`:
- Around line 17-47: fetchAddress is recreated each render causing unstable
references; wrap the async function in React's useCallback to stabilize it.
Update the definition of fetchAddress (the async function that validates/cleans
cep, calls fetch, sets setIsLoading/setError and returns ViaCepResponse|null) to
use useCallback(() => { ... }, []) so its identity is stable for consumers
(e.g., effects or prop deps); ensure any state setters used (setIsLoading,
setError) are referenced safely and include the same logic inside the callback.
In `@src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts`:
- Around line 136-157: PROVIDER_* label maps are fine but to avoid drift you
should import and reuse the canonical enums from Shared.Contracts instead of
relying on local enum definitions; update the module to reference the shared
EProviderType, EProviderStatus and EProviderTier types and ensure
PROVIDER_TYPE_LABELS, PROVIDER_STATUS_LABELS and PROVIDER_TIER_LABELS are keyed
by those imported enums (or generate the maps from a shared contract/utility at
build time), keeping the existing translated strings (note the intentional
"Standard" comment) and add a short TODO comment to indicate auto-generation is
preferred.
src/Modules/Providers/Tests/Unit/Application/Validators/AddDocumentRequestValidatorTests.cs
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/perfil/endereco/page.tsx
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/endereco/page.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/hooks/use-my-provider-profile.ts
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/hooks/use-update-provider-profile.ts
Outdated
Show resolved
Hide resolved
…hentication, authorization, CORS, and rate limiting.
…d detailed viewing pages with supporting API types and hooks.
… validation rules for document number and type.
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (10)
src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts (3)
28-28: Extract the ViaCEP base URL to a named constant.The URL is hardcoded inline and would need updating in multiple places if the hook is later extended (or if a mock/proxy is needed in test environments).
♻️ Proposed refactor: named constant
+const VIA_CEP_BASE_URL = "https://viacep.com.br/ws"; + export function useViaCep() { ... - const response = await fetch(`https://viacep.com.br/ws/${cleanCep}/json/`); + const response = await fetch(`${VIA_CEP_BASE_URL}/${cleanCep}/json/`);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts` at line 28, The fetch URL in the useViaCep hook is hardcoded; extract the ViaCEP base URL into a named constant (e.g., VIACEP_BASE_URL) and use it when building the request (replace the inline string in the fetch call that uses cleanCep). Update any other occurrences in useViaCep to reference the constant so tests or environments can override it easily and keep behavior unchanged.
41-43:erris declared but never used — prefer a no-binding catch or a typed guard.♻️ Proposed fix
- } catch (err) { + } catch { setError("Falha ao consultar CEP"); return null;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts` around lines 41 - 43, The catch block in use-via-cep.ts declares an unused variable err; remove the unused binding by changing the catch to a no-binding catch (catch { ... }) and keep the existing setError("Falha ao consultar CEP"); return null; flow, or if you need error details, type the parameter as unknown (catch (err: unknown)) and add a narrow/checked handling (e.g., extract message safely) before logging or using it; update the catch around the code in the useViaCep hook where setError is called.
17-47: AddAbortControllerto prevent stale state from concurrent/overlapping requests.If
fetchAddressis triggered on rapid successive inputs (or on every keystroke), earlier in-flight requests can resolve after later ones — leavingerror/isLoadingreflecting a stale response. Additionally, if the component unmounts mid-flight, thefinallyblock still callssetIsLoading(false)on a dead component tree.♻️ Proposed refactor: AbortController for request lifecycle
- const fetchAddress = useCallback(async (cep: string): Promise<ViaCepResponse | null> => { + const abortControllerRef = useRef<AbortController | null>(null); + + const fetchAddress = useCallback(async (cep: string): Promise<ViaCepSuccessResponse | null> => { const cleanCep = cep.replace(/\D/g, ""); if (cleanCep.length !== 8) { return null; } + // Cancel any in-flight request before starting a new one + abortControllerRef.current?.abort(); + const controller = new AbortController(); + abortControllerRef.current = controller; + setIsLoading(true); setError(null); try { - const response = await fetch(`https://viacep.com.br/ws/${cleanCep}/json/`); + const response = await fetch(`https://viacep.com.br/ws/${cleanCep}/json/`, { + signal: controller.signal, + }); if (!response.ok) { throw new Error("Erro ao buscar CEP"); } - const data: ViaCepResponse = await response.json(); + const data: ViaCepApiResponse = await response.json(); if (data.erro) { setError("CEP não encontrado"); return null; } - return data; - } catch (err) { + return data as ViaCepSuccessResponse; + } catch (err) { + if (err instanceof Error && err.name === "AbortError") return null; setError("Falha ao consultar CEP"); return null; } finally { setIsLoading(false); } }, []);Also add
useRefto the import on line 1:-import { useState, useCallback } from "react"; +import { useState, useCallback, useRef } from "react";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts` around lines 17 - 47, The fetchAddress useCallback can leave stale state from overlapping requests and update state after unmount; fix it by adding an AbortController stored in a ref (add useRef to imports) and, inside fetchAddress, abort any previous controller.current before creating a new controller, pass controller.signal to fetch, and set controller.current = controller; in catch, ignore AbortError (don't call setError) and in finally only call setIsLoading(false) when controller.signal.aborted is false (or when the current controller still matches the one used for this fetch) to avoid updating state from stale or unmounted requests; also add a useEffect cleanup that aborts controller.current on unmount.src/Web/MeAjudaAi.Web.Customer/app/(main)/prestador/[id]/page.tsx (2)
20-20:typepreprocessing silently coerces any string toEProviderType.Individual.
typeof val === 'string' ? EProviderType.Individual : valmaps every string value —"Company","Freelancer","2"— toIndividual(1). Whiletypeis not rendered in the current JSX, this schema will mislead any future consumer that readsproviderData.type. A proper string-to-enum mapping or a plain.optional()without coercion would be more correct.♻️ Proposed fix
- type: z.preprocess((val) => typeof val === 'string' ? EProviderType.Individual : val, z.nativeEnum(EProviderType).optional()), // Relaxed + type: z.preprocess((val) => { + if (typeof val === 'number') return val; + if (typeof val === 'string') { + const key = val as keyof typeof EProviderType; + return EProviderType[key] ?? EProviderType.Individual; + } + return val; + }, z.nativeEnum(EProviderType).optional()),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx at line 20, The z.preprocess for the `type` field currently coerces any string to EProviderType.Individual which is incorrect; update the preprocessing in the schema that defines `type` so it either (a) maps known string values to the corresponding EProviderType members (e.g., convert "Individual"/"Company" strings to EProviderType.Individual/EProviderType.Company) and returns the original value for non-string inputs, or (b) remove the coercion entirely and use z.nativeEnum(EProviderType).optional() so strings are not silently converted; ensure references like providerData.type and the enum EProviderType are used to validate/convert only well-known values.
198-198: Prefer??over||for numeric-enum fallback.
providerData.verificationStatus || EVerificationStatus.Pendingtreats0(Pending) as falsy and falls through to the right-hand side — which is also0, so the result is accidentally correct today. IfEVerificationStatusvalues are ever reshuffled,||will silently pass0through incorrectly. Use??to be explicit.-<VerifiedBadge status={providerData.verificationStatus || EVerificationStatus.Pending} size="lg" /> +<VerifiedBadge status={providerData.verificationStatus ?? EVerificationStatus.Pending} size="lg" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx at line 198, Replace the falsy-check fallback using || with the nullish coalescing operator so numeric enum values like 0 aren't treated as absent: update the VerifiedBadge prop expression that uses providerData.verificationStatus || EVerificationStatus.Pending to use providerData.verificationStatus ?? EVerificationStatus.Pending instead, leaving the VerifiedBadge component and the enum EVerificationStatus unchanged.src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx (1)
10-10: Import path inconsistency: use@/types/api/providerdirectly.This file imports
EProviderTypefrom@/types/provider, while every other file in this PR (e.g.,prestador/[id]/page.tsx,prestador/page.tsx,verified-badge.tsx) imports from@/types/api/provider. If@/types/provideris a thin re-export, both work, but the inconsistency is confusing and could diverge if@/types/provideris extended independently.-import { EProviderType } from "@/types/provider"; +import { EProviderType } from "@/types/api/provider";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx` at line 10, The import for EProviderType is inconsistent—change the import source from "@/types/provider" to "@/types/api/provider" so it matches other files (e.g., prestador/[id]/page.tsx, prestador/page.tsx, verified-badge.tsx); update the import statement that references EProviderType in the cadastro/prestador/page.tsx module to import EProviderType from "@/types/api/provider" to avoid divergence.src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs (1)
455-464: Extract the policy name to a constant and consider the "anonymous" IP-fallback risk.The raw string
"provider-registration"is used consistently in both the policy definition (line 455) and the endpoint requirement (ProviderRegistrationEndpoints.cs). However, the pattern exists elsewhere inRateLimitPolicies(e.g.,RateLimitPolicies.Public). Extract the policy name as a constant to align with the existing convention:public const string ProviderRegistration = "provider-registration";Additionally, consider the "anonymous" IP-fallback: every request with a
nullRemoteIpAddress(common behind certain reverse proxies or in containerized environments) shares a single 5-req/min partition. This can simultaneously false-throttle legitimate users and be exploited by attackers who strip the IP header. Prefer keying onX-Forwarded-For(with validation) or a unique per-connection identifier likecontext.Connection.Id.Suggested approach
- options.AddPolicy("provider-registration", context => + options.AddPolicy(RateLimitPolicies.ProviderRegistration, context => RateLimitPartition.GetFixedWindowLimiter( - partitionKey: context.Connection.RemoteIpAddress?.ToString() ?? "anonymous", + partitionKey: context.Request.Headers["X-Forwarded-For"].FirstOrDefault() + ?? context.Connection.RemoteIpAddress?.ToString() + ?? context.Connection.Id,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs` around lines 455 - 464, Extract the literal "provider-registration" into a constant (e.g., add public const string ProviderRegistration = "provider-registration" in RateLimitPolicies) and use that constant when calling options.AddPolicy and when requiring the policy in ProviderRegistrationEndpoints; also stop using the hardcoded "anonymous" fallback for the partition key — replace partitionKey: context.Connection.RemoteIpAddress?.ToString() ?? "anonymous" with a fallback that uses a validated X-Forwarded-For header when present (sanitized/whitelisted) or, if absent, a per-connection identifier such as context.Connection.Id to avoid collapsing all null-IP requests into one shared bucket.src/Web/MeAjudaAi.Web.Customer/hooks/use-update-provider-profile.ts (2)
36-39: Unuseddataparameter inonSuccess.The callback receives the mutation result but never uses it. Prefix with
_or drop it to silence the linter.♻️ Proposed fix
- onSuccess: (data) => { + onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["providerStatus"] }); queryClient.invalidateQueries({ queryKey: ["myProviderProfile"] }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-update-provider-profile.ts` around lines 36 - 39, The onSuccess callback in the use-update-provider-profile hook declares a parameter named data that is never used; either remove the parameter or rename it to _data to satisfy the linter. Update the onSuccess declaration in use-update-provider-profile.ts (the onSuccess handler that calls queryClient.invalidateQueries and queryClient.invalidateQueries for "providerStatus" and "myProviderProfile") so it no longer introduces an unused variable.
3-6: Remove unused importsclientandApiResponse.
client(Line 3) andApiResponse(Line 5) are never referenced in this file. The hook fully delegates toauthenticatedFetch, which internally callsclient.getConfig()and normalisesApiResponse<T>/Result<T>wrappers — the consumer no longer needs either import.♻️ Proposed fix
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useSession } from "next-auth/react"; -import { client } from "@/lib/api/client"; import { ProviderDto, ContactInfoDto, AddressDto } from "@/types/api/provider"; -import { ApiResponse } from "@/types/api"; import { authenticatedFetch } from "@/lib/api/fetch-client";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-update-provider-profile.ts` around lines 3 - 6, Remove the unused imports `client` and `ApiResponse` from the top of use-update-provider-profile.ts; keep the imports needed by the hook (e.g., `authenticatedFetch`, `ProviderDto`, `ContactInfoDto`, `AddressDto`) and ensure no other references to `client` or `ApiResponse` remain in the file (the hook delegates to `authenticatedFetch` which handles the client/config and response normalization).src/Web/MeAjudaAi.Web.Customer/app/(main)/buscar/page.tsx (1)
207-207: Redundant non-null assertion!alongside.filter(Boolean).
s.serviceName!asserts the value is non-null to satisfy TypeScript, but.filter(Boolean)discards falsy values at runtime anyway — the assertion suppresses the type error that the filter correctly handles. Using a type predicate in the filter removes the need for!and preserves narrowed typing.♻️ Proposed fix
- services={provider.services.map(s => s.serviceName!).filter(Boolean)} + services={provider.services + .map(s => s.serviceName) + .filter((name): name is string => Boolean(name))}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/buscar/page.tsx at line 207, The mapping currently uses a redundant non-null assertion in services={provider.services.map(s => s.serviceName!).filter(Boolean)}; remove the unnecessary `!` and use a type-narrowing predicate so TypeScript knows falsy values are filtered out — e.g. map to s.serviceName (no !) and then filter with a type guard like .filter((v): v is string => Boolean(v)) so the resulting array is correctly typed as string[] for the services prop.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs`:
- Around line 52-53: The provider document type mapping currently treats any
non-Individual as CNPJ; update the logic in RegisterProviderCommandHandler where
docType is derived from command.Type so that EProviderType.Individual and
EProviderType.Freelancer map to EDocumentType.CPF and EProviderType.Company and
EProviderType.Cooperative map to EDocumentType.CNPJ, then construct the
Document(command.DocumentNumber, docType, isPrimary: true) as before to accept
Freelancer CPF numbers correctly.
In
`@src/Modules/Providers/Tests/Unit/Application/Validators/AddDocumentRequestValidatorTests.cs`:
- Around line 126-128: Remove the duplicated assertion call and chain the
error-message assertion onto the single ShouldHaveValidationErrorFor invocation:
replace the two separate calls to result.ShouldHaveValidationErrorFor(x =>
x.DocumentType) with one call followed by .WithErrorMessage(...). Update the
test in AddDocumentRequestValidatorTests (the assertion using the result
variable) so it reads a single ShouldHaveValidationErrorFor(x =>
x.DocumentType).WithErrorMessage("Tipo de documento inválido. Valores aceitos:
None, CPF, CNPJ, RG, CNH, Passport, Other").
In `@src/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsx`:
- Around line 79-89: Replace the hardcoded English fallback "Unknown" in the
provider tier and status displays with a Portuguese localized value; update the
expressions that use PROVIDER_TIER_LABELS[providerStatus.tier] ?? "Unknown" and
PROVIDER_STATUS_LABELS[providerStatus.status] ?? "Unknown" to return a PT-BR
string (e.g., "Desconhecido" or a i18n lookup like t('common.unknown')). Ensure
you apply this change where providerStatus.tier and providerStatus.status are
used and keep the existing conditional classes for EProviderStatus.Active,
EProviderStatus.Rejected, and EProviderStatus.Suspended unchanged.
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/verified-badge.tsx`:
- Around line 26-32: The visible label for the rejected verification branch
currently shows "Suspenso" which conflates verification rejection with provider
suspension; in the VerifiedBadge render branch that checks
EVerificationStatus.Rejected (the component/JSX block that returns the Ban icon
and uses showLabel), change the visible text when showLabel is true from
"Suspenso" to "Rejeitado" while keeping the existing title/tooltip as-is (or
adjust to just "Rejeitado" if desired); update only the text node associated
with the showLabel span to reflect EVerificationStatus.Rejected correctly.
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts`:
- Around line 3-11: The ViaCepResponse interface is inaccurate because ViaCEP
can return just { erro: true } (no string fields); change ViaCepResponse to a
discriminated union (e.g. { erro: true } | { erro?: false; cep?: string;
logradouro?: string; complemento?: string; bairro?: string; localidade?: string;
uf?: string }) or make the string fields optional, then update the code in the
useViaCep hook where you cast/assign data (the variable named data) so the
runtime guard (data.erro) properly narrows to the non-error branch instead of
relying on an unsafe cast, and update the exported hook return type to reflect
that those address fields may be undefined (or use the union type) so consumers
get correct types.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts`:
- Around line 46-49: The handling of 204 in fetch-client.ts currently returns an
unsafe "{} as T" from authenticatedFetch which lies about the result shape;
remove the "{} as T" return and instead either (A) change authenticatedFetch's
contract to Promise<T | undefined> and return undefined when response.status ===
204 (then update callers), or (B) add an explicit overload / separate helper
(e.g. authenticatedVoidFetch or an authenticatedFetch<void> overload) for
no-body endpoints and return void/undefined there; update the response.status
=== 204 branch in authenticatedFetch to return undefined (or call the new
void-helper) and adjust calling sites accordingly so no caller assumes a
populated T.
- Around line 51-66: authenticatedFetch currently uses body?: any and suppresses
TypeScript checks with `@ts-ignore` when unwrapping ApiResponse<T>, which can
return data: null and violate the Promise<T> contract; change the request body
parameter from any to a stricter type (e.g., body?: unknown or body?:
Record<string, unknown>) and update callers to validate/serialize as needed,
then remove the `@ts-ignore` lines and add a runtime null-check for ApiResponse<T>
shapes inside authenticatedFetch (the block that checks 'data' in json): if
json.data is null, throw a clear error (or return a typed Result/optional)
rather than returning null so authenticatedFetch<T> always fulfills its
Promise<T> contract; update any tests/call-sites accordingly.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Around line 61-62: The mapping currently hardcodes status:
EProviderStatus.Active and verificationStatus: EVerificationStatus.Verified
which makes every search result appear active and verified; update the mapper
that builds the SearchableProviderDto (the block setting status and
verificationStatus in src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts) to use
safe defaults instead—set verificationStatus to EVerificationStatus.Pending (or
derive from actual source if available) and status to a neutral/default like
EProviderStatus.Pending/Inactive as appropriate, so ProviderCard/VerifiedBadge
reflect real provider state instead of always showing Verified/Active.
- Line 137: The mapping currently substitutes new Date().toISOString() when
dto.createdAt is missing, creating a misleading "now" timestamp; update the
mapping(s) so createdAt is left undefined if absent by replacing the fallback
expression with a direct pass-through (e.g., use dto.createdAt or dto.createdAt
?? undefined) in both mapSearchableProviderToProvider and the other mapping
function that sets createdAt at the shown line (remove the new
Date().toISOString() fallback).
In `@src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts`:
- Around line 56-99: Update the TypeScript enums to precisely match the C#
backend numeric values: change EProviderStatus members to the backend mapping
(PendingBasicInfo=1, PendingDocumentVerification=2, Active=3, Suspended=4,
Rejected=5) so comparisons like provider.status === EProviderStatus.Active
resolve correctly; replace EVerificationStatus with the full backend set and
values (None=0, Pending=1, InProgress=2, Verified=3, Rejected=4, Suspended=5);
set EDocumentType.Other to 99 to match C#; and replace EDocumentStatus with the
Documents module values (Uploaded=1, PendingVerification=2, Verified=3). Locate
and update the enums EProviderStatus, EVerificationStatus, EDocumentType, and
EDocumentStatus in provider.ts to use these exact numeric values.
---
Duplicate comments:
In `@docs/roadmap.md`:
- Line 10: The roadmap contains inconsistent MVP dates: both "28 de Março de
2026" and "14 de Março" appear; decide on the single canonical launch date and
update every occurrence in docs/roadmap.md so they match (replace all instances
of "14 de Março" or "28 de Março de 2026" depending on the chosen date).
Specifically search and replace the strings found in the sprint table section
and the phase summary text (and any other mentions) to ensure the MVP Target,
sprint table, and phase summary all show the same date.
- Around line 41-47: Update all occurrences in docs/roadmap.md to make Sprint
labels consistent: replace any instance that lists "Sprint 8B" as Mobile with
"Sprint 8B - Authentication & Onboarding Flow", ensure "Sprint 8C" is "Sprint 8C
- Mobile App (React Native + Expo)" and mark "Sprint 8D" as cancelled (keep "🚫
Sprint 8D - Admin Portal Migration (Manter Blazor)"). Search for the specific
strings "Sprint 8B", "Sprint 8C", and "Sprint 8D" (and any phase/priority
summaries mentioning them) and harmonize their descriptions so the Phase
summary, priorities, and sprint list all match the new labels.
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx`:
- Around line 5-62: Avatar currently spreads ...rest onto the wrapper <div>, so
image-specific attributes (loading, onError, referrerPolicy, sizes, decoding,
fetchPriority, etc.) never reach the <Image>; change Avatar to forward image
props to <Image> instead: update AvatarProps to separate container props from
image props (e.g., keep existing props but add imageProps?:
ComponentProps<"img"> or rename incoming rest to containerProps and imageProps),
stop spreading ...rest on the div, spread only containerProps there and pass
imageProps (or the extracted image-specific keys from rest) into the <Image>
element along with src, alt, width/height and its className; ensure types are
updated (AvatarProps and any usages) and merge className appropriately for both
wrapper and Image.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Around line 60-63: The magic-number placeholders were replaced with enums;
update the mapping in the provider DTO->model code to use the enum constants
EProviderType.Individual, EProviderStatus.Active, EVerificationStatus.Verified,
and EProviderTier.Standard (already present) and ensure you actually map a real
provider type when available—fall back to EProviderType.Individual only if the
DTO has no type; remove or update the inline comment "// Default or map if
available" so it reflects this fallback behavior in the function that constructs
the provider model.
---
Nitpick comments:
In `@src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs`:
- Around line 455-464: Extract the literal "provider-registration" into a
constant (e.g., add public const string ProviderRegistration =
"provider-registration" in RateLimitPolicies) and use that constant when calling
options.AddPolicy and when requiring the policy in
ProviderRegistrationEndpoints; also stop using the hardcoded "anonymous"
fallback for the partition key — replace partitionKey:
context.Connection.RemoteIpAddress?.ToString() ?? "anonymous" with a fallback
that uses a validated X-Forwarded-For header when present
(sanitized/whitelisted) or, if absent, a per-connection identifier such as
context.Connection.Id to avoid collapsing all null-IP requests into one shared
bucket.
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/buscar/page.tsx:
- Line 207: The mapping currently uses a redundant non-null assertion in
services={provider.services.map(s => s.serviceName!).filter(Boolean)}; remove
the unnecessary `!` and use a type-narrowing predicate so TypeScript knows falsy
values are filtered out — e.g. map to s.serviceName (no !) and then filter with
a type guard like .filter((v): v is string => Boolean(v)) so the resulting array
is correctly typed as string[] for the services prop.
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx:
- Line 20: The z.preprocess for the `type` field currently coerces any string to
EProviderType.Individual which is incorrect; update the preprocessing in the
schema that defines `type` so it either (a) maps known string values to the
corresponding EProviderType members (e.g., convert "Individual"/"Company"
strings to EProviderType.Individual/EProviderType.Company) and returns the
original value for non-string inputs, or (b) remove the coercion entirely and
use z.nativeEnum(EProviderType).optional() so strings are not silently
converted; ensure references like providerData.type and the enum EProviderType
are used to validate/convert only well-known values.
- Line 198: Replace the falsy-check fallback using || with the nullish
coalescing operator so numeric enum values like 0 aren't treated as absent:
update the VerifiedBadge prop expression that uses
providerData.verificationStatus || EVerificationStatus.Pending to use
providerData.verificationStatus ?? EVerificationStatus.Pending instead, leaving
the VerifiedBadge component and the enum EVerificationStatus unchanged.
In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx`:
- Line 10: The import for EProviderType is inconsistent—change the import source
from "@/types/provider" to "@/types/api/provider" so it matches other files
(e.g., prestador/[id]/page.tsx, prestador/page.tsx, verified-badge.tsx); update
the import statement that references EProviderType in the
cadastro/prestador/page.tsx module to import EProviderType from
"@/types/api/provider" to avoid divergence.
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-update-provider-profile.ts`:
- Around line 36-39: The onSuccess callback in the use-update-provider-profile
hook declares a parameter named data that is never used; either remove the
parameter or rename it to _data to satisfy the linter. Update the onSuccess
declaration in use-update-provider-profile.ts (the onSuccess handler that calls
queryClient.invalidateQueries and queryClient.invalidateQueries for
"providerStatus" and "myProviderProfile") so it no longer introduces an unused
variable.
- Around line 3-6: Remove the unused imports `client` and `ApiResponse` from the
top of use-update-provider-profile.ts; keep the imports needed by the hook
(e.g., `authenticatedFetch`, `ProviderDto`, `ContactInfoDto`, `AddressDto`) and
ensure no other references to `client` or `ApiResponse` remain in the file (the
hook delegates to `authenticatedFetch` which handles the client/config and
response normalization).
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts`:
- Line 28: The fetch URL in the useViaCep hook is hardcoded; extract the ViaCEP
base URL into a named constant (e.g., VIACEP_BASE_URL) and use it when building
the request (replace the inline string in the fetch call that uses cleanCep).
Update any other occurrences in useViaCep to reference the constant so tests or
environments can override it easily and keep behavior unchanged.
- Around line 41-43: The catch block in use-via-cep.ts declares an unused
variable err; remove the unused binding by changing the catch to a no-binding
catch (catch { ... }) and keep the existing setError("Falha ao consultar CEP");
return null; flow, or if you need error details, type the parameter as unknown
(catch (err: unknown)) and add a narrow/checked handling (e.g., extract message
safely) before logging or using it; update the catch around the code in the
useViaCep hook where setError is called.
- Around line 17-47: The fetchAddress useCallback can leave stale state from
overlapping requests and update state after unmount; fix it by adding an
AbortController stored in a ref (add useRef to imports) and, inside
fetchAddress, abort any previous controller.current before creating a new
controller, pass controller.signal to fetch, and set controller.current =
controller; in catch, ignore AbortError (don't call setError) and in finally
only call setIsLoading(false) when controller.signal.aborted is false (or when
the current controller still matches the one used for this fetch) to avoid
updating state from stale or unmounted requests; also add a useEffect cleanup
that aborts controller.current on unmount.
src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs
Outdated
Show resolved
Hide resolved
src/Modules/Providers/Tests/Unit/Application/Validators/AddDocumentRequestValidatorTests.cs
Outdated
Show resolved
Hide resolved
…supporting API and UI components.
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (4)
tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs (1)
56-66: Consider adding test coverage for new provider-tier features.The new provider-tier functionality introduced in
UserRoles.cslacks corresponding test coverage:
RoleConstants_ShouldHaveExpectedValuesdoesn't verify the new provider-tier constants (ProviderStandard,ProviderSilver,ProviderGold,ProviderPlatinum).IsValidRole_WithValidRole_ShouldReturnTrueInlineData (lines 73-79) doesn't include the new provider-tier role strings.- No
#region IsProviderRole Testsanalogous toIsAdminRole Tests.- No
ProviderRoles_ShouldBeSubsetOfAllRolesconsistency test.Would you like me to generate the missing test methods for the new provider-tier roles?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs` around lines 56 - 66, The tests are missing coverage for the new provider-tier roles: update RoleConstants_ShouldHaveExpectedValues to assert UserRoles.ProviderStandard, ProviderSilver, ProviderGold, and ProviderPlatinum equal their expected strings; add the provider-tier strings to the InlineData list in IsValidRole_WithValidRole_ShouldReturnTrue so those cases return true; add a new region "IsProviderRole Tests" with unit tests for the IsProviderRole method (valid provider roles -> true, non-provider roles -> false, null/empty -> false); and add a ProviderRoles_ShouldBeSubsetOfAllRoles test that verifies the UserRoles.ProviderRoles collection is a subset of UserRoles.AllRoles to ensure consistency.src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts (1)
52-58: MockbusinessProfileusesas anycast.The partial mock object is cast to
anyto bypass type checking. If theBusinessProfileDtointerface changes, this won't surface compile-time errors.♻️ Type the partial mock explicitly
businessProfile: { legalName: dto.name ?? '', contactInfo: { email: '' }, primaryAddress: { street: '', number: '', neighborhood: '', city: dto.city ?? '', state: dto.state ?? '', zipCode: '', country: 'Brasil' } - } as any, // Partial mock since Search doesn't return full profile + } as BusinessProfileDto, // Ensure import and fill required fieldsOr define a minimal
createMockBusinessProfilehelper that returns a properly-typed partial.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts` around lines 52 - 58, Replace the unsafe "as any" cast on the partial businessProfile in mappers.ts by returning a properly-typed partial: either create a small helper function createMockBusinessProfile(dto) that builds and returns a Partial<BusinessProfileDto> (populating legalName, contactInfo.email, primaryAddress fields) and use that instead of the inline object, or explicitly type the inline object as Partial<BusinessProfileDto> (not any) so future changes to BusinessProfileDto surface compile-time errors; update references to businessProfile in the mapper to accept Partial<BusinessProfileDto> if needed.src/Web/MeAjudaAi.Web.Customer/components/ui/verified-badge.tsx (1)
12-44: Missing handling forInProgress,Suspended, andNonestatuses.The component only renders badges for
Verified,Rejected, andPending. WithEVerificationStatusnow includingNone=0,InProgress=2, andSuspended=5, providers in these states will show no badge (returnsnull). Consider whetherInProgressshould display a distinct indicator or fall through toPendingstyling.♻️ Optional: Handle InProgress status
+ if (status === EVerificationStatus.InProgress) { + return ( + <div className={cn("flex items-center gap-1 text-blue-400", className)} title="Verificação em Andamento"> + <BadgeCheck size={iconSize} className="text-blue-400 animate-pulse" /> + {showLabel && <span className="font-medium text-sm">Em Análise</span>} + </div> + ); + } + if (status === EVerificationStatus.Pending) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/ui/verified-badge.tsx` around lines 12 - 44, VerifiedBadge currently only handles EVerificationStatus.Verified, Rejected, and Pending so statuses like InProgress, Suspended, and None render nothing; update VerifiedBadge to handle EVerificationStatus.InProgress (either reuse Pending styling or add a distinct look), EVerificationStatus.Suspended (treat same as Rejected or add separate styling), and EVerificationStatus.None (explicitly return null or a neutral/hidden state) by adding branches for these enum values in the VerifiedBadge function and adjusting icon/text/title/className appropriately (refer to VerifiedBadge, EVerificationStatus, and the existing branches for Verified/Rejected/Pending to copy patterns).src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx (1)
88-117: Type selector buttons may overflow on smaller viewports.The four buttons in a horizontal
flex gap-4layout will likely wrap awkwardly or overflow on mobile screens. Consider a responsive grid or wrapping strategy.♻️ Suggested responsive layout
<FormControl> - <div className="flex gap-4"> + <div className="grid grid-cols-2 gap-2 sm:flex sm:gap-4"> <Button🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx` around lines 88 - 117, The horizontal button group using <div className="flex gap-4"> with <Button> instances (tied to field and EProviderType values) can overflow on small viewports; change the layout to be responsive by enabling wrapping and stacking on narrow screens — e.g., replace the container's fixed flex with a responsive pattern (flex flex-wrap gap-4 and use responsive child sizing like w-full sm:w-auto or a responsive grid such as grid grid-cols-1 sm:grid-cols-2 gap-4) so the <Button> elements don't overflow and the active selection logic (field.onChange(EProviderType.*)) remains unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs`:
- Around line 29-43: The ContactInfo constructor can throw ArgumentException for
malformed emails which escapes the DomainException handler in
RegisterProviderCommandHandler; either validate the email format before creating
ContactInfo in RegisterProviderCommandHandler.Handle (e.g., using a reusable
EmailValidator or regex) and return a BadRequest for invalid formats, or expand
the existing exception handling block to catch ArgumentException alongside
DomainException (add ArgumentException to the catch that currently handles
DomainException) so malformed-email errors map to 400 instead of falling through
to the generic Exception handler.
- Around line 65-74: Replace the brittle string-match catch in
RegisterProviderCommandHandler with the project's
PostgreSqlExceptionProcessor-based handling: remove the DbUpdateException catch
that checks ex.InnerException?.Message.Contains("ix_providers_user_id") and
either let GlobalExceptionHandler handle the constraint violation or invoke
PostgreSqlExceptionProcessor.ProcessException(ex) and handle a returned
UniqueConstraintException accordingly (e.g., resolve existing via
providerRepository.GetByUserIdAsync(command.UserId) if needed, or rethrow/return
a conflict result). Use the PostgreSqlExceptionProcessor and
UniqueConstraintException types rather than matching error text to detect
unique-constraint violations.
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx:
- Around line 38-46: The verificationStatus z.preprocess currently maps only
'verified' and 'rejected' (defaulting all other strings to Pending), which
misses other API string values; update the preprocessing in verificationStatus
to explicitly map 'inprogress', 'suspended', and 'none' (and any other expected
string variants/casing) to their corresponding EVerificationStatus enum members
using EVerificationStatus.InProgress, EVerificationStatus.Suspended,
EVerificationStatus.None (or the exact enum member names in
EVerificationStatus), while preserving existing handling for 'verified' and
'rejected' and leaving non-string values to pass through to
z.nativeEnum(EVerificationStatus).optional().nullable().
In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx`:
- Around line 35-44: The useEffect is depending on the entire form object which
is unstable and causes extra re-renders; destructure the stable methods from the
form (e.g. const { getValues, setValue } = form) and update the effect to use
those instead of form, then change the dependency array to [session, getValues,
setValue] and keep the logic that calls getValues("name")/getValues("email") and
setValue(...) inside the effect (references: useEffect, session, form.getValues,
form.setValue).
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts`:
- Around line 32-41: In fetchAddress (the useCallback that defines
fetchAddress), move the "Cancel previous request" logic (checking and calling
abortControllerRef.current.abort()) and any stale-error clearing (e.g.,
setError(null) or similar) to occur before computing/validating cleanCep and the
early return; this ensures any in-flight request is aborted and stale errors
cleared even when the cleaned CEP length is invalid, while keeping the existing
validation (cleanCep.length !== 8) and subsequent behavior intact.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Around line 77-79: The generated SDK type
MeAjudaIModulesProvidersApplicationDtosProviderDto is missing properties
(services, averageRating, reviewCount, tier) and causing many `@ts-ignore` uses
around businessProfile and contactInfo in mappers.ts; fix by regenerating the
SDK types from the updated OpenAPI spec so the DTO matches the real API, or add
a local interface (e.g. ProviderDtoExact) that includes services: ServiceDto[],
averageRating: number, reviewCount: number, tier: string and the
optional/required shapes for businessProfile and contactInfo, replace usages of
MeAjudaIModulesProvidersApplicationDtosProviderDto (or cast to the new
interface) and remove the `@ts-ignore` lines, and update any mapping functions in
this file (look for contactInfo and businessProfile references) to use the
properly typed properties.
In `@src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts`:
- Around line 98-102: The frontend EDocumentStatus enum is missing backend
states which will break mapping for statuses 4 and 5; update the EDocumentStatus
enum (symbol: EDocumentStatus) to include Rejected = 4 and Failed = 5 so it
matches the backend contract and correctly handles API responses with those
statuses.
---
Duplicate comments:
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts`:
- Around line 46-49: The code currently returns "{} as T" for 204 No Content in
the fetch client (the branch checking response.status === 204); change this to
return undefined instead and update the function's type signature to allow
undefined (e.g., make authenticatedFetch<T> return Promise<T | undefined>) or
add a dedicated void variant (e.g., authenticatedVoidFetch) for 204 endpoints;
ensure all call sites (authenticatedFetch / authenticatedVoidFetch) are updated
to handle undefined or use the void variant accordingly rather than fabricating
an empty object.
- Around line 58-65: The current ApiResponse<T> handling in fetch-client.ts only
throws when apiRes.data === null && apiRes.isSuccess === false, but ApiResponse
allows data to be null and callers (e.g., use-update-provider-profile,
use-register-provider) expect non-nullable T; update the block that handles
ApiResponse<T> (the code using apiRes = json as ApiResponse<T>) to always throw
if apiRes.data === null (regardless of apiRes.isSuccess) and include
apiRes.message in the thrown Error to preserve context; keep returning
apiRes.data as T only when it is non-null.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Line 66: The mapper currently fabricates a timestamp by always setting
createdAt: new Date().toISOString(); modify the mapping in
src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts so that the createdAt field
uses the source value when present (e.g., parse/format source.createdAt) and
otherwise returns null/undefined (or omits the field) instead of generating the
current time; update the mapping function (e.g., mapSearchResult or the mapper
that sets createdAt) to perform a presence check and proper conversion to ISO
only when an original timestamp exists.
---
Nitpick comments:
In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx`:
- Around line 88-117: The horizontal button group using <div className="flex
gap-4"> with <Button> instances (tied to field and EProviderType values) can
overflow on small viewports; change the layout to be responsive by enabling
wrapping and stacking on narrow screens — e.g., replace the container's fixed
flex with a responsive pattern (flex flex-wrap gap-4 and use responsive child
sizing like w-full sm:w-auto or a responsive grid such as grid grid-cols-1
sm:grid-cols-2 gap-4) so the <Button> elements don't overflow and the active
selection logic (field.onChange(EProviderType.*)) remains unchanged.
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/verified-badge.tsx`:
- Around line 12-44: VerifiedBadge currently only handles
EVerificationStatus.Verified, Rejected, and Pending so statuses like InProgress,
Suspended, and None render nothing; update VerifiedBadge to handle
EVerificationStatus.InProgress (either reuse Pending styling or add a distinct
look), EVerificationStatus.Suspended (treat same as Rejected or add separate
styling), and EVerificationStatus.None (explicitly return null or a
neutral/hidden state) by adding branches for these enum values in the
VerifiedBadge function and adjusting icon/text/title/className appropriately
(refer to VerifiedBadge, EVerificationStatus, and the existing branches for
Verified/Rejected/Pending to copy patterns).
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Around line 52-58: Replace the unsafe "as any" cast on the partial
businessProfile in mappers.ts by returning a properly-typed partial: either
create a small helper function createMockBusinessProfile(dto) that builds and
returns a Partial<BusinessProfileDto> (populating legalName, contactInfo.email,
primaryAddress fields) and use that instead of the inline object, or explicitly
type the inline object as Partial<BusinessProfileDto> (not any) so future
changes to BusinessProfileDto surface compile-time errors; update references to
businessProfile in the mapper to accept Partial<BusinessProfileDto> if needed.
In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs`:
- Around line 56-66: The tests are missing coverage for the new provider-tier
roles: update RoleConstants_ShouldHaveExpectedValues to assert
UserRoles.ProviderStandard, ProviderSilver, ProviderGold, and ProviderPlatinum
equal their expected strings; add the provider-tier strings to the InlineData
list in IsValidRole_WithValidRole_ShouldReturnTrue so those cases return true;
add a new region "IsProviderRole Tests" with unit tests for the IsProviderRole
method (valid provider roles -> true, non-provider roles -> false, null/empty ->
false); and add a ProviderRoles_ShouldBeSubsetOfAllRoles test that verifies the
UserRoles.ProviderRoles collection is a subset of UserRoles.AllRoles to ensure
consistency.
src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs
Show resolved
Hide resolved
src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/app/(main)/prestador/[id]/page.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Web/MeAjudaAi.Web.Customer/components/ui/verified-badge.tsx (1)
35-44:⚠️ Potential issue | 🟡 MinorHandle InProgress/Suspended verification states.
EVerificationStatus.InProgressandSuspendedcurrently render nothing, which can hide meaningful status.🛠️ Suggested handling
- if (status === EVerificationStatus.Pending) { + if (status === EVerificationStatus.InProgress) { + return ( + <div className={cn("flex items-center gap-1 text-amber-600", className)} title="Em Análise"> + <BadgeCheck size={iconSize} className="text-amber-600" /> + {showLabel && <span className="font-medium text-sm">Em análise</span>} + </div> + ); + } + + if (status === EVerificationStatus.Suspended) { + return ( + <div className={cn("flex items-center gap-1 text-red-500", className)} title="Prestador Suspenso"> + <Ban size={iconSize} /> + {showLabel && <span className="font-medium text-sm">Suspenso</span>} + </div> + ); + } + + if (status === EVerificationStatus.Pending) { return ( <div className={cn("flex items-center gap-1 text-yellow-600", className)} title="Pendente de Verificação"> <BadgeCheck size={iconSize} className="text-yellow-600" /> {showLabel && <span className="font-medium text-sm">Pendente</span>} </div> ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/ui/verified-badge.tsx` around lines 35 - 44, The component currently only renders when status === EVerificationStatus.Pending and returns null for EVerificationStatus.InProgress and EVerificationStatus.Suspended; update the verified-badge component to handle those cases by adding branches for EVerificationStatus.InProgress and EVerificationStatus.Suspended (use the existing props status, showLabel, iconSize, className and icon components like BadgeCheck or swap to appropriate icons for each state) and render a distinct icon, color class and label for each (e.g., InProgress -> spinner/clock icon with blue/gray text and label "Em andamento"; Suspended -> warning/x icon with red/gray text and label "Suspenso") instead of returning null so those statuses are visible. Ensure to reuse the same JSX structure (div with cn(...) and conditional showLabel) and only change icon, text and color classes for each status.
🧹 Nitpick comments (4)
src/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts (1)
38-44: Consider a typed error class for attachingstatus.Using
(err as any).statusworks but bypasses type checking. A small custom error class improves downstream handling:♻️ Suggested improvement
// At the top of the file or in a shared types module class ApiError extends Error { constructor(message: string, public status: number) { super(message); this.name = 'ApiError'; } } // In the error handling block if (!response.ok) { const error = await response.json().catch(() => ({})); const userMessage = error.message || error.detail || `Request failed: ${response.statusText}`; throw new ApiError(userMessage, response.status); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts` around lines 38 - 44, Create a small typed error class (e.g., ApiError extends Error with a public status: number property and name = 'ApiError') in this module (or a shared types module) and replace the untyped cast in the response error branch inside the fetch-client error handling: instead of setting (err as any).status, throw new ApiError(userMessage, response.status) where userMessage is computed from the parsed error or response.statusText; update any callers if they currently rely on the loose shape to expect ApiError for typed status access.tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs (2)
15-28: Add test coverage for the new provider-tier roles beyondAllRoles.With the provider tiers now part of
AllRoles, consider extendingRoleConstants_ShouldHaveExpectedValuesand theIsValidRoletest cases to includeProviderStandard,ProviderSilver,ProviderGold, andProviderPlatinumso regressions in the new role surface are caught early.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs` around lines 15 - 28, Extend the existing tests to explicitly assert the new provider-tier roles: add assertions for ProviderStandard, ProviderSilver, ProviderGold, and ProviderPlatinum in the RoleConstants_ShouldHaveExpectedValues test (or equivalent that verifies role constants) and include those four values in the IsValidRole test cases that validate valid/invalid roles; update any test data arrays or parameterized test inputs that enumerate roles (e.g., the AllRoles containment check and IsValidRole parameter list) so the new provider-tier constants are individually asserted and validated.
14-16: Translate C# comments to Portuguese to comply with the language policy.Comments like
// Assertshould be in Portuguese across this file to align with the repository’s language rules.Based on learnings: Adopt a language policy across the codebase: 1) All logs must be in English, 2) Comments and XML summaries must be in Portuguese, 3) User-facing validation messages and API responses must be in Portuguese.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs` around lines 14 - 16, Replace inline C# comments in this test file (e.g., the "// Assert" comment near the assertions that reference UserRoles.AllRoles and any other single-line comments) with Portuguese equivalents (e.g., "// Asserção" or "// Verificar"), and update any XML doc summaries in the same file to Portuguese as well; leave code, identifiers (UserRoles, AllRoles, test method names) and logging text untouched and continue to keep logs in English per policy.src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts (1)
56-102: Add provider and document enums to Shared.Contracts to prevent duplication and drift.These enums duplicate backend domain definitions (e.g.,
src/Modules/Providers/Domain/Enums/EProviderType.cs). Enums should be defined inContractslayer and imported here, aligning Web with the backend source of truth.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts` around lines 56 - 102, The enums EProviderType, EProviderStatus, EVerificationStatus, EProviderTier, EDocumentType, and EDocumentStatus defined in this file should be removed and replaced by imports from the centralized Contracts package: create or reuse equivalent enums in the Shared.Contracts/Contracts layer (matching the backend domain enums), export them there, then update this file to import those enums and re-export/use them instead of redefining; ensure names (EProviderType, EProviderStatus, EVerificationStatus, EProviderTier, EDocumentType, EDocumentStatus) match exactly so existing usages compile and remove the duplicated enum declarations from provider.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/verified-badge.tsx`:
- Around line 35-44: The component currently only renders when status ===
EVerificationStatus.Pending and returns null for EVerificationStatus.InProgress
and EVerificationStatus.Suspended; update the verified-badge component to handle
those cases by adding branches for EVerificationStatus.InProgress and
EVerificationStatus.Suspended (use the existing props status, showLabel,
iconSize, className and icon components like BadgeCheck or swap to appropriate
icons for each state) and render a distinct icon, color class and label for each
(e.g., InProgress -> spinner/clock icon with blue/gray text and label "Em
andamento"; Suspended -> warning/x icon with red/gray text and label "Suspenso")
instead of returning null so those statuses are visible. Ensure to reuse the
same JSX structure (div with cn(...) and conditional showLabel) and only change
icon, text and color classes for each status.
---
Duplicate comments:
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts`:
- Around line 32-41: The early-return when CEP is invalid currently skips
cancelling any in-flight request and leaves stale error state; inside
fetchAddress, before returning null when cleanCep.length !== 8, abort any
existing request via abortControllerRef.current.abort() and set
abortControllerRef.current = null, and also clear the component's via-CEP error
state (call the appropriate setter, e.g., setViaCepError(null) or setError(null)
used in this hook) so no stale error remains.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts`:
- Around line 46-49: The code returns "{} as T" for 204 responses which fakes
the type; instead return a real empty value and update the function signature to
reflect it (e.g., return undefined or null). In fetch-client.ts, modify the
response handling for "response.status === 204" to "return undefined" (or
"null") and change the fetch wrapper's generic return type (the function that
processes the Response in this file, e.g., the fetchClient/fetchJson handler)
from Promise<T> to Promise<T | undefined> (or Promise<T | null>), then update
callers to handle the new nullable/undefined return or accept an injectable
default value.
- Around line 54-56: The current Result<T> unwrapping (the if branch checking
"json && typeof json === 'object' && 'value' in json") must also verify that
(json as any).value is not null or undefined; update that branch in
fetch-client.ts to check value !== null && value !== undefined before returning
it, and if the check fails throw or reject with a clear error (e.g., "Response
contained null/undefined value for expected Result<T>") so the function's
Promise<T> contract isn't violated; refer to the existing conditional and the
returned expression (json as any).value to locate and change the code.
- Around line 58-65: The null guard for ApiResponse<T> is too permissive; update
the logic in fetch-client.ts where you handle ApiResponse to throw whenever
apiRes.data === null (regardless of apiRes.isSuccess), using apiRes.message ||
"API interaction failed" for the Error message, so the function never returns
null as T; locate the ApiResponse<T> handling block (the const apiRes = json as
ApiResponse<T> section) and replace the current conditional (apiRes.data ===
null && apiRes.isSuccess === false) with a check that throws on apiRes.data ===
null.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Around line 60-67: The mapping currently fabricates a createdAt value with
createdAt: new Date().toISOString(); instead, stop creating phantom timestamps:
map createdAt from the source payload when present (use the original field if
available) or leave createdAt undefined/null when missing, and do not assign a
default timestamp; update the mapper that produces the object (the block using
EProviderType, EProviderStatus, EVerificationStatus, EProviderTier, documents,
qualifications) and ensure the target type/interface allows createdAt to be
optional so no fake date is emitted for search results.
- Around line 73-135: The mapper mapApiProviderToProvider is littered with
`@ts-ignore` because the generated DTO types (e.g., ProviderDto, businessProfile,
contactInfo, address, services and fields like dto.type/dto.status) are out of
sync with the actual API response; fix by either regenerating the SDK types from
the latest OpenAPI spec so ProviderDto and related types (BusinessProfileDto,
ContactInfoDto, ProviderServiceDto, etc.) match the runtime shape, or declare a
precise local interface that matches the API response and replace the `@ts-ignore`
usages by typing dto and intermediate vars (businessProfile, contactInfo,
address, services) correctly and removing all ignores; ensure fields mapped
(email, phoneNumber, serviceId, price, currency, serviceName, type/status enums)
have correct names/types and convert/cast only after validating those types in
mapApiProviderToProvider.
In `@src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts`:
- Around line 98-102: EDocumentStatus enum in provider.ts currently defines
Uploaded/PendingVerification/Verified and may be missing backend states like
Rejected/Failed; confirm the backend EDocumentStatus values and add any missing
members (e.g., Rejected, Failed) to the EDocumentStatus enum in this file with
the exact numeric values used by the backend, then update any switch/conditional
logic that consumes EDocumentStatus (search for usages of EDocumentStatus) and
adjust UI/serialization handling/tests to account for the new statuses.
---
Nitpick comments:
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts`:
- Around line 38-44: Create a small typed error class (e.g., ApiError extends
Error with a public status: number property and name = 'ApiError') in this
module (or a shared types module) and replace the untyped cast in the response
error branch inside the fetch-client error handling: instead of setting (err as
any).status, throw new ApiError(userMessage, response.status) where userMessage
is computed from the parsed error or response.statusText; update any callers if
they currently rely on the loose shape to expect ApiError for typed status
access.
In `@src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts`:
- Around line 56-102: The enums EProviderType, EProviderStatus,
EVerificationStatus, EProviderTier, EDocumentType, and EDocumentStatus defined
in this file should be removed and replaced by imports from the centralized
Contracts package: create or reuse equivalent enums in the
Shared.Contracts/Contracts layer (matching the backend domain enums), export
them there, then update this file to import those enums and re-export/use them
instead of redefining; ensure names (EProviderType, EProviderStatus,
EVerificationStatus, EProviderTier, EDocumentType, EDocumentStatus) match
exactly so existing usages compile and remove the duplicated enum declarations
from provider.ts.
In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs`:
- Around line 15-28: Extend the existing tests to explicitly assert the new
provider-tier roles: add assertions for ProviderStandard, ProviderSilver,
ProviderGold, and ProviderPlatinum in the RoleConstants_ShouldHaveExpectedValues
test (or equivalent that verifies role constants) and include those four values
in the IsValidRole test cases that validate valid/invalid roles; update any test
data arrays or parameterized test inputs that enumerate roles (e.g., the
AllRoles containment check and IsValidRole parameter list) so the new
provider-tier constants are individually asserted and validated.
- Around line 14-16: Replace inline C# comments in this test file (e.g., the "//
Assert" comment near the assertions that reference UserRoles.AllRoles and any
other single-line comments) with Portuguese equivalents (e.g., "// Asserção" or
"// Verificar"), and update any XML doc summaries in the same file to Portuguese
as well; leave code, identifiers (UserRoles, AllRoles, test method names) and
logging text untouched and continue to keep logs in English per policy.
… API mappers, backend command, and user role tests.
|
@coderabbitai resume |
✅ Actions performedReviews resumed. |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (8)
tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs (2)
76-91: New provider roles missing fromIsValidRole_WithValidRole_ShouldReturnTrue.The four new provider roles (
provider-standard,provider-silver,provider-gold,provider-platinum) are part ofAllRolesand should returntruefromIsValidRole, but they are not covered by this theory. The existingIsProviderRole_WithProviderRole_ShouldReturnTruedoes not close this gap since it only exercisesIsProviderRole, notIsValidRole.✅ Proposed fix — extend InlineData
[InlineData("customer")] +[InlineData("provider-standard")] +[InlineData("provider-silver")] +[InlineData("provider-gold")] +[InlineData("provider-platinum")] public void IsValidRole_WithValidRole_ShouldReturnTrue(string role)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs` around lines 76 - 91, The test IsValidRole_WithValidRole_ShouldReturnTrue is missing the four new provider roles; update its InlineData to include "provider-standard", "provider-silver", "provider-gold", and "provider-platinum" so that UserRoles.IsValidRole(role) is exercised for those values as well, ensuring the test covers all entries in UserRoles.AllRoles.
204-252:IsProviderRolelacks a case-insensitive theory test.
IsValidRole(lines 93-105) andIsAdminRole(lines 152-163) both have a dedicated_WithValidRoleDifferentCase_ShouldReturnTruetheory that exercisesOrdinalIgnoreCasebehavior.IsProviderRoleis implemented with the same comparer (fromsrc/Shared/Utilities/UserRoles.csline 141) but has no equivalent coverage.✅ Proposed addition — case-insensitive theory for IsProviderRole
+[Theory] +[InlineData("PROVIDER-STANDARD")] +[InlineData("Provider-Silver")] +[InlineData("PROVIDER-GOLD")] +public void IsProviderRole_WithProviderRoleDifferentCase_ShouldReturnTrue(string role) +{ + // Ação + var result = UserRoles.IsProviderRole(role); + + // Asserção + result.Should().BeTrue(); +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs` around lines 204 - 252, Add a case-insensitive theory for UserRoles.IsProviderRole similar to the existing tests for IsValidRole and IsAdminRole: create a new Theory named IsProviderRole_WithValidRoleDifferentCase_ShouldReturnTrue that passes provider role strings with varying case (e.g., "Provider-Standard", "PROVIDER-SILVER", "provider-GOLD", "PrOvIdEr-PlAtInUm") and asserts UserRoles.IsProviderRole(...) returns true; reference the UserRoles.IsProviderRole method and reuse the test naming pattern used by IsValidRole and IsAdminRole to verify OrdinalIgnoreCase behavior.src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx (1)
60-61: Redundantas numbercast.
sizePx[size]already resolves tonumber(the object's value type), so theas numberassertion adds no type safety.♻️ Proposed cleanup
- width={sizePx[size] as number} - height={sizePx[size] as number} + width={sizePx[size]} + height={sizePx[size]}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx` around lines 60 - 61, In Avatar (avatar.tsx) the props width={sizePx[size] as number} and height={sizePx[size] as number} use a redundant type assertion; remove the "as number" casts and pass width={sizePx[size]} and height={sizePx[size]} directly (ensure the sizePx constant/lookup is correctly typed as mapping to number so TypeScript still infers number for sizePx and size).src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts (1)
75-75: Optional: replaceconsole.errorwith structured logging.
console.errorin a production hook is noisy and uncontrolled. Consider wiring into whatever logging/observability layer the project uses, or at least guarding it behind aprocess.env.NODE_ENV !== 'production'check.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts` at line 75, The console.error(err) call inside the use-via-cep hook should be replaced with the project's structured logging or at minimum guarded in non-production; locate the error handling in the useViaCep hook (the function where console.error(err) is used) and either call the shared logger (e.g., logger.error with a contextual message and the err object) or wrap the console.error call with a check like process.env.NODE_ENV !== 'production' so that logs in production use the centralized observability layer instead of console.error.src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs (1)
67-80:throw processedExon line 80 silently exits theResult<ProviderDto>contract for non-unique-constraint DB errors.When
PostgreSqlExceptionProcessor.ProcessException(ex)returns anything other thanUniqueConstraintException(FK violation, check constraint, etc.), or whenexistingisnulldespite a unique constraint hit, the re-thrown exception propagates out ofHandleAsyncuncaught. The siblingcatch (Exception ex)at line 87 cannot intercept exceptions thrown from anothercatchblock in C#—only middleware (GlobalExceptionHandler) will catch them. This is intentional given the codebase's pattern, but the inconsistency means callers of this method get an exception instead of aResultfor unexpected DB errors.Consider wrapping in a final
Result.Failureor explicitly documenting the reliance on middleware for these paths:♻️ Optional: make the handler fully Result-returning for DB errors
catch (DbUpdateException ex) { var processedEx = PostgreSqlExceptionProcessor.ProcessException(ex); if (processedEx is UniqueConstraintException) { logger.LogWarning(ex, "Duplicate provider registration attempt for user {UserId}", command.UserId); var existing = await providerRepository.GetByUserIdAsync(command.UserId, cancellationToken); if (existing is not null) { return Result<ProviderDto>.Success(existing.ToDto()); } } - throw processedEx; + logger.LogError(processedEx, "Database error in RegisterProviderCommand for user {UserId}", command.UserId); + return Result<ProviderDto>.Failure(new Error("Erro inesperado ao registrar prestador.", 500)); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs` around lines 67 - 80, In RegisterProviderCommandHandler.HandleAsync, do not re-throw the processed exception from PostgreSqlExceptionProcessor.ProcessException(ex); instead handle non-UniqueConstraintException and the case where UniqueConstraintException yields a null existing provider by logging the error (use logger) and returning a Result<ProviderDto>.Failure with an appropriate error message/details so the method always returns a Result; keep the existing branch that returns Result<ProviderDto>.Success(existing.ToDto()) when existing is found, but replace the throw processedEx and the null path after providerRepository.GetByUserIdAsync with calls to Result<ProviderDto>.Failure (including processedEx.Message or a mapped error code) to preserve the Result contract.src/Web/MeAjudaAi.Web.Customer/app/(main)/prestador/[id]/page.tsx (2)
20-28:typepreprocess only handles numeric strings, unlikeverificationStatuswhich maps named strings.The
verificationStatuspreprocess (lines 38-49) maps named strings like'verified','rejected', etc. to enum values. But thetypepreprocess only doesparseIntfor numeric strings — if the API returns"Individual"or"Company"as a string, it will produceNaN, pass through as the raw string, and failz.nativeEnum(EProviderType)validation.Consider adding named string mapping for consistency:
Proposed fix
type: z.preprocess((val) => { if (typeof val === 'string') { - // Basic mapping from string to Enum if needed, or rely on nativeEnum to handle values - // If val comes as "1", "2" string, we might need to parse int. const parsed = parseInt(val, 10); if (!isNaN(parsed)) return parsed; + const lower = val.toLowerCase(); + if (lower === 'individual') return EProviderType.Individual; + if (lower === 'company') return EProviderType.Company; + if (lower === 'freelancer') return EProviderType.Freelancer; + if (lower === 'cooperative') return EProviderType.Cooperative; + if (lower === 'none') return EProviderType.None; } return val; }, z.nativeEnum(EProviderType).optional()),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx around lines 20 - 28, The type preprocess for the Zod schema (the preprocess passed for the 'type' field) only parses numeric strings but doesn't map named string values to the EProviderType enum like the verificationStatus preprocess does; update the preprocess for 'type' to detect named strings (e.g., 'Individual', 'Company' and common lowercase variants or API-specific names) and map them to the corresponding EProviderType enum members before returning, while still handling numeric-string parsing and returning the raw value otherwise so z.nativeEnum(EProviderType).optional() can validate correctly.
86-90: 404 detection via string matching onerror.messageis fragile.Line 87 checks
error.message.includes("404")which could match unrelated messages containing "404". The explicitres.status === 404check on line 61 already handles the fetch response case. This catch block pattern could mask real errors if their messages happen to contain "404".Consider narrowing this to only re-throw:
Proposed simplification
} catch (error) { - if (error instanceof Error && (error.message.includes("404") || (error as { status?: number }).status === 404)) return null; console.error(`Exception fetching public provider ${id}:`, error); throw error; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx around lines 86 - 90, The catch block in page.tsx currently treats any error whose message includes "404" as a not-found and returns null; remove the fragile string-match and only treat explicit 404 cases (e.g., where (error as { status?: number }).status === 404 or a thrown NotFoundError) as not-found. Update the catch in the async fetch/loader (the block that logs "Exception fetching public provider ${id}:") to drop error.message.includes("404") and instead rely on the existing response-status handling or a specific error type/`status` property; for all other errors, log and rethrow unchanged.src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts (1)
52-58:as anyon partialbusinessProfilesilently drops type enforcement.The search DTO doesn't carry a full
BusinessProfileDto, so the partial object is cast toany. Consider typing it asPartial<BusinessProfileDto>and makingbusinessProfileoptional onProviderDto, or create a minimal search-specific type. This would catch field-access errors downstream instead of silently passing.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts` around lines 52 - 58, The current mapper in mappers.ts casts a partial businessProfile to any which removes type safety; change the businessProfile assignment to use a proper type (e.g., Partial<BusinessProfileDto>) instead of as any, and update ProviderDto to make businessProfile optional or introduce a SearchProviderDto/minimal DTO that defines businessProfile as Partial<BusinessProfileDto>; ensure the mapper returns that typed shape (referencing businessProfile, ProviderDto, and BusinessProfileDto) so downstream code cannot silently access missing fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx:
- Around line 13-16: Duplicate comment "// Zod Schema for Runtime Validation"
appears twice; remove the redundant instance so the file only contains a single
comment occurrence. Edit the page component file (page.tsx) and delete one of
the duplicate comment lines adjacent to the imports (keep the one that best
matches surrounding context), ensuring imports like EVerificationStatus and
EProviderType remain unchanged and the file compiles.
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/verified-badge.tsx`:
- Around line 26-33: The title tooltip in the JSX branch that checks status ===
EVerificationStatus.Rejected still reads "Prestador Suspenso/Rejeitado"; update
that title to match the visible label (e.g., "Rejeitado" or "Prestador
Rejeitado") in the component (verified-badge.tsx) so the Rejected branch’s div
(the one rendering <Ban size={iconSize} /> and using className, showLabel and
iconSize) uses the corrected title text.
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts`:
- Around line 33-46: The loading flag can remain true when an in-flight fetch is
aborted then an early return happens; to fix, ensure setIsLoading(false) is
invoked whenever you abort/clear the current controller or when you early-return
for an invalid CEP. Concretely, inside use-via-cep.ts update the abort block
that manipulates abortControllerRef (and the similar block at 78–82) to call
setIsLoading(false) after aborting and before nulling
abortControllerRef.current, and/or call setIsLoading(false) immediately before
returning when cleanCep.length !== 8; keep the existing finally handler that
checks the controller variable (the same controller created for the fetch)
untouched so it still clears loading for normal completion.
---
Duplicate comments:
In `@src/Web/MeAjudaAi.Web.Customer/app/cadastro/prestador/page.tsx`:
- Around line 34-45: The effect is re-running because the whole form object
(which can be unstable) is in the dependency array; pull stable callbacks out
and depend on them instead — destructure const { getValues, setValue } = form
outside the useEffect and then change the effect dependencies to [session,
getValues, setValue] (or otherwise use stable refs) so the useEffect that sets
name/email only runs when session or the actual form methods change; update the
useEffect callback to use those extracted getValues and setValue references.
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx`:
- Around line 57-64: The Image in Avatar component uses external absolute avatar
URLs (see Image usage in avatar.tsx), so update next.config.ts to include
images.remotePatterns entries for the OAuth hosts (e.g.,
lh3.googleusercontent.com and avatars.githubusercontent.com) allowing https
hostnames and appropriate path patterns; ensure the exported NextConfig includes
this images.remotePatterns block and that the config is being used by the build
so next/image will accept those external avatar URLs at runtime.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Around line 108-175: The mapApiProviderToProvider function currently uses many
unsafe casts (e.g., as unknown as, as any, and : any) around LocalProviderDto,
dto.type, dto.status, dto.verificationStatus, documents and qualifications;
regenerate the SDK types (types.gen.ts) from the updated OpenAPI spec to restore
correct typings, then replace the ad-hoc casts in mapApiProviderToProvider with
the proper generated types so you can remove LocalProviderDto shims and narrow
DTO properties (documents, qualifications, type/status enums,
createdAt/updatedAt) to their real types; after regen, update mapping logic to
use the concrete SDK interfaces instead of as any/unknown and adjust any
nullable/optional handling accordingly.
In `@src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts`:
- Around line 98-102: The frontend enum EDocumentStatus is missing backend
values Rejected = 4 and Failed = 5; update the EDocumentStatus enum to include
Rejected = 4 and Failed = 5 so numeric values match the backend contract (ensure
any switch/map logic consuming EDocumentStatus handles these new members).
---
Nitpick comments:
In
`@src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs`:
- Around line 67-80: In RegisterProviderCommandHandler.HandleAsync, do not
re-throw the processed exception from
PostgreSqlExceptionProcessor.ProcessException(ex); instead handle
non-UniqueConstraintException and the case where UniqueConstraintException
yields a null existing provider by logging the error (use logger) and returning
a Result<ProviderDto>.Failure with an appropriate error message/details so the
method always returns a Result; keep the existing branch that returns
Result<ProviderDto>.Success(existing.ToDto()) when existing is found, but
replace the throw processedEx and the null path after
providerRepository.GetByUserIdAsync with calls to Result<ProviderDto>.Failure
(including processedEx.Message or a mapped error code) to preserve the Result
contract.
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx:
- Around line 20-28: The type preprocess for the Zod schema (the preprocess
passed for the 'type' field) only parses numeric strings but doesn't map named
string values to the EProviderType enum like the verificationStatus preprocess
does; update the preprocess for 'type' to detect named strings (e.g.,
'Individual', 'Company' and common lowercase variants or API-specific names) and
map them to the corresponding EProviderType enum members before returning, while
still handling numeric-string parsing and returning the raw value otherwise so
z.nativeEnum(EProviderType).optional() can validate correctly.
- Around line 86-90: The catch block in page.tsx currently treats any error
whose message includes "404" as a not-found and returns null; remove the fragile
string-match and only treat explicit 404 cases (e.g., where (error as { status?:
number }).status === 404 or a thrown NotFoundError) as not-found. Update the
catch in the async fetch/loader (the block that logs "Exception fetching public
provider ${id}:") to drop error.message.includes("404") and instead rely on the
existing response-status handling or a specific error type/`status` property;
for all other errors, log and rethrow unchanged.
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx`:
- Around line 60-61: In Avatar (avatar.tsx) the props width={sizePx[size] as
number} and height={sizePx[size] as number} use a redundant type assertion;
remove the "as number" casts and pass width={sizePx[size]} and
height={sizePx[size]} directly (ensure the sizePx constant/lookup is correctly
typed as mapping to number so TypeScript still infers number for sizePx and
size).
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-via-cep.ts`:
- Line 75: The console.error(err) call inside the use-via-cep hook should be
replaced with the project's structured logging or at minimum guarded in
non-production; locate the error handling in the useViaCep hook (the function
where console.error(err) is used) and either call the shared logger (e.g.,
logger.error with a contextual message and the err object) or wrap the
console.error call with a check like process.env.NODE_ENV !== 'production' so
that logs in production use the centralized observability layer instead of
console.error.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Around line 52-58: The current mapper in mappers.ts casts a partial
businessProfile to any which removes type safety; change the businessProfile
assignment to use a proper type (e.g., Partial<BusinessProfileDto>) instead of
as any, and update ProviderDto to make businessProfile optional or introduce a
SearchProviderDto/minimal DTO that defines businessProfile as
Partial<BusinessProfileDto>; ensure the mapper returns that typed shape
(referencing businessProfile, ProviderDto, and BusinessProfileDto) so downstream
code cannot silently access missing fields.
In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs`:
- Around line 76-91: The test IsValidRole_WithValidRole_ShouldReturnTrue is
missing the four new provider roles; update its InlineData to include
"provider-standard", "provider-silver", "provider-gold", and "provider-platinum"
so that UserRoles.IsValidRole(role) is exercised for those values as well,
ensuring the test covers all entries in UserRoles.AllRoles.
- Around line 204-252: Add a case-insensitive theory for
UserRoles.IsProviderRole similar to the existing tests for IsValidRole and
IsAdminRole: create a new Theory named
IsProviderRole_WithValidRoleDifferentCase_ShouldReturnTrue that passes provider
role strings with varying case (e.g., "Provider-Standard", "PROVIDER-SILVER",
"provider-GOLD", "PrOvIdEr-PlAtInUm") and asserts UserRoles.IsProviderRole(...)
returns true; reference the UserRoles.IsProviderRole method and reuse the test
naming pattern used by IsValidRole and IsAdminRole to verify OrdinalIgnoreCase
behavior.
src/Web/MeAjudaAi.Web.Customer/app/(main)/prestador/[id]/page.tsx
Outdated
Show resolved
Hide resolved
…lic "become provider" endpoint and introduce a new base class for integration tests.
There was a problem hiding this comment.
🧹 Nitpick comments (3)
src/Modules/Providers/API/Endpoints/Public/BecomeProviderEndpoint.cs (1)
62-62: Hardcoded location URI is fragile.The
"/api/v1/providers/me"string duplicates the route prefix logic fromBaseEndpoint.CreateVersionedGroup. If the API version or module name changes, this will silently become stale.Consider building the URI dynamically or using a named route with
TypedResults.CreatedAtRoute.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Providers/API/Endpoints/Public/BecomeProviderEndpoint.cs` at line 62, The return uses a hardcoded URI string in BecomeProviderEndpoint which will break if BaseEndpoint.CreateVersionedGroup route/version changes; instead register the provider GET route with a route name (e.g., "GetCurrentProvider" or reuse an existing named route) and replace Results.Created($"/api/v1/providers/me", ...) with Results.CreatedAtRoute(routeName, new { /* route values: id or "me" */ }, new Response<ProviderDto>(result.Value!)); ensure the named route is defined on the corresponding GET endpoint and pass the correct route values so the location is generated dynamically.tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs (2)
479-486: Broadcatch (Exception)silently swallows non-PostGIS failures.
catch (Exception ex)will also catchOperationCanceledException, Npgsql authentication errors, or a wrong connection string, masking real infrastructure problems. The warning message alone ("Migrations might fail if not included") may not be enough signal for those cases.Consider narrowing the catch to PostGIS-specific errors:
♻️ Proposed refinement
-catch (Exception ex) +catch (Npgsql.PostgresException ex) { - logger?.LogWarning(ex, "⚠️ Failed to explicitly create PostGIS extension. Migrations might fail if not included."); + logger?.LogWarning(ex, "Failed to create PostGIS extension (SqlState={SqlState}). Migrations may fail if PostGIS is unavailable.", ex.SqlState); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs` around lines 479 - 486, The current try/catch around context.Database.ExecuteSqlRawAsync("CREATE EXTENSION IF NOT EXISTS postgis;") swallows all exceptions; change it to only catch Postgres/Npgsql errors related to CREATE EXTENSION (e.g., catch Npgsql.PostgresException or PostgresException) and log the warning via logger.LogWarning(ex, ...) for those cases, while letting other exceptions (like OperationCanceledException, authentication, or connection string errors) bubble up (or rethrow) so infrastructure failures are not masked; keep the existing warning message for Postgres-specific exceptions and ensure other unexpected exceptions are not caught silently.
476-479: New inline comments are in English — should be in Portuguese.The three comments introduced in the changed segment are in English. Per the project's coding guidelines, all in-code comments must be written in Portuguese (log messages remain in English).
♻️ Proposed translation
- // Ensure PostGIS extension exists (required for geometry types) - // This is necessary because EnsureDeletedAsync drops the database and extension - // And we need it before SearchProviders migrations if they use geometry types + // Garante que a extensão PostGIS existe (obrigatória para tipos de geometria) + // Necessário pois EnsureDeletedAsync remove o banco e a extensão + // Deve ser executado antes das migrations do SearchProviders que usam tipos de geometriaBased on learnings: "In all C# files, keep logging messages in English, but ensure comments and XML documentation are written in Portuguese across the codebase."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs` around lines 476 - 479, Os comentários recém-adicionados acima do bloco try (os três comentários que explicam a necessidade da extensão PostGIS antes das migrations do SearchProviders e que EnsureDeletedAsync apaga a base e a extensão) estão em inglês; traduza-os para português mantendo o mesmo significado e contexto (por exemplo referenciando "extensão PostGIS", "EnsureDeletedAsync" e "migrations do SearchProviders") para cumprir a diretriz do projeto de comentários em português enquanto preserva logs/strings em inglês.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/Modules/Providers/API/Endpoints/Public/BecomeProviderEndpoint.cs`:
- Line 62: The return uses a hardcoded URI string in BecomeProviderEndpoint
which will break if BaseEndpoint.CreateVersionedGroup route/version changes;
instead register the provider GET route with a route name (e.g.,
"GetCurrentProvider" or reuse an existing named route) and replace
Results.Created($"/api/v1/providers/me", ...) with
Results.CreatedAtRoute(routeName, new { /* route values: id or "me" */ }, new
Response<ProviderDto>(result.Value!)); ensure the named route is defined on the
corresponding GET endpoint and pass the correct route values so the location is
generated dynamically.
In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs`:
- Around line 479-486: The current try/catch around
context.Database.ExecuteSqlRawAsync("CREATE EXTENSION IF NOT EXISTS postgis;")
swallows all exceptions; change it to only catch Postgres/Npgsql errors related
to CREATE EXTENSION (e.g., catch Npgsql.PostgresException or PostgresException)
and log the warning via logger.LogWarning(ex, ...) for those cases, while
letting other exceptions (like OperationCanceledException, authentication, or
connection string errors) bubble up (or rethrow) so infrastructure failures are
not masked; keep the existing warning message for Postgres-specific exceptions
and ensure other unexpected exceptions are not caught silently.
- Around line 476-479: Os comentários recém-adicionados acima do bloco try (os
três comentários que explicam a necessidade da extensão PostGIS antes das
migrations do SearchProviders e que EnsureDeletedAsync apaga a base e a
extensão) estão em inglês; traduza-os para português mantendo o mesmo
significado e contexto (por exemplo referenciando "extensão PostGIS",
"EnsureDeletedAsync" e "migrations do SearchProviders") para cumprir a diretriz
do projeto de comentários em português enquanto preserva logs/strings em inglês.
…with new UI components and API integration.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (10)
tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs (2)
47-53: Add a mirror test forProviderRolescontent and count.
AdminRoles_ShouldContainOnlyAdminRoles(line 33) andCustomerRoles_ShouldContainCustomerRole(line 48) both pin the count and exact members of their collections.ProviderRoleshas no equivalent, meaning an accidental modification to the array (e.g., a role added or removed) would go undetected until the subset consistency test or a runtime bug surfaces it.♻️ Proposed addition (place alongside the other Constants Tests)
+[Fact] +public void ProviderRoles_ShouldContainOnlyProviderRoles() +{ + // Asserção + UserRoles.ProviderRoles.Should().HaveCount(4); + UserRoles.ProviderRoles.Should().Contain(new[] + { + UserRoles.ProviderStandard, + UserRoles.ProviderSilver, + UserRoles.ProviderGold, + UserRoles.ProviderPlatinum + }); +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs` around lines 47 - 53, Add a new unit test that mirrors the existing CustomerRoles_ShouldContainCustomerRole to assert ProviderRoles' count and exact members: create a test (e.g., ProviderRoles_ShouldContainOnlyProviderRoles) that uses UserRoles.ProviderRoles and verifies the expected number of roles with HaveCount(...) and that it contains the exact role constants from UserRoles.Provider (or the specific provider role constants), following the pattern used by AdminRoles_ShouldContainOnlyAdminRoles and CustomerRoles_ShouldContainCustomerRole so any accidental additions/removals to ProviderRoles are caught.
169-179: Add provider-tier roles toIsAdminRole_WithNonAdminRole_ShouldReturnFalsecoverage.The four new provider roles are valid system roles but are not admin roles — they should be explicitly asserted as such to prevent regression if
AdminRolesis inadvertently expanded. Currently only"customer"and"viewer"are tested.♻️ Proposed addition
[Theory] [InlineData("customer")] [InlineData("viewer")] +[InlineData("provider-standard")] +[InlineData("provider-silver")] +[InlineData("provider-gold")] +[InlineData("provider-platinum")] public void IsAdminRole_WithNonAdminRole_ShouldReturnFalse(string role)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs` around lines 169 - 179, Update the IsAdminRole_WithNonAdminRole_ShouldReturnFalse test to include the four provider-tier role values so they are explicitly asserted non-admin; add additional [InlineData(...)] entries for each provider role string (or reference the constants if provider role constants exist) and ensure the test calls UserRoles.IsAdminRole(role) and asserts result.Should().BeFalse() for those values (keep the test name and assertion logic unchanged).src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx (2)
60-61: Unnecessaryas numbercasts.
sizePx[size]is already typed asnumber— the casts are redundant.Suggested cleanup
- width={sizePx[size] as number} - height={sizePx[size] as number} + width={sizePx[size]} + height={sizePx[size]}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx` around lines 60 - 61, The width/height props in the Avatar component are using redundant casts — remove the unnecessary "as number" from the assignments where you set width={sizePx[size] as number} and height={sizePx[size] as number}; instead use width={sizePx[size]} and height={sizePx[size]}; ensure the referenced symbol sizePx retains a number-typed index signature (e.g., number[] or Record<... , number>) and that the prop names/variables size and sizePx in avatar.tsx remain correctly typed so the compiler accepts the direct usage.
5-5: Type derivation mismatch:AvatarPropsshould derive fromImagePropsinstead of native img props.
AvatarPropsextendsOmit<ComponentProps<"img">, "src">, but the component rendersnext/image<Image>. While current usages only pass safe props (src,alt,className,fallback), this type structure allows consumers to pass native img props incompatible with<Image>(e.g.,srcSet,useMap).Consider using
ImagePropsas the base to align the type with the rendered component and prevent future misuse. Also, theas numbercast on lines 60–61 is unnecessary sincesizePx[size]is already typed asnumber.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx` at line 5, Change AvatarProps to extend Next.js ImageProps instead of ComponentProps<"img"> so the prop types match the rendered <Image> component (replace the current extends Omit<ComponentProps<"img">, "src"> with something based on ImageProps and omit/adjust any conflicting fields like "src" if needed); update any imports to pull ImageProps from "next/image"; also remove the unnecessary "as number" casts around sizePx[size] in the avatar sizing logic since sizePx is already typed as number and the cast is redundant (refer to AvatarProps, sizePx and the usage of size in the component).src/Modules/Providers/API/Endpoints/Public/BecomeProviderEndpoint.cs (1)
66-72: Add input validation toRegisterProviderApiRequest.
NameandDocumentNumberare non-nullable strings with no length or format constraints, andTypehas no[EnumDataType]guard. Without annotations (or a registered FluentValidation validator), the model binder will silently accept empty strings, oversized payloads, or out-of-range enum values.♻️ Proposed fix
+using System.ComponentModel.DataAnnotations; + public record RegisterProviderApiRequest( + [Required, MinLength(2), MaxLength(100)] string Name, + [EnumDataType(typeof(EProviderType))] EProviderType Type, + [Required, MinLength(5), MaxLength(20)] string DocumentNumber, string? PhoneNumber, string? Email );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Providers/API/Endpoints/Public/BecomeProviderEndpoint.cs` around lines 66 - 72, RegisterProviderApiRequest lacks validation attributes on Name, DocumentNumber and Type; add data-annotation attributes to enforce non-empty and size constraints and validate the enum. Annotate Name with [Required] and a reasonable [StringLength(max)] (and optionally [MinLength]) to prevent empty/oversized values; annotate DocumentNumber with [Required], a [StringLength(max)] and an appropriate [RegularExpression] if a format is expected; and annotate Type with [Required] and [EnumDataType(typeof(EProviderType))] to reject out-of-range enum values. Ensure nullable PhoneNumber and Email keep appropriate validation (e.g. [Phone], [EmailAddress]) if desired.src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs (1)
80-84: Inline comments should be in Portuguese per language policy.Lines 80–81 and 84 use English comments, but the project convention requires comments to be in Portuguese (logs stay in English, comments/XML in Portuguese).
Suggested translation
- // If we have a unique constraint violation but can't find the provider, - // it implies a race condition or data inconsistency that we should report as a failure + // Se houve violação de constraint única mas o prestador não foi encontrado, + // isso indica uma condição de corrida ou inconsistência de dados return Result<ProviderDto>.Failure(new Error("Um prestador já está registrado para este usuário.", 409)); } - // For other database errors, rethrow to be handled by global exception handler or outer catch + // Para outros erros de banco de dados, relançar para ser tratado pelo handler global de exceções throw processedEx;Based on learnings: "Comments and XML summaries (///,
, , etc.) must be in Portuguese."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs` around lines 80 - 84, Os comentários em inglês dentro do manipulador RegisterProviderCommandHandler (na seção que trata da violação de constraint única e do rethrow para o handler global) devem ser convertidos para português; localize o trecho que retorna Result<ProviderDto>.Failure(...) (e o comentário logo após) e substitua os comentários em inglês por comentários em português mantendo o código inalterado (mensagens de erro/logs em inglês permanecem como estão) — garanta que as observações sobre condição de corrida/consistência de dados e sobre rethrow para o handler global sejam expressas claramente em português.src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts (3)
52-58: Avoidas anyon the mockbusinessProfile.The
as anycast silently hides shape mismatches between this mock object andBusinessProfileDto. Since the targetProviderDto.businessProfileexpectsContactInfoDtoandAddressDto, type the mock properly or use a type assertion toBusinessProfileDtoso you get compile-time feedback if the shape drifts.♻️ Proposed fix
+ businessProfile: { + legalName: dto.name ?? '', + fantasyName: null, + description: null, + contactInfo: { email: '' }, + primaryAddress: { + street: '', number: '', neighborhood: '', city: dto.city ?? '', state: dto.state ?? '', zipCode: '', country: 'Brasil' + } + } as BusinessProfileDto, - businessProfile: { - legalName: dto.name ?? '', - contactInfo: { email: '' }, - primaryAddress: { - street: '', number: '', neighborhood: '', city: dto.city ?? '', state: dto.state ?? '', zipCode: '', country: 'Brasil' - } - } as any, // Partial mock since Search doesn't return full profileAdd
BusinessProfileDtoto the import on line 5:import type { ProviderDto, BusinessProfileDto } from '@/types/api/provider';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts` around lines 52 - 58, The mock businessProfile is cast with "as any", hiding shape mismatches; update the mapper to import and use the proper BusinessProfileDto type and replace "as any" with a typed object or a type assertion to BusinessProfileDto so TypeScript enforces the expected ContactInfoDto and AddressDto shapes for ProviderDto.businessProfile; ensure imports include BusinessProfileDto (alongside ProviderDto) and that the object fields match the BusinessProfileDto structure used by the code paths that consume businessProfile.
210-221: Triple casts (as unknown as number) as any) defeat the purpose of the type extension.Lines 213 and 216 use
(d.documentType as unknown as number) as anyand(d.status as unknown as number) as any. SinceExtendedProviderDtoalready definesdocumentTypeandstatusasnumber, these casts should be unnecessary. If the targetDocumentDtouses enums (EDocumentType,EDocumentStatus), cast directly to those enums instead ofany.♻️ Proposed fix
documents: dto.documents?.map(d => ({ id: d.id ?? '', providerId: d.providerId ?? '', - documentType: (d.documentType as unknown as number) as any, + documentType: (d.documentType ?? 0) as EDocumentType, fileName: d.fileName ?? '', fileUrl: d.fileUrl ?? '', - status: (d.status as unknown as number) as any, + status: (d.status ?? 1) as EDocumentStatus, uploadedAt: d.uploadedAt ?? '', verifiedAt: d.verifiedAt, rejectionReason: d.rejectionReason, ocrData: d.ocrData })) || [],You'll need to add
EDocumentTypeandEDocumentStatusto the import on line 8:import { EProviderType, EProviderStatus, EVerificationStatus, EProviderTier, EDocumentType, EDocumentStatus, ProviderServiceDto } from '@/types/api/provider';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts` around lines 210 - 221, The triple-cast usage in the mapper for documents is unnecessary and unsafe: replace `(d.documentType as unknown as number) as any` and `(d.status as unknown as number) as any` with direct casts to the target enums (EDocumentType and EDocumentStatus) so DocumentDto.documentType and DocumentDto.status get proper enum types; add EDocumentType and EDocumentStatus to the import list (alongside EProviderType/EProviderStatus/etc.) and change the mapping inside documents (the mapping function that references documentType and status on the ExtendedProviderDto `d`) to cast to those enums instead of any.
70-103: Remove unused interface definitionsLocalBusinessProfileDtoandLocalProviderDto.These two interfaces (lines 70–103) are defined but never used anywhere in the file or project.
mapApiProviderToProviderusesExtendedProviderDtoinstead, making these definitions redundant dead code.♻️ Proposed fix
-// Interface definitions to match actual API response structure to avoid generation sync issues -interface LocalBusinessProfileDto { - legalName: string; - fantasyName?: string; - description?: string; - contactInfo?: { - email: string; - phoneNumber: string; - }; - primaryAddress?: { - street: string; - number: string; - complement?: string; - neighborhood: string; - city: string; - state: string; - zipCode: string; - country: string; - }; -} - -interface LocalProviderDto extends MeAjudaAiModulesProvidersApplicationDtosProviderDto { - businessProfile?: LocalBusinessProfileDto; - services?: Array<{ - serviceId: string; - price: number; - currency: string; - serviceName: string; - }>; - averageRating?: number; - reviewCount?: number; - tier?: EProviderTier; - createdAt?: string; -}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts` around lines 70 - 103, Remove the dead interfaces LocalBusinessProfileDto and LocalProviderDto from this file: they are not referenced (mapApiProviderToProvider uses ExtendedProviderDto), so delete both interface declarations and any unused imports that were only needed for them; search for LocalBusinessProfileDto and LocalProviderDto to ensure no external usages, update mapApiProviderToProvider if it incorrectly referenced them, then run the TypeScript build/linters to confirm no type errors remain.src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts (1)
119-148: Consider sourcing these enums from a shared contracts package.The enums and DTOs here are manually synchronized with the C# backend. Per project conventions, reusing
Shared.Contractsfor enums/constants helps prevent future drift. This is fine for now but worth tracking as a follow-up. Based on learnings, prefer reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts` around lines 119 - 148, ProviderDto currently declares enum types (EProviderType, EProviderStatus, EVerificationStatus, EProviderTier) that are manually synchronized with backend; update ProviderDto to import and use the shared enums/constants from the centralized Shared.Contracts package instead of local copies. Locate occurrences of EProviderType, EProviderStatus, EVerificationStatus, and EProviderTier in the ProviderDto declaration and replace them with the imported symbols from Shared.Contracts (ensuring the BusinessProfileDto/DocumentDto/QualificationDto/ProviderServiceDto DTOs stay compatible), add the appropriate import statements at the top of the file, and remove or mark any local duplicate enum definitions so the web types reference the single source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Modules/Providers/API/Endpoints/Public/BecomeProviderEndpoint.cs`:
- Around line 38-44: The handler in BecomeProviderEndpoint.cs currently falls
back to request.Email when the JWT lacks an email claim (variable email from
context.User?.FindFirst("email")?.Value), allowing unverified client-supplied
addresses; change the logic so that if the token has no email claim you
immediately return BadRequest("Email é obrigatório e não foi encontrado no
token.") instead of reading request.Email, i.e., remove the fallback to
request.Email and require the email-only from the JWT
(context.User.FindFirst("email")), ensuring provider creation uses only the
verified token claim.
In
`@src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs`:
- Around line 80-85: Replace the incorrect Error constructor call in
RegisterProviderCommandHandler that passes two strings; the Error type expects
(string Message, int StatusCode). Construct the Error so the second argument is
the HTTP 409 status code and include the Portuguese message in the first
argument (or combine the key and message into the Message string) before
returning Result<ProviderDto>.Failure; reference:
RegisterProviderCommandHandler, ProviderDto, Error.
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx:
- Around line 88-94: Remove the dead 404 branch in the catch block: the thrown
errors from the try (plain Error instances created earlier) never have a status,
so delete the `(error as { status?: number }).status === 404` check and its
`return null;` path; instead, in the catch for the public provider fetch (the
block that logs `Exception fetching public provider ${id}:`), just log the error
and rethrow it (keep the existing console.error and `throw error;`) so error
handling relies on the earlier `res.status === 404` check.
- Around line 20-32: The type preprocess in page.tsx currently converts several
string labels to EProviderType but omits the 'none' case causing validation
failures; update the z.preprocess for the type field to detect when val (string)
lowercased === 'none' and return EProviderType.None (the enum value 0) before
attempting parseInt, so z.nativeEnum(EProviderType).optional() receives a valid
enum value.
---
Duplicate comments:
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx:
- Around line 13-16: Remove the duplicated comment string "// Zod Schema for
Runtime Validation" so it only appears once; locate the two identical comments
surrounding the import of EVerificationStatus and EProviderType in the
prestador/[id]/page.tsx module and delete the redundant occurrence, leaving a
single clear comment above the relevant Zod-related imports or declarations.
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx`:
- Around line 57-64: The Avatar component uses next/image with external OAuth
avatar URLs and will fail unless next.config.ts includes those hostnames in
images.remotePatterns; update next.config.ts (images.remotePatterns) to allow
common OAuth avatar hosts such as googleusercontent.com,
lh3.googleusercontent.com, avatars.githubusercontent.com (and any other provider
hostnames you expect), specifying the proper protocol/hostname patterns so the
Image in the Avatar component can load external avatars without runtime errors.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Around line 148-149: The double-cast "const dto = rawDto as unknown as
ExtendedProviderDto" bypasses type safety; add a TODO comment immediately above
this line referencing the tracking issue (e.g., TODO: replace double-cast when
SDK types are updated — tracking: <link-to-issue>) and include a short note to
remove the cast and restore proper typed usage of rawDto/ExtendedProviderDto
when the generated SDK is fixed; optionally mention adding runtime validation
(e.g., a small isExtendedProviderDto check) as a follow-up to avoid silent
runtime failures.
---
Nitpick comments:
In `@src/Modules/Providers/API/Endpoints/Public/BecomeProviderEndpoint.cs`:
- Around line 66-72: RegisterProviderApiRequest lacks validation attributes on
Name, DocumentNumber and Type; add data-annotation attributes to enforce
non-empty and size constraints and validate the enum. Annotate Name with
[Required] and a reasonable [StringLength(max)] (and optionally [MinLength]) to
prevent empty/oversized values; annotate DocumentNumber with [Required], a
[StringLength(max)] and an appropriate [RegularExpression] if a format is
expected; and annotate Type with [Required] and
[EnumDataType(typeof(EProviderType))] to reject out-of-range enum values. Ensure
nullable PhoneNumber and Email keep appropriate validation (e.g. [Phone],
[EmailAddress]) if desired.
In
`@src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs`:
- Around line 80-84: Os comentários em inglês dentro do manipulador
RegisterProviderCommandHandler (na seção que trata da violação de constraint
única e do rethrow para o handler global) devem ser convertidos para português;
localize o trecho que retorna Result<ProviderDto>.Failure(...) (e o comentário
logo após) e substitua os comentários em inglês por comentários em português
mantendo o código inalterado (mensagens de erro/logs em inglês permanecem como
estão) — garanta que as observações sobre condição de corrida/consistência de
dados e sobre rethrow para o handler global sejam expressas claramente em
português.
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx`:
- Around line 60-61: The width/height props in the Avatar component are using
redundant casts — remove the unnecessary "as number" from the assignments where
you set width={sizePx[size] as number} and height={sizePx[size] as number};
instead use width={sizePx[size]} and height={sizePx[size]}; ensure the
referenced symbol sizePx retains a number-typed index signature (e.g., number[]
or Record<... , number>) and that the prop names/variables size and sizePx in
avatar.tsx remain correctly typed so the compiler accepts the direct usage.
- Line 5: Change AvatarProps to extend Next.js ImageProps instead of
ComponentProps<"img"> so the prop types match the rendered <Image> component
(replace the current extends Omit<ComponentProps<"img">, "src"> with something
based on ImageProps and omit/adjust any conflicting fields like "src" if
needed); update any imports to pull ImageProps from "next/image"; also remove
the unnecessary "as number" casts around sizePx[size] in the avatar sizing logic
since sizePx is already typed as number and the cast is redundant (refer to
AvatarProps, sizePx and the usage of size in the component).
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Around line 52-58: The mock businessProfile is cast with "as any", hiding
shape mismatches; update the mapper to import and use the proper
BusinessProfileDto type and replace "as any" with a typed object or a type
assertion to BusinessProfileDto so TypeScript enforces the expected
ContactInfoDto and AddressDto shapes for ProviderDto.businessProfile; ensure
imports include BusinessProfileDto (alongside ProviderDto) and that the object
fields match the BusinessProfileDto structure used by the code paths that
consume businessProfile.
- Around line 210-221: The triple-cast usage in the mapper for documents is
unnecessary and unsafe: replace `(d.documentType as unknown as number) as any`
and `(d.status as unknown as number) as any` with direct casts to the target
enums (EDocumentType and EDocumentStatus) so DocumentDto.documentType and
DocumentDto.status get proper enum types; add EDocumentType and EDocumentStatus
to the import list (alongside EProviderType/EProviderStatus/etc.) and change the
mapping inside documents (the mapping function that references documentType and
status on the ExtendedProviderDto `d`) to cast to those enums instead of any.
- Around line 70-103: Remove the dead interfaces LocalBusinessProfileDto and
LocalProviderDto from this file: they are not referenced
(mapApiProviderToProvider uses ExtendedProviderDto), so delete both interface
declarations and any unused imports that were only needed for them; search for
LocalBusinessProfileDto and LocalProviderDto to ensure no external usages,
update mapApiProviderToProvider if it incorrectly referenced them, then run the
TypeScript build/linters to confirm no type errors remain.
In `@src/Web/MeAjudaAi.Web.Customer/types/api/provider.ts`:
- Around line 119-148: ProviderDto currently declares enum types (EProviderType,
EProviderStatus, EVerificationStatus, EProviderTier) that are manually
synchronized with backend; update ProviderDto to import and use the shared
enums/constants from the centralized Shared.Contracts package instead of local
copies. Locate occurrences of EProviderType, EProviderStatus,
EVerificationStatus, and EProviderTier in the ProviderDto declaration and
replace them with the imported symbols from Shared.Contracts (ensuring the
BusinessProfileDto/DocumentDto/QualificationDto/ProviderServiceDto DTOs stay
compatible), add the appropriate import statements at the top of the file, and
remove or mark any local duplicate enum definitions so the web types reference
the single source of truth.
In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs`:
- Around line 47-53: Add a new unit test that mirrors the existing
CustomerRoles_ShouldContainCustomerRole to assert ProviderRoles' count and exact
members: create a test (e.g., ProviderRoles_ShouldContainOnlyProviderRoles) that
uses UserRoles.ProviderRoles and verifies the expected number of roles with
HaveCount(...) and that it contains the exact role constants from
UserRoles.Provider (or the specific provider role constants), following the
pattern used by AdminRoles_ShouldContainOnlyAdminRoles and
CustomerRoles_ShouldContainCustomerRole so any accidental additions/removals to
ProviderRoles are caught.
- Around line 169-179: Update the IsAdminRole_WithNonAdminRole_ShouldReturnFalse
test to include the four provider-tier role values so they are explicitly
asserted non-admin; add additional [InlineData(...)] entries for each provider
role string (or reference the constants if provider role constants exist) and
ensure the test calls UserRoles.IsAdminRole(role) and asserts
result.Should().BeFalse() for those values (keep the test name and assertion
logic unchanged).
src/Modules/Providers/Application/Handlers/Commands/RegisterProviderCommandHandler.cs
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/app/(main)/prestador/[id]/page.tsx
Outdated
Show resolved
Hide resolved
…rs, configure Next.js for image handling, and introduce unit tests for user roles.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Web/MeAjudaAi.Web.Customer/app/(main)/prestador/[id]/page.tsx (1)
148-152:⚠️ Potential issue | 🟠 MajorWhatsApp link double-prepends country code
55when API returns phone with country code included.The API returns phone numbers with the Brazilian country code (e.g.,
"+5511999999999"). The frontend function strips non-digits to get"5511999999999", then unconditionally prepends"55", resulting in"555511999999999"— an invalid WhatsApp link.The fix should guard against the country code already being present:
🐛 Proposed fix — guard against existing country code
const getWhatsappLink = (phone: string) => { const cleanPhone = phone.replace(/\D/g, ""); - // Validate: Brazilian phone should have at least 10 digits (DDD + number) - return cleanPhone.length >= 10 ? `https://wa.me/55${cleanPhone}` : null; + // Validate: Brazilian phone should have at least 10 digits (DDD + number) + if (cleanPhone.length < 10) return null; + // Avoid double-prepending country code + const fullNumber = cleanPhone.startsWith('55') && cleanPhone.length >= 12 + ? cleanPhone + : `55${cleanPhone}`; + return `https://wa.me/${fullNumber}`; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx around lines 148 - 152, The getWhatsappLink function currently always prepends "55" after stripping non-digits, causing double country codes when the API already returns +55; update getWhatsappLink to: strip non-digits into cleanPhone, detect and remove any leading international prefixes as needed (e.g., if cleanPhone startsWith "55" treat it as already containing the country code and do not add another "55"), validate that the resulting number (without the country code) has at least 10 digits (DDD + number), and return the wa.me link using a single "55" prefix or null if invalid; refer to the getWhatsappLink function to implement this guard and validation.
🧹 Nitpick comments (7)
tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs (1)
226-237: Optional: extendIsProviderRole_WithNonProviderRole_ShouldReturnFalsewith admin-tier roles for symmetry.
IsAdminRole_WithNonAdminRole_ShouldReturnFalseexplicitly includes all four new provider-tier roles to guard against category bleed. The inverse test here only coversadmin,customer, andviewer, leaving the admin-tier roles (provider-manager,document-reviewer,catalog-manager,operator) untested as non-provider roles.♻️ Suggested additional InlineData entries
[Theory] [InlineData("admin")] +[InlineData("provider-manager")] +[InlineData("document-reviewer")] +[InlineData("catalog-manager")] +[InlineData("operator")] [InlineData("customer")] [InlineData("viewer")] public void IsProviderRole_WithNonProviderRole_ShouldReturnFalse(string role)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs` around lines 226 - 237, Update the test IsProviderRole_WithNonProviderRole_ShouldReturnFalse so it also asserts that the new admin-tier roles are not considered provider roles: add InlineData entries for "provider-manager", "document-reviewer", "catalog-manager", and "operator" and keep the assertion calling UserRoles.IsProviderRole(role).Should().BeFalse(); to mirror the symmetry used in IsAdminRole_WithNonAdminRole_ShouldReturnFalse.src/Modules/Providers/API/Endpoints/Public/BecomeProviderEndpoint.cs (1)
64-70: DeadRegisterProviderApiRequest— remove it.Since the handler now derives the email exclusively from the JWT (
context.User?.FindFirst("email")?.Value) and never readsrequest.Email, the
- misleads API consumers into believing they can supply/override the email via the request body, and
- runs
[EmailAddress]validation on a value that is silently ignored.♻️ Proposed refactor
public record RegisterProviderApiRequest( [Required, StringLength(100)] string Name, [Required, EnumDataType(typeof(EProviderType))] EProviderType Type, [Required, StringLength(20)] string DocumentNumber, - [Phone, StringLength(20)] string? PhoneNumber, - [EmailAddress] string? Email + [Phone, StringLength(20)] string? PhoneNumber );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Providers/API/Endpoints/Public/BecomeProviderEndpoint.cs` around lines 64 - 70, Remove the dead Email field from the RegisterProviderApiRequest record since the handler derives email from the JWT (context.User?.FindFirst("email")?.Value) and never uses request.Email; update the record declaration to drop the Email property and any [EmailAddress] attribute, then search for any references to RegisterProviderApiRequest.Email and remove or refactor them so nothing relies on the removed property (this keeps model validation and API surface accurate).src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx (1)
5-10:width,height, andfillshould be omitted fromAvatarPropsto prevent silent dimension conflicts.Because
AvatarPropsextendsOmit<ImageProps, "src" | "alt">,width,height, andfillare all valid consumer props. Two problems follow:
width/heightoverride —{...rest}is spread after the explicitwidth={sizePx[size]}/height={sizePx[size]}, so any caller-suppliedwidthorheightsilently replaces the size-derived values, undermining thesizeprop contract.fillconflict —fill={true}passed throughrestconflicts with the explicit fixed dimensions; Next.js raises a dev error ("Image with src ... has both "fill" and "width" props.") and layout breaks.♻️ Proposed fix
-export interface AvatarProps extends Omit<ImageProps, "src" | "alt"> { +export interface AvatarProps extends Omit<ImageProps, "src" | "alt" | "width" | "height" | "fill"> {Also applies to: 60-63
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx` around lines 5 - 10, The AvatarProps type currently extends Omit<ImageProps, "src" | "alt"> which still allows caller-supplied width/height/fill to override the component's size behavior; update the type to Omit<ImageProps, "src" | "alt" | "width" | "height" | "fill"> so width, height and fill are removed from the public props, and keep the explicit width={sizePx[size]} / height={sizePx[size]} and fill handling inside the Avatar component (e.g., where AvatarProps and the Image usage are defined) so callers cannot silently override dimensions or pass fill.src/Web/MeAjudaAi.Web.Customer/app/(main)/prestador/[id]/page.tsx (1)
58-68: Consider guarding against missing API URL in production.Line 60 falls back to
'http://localhost:7002'if neitherAPI_URLnorNEXT_PUBLIC_API_URLis set. In a production deployment this would silently send requests to localhost, causing confusing failures. A defensive approach would log a warning or throw during build/startup if the env var is absent.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx around lines 58 - 68, The current getCachedProvider function falls back to 'http://localhost:7002' when API_URL/NEXT_PUBLIC_API_URL are missing; change this to fail fast: inside getCachedProvider (where apiUrl is computed) detect if neither process.env.API_URL nor process.env.NEXT_PUBLIC_API_URL is set and throw a clear Error (or call a logger.warn + throw) that names the missing env vars, so production doesn't silently call localhost; update any related error handling to surface this during startup/runtime rather than using the localhost fallback.src/Web/MeAjudaAi.Web.Customer/next.config.ts (1)
6-19: Omittingpathnameimplies**on all patterns — consider adding explicit path restrictions.When omitting
protocol,port,pathname, orsearch, the wildcard**is implied. This is not recommended because it may allow malicious actors to optimize URLs you did not intend. All three entries leavepathnameunspecified, which allows Next.js to proxy and optimize any image path from those domains through/_next/image. While fine for profile avatars today, consider restricting to actual avatar path prefixes for better posture.♻️ Proposed path restrictions
{ protocol: "https", hostname: "avatars.githubusercontent.com", + pathname: "/u/**", }, { protocol: "https", hostname: "**.googleusercontent.com", + pathname: "/a/**", },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/next.config.ts` around lines 6 - 19, The remotePatterns entries in next.config.ts (the remotePatterns array) omit pathname which implies a ** wildcard and allows any path to be proxied; update each object in remotePatterns to include explicit pathname restrictions that match the actual avatar/image path prefixes used by those hosts (e.g., narrow to the avatar or profile image path segments for lh3.googleusercontent.com, avatars.githubusercontent.com, and googleusercontent.com) so Next.js only optimizes intended URLs; modify the remotePatterns objects (protocol/hostname pairs) to add appropriate pathname strings rather than leaving them unspecified.src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts (2)
59-65:as BusinessProfileDtocast may silently suppress structural type errors.
contactInfo: { email: '' }is cast viaas BusinessProfileDtorather than typed structurally. IfContactInfoDtohas additional required fields (e.g.,phoneNumber), TypeScript won't surface the gap. Consider typingcontactInfoinline against the actualContactInfoDtoshape, or usingas unknown as BusinessProfileDtowith an explicit comment noting this is intentionally partial.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts` around lines 59 - 65, The businessProfile object in the mapper uses a loose cast "as BusinessProfileDto" which can hide missing required fields (specifically contactInfo). Replace the inline contactInfo literal with a value explicitly typed to the actual ContactInfoDto shape (e.g., declare contactInfo: ContactInfoDto = { ... } or annotate the property as ContactInfoDto) so TypeScript validates required fields, or if you intentionally want a partial stub, change the cast to "as unknown as BusinessProfileDto" and add a clear comment on businessProfile / contactInfo noting this is an intentional partial mock; update the businessProfile assignment in mappers.ts (the businessProfile / contactInfo entries) accordingly.
135-135:?? undefinedis a no-op — remove for clarity.
contactInfo?.phoneNumber ?? undefinedandq.fileUrl ?? undefinedboth reduce to the left-hand expression;undefinedis already the result when the operand isundefined. The??fallback serves no purpose here.♻️ Proposed fix
- phone: contactInfo?.phoneNumber ?? undefined, + phone: contactInfo?.phoneNumber,- fileUrl: q.fileUrl ?? undefined + fileUrl: q.fileUrlAlso applies to: 196-196
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts` at line 135, The expressions using the nullish coalescing fallback to undefined are redundant; remove the "?? undefined" occurrences so fields like the phone mapping (phone: contactInfo?.phoneNumber ?? undefined) and file URL mapping (q.fileUrl ?? undefined) simply use the optional chain result (contactInfo?.phoneNumber, q.fileUrl). Update the mapper code in mappers.ts where these patterns appear (e.g., the phone mapping and the q.fileUrl mapping) to return the left-hand expression directly for clarity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Modules/Providers/API/Endpoints/Public/BecomeProviderEndpoint.cs`:
- Around line 38-42: The comment above the email extraction in
BecomeProviderEndpoint.cs is stale—remove or rewrite the phrase "senão usar do
request" so it accurately reflects that the handler now strictly requires the
token claim; update the comment that precedes the
context.User?.FindFirst("email")?.Value extraction (and the subsequent
string.IsNullOrEmpty(email) check) to state that email must come from the token
and there is no fallback to request.Email.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Line 183: When mapping ExtendedProviderDto -> DocumentDto ensure you provide
safe fallbacks for numeric-optional enum fields: replace the direct casts of
d.documentType and d.status to EDocumentType/EDocumentStatus with
nullish-coalescing fallbacks (e.g., d.documentType ?? EDocumentType.Other and
d.status ?? EDocumentStatus.Pending) so undefined values aren’t silently cast;
update the mapper that constructs DocumentDto (references: ExtendedProviderDto,
DocumentDto, d.documentType, d.status, EDocumentType, EDocumentStatus) to use
those appropriate sentinel defaults.
- Line 68: The mapper currently sets status: EProviderStatus.PendingBasicInfo
which causes search results to render as "incomplete"—change the mapped status
in lib/api/mappers.ts to EProviderStatus.Active (keeping or adding the note that
the Search API only indexes active providers) so ProviderCard sees an active
status; update the mapping that produces the provider object (reference
EProviderStatus and the provider mapping function in this file) to use
EProviderStatus.Active instead of PendingBasicInfo.
In `@src/Web/MeAjudaAi.Web.Customer/next.config.ts`:
- Around line 7-18: Replace the two separate hostname entries
("lh3.googleusercontent.com" and "googleusercontent.com") with a single wildcard
entry matching all subdomains (use "**.googleusercontent.com") so Next.js image
hostname matching will allow lh3, lh4, etc.; update the hostname objects in the
images/domains configuration (where "lh3.googleusercontent.com" and
"googleusercontent.com" are defined) to a single object with the wildcard
hostname.
---
Outside diff comments:
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx:
- Around line 148-152: The getWhatsappLink function currently always prepends
"55" after stripping non-digits, causing double country codes when the API
already returns +55; update getWhatsappLink to: strip non-digits into
cleanPhone, detect and remove any leading international prefixes as needed
(e.g., if cleanPhone startsWith "55" treat it as already containing the country
code and do not add another "55"), validate that the resulting number (without
the country code) has at least 10 digits (DDD + number), and return the wa.me
link using a single "55" prefix or null if invalid; refer to the getWhatsappLink
function to implement this guard and validation.
---
Duplicate comments:
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx:
- Around line 88-93: Previous dead check for `status === 404` has been removed;
ensure the catch block in the fetch flow (the catch that logs `Exception
fetching public provider ${id}:`) simply logs the error and rethrows it without
reintroducing any status-specific handling. Remove any remaining conditional
checks for `status === 404` in that catch and keep the single console.error +
throw error behavior so the 404 handling on the earlier path remains the single
source of truth.
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx`:
- Around line 57-64: The avatar Image in components/ui/avatar.tsx uses external
OAuth avatar URLs (prop src passed into the Image component), so ensure Next.js
image config allows those hosts by adding the OAuth providers to
images.remotePatterns or images.domains in next.config.ts; update next.config.ts
to include remotePatterns entries (or domain entries) for googleusercontent.com
and avatars.githubusercontent.com (and any other OAuth hostnames used by your
auth code), then restart the dev server and verify the avatar Image renders
without Next.js host-blocking errors.
---
Nitpick comments:
In `@src/Modules/Providers/API/Endpoints/Public/BecomeProviderEndpoint.cs`:
- Around line 64-70: Remove the dead Email field from the
RegisterProviderApiRequest record since the handler derives email from the JWT
(context.User?.FindFirst("email")?.Value) and never uses request.Email; update
the record declaration to drop the Email property and any [EmailAddress]
attribute, then search for any references to RegisterProviderApiRequest.Email
and remove or refactor them so nothing relies on the removed property (this
keeps model validation and API surface accurate).
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/prestador/[id]/page.tsx:
- Around line 58-68: The current getCachedProvider function falls back to
'http://localhost:7002' when API_URL/NEXT_PUBLIC_API_URL are missing; change
this to fail fast: inside getCachedProvider (where apiUrl is computed) detect if
neither process.env.API_URL nor process.env.NEXT_PUBLIC_API_URL is set and throw
a clear Error (or call a logger.warn + throw) that names the missing env vars,
so production doesn't silently call localhost; update any related error handling
to surface this during startup/runtime rather than using the localhost fallback.
In `@src/Web/MeAjudaAi.Web.Customer/components/ui/avatar.tsx`:
- Around line 5-10: The AvatarProps type currently extends Omit<ImageProps,
"src" | "alt"> which still allows caller-supplied width/height/fill to override
the component's size behavior; update the type to Omit<ImageProps, "src" | "alt"
| "width" | "height" | "fill"> so width, height and fill are removed from the
public props, and keep the explicit width={sizePx[size]} / height={sizePx[size]}
and fill handling inside the Avatar component (e.g., where AvatarProps and the
Image usage are defined) so callers cannot silently override dimensions or pass
fill.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/mappers.ts`:
- Around line 59-65: The businessProfile object in the mapper uses a loose cast
"as BusinessProfileDto" which can hide missing required fields (specifically
contactInfo). Replace the inline contactInfo literal with a value explicitly
typed to the actual ContactInfoDto shape (e.g., declare contactInfo:
ContactInfoDto = { ... } or annotate the property as ContactInfoDto) so
TypeScript validates required fields, or if you intentionally want a partial
stub, change the cast to "as unknown as BusinessProfileDto" and add a clear
comment on businessProfile / contactInfo noting this is an intentional partial
mock; update the businessProfile assignment in mappers.ts (the businessProfile /
contactInfo entries) accordingly.
- Line 135: The expressions using the nullish coalescing fallback to undefined
are redundant; remove the "?? undefined" occurrences so fields like the phone
mapping (phone: contactInfo?.phoneNumber ?? undefined) and file URL mapping
(q.fileUrl ?? undefined) simply use the optional chain result
(contactInfo?.phoneNumber, q.fileUrl). Update the mapper code in mappers.ts
where these patterns appear (e.g., the phone mapping and the q.fileUrl mapping)
to return the left-hand expression directly for clarity.
In `@src/Web/MeAjudaAi.Web.Customer/next.config.ts`:
- Around line 6-19: The remotePatterns entries in next.config.ts (the
remotePatterns array) omit pathname which implies a ** wildcard and allows any
path to be proxied; update each object in remotePatterns to include explicit
pathname restrictions that match the actual avatar/image path prefixes used by
those hosts (e.g., narrow to the avatar or profile image path segments for
lh3.googleusercontent.com, avatars.githubusercontent.com, and
googleusercontent.com) so Next.js only optimizes intended URLs; modify the
remotePatterns objects (protocol/hostname pairs) to add appropriate pathname
strings rather than leaving them unspecified.
In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs`:
- Around line 226-237: Update the test
IsProviderRole_WithNonProviderRole_ShouldReturnFalse so it also asserts that the
new admin-tier roles are not considered provider roles: add InlineData entries
for "provider-manager", "document-reviewer", "catalog-manager", and "operator"
and keep the assertion calling
UserRoles.IsProviderRole(role).Should().BeFalse(); to mirror the symmetry used
in IsAdminRole_WithNonAdminRole_ShouldReturnFalse.
src/Modules/Providers/API/Endpoints/Public/BecomeProviderEndpoint.cs
Outdated
Show resolved
Hide resolved
…e display with supporting UI components and configurations.
…t upload, address lookup, and new UI components.
…lf-or-admin handler, auth provider enum, and user interface components.
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (1)
src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx (1)
91-99:⚠️ Potential issue | 🟠 MajorReset social-login loading state on every non-navigation outcome.
isLoadingis set on Line 93 but only cleared in thecatchblock (Line 98). While OAuth redirects are typically implemented as thrown errors and will trigger the catch handler, defensive programming requires thefinallyblock to ensure loading state clears in all code paths—particularly if the redirect mechanism fails to trigger. This pattern is already correctly applied to the credentials login handler (lines 86–88); apply the same pattern here for consistency.Suggested fix
const handleSocialLogin = async (provider: string) => { if (isLoading) return; + setError(""); setIsLoading(true); try { await signIn("keycloak", { callbackUrl }, { kc_idp_hint: provider.toLowerCase() }); } catch { setError("Ocorreu um erro. Tente novamente."); - setIsLoading(false); + } finally { + setIsLoading(false); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx` around lines 91 - 99, The handleSocialLogin function sets isLoading but only clears it in the catch branch; modify handleSocialLogin (the async function invoking signIn) to ensure setIsLoading(false) runs for every outcome by moving the loading-clear into a finally block so that isLoading is reset whether signIn redirects, throws, or returns; keep the existing catch logic that sets setError(...) and maintain the early-return when isLoading is true.
🧹 Nitpick comments (3)
src/Bootstrapper/MeAjudaAi.ApiService/Handlers/SelfOrAdminHandler.cs (1)
29-31: Replace hardcodedsuper-adminwith a shared role constant.Line 31 still uses a string literal, which can drift from shared authorization definitions over time.
♻️ Suggested refactor
- string.Equals(r, "super-admin", StringComparison.OrdinalIgnoreCase))) + string.Equals(r, MeAjudaAi.Shared.Utilities.UserRoles.SuperAdmin, StringComparison.OrdinalIgnoreCase)))Based on learnings: prefer reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Bootstrapper/MeAjudaAi.ApiService/Handlers/SelfOrAdminHandler.cs` around lines 29 - 31, Replace the hardcoded "super-admin" string in the roles check inside SelfOrAdminHandler with the shared role constant instead: locate the conditional that compares roles to MeAjudaAi.Shared.Utilities.UserRoles.Admin and the literal "super-admin", and swap the literal for the corresponding shared constant (e.g., MeAjudaAi.Shared.Utilities.UserRoles.SuperAdmin or the Shared.Contracts enum/constant that defines SuperAdmin) while keeping the StringComparison.OrdinalIgnoreCase comparison; ensure both comparisons use the same constant source to avoid drift.src/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts (2)
4-4: Consider sourcingHttpMethodfrom shared contracts instead of redefining it locally.This avoids frontend/backend drift for allowed verbs as auth/onboarding APIs evolve.
Based on learnings: prioritize reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts` at line 4, Replace the local HttpMethod type definition with the shared contract enum/type from Shared.Contracts to avoid verb drift: remove the local "type HttpMethod" in fetch-client.ts and import the shared HttpMethod symbol (e.g., Shared.Contracts.HttpMethod or the project’s Shared.Contracts export that exposes HTTP verbs), then update any usages in this file (references to HttpMethod) to use the imported symbol so the client stays aligned with backend/shared definitions.
70-80: UseApiErrorconsistently for non-HTTP failure paths.These branches currently throw plain
Error, while HTTP failures throwApiError, which fragments caller-side error handling.♻️ Proposed fix
- if (value === null || value === undefined) { - throw new Error("Response contained null/undefined value for expected Result<T>"); - } + if (value === null || value === undefined) { + throw new ApiError("Response contained null/undefined value for expected Result<T>"); + } @@ - if (apiRes.data === null || apiRes.data === undefined) { - throw new Error(apiRes.message || "API interaction failed"); - } + if (apiRes.data === null || apiRes.data === undefined) { + throw new ApiError(apiRes.message || "API interaction failed", response.status); + } @@ - if (requireAuth && !token) { - throw new Error("Missing access token"); - } + if (requireAuth && !token) { + throw new ApiError("Missing access token", 401); + }Also applies to: 96-98
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts` around lines 70 - 80, Replace plain Error throws in the non-HTTP failure paths with ApiError so callers can uniformly handle API errors: where the code currently throws new Error("Response contained null/undefined value for expected Result<T>") and new Error(apiRes.message || "API interaction failed") (and the similar throw at the 96-98 branch), construct and throw an ApiError instead (include the original message and any available metadata like status or body if available), and ensure ApiError is imported/used consistently alongside ApiResponse and Result<T>.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/roadmap.md`:
- Around line 48-49: Update the timeline entry that currently reads "🔮 **Março
2026+**: Fase 3 - Reviews, Assinaturas (Stripe), Agendamentos" so it does not
overlap the MVP date; replace it with a post-MVP phrasing such as "🔮 **Pós-MVP
(Abril 2026+)**: Fase 3 - Reviews, Assinaturas (Stripe), Agendamentos" (locate
the exact string "🔮 **Março 2026+**: Fase 3 - Reviews, Assinaturas (Stripe),
Agendamentos" in docs/roadmap.md and update that line accordingly).
In `@src/Modules/Users/API/Endpoints/Public/GetAuthProvidersEndpoint.cs`:
- Around line 15-16: O endpoint GetAuthProvidersEndpoint está retornando
Enum.GetValues<EAuthProvider>() (providers) que pode ser serializado como
inteiros; atualize o retorno para expor nomes de enum legíveis e estáveis (por
exemplo usando Enum.GetNames<EAuthProvider>() ou mapeando cada valor para sua
string) para evitar exposição de serialização numérica no contrato público;
altere a variável providers e o Results.Ok(...) para devolver explicitamente
strings dos nomes do enum.
In `@src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs`:
- Around line 69-76: Replace the non-existent DeleteUserAsync call with the
interface method DeactivateUserAsync on keycloakService (i.e., call
keycloakService.DeactivateUserAsync(keycloakResult.Value, cancellationToken)
inside KeycloakUserDomainService) and in the catch block log the caught
exception instead of swallowing it—use the class's logger instance (e.g.,
_logger or existing ILogger field) to emit an error/debug message including the
exception and context so compensation failures are recorded for diagnosis.
In `@src/Modules/Users/Tests/Builders/UserBuilder.cs`:
- Around line 24-36: The code declares a local "user" but then checks
"userResult" and later assigns "userResult.Value", causing a name mismatch and
also calls User.Create without the required keycloakId parameter; fix by
changing the call to User.Create to capture its result into a variable named
"userResult" (e.g., var userResult = User.Create(...)) and include the missing
keycloakId argument when calling User.Create (use the builder field or a newly
generated keycloakId), then use userResult.IsFailure and var user =
userResult.Value as currently intended.
In `@src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx`:
- Around line 54-56: Normalize provider IDs immediately after fetching in the
setProviders flow: when you receive data in the block that calls setProviders,
map/transform each provider id to a canonical lowercase/known token (e.g.,
"google", "facebook" or use the Shared.Contracts enum/constant values) so later
checks like providers.includes("Google") and the kc_idp_hint construction use
the same normalized values; update the rendering code and sign-in hint logic
(the array used by providers.includes and wherever kc_idp_hint is built) to
expect and compare against those canonical tokens rather than display names, and
prefer importing the Shared.Contracts provider enum/constants to drive both
normalization and comparisons.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts`:
- Around line 106-113: The current truthy checks around the request body in
fetch-client (the `if (body)` that sets requestHeaders["Content-Type"] and the
`body: body ? JSON.stringify(body) : undefined` in the fetch call) drop valid
falsy JSON payloads (false, 0, ""). Update both checks to use explicit undefined
comparison (e.g., `body !== undefined`) so Content-Type is set and the body is
JSON-stringified when body is intentionally provided but falsy; keep the
fallback to undefined when body is truly absent.
---
Duplicate comments:
In `@src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx`:
- Around line 91-99: The handleSocialLogin function sets isLoading but only
clears it in the catch branch; modify handleSocialLogin (the async function
invoking signIn) to ensure setIsLoading(false) runs for every outcome by moving
the loading-clear into a finally block so that isLoading is reset whether signIn
redirects, throws, or returns; keep the existing catch logic that sets
setError(...) and maintain the early-return when isLoading is true.
---
Nitpick comments:
In `@src/Bootstrapper/MeAjudaAi.ApiService/Handlers/SelfOrAdminHandler.cs`:
- Around line 29-31: Replace the hardcoded "super-admin" string in the roles
check inside SelfOrAdminHandler with the shared role constant instead: locate
the conditional that compares roles to
MeAjudaAi.Shared.Utilities.UserRoles.Admin and the literal "super-admin", and
swap the literal for the corresponding shared constant (e.g.,
MeAjudaAi.Shared.Utilities.UserRoles.SuperAdmin or the Shared.Contracts
enum/constant that defines SuperAdmin) while keeping the
StringComparison.OrdinalIgnoreCase comparison; ensure both comparisons use the
same constant source to avoid drift.
In `@src/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts`:
- Line 4: Replace the local HttpMethod type definition with the shared contract
enum/type from Shared.Contracts to avoid verb drift: remove the local "type
HttpMethod" in fetch-client.ts and import the shared HttpMethod symbol (e.g.,
Shared.Contracts.HttpMethod or the project’s Shared.Contracts export that
exposes HTTP verbs), then update any usages in this file (references to
HttpMethod) to use the imported symbol so the client stays aligned with
backend/shared definitions.
- Around line 70-80: Replace plain Error throws in the non-HTTP failure paths
with ApiError so callers can uniformly handle API errors: where the code
currently throws new Error("Response contained null/undefined value for expected
Result<T>") and new Error(apiRes.message || "API interaction failed") (and the
similar throw at the 96-98 branch), construct and throw an ApiError instead
(include the original message and any available metadata like status or body if
available), and ensure ApiError is imported/used consistently alongside
ApiResponse and Result<T>.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
docs/roadmap.mdsrc/Bootstrapper/MeAjudaAi.ApiService/Handlers/SelfOrAdminHandler.cssrc/Contracts/Identity/Enums/EAuthProvider.cssrc/Modules/Users/API/Endpoints/Public/GetAuthProvidersEndpoint.cssrc/Modules/Users/API/Endpoints/UsersModuleEndpoints.cssrc/Modules/Users/Domain/Entities/User.cssrc/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cssrc/Modules/Users/Tests/Builders/UserBuilder.cssrc/Modules/Users/Tests/Infrastructure/Mocks/MockUserDomainService.cssrc/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsxsrc/Web/MeAjudaAi.Web.Customer/components/layout/user-menu.tsxsrc/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.tssrc/Web/MeAjudaAi.Web.Customer/lib/api/response-utils.tstests/MeAjudaAi.Integration.Tests/Database/DbContextConcurrencyTests.cstests/MeAjudaAi.Integration.Tests/Modules/Users/UserRepositoryIntegrationTests.cstests/MeAjudaAi.Integration.Tests/Modules/Users/UsersIntegrationTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
- src/Web/MeAjudaAi.Web.Customer/lib/api/response-utils.ts
src/Modules/Users/API/Endpoints/Public/GetAuthProvidersEndpoint.cs
Outdated
Show resolved
Hide resolved
src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs
Outdated
Show resolved
Hide resolved
…handlers, and new customer/provider UI components.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Bootstrapper/MeAjudaAi.ApiService/Handlers/SelfOrAdminHandler.cs (1)
29-35:⚠️ Potential issue | 🔴 CriticalBuild failure:
UserRoles.SuperAdmindoes not exist.The code at lines 29-31 references
MeAjudaAi.Shared.Utilities.UserRoles.SuperAdmin, which is not defined in theUserRolesclass. This causes a CS0117 compilation error and prevents the build from succeeding.Add the missing constant to
src/Shared/Utilities/UserRoles.cs:🐛 Add missing SuperAdmin constant
public static class UserRoles { /// <summary> /// Administrador com permissões elevadas - acesso total ao Admin Portal /// </summary> public const string Admin = "admin"; + + /// <summary> + /// Super administrador com permissões máximas do sistema + /// </summary> + public const string SuperAdmin = "super-admin"; /// <summary> /// Gerente de provedores - pode criar, editar e deletar provedores /// </summary> public const string ProviderManager = "provider-manager";Also update the
AdminRolesarray to include the new constant:public static readonly string[] AdminRoles = [ Admin, + SuperAdmin, ProviderManager, DocumentReviewer, CatalogManager, Operator ];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Bootstrapper/MeAjudaAi.ApiService/Handlers/SelfOrAdminHandler.cs` around lines 29 - 35, The build fails because SelfOrAdminHandler.cs references MeAjudaAi.Shared.Utilities.UserRoles.SuperAdmin which doesn't exist; fix by adding a SuperAdmin constant in the UserRoles class (e.g., public const string SuperAdmin = "SuperAdmin") and update the UserRoles.AdminRoles array to include UserRoles.SuperAdmin so AdminRoles contains the new constant; ensure the symbol names match exactly (UserRoles.SuperAdmin and AdminRoles) so SelfOrAdminHandler's role checks compile.
♻️ Duplicate comments (2)
docs/roadmap.md (1)
2210-2211:⚠️ Potential issue | 🟡 MinorAjustar referência “Post-MVP” que ainda aponta para Fevereiro/2026.
Com o alvo de MVP em 28 de Março de 2026 (Line 2210), a menção próxima de Fase 3+ em “Fevereiro 2026+” fica cronologicamente inválida. Atualize para um período pós-MVP (ex.: Abril 2026+).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/roadmap.md` around lines 2210 - 2211, Update the post-MVP timeline text that still reads "Fevereiro 2026+" to a correct post-launch period (e.g., "Abril 2026+") so it aligns with the MVP Launch Target "MVP Launch Target: 28 de Março de 2026" and the nearby "Fase 3+" mention; ensure the phrase "Fevereiro 2026+" is replaced wherever it appears in the same section and adjust any adjacent date references to remain consistent with the new post-MVP timeline.src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs (1)
76-76:⚠️ Potential issue | 🟡 MinorKeep inline C# comments in Portuguese for consistency.
At Line [76], the comment is in English. Please translate it to Portuguese to match the project language policy for comments/XML docs.
✏️ Suggested wording
- // Silence compensation failures to prevent masking the original validation error + // Silencia falhas de compensação para não mascarar o erro original de validaçãoBased on learnings: Comments and XML documentation (
/// <summary>,<param>, etc.) should be in Portuguese across the C# codebase.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs` at line 76, Replace the English inline comment "// Silence compensation failures to prevent masking the original validation error" with its Portuguese equivalent to comply with project policy; locate the comment inside the KeycloakUserDomainService class (in KeycloakUserDomainService.cs) near the compensation/error handling block and change it to "// Silenciar falhas de compensação para evitar mascarar o erro de validação original".
🧹 Nitpick comments (1)
src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx (1)
48-64: Provider fetch addresses previous sync concerns.Fetching providers from the backend eliminates the frontend/backend drift risk previously flagged. Normalization to lowercase on line 55 ensures consistent matching.
Minor inconsistency: The fallback array includes
"microsoft"and"apple", but the UI only renders buttons for Google and Facebook. Consider aligning the fallback with supported providers:Optional fix
- setProviders(["google", "facebook", "microsoft", "apple"]); + setProviders(["google", "facebook"]);,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx` around lines 48 - 64, The fallback provider list in the fetchProviders useEffect includes "microsoft" and "apple" but the UI only renders Google and Facebook buttons; update the fallback in fetchProviders (where setProviders is called) to match supported providers (e.g., ["google","facebook"]) or alternatively add UI support for "microsoft" and "apple" so providers array and rendered buttons stay in sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs`:
- Around line 69-72: The call to keycloakService.DeactivateUserAsync currently
ignores its Result and uses the incoming cancellationToken which can cancel the
compensating action; update the compensation block in KeycloakUserDomainService
to perform a best-effort deactivation by awaiting
DeactivateUserAsync(keycloakResult.Value, CancellationToken.None) (or a short
linked timeout token) and inspect the returned Result/response: if it indicates
failure, log an explicit error via the existing logger including
keycloakResult.Value and the failure details; apply the same change to the other
compensating call at the later block (lines 78-79) so non-exception failures are
logged rather than swallowed.
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/perfil/editar/page.tsx:
- Around line 22-25: The page currently calls authenticatedFetch using
session.accessToken without guarding for a missing token or auth error; add a
pre-check in the page component (where session and authenticatedFetch are used)
to verify session?.accessToken and absence of session.error and, if
missing/invalid, perform a redirect to the sign-in route (same behavior as the
profile page) instead of calling
authenticatedFetch(`/api/v1/users/${session.user.id}`, ...); ensure you
reference the existing session variable and authenticatedFetch call so the fetch
is only invoked when the token exists.
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/perfil/page.tsx:
- Around line 24-27: The page currently calls authenticatedFetch using
session.accessToken without verifying the token; add a guard before calling
authenticatedFetch to check session?.accessToken (and session?.user?.id) and
handle the missing/invalid token by redirecting to the sign-in flow or returning
an authentication-specific response instead of performing the API call; update
the logic around the authenticatedFetch call so that when session.accessToken is
falsy you call the app's sign-in/redirect helper (or throw an auth redirect) and
only call authenticatedFetch(`/api/v1/users/${session.user.id}`, { token: token
}) when a valid token exists.
In `@src/Web/MeAjudaAi.Web.Customer/components/auth/customer-register-form.tsx`:
- Around line 65-66: The form currently hardcodes acceptedPrivacyPolicy: true
(symbol: acceptedPrivacyPolicy) which falsely records consent; remove the
hardcoded true and wire acceptedPrivacyPolicy to the checkbox/form field used
for Terms (the checkbox state/field rendered around line 225), ensuring the
form's initial values reflect false/undefined and the submit/validation logic
(the form submit handler and any validation rules) reads the actual checkbox
value instead of assuming consent; update any references in the component (e.g.,
initialValues, submit payload, and validation schema) to use the checkbox-bound
field name so consent is only recorded when the user explicitly checks it.
---
Outside diff comments:
In `@src/Bootstrapper/MeAjudaAi.ApiService/Handlers/SelfOrAdminHandler.cs`:
- Around line 29-35: The build fails because SelfOrAdminHandler.cs references
MeAjudaAi.Shared.Utilities.UserRoles.SuperAdmin which doesn't exist; fix by
adding a SuperAdmin constant in the UserRoles class (e.g., public const string
SuperAdmin = "SuperAdmin") and update the UserRoles.AdminRoles array to include
UserRoles.SuperAdmin so AdminRoles contains the new constant; ensure the symbol
names match exactly (UserRoles.SuperAdmin and AdminRoles) so
SelfOrAdminHandler's role checks compile.
---
Duplicate comments:
In `@docs/roadmap.md`:
- Around line 2210-2211: Update the post-MVP timeline text that still reads
"Fevereiro 2026+" to a correct post-launch period (e.g., "Abril 2026+") so it
aligns with the MVP Launch Target "MVP Launch Target: 28 de Março de 2026" and
the nearby "Fase 3+" mention; ensure the phrase "Fevereiro 2026+" is replaced
wherever it appears in the same section and adjust any adjacent date references
to remain consistent with the new post-MVP timeline.
In `@src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs`:
- Line 76: Replace the English inline comment "// Silence compensation failures
to prevent masking the original validation error" with its Portuguese equivalent
to comply with project policy; locate the comment inside the
KeycloakUserDomainService class (in KeycloakUserDomainService.cs) near the
compensation/error handling block and change it to "// Silenciar falhas de
compensação para evitar mascarar o erro de validação original".
---
Nitpick comments:
In `@src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx`:
- Around line 48-64: The fallback provider list in the fetchProviders useEffect
includes "microsoft" and "apple" but the UI only renders Google and Facebook
buttons; update the fallback in fetchProviders (where setProviders is called) to
match supported providers (e.g., ["google","facebook"]) or alternatively add UI
support for "microsoft" and "apple" so providers array and rendered buttons stay
in sync.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
docs/roadmap.mdsrc/Bootstrapper/MeAjudaAi.ApiService/Handlers/SelfOrAdminHandler.cssrc/Modules/Users/API/Endpoints/Public/GetAuthProvidersEndpoint.cssrc/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cssrc/Modules/Users/Tests/Builders/UserBuilder.cssrc/Web/MeAjudaAi.Web.Admin/packages.lock.jsonsrc/Web/MeAjudaAi.Web.Customer/app/(main)/perfil/editar/page.tsxsrc/Web/MeAjudaAi.Web.Customer/app/(main)/perfil/page.tsxsrc/Web/MeAjudaAi.Web.Customer/components/auth/customer-register-form.tsxsrc/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsxsrc/Web/MeAjudaAi.Web.Customer/components/providers/dashboard-client.tsxsrc/Web/MeAjudaAi.Web.Customer/hooks/use-document-upload.tssrc/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.tssrc/Web/MeAjudaAi.Web.Customer/hooks/use-update-provider-profile.tssrc/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts
💤 Files with no reviewable changes (1)
- src/Web/MeAjudaAi.Web.Admin/packages.lock.json
🚧 Files skipped from review as they are similar to previous changes (4)
- src/Web/MeAjudaAi.Web.Customer/hooks/use-update-provider-profile.ts
- src/Web/MeAjudaAi.Web.Customer/hooks/use-document-upload.ts
- src/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.ts
- src/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts
src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/app/(main)/perfil/editar/page.tsx
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/components/auth/customer-register-form.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (4)
src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs (1)
69-72:⚠️ Potential issue | 🟠 MajorHandle non-exception compensation failures explicitly and decouple rollback from request cancellation.
At Line [71],
DeactivateUserAsynccan fail viaResult.IsFailurewithout throwing, but that failure is currently ignored. Also, using the incomingcancellationTokencan cancel the compensation itself, leaving orphan users.🔧 Suggested fix
if (userResult.IsFailure) { try { - await keycloakService.DeactivateUserAsync(keycloakResult.Value, cancellationToken); + var deactivateResult = await keycloakService.DeactivateUserAsync( + keycloakResult.Value, + CancellationToken.None); + + if (deactivateResult.IsFailure) + { + logger.LogWarning( + "Failed to deactivate Keycloak user {KeycloakId} during compensation: {Error}", + keycloakResult.Value, + deactivateResult.Error); + } } catch (Exception ex) { logger.LogWarning(ex, "Failed to deactivate Keycloak user {KeycloakId} during compensation for local user creation failure.", keycloakResult.Value); // Silence compensation failures to prevent masking the original validation error } return Result<User>.Failure(userResult.Error); }Also applies to: 75-78
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs` around lines 69 - 72, The DeactivateUserAsync call currently ignores non-exception failures and uses the incoming cancellationToken which may cancel the compensation; update the rollback logic in KeycloakUserDomainService so that after calling keycloakService.DeactivateUserAsync(keycloakResult.Value, ...) you check the returned Result/response for IsFailure and handle it (log the failure and/or throw a specific exception or return a failed Result) instead of ignoring it, and invoke the compensation with a non-cancelable or dedicated cancellation token (e.g., CancellationToken.None or a new token sourced from a linked token with its own timeout) so the rollback is not cancelled by the original request; apply the same fix pattern to the similar block around the later DeactivateUserAsync usage (the 75-78 area).src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx (1)
91-99:⚠️ Potential issue | 🟡 MinorClear stale auth errors before social login attempts.
A previous error remains visible while a new social auth attempt is in progress.
Suggested fix
const handleSocialLogin = async (provider: string) => { if (isLoading) return; + setError(""); setIsLoading(true); try { await signIn("keycloak", { callbackUrl }, { kc_idp_hint: provider });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx` around lines 91 - 99, In handleSocialLogin, clear any stale auth errors before starting a social sign-in so previous error messages don't persist: when entering handleSocialLogin (before early return and before setIsLoading(true)), call setError(undefined) or setError("") to reset error state, then proceed with setIsLoading(true) and the existing signIn("keycloak", { callbackUrl }, { kc_idp_hint: provider })/try-catch/finally flow; ensure setError is used in the catch to set the new error as currently implemented.src/Web/MeAjudaAi.Web.Customer/app/(main)/perfil/page.tsx (1)
24-27:⚠️ Potential issue | 🟠 MajorProtect the fetch with token/session-error guard.
Line 24-Line 27 still call the protected endpoint without guarding
session.accessTokenandsession.error. Redirect to sign-in before fetch to avoid auth failures degrading into generic error UI.🔧 Suggested fix
+ if (!session.accessToken || session.error) { + redirect("/auth/signin"); + } + - const token = session.accessToken; - const data = await authenticatedFetch<MeAjudaAiModulesUsersApplicationDtosUserDto>(`/api/v1/users/${session.user.id}`, { - token: token + token: session.accessToken });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/perfil/page.tsx around lines 24 - 27, The code in page.tsx calls authenticatedFetch(`/api/v1/users/${session.user.id}`) using session.accessToken without guarding for session.accessToken or session.error; update the logic in the component (where session is used) to check for session?.accessToken and absence of session?.error before performing authenticatedFetch, and if missing or errored, trigger a redirect to sign-in (or return the appropriate redirect response) instead of calling authenticatedFetch so the protected endpoint is never called with an invalid session.src/Web/MeAjudaAi.Web.Customer/app/(main)/perfil/editar/page.tsx (1)
22-27:⚠️ Potential issue | 🟠 MajorGuard auth state before protected fetch.
Line 22 uses
session.accessTokenwithout checking missing token orsession.error. This still falls into a generic error state instead of re-auth flow; redirect before callingauthenticatedFetch.🔧 Suggested fix
+ if (!session.accessToken || session.error) { + redirect("/auth/signin"); + } + - const token = session.accessToken; - const data = await authenticatedFetch<MeAjudaAiModulesUsersApplicationDtosUserDto>(`/api/v1/users/${session.user.id}`, { - token: token + token: session.accessToken });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/perfil/editar/page.tsx around lines 22 - 27, The code reads session.accessToken and calls authenticatedFetch without guarding auth state; update the logic around session, checking for session?.accessToken and session?.error (or equivalent auth flags) before calling authenticatedFetch(`/api/v1/users/${session.user.id}`, ...); if the token is missing or session.error set, perform the re-auth/redirect flow (e.g., call your sign-in redirect helper or router push to login) and return early so authenticatedFetch is never invoked with an invalid session; ensure you reference the same symbols (session, session.accessToken, session.user.id, authenticatedFetch) when modifying the function.
🧹 Nitpick comments (1)
docs/roadmap.md (1)
10-10: Sugestão: reduzir risco de nova divergência de status/datas.Como o arquivo replica “status geral” em vários blocos, vale manter um bloco canônico (ex.: tabela de sprints) e referenciar os demais para minimizar drift futuro.
Based on learnings: For frigini/MeAjudaAi Web reviews, prefer commit-by-commit (newest-first) with concise verdicts and concrete follow-ups.
Also applies to: 103-103, 5088-5090
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/roadmap.md` at line 10, O bloco "Status Geral" está replicado e pode divergir; consolidate num único bloco canônico (ex.: uma "tabela de sprints" contendo colunas Sprint, Período, Status, Target) e altere as outras ocorrências no documento para referências/redirecionamentos a esse bloco canônico (ex.: link interno ou nota "ver Tabela de Sprints"); atualize as entradas atuais (ex.: Sprint 8A/8B, MVP Target 28/Março/2026) para a nova tabela e remova duplicatas; também alinhe o processo de revisão mencionado (commit-by-commit newest-first com veredicto conciso e follow-ups) num breve parágrafo de procedimentos para serem seguidos em futuras atualizações.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs`:
- Around line 43-53: SecurityHeadersMiddleware currently unconditionally assigns
headers using headers[header.Key] = header.Value and sets
headers.StrictTransportSecurity = HstsHeader, which overwrites headers set by
downstream middleware; change the loop to check headers.ContainsKey(header.Key)
and call headers.Append(header.Key, header.Value) only when the key is missing,
and for HSTS guard with if (ctx.Request.IsHttps && !_isDevelopment &&
!headers.ContainsKey("Strict-Transport-Security")) then call
headers.Append("Strict-Transport-Security", HstsHeader) so existing downstream
values are preserved (refer to StaticHeaders, headers, ctx.Request.IsHttps,
_isDevelopment, HstsHeader and the middleware's OnStarting callback).
In `@src/Shared/Utilities/UserRoles.cs`:
- Around line 8-11: The UserRoles.SuperAdmin constant is declared but not
included in the role registries, causing IsValidRole and IsAdminRole to return
false for "super-admin"; update the role collections (AllRoles and AdminRoles)
inside the UserRoles class to include UserRoles.SuperAdmin so that
IsValidRole("super-admin") and IsAdminRole("super-admin") behave correctly and
consistently across consumers.
In `@src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx`:
- Around line 57-61: The catch block in the login form currently unconditionally
sets a hardcoded providers list, which should be restricted to non-production;
update the catch inside the fetch/providers useEffect (or function that calls
setProviders in the LoginForm component) to only call
setProviders(["google","facebook","microsoft","apple"]) when running in a
non-production environment (e.g., process.env.NODE_ENV !== "production" or a
local/DEV feature flag), otherwise leave providers empty (or setProviders([]))
and log the error so production does not render unavailable auth buttons.
- Line 155: The code currently falls back to a hardcoded localhost URL when
building the reset-password href; remove the unsafe default by using
process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER without the "http://localhost:8080/..."
fallback and ensure the code handles a missing issuer (e.g. fail-fast or render
a warning) before constructing the URL; update the href expression that
references process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER and
process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID (and the encodeURIComponent usage) so
it does not silently use localhost in production.
---
Duplicate comments:
In `@src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs`:
- Around line 69-72: The DeactivateUserAsync call currently ignores
non-exception failures and uses the incoming cancellationToken which may cancel
the compensation; update the rollback logic in KeycloakUserDomainService so that
after calling keycloakService.DeactivateUserAsync(keycloakResult.Value, ...) you
check the returned Result/response for IsFailure and handle it (log the failure
and/or throw a specific exception or return a failed Result) instead of ignoring
it, and invoke the compensation with a non-cancelable or dedicated cancellation
token (e.g., CancellationToken.None or a new token sourced from a linked token
with its own timeout) so the rollback is not cancelled by the original request;
apply the same fix pattern to the similar block around the later
DeactivateUserAsync usage (the 75-78 area).
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/perfil/editar/page.tsx:
- Around line 22-27: The code reads session.accessToken and calls
authenticatedFetch without guarding auth state; update the logic around session,
checking for session?.accessToken and session?.error (or equivalent auth flags)
before calling authenticatedFetch(`/api/v1/users/${session.user.id}`, ...); if
the token is missing or session.error set, perform the re-auth/redirect flow
(e.g., call your sign-in redirect helper or router push to login) and return
early so authenticatedFetch is never invoked with an invalid session; ensure you
reference the same symbols (session, session.accessToken, session.user.id,
authenticatedFetch) when modifying the function.
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/perfil/page.tsx:
- Around line 24-27: The code in page.tsx calls
authenticatedFetch(`/api/v1/users/${session.user.id}`) using session.accessToken
without guarding for session.accessToken or session.error; update the logic in
the component (where session is used) to check for session?.accessToken and
absence of session?.error before performing authenticatedFetch, and if missing
or errored, trigger a redirect to sign-in (or return the appropriate redirect
response) instead of calling authenticatedFetch so the protected endpoint is
never called with an invalid session.
In `@src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx`:
- Around line 91-99: In handleSocialLogin, clear any stale auth errors before
starting a social sign-in so previous error messages don't persist: when
entering handleSocialLogin (before early return and before setIsLoading(true)),
call setError(undefined) or setError("") to reset error state, then proceed with
setIsLoading(true) and the existing signIn("keycloak", { callbackUrl }, {
kc_idp_hint: provider })/try-catch/finally flow; ensure setError is used in the
catch to set the new error as currently implemented.
---
Nitpick comments:
In `@docs/roadmap.md`:
- Line 10: O bloco "Status Geral" está replicado e pode divergir; consolidate
num único bloco canônico (ex.: uma "tabela de sprints" contendo colunas Sprint,
Período, Status, Target) e altere as outras ocorrências no documento para
referências/redirecionamentos a esse bloco canônico (ex.: link interno ou nota
"ver Tabela de Sprints"); atualize as entradas atuais (ex.: Sprint 8A/8B, MVP
Target 28/Março/2026) para a nova tabela e remova duplicatas; também alinhe o
processo de revisão mencionado (commit-by-commit newest-first com veredicto
conciso e follow-ups) num breve parágrafo de procedimentos para serem seguidos
em futuras atualizações.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
docs/roadmap.mdsrc/Bootstrapper/MeAjudaAi.ApiService/Handlers/SelfOrAdminHandler.cssrc/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cssrc/Modules/Users/API/Endpoints/Public/GetAuthProvidersEndpoint.cssrc/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cssrc/Modules/Users/Tests/Builders/UserBuilder.cssrc/Shared/Utilities/UserRoles.cssrc/Web/MeAjudaAi.Web.Admin/packages.lock.jsonsrc/Web/MeAjudaAi.Web.Customer/app/(main)/perfil/editar/page.tsxsrc/Web/MeAjudaAi.Web.Customer/app/(main)/perfil/page.tsxsrc/Web/MeAjudaAi.Web.Customer/components/auth/customer-register-form.tsxsrc/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsxsrc/Web/MeAjudaAi.Web.Customer/components/providers/dashboard-client.tsxsrc/Web/MeAjudaAi.Web.Customer/hooks/use-document-upload.tssrc/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.tssrc/Web/MeAjudaAi.Web.Customer/hooks/use-update-provider-profile.tssrc/Web/MeAjudaAi.Web.Customer/lib/api/fetch-client.ts
💤 Files with no reviewable changes (1)
- src/Web/MeAjudaAi.Web.Admin/packages.lock.json
🚧 Files skipped from review as they are similar to previous changes (2)
- src/Web/MeAjudaAi.Web.Customer/hooks/use-update-provider-profile.ts
- src/Web/MeAjudaAi.Web.Customer/components/auth/customer-register-form.tsx
src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs
Show resolved
Hide resolved
…tion forms, and core security infrastructure, alongside project roadmap documentation.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx (1)
159-163:⚠️ Potential issue | 🟡 MinorAvoid dead
"#"reset-password links when issuer is missing.Using
"#"creates a clickable control that does nothing meaningful. Prefer not rendering the link (or render disabled/help text) when config is absent.Suggested fix
+ const resetPasswordHref = process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER + ? `${process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER}/login-actions/reset-credentials?client_id=${encodeURIComponent(process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID || "customer-app")}` + : null ... <div className="text-center pt-1"> - <Link - href={process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER - ? `${process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER}/login-actions/reset-credentials?client_id=${encodeURIComponent(process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID || "customer-app")}` - : "#"} - className="text-sm font-medium underline underline-offset-4 hover:text-primary" - > - Esqueci minha senha - </Link> + {resetPasswordHref && ( + <Link + href={resetPasswordHref} + className="text-sm font-medium underline underline-offset-4 hover:text-primary" + > + Esqueci minha senha + </Link> + )} </div>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx` around lines 159 - 163, The reset-password Link in login-form.tsx currently renders as a dead "#" when NEXT_PUBLIC_KEYCLOAK_ISSUER is missing; change to conditional rendering so the clickable <Link> is only rendered when process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER is present (building the URL with encodeURIComponent(process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID || "customer-app")), otherwise render a non-clickable element (e.g., a <span> with the same styling but disabled/low-opacity and/or helper text) so users aren't presented with a misleading, inert link.
🧹 Nitpick comments (5)
src/Modules/Users/Tests/Unit/Infrastructure/Persistence/UserRepositoryTests.cs (1)
58-75: Consider asserting the actual KeycloakId value for consistency.The assertion at line 75 only checks
.NotBeNull(), which is weaker than asserting the actual value. Since the mock returns the sameuserobject, you can verify the complete round-trip. This would be consistent with the pattern used inGetByKeycloakIdAsync_WithExistingKeycloakId_ShouldReturnUser(line 200).💡 Suggested improvement
- result.KeycloakId.Should().NotBeNull(); + result.KeycloakId.Should().Be(user.KeycloakId);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Modules/Users/Tests/Unit/Infrastructure/Persistence/UserRepositoryTests.cs` around lines 58 - 75, Update the assertion to verify the exact KeycloakId value returned by the mock instead of only checking NotBeNull: after arranging the mock to return user, assert that result.KeycloakId equals user.KeycloakId (or the specific Guid string used when building user) so the GetByIdAsync round-trip is fully verified; keep references to _mockUserRepository, GetByIdAsync and the user builder to locate the code to change.src/Shared/Utilities/UserRoles.cs (1)
48-71: Consider adding a mapping utility or enum-to-role conversion helper.The provider tier role strings (
provider-standard,provider-silver, etc.) are logically aligned withEProviderTierenum values (Standard, Silver, Gold, Platinum), but there is no explicit conversion or validation mechanism linking them. While the current implementation maintains alignment through consistent naming conventions, adding a utility method to map between enum values and role strings would prevent future drift and improve maintainability.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Shared/Utilities/UserRoles.cs` around lines 48 - 71, Add a small mapping helper to centralize conversions between the EProviderTier enum and the role constants defined in UserRoles (ProviderStandard, ProviderSilver, ProviderGold, ProviderPlatinum); implement a method (e.g., UserRoles.FromProviderTier(EProviderTier) and/or UserRoles.TryParseProviderRole(string, out EProviderTier)) that returns the correct role string for a given EProviderTier and validates/parses role strings back to enum values so callers use the helper instead of hardcoding strings—update call sites to use these helpers to avoid drift between the enum and the role constants.src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs (1)
53-55: Use a constant for theStrict-Transport-Securityheader name.This avoids duplicated string literals and reduces typo risk in future edits.
♻️ Suggested refactor
private const string HstsHeader = "max-age=31536000; includeSubDomains"; + private const string StrictTransportSecurityHeaderName = "Strict-Transport-Security"; ... - if (ctx.Request.IsHttps && !_isDevelopment && !headers.ContainsKey("Strict-Transport-Security")) + if (ctx.Request.IsHttps && !_isDevelopment && !headers.ContainsKey(StrictTransportSecurityHeaderName)) { - headers.Append("Strict-Transport-Security", HstsHeader); + headers.Append(StrictTransportSecurityHeaderName, HstsHeader); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs` around lines 53 - 55, The code uses the literal "Strict-Transport-Security" when appending HSTS; introduce a single constant (e.g., a private static readonly string HstsHeaderName or const string HstsHeaderName) in the SecurityHeadersMiddleware class and replace the literal with that constant where headers.Append("Strict-Transport-Security", HstsHeader) is called (and anywhere else the header name is used) to avoid duplicated literals and typos while keeping the existing HstsHeader value for the header value.src/Web/MeAjudaAi.Web.Customer/app/(main)/perfil/editar/page.tsx (1)
22-25: Inline the temporarytokenvariable.
tokenis single-use here, so inlining reduces noise.♻️ Proposed cleanup
- const token = session.accessToken; - const data = await authenticatedFetch<MeAjudaAiModulesUsersApplicationDtosUserDto>(`/api/v1/users/${session.user.id}`, { - token: token + token: session.accessToken });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/perfil/editar/page.tsx around lines 22 - 25, The local variable token is only used once; remove the temporary const token and inline session.accessToken directly into the authenticatedFetch call (the call that invokes authenticatedFetch<MeAjudaAiModulesUsersApplicationDtosUserDto>(`/api/v1/users/${session.user.id}`, { token: token })). Update that call to pass token: session.accessToken (or equivalent) and delete the unused const token declaration.src/Web/MeAjudaAi.Web.Customer/components/auth/customer-register-form.tsx (1)
53-55: Guard against rapid re-submit before disabled state applies.A quick double-submit can trigger
onSubmittwice beforedisabled={isLoading}rerenders. Add an early return guard at Line 54.💡 Proposed fix
async function onSubmit(data: RegisterCustomerSchema) { + if (isLoading) return; setIsLoading(true); try {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/components/auth/customer-register-form.tsx` around lines 53 - 55, The onSubmit handler (async function onSubmit) can be invoked twice before the UI re-renders disabled={isLoading}; add an immediate guard at the start of onSubmit that checks the isLoading flag and returns early if true to prevent duplicate submissions, e.g., inside onSubmit check if (isLoading) return; then proceed to setIsLoading(true) and the existing try/catch/finally logic; reference the onSubmit function and the isLoading/setIsLoading state to locate where to add the guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/roadmap.md`:
- Line 10: Add a stable explicit anchor for the sprint schedule and update the
two internal references to point to it: insert an HTML anchor with id
"cronograma-de-sprints" immediately before the section header "## 📅 Cronograma
de Sprints (Novembro 2025-Março 2026)" (or equivalent header) and change both
link fragments that currently target the failing anchors to use
"#cronograma-de-sprints" so MD051/link-fragments no longer triggers and
navigation works reliably.
In `@src/Web/MeAjudaAi.Web.Customer/components/auth/customer-register-form.tsx`:
- Around line 135-140: The phone input onChange handler currently always applies
a 5+4 grouping (v = v.replace(/(\d{5})(\d)/, "$1-$2")), which misformats
10-digit numbers; update the mask logic in the onChange handler (the function
using field.onChange) to branch on the raw digit count: if digits length >= 11
use the 5+4 pattern, otherwise use a 4+4 pattern for 10-digit/landline numbers,
then apply the existing area code formatting and call field.onChange with the
capped substring as before.
In `@src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx`:
- Around line 112-151: Add explicit, programmatic labels for the email and
password inputs instead of relying on placeholders/icons: add a visually-hidden
label element (e.g., using a "sr-only" class) for the email input and another
for the password input, give each Input a unique id (e.g., id="email" and
id="password") and reference them from the corresponding label via htmlFor,
ensure the Mail and Lock icons are aria-hidden or decorative so they don't
duplicate label text, and keep the existing aria-label on the password toggle
Button (toggle controlled by showPassword) intact.
---
Duplicate comments:
In `@src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx`:
- Around line 159-163: The reset-password Link in login-form.tsx currently
renders as a dead "#" when NEXT_PUBLIC_KEYCLOAK_ISSUER is missing; change to
conditional rendering so the clickable <Link> is only rendered when
process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER is present (building the URL with
encodeURIComponent(process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID ||
"customer-app")), otherwise render a non-clickable element (e.g., a <span> with
the same styling but disabled/low-opacity and/or helper text) so users aren't
presented with a misleading, inert link.
---
Nitpick comments:
In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs`:
- Around line 53-55: The code uses the literal "Strict-Transport-Security" when
appending HSTS; introduce a single constant (e.g., a private static readonly
string HstsHeaderName or const string HstsHeaderName) in the
SecurityHeadersMiddleware class and replace the literal with that constant where
headers.Append("Strict-Transport-Security", HstsHeader) is called (and anywhere
else the header name is used) to avoid duplicated literals and typos while
keeping the existing HstsHeader value for the header value.
In
`@src/Modules/Users/Tests/Unit/Infrastructure/Persistence/UserRepositoryTests.cs`:
- Around line 58-75: Update the assertion to verify the exact KeycloakId value
returned by the mock instead of only checking NotBeNull: after arranging the
mock to return user, assert that result.KeycloakId equals user.KeycloakId (or
the specific Guid string used when building user) so the GetByIdAsync round-trip
is fully verified; keep references to _mockUserRepository, GetByIdAsync and the
user builder to locate the code to change.
In `@src/Shared/Utilities/UserRoles.cs`:
- Around line 48-71: Add a small mapping helper to centralize conversions
between the EProviderTier enum and the role constants defined in UserRoles
(ProviderStandard, ProviderSilver, ProviderGold, ProviderPlatinum); implement a
method (e.g., UserRoles.FromProviderTier(EProviderTier) and/or
UserRoles.TryParseProviderRole(string, out EProviderTier)) that returns the
correct role string for a given EProviderTier and validates/parses role strings
back to enum values so callers use the helper instead of hardcoding
strings—update call sites to use these helpers to avoid drift between the enum
and the role constants.
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/perfil/editar/page.tsx:
- Around line 22-25: The local variable token is only used once; remove the
temporary const token and inline session.accessToken directly into the
authenticatedFetch call (the call that invokes
authenticatedFetch<MeAjudaAiModulesUsersApplicationDtosUserDto>(`/api/v1/users/${session.user.id}`,
{ token: token })). Update that call to pass token: session.accessToken (or
equivalent) and delete the unused const token declaration.
In `@src/Web/MeAjudaAi.Web.Customer/components/auth/customer-register-form.tsx`:
- Around line 53-55: The onSubmit handler (async function onSubmit) can be
invoked twice before the UI re-renders disabled={isLoading}; add an immediate
guard at the start of onSubmit that checks the isLoading flag and returns early
if true to prevent duplicate submissions, e.g., inside onSubmit check if
(isLoading) return; then proceed to setIsLoading(true) and the existing
try/catch/finally logic; reference the onSubmit function and the
isLoading/setIsLoading state to locate where to add the guard.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
docs/roadmap.mdsrc/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cssrc/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cssrc/Modules/Users/Tests/Infrastructure/Mocks/MockKeycloakService.cssrc/Modules/Users/Tests/Integration/LocalDevelopmentServicesIntegrationTests.cssrc/Modules/Users/Tests/Integration/UserRepositoryIntegrationTests.cssrc/Modules/Users/Tests/Unit/Application/Mappers/UserMappersTests.cssrc/Modules/Users/Tests/Unit/Application/Queries/GetUserByIdQueryHandlerTests.cssrc/Modules/Users/Tests/Unit/Infrastructure/Events/Handlers/UserRegisteredDomainEventHandlerTests.cssrc/Modules/Users/Tests/Unit/Infrastructure/Persistence/UserRepositoryTests.cssrc/Modules/Users/Tests/Unit/Infrastructure/Services/KeycloakUserDomainServiceTests.cssrc/Modules/Users/Tests/Unit/Mocks/Services/MockKeycloakServiceTests.cssrc/Shared/Utilities/UserRoles.cssrc/Web/MeAjudaAi.Web.Customer/app/(main)/perfil/editar/page.tsxsrc/Web/MeAjudaAi.Web.Customer/app/(main)/perfil/page.tsxsrc/Web/MeAjudaAi.Web.Customer/components/auth/customer-register-form.tsxsrc/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- src/Modules/Users/Tests/Unit/Infrastructure/Services/KeycloakUserDomainServiceTests.cs
- src/Web/MeAjudaAi.Web.Customer/app/(main)/perfil/page.tsx
src/Web/MeAjudaAi.Web.Customer/components/auth/customer-register-form.tsx
Show resolved
Hide resolved
…eaders middleware, and introduce new customer authentication UI components.
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs (1)
147-160:⚠️ Potential issue | 🟡 MinorAdd missing SuperAdmin test case to IsAdminRole_WithAdminRole_ShouldReturnTrue.
The
AdminRolescollection includesUserRoles.SuperAdmin(verified as"super-admin"), but this test method doesn't include a test case for it.🧪 Proposed fix
[Theory] +[InlineData("super-admin")] [InlineData("admin")] [InlineData("provider-manager")] [InlineData("document-reviewer")] [InlineData("catalog-manager")] [InlineData("operator")] public void IsAdminRole_WithAdminRole_ShouldReturnTrue(string role)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs` around lines 147 - 160, The test IsAdminRole_WithAdminRole_ShouldReturnTrue is missing coverage for the SuperAdmin role; update the Theory's InlineData attributes to include UserRoles.SuperAdmin (i.e. add an InlineData("super-admin")) so the test calls UserRoles.IsAdminRole with the super-admin value and asserts true, ensuring AdminRoles (and specifically UserRoles.SuperAdmin) are validated by this test.
🧹 Nitpick comments (3)
src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs (2)
57-60: Keep inline comments in Portuguese for consistency.Line 57 and Line 60 comments are in English, while this codebase policy expects comments/XML docs in Portuguese.
Based on learnings: comments and XML documentation should be written in Portuguese across C# files, while logs remain in English.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs` around lines 57 - 60, Troque os comentários em inglês nessa lógica de parsing do nome para português: substitua as duas linhas de comentário acima de "var nameParts = request.Name.Trim().Split(' ', 2);" e do comentário ao lado de "var lastName = ..." por textos em português (por exemplo explicando que se divide o nome em até duas partes e que, na ausência de sobrenome, usa-se o primeiro nome como fallback). Altere apenas os comentários relacionados a request.Name, nameParts, firstName e lastName para manter a consistência da base.
36-36: Replace hardcoded rate-limit policy string with a shared constant.On Line 36, using a string literal (
"provider-registration") can drift from central policy definitions. Prefer the shared constant to keep API and security config aligned.Suggested fix
- .RequireRateLimiting("provider-registration"); + .RequireRateLimiting(RateLimitPolicies.ProviderRegistration);Based on learnings: prefer reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs` at line 36, Replace the hardcoded policy name in the ProviderRegistrationEndpoints routing call by referencing the shared constant from Shared.Contracts instead of the string literal; specifically, in ProviderRegistrationEndpoints change .RequireRateLimiting("provider-registration") to use the shared enum/constant (e.g., Shared.Contracts.RateLimitPolicies.ProviderRegistration or the equivalent constant name provided by Shared.Contracts) so the endpoint uses the centralized policy identifier.tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs (1)
60-72: Consider adding SuperAdmin constant value verification.Since
UserRoles.SuperAdminis now included in theAllRoles(line 18) andAdminRoles(line 40) assertions, consider adding its expected string value verification here for completeness.📝 Suggested addition
// Assert + UserRoles.SuperAdmin.Should().Be("super-admin"); UserRoles.Admin.Should().Be("admin"); UserRoles.ProviderManager.Should().Be("provider-manager");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs` around lines 60 - 72, Add an assertion verifying the string value of UserRoles.SuperAdmin to the test; update the assertions block in UserRolesTests (near the existing checks like UserRoles.Admin and UserRoles.ProviderManager) to include UserRoles.SuperAdmin.Should().Be("super-admin") so the SuperAdmin constant is explicitly validated alongside AllRoles and AdminRoles coverage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs`:
- Around line 115-117: The compensation currently only catches exceptions when
sending DeleteUserCommand, but it must also check the SendAsync result for
failures; after creating DeleteUserCommand(userResult.Value!.Id) and awaiting
commandDispatcher.SendAsync<DeleteUserCommand, Result>(...), inspect the
returned Result and if Result.IsFailure log an error and handle the failure
(e.g., retry, escalate, or record the orphaned identity for later cleanup)
instead of silently continuing; update the compensation block around
DeleteUserCommand to treat non-exceptional failure results the same way as
exceptions by logging the Result.Error (or similar) and taking the chosen
remediation path.
- Around line 54-55: The sanitized phone string (variable phone) can be empty
causing username = "provider_" collisions; in ProviderRegistrationEndpoints
(where request.PhoneNumber is sanitized) check that phone is not empty after
Regex.Replace and handle it explicitly: either return a BadRequest/validation
error referencing request.PhoneNumber or generate a safe fallback username
(e.g., include a GUID like $"provider_{Guid.NewGuid():N}") to avoid collisions;
update the code paths that use username to rely on this validated/fallback
value.
In `@src/Modules/Providers/Domain/Enums/ProviderTierExtensions.cs`:
- Around line 26-45: The TryParseRole method currently calls
role?.ToLowerInvariant() and compares to UserRoles constants which couples
parsing to casing and fails for surrounding whitespace; change it to trim the
input (role?.Trim()) and perform comparisons using case-insensitive string
equality (String.Equals with OrdinalIgnoreCase) against the UserRoles constants
so parsing is robust to casing changes and extra whitespace; update the switch
(or replace with if/else using String.Equals) in TryParseRole to use the
trimmed, un-normalized input and set the appropriate EProviderTier values
(EProviderTier.Standard, Silver, Gold, Platinum) or default as before.
In `@src/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsx`:
- Around line 48-68: The async effect in useEffect calls fetchProviders which
awaits and then calls setProviders; guard against updating state after unmount
by adding a mounted flag (e.g., let isMounted = true) inside the effect, check
isMounted before any setProviders calls in fetchProviders (including the
fallback in the catch), and return a cleanup function that sets isMounted =
false to prevent stale updates; update references to setProviders within
fetchProviders and the catch block accordingly to only run when isMounted is
true.
- Around line 70-74: handleSubmit currently lacks a re-entry guard and can fire
multiple credential requests; in the handleSubmit function add an early return
that checks the isLoading state (e.g., if (isLoading) return) before proceeding
to prevent duplicate submissions, keeping the rest of the logic (setError,
setIsLoading(true), credential handling) unchanged.
---
Outside diff comments:
In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs`:
- Around line 147-160: The test IsAdminRole_WithAdminRole_ShouldReturnTrue is
missing coverage for the SuperAdmin role; update the Theory's InlineData
attributes to include UserRoles.SuperAdmin (i.e. add an
InlineData("super-admin")) so the test calls UserRoles.IsAdminRole with the
super-admin value and asserts true, ensuring AdminRoles (and specifically
UserRoles.SuperAdmin) are validated by this test.
---
Nitpick comments:
In
`@src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs`:
- Around line 57-60: Troque os comentários em inglês nessa lógica de parsing do
nome para português: substitua as duas linhas de comentário acima de "var
nameParts = request.Name.Trim().Split(' ', 2);" e do comentário ao lado de "var
lastName = ..." por textos em português (por exemplo explicando que se divide o
nome em até duas partes e que, na ausência de sobrenome, usa-se o primeiro nome
como fallback). Altere apenas os comentários relacionados a request.Name,
nameParts, firstName e lastName para manter a consistência da base.
- Line 36: Replace the hardcoded policy name in the
ProviderRegistrationEndpoints routing call by referencing the shared constant
from Shared.Contracts instead of the string literal; specifically, in
ProviderRegistrationEndpoints change
.RequireRateLimiting("provider-registration") to use the shared enum/constant
(e.g., Shared.Contracts.RateLimitPolicies.ProviderRegistration or the equivalent
constant name provided by Shared.Contracts) so the endpoint uses the centralized
policy identifier.
In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs`:
- Around line 60-72: Add an assertion verifying the string value of
UserRoles.SuperAdmin to the test; update the assertions block in UserRolesTests
(near the existing checks like UserRoles.Admin and UserRoles.ProviderManager) to
include UserRoles.SuperAdmin.Should().Be("super-admin") so the SuperAdmin
constant is explicitly validated alongside AllRoles and AdminRoles coverage.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
docs/roadmap.mdsrc/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cssrc/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cssrc/Modules/Providers/Domain/Enums/ProviderTierExtensions.cssrc/Modules/Users/Tests/Unit/Infrastructure/Persistence/UserRepositoryTests.cssrc/Web/MeAjudaAi.Web.Customer/app/(main)/perfil/editar/page.tsxsrc/Web/MeAjudaAi.Web.Customer/components/auth/customer-register-form.tsxsrc/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsxtests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs
🚧 Files skipped from review as they are similar to previous changes (4)
- src/Web/MeAjudaAi.Web.Customer/app/(main)/perfil/editar/page.tsx
- src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs
- src/Modules/Users/Tests/Unit/Infrastructure/Persistence/UserRepositoryTests.cs
- src/Web/MeAjudaAi.Web.Customer/components/auth/customer-register-form.tsx
src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs
Outdated
Show resolved
Hide resolved
src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs
Show resolved
Hide resolved
…, provider registration endpoints, and user role unit tests.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs (1)
79-91: Addsuper-admintoIsValidRoletheory inputs for full parity.
AllRolesnow includessuper-admin, but valid-role theory data still doesn’t exercise it (including mixed-case). Add both to close the gap.♻️ Proposed patch
[Theory] + [InlineData("super-admin")] [InlineData("admin")] [InlineData("provider-manager")] @@ [Theory] + [InlineData("SUPER-ADMIN")] [InlineData("ADMIN")] [InlineData("Provider-Manager")]Also applies to: 100-109
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs` around lines 79 - 91, The theory for IsValidRole_WithValidRole_ShouldReturnTrue is missing the "super-admin" cases now present in AllRoles; update the test by adding InlineData("super-admin") and InlineData("Super-Admin") to the list of inputs (also mirror the same additions in the corresponding test block around lines 100-109), so the IsValidRole_WithValidRole_ShouldReturnTrue method exercises both the lowercase and mixed-case super-admin variants.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs`:
- Around line 79-91: The theory for IsValidRole_WithValidRole_ShouldReturnTrue
is missing the "super-admin" cases now present in AllRoles; update the test by
adding InlineData("super-admin") and InlineData("Super-Admin") to the list of
inputs (also mirror the same additions in the corresponding test block around
lines 100-109), so the IsValidRole_WithValidRole_ShouldReturnTrue method
exercises both the lowercase and mixed-case super-admin variants.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cssrc/Modules/Providers/Domain/Enums/ProviderTierExtensions.cssrc/Web/MeAjudaAi.Web.Customer/components/auth/login-form.tsxtests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs
🚧 Files skipped from review as they are similar to previous changes (2)
- src/Modules/Providers/Domain/Enums/ProviderTierExtensions.cs
- src/Bootstrapper/MeAjudaAi.ApiService/Endpoints/ProviderRegistrationEndpoints.cs
… UI, Keycloak realm, and development environment script.
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (4)
src/Bootstrapper/MeAjudaAi.ApiService/MigrationExtensions.cs (1)
33-37: Make migration ordering fully deterministic.Line 33 currently sorts only by priority. Add a stable tie-breaker (e.g., module name/full type name) so fallback modules (
99) and equal priorities always execute in predictable order.Deterministic ordering refactor
- dbContextTypes = dbContextTypes.OrderBy(t => - { - var moduleName = ExtractModuleName(t); - return modulePriority.TryGetValue(moduleName, out var p) ? p : 99; - }).ToList(); + dbContextTypes = dbContextTypes + .OrderBy(t => + { + var moduleName = ExtractModuleName(t); + return modulePriority.TryGetValue(moduleName, out var p) ? p : 99; + }) + .ThenBy(t => ExtractModuleName(t), StringComparer.Ordinal) + .ThenBy(t => t.FullName, StringComparer.Ordinal) + .ToList();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Bootstrapper/MeAjudaAi.ApiService/MigrationExtensions.cs` around lines 33 - 37, The current dbContextTypes ordering uses only modulePriority which can produce non-deterministic ties; update the sorting in the code that builds dbContextTypes (the OrderBy block that calls ExtractModuleName and modulePriority) to include a stable tie-breaker by chaining a secondary comparer (e.g., ThenBy) on moduleName or the type's full name (t.FullName) so that items with equal priority (including fallback 99) are ordered predictably; keep the existing priority lookup (modulePriority.TryGetValue) and then apply ThenBy(moduleName) or ThenBy(t => t.FullName) before ToList().src/Aspire/MeAjudaAi.AppHost/Program.cs (1)
140-140: Remove debug logging or make it conditional.This debug statement logs configuration values to console, which may expose configuration details in logs and provides limited value in production. Consider removing it or guarding with a debug flag.
🧹 Proposed fix
- Console.WriteLine($"[Aspire Debug] GOOGLE_CLIENT_ID = '{builder.Configuration["GOOGLE_CLIENT_ID"]}'");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Aspire/MeAjudaAi.AppHost/Program.cs` at line 140, The Console.WriteLine that prints builder.Configuration["GOOGLE_CLIENT_ID"] in Program.cs should be removed or made conditional; locate the Console.WriteLine($"[Aspire Debug] GOOGLE_CLIENT_ID = '{builder.Configuration["GOOGLE_CLIENT_ID"]}'") statement in Program.cs (Program main/bootstrap) and either delete it or wrap it behind a development-only check (e.g., builder.Environment.IsDevelopment() or a specific debug configuration flag) so it does not run in production; if you need visibility keep it as a logger.Debug/Trace call (not Console) without printing secrets directly.tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs (1)
236-251: Consider adding"super-admin"to non-provider role test data for completeness.The test covers most admin roles but omits
"super-admin". While not critical (the logic is sound), adding it would make the test exhaustive for all non-provider roles.♻️ Optional: Add super-admin to test data
[Theory] + [InlineData("super-admin")] [InlineData("admin")] [InlineData("provider-manager")] [InlineData("document-reviewer")] [InlineData("catalog-manager")] [InlineData("operator")] [InlineData("customer")] [InlineData("viewer")] public void IsProviderRole_WithNonProviderRole_ShouldReturnFalse(string role)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs` around lines 236 - 251, Add "super-admin" to the non-provider role test data for the IsProviderRole unit test: update the theory's InlineData attributes used by the IsProviderRole_WithNonProviderRole_ShouldReturnFalse test so one of the InlineData entries is "super-admin", ensuring UserRoles.IsProviderRole is exercised against that non-provider role as well.src/Web/MeAjudaAi.Web.Customer/layout.css (1)
1-4: Prefer source CSS over committed generated Tailwind bundleLines 1-4 show this file is generated from
./app/globals.css. Keeping generated output in source creates noisy diffs and recurring lint errors that are not meaningful for code review.Consider keeping
globals.cssas the reviewed source-of-truth and either:
- generating this file in build artifacts only, or
- excluding this file from stylelint/biome checks if it must be committed.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/layout.css` around lines 1 - 4, This committed file is a generated Tailwind bundle (layout.css) produced from ./app/globals.css and should not be treated as source; remove the generated layout.css from the repo (or stop committing it), add it to .gitignore, and update your build pipeline to generate it only as a build artifact, and/or exclude the generated file from style linting by adding an ignore pattern for the generated header/filename in your linter config (e.g., stylelint/biome ignorePatterns for layout.css or the Tailwind bundle comment) so that ./app/globals.css remains the single source-of-truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/dev.ps1`:
- Around line 30-32: The current loop in scripts/dev.ps1 reads .env lines into
$name and $value and calls Set-Item with $value.Trim(), but it doesn't remove
surrounding quotes so values like KEY="value" keep the quotes; update the
ForEach-Object block (the Get-Content ... ForEach-Object pipeline that assigns
$name, $value) to strip matching leading and trailing single or double quotes
from $value after Trim() before calling Set-Item (handle empty values and
preserve inner quotes). Ensure you still split with Split('=', 2) and use the
same Set-Item path ("env:$name") but assign the cleaned/unquoted value instead
of the raw trimmed string.
In `@src/Aspire/MeAjudaAi.AppHost/Program.cs`:
- Around line 141-147: The Keycloak environment wiring uses
builder.Configuration[...] ?? "" which masks missing OAuth credentials and
causes silent runtime failures; instead, read each credential into local vars
from builder.Configuration (e.g., googleClientId, googleClientSecret,
facebookAppId, etc.), validate with string.IsNullOrWhiteSpace, and for any
missing credential either log a clear warning (including the provider name) or
fail fast; then only call keycloak.Keycloak.WithEnvironment for providers that
have valid non-empty values (or explicitly disable the provider in the Keycloak
config) so WithEnvironment isn't invoked with empty strings.
In `@src/Bootstrapper/MeAjudaAi.ApiService/MigrationExtensions.cs`:
- Around line 22-23: A linha de comentário acima da variável modulePriority em
MigrationExtensions.cs está em inglês ("Ensure ServiceCatalogs runs before
Providers (cross-module SQL dependency in migrations)"); substitua esse
comentário por um equivalente em português, por exemplo explicando que o módulo
ServiceCatalogs deve rodar antes de Providers devido à dependência SQL entre
módulos, mantendo o contexto entre parênteses; altere apenas o texto do
comentário e não o código ao redor (procure a variável modulePriority para
localizar o local).
In `@src/Web/MeAjudaAi.Web.Customer/layout.css`:
- Around line 979-1002: Several border utility classes (.border-border,
.border-destructive, .border-gray-100/200/300, .border-green-100/200,
.border-input) hardcode light-mode colors and bypass theme tokens; replace their
literal hex values with the corresponding CSS custom properties (e.g., use
var(--color-border), var(--color-destructive), var(--color-gray-100) etc.) so
they respect the dark-mode token overrides defined in the theme, and do the same
audit/fix for nearby utilities mentioned (.bg-surface, .bg-popover,
.text-muted-foreground) to ensure all semantic utilities reference theme tokens
instead of fixed colors.
- Around line 2157-2162: Remove the duplicate malformed utility block for the
class .\[\\u0026_svg\]\:size-5 that uses the invalid selector &:is(\u0026 svg);
locate the duplicate definition (the one containing &:is(\u0026 svg)) and delete
it, keeping the correct existing rule that uses the valid selector & svg so the
size-5 SVG utility remains defined only once.
---
Nitpick comments:
In `@src/Aspire/MeAjudaAi.AppHost/Program.cs`:
- Line 140: The Console.WriteLine that prints
builder.Configuration["GOOGLE_CLIENT_ID"] in Program.cs should be removed or
made conditional; locate the Console.WriteLine($"[Aspire Debug] GOOGLE_CLIENT_ID
= '{builder.Configuration["GOOGLE_CLIENT_ID"]}'") statement in Program.cs
(Program main/bootstrap) and either delete it or wrap it behind a
development-only check (e.g., builder.Environment.IsDevelopment() or a specific
debug configuration flag) so it does not run in production; if you need
visibility keep it as a logger.Debug/Trace call (not Console) without printing
secrets directly.
In `@src/Bootstrapper/MeAjudaAi.ApiService/MigrationExtensions.cs`:
- Around line 33-37: The current dbContextTypes ordering uses only
modulePriority which can produce non-deterministic ties; update the sorting in
the code that builds dbContextTypes (the OrderBy block that calls
ExtractModuleName and modulePriority) to include a stable tie-breaker by
chaining a secondary comparer (e.g., ThenBy) on moduleName or the type's full
name (t.FullName) so that items with equal priority (including fallback 99) are
ordered predictably; keep the existing priority lookup
(modulePriority.TryGetValue) and then apply ThenBy(moduleName) or ThenBy(t =>
t.FullName) before ToList().
In `@src/Web/MeAjudaAi.Web.Customer/layout.css`:
- Around line 1-4: This committed file is a generated Tailwind bundle
(layout.css) produced from ./app/globals.css and should not be treated as
source; remove the generated layout.css from the repo (or stop committing it),
add it to .gitignore, and update your build pipeline to generate it only as a
build artifact, and/or exclude the generated file from style linting by adding
an ignore pattern for the generated header/filename in your linter config (e.g.,
stylelint/biome ignorePatterns for layout.css or the Tailwind bundle comment) so
that ./app/globals.css remains the single source-of-truth.
In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs`:
- Around line 236-251: Add "super-admin" to the non-provider role test data for
the IsProviderRole unit test: update the theory's InlineData attributes used by
the IsProviderRole_WithNonProviderRole_ShouldReturnFalse test so one of the
InlineData entries is "super-admin", ensuring UserRoles.IsProviderRole is
exercised against that non-provider role as well.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3dbf1cd5-e960-46a8-99a9-6dd303ad1c2b
📒 Files selected for processing (10)
infrastructure/keycloak/realms/meajudaai-realm.dev.jsonscripts/dev.ps1src/Aspire/MeAjudaAi.AppHost/Program.cssrc/Bootstrapper/MeAjudaAi.ApiService/MigrationExtensions.cssrc/Modules/Documents/API/Extensions.cssrc/Modules/Providers/API/Extensions.cssrc/Modules/ServiceCatalogs/API/Extensions.cssrc/Modules/Users/API/Extensions.cssrc/Web/MeAjudaAi.Web.Customer/layout.csstests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs
💤 Files with no reviewable changes (4)
- src/Modules/Documents/API/Extensions.cs
- src/Modules/Users/API/Extensions.cs
- src/Modules/Providers/API/Extensions.cs
- src/Modules/ServiceCatalogs/API/Extensions.cs
🚧 Files skipped from review as they are similar to previous changes (1)
- infrastructure/keycloak/realms/meajudaai-realm.dev.json
…vider registration pages, establish initial Aspire AppHost, and configure CI workflow.
…ustomer web app, alongside Aspire AppHost and development environment setup.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (5)
src/Web/MeAjudaAi.Web.Customer/app/globals.css (1)
34-41: Minor: Dark mode foreground tokens use hardcoded values instead ofvar(--foreground).In light mode,
--popover-foreground,--card-foreground, and--accent-foregroundreferencevar(--foreground), but their dark mode counterparts use the hardcoded value#ededed. While functionally equivalent, this inconsistency could cause divergence if--foregroundis later changed.♻️ Suggested fix for consistency
--popover: `#1a1a1a`; - --popover-foreground: `#ededed`; + --popover-foreground: var(--foreground); --card: `#1a1a1a`; - --card-foreground: `#ededed`; + --card-foreground: var(--foreground); --muted: `#262626`; --muted-foreground: `#a3a3a3`; --accent: `#262626`; - --accent-foreground: `#ededed`; + --accent-foreground: var(--foreground);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/globals.css` around lines 34 - 41, Dark mode CSS variables --popover-foreground, --card-foreground, and --accent-foreground are hardcoded to `#ededed`; replace those hardcoded values with var(--foreground) so they mirror the light-mode definitions and remain consistent if --foreground changes (update the dark-mode rule that currently sets --popover-foreground, --card-foreground, and --accent-foreground).infrastructure/keycloak/themes/meajudaai/login/resources/css/login.css (2)
196-208: Add explicit keyboard focus style for#updateProfile.Line 197-208 define default/hover states, but there is no explicit
:focus-visibletreatment for keyboard navigation.Suggested patch
`#updateProfile` { background: transparent !important; border: 2px solid var(--primary-blue) !important; color: var(--primary-blue) !important; box-shadow: none !important; + transition: all 0.2s ease; } `#updateProfile`:hover { background: rgba(57, 88, 115, 0.05) !important; border-color: var(--primary-blue-dark) !important; color: var(--primary-blue-dark) !important; } + +#updateProfile:focus-visible { + outline: 2px solid var(--primary-blue-dark); + outline-offset: 2px; +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/keycloak/themes/meajudaai/login/resources/css/login.css` around lines 196 - 208, The `#updateProfile` button lacks an explicit keyboard focus style; add a :focus-visible rule for `#updateProfile` that applies a clear accessible focus indicator (e.g., a visible outline or ring using var(--primary-blue-dark) and optionally a subtle box-shadow) while preserving existing transparent background and border; ensure the rule uses :focus-visible (not just :focus) so mouse interactions are unaffected and include !important only if necessary to override existing styles in login.css.
205-205: Prefer tokenized color over hard-coded RGB in hover background.Line 205 hard-codes
rgba(57, 88, 115, 0.05), which can drift from theme tokens later. Consider deriving this from a variable to keep palette changes centralized.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@infrastructure/keycloak/themes/meajudaai/login/resources/css/login.css` at line 205, The hard-coded background rgba(57, 88, 115, 0.05) should be replaced with a theme token: create or reuse a CSS variable (e.g. --ma-login-hover-bg) in your theme root/variables and then update the rule that currently has background: rgba(57, 88, 115, 0.05) !important; to background: var(--ma-login-hover-bg, rgba(57,88,115,0.05)) !important; so palette changes stay centralized (define the variable once and reference it in the login.css rule).src/Aspire/MeAjudaAi.AppHost/Program.cs (1)
140-177: Extract social provider credential wiring into one helper.The three blocks are structurally identical; consolidating them will reduce drift and keep warning behavior consistent across providers.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Aspire/MeAjudaAi.AppHost/Program.cs` around lines 140 - 177, The three repeated blocks wiring OAuth credentials should be extracted into a single helper (e.g., ConfigureSocialProvider or AddSocialProviderEnv) that takes the provider name and configuration keys, reads builder.Configuration for the client id/secret, calls keycloak.Keycloak.WithEnvironment for the two variables when present, and logs the consistent warning ("⚠️ WARNING: {Provider} OAuth credentials are missing. {Provider} Login may fail.") when either value is missing; replace the inline Google/Facebook/Instagram blocks with three calls to this helper so the WithEnvironment usage and warning behavior are centralized and identical.scripts/dev.ps1 (1)
30-31: Consider relaxing the.envkey parser for spacing variants.Current filter
^[\w-]+=skips lines likeKEY = valueor lines with leading whitespace. If you want broader.envcompatibility, trim$nameand allow optional whitespace around=.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/dev.ps1` around lines 30 - 31, The current line-filter regex in the Get-Content pipeline is too strict and skips keys with leading/trailing whitespace around the name or the '='; update the Where-Object match to allow optional leading whitespace and optional spaces around the '=' (e.g., use a pattern like '^\s*[\w-]+\s*=') and after splitting with $_.Split('=',2) trim both $name and $value (call $name.Trim() and $value.Trim()) so keys like " KEY = value " are accepted; refer to $envFilePath, Get-Content, Where-Object, the Split call, and the $name/$value variables to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Aspire/MeAjudaAi.AppHost/Program.cs`:
- Around line 166-177: Program.cs currently injects INSTAGRAM_CLIENT_ID and
INSTAGRAM_CLIENT_SECRET via variables instagramClientId and
instagramClientSecret and calls keycloak.Keycloak.WithEnvironment(...), but the
dev realm JSON has no Instagram IdP so credentials are unused; either add an
Instagram identity provider entry to the realm import
(infrastructure/keycloak/realms/meajudaai-realm.dev.json) matching those
environment keys, or remove the instagramClientId/instagramClientSecret block
(the keycloak.Keycloak.WithEnvironment calls) until an Instagram IdP is added so
you don’t inject unused credentials.
In
`@src/Web/MeAjudaAi.Web.Customer/app/`(main)/cadastro/prestador/perfil/endereco/page.tsx:
- Around line 64-67: In handleCepBlur, normalize the CEP by stripping non-digit
chars from e.target.value and only call fetchAddress when the normalized CEP has
exactly 8 digits; pass the normalized value to fetchAddress (and update any
dependent state/input value if necessary) so malformed or formatted CEPs (with
dashes/spaces) do not trigger lookup and ViaCEP always receives an 8-digit
string.
- Around line 259-279: The city and state FormField inputs (name="city" and
name="state") are marked readOnly which prevents users from correcting required
fields in addressSchema if the CEP lookup fails; update the render logic in the
FormField for "city" and "state" to allow manual editing when the lookup didn't
populate values (e.g., conditionally set readOnly based on a lookup success flag
or the presence of form.getValues('city')/form.getValues('state')) or remove the
readOnly attribute entirely so users can input values; keep existing validation
(addressSchema) and UI classes, but ensure any visual “readonly” styling is only
applied when the lookup truly succeeded.
In
`@src/Web/MeAjudaAi.Web.Customer/app/`(main)/cadastro/prestador/perfil/page.tsx:
- Around line 72-77: Step 2/3 are being marked complete too easily because
hasAddress and hasDocuments only check superficial truthiness
(profile.businessProfile?.primaryAddress?.street and documents.length), so
update those checks to validate real, non-placeholder data: change hasAddress to
require the primaryAddress object and required fields (e.g., street, number,
city, postalCode) be non-empty and not placeholder values (trimmed strings like
"-", "N/A", "unknown"), and change hasDocuments to require at least one document
entry in profile.documents with a real type and an uploaded/valid file indicator
(e.g., fileUrl present or status === "uploaded") rather than just length; keep
step2Completed/step3Completed semantics but combine these stricter
hasAddress/hasDocuments checks with isPendingVerification only (do not rely on
placeholder fields or isVerified/isSuspended to mark steps complete).
---
Nitpick comments:
In `@infrastructure/keycloak/themes/meajudaai/login/resources/css/login.css`:
- Around line 196-208: The `#updateProfile` button lacks an explicit keyboard
focus style; add a :focus-visible rule for `#updateProfile` that applies a clear
accessible focus indicator (e.g., a visible outline or ring using
var(--primary-blue-dark) and optionally a subtle box-shadow) while preserving
existing transparent background and border; ensure the rule uses :focus-visible
(not just :focus) so mouse interactions are unaffected and include !important
only if necessary to override existing styles in login.css.
- Line 205: The hard-coded background rgba(57, 88, 115, 0.05) should be replaced
with a theme token: create or reuse a CSS variable (e.g. --ma-login-hover-bg) in
your theme root/variables and then update the rule that currently has
background: rgba(57, 88, 115, 0.05) !important; to background:
var(--ma-login-hover-bg, rgba(57,88,115,0.05)) !important; so palette changes
stay centralized (define the variable once and reference it in the login.css
rule).
In `@scripts/dev.ps1`:
- Around line 30-31: The current line-filter regex in the Get-Content pipeline
is too strict and skips keys with leading/trailing whitespace around the name or
the '='; update the Where-Object match to allow optional leading whitespace and
optional spaces around the '=' (e.g., use a pattern like '^\s*[\w-]+\s*=') and
after splitting with $_.Split('=',2) trim both $name and $value (call
$name.Trim() and $value.Trim()) so keys like " KEY = value " are accepted; refer
to $envFilePath, Get-Content, Where-Object, the Split call, and the $name/$value
variables to locate the change.
In `@src/Aspire/MeAjudaAi.AppHost/Program.cs`:
- Around line 140-177: The three repeated blocks wiring OAuth credentials should
be extracted into a single helper (e.g., ConfigureSocialProvider or
AddSocialProviderEnv) that takes the provider name and configuration keys, reads
builder.Configuration for the client id/secret, calls
keycloak.Keycloak.WithEnvironment for the two variables when present, and logs
the consistent warning ("⚠️ WARNING: {Provider} OAuth credentials are missing.
{Provider} Login may fail.") when either value is missing; replace the inline
Google/Facebook/Instagram blocks with three calls to this helper so the
WithEnvironment usage and warning behavior are centralized and identical.
In `@src/Web/MeAjudaAi.Web.Customer/app/globals.css`:
- Around line 34-41: Dark mode CSS variables --popover-foreground,
--card-foreground, and --accent-foreground are hardcoded to `#ededed`; replace
those hardcoded values with var(--foreground) so they mirror the light-mode
definitions and remain consistent if --foreground changes (update the dark-mode
rule that currently sets --popover-foreground, --card-foreground, and
--accent-foreground).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 716e608d-1a8f-4a74-a543-7e8c2dca0433
📒 Files selected for processing (12)
.github/workflows/pr-validation.yml.gitignoreinfrastructure/keycloak/themes/meajudaai/login/resources/css/login.cssscripts/dev.ps1src/Aspire/MeAjudaAi.AppHost/Program.cssrc/Bootstrapper/MeAjudaAi.ApiService/MigrationExtensions.cssrc/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/page.tsxsrc/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/documentos/page.tsxsrc/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/endereco/page.tsxsrc/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/page.tsxsrc/Web/MeAjudaAi.Web.Customer/app/globals.csstests/MeAjudaAi.Shared.Tests/Unit/Utilities/UserRolesTests.cs
✅ Files skipped from review due to trivial changes (1)
- .gitignore
🚧 Files skipped from review as they are similar to previous changes (1)
- src/Bootstrapper/MeAjudaAi.ApiService/MigrationExtensions.cs
src/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/endereco/page.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/endereco/page.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/page.tsx
Outdated
Show resolved
Hide resolved
…ion and implement the initial provider registration flow in the customer web application.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/page.tsx (1)
71-79:⚠️ Potential issue | 🟡 MinorStep completion checks are still slightly permissive.
At Line 71-79,
hasAddressdoes not validatestate, andhasDocumentstreats any truthyfileUrlas valid (including whitespace). This can still mark steps complete prematurely.Suggested tightening
- const hasAddress = primaryAddress && + const hasAddress = !!(primaryAddress && primaryAddress.street?.trim() && primaryAddress.street !== "-" && primaryAddress.street !== "N/A" && primaryAddress.street !== "unknown" && primaryAddress.number?.trim() && primaryAddress.number !== "-" && primaryAddress.number !== "N/A" && primaryAddress.city?.trim() && primaryAddress.city !== "-" && primaryAddress.city !== "N/A" && - primaryAddress.zipCode?.trim() && primaryAddress.zipCode !== "-" && primaryAddress.zipCode !== "N/A"; + primaryAddress.state?.trim() && primaryAddress.state !== "-" && primaryAddress.state !== "N/A" && + primaryAddress.zipCode?.trim() && primaryAddress.zipCode !== "-" && primaryAddress.zipCode !== "N/A"); - const hasDocuments = profile.documents?.length > 0 && - profile.documents.some(doc => doc.documentType && (doc.status === EDocumentStatus.Uploaded || doc.fileUrl)); + const hasDocuments = (profile.documents?.length ?? 0) > 0 && + profile.documents.some(doc => + !!doc.documentType && + (doc.status === EDocumentStatus.Uploaded || !!doc.fileUrl?.trim()) + );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/cadastro/prestador/perfil/page.tsx around lines 71 - 79, hasAddress is missing validation for primaryAddress.state and hasDocuments treats any truthy fileUrl (including whitespace) as valid; update the hasAddress check to include primaryAddress.state?.trim() and ensure state is not "-" / "N/A" / "unknown", and tighten hasDocuments by trimming doc.fileUrl (e.g., Boolean(doc.fileUrl?.trim())) when deciding validity alongside checking doc.status === EDocumentStatus.Uploaded; locate the expressions named hasAddress and hasDocuments in page.tsx and apply these stricter checks to avoid marking steps complete prematurely.
🧹 Nitpick comments (1)
scripts/dev.ps1 (1)
33-34: RedundantTrim()call.Line 33 already trims
$parts[1]into$value, so line 34's$value.Trim()is redundant.♻️ Suggested simplification
$name = $parts[0].Trim() - $value = $parts[1].Trim() - $cleanValue = $value.Trim() + $cleanValue = $parts[1].Trim()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/dev.ps1` around lines 33 - 34, The assignment unnecessarily calls Trim() twice: $parts[1] is already trimmed into $value, so change the code that sets $cleanValue (or simplify both lines) to avoid the redundant $value.Trim() call — either set $cleanValue = $value or inline the trim as $cleanValue = $parts[1].Trim(); update the symbols $parts, $value, and $cleanValue accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/cadastro/prestador/page.tsx:
- Around line 229-230: The page component contains inconsistent legal route
usages in multiple Link hrefs (some use "/termos-de-uso" and
"/politica-de-privacidade", others use "/termos" and "/privacidade"); pick a
single canonical pair (e.g., "/termos-de-uso" and "/politica-de-privacidade")
and update every Link in this component (the FormLabel/Link elements that render
the "Aceito os ..." and similar consent lines) to use those canonical hrefs
while preserving target, rel and className attributes so all user flows point to
the same legal pages.
In `@src/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.ts`:
- Around line 11-20: The hook's mutationFn currently forwards the full
RegisterProviderRequest (in use-register-provider mutationFn and the
authenticatedFetch call) but the backend derives email from the token and only
accepts optional phone; update the payload to match the backend contract by
mapping the incoming RegisterProviderRequest to a narrower request shape (e.g.,
Omit<RegisterProviderRequest, 'email'> or an explicit CreateProvider payload
containing only the allowed fields such as phone and other permitted properties)
before calling authenticatedFetch("/api/v1/providers/become"). Replace the
direct use of RegisterProviderRequest as the fetch body, update the mutationFn
signature/type accordingly, and import/reuse the shared enums/constants from
Shared.Contracts where applicable to keep Web aligned with backend/shared types.
---
Duplicate comments:
In
`@src/Web/MeAjudaAi.Web.Customer/app/`(main)/cadastro/prestador/perfil/page.tsx:
- Around line 71-79: hasAddress is missing validation for primaryAddress.state
and hasDocuments treats any truthy fileUrl (including whitespace) as valid;
update the hasAddress check to include primaryAddress.state?.trim() and ensure
state is not "-" / "N/A" / "unknown", and tighten hasDocuments by trimming
doc.fileUrl (e.g., Boolean(doc.fileUrl?.trim())) when deciding validity
alongside checking doc.status === EDocumentStatus.Uploaded; locate the
expressions named hasAddress and hasDocuments in page.tsx and apply these
stricter checks to avoid marking steps complete prematurely.
---
Nitpick comments:
In `@scripts/dev.ps1`:
- Around line 33-34: The assignment unnecessarily calls Trim() twice: $parts[1]
is already trimmed into $value, so change the code that sets $cleanValue (or
simplify both lines) to avoid the redundant $value.Trim() call — either set
$cleanValue = $value or inline the trim as $cleanValue = $parts[1].Trim();
update the symbols $parts, $value, and $cleanValue accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9958b486-d3ad-47f7-8017-639f48ca00b8
📒 Files selected for processing (9)
infrastructure/keycloak/themes/meajudaai/login/resources/css/login.cssscripts/dev.ps1src/Aspire/MeAjudaAi.AppHost/Program.cssrc/Modules/Providers/Application/Extensions.cssrc/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/page.tsxsrc/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/endereco/page.tsxsrc/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/page.tsxsrc/Web/MeAjudaAi.Web.Customer/app/globals.csssrc/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- infrastructure/keycloak/themes/meajudaai/login/resources/css/login.css
…tus and credentialing steps.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/page.tsx (1)
68-83:⚠️ Potential issue | 🟠 MajorStep completion logic doesn't account for
ActiveorSuspendedproviders.If a provider has
ActiveorSuspendedstatus but their address/document data is incomplete due to a backend issue or lazy loading,step2Completedandstep3Completedwill befalse. This causes verified providers to incorrectly see "Preencher Endereço" or "Enviar Documentos" buttons.🔧 Proposed fix
const isPendingVerification = profile.status === EProviderStatus.PendingDocumentVerification; +const isVerified = profile.status === EProviderStatus.Active; +const isSuspended = profile.status === EProviderStatus.Suspended; const primaryAddress = profile.businessProfile?.primaryAddress; const hasAddress = primaryAddress && primaryAddress.street?.trim() && primaryAddress.street !== "-" && primaryAddress.street !== "N/A" && primaryAddress.street !== "unknown" && primaryAddress.number?.trim() && primaryAddress.number !== "-" && primaryAddress.number !== "N/A" && primaryAddress.city?.trim() && primaryAddress.city !== "-" && primaryAddress.city !== "N/A" && primaryAddress.state?.trim() && primaryAddress.state !== "-" && primaryAddress.state !== "N/A" && primaryAddress.state !== "unknown" && primaryAddress.zipCode?.trim() && primaryAddress.zipCode !== "-" && primaryAddress.zipCode !== "N/A"; const hasDocuments = profile.documents?.length > 0 && profile.documents.some(doc => doc.documentType && (doc.status === EDocumentStatus.Uploaded || doc.fileUrl?.trim())); // Estados derivados para etapas concluídas com base na solicitação do usuário -const step2Completed = !!hasAddress || isPendingVerification; -const step3Completed = !!hasDocuments || isPendingVerification; +const step2Completed = !!hasAddress || isPendingVerification || isVerified || isSuspended; +const step3Completed = !!hasDocuments || isPendingVerification || isVerified || isSuspended;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/cadastro/prestador/perfil/page.tsx around lines 68 - 83, The step completion logic currently uses isPendingVerification to short-circuit step2Completed and step3Completed but misses cases where profile.status is Active or Suspended; update the check so that step2Completed and step3Completed also evaluate to true when profile.status === EProviderStatus.Active or profile.status === EProviderStatus.Suspended (you can extend isPendingVerification or create a new flag like isVerifiedOrSuspended), ensuring step2Completed and step3Completed compute as !!hasAddress || isVerifiedOrSuspended and !!hasDocuments || isVerifiedOrSuspended; adjust references to EProviderStatus, isPendingVerification (or the new flag), step2Completed and step3Completed accordingly.
🧹 Nitpick comments (1)
scripts/dev.ps1 (1)
26-43: Consider saving this script as UTF-8 with BOM.This block contains non-ASCII text, and static analysis reported
PSUseBOMForUnicodeEncodedFile; adding BOM avoids mojibake/parsing issues in older PowerShell hosts.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/dev.ps1` around lines 26 - 43, The script contains non-ASCII characters (emoji and Portuguese text) which triggers PSUseBOMForUnicodeEncodedFile; to fix, re-save the file with UTF-8 with BOM encoding so PowerShell hosts correctly recognize the encoding; locate the file that uses envFilePath and the block that reads and sets env variables (Test-Path, Get-Content, Set-Item) and re-encode the file in your editor/IDE (or use a tool/command to write UTF-8 with BOM) so the BOM is present when the script runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/roadmap.md`:
- Line 73: Update the sprint status text that currently reads "**📅 Sprint 8B em
andamento**: Fevereiro/Março de 2026" to reflect the correct state now that the
period ended on 4 March 2026 (e.g., change "em andamento" to "concluído" or
"atrasado"), update any corresponding date ranges if needed, and update the
"Última atualização" field mentioned elsewhere in the document; apply the same
correction to the other occurrences noted (the lines containing the same sprint
status/labels at the referenced locations).
In `@scripts/dev.ps1`:
- Around line 30-40: The loader currently keeps inline comments in values;
modify the parsing inside the Get-Content loop (where $parts, $value,
$cleanValue are set and before Set-Item) to strip inline comments: detect the
first # that is not inside matching quotes and truncate the value at that
position, or if simpler, trim trailing whitespace then remove " #..." only when
the # is outside quotes; then trim again and only afterwards remove surrounding
quotes (the existing StartsWith/EndsWith logic) and assign to Set-Item. Use the
existing $value/$cleanValue variables and update that block so quoted values
containing # remain intact while inline comments are dropped.
---
Duplicate comments:
In
`@src/Web/MeAjudaAi.Web.Customer/app/`(main)/cadastro/prestador/perfil/page.tsx:
- Around line 68-83: The step completion logic currently uses
isPendingVerification to short-circuit step2Completed and step3Completed but
misses cases where profile.status is Active or Suspended; update the check so
that step2Completed and step3Completed also evaluate to true when profile.status
=== EProviderStatus.Active or profile.status === EProviderStatus.Suspended (you
can extend isPendingVerification or create a new flag like
isVerifiedOrSuspended), ensuring step2Completed and step3Completed compute as
!!hasAddress || isVerifiedOrSuspended and !!hasDocuments ||
isVerifiedOrSuspended; adjust references to EProviderStatus,
isPendingVerification (or the new flag), step2Completed and step3Completed
accordingly.
---
Nitpick comments:
In `@scripts/dev.ps1`:
- Around line 26-43: The script contains non-ASCII characters (emoji and
Portuguese text) which triggers PSUseBOMForUnicodeEncodedFile; to fix, re-save
the file with UTF-8 with BOM encoding so PowerShell hosts correctly recognize
the encoding; locate the file that uses envFilePath and the block that reads and
sets env variables (Test-Path, Get-Content, Set-Item) and re-encode the file in
your editor/IDE (or use a tool/command to write UTF-8 with BOM) so the BOM is
present when the script runs.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: afc04a44-defd-4bba-a9a2-e7e73d2c3a36
📒 Files selected for processing (5)
docs/roadmap.mdscripts/dev.ps1src/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/page.tsxsrc/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/page.tsxsrc/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/Web/MeAjudaAi.Web.Customer/hooks/use-register-provider.ts
- src/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/page.tsx
…omprehensive project roadmap, and add a new development script.
There was a problem hiding this comment.
♻️ Duplicate comments (2)
docs/roadmap.md (1)
1533-1536:⚠️ Potential issue | 🟡 MinorUpdate remaining Sprint 8B statuses to avoid contradictions.
These lines still show “EM ANDAMENTO”, but other updated sections now state Sprint 8B was finalized on 4 de Março de 2026 (e.g., Line 73 and Line 5081). Since today is 5 de Março de 2026, this is stale/inconsistent.
Please normalize all Sprint 8B references to the same final status (e.g., ✅ CONCLUÍDO).
Also applies to: 2154-2155, 2196-2196
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/roadmap.md` around lines 1533 - 1536, Update all Sprint 8B status annotations that still read "EM ANDAMENTO" to the normalized final status (e.g., "✅ CONCLUÍDO"); specifically locate and replace the heading "### 🔄 Sprint 8B - Authentication & Onboarding Flow (EM ANDAMENTO)" and any other occurrences referenced (around the headings/lines noted) so they consistently reflect the concluded status across the document (also fix the other occurrences mentioned near lines 2154-2155 and 2196). Ensure the text and any nearby date ranges remain unchanged except for the status label so all Sprint 8B references match the finalized state.src/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/page.tsx (1)
70-76:⚠️ Potential issue | 🟡 MinorNormalize address fields before placeholder comparison.
hasAddresstrims for emptiness but compares raw values for placeholders. Inputs like" - "or" Unknown "can still pass and mark steps as completed.🔧 Suggested fix
- const hasAddress = primaryAddress && - primaryAddress.street?.trim() && primaryAddress.street !== "-" && primaryAddress.street !== "N/A" && primaryAddress.street !== "unknown" && - primaryAddress.number?.trim() && primaryAddress.number !== "-" && primaryAddress.number !== "N/A" && - primaryAddress.city?.trim() && primaryAddress.city !== "-" && primaryAddress.city !== "N/A" && - primaryAddress.state?.trim() && primaryAddress.state !== "-" && primaryAddress.state !== "N/A" && primaryAddress.state !== "unknown" && - primaryAddress.zipCode?.trim() && primaryAddress.zipCode !== "-" && primaryAddress.zipCode !== "N/A"; + const normalize = (value?: string | null) => value?.trim().toLowerCase() ?? ""; + const isPlaceholder = (value: string) => value === "-" || value === "n/a" || value === "unknown"; + + const hasAddress = !!primaryAddress && (() => { + const street = normalize(primaryAddress.street); + const number = normalize(primaryAddress.number); + const city = normalize(primaryAddress.city); + const state = normalize(primaryAddress.state); + const zipCode = normalize(primaryAddress.zipCode); + + return ( + !!street && !isPlaceholder(street) && + !!number && !isPlaceholder(number) && + !!city && !isPlaceholder(city) && + !!state && !isPlaceholder(state) && + !!zipCode && !isPlaceholder(zipCode) + ); + })();Also applies to: 83-84
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Customer/app/`(main)/cadastro/prestador/perfil/page.tsx around lines 70 - 76, The hasAddress check uses raw values for placeholder comparisons; normalize each address field first (trim and toLowerCase) and then compare against a set of placeholder values (e.g., "-", "n/a", "unknown", "") to determine emptiness. Update the logic around primaryAddress and hasAddress: derive normalized variables (e.g., streetNorm, numberNorm, cityNorm, stateNorm, zipCodeNorm) from primaryAddress.* using ?.trim()?.toLowerCase() and then check that each normalized value is not in the placeholder set; apply the same normalization pattern to the other similar address checks referenced (lines 83-84) so all placeholder variants with whitespace or case differences are handled consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@docs/roadmap.md`:
- Around line 1533-1536: Update all Sprint 8B status annotations that still read
"EM ANDAMENTO" to the normalized final status (e.g., "✅ CONCLUÍDO");
specifically locate and replace the heading "### 🔄 Sprint 8B - Authentication &
Onboarding Flow (EM ANDAMENTO)" and any other occurrences referenced (around the
headings/lines noted) so they consistently reflect the concluded status across
the document (also fix the other occurrences mentioned near lines 2154-2155 and
2196). Ensure the text and any nearby date ranges remain unchanged except for
the status label so all Sprint 8B references match the finalized state.
In
`@src/Web/MeAjudaAi.Web.Customer/app/`(main)/cadastro/prestador/perfil/page.tsx:
- Around line 70-76: The hasAddress check uses raw values for placeholder
comparisons; normalize each address field first (trim and toLowerCase) and then
compare against a set of placeholder values (e.g., "-", "n/a", "unknown", "") to
determine emptiness. Update the logic around primaryAddress and hasAddress:
derive normalized variables (e.g., streetNorm, numberNorm, cityNorm, stateNorm,
zipCodeNorm) from primaryAddress.* using ?.trim()?.toLowerCase() and then check
that each normalized value is not in the placeholder set; apply the same
normalization pattern to the other similar address checks referenced (lines
83-84) so all placeholder variants with whitespace or case differences are
handled consistently.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0f7e075e-b826-4a07-90d4-f53d23dbab7a
📒 Files selected for processing (3)
docs/roadmap.mdscripts/dev.ps1src/Web/MeAjudaAi.Web.Customer/app/(main)/cadastro/prestador/perfil/page.tsx
…r registration page in the customer web app.
Summary by CodeRabbit
New Features
Documentation
Localization