feat(Admin): Migrate Admin Portal from Blazor WASM to React/Next.js#178
feat(Admin): Migrate Admin Portal from Blazor WASM to React/Next.js#178
Conversation
- Create MeAjudaAi.Web.Admin-React with NX - Add Tailwind CSS v4 configuration - Add basic Next.js App Router structure
- Add Tailwind v4 CSS variables theme in global.css - Create admin layout with sidebar navigation - Add UI components: Card, Button, Badge, Input - Create dashboard with KPI cards and Recharts pie chart - Add providers page with mock data table - Add placeholder pages: documents, categories, services, allowed-cities, settings - Configure next-auth with Keycloak provider - Set up AppProviders with TanStack Query
- Generate API types from OpenAPI spec (copied from Provider app) - Create admin hooks: useProviders, useAllowedCities, useCategories, useUsers - Add type definitions with Brazilian Portuguese labels - Update providers page to use real API with loading/error states - Add local .gitignore to track OpenAPI generated files
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdição de um portal administrativo Next.js completo: configuração build/tooling, OpenAPI config, NextAuth (Keycloak) + middleware, providers (Session/Query/Theme/Toaster), primitives UI, hooks React Query para CRUD, múltiplas páginas admin (listas, CRUD, detalhe, dashboard) e documentação extensa. Changes
sequenceDiagram
participant Browser as Browser
participant AdminApp as AdminApp
participant NextAuth as NextAuth
participant Keycloak as Keycloak
participant API as API
Browser->>AdminApp: Clica "Entrar com Keycloak" (/login)
AdminApp->>NextAuth: signIn("keycloak")
NextAuth->>Keycloak: Redirecionamento OAuth
Keycloak->>NextAuth: Retorna tokens/claims
NextAuth->>Browser: Cookie/session NextAuth
Browser->>AdminApp: Requisições autenticadas (ex: /dashboard)
AdminApp->>API: useDashboardStats() -> múltiplas chamadas paginadas
API-->>AdminApp: Dados agregados (DashboardStats)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Update dashboard with real API data and interactive charts - Add Dialog and Select components using @base-ui/react - Complete Allowed Cities CRUD with create, edit, delete - Complete Categories CRUD with create, edit, delete - Complete Services CRUD with create, edit, delete - Add dashboard stats hook for provider metrics - Add services hook for TanStack Query integration - Update documents page with placeholder info
- Create dynamic route /providers/[id] for provider details - Display provider contact info, address, documents, and services - Add approve/suspend actions in detail page - Link provider names to detail view in list page
- Create custom ThemeProvider with localStorage persistence - Create ThemeToggle button component - Add theme toggle to sidebar - Update global.css with dark mode CSS variables
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (9)
src/Web/MeAjudaAi.Web.Admin-React/.swcrc (1)
6-11: Configuração de decorators pode ser desnecessária.O projeto usa React/Next.js onde decorators não são comumente utilizados. As opções
decorators: true,decoratorMetadata: trueelegacyDecorator: truepodem ser removidas se não houver uso de decorators no código.♻️ Sugestão de simplificação (se decorators não forem usados)
"parser": { "syntax": "typescript", - "decorators": true, "dynamicImport": true }, - "transform": { - "decoratorMetadata": true, - "legacyDecorator": true - },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/.swcrc` around lines 6 - 11, Remova as opções relacionadas a decorators do arquivo .swcrc se o projeto não usa decorators: retire as chaves/entries "decorators": true, "transform" -> "decoratorMetadata": true e "transform" -> "legacyDecorator": true; garanta também que nenhuma configuração ou dependência (Babel/TypeScript decorator usage) dependa desses flags antes de remover para evitar quebra; verifique usos de decorators buscando por "@" em componentes e se não encontrar, aplique a remoção.src/Web/MeAjudaAi.Web.Admin-React/index.d.ts (1)
1-6: Considere tipagem mais específica para SVGs.A declaração atual usa
any, o que desabilita verificações de tipo. Para melhor type-safety, considere uma tipagem mais específica.♻️ Tipagem mais específica para SVGs
-/* eslint-disable `@typescript-eslint/no-explicit-any` */ declare module '*.svg' { - const content: any; - export const ReactComponent: any; + import { FC, SVGProps } from 'react'; + const content: string; + export const ReactComponent: FC<SVGProps<SVGSVGElement>>; export default content; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/index.d.ts` around lines 1 - 6, A declaração do module '*.svg' usa any para `content` e `ReactComponent`; altere para tipos mais específicos: mantenha a declaração `declare module '*.svg'` mas tipar `content` como `string` (path) e `ReactComponent` como `React.FunctionComponent<React.SVGProps<SVGSVGElement>>` (ou `React.ComponentType<React.SVGProps<SVGSVGElement>>`) para preservar verificação de props SVG; importe/qualifique `React` e `React.SVGProps` na declaração para que os símbolos `ReactComponent` e `content` sejam corretamente tipados em vez de any.src/Web/MeAjudaAi.Web.Admin-React/src/app/api/hello/route.ts (1)
1-3: Parâmetrorequestnão utilizado.O parâmetro
requestnão está sendo usado na função. Considere removê-lo ou prefixá-lo com underscore para indicar que é intencionalmente ignorado.♻️ Remover parâmetro não utilizado
-export async function GET(request: Request) { +export async function GET() { return new Response('Hello, from API!'); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/api/hello/route.ts` around lines 1 - 3, The GET handler's unused parameter request in function GET should be removed or renamed to indicate it's intentionally unused; update the function signature from GET(request: Request) to either GET() if you remove it, or GET(_request: Request) / GET(_: Request) if you prefer to keep the type for future use, and ensure any callers or exports relying on the exact signature still work (refer to the GET function in route.ts).src/Web/package.json (1)
6-10: Projeto Admin-React não está incluído no arrayworkspaces.O
MeAjudaAi.Web.Admin-Reactnão está listado no arrayworkspaces, diferente deMeAjudaAi.Web.Customerque está incluído. Isso pode causar problemas de resolução de dependências se o projeto precisar de seu própriopackage.json.♻️ Adicionar projeto ao workspaces
"workspaces": [ "apps/*", "libs/*", - "MeAjudaAi.Web.Customer" + "MeAjudaAi.Web.Customer", + "MeAjudaAi.Web.Admin-React" ],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/package.json` around lines 6 - 10, O array workspaces no package.json não inclui o projeto MeAjudaAi.Web.Admin-React; abra o arquivo onde a chave "workspaces" é definida e adicione a string "MeAjudaAi.Web.Admin-React" ao array (junto a "MeAjudaAi.Web.Customer", "apps/*", "libs/*") para garantir que o workspace seja resolvido corretamente pelo gerenciador de pacotes.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/page.tsx (1)
1-1: Considere remover"use client"para esta página estática.Esta página não utiliza hooks, event handlers ou qualquer funcionalidade que exija renderização no cliente. Removê-la permitiria que o Next.js a renderize como Server Component, melhorando a performance inicial.
♻️ Sugestão de refatoração
-"use client"; - import { Settings } from "lucide-react";Nota: Isso pode exigir que os componentes
Card*também sejam compatíveis com Server Components. Verifique se eles não usam hooks internamente.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/settings/page.tsx at line 1, Remova a diretiva "use client" do topo da página page.tsx para permitir que o Next.js trate-a como Server Component; verifique os componentes usados na árvore (ex.: Card*, CardHeader, CardContent, CardFooter ou quaisquer componentes importados nesta página) e garanta que eles não utilizem hooks, event handlers ou APIs do cliente; se algum componente usar hooks, refatore-o para suportar Server Components ou extraia a lógica do cliente para um componente filho com "use client" apenas onde necessário.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/services/page.tsx (1)
7-28: Estrutura consistente com outras páginas placeholder.Mesma observação do
AllowedCitiesPage: o botão "Novo Serviço" (linha 15) não possui handler. Considere desabilitá-lo para indicar que a funcionalidade está em desenvolvimento.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/services/page.tsx around lines 7 - 28, The "Novo Serviço" Button in ServicesPage has no click handler while the feature is in development; update the Button component instance in ServicesPage to reflect a non-interactive state (e.g., add the disabled prop and appropriate accessible label/aria-disabled) so it matches the placeholder pattern used elsewhere (like AllowedCitiesPage) and visually indicates the feature is unavailable; ensure you reference the Button inside the ServicesPage function and keep the Plus icon and text but make the button non-clickable and accessible.src/Web/MeAjudaAi.Web.Admin-React/openapi-ts.config.ts (1)
4-4: Considere usar variável de ambiente sem prefixoNEXT_PUBLIC_.O arquivo de configuração OpenAPI é executado em tempo de build, não no navegador. O prefixo
NEXT_PUBLIC_expõe desnecessariamente a URL no bundle do cliente. Use uma variável sem prefixo (ex:OPENAPI_SPEC_URL) para configurações de build.♻️ Sugestão
-input: process.env.NEXT_PUBLIC_OPENAPI_SPEC_URL ?? 'http://localhost:7002/api-docs/v1/swagger.json', +input: process.env.OPENAPI_SPEC_URL ?? 'http://localhost:7002/api-docs/v1/swagger.json',🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/openapi-ts.config.ts` at line 4, A configuração do campo input em openapi-ts.config.ts está usando process.env.NEXT_PUBLIC_OPENAPI_SPEC_URL (que expõe a URL ao bundle do cliente); mude para uma variável de ambiente de build sem prefixo (por exemplo process.env.OPENAPI_SPEC_URL) e atualize qualquer documentação/README e pipeline de CI para definir OPENAPI_SPEC_URL; verifique o fallback padrão ('http://localhost:7002/api-docs/v1/swagger.json') permanece e que nenhuma referência restante a NEXT_PUBLIC_OPENAPI_SPEC_URL exista no projeto.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/layout.tsx (1)
9-11: Acoplamento de largura fixa entre layout e sidebar.O valor
ml-64está hardcoded para corresponder aow-64do Sidebar. Considere usar uma variável CSS ou constante compartilhada para evitar dessincronização futura.♻️ Sugestão para reduzir acoplamento
Crie uma constante compartilhada:
// src/components/layout/constants.ts export const SIDEBAR_WIDTH = "16rem"; // 64 * 0.25rem = 16rem export const SIDEBAR_WIDTH_CLASS = "w-64"; export const MAIN_MARGIN_CLASS = "ml-64";Ou use CSS custom properties no
global.css::root { --sidebar-width: 16rem; }E aplique via Tailwind arbitrary values:
-<main className="flex-1 ml-64">{children}</main> +<main className="flex-1 ml-[var(--sidebar-width)]">{children}</main>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/layout.tsx around lines 9 - 11, Replace the hardcoded margin class "ml-64" in layout.tsx with a shared value to avoid coupling to Sidebar's width: create a shared constant (e.g. export SIDEBAR_WIDTH_CLASS or MAIN_MARGIN_CLASS from a new constants module and import it into layout.tsx) and use that constant in place of "ml-64" when rendering <main>, or alternatively define a CSS custom property (e.g. --sidebar-width in global.css) and apply it to the main element (using Tailwind arbitrary values or inline style) so Sidebar (component Sidebar) and layout.tsx both derive their width/margin from the same source.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsx (1)
15-15: Botão sem funcionalidade pode confundir usuários.O botão "Nova Cidade" não possui
onClickhandler nem está desabilitado. Para uma página placeholder, considere desabilitar o botão ou adicionar um tooltip indicando que está em desenvolvimento.♻️ Sugestão
-<Button><Plus className="mr-2 h-4 w-4" />Nova Cidade</Button> +<Button disabled title="Em desenvolvimento"><Plus className="mr-2 h-4 w-4" />Nova Cidade</Button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx at line 15, O botão "Nova Cidade" (JSX Button element containing the Plus icon) está sem comportamento e pode confundir usuários; either wire an onClick handler on that Button to open the create-city flow (e.g., call your modal/openForm function) or explicitly disable it (add disabled prop) and surface its status with a tooltip/title like "Em desenvolvimento" using your Tooltip component or the title attribute so users understand it's not available; update the Button element accordingly (reference Button and Plus in the page component).
🤖 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.Admin-React/src/app/`(admin)/providers/page.tsx:
- Line 101: The table row key uses provider.id which is optional and can be
undefined; update the row rendering (where providers.map renders <tr
key={provider.id} ...>) to guarantee a stable key by falling back to another
unique field or the map index—for example: use a computed key like provider.id
?? provider.email ?? `provider-${index}` (reference the providers.map callback
and the <tr key={provider.id}> element) so every <tr> always has a defined,
stable key.
- Around line 118-122: The icon-only action Buttons in page.tsx (the Button
components rendering Eye, Pencil, CheckCircle, XCircle, Trash2) lack accessible
labels; update each Button to include an appropriate aria-label (e.g., "View",
"Edit", "Approve", "Reject", "Delete") that matches the action and preserve the
existing variant/size props, and optionally add a title prop for tooltip support
to improve accessibility for assistive technologies.
- Around line 38-40: The providers list is being read from data?.data but
useProviders() already returns the final array (via its select), so change the
assignment to use the returned data directly: replace the providers
initialization (currently using data?.data) with one that uses data (e.g., const
providers = data ?? []). Update the reference in page.tsx where providers is
declared to stop accessing .data on the hook result and default to an empty
array when data is undefined.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/global.css`:
- Line 3: O uso de "@theme inline" é incompatível com Tailwind CSS v3.4.3;
corrija removendo essa diretiva e ou atualize a dependência para Tailwind v4, ou
converta as definições de tema para uma forma compatível com v3: mover as
configurações de tema para tailwind.config.js sob a chave theme.extend ou
reimplementar os tokens como variáveis CSS padrão; procure pela ocorrência
"@theme inline" no ficheiro CSS e aplique uma das duas opções (upgrade do pacote
ou migração das definições para tailwind.config.js -> theme.extend / variáveis
CSS).
- Around line 20-56: Atualize a definição das variáveis `--color-*` na diretiva
`@theme` para referenciar as variáveis reativas de `:root` (por exemplo,
substituir `--color-background: `#ffffff`;` por `--color-background:
var(--background);`, `--color-foreground: var(--foreground);`, `--color-surface:
var(--surface);`, `--color-surface-raised: var(--surface-raised);`,
`--color-foreground-subtle: var(--foreground-subtle);`, `--color-card:
var(--surface);`, `--color-card-foreground: var(--foreground);` e ajustar
`--color-ring` para usar uma variável apropriada ou deixar como fallback
`var(--ring, `#395873`)`), assim as classes que usam `--color-*` (ex.:
bg-background) refletirão as mudanças aplicadas no bloco `:root` e no `@media
(prefers-color-scheme: dark)`.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsx`:
- Line 67: A expressão atual usa nullish coalescing on
session?.user?.name?.charAt(0).toUpperCase() ?? "A", which doesn't fallback when
name is an empty string; change the logic so you first check session?.user?.name
for truthiness after trimming (e.g., use session?.user?.name?.trim() in a
conditional) and only then compute charAt(0).toUpperCase(), otherwise return the
"A" fallback; update the JSX expression that references session?.user?.name,
charAt, and toUpperCase accordingly.
In
`@src/Web/MeAjudaAi.Web.Admin-React/src/components/providers/app-providers.tsx`:
- Around line 8-23: O componente AppProviders está usando <SessionProvider> sem
passar a sessão, causando fetch client-side e flicker; atualize a função
AppProviders({ children, session }: { children: React.ReactNode; session?:
Session }) para aceitar e repassar a prop session ao <SessionProvider
session={session}>; adicione uma rota NextAuth que exporte as auth options e
handler (a implementação padrão de NextAuth, por ex. export const handler =
NextAuth(authOptions)) para suportar /api/auth; por fim atualize o layout para
obter a sessão no servidor com getServerSession(authOptions) e passar essa
sessão para <AppProviders session={session}>.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-providers.ts`:
- Around line 69-76: useProvidersByType is calling the wrong generated endpoint
(apiProvidersGet3 which expects ApiProvidersGet3Data with path: { id }) and is
force-cast to hide the type mismatch; replace the apiProvidersGet3 call with
apiProvidersGet2 (the same pattern used in useProvidersByStatus) and pass the
correct query shape ({ query: { type } }) without casting to
ApiProvidersGet3Data, keeping the queryKey providerKeys.byType(type) and select:
(data) => data.data unchanged.
- Around line 40-44: A função useProviders está usando o endpoint e tipagem
errados: troque a chamada apiProvidersGet por apiProvidersGet2 dentro de
useProviders (queryFn) para usar o endpoint administrativo, e atualize a tipagem
de filters e do retorno de ApiProvidersGetData para ApiProvidersGet2Data (a
assinatura export function useProviders(...) e o uso de ApiProvidersGetData na
definição do filtro/retorno). Preserve providerKeys.list(filters) e apenas
ajuste os identificadores para apontarem ao apiProvidersGet2 e
ApiProvidersGet2Data.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/lib/auth/auth.ts`:
- Around line 16-20: Adicione os callbacks jwt e session na configuração
exportada em auth.ts para popular o token e a sessão: implemente callback jwt({
token, user, account, profile }) que, quando profile.sub existir, define
token.id = profile.sub e também copie accessToken/refreshToken/exp se
disponíveis; e implemente callback session({ session, token }) que popula
session.user.id = token.id e session.user.roles = token.realm_access?.roles ||
token.roles (mapeando conforme Keycloak), garantindo assim que session.user.id e
session.user.roles estejam presentes (use o padrão visto em
src/Web/MeAjudaAi.Web.Customer/auth.ts como referência).
- Around line 7-10: O código atual silencia erros de configuração ao usar
defaults para clientId/clientSecret/issuer e não mapeia claims para a sessão;
atualize a inicialização em auth.ts para validar e falhar cedo se
process.env.KEYCLOAK_ADMIN_CLIENT_ID, KEYCLOAK_ADMIN_CLIENT_SECRET ou
KEYCLOAK_ISSUER estiverem ausentes (lançando um Error durante bootstrap em vez
de usar valores padrão) e remova os fallbacks. Além disso, adicione callbacks
jwt e session nas opções de autenticação (ex.: nas funções/objetos relacionados
a NextAuth/NextAuthOptions ou no export default do provedor Keycloak) para
copiar os claims relevantes do token para token JWT e depois para session.user
(assegurando que atributos como roles/realm_access sejam propagados).
In `@src/Web/MeAjudaAi.Web.Admin-React/src/lib/types.ts`:
- Around line 30-37: O mapeamento de enum/local constants EProviderStatus (e
também EVerificationStatus nas linhas indicadas) está desalinhado com os valores
do backend; atualize as chaves/valores em EProviderStatus e EVerificationStatus
para refletirem exatamente o contrato do backend (por exemplo ajustar o valor de
Active de 5 para 3 conforme backend), mantendo os mesmos nomes (Pending,
BasicInfoRequired, BasicInfoSubmitted, DocumentsRequired, DocumentsSubmitted,
Active) e reveja/ajuste quaisquer labels vinculados a essas constantes usadas em
filtros/badges para garantir consistência nas funções que consomem esses
símbolos.
In `@src/Web/package.json`:
- Line 69: Remove the incorrect v3 dependency "tailwindcss": "3.4.3" from
package.json so the repo relies on the v4 ecosystem; then update Admin-React's
PostCSS setup to match the other apps by replacing the v3 plugin usage with
"@tailwindcss/postcss" in postcss.config.js and ensure its tailwind.config.js is
migrated to the v4 format (aligning with the v4 syntax used in
global.css/globals.css: `@import` "tailwindcss" and `@theme` inline). Target the
"tailwindcss" dependency entry in package.json and the Admin-React files
postcss.config.js and tailwind.config.js when making these changes.
---
Nitpick comments:
In `@src/Web/MeAjudaAi.Web.Admin-React/.swcrc`:
- Around line 6-11: Remova as opções relacionadas a decorators do arquivo .swcrc
se o projeto não usa decorators: retire as chaves/entries "decorators": true,
"transform" -> "decoratorMetadata": true e "transform" -> "legacyDecorator":
true; garanta também que nenhuma configuração ou dependência (Babel/TypeScript
decorator usage) dependa desses flags antes de remover para evitar quebra;
verifique usos de decorators buscando por "@" em componentes e se não encontrar,
aplique a remoção.
In `@src/Web/MeAjudaAi.Web.Admin-React/index.d.ts`:
- Around line 1-6: A declaração do module '*.svg' usa any para `content` e
`ReactComponent`; altere para tipos mais específicos: mantenha a declaração
`declare module '*.svg'` mas tipar `content` como `string` (path) e
`ReactComponent` como `React.FunctionComponent<React.SVGProps<SVGSVGElement>>`
(ou `React.ComponentType<React.SVGProps<SVGSVGElement>>`) para preservar
verificação de props SVG; importe/qualifique `React` e `React.SVGProps` na
declaração para que os símbolos `ReactComponent` e `content` sejam corretamente
tipados em vez de any.
In `@src/Web/MeAjudaAi.Web.Admin-React/openapi-ts.config.ts`:
- Line 4: A configuração do campo input em openapi-ts.config.ts está usando
process.env.NEXT_PUBLIC_OPENAPI_SPEC_URL (que expõe a URL ao bundle do cliente);
mude para uma variável de ambiente de build sem prefixo (por exemplo
process.env.OPENAPI_SPEC_URL) e atualize qualquer documentação/README e pipeline
de CI para definir OPENAPI_SPEC_URL; verifique o fallback padrão
('http://localhost:7002/api-docs/v1/swagger.json') permanece e que nenhuma
referência restante a NEXT_PUBLIC_OPENAPI_SPEC_URL exista no projeto.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx:
- Line 15: O botão "Nova Cidade" (JSX Button element containing the Plus icon)
está sem comportamento e pode confundir usuários; either wire an onClick handler
on that Button to open the create-city flow (e.g., call your modal/openForm
function) or explicitly disable it (add disabled prop) and surface its status
with a tooltip/title like "Em desenvolvimento" using your Tooltip component or
the title attribute so users understand it's not available; update the Button
element accordingly (reference Button and Plus in the page component).
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/layout.tsx:
- Around line 9-11: Replace the hardcoded margin class "ml-64" in layout.tsx
with a shared value to avoid coupling to Sidebar's width: create a shared
constant (e.g. export SIDEBAR_WIDTH_CLASS or MAIN_MARGIN_CLASS from a new
constants module and import it into layout.tsx) and use that constant in place
of "ml-64" when rendering <main>, or alternatively define a CSS custom property
(e.g. --sidebar-width in global.css) and apply it to the main element (using
Tailwind arbitrary values or inline style) so Sidebar (component Sidebar) and
layout.tsx both derive their width/margin from the same source.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/services/page.tsx:
- Around line 7-28: The "Novo Serviço" Button in ServicesPage has no click
handler while the feature is in development; update the Button component
instance in ServicesPage to reflect a non-interactive state (e.g., add the
disabled prop and appropriate accessible label/aria-disabled) so it matches the
placeholder pattern used elsewhere (like AllowedCitiesPage) and visually
indicates the feature is unavailable; ensure you reference the Button inside the
ServicesPage function and keep the Plus icon and text but make the button
non-clickable and accessible.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/settings/page.tsx:
- Line 1: Remova a diretiva "use client" do topo da página page.tsx para
permitir que o Next.js trate-a como Server Component; verifique os componentes
usados na árvore (ex.: Card*, CardHeader, CardContent, CardFooter ou quaisquer
componentes importados nesta página) e garanta que eles não utilizem hooks,
event handlers ou APIs do cliente; se algum componente usar hooks, refatore-o
para suportar Server Components ou extraia a lógica do cliente para um
componente filho com "use client" apenas onde necessário.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/api/hello/route.ts`:
- Around line 1-3: The GET handler's unused parameter request in function GET
should be removed or renamed to indicate it's intentionally unused; update the
function signature from GET(request: Request) to either GET() if you remove it,
or GET(_request: Request) / GET(_: Request) if you prefer to keep the type for
future use, and ensure any callers or exports relying on the exact signature
still work (refer to the GET function in route.ts).
In `@src/Web/package.json`:
- Around line 6-10: O array workspaces no package.json não inclui o projeto
MeAjudaAi.Web.Admin-React; abra o arquivo onde a chave "workspaces" é definida e
adicione a string "MeAjudaAi.Web.Admin-React" ao array (junto a
"MeAjudaAi.Web.Customer", "apps/*", "libs/*") para garantir que o workspace seja
resolvido corretamente pelo gerenciador de pacotes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3ce840a2-1168-47b6-80d4-1c750ef41dc1
⛔ Files ignored due to path filters (20)
src/Web/MeAjudaAi.Web.Admin-React/public/favicon.icois excluded by!**/*.icosrc/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/@tanstack/react-query.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/client.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/client/client.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/client/index.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/client/types.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/client/utils.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/auth.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/bodySerializer.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/params.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/pathSerializer.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/queryKeySerializer.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/serverSentEvents.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/types.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/utils.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/index.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/sdk.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/types.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/zod.gen.tsis excluded by!**/generated/**src/Web/package-lock.jsonis excluded by!**/package-lock.json,!**/package-lock.json
📒 Files selected for processing (37)
src/Web/MeAjudaAi.Web.Admin-React/.gitignoresrc/Web/MeAjudaAi.Web.Admin-React/.swcrcsrc/Web/MeAjudaAi.Web.Admin-React/index.d.tssrc/Web/MeAjudaAi.Web.Admin-React/next-env.d.tssrc/Web/MeAjudaAi.Web.Admin-React/next.config.jssrc/Web/MeAjudaAi.Web.Admin-React/openapi-ts.config.tssrc/Web/MeAjudaAi.Web.Admin-React/postcss.config.jssrc/Web/MeAjudaAi.Web.Admin-React/project.jsonsrc/Web/MeAjudaAi.Web.Admin-React/public/.gitkeepsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/dashboard/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/documents/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/layout.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/services/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/api/hello/route.tssrc/Web/MeAjudaAi.Web.Admin-React/src/app/global.csssrc/Web/MeAjudaAi.Web.Admin-React/src/app/layout.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/providers/app-providers.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/badge.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/button.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/card.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/input.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/index.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-allowed-cities.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-providers.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-users.tssrc/Web/MeAjudaAi.Web.Admin-React/src/lib/auth/auth.tssrc/Web/MeAjudaAi.Web.Admin-React/src/lib/types.tssrc/Web/MeAjudaAi.Web.Admin-React/tailwind.config.jssrc/Web/MeAjudaAi.Web.Admin-React/tsconfig.jsonsrc/Web/package.json
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsx
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsx
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsx
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsx
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-providers.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 13
♻️ Duplicate comments (1)
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsx (1)
39-42:⚠️ Potential issue | 🔴 CriticalConsuma o retorno de
useProviders()diretamente.Aqui o hook já devolve a lista final; acessar
.datafazproviderscair em[]quando a query resolve e a tabela tende a ficar sempre vazia.🐛 Correção sugerida
- const providers = data?.data ?? []; + const providers = data ?? [];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx around lines 39 - 42, O hook useProviders já retorna a lista final; ao acessar data?.data a variável providers fica vazia quando a query resolve. Altere a atribuição de providers para usar diretamente o retorno de useProviders (substituir data?.data por data ?? [] ou renomear a propriedade retornada para providers) e mantenha os nomes useProviders, data, isLoading e error para não afetar o restante do componente.
🧹 Nitpick comments (4)
src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts (1)
39-39: Eviteasem chamadas do SDK quando o objeto já atende ao contrato.Asserções forçadas (
as ApiCategoriesGet2Data/as ApiCategoriesDeleteData) podem mascarar incompatibilidades futuras de tipo sem necessidade real aqui.♻️ Refatoração sugerida
- queryFn: () => apiCategoriesGet2({ path: { id } } as ApiCategoriesGet2Data), + queryFn: () => apiCategoriesGet2({ path: { id } }), ... - mutationFn: (id: string) => - apiCategoriesDelete({ path: { id } } as ApiCategoriesDeleteData), + mutationFn: (id: string) => apiCategoriesDelete({ path: { id } }),Also applies to: 78-78
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts` at line 39, Remova as asserções de tipo forçadas nas chamadas do SDK (por exemplo em apiCategoriesGet2 e na chamada de exclusão mencionada) e passe objetos que já correspondam ao contrato requerido; em particular atualize a chamada queryFn: () => apiCategoriesGet2({ path: { id } }) e a chamada de delete para fornecer o objeto com forma correta em vez de usar "as ApiCategoriesGet2Data" / "as ApiCategoriesDeleteData", ajustando nomes de propriedades se necessário para atender ao tipo esperado pelo SDK (verifique as assinaturas de apiCategoriesGet2 e apiCategoriesDelete para garantir conformidade).src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/theme-toggle.tsx (1)
11-17: Adicionararia-pressedmelhora a semântica de toggle para acessibilidade.O botão já tem
aria-label, mas por representar um estado binário, vale expor também o estado atual (pressionado) para leitores de tela.♿ Ajuste sugerido
<Button variant="ghost" size="icon" onClick={toggleTheme} className="h-9 w-9" aria-label={`Switch to ${theme === "light" ? "dark" : "light"} mode`} + aria-pressed={theme === "dark"} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/theme-toggle.tsx` around lines 11 - 17, Add aria-pressed to the theme toggle Button to expose the binary state to assistive tech: in the ThemeToggle component, on the Button element that calls toggleTheme and uses the theme variable, set aria-pressed={theme === "dark"} (or the appropriate boolean expression reflecting the current "pressed" state) so the button communicates its current state to screen readers while keeping the existing aria-label and event handler.src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.ts (1)
40-54: Considere usar constantes ou enums para os valores de status e tipo.Os valores numéricos (0-4 para
verificationStatus, 1-4 paratype) são "magic numbers" que dificultam a manutenção. Se os valores da API mudarem, será necessário atualizar em múltiplos lugares.♻️ Sugestão de refatoração com constantes
// Extrair para arquivo de constantes compartilhado const VerificationStatus = { Pending: 0, UnderReview: 1, Approved: 2, Rejected: 3, Suspended: 4, } as const; const ProviderType = { Individual: 1, Company: 2, Cooperative: 3, Freelancer: 4, } as const; // Uso no switch switch (p.verificationStatus) { case VerificationStatus.Pending: stats.pending++; break; case VerificationStatus.UnderReview: stats.underReview++; break; // ... }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.ts` around lines 40 - 54, Troque os "magic numbers" usados em providers.forEach (p.verificationStatus e p.type) por constantes/enums compartilhadas: crie/importe VerificationStatus (Pending, UnderReview, Approved, Rejected, Suspended) e ProviderType (Individual, Company, Cooperative, Freelancer) e substitua os casos dos switch em use-dashboard.ts para usar esses símbolos em vez dos números, garantindo também que testes/tipos/places que referenciam stats.* (stats.pending, stats.underReview, stats.approved, stats.rejected, stats.suspended, stats.individual, stats.company, stats.cooperative, stats.freelancer) continuem corretos após a refatoração.src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-services.ts (1)
73-81: Limpe também o cache de detalhe na exclusão.Após a exclusão, apenas a lista é invalidada; o cache
serviceKeys.detail(id)permanecerá até expirar pelostaleTimeconfigurado. A mesma lacuna existe nos demais hooks de delete do projeto.Ajuste recomendado
export function useDeleteService() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => apiServicesDelete({ path: { id } } as ApiServicesDeleteData), - onSuccess: () => { + onSuccess: (_, id) => { + queryClient.removeQueries({ queryKey: serviceKeys.detail(id) }); queryClient.invalidateQueries({ queryKey: serviceKeys.lists() }); }, }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-services.ts` around lines 73 - 81, Ao excluir um serviço o hook useDeleteService apenas invalida a lista; ajuste para também limpar o cache de detalhes chamando queryClient.invalidateQueries({ queryKey: serviceKeys.detail(id) }) (ou passar o id correto) dentro de onSuccess; localize a função useDeleteService e o callback onSuccess e adicione a invalidação do detalhe usando o id recebido pela mutationFn; aplique mesmo padrão aos demais hooks de delete do projeto (procure por nomes semelhantes como useDeleteX) para garantir que serviceKeys.detail(...) não permaneça stale.
🤖 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.Admin-React/src/app/`(admin)/dashboard/page.tsx:
- Around line 26-39: The dashboard cards use counts from useDashboardStats
(consumed as stats?.approved, stats?.pending, stats?.individual, etc.) but the
hook currently processes only the first PagedResponse page; update
useDashboardStats to iterate over PagedResponse pages (using
pageNumber/pageSize/totalPages or totalRecords) and aggregate all provider
records before computing totals so verification counts (approved, underReview,
pending, rejected, suspended) and type counts (individual, company, freelancer,
cooperative) returned by the hook reflect all pages; ensure the hook still
returns the same stats shape so page.tsx (verificationData/typeData) can use
stats?.<field> unchanged.
- Around line 41-42: As variáveis approvedPercentage e rejectedPercentage estão
retornando tipos inconsistentes (string quando stats.total existe por causa de
toFixed(0), number quando não), então altere a lógica para sempre produzir um
number; por exemplo, compute a porcentagem como ((stats.approved / stats.total)
* 100) e aplique Math.round (ou Number(...toFixed(0))) para garantir um number
fallback 0 quando stats for nulo/total for 0; atualize ambas variáveis
(approvedPercentage e rejectedPercentage) usando esta abordagem e mantenha a
verificação defensiva em stats/total.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/[id]/page.tsx:
- Around line 50-58: As mutações em handleApprove e handleReject podem rejeitar
e atualmente lançam exceção sem tratamento; envolva
activateMutation.mutateAsync(id) e deactivateMutation.mutateAsync(id) em
try/catch, só chame setIsApproveOpen(false) / setIsRejectOpen(false) no caminho
de sucesso, e no catch mostre feedback ao usuário (ex.: chamar um
toast/notification ou atualizar estado de erro) e trate/logue o erro para evitar
unhandled rejection no console; localize as funções handleApprove e handleReject
e as chamadas activateMutation.mutateAsync / deactivateMutation.mutateAsync para
aplicar essa alteração.
- Around line 179-189: The current rendering simplifies any
doc.verificationStatus !== 2 to a single "warning"/"Pendente"; update the
conditional to explicitly map known statuses to proper Badge variants and labels
instead of a binary check: inspect provider.documents and for each doc use a
small switch/lookup on doc.verificationStatus to return the correct Badge
variant and text (e.g., 2 => "success"/"Verificado", add explicit cases for
rejected/recused and expired with appropriate variants like
"destructive"/"error" or "secondary" and labels like "Recusado"/"Expirado", and
a default case for unknown statuses -> "warning"/"Pendente"); change the code
around doc.verificationStatus and the Badge component to use that mapping and
handle undefined/null safely.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx:
- Around line 51-56: The "Novo Prestador" CTA is currently inert; update the JSX
around the Button (the element rendering <Button><Plus className="mr-2 h-4 w-4"
/>Novo Prestador</Button> in page.tsx) to either hide it behind a feature flag
or mark it disabled until the modal/navigation handler is implemented: add the
disabled prop to the Button (and optionally the aria-disabled attribute and a
reduced opacity class) or wrap the Button in a conditional so it is not
rendered, ensuring the UI cannot present a clickable-but-no-op control; keep the
Plus icon and label but make the button visually and functionally disabled until
you wire the onClick handler (or enable feature flag).
- Around line 126-129: Os quatro botões de ação (Button com ícones Pencil,
CheckCircle, XCircle, Trash2) estão sem onClick/navegação; remova-os ou
desabilite-os até implementar o fluxo, ou mantenha apenas o botão "Visualizar".
Se optar por implementar, adicione handlers nomeados claramente (por exemplo
handleEdit, handleApprove, handleSuspend, handleDelete) e associe-os aos
respectivos Button onClick; para navegação use o router (p.ex. useRouter().push)
ou abra modais conforme o comportamento esperado. Garanta também que os ícones e
Buttons referenciados (Pencil, CheckCircle, XCircle, Trash2, Button) sejam
atualizados para refletir o estado disabled quando o fluxo não existir.
- Around line 63-68: The search Input lacks an accessible label; update the
Input (the element with placeholder "Buscar por nome ou email..." and props
value={search} onChange={(e) => setSearch(e.target.value)}) to include an
accessible label by either adding an aria-label prop (e.g., aria-label="Buscar
por nome ou email") or by rendering a <label> tied to the input via id/htmlFor
(give the Input an id and add a visible or visually-hidden label) so screen
readers receive proper context.
In
`@src/Web/MeAjudaAi.Web.Admin-React/src/components/providers/theme-provider.tsx`:
- Around line 21-24: O valor lido de localStorage em stored não deve ser
assumido como válido apenas pelo cast; implemente uma whitelist/validação (por
exemplo uma função isValidTheme or array ALLOWED_THEMES) e só chame
setThemeState(stored) e document.documentElement.classList.toggle("dark", stored
=== "dark") quando stored passar a validação; caso contrário, ignore o valor ou
defina o tema padrão. Referencie a variável stored, a função setThemeState e a
chamada document.documentElement.classList.toggle dentro de theme-provider.tsx
ao aplicar essa verificação.
- Around line 41-43: O problema é que quando mounted é false o componente
retorna apenas {children}, fazendo os consumidores (ex.: ThemeToggle que usa
useTheme()) serem renderizados fora do ThemeContext.Provider e dispararem erro;
corrija retornando os children dentro de <ThemeContext.Provider> mesmo no
pré-mount, fornecendo um valor seguro com a mesma forma do contexto (ex.: theme,
setTheme no-op, systemTheme/resolvedTheme) ou reutilizando os estados locais
(theme, setTheme) já definidos no ThemeProvider para evitar chamadas a
useTheme() fora do provider; localize o componente ThemeProvider e as variáveis
mounted, children, theme/setTheme e ajuste o branch if (!mounted) para envolver
children com ThemeContext.Provider com um valor fallback válido.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/dialog.tsx`:
- Around line 109-113: O componente DialogClose declara a prop onClick mas nunca
a utiliza; altere a implementação de DialogClose para propagar a callback
onClick para o botão padrão retornado (Button) quando children não for
fornecido, ou alternativamente remova a prop onClick da assinatura se preferir
não suportá‑la; especificamente, passe onClick para o elemento Button dentro do
bloco que renderiza <Button variant="outline">Cancelar</Button> (mantendo
DialogPrimitive.Close como pai) para que quem usar DialogClose possa executar
limpeza ao cancelar.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts`:
- Around line 66-69: When updating or deleting a category in useUpdateCategory
and useDeleteCategory, also invalidate the services cache and the deleted
category detail to avoid UI inconsistency: in useUpdateCategory’s onSuccess add
queryClient.invalidateQueries({ queryKey: serviceKeys.lists() }) (and any
serviceKeys.detail(...) if you rely on per-service detail keys), and in
useDeleteCategory’s onSuccess invalidate categoryKeys.detail(id) for the deleted
id plus serviceKeys.lists(); update the onSuccess handlers in those hooks
(referencing useUpdateCategory, useDeleteCategory, categoryKeys and serviceKeys)
to call queryClient.invalidateQueries for the services and the category detail
accordingly.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.ts`:
- Around line 23-28: No lugar de apiProvidersGet na queryFn de use-dashboard.ts,
troque para apiProvidersGet2 e ajuste o parsing do response para usar a
estrutura paginada retornada por esse endpoint (p. ex. extrair a lista em
response.data.data.items ou results conforme o contrato de apiProvidersGet2)
para calcular DashboardStats.total e demais métricas; garanta também que o tipo
do parâmetro/chamada corresponda a ApiProvidersGet2Data e que a lógica que
computa verificationStatus/type itere sobre os itens corretos da páginação. Além
disso, substitua quaisquer números mágicos usados nos switchs nessa mesma função
por constantes nomeadas (ex.: VERIFICATION_STATUS_APPROVED, USER_TYPE_PROVIDER,
etc.) e use essas constantes nas cláusulas switch/case para melhorar
legibilidade e manutenção.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-services.ts`:
- Around line 28-33: The useServices hook calls the wrong endpoint when
categoryId is provided and masks types with a cast; update useServices so its
queryFn chooses the correct API: if categoryId is present call apiCategoryGet
(or the provided function that maps to /services/category/{categoryId}) with the
categoryId as the path parameter, otherwise call apiServicesGet with no
categoryId query; remove the incorrect cast to ApiServicesGetData and ensure
select still returns data.data, and keep the queryKey usage serviceKeys.list({
categoryId }) unchanged so caching keys reflect the branch.
---
Duplicate comments:
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx:
- Around line 39-42: O hook useProviders já retorna a lista final; ao acessar
data?.data a variável providers fica vazia quando a query resolve. Altere a
atribuição de providers para usar diretamente o retorno de useProviders
(substituir data?.data por data ?? [] ou renomear a propriedade retornada para
providers) e mantenha os nomes useProviders, data, isLoading e error para não
afetar o restante do componente.
---
Nitpick comments:
In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/theme-toggle.tsx`:
- Around line 11-17: Add aria-pressed to the theme toggle Button to expose the
binary state to assistive tech: in the ThemeToggle component, on the Button
element that calls toggleTheme and uses the theme variable, set
aria-pressed={theme === "dark"} (or the appropriate boolean expression
reflecting the current "pressed" state) so the button communicates its current
state to screen readers while keeping the existing aria-label and event handler.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts`:
- Line 39: Remova as asserções de tipo forçadas nas chamadas do SDK (por exemplo
em apiCategoriesGet2 e na chamada de exclusão mencionada) e passe objetos que já
correspondam ao contrato requerido; em particular atualize a chamada queryFn: ()
=> apiCategoriesGet2({ path: { id } }) e a chamada de delete para fornecer o
objeto com forma correta em vez de usar "as ApiCategoriesGet2Data" / "as
ApiCategoriesDeleteData", ajustando nomes de propriedades se necessário para
atender ao tipo esperado pelo SDK (verifique as assinaturas de apiCategoriesGet2
e apiCategoriesDelete para garantir conformidade).
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.ts`:
- Around line 40-54: Troque os "magic numbers" usados em providers.forEach
(p.verificationStatus e p.type) por constantes/enums compartilhadas:
crie/importe VerificationStatus (Pending, UnderReview, Approved, Rejected,
Suspended) e ProviderType (Individual, Company, Cooperative, Freelancer) e
substitua os casos dos switch em use-dashboard.ts para usar esses símbolos em
vez dos números, garantindo também que testes/tipos/places que referenciam
stats.* (stats.pending, stats.underReview, stats.approved, stats.rejected,
stats.suspended, stats.individual, stats.company, stats.cooperative,
stats.freelancer) continuem corretos após a refatoração.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-services.ts`:
- Around line 73-81: Ao excluir um serviço o hook useDeleteService apenas
invalida a lista; ajuste para também limpar o cache de detalhes chamando
queryClient.invalidateQueries({ queryKey: serviceKeys.detail(id) }) (ou passar o
id correto) dentro de onSuccess; localize a função useDeleteService e o callback
onSuccess e adicione a invalidação do detalhe usando o id recebido pela
mutationFn; aplique mesmo padrão aos demais hooks de delete do projeto (procure
por nomes semelhantes como useDeleteX) para garantir que serviceKeys.detail(...)
não permaneça stale.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: cb6344cb-359d-40f7-90c7-e18b5065a712
📒 Files selected for processing (17)
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/dashboard/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/documents/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/[id]/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/services/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/providers/app-providers.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/providers/theme-provider.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/dialog.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/select.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/theme-toggle.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/index.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-services.ts
✅ Files skipped from review due to trivial changes (1)
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/services/page.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
- src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/index.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/documents/page.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/components/providers/app-providers.tsx
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/[id]/page.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/[id]/page.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsx
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/components/providers/theme-provider.tsx
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.ts
Outdated
Show resolved
Hide resolved
…pagination - Add middleware for route protection with next-auth - Add Keycloak login page with error handling - Add Zod validation to all CRUD forms - Add Sonner toast notifications for CRUD operations - Add pagination to providers, services, cities, and documents pages - Complete settings page with tabs (profile, notifications, security, appearance) - Complete documents page with document status tracking - Update auth.ts with JWT callbacks for role extraction
There was a problem hiding this comment.
Actionable comments posted: 13
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (8)
docs/customer-frontend-documentation.md-2634-2634 (1)
2634-2634:⚠️ Potential issue | 🟡 MinorAjustar concordância no texto de assets.
Em Line 2634, “Ilustração homem” fica melhor como “Ilustração do homem” (ou “Ilustração: homem”) para manter consistência textual do documento.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/customer-frontend-documentation.md` at line 2634, Corrija a concordância do texto do asset identificado como `illustration-man.png` que atualmente está "Ilustração homem (CTA Prestadores)"; altere para "Ilustração do homem (CTA Prestadores)" ou "Ilustração: homem (CTA Prestadores)" para manter consistência textual no documento (procure pela string `Ilustração homem` e substitua).docs/customer-frontend-documentation.md-305-320 (1)
305-320:⚠️ Potential issue | 🟡 MinorCorrigir referência de variável no grid de prestadores.
No trecho de Line 305 a Line 320, o
mapiteragridItems, mas oServiceCardusaprovider.*(não definido nesse escopo). Esse snippet está inconsistente e quebra em copy/paste.💡 Ajuste sugerido
- {gridItems.map((item, index) => { + {gridItems.map((item, index) => { if (item.type === 'ad') { return <AdCard key={`ad-${index}`} />; } + const provider = item.provider; return ( <ServiceCard key={provider.id}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/customer-frontend-documentation.md` around lines 305 - 320, The gridItems.map callback incorrectly references provider.* (provider.id, provider.name, etc.) which doesn't exist in that scope; update the map callback to use the actual item variable (e.g., item or rename to provider) and pass the correct properties into ServiceCard (use item.id, item.name, item.avatarUrl, item.description, item.services, item.averageRating, item.reviewCount or rename the map parameter to provider and keep the existing props), and ensure services mapping uses the correct nested field (item.services.map(...)) and fallback/default values remain intact.docs/customer-frontend-documentation.md-1172-1174 (1)
1172-1174:⚠️ Potential issue | 🟡 MinorRemove
typeda importação deSlotna linha 1173.Em Line 1173,
Slotfoi importado comimport type, porém é renderizado como componente JSX em Line 1244. Isso gera erro de compilação TypeScript quando o snippet é reaproveitado, pois importaçõestypesão removidas em tempo de execução e não podem ser usadas como valores.Altere para:
import { Slot } from "@radix-ui/react-slot";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/customer-frontend-documentation.md` around lines 1172 - 1174, A importação de Slot está marcada como tipo ("import type { Slot }") mas o símbolo Slot é usado como componente JSX (renderizado na linha onde o snippet usa <Slot ...>), o que causa erro porque importações de tipo são removidas em tempo de execução; altere a importação para um import de valor (import { Slot } from "@radix-ui/react-slot") para que Slot exista em runtime e continue sendo usado por seu componente JSX (procure pelas referências a Slot e pela import atual junto ao import de LabelPrimitive).docs/provider-frontend-documentation.md-853-853 (1)
853-853:⚠️ Potential issue | 🟡 MinorCorrigir handler inexistente no modal de desativação.
Na Line 853, o botão usa
onClick={confirmDeactivation}, mas esse handler não está definido no snippet. Isso quebra a consistência do exemplo e induz cópia de código inválido.💡 Ajuste sugerido
- <Button variant="destructive" onClick={confirmDeactivation} disabled={deactivateMutation.isPending}> + <Button + variant="destructive" + onClick={() => deactivateMutation.mutate()} + disabled={deactivateMutation.isPending} + >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-frontend-documentation.md` at line 853, O snippet do modal de desativação referencia um handler inexistente: onClick={confirmDeactivation} (componente Button) enquanto confirmDeactivation não está definido, causando código inválido; corrija definindo a função confirmDeactivation (por exemplo const confirmDeactivation = () => { /* chamar deactivateMutation.mutate() ou lógica de confirmação */ }) ou altere a referência para o handler existente (por exemplo onClick={handleDeactivate} ou similar) e mantenha a condição disabled={deactivateMutation.isPending} intacta para evitar estado inválido.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsx-156-163 (1)
156-163:⚠️ Potential issue | 🟡 MinorBotões de paginação sem
aria-label.Os botões de navegação (anterior/próximo) usam apenas ícones. Adicione
aria-labelpara acessibilidade.♿ Correção sugerida
<Button variant="outline" size="icon" + aria-label="Página anterior" disabled={currentPage === 1} onClick={() => setCurrentPage((p) => p - 1)} > <ChevronLeft className="h-4 w-4" /> </Button><Button variant="outline" size="icon" + aria-label="Próxima página" disabled={currentPage === totalPages} onClick={() => setCurrentPage((p) => p + 1)} > <ChevronRight className="h-4 w-4" /> </Button>Also applies to: 186-193
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx around lines 156 - 163, The pagination icon-only buttons lack accessible labels; update the two Button components that render ChevronLeft and ChevronRight (the ones using onClick={() => setCurrentPage((p) => p - 1)} and the corresponding next-page setCurrentPage((p) => p + 1)) to include descriptive aria-label attributes (e.g., aria-label="Previous page" and aria-label="Next page" or localized equivalents) so screen readers can identify their purpose; ensure both the left-chevron Button and the right-chevron Button instances referenced in this file receive the aria-labels and keep existing props (disabled, variant, size, onClick) unchanged.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsx-200-202 (1)
200-202:⚠️ Potential issue | 🟡 MinorMensagem de "vazio" pode ser exibida junto com a tabela vazia.
A condição na linha 200 (
filteredProviders.length === 0) está fora do bloco que renderiza a tabela (linhas 98-198). Isso significa que quando não há resultados, a tabela (comtbodyvazio) é renderizada E a mensagem de vazio também aparece. Mova a verificação para dentro do bloco ou evite renderizar a tabela quando não houver dados.🐛 Correção sugerida
{!isLoading && !error && ( <> - <div className="overflow-x-auto"> + {filteredProviders.length === 0 ? ( + <div className="p-8 text-center text-muted-foreground">Nenhum prestador encontrado</div> + ) : ( + <> + <div className="overflow-x-auto"> - <table className="w-full"> + <table className="w-full"> ... - </table> - </div> + </table> + </div> {totalPages > 1 && ( ... )} + </> + )} </> )} - - {!isLoading && !error && filteredProviders.length === 0 && ( - <div className="p-8 text-center text-muted-foreground">Nenhum prestador encontrado</div> - )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx around lines 200 - 202, The empty-state message is being shown alongside an empty table because the check (filteredProviders.length === 0) is outside the JSX block that renders the table; update the rendering logic so the table is not rendered when there are no providers — either move the filteredProviders.length === 0 condition inside the same conditional that renders the table JSX (the block that iterates filteredProviders to build <tbody>) or add an explicit guard before rendering that table (check isLoading, error, and filteredProviders.length > 0) so that when filteredProviders is empty only the "Nenhum prestador encontrado" message is rendered.src/Web/MeAjudaAi.Web.Admin-React/src/app/login/page.tsx-14-17 (1)
14-17:⚠️ Potential issue | 🟡 MinorFalta tratamento de erro em
handleSignIn.Se
signIn()falhar (erro de rede, timeout, etc.),isLoadingpermanecetrueindefinidamente e o botão fica desabilitado, impedindo nova tentativa.🛡️ Correção proposta
const handleSignIn = async () => { setIsLoading(true); - await signIn("keycloak", { callbackUrl: "/dashboard" }); + try { + await signIn("keycloak", { callbackUrl: "/dashboard" }); + } catch (error) { + console.error("Erro ao iniciar login:", error); + setIsLoading(false); + } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/login/page.tsx` around lines 14 - 17, handleSignIn currently sets isLoading true then awaits signIn without error handling, so failures leave isLoading true and the button disabled; wrap the await signIn("keycloak", { callbackUrl: "/dashboard" }) call in a try/catch/finally inside handleSignIn, in the catch handle/log the error and surface feedback (e.g., show a toast or set an error state), and always call setIsLoading(false) in finally so the button is re-enabled; reference the handleSignIn function, the signIn call and setIsLoading to locate where to add the try/catch/finally and error reporting.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/documents/page.tsx-46-48 (1)
46-48:⚠️ Potential issue | 🟡 MinorFaça o clamp de
currentPagecontratotalPages.
handleSearchreseta a paginação, mas um refetch/invalidation que reduzafilteredProviderspode deixarcurrentPagefora do intervalo válido. Nesse estado a tabela fica vazia mesmo com resultados, o texto “Mostrando …” pode sair invertido e os botões anterior/próxima entram em estado incoerente.Also applies to: 182-225
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/documents/page.tsx around lines 46 - 48, Clamp currentPage to the valid range before computing startIndex/paginatedProviders: compute totalPages = Math.max(1, Math.ceil(filteredProviders.length / ITEMS_PER_PAGE)), then set currentPage = Math.min(Math.max(currentPage, 1), totalPages) (or update state via setCurrentPage) so startIndex and paginatedProviders use the clamped value; apply the same fix where pagination is computed (the other paginatedProviders / startIndex block and anywhere handleSearch resets pagination) to keep the "Mostrando…" text and prev/next buttons consistent after refetches that shrink filteredProviders.
🧹 Nitpick comments (3)
docs/customer-frontend-documentation.md (1)
1118-1120: Evitar valores mágicos para status de verificação na documentação.No snippet de Line 1118 a Line 1120, a regra usa
2 | "approved" | "APPROVED". Recomendo documentar o uso de enum/constante compartilhada (ex.: Shared.Contracts) para manter frontend e backend alinhados e evitar drift semântico.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 `@docs/customer-frontend-documentation.md` around lines 1118 - 1120, Replace the magic-checks in VerifiedBadge (the isVerified calculation that compares status to 2 | "approved" | "APPROVED") with a single source-of-truth enum/constant from the shared contract (e.g., import Shared.Contracts.VerificationStatus or equivalent) and use those named members to test the status; if the shared type has both numeric and string variants, normalize the incoming status (or map both variants) using a small helper like isVerifiedStatus(status) that uses Shared.Contracts.VerificationStatus values, then update VerifiedBadge to call that helper rather than hard-coded literals so frontend and backend share the same enum.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsx (1)
20-28: Considere tratar o status 5 (Correção de Dados Necessária) explicitamente.O status
5cai nodefault("secondary"), mas semanticamente representa uma ação pendente do prestador. Pode fazer sentido usar"warning"para destacar visualmente.♻️ Sugestão
const getVerificationBadgeVariant = (status?: VerificationStatus) => { switch (status) { case 2: return "success" as const; case 0: return "warning" as const; + case 5: return "warning" as const; case 3: case 4: return "destructive" as const; default: return "secondary" as const; } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx around lines 20 - 28, The getVerificationBadgeVariant function currently maps VerificationStatus values but leaves status 5 to the default "secondary"; update the switch in getVerificationBadgeVariant to handle case 5 explicitly and return "warning" (so status 5 yields a warning badge instead of secondary), keeping the existing cases for 0,2,3,4 and the default for any other unknown statuses; ensure the function signature (VerificationStatus) is unchanged.src/Web/MeAjudaAi.Web.Admin-React/src/app/login/page.tsx (1)
30-40: Considere simplificar a lógica de mensagens de erro.O código atual funciona, mas múltiplas condições separadas podem ser simplificadas com um objeto de mapeamento, melhorando legibilidade e manutenção.
♻️ Refatoração sugerida
+const errorMessages: Record<string, string> = { + OAuthSignin: "Erro ao iniciar autenticação. Tente novamente.", + OAuthCallback: "Erro no processo de autenticação.", + OAuthAccountNotLinked: "Conta não vinculada.", + CredentialsSignin: "Credenciais inválidas.", +}; {error && ( <div className="flex items-center gap-2 rounded-lg bg-destructive/10 p-3 text-sm text-destructive"> <AlertCircle className="h-4 w-4" /> - {error === "OAuthSignin" && "Erro ao iniciar autenticação. Tente novamente."} - {error === "OAuthCallback" && "Erro no processo de autenticação."} - {error === "OAuthAccountNotLinked" && "Conta não vinculada."} - {error === "CredentialsSignin" && "Credenciais inválidas."} - {!["OAuthSignin", "OAuthCallback", "OAuthAccountNotLinked", "CredentialsSignin"].includes(error) && - "Erro de autenticação. Tente novamente."} + {errorMessages[error] ?? "Erro de autenticação. Tente novamente."} </div> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/login/page.tsx` around lines 30 - 40, Replace the multiple conditional checks in the login page JSX that render error messages for the error variable with a single mapping lookup: create an object (e.g., errorMessages) that maps keys "OAuthSignin", "OAuthCallback", "OAuthAccountNotLinked", "CredentialsSignin" to their respective messages and then render errorMessages[error] || "Erro de autenticação. Tente novamente." inside the existing div (where AlertCircle and the error checks currently live) so the logic in page.tsx is simplified and easier to maintain.
🤖 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/provider-frontend-documentation.md`:
- Line 902: A importação de useEffect está incorreta: atualmente useEffect é
importado de "react-hook-form" junto com useForm e useFieldArray; corrija a
declaração de import para importar useForm e useFieldArray de "react-hook-form"
e importar useEffect de "react" (mantenha os símbolos useEffect, useForm e
useFieldArray para localizar a linha a ajustar).
- Line 1641: A importação de Review de 'lucide-react' é inválida e quebra o
build; substitua o uso/import de Review pela(s) alternativa(s) suportada(s) como
Star ou UserStar: atualize a declaração de import (por exemplo: importar Star
e/ou UserStar de 'lucide-react') e troque todas as ocorrências de Review no
código pelo ícone escolhido (ou, se for ícone de comentário, use MessageSquare
conforme apropriado), garantindo que os nomes (Review → Star/UserStar)
correspondam aos componentes exportados.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx:
- Around line 79-81: Clamp currentPage whenever totalPages is recomputed so it
never exceeds the new total (and is at least 1): after computing totalPages from
filteredCities.length and ITEMS_PER_PAGE, update the pagination state to set
currentPage = min(currentPage, max(1, totalPages)) so paginatedCities is never
empty due to an out-of-range page; apply the same clamp in the duplicate
pagination logic around the other block (lines handling the second pagination
instance).
- Around line 188-193: O campo de busca e os botões que usam apenas ícones estão
sem nomes acessíveis; atualize o componente Input (onde value={search} e
onChange={(e) => handleSearch(e.target.value)}) para incluir um atributo
acessível (ex.: aria-label="Buscar cidade ou estado") e, para todos os
IconButtons/Buttons de ação de linha e paginação (os controles referenciados nas
mesmas áreas que a pesquisa), adicione aria-labels descritivos ou texto oculto
visualmente (visually-hidden) que descreva a ação (ex.: "Editar cidade",
"Excluir", "Próxima página"), garantindo que cada controle sem texto visível
tenha um identificador acessível único e significativo.
- Around line 109-119: O envio atual está aninhando o payload (usando { body:
... } para POST e { data: ... } para PUT), o que quebra o contrato definido em
use-allowed-cities.ts que já monta o body; ajuste as chamadas de mutation em
handleSubmitCreate (e a outra em 127-140, ex: handleSubmitEdit) para passar o
objeto esperado diretamente para createMutation.mutateAsync /
updateMutation.mutateAsync (não envolver em { body: ... } ou { data: ... }),
mantendo as propriedades city, state, country, serviceRadiusKm e isActive na
raiz do objeto enviado para corresponder ao shape produzido por
use-allowed-cities.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/documents/page.tsx:
- Around line 68-73: The search Input relies only on a placeholder and icon-only
pagination buttons lack accessible names; update the Input (component named
Input used with value={search} and onChange={handleSearch}) to include an
accessible label (either add an associated <label> or add
aria-label/aria-labelledby) and add aria-label attributes to the icon-only
pagination controls (the previous/next button components) so screen readers
receive clear names like "Search providers", "Previous page" and "Next page";
apply the same change to the other Input and icon-only buttons referenced
elsewhere in this file (the other Input instances and pagination controls).
- Around line 78-123: The KPI cards currently always render using the fallback
providers = [], causing zero counts before data loads or when errors occur;
update the component to conditionally render the KPI Card block: only render the
cards when the data fetch is successful (e.g., isSuccess or providers is a
populated array) and use the same loading/error flags the table uses (e.g.,
isLoading/isError/isSuccess) to instead render skeletons or an error state; keep
the counts logic that uses providers.filter(...) and
getProviderDocumentStatus(p) inside the success branch and replace the top Card
block with a skeleton/error placeholder when isLoading or isError so KPIs stay
consistent with the table state.
- Around line 168-172: The Link currently wraps a Button causing nested
interactive elements; update the Button component (Button) to support an asChild
prop using Radix's Slot: add an optional boolean prop asChild, set the rendered
element to Slot when asChild true (otherwise default to "button"), forwardRef
and all props, and preserve existing variant/size class logic; then change the
usage in page.tsx to render <Button asChild ... aria-label={`Ver documentos de
${provider.name ?? "prestador"}`}> with the <Link href=...> as the child (or
alternatively replace the Button with a styled Link or apply the Button's
ghost/icon classes directly to the Link). Ensure focus, keyboard behavior, and
accessibility attributes remain intact.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/services/page.tsx:
- Line 3: Ensure currentPage is clamped whenever the available pages change:
whenever you update totalPages (or the data list length) check if currentPage >
totalPages and if so call setCurrentPage(Math.max(1, totalPages)); update this
logic where totalPages is computed/updated (reference totalPages and
setTotalPages) or right after any mutation that can shrink the list, and also
add a useEffect that watches totalPages and calls setCurrentPage(Math.max(1,
totalPages)) to keep the UI from showing an empty page (use currentPage and
setCurrentPage identifiers).
- Around line 220-256: The icon-only Buttons (the ones calling
handleOpenEdit(service), handleOpenDelete(service), and the pagination Buttons
using ChevronLeft/ChevronRight and numeric page buttons) lack accessible names;
add descriptive aria-label attributes (e.g., aria-label={`Editar
${service.name}`} for the Pencil button, aria-label={`Excluir ${service.name}`}
for the Trash2 button, aria-label="Página anterior" / "Página seguinte" for the
chevrons, and aria-label={`Ir para a página ${pageNum}`} for each page button)
or include visually-hidden text inside the Button to provide screen-reader text
while keeping the icon-only appearance; update the Button JSX where Pencil,
Trash2, ChevronLeft, ChevronRight and page Buttons are rendered to include these
labels.
- Around line 106-115: No handleSubmitCreate está sendo passado um payload com
um wrapper extra ("body") para createMutation; remova esse wrapper e passe o
objeto de serviço diretamente para createMutation.mutateAsync para que
useCreateService/apiServicesPost receba o shape esperado. Localize
handleSubmitCreate e substituir a chamada que envia { body: { ... } } por uma
chamada que envie apenas o objeto com name, description, categoryId e isActive,
alinhando com useCreateService e apiServicesPost.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/login/page.tsx`:
- Around line 9-12: O componente LoginPage está chamando useSearchParams()
diretamente (const searchParams = useSearchParams()) em um componente client sem
Suspense, o que causa erro/noise no Next.js 15; refatore extraindo a lógica que
usa useSearchParams() para um componente filho assíncrono envolvido por
React.Suspense (ou mova esse filho para um componente separado que importe/use
useSearchParams), atualize LoginPage para renderizar <Suspense
fallback={...}><ChildUsingSearchParams /></Suspense>, e mantenha isLoading/state
e demais handlers no componente pai conforme necessário; procure símbolos
LoginPage e useSearchParams para aplicar a mudança.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/middleware.ts`:
- Line 1: O arquivo exporta `auth` como `middleware` (export { auth as
middleware }) que é compatível com next-auth v5, não com next-auth v4.24.13;
para corrigir substitua essa exportação pelo padrão do v4: importe e re-exporte
o `default` de "next-auth/middleware" ou envolva sua lógica com `withAuth` de
"next-auth/middleware" (referencie a função `withAuth` e forneça `pages.signIn`
se necessário) e adicione o `config.matcher` apropriado para excluir rotas como
api, _next e /login; ajuste/importe onde necessário seu módulo `@/lib/auth/auth`
para usar esse novo padrão.
---
Minor comments:
In `@docs/customer-frontend-documentation.md`:
- Line 2634: Corrija a concordância do texto do asset identificado como
`illustration-man.png` que atualmente está "Ilustração homem (CTA Prestadores)";
altere para "Ilustração do homem (CTA Prestadores)" ou "Ilustração: homem (CTA
Prestadores)" para manter consistência textual no documento (procure pela string
`Ilustração homem` e substitua).
- Around line 305-320: The gridItems.map callback incorrectly references
provider.* (provider.id, provider.name, etc.) which doesn't exist in that scope;
update the map callback to use the actual item variable (e.g., item or rename to
provider) and pass the correct properties into ServiceCard (use item.id,
item.name, item.avatarUrl, item.description, item.services, item.averageRating,
item.reviewCount or rename the map parameter to provider and keep the existing
props), and ensure services mapping uses the correct nested field
(item.services.map(...)) and fallback/default values remain intact.
- Around line 1172-1174: A importação de Slot está marcada como tipo ("import
type { Slot }") mas o símbolo Slot é usado como componente JSX (renderizado na
linha onde o snippet usa <Slot ...>), o que causa erro porque importações de
tipo são removidas em tempo de execução; altere a importação para um import de
valor (import { Slot } from "@radix-ui/react-slot") para que Slot exista em
runtime e continue sendo usado por seu componente JSX (procure pelas referências
a Slot e pela import atual junto ao import de LabelPrimitive).
In `@docs/provider-frontend-documentation.md`:
- Line 853: O snippet do modal de desativação referencia um handler inexistente:
onClick={confirmDeactivation} (componente Button) enquanto confirmDeactivation
não está definido, causando código inválido; corrija definindo a função
confirmDeactivation (por exemplo const confirmDeactivation = () => { /* chamar
deactivateMutation.mutate() ou lógica de confirmação */ }) ou altere a
referência para o handler existente (por exemplo onClick={handleDeactivate} ou
similar) e mantenha a condição disabled={deactivateMutation.isPending} intacta
para evitar estado inválido.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/documents/page.tsx:
- Around line 46-48: Clamp currentPage to the valid range before computing
startIndex/paginatedProviders: compute totalPages = Math.max(1,
Math.ceil(filteredProviders.length / ITEMS_PER_PAGE)), then set currentPage =
Math.min(Math.max(currentPage, 1), totalPages) (or update state via
setCurrentPage) so startIndex and paginatedProviders use the clamped value;
apply the same fix where pagination is computed (the other paginatedProviders /
startIndex block and anywhere handleSearch resets pagination) to keep the
"Mostrando…" text and prev/next buttons consistent after refetches that shrink
filteredProviders.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx:
- Around line 156-163: The pagination icon-only buttons lack accessible labels;
update the two Button components that render ChevronLeft and ChevronRight (the
ones using onClick={() => setCurrentPage((p) => p - 1)} and the corresponding
next-page setCurrentPage((p) => p + 1)) to include descriptive aria-label
attributes (e.g., aria-label="Previous page" and aria-label="Next page" or
localized equivalents) so screen readers can identify their purpose; ensure both
the left-chevron Button and the right-chevron Button instances referenced in
this file receive the aria-labels and keep existing props (disabled, variant,
size, onClick) unchanged.
- Around line 200-202: The empty-state message is being shown alongside an empty
table because the check (filteredProviders.length === 0) is outside the JSX
block that renders the table; update the rendering logic so the table is not
rendered when there are no providers — either move the filteredProviders.length
=== 0 condition inside the same conditional that renders the table JSX (the
block that iterates filteredProviders to build <tbody>) or add an explicit guard
before rendering that table (check isLoading, error, and
filteredProviders.length > 0) so that when filteredProviders is empty only the
"Nenhum prestador encontrado" message is rendered.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/login/page.tsx`:
- Around line 14-17: handleSignIn currently sets isLoading true then awaits
signIn without error handling, so failures leave isLoading true and the button
disabled; wrap the await signIn("keycloak", { callbackUrl: "/dashboard" }) call
in a try/catch/finally inside handleSignIn, in the catch handle/log the error
and surface feedback (e.g., show a toast or set an error state), and always call
setIsLoading(false) in finally so the button is re-enabled; reference the
handleSignIn function, the signIn call and setIsLoading to locate where to add
the try/catch/finally and error reporting.
---
Nitpick comments:
In `@docs/customer-frontend-documentation.md`:
- Around line 1118-1120: Replace the magic-checks in VerifiedBadge (the
isVerified calculation that compares status to 2 | "approved" | "APPROVED") with
a single source-of-truth enum/constant from the shared contract (e.g., import
Shared.Contracts.VerificationStatus or equivalent) and use those named members
to test the status; if the shared type has both numeric and string variants,
normalize the incoming status (or map both variants) using a small helper like
isVerifiedStatus(status) that uses Shared.Contracts.VerificationStatus values,
then update VerifiedBadge to call that helper rather than hard-coded literals so
frontend and backend share the same enum.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx:
- Around line 20-28: The getVerificationBadgeVariant function currently maps
VerificationStatus values but leaves status 5 to the default "secondary"; update
the switch in getVerificationBadgeVariant to handle case 5 explicitly and return
"warning" (so status 5 yields a warning badge instead of secondary), keeping the
existing cases for 0,2,3,4 and the default for any other unknown statuses;
ensure the function signature (VerificationStatus) is unchanged.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/login/page.tsx`:
- Around line 30-40: Replace the multiple conditional checks in the login page
JSX that render error messages for the error variable with a single mapping
lookup: create an object (e.g., errorMessages) that maps keys "OAuthSignin",
"OAuthCallback", "OAuthAccountNotLinked", "CredentialsSignin" to their
respective messages and then render errorMessages[error] || "Erro de
autenticação. Tente novamente." inside the existing div (where AlertCircle and
the error checks currently live) so the logic in page.tsx is simplified and
easier to maintain.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9adc5696-ff1e-4276-9d45-edce105eb790
📒 Files selected for processing (13)
docs/customer-frontend-documentation.mddocs/provider-frontend-documentation.mdsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/documents/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/services/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/login/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/providers/app-providers.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/providers/toast-provider.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/lib/auth/auth.tssrc/Web/MeAjudaAi.Web.Admin-React/src/middleware.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- src/Web/MeAjudaAi.Web.Admin-React/src/components/providers/app-providers.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/page.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/lib/auth/auth.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsx
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsx
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/services/page.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
docs/admin-frontend-documentation.md (1)
505-509: Consolide a linha duplicada deAlertCirclena tabela de ícones.
AlertCircleaparece duas vezes; vale unificar em uma única linha com múltiplos usos para evitar ambiguidade na referência.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/admin-frontend-documentation.md` around lines 505 - 509, Consolide a linha duplicada do ícone AlertCircle na tabela: remova uma das entradas duplicadas e mantenha uma única linha para `AlertCircle` listando ambos os usos (por exemplo “Error messages, Rejected KPI”); garanta que as outras entradas (`TrendingUp`, `Clock`) permaneçam inalteradas e que a tabela mantenha o mesmo formato Markdown.
🤖 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/admin-frontend-documentation.md`:
- Around line 614-665: The fenced code block in
docs/admin-frontend-documentation.md (the file tree starting at the block around
line 614) lacks a language tag, triggering MD040; update that block to use a
language identifier like "text" or "bash" (e.g., change ``` to ```text) so the
markdown linter warning is resolved while preserving the file tree contents.
---
Nitpick comments:
In `@docs/admin-frontend-documentation.md`:
- Around line 505-509: Consolide a linha duplicada do ícone AlertCircle na
tabela: remova uma das entradas duplicadas e mantenha uma única linha para
`AlertCircle` listando ambos os usos (por exemplo “Error messages, Rejected
KPI”); garanta que as outras entradas (`TrendingUp`, `Clock`) permaneçam
inalteradas e que a tabela mantenha o mesmo formato Markdown.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 66b12f4e-6100-4e6c-b637-7619c3be791c
📒 Files selected for processing (1)
docs/admin-frontend-documentation.md
…ing code quality, build, and test steps for frontend and backend.
There was a problem hiding this comment.
Actionable comments posted: 13
♻️ Duplicate comments (6)
docs/provider-frontend-documentation.md (2)
1641-1652:⚠️ Potential issue | 🟠 MajorImport de ícone
Reviewé inválido nolucide-react(já apontado antes).No Line 1641,
Reviewnão é export conhecido e pode quebrar build. Troque por ícone existente (MessageSquare,Star,UserStar, etc.).💡 Ajuste sugerido
-import { Review, Star } from "lucide-react"; +import { MessageSquare, Star } from "lucide-react"; @@ - <Review className="h-4 w-4" /> + <MessageSquare className="h-4 w-4" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-frontend-documentation.md` around lines 1641 - 1652, The import and usage of the Review icon in the ProfileReviews component is invalid (lucide-react doesn't export Review); update the import statement to use a valid exported icon (for example MessageSquare or Star) and replace the JSX <Review ... /> usage inside the ProfileReviews function accordingly so the component imports and renders a valid lucide-react icon (adjust the imported symbol and the JSX tag name in ProfileReviews).
902-902:⚠️ Potential issue | 🟠 MajorImport de
useEffectestá no pacote errado (já apontado antes).No Line 902,
useEffectdeve vir dereact, não dereact-hook-form. Isso quebra tipagem/build em TS strict.💡 Ajuste sugerido
- import { useForm, useFieldArray, useEffect } from "react-hook-form"; + import { useEffect } from "react"; + import { useForm, useFieldArray } from "react-hook-form";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-frontend-documentation.md` at line 902, The import statement incorrectly pulls useEffect from "react-hook-form"; remove useEffect from the import list in the useForm/useFieldArray import and instead import useEffect from "react" (either by adding a separate import { useEffect } from "react" or merging it into an existing React import). Update the import that declares useForm and useFieldArray (the import containing useForm and useFieldArray) to no longer include useEffect so TypeScript strict typing/builds succeed.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/[id]/page.tsx (2)
51-59:⚠️ Potential issue | 🟠 MajorNão feche o diálogo no caminho de erro.
O
catchagora mostra toast, mas ofinallyainda fecha o modal mesmo quando a mutação falha. Isso tira o contexto do usuário e obriga a reabrir o fluxo para tentar novamente.💡 Ajuste sugerido
const handleApprove = async () => { try { await activateMutation.mutateAsync(id); toast.success("Prestador aprovado com sucesso"); + setIsApproveOpen(false); } catch { toast.error("Erro ao aprovar prestador"); - } finally { - setIsApproveOpen(false); } }; const handleReject = async () => { try { await deactivateMutation.mutateAsync(id); toast.success("Prestador suspenso com sucesso"); + setIsRejectOpen(false); } catch { toast.error("Erro ao suspender prestador"); - } finally { - setIsRejectOpen(false); } };Also applies to: 62-70
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/[id]/page.tsx around lines 51 - 59, The dialog is closed in the finally block even on mutation failure; update handleApprove so setIsApproveOpen(false) is called only on success (inside the try after await activateMutation.mutateAsync(id)) and remove it from the finally block; do the same for the reject flow (e.g., handleReject / deactivateMutation.mutateAsync or similar) so dialogs are closed only when the mutation succeeds and remain open on errors to keep user context.
200-202:⚠️ Potential issue | 🟠 MajorNão reduza o status do documento a “verificado” ou “pendente”.
A página já tem
getVerificationBadgeVarianteverificationStatusLabels, mas os documentos continuam escondendo qualquer estado diferente de2. Isso mascara casos como recusado/expirado e pode levar a decisão errada no admin.💡 Ajuste sugerido
- <Badge variant={doc.verificationStatus === 2 ? "success" : "warning"}> - {doc.verificationStatus === 2 ? "Verificado" : "Pendente"} + <Badge variant={getVerificationBadgeVariant(doc.verificationStatus as VerificationStatus)}> + {verificationStatusLabels[doc.verificationStatus as keyof typeof verificationStatusLabels] ?? "-"} </Badge>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/[id]/page.tsx around lines 200 - 202, O trecho que reduz todos os estados a "Verificado"/"Pendente" usando doc.verificationStatus === 2 deve ser substituído pelo uso das helpers já existentes: chame getVerificationBadgeVariant(doc.verificationStatus) para determinar o variant do <Badge> e use verificationStatusLabels[doc.verificationStatus] (ou a função/const equivalente) como o texto do Badge, garantindo que estados como recusado/expirado sejam exibidos corretamente; remova a lógica hardcoded em favor dessas helpers para <Badge> e mantenha doc.verificationStatus como fonte única do estado.src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-services.ts (1)
28-33:⚠️ Potential issue | 🔴 Critical
useServices(categoryId)ainda consulta a rota errada.Quando
categoryIdé informado, o hook continua chamando a listagem geral e empurrando o filtro viaany. Esse ramo pode devolver serviços fora da categoria e ainda mantém o contrato tipado mascarado.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-services.ts` around lines 28 - 33, The hook useServices currently always calls the general list endpoint and masks types with casts; when categoryId is provided you must call the category-specific API rather than pushing the filter into an any-cast. Update the queryFn in useServices so that: if categoryId is present it calls the category-specific function (e.g., apiServicesGetByCategory or the proper API that accepts a categoryId) with a typed parameter for categoryId, otherwise call the general list API; keep the queryKey as serviceKeys.list({ categoryId }) and remove the unsafe any casts so responses remain correctly typed and filtered by category.src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts (1)
74-83:⚠️ Potential issue | 🟠 Major
useDeleteCategorynão invalida cache de serviços nem o detail da categoria deletada.O
useUpdateCategoryagora invalida["services"](linha 69), masuseDeleteCategorynão faz o mesmo. Além disso, o detail da categoria deletada permanece em cache, podendo causar inconsistências se a categoria for acessada por ID posteriormente.🔧 Ajuste sugerido
export function useDeleteCategory() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: any) => apiCategoriesDelete(data.path ? data : { path: { id: data } } as any), - onSuccess: () => { + onSuccess: (_, variables: any) => { + const id = variables?.path?.id ?? variables; + queryClient.invalidateQueries({ queryKey: categoryKeys.detail(id) }); queryClient.invalidateQueries({ queryKey: categoryKeys.lists() }); + queryClient.invalidateQueries({ queryKey: ["services"] }); }, }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts` around lines 74 - 83, Update useDeleteCategory to invalidate both the services list and the deleted category's detail cache after a successful mutation: after calling apiCategoriesDelete in useDeleteCategory, call queryClient.invalidateQueries for the services cache (["services"]) and also compute the deleted category id from the mutation input (use data.path.id when data.path exists or treat data as the id) and call queryClient.invalidateQueries with categoryKeys.detail(id); keep the existing invalidate of categoryKeys.lists() as well so all three caches are refreshed.
🧹 Nitpick comments (8)
src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/select.tsx (1)
6-16: Considere expor props adicionais do Radix UI para maior flexibilidade.O componente não encaminha props comumente usadas como
disabled,nameedefaultValuepara oSelectPrimitive.Root. Isso limita casos de uso como integração com formulários e desabilitação condicional.♻️ Sugestão para adicionar props comuns
export interface SelectProps { value?: string; + defaultValue?: string; onValueChange?: (value: string) => void; children: React.ReactNode; placeholder?: string; className?: string; + disabled?: boolean; + name?: string; } -export function Select({ value, onValueChange, children, placeholder, className = "" }: SelectProps) { +export function Select({ value, defaultValue, onValueChange, children, placeholder, className = "", disabled, name }: SelectProps) { return ( - <SelectPrimitive.Root value={value} onValueChange={onValueChange}> + <SelectPrimitive.Root value={value} defaultValue={defaultValue} onValueChange={onValueChange} disabled={disabled} name={name}>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/select.tsx` around lines 6 - 16, O componente Select e a interface SelectProps não expõem/encaminham props úteis do Radix (por exemplo disabled, name, defaultValue) para permitir uso em formulários e controle de estado; adicione essas propriedades ao SelectProps (disabled?: boolean, name?: string, defaultValue?: string) e passe-as para <SelectPrimitive.Root> (ou aceite um rest props: ...props e espalhe em SelectPrimitive.Root) garantindo que onValueChange e value permaneçam intactos; atualize a assinatura da função Select para desestruturar/encaminhar essas props (referencie SelectProps e a função Select) sem alterar a API existente dos handlers.docs/provider-frontend-documentation.md (1)
289-307: Centralize os labels de status/tipo em contrato compartilhado.Os
switchdegetVerificationLabelegetTypeLabelcodificam números mágicos no frontend, o que tende a desalinhar com backend ao evoluir enums. Prefira mapear a partir de enums/constantes compartilhadas.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 `@docs/provider-frontend-documentation.md` around lines 289 - 307, The getVerificationLabel and getTypeLabel functions hard-code numeric "magic" values in the frontend; move these mappings to a shared contract (e.g., Shared.Contracts enums or constants) and import them instead of using literal numbers and strings. Replace the switch logic in getVerificationLabel and getTypeLabel with lookups keyed by the shared enum/constant (preserving label and color shapes for verification) so the frontend reads from the authoritative Shared.Contracts definitions and falls back to safe defaults if an unknown value is encountered.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsx (4)
4-4: Importação não utilizada:Settings.O ícone
Settingsé importado mas nunca utilizado no componente.♻️ Correção sugerida
-import { Settings, User, Bell, Shield, Palette, Save, Loader2 } from "lucide-react"; +import { User, Bell, Shield, Palette, Save, Loader2 } 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.Admin-React/src/app/`(admin)/settings/settings-client.tsx at line 4, Remove the unused Settings import from the icon import list in settings-client.tsx: update the import statement that currently lists Settings, User, Bell, Shield, Palette, Save, Loader2 (from "lucide-react") by deleting the Settings symbol so only the actually used icons remain.
199-203: Dois ícones exibidos simultaneamente durante o salvamento.Quando
isSavingétrue, tanto oLoader2quanto oSavesão renderizados, resultando em dois ícones antes do texto. Geralmente, mostra-se um ou outro.✨ Correção sugerida
<Button onClick={handleSave} disabled={isSaving}> - {isSaving && <Loader2 className="mr-2 h-4 w-4 animate-spin" />} - <Save className="mr-2 h-4 w-4" /> + {isSaving ? ( + <Loader2 className="mr-2 h-4 w-4 animate-spin" /> + ) : ( + <Save className="mr-2 h-4 w-4" /> + )} Salvar Alterações </Button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/settings/settings-client.tsx around lines 199 - 203, The button currently renders both Loader2 and Save icons when isSaving is true; update the JSX in settings-client.tsx (the Button inside the component that calls handleSave) to render Loader2 only when isSaving and render Save only when !isSaving (i.e., show one icon at a time). Locate the Button that uses isSaving and the Loader2/Save components and change the conditional rendering so Save is suppressed while the spinner is shown; keep the existing classNames and disabled={isSaving} logic intact.
41-58: Melhorar acessibilidade da navegação por abas.A navegação por tabs funciona corretamente, mas poderia se beneficiar de atributos ARIA para melhor suporte a leitores de tela. Considere adicionar
role="tablist"no container erole="tab"+aria-selectednos botões.♿ Sugestão de acessibilidade
- <nav className="space-y-1"> + <nav className="space-y-1" role="tablist" aria-label="Configurações"> {tabs.map((tab) => { const Icon = tab.icon; return ( <button key={tab.id} + role="tab" + aria-selected={activeTab === tab.id} + aria-controls={`panel-${tab.id}`} onClick={() => setActiveTab(tab.id)}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/settings/settings-client.tsx around lines 41 - 58, The tab navigation lacks ARIA roles and selected state: add role="tablist" to the nav element and for each mapped button (inside tabs.map) set role="tab", add aria-selected={activeTab === tab.id}, and give each button a stable id (e.g., `tab-${tab.id}`) so it can be referenced; also ensure each tab panel uses aria-labelledby pointing to that id (match with your panel rendering) and keep setActiveTab and activeTab logic unchanged.
123-160: Tab de segurança com mesmos problemas de acessibilidade.Os mesmos problemas de associação label/input identificados anteriormente se aplicam aqui. Além disso, quando a funcionalidade real for implementada, considere adicionar validação client-side para confirmar que "Nova senha" e "Confirmar nova senha" coincidem antes de permitir o envio.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/settings/settings-client.tsx around lines 123 - 160, The "Alterar senha" inputs lack proper label associations and client-side validation; add unique id attributes to the three Input components (e.g., currentPasswordId, newPasswordId, confirmPasswordId) and set each corresponding label's htmlFor to match so screen readers associate labels with inputs, ensure Inputs also have name attributes and appropriate type/password props, wrap them in a form with an onSubmit handler (e.g., handlePasswordChange) and implement client-side validation inside that handler to check newPassword === confirmPassword before submitting, and surface validation errors via an accessible element (aria-live region or descriptive text tied with aria-describedby) so users receive clear feedback.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsx (1)
71-77: Considere substituiranypor tipos mais específicos.O uso extensivo de
any(linhas 71, 74, 226) reduz a segurança de tipos e pode mascarar erros em tempo de compilação. SeAllowedCityDtorepresenta a estrutura retornada pela API, o cast pode ser removido ou ajustado.♻️ Sugestão de refatoração
- const cities: any[] = (citiesResponse as any)?.value ?? (citiesResponse as any) ?? []; + const cities: AllowedCityDto[] = (citiesResponse as { value?: AllowedCityDto[] })?.value ?? (citiesResponse as AllowedCityDto[]) ?? []; - const filteredCities = cities.filter( - (c: any) => + const filteredCities = cities.filter( + (c) => (c.cityName?.toLowerCase() ?? "").includes(search.toLowerCase()) || (c.stateSigla?.toLowerCase() ?? "").includes(search.toLowerCase()) );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx around lines 71 - 77, Substitua os usos de any por um tipo específico (por exemplo AllowedCityDto) para recuperar segurança de tipos: declare citiesResponse com o tipo apropriado vindo da API e troque const cities: any[] e o cast (citiesResponse as any) por const cities: AllowedCityDto[] = (citiesResponse as AllowedCityDto[]) ?? []; atualize também filteredCities para inferir AllowedCityDto (ou tipar como AllowedCityDto[]) e remova casts desnecessários em cityName/stateSigla; garanta que AllowedCityDto contenha os campos cityName e stateSigla para que as chamadas como c.cityName?.toLowerCase() compile corretamente.src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts (1)
28-43: Considere tipagem mais estrita ao invés deany.O uso de
anyemqueryFn,select, emutationFnenfraquece a verificação de tipos. Como os tiposApiCategoriesGetData, etc., já estão importados, é possível aproveitar a tipagem gerada pela OpenAPI.♻️ Exemplo de refatoração parcial
export function useCategories() { return useQuery({ queryKey: categoryKeys.lists(), - queryFn: () => apiCategoriesGet() as any, - select: (data: any) => data.data ?? data, + queryFn: () => apiCategoriesGet(), + select: (data) => ('data' in data ? data.data : data), }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts` around lines 28 - 43, Substitua os usos de any em useCategories e useCategoryById por tipos gerados da OpenAPI: declare o tipo de retorno de queryFn como ApiCategoriesGetData (ou ApiCategoryGetData equivalente) ao chamar apiCategoriesGet / apiCategoriesGet2, passe esse tipo como generics para useQuery (useQuery<ApiCategoriesGetData, Error, ApiCategoriesGetData>), e tipar corretamente o parâmetro do select (ex.: select: (data: ApiCategoriesGetData) => data.data ?? data); aplique o mesmo padrão a qualquer mutationFn equivalente para manter verificação estrita e evitar any.
🤖 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/provider-frontend-documentation.md`:
- Around line 795-800: The destructive mutation (deactivateMutation) is calling
the endpoint with providerData?.data?.data?.id which can be undefined; update
the mutationFn to validate the id before performing the fetch (reject/throw or
return early) and ensure UI triggers (buttons/menus) are disabled when
providerData or the id is missing; apply the same guard for the other similar
mutation around lines 805-810 so neither `/providers/undefined/...` nor
`/users/undefined` requests are ever issued (refer to deactivateMutation and the
other mutation that reads providerData?.data?.data?.id).
- Line 806: The confirmation message text contains a typo: change the toast
error string in the delete confirmation check (the conditional that tests
deleteConfirmation !== "EXCLUIR") from "Digite EXACTAMENTE EXCLUIR para
confirmar" to the correct pt-BR spelling "Digite EXATAMENTE EXCLUIR para
confirmar" so users see the proper wording; update the string used in that
conditional's toast.error call accordingly.
- Around line 1294-1304: The file input overlay is absolutely positioned but its
parent div (the dropzone) isn't positioned, so the input won't reliably cover
the dropzone; update the dropzone container (the div that uses
handleDrag/handleDrop and currently sets className via cn(...)) to include a
positioned context (e.g., add "relative" to its class list) so the absolute
input with onChange={handleChange} and z-index properly overlays the dropzone;
keep the input's absolute, inset-0, z-10, h-full and w-full classes as-is to
ensure full coverage.
- Line 1286: A função removeFile apenas limpa o estado local (setFile(null)) mas
não atualiza o form do RHF; atualize removeFile para também chamar os helpers do
react-hook-form (por exemplo setValue('FIELD_NAME', null),
clearErrors('FIELD_NAME') e opcionalmente trigger('FIELD_NAME')) para
sincronizar o formulário — substitua FIELD_NAME pelo nome do campo usado no
formulário e garanta que removeFile importe/tenha acesso a
setValue/clearErrors/trigger do useForm.
- Around line 712-718: The mutationFn currently PUTs the file to uploadUrl
without validating the fetch response, causing false-success paths; update the
mutationFn to capture the fetch response (from fetch(uploadUrl...)), check
response.ok (and/or response.status), and if not ok throw or return an error so
the caller won't proceed; keep the existing setUploadProgress and return {
documentId, fileName } only after a successful response, and preserve error
handling around apiUploadPost and uploadUrl extraction.
- Around line 1167-1169: The CardTitle forwardRef types declare
HTMLParagraphElement but render an <h3>, causing a type mismatch; update the
generic type parameters for React.forwardRef used in CardTitle to use
HTMLHeadingElement (or use React.ComponentPropsWithoutRef<'h3'> for props) so
the ref and props types match the rendered <h3>, and ensure the function
signature and destructured props remain unchanged.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx:
- Around line 118-142: The issue is broken routes and nested interactive
elements when provider.id is missing; inside the paginatedProviders.map loop,
only render Link elements that use `/providers/${provider.id}` when provider.id
is truthy (for both the name link and the action link wrapping the Button);
otherwise render non-link fallbacks (plain text for provider.name and a
non-anchor interactive element like a disabled Button or an IconButton without a
surrounding Link) so you never produce `/providers/undefined` or put a <Button>
inside an <a>; update the mapping around the Link hrefs and the action that
renders <Button variant="ghost" size="icon"> accordingly to conditionally render
based on provider.id.
In
`@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/settings/settings-client.tsx:
- Around line 72-79: Os labels não estão associados aos inputs; atualize o JSX
onde aparecem <label className="text-sm font-medium">Nome</label> e <label
className="text-sm font-medium">Email</label> para usar htmlFor e dê ids
correspondentes aos componentes Input (por exemplo id="name" e id="email" ou ids
únicos gerados) no arquivo que contém o componente SettingsClient (ou
função/const que renderiza esses elementos); isso envolve adicionar
htmlFor="name" ao label "Nome" e id="name" ao <Input ... />, e htmlFor="email"
ao label "Email" e id="email" ao <Input ... />, garantindo que o tipo/email
permaneça inalterado.
- Around line 17-22: A função handleSave apenas simula um delay e não persiste
os valores do formulário; substitua os inputs não controlados (que usam
defaultValue) por estado controlado (useState) ou refs, faça o gather dos
valores dentro de handleSave (referenciando os nomes dos campos usados no
componente) e envie-os para a camada de persistência (chamar a API/updateConfig
ou executar a função de callback recebida via props) enquanto mantém
setIsSaving(true/false) e toast.success; se ainda for placeholder, ao menos
adicione um TODO claramente marcado em handleSave explicando que a persistência
precisa ser implementada.
- Around line 98-118: The three checkbox inputs in the settings-client JSX (the
input elements with attributes like type="checkbox" defaultChecked
className="h-5 w-5") are not associated with their descriptive text, harming
accessibility; fix by adding proper label associations—either wrap each input in
a <label> that includes the descriptive <p> content or give each input a unique
id and add a corresponding <label htmlFor="that-id"> around the descriptive text
(update the inputs for "Novos cadastros de prestadores", "Solicitações de
verificação pendentes", and "Relatórios diários" accordingly) so clicking the
text toggles the checkbox and screen readers announce the label.
- Around line 169-185: Add a controlled theme state and handlers: create a
useState (e.g., const [theme, setTheme] = useState<'light'|'dark'|'system'>(()
=> /* read localStorage or prefers-color-scheme */)) and a useEffect to apply
the theme to document.documentElement (toggle the 'dark' class for 'dark',
respect prefers-color-scheme for 'system'), and persist changes to localStorage
under a key like 'theme'; update the three buttons (Claro/Escuro/Sistema) to
call setTheme('light'|'dark'|'system') onClick and make their className
conditional (show border-primary when theme === 'light' for Claro, theme ===
'dark' for Escuro, theme === 'system' for Sistema); optionally add a matchMedia
listener inside the effect to handle system changes when theme === 'system'.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-allowed-cities.ts`:
- Around line 62-67: O fallback da mutation está pegando payload em data.data,
descartando chamadas que passam { id, body }; ao invés disso, ajustar a função
em useAllowedCities para que a mutationFn use apiAllowedCitiesPut(data.path ?
data : { path: { id: data.id }, body: data.body ?? data }) e aplicar o mesmo
conserto no trecho que usa apiAllowedCitiesPatch (outra mutation similar nas
linhas indicadas) para garantir que body seja retirado de data.body quando
presente, caso contrário usar o próprio data.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-providers.ts`:
- Around line 81-139: Update useActivateProvider and useDeactivateProvider so
their onSuccess handlers invalidate the provider detail and broad keys in
addition to the lists: call queryClient.invalidateQueries for
providerKeys.detail(id) (use the mutation variable id similar to
useUpdateProvider's onSuccess signature) and also invalidate providerKeys.all()
to refresh derived filters; keep invalidation of providerKeys.lists() as well.
Ensure you access the id from the mutation variables in both useActivateProvider
and useDeactivateProvider and mirror the behavior used in useUpdateProvider.
---
Duplicate comments:
In `@docs/provider-frontend-documentation.md`:
- Around line 1641-1652: The import and usage of the Review icon in the
ProfileReviews component is invalid (lucide-react doesn't export Review); update
the import statement to use a valid exported icon (for example MessageSquare or
Star) and replace the JSX <Review ... /> usage inside the ProfileReviews
function accordingly so the component imports and renders a valid lucide-react
icon (adjust the imported symbol and the JSX tag name in ProfileReviews).
- Line 902: The import statement incorrectly pulls useEffect from
"react-hook-form"; remove useEffect from the import list in the
useForm/useFieldArray import and instead import useEffect from "react" (either
by adding a separate import { useEffect } from "react" or merging it into an
existing React import). Update the import that declares useForm and
useFieldArray (the import containing useForm and useFieldArray) to no longer
include useEffect so TypeScript strict typing/builds succeed.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/[id]/page.tsx:
- Around line 51-59: The dialog is closed in the finally block even on mutation
failure; update handleApprove so setIsApproveOpen(false) is called only on
success (inside the try after await activateMutation.mutateAsync(id)) and remove
it from the finally block; do the same for the reject flow (e.g., handleReject /
deactivateMutation.mutateAsync or similar) so dialogs are closed only when the
mutation succeeds and remain open on errors to keep user context.
- Around line 200-202: O trecho que reduz todos os estados a
"Verificado"/"Pendente" usando doc.verificationStatus === 2 deve ser substituído
pelo uso das helpers já existentes: chame
getVerificationBadgeVariant(doc.verificationStatus) para determinar o variant do
<Badge> e use verificationStatusLabels[doc.verificationStatus] (ou a
função/const equivalente) como o texto do Badge, garantindo que estados como
recusado/expirado sejam exibidos corretamente; remova a lógica hardcoded em
favor dessas helpers para <Badge> e mantenha doc.verificationStatus como fonte
única do estado.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts`:
- Around line 74-83: Update useDeleteCategory to invalidate both the services
list and the deleted category's detail cache after a successful mutation: after
calling apiCategoriesDelete in useDeleteCategory, call
queryClient.invalidateQueries for the services cache (["services"]) and also
compute the deleted category id from the mutation input (use data.path.id when
data.path exists or treat data as the id) and call queryClient.invalidateQueries
with categoryKeys.detail(id); keep the existing invalidate of
categoryKeys.lists() as well so all three caches are refreshed.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-services.ts`:
- Around line 28-33: The hook useServices currently always calls the general
list endpoint and masks types with casts; when categoryId is provided you must
call the category-specific API rather than pushing the filter into an any-cast.
Update the queryFn in useServices so that: if categoryId is present it calls the
category-specific function (e.g., apiServicesGetByCategory or the proper API
that accepts a categoryId) with a typed parameter for categoryId, otherwise call
the general list API; keep the queryKey as serviceKeys.list({ categoryId }) and
remove the unsafe any casts so responses remain correctly typed and filtered by
category.
---
Nitpick comments:
In `@docs/provider-frontend-documentation.md`:
- Around line 289-307: The getVerificationLabel and getTypeLabel functions
hard-code numeric "magic" values in the frontend; move these mappings to a
shared contract (e.g., Shared.Contracts enums or constants) and import them
instead of using literal numbers and strings. Replace the switch logic in
getVerificationLabel and getTypeLabel with lookups keyed by the shared
enum/constant (preserving label and color shapes for verification) so the
frontend reads from the authoritative Shared.Contracts definitions and falls
back to safe defaults if an unknown value is encountered.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx:
- Around line 71-77: Substitua os usos de any por um tipo específico (por
exemplo AllowedCityDto) para recuperar segurança de tipos: declare
citiesResponse com o tipo apropriado vindo da API e troque const cities: any[] e
o cast (citiesResponse as any) por const cities: AllowedCityDto[] =
(citiesResponse as AllowedCityDto[]) ?? []; atualize também filteredCities para
inferir AllowedCityDto (ou tipar como AllowedCityDto[]) e remova casts
desnecessários em cityName/stateSigla; garanta que AllowedCityDto contenha os
campos cityName e stateSigla para que as chamadas como c.cityName?.toLowerCase()
compile corretamente.
In
`@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/settings/settings-client.tsx:
- Line 4: Remove the unused Settings import from the icon import list in
settings-client.tsx: update the import statement that currently lists Settings,
User, Bell, Shield, Palette, Save, Loader2 (from "lucide-react") by deleting the
Settings symbol so only the actually used icons remain.
- Around line 199-203: The button currently renders both Loader2 and Save icons
when isSaving is true; update the JSX in settings-client.tsx (the Button inside
the component that calls handleSave) to render Loader2 only when isSaving and
render Save only when !isSaving (i.e., show one icon at a time). Locate the
Button that uses isSaving and the Loader2/Save components and change the
conditional rendering so Save is suppressed while the spinner is shown; keep the
existing classNames and disabled={isSaving} logic intact.
- Around line 41-58: The tab navigation lacks ARIA roles and selected state: add
role="tablist" to the nav element and for each mapped button (inside tabs.map)
set role="tab", add aria-selected={activeTab === tab.id}, and give each button a
stable id (e.g., `tab-${tab.id}`) so it can be referenced; also ensure each tab
panel uses aria-labelledby pointing to that id (match with your panel rendering)
and keep setActiveTab and activeTab logic unchanged.
- Around line 123-160: The "Alterar senha" inputs lack proper label associations
and client-side validation; add unique id attributes to the three Input
components (e.g., currentPasswordId, newPasswordId, confirmPasswordId) and set
each corresponding label's htmlFor to match so screen readers associate labels
with inputs, ensure Inputs also have name attributes and appropriate
type/password props, wrap them in a form with an onSubmit handler (e.g.,
handlePasswordChange) and implement client-side validation inside that handler
to check newPassword === confirmPassword before submitting, and surface
validation errors via an accessible element (aria-live region or descriptive
text tied with aria-describedby) so users receive clear feedback.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/select.tsx`:
- Around line 6-16: O componente Select e a interface SelectProps não
expõem/encaminham props úteis do Radix (por exemplo disabled, name,
defaultValue) para permitir uso em formulários e controle de estado; adicione
essas propriedades ao SelectProps (disabled?: boolean, name?: string,
defaultValue?: string) e passe-as para <SelectPrimitive.Root> (ou aceite um rest
props: ...props e espalhe em SelectPrimitive.Root) garantindo que onValueChange
e value permaneçam intactos; atualize a assinatura da função Select para
desestruturar/encaminhar essas props (referencie SelectProps e a função Select)
sem alterar a API existente dos handlers.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts`:
- Around line 28-43: Substitua os usos de any em useCategories e useCategoryById
por tipos gerados da OpenAPI: declare o tipo de retorno de queryFn como
ApiCategoriesGetData (ou ApiCategoryGetData equivalente) ao chamar
apiCategoriesGet / apiCategoriesGet2, passe esse tipo como generics para
useQuery (useQuery<ApiCategoriesGetData, Error, ApiCategoriesGetData>), e tipar
corretamente o parâmetro do select (ex.: select: (data: ApiCategoriesGetData) =>
data.data ?? data); aplique o mesmo padrão a qualquer mutationFn equivalente
para manter verificação estrita e evitar any.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 8e031f7a-e250-414b-89fd-faa6b536ca98
⛔ Files ignored due to path filters (7)
src/Web/MeAjudaAi.Web.Customer/public/file.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Customer/public/globe.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Customer/public/next.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Customer/public/vercel.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Customer/public/window.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Provider/package.jsonis excluded by!**/MeAjudaAi.Web.Provider/**src/Web/package-lock.jsonis excluded by!**/package-lock.json,!**/package-lock.json
📒 Files selected for processing (37)
docs/provider-frontend-documentation.mdsrc/Web/MeAjudaAi.Web.Admin-React/index.d.tssrc/Web/MeAjudaAi.Web.Admin-React/openapi-ts.config.tssrc/Web/MeAjudaAi.Web.Admin-React/package.jsonsrc/Web/MeAjudaAi.Web.Admin-React/postcss.config.jssrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/dashboard/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/documents/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/layout.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/[id]/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/services/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/api/auth/[...nextauth]/route.tssrc/Web/MeAjudaAi.Web.Admin-React/src/app/api/hello/route.tssrc/Web/MeAjudaAi.Web.Admin-React/src/app/layout.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/login/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/providers/app-providers.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/providers/theme-provider.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/card.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/dialog.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/select.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/theme-toggle.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-allowed-cities.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-providers.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-services.tssrc/Web/MeAjudaAi.Web.Admin-React/src/lib/auth/auth.tssrc/Web/MeAjudaAi.Web.Admin-React/src/lib/types.tssrc/Web/MeAjudaAi.Web.Admin-React/src/middleware.tssrc/Web/MeAjudaAi.Web.Admin-React/tsconfig.jsonsrc/Web/MeAjudaAi.Web.Admin-React/tsconfig.tsbuildinfosrc/Web/package.json
✅ Files skipped from review due to trivial changes (12)
- src/Web/MeAjudaAi.Web.Admin-React/package.json
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/page.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/layout.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/app/api/hello/route.ts
- src/Web/package.json
- src/Web/MeAjudaAi.Web.Admin-React/postcss.config.js
- src/Web/MeAjudaAi.Web.Admin-React/src/lib/types.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/documents/page.tsx
- src/Web/MeAjudaAi.Web.Admin-React/tsconfig.json
- src/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/services/page.tsx
🚧 Files skipped from review as they are similar to previous changes (11)
- src/Web/MeAjudaAi.Web.Admin-React/openapi-ts.config.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/theme-toggle.tsx
- src/Web/MeAjudaAi.Web.Admin-React/index.d.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/components/providers/app-providers.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/app/login/page.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/middleware.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/dashboard/page.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/components/providers/theme-provider.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/card.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/dialog.tsx
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsx
Show resolved
Hide resolved
…arch, and pagination, and introduce provider management pages with new data hooks and UI components.
|
@coderabbitai full review |
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/documents/page.tsx (2)
184-188:⚠️ Potential issue | 🟠 MajorElementos interativos aninhados violam semântica HTML.
O
Linkestá dentro doButton, criando um<a>dentro de<button>. Isso viola a semântica HTML, quebra a navegação por teclado e causa problemas com leitores de tela.💡 Solução recomendada
Inverta a estrutura ou use apenas um elemento interativo:
- <Button variant="ghost" size="icon" aria-label="Visualizar documentos"> - <Link href={`/providers/${provider.id}`}> - <Eye className="h-4 w-4" /> - </Link> - </Button> + <Link + href={`/providers/${provider.id}`} + className="inline-flex h-9 w-9 items-center justify-center rounded-md hover:bg-muted" + aria-label="Visualizar documentos" + > + <Eye className="h-4 w-4" /> + </Link>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/documents/page.tsx around lines 184 - 188, The current JSX nests Link inside Button (Button > Link > Eye), producing an anchor inside a button which breaks HTML semantics; fix by removing the nested interactive element: either make Link the outer element and place the Button/ Eye inside it (so the clickable element is a single anchor), or keep Button and remove Link by handling navigation from Button (onClick navigate to `/providers/${provider.id}`) or render Button as an anchor/Link-friendly element (use your UI Button's "as" or "asChild" prop if available) so there is only one interactive control (reference: Button, Link, Eye, provider.id).
97-133:⚠️ Potential issue | 🟡 MinorKPIs exibem dados desatualizados durante carregamento.
Os KPIs são exibidos quando
providers.length > 0, mas durante o carregamento inicialprovidersé um array vazio, então os cards ficam ocultos. No entanto, se os dados já estiverem em cache e uma refetch ocorrer, os KPIs mostram valores antigos enquanto a tabela exibe o spinner de loading.Considere sincronizar o estado de loading entre KPIs e tabela.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/documents/page.tsx around lines 97 - 133, KPIs are hidden when providers.length === 0 causing stale KPI values during refetch; change the render condition to also show KPIs while the providers query is loading or fetching and ensure kpis come from the latest query result (or show a loading skeleton). Concretely, use the loading flags from your data hook (e.g., isLoading or isFetching from the providers query) alongside providers (replace the condition providers.length > 0 with providers.length > 0 || isLoading || isFetching) and ensure the component that computes/sets kpis (referenced as kpis) is updated from the same query result so KPIs reflect current fetch state (or render placeholders when loading) to keep KPIs and the table synchronized.
🧹 Nitpick comments (8)
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsx (3)
71-71: Evite castingas any— prefira tipagem segura.O casting
as anymascara possíveis erros de tipo. Se a resposta da API pode ter formatos diferentes, defina um tipo union ou use type guards.♻️ Sugestão de refatoração
- const cities: AllowedCityDto[] = (citiesResponse as any)?.value ?? (citiesResponse as any) ?? []; + const cities: AllowedCityDto[] = Array.isArray(citiesResponse) + ? citiesResponse + : (citiesResponse && typeof citiesResponse === "object" && "value" in citiesResponse) + ? (citiesResponse.value as AllowedCityDto[]) ?? [] + : [];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx at line 71, A atribuição que usa casting "as any" para construir const cities: AllowedCityDto[] mascara erros de tipo; substitua-a por tipagem segura criando um tipo union para o retorno da API (por exemplo AllowedCityResponse | AllowedCityDto[]) ou implementando um type guard (ex: isAllowedCityArray(response): response is AllowedCityDto[]) e então atribua cities com uma checagem: se isAllowedCityArray(citiesResponse) use citiesResponse, se for o outro formato extraia .value; atualize a declaração envolvendo AllowedCityDto, citiesResponse e a variável cities em page.tsx para remover todos os "as any" e garantir tipos explícitos e seguros.
304-310: Estilização do<select>nativo pode não ser consistente.O uso de
<select>nativo com classes Tailwind pode ter aparência inconsistente entre navegadores. Considere usar um componenteSelectda biblioteca de UI para manter consistência visual.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx around lines 304 - 310, The native <select> used for state selection (registered via createForm.register("state") and rendering options from brazilianStates) can render inconsistently; replace it with your UI library's controlled Select component (e.g., <Select> or equivalent) and wire it to the form by using createForm.setValue / createForm.register or the library's Controller integration so validation in createForm.formState.errors.state still works; ensure you map brazilianStates to Select options and preserve the empty "Selecione..." option and the existing error message rendering.
290-330: Considere usarautoFocusno primeiro campo do dialog.Para melhor UX, o primeiro campo do formulário pode receber foco automaticamente quando o dialog abre.
♻️ Sugestão
- <Input id="city" {...createForm.register("city")} placeholder="Ex: São Paulo" /> + <Input id="city" {...createForm.register("city")} placeholder="Ex: São Paulo" autoFocus />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx around lines 290 - 330, Add automatic focus to the first field in the create dialog: when isCreateOpen flips true focus the city input. Update the Input with id "city" (the Input component used alongside createForm.register("city")) to accept an autoFocus prop (or, if the Input wrapper doesn’t forward autoFocus, add a useEffect that calls focus() on a ref tied to that input when isCreateOpen becomes true). Ensure the ref or autoFocus is wired to the same input registered by createForm so the first field is focused whenever the Dialog (isCreateOpen) opens.docs/provider-frontend-documentation.md (3)
311-313: Considere adicionar comentário explicativo sobre o hookuse().O código utiliza corretamente o hook
use()do React 19 para desempacotar parâmetros assíncronos (uma novidade do Next.js 15), mas leitores menos familiarizados com essa API recente podem se beneficiar de um comentário breve.💬 Sugestão de comentário
export default function ProviderPublicPage({ params }: PageProps) { // Next.js 15: params é uma Promise, use() desempacota valores assíncronos const resolvedParams = use(params); const slug = resolvedParams.slug;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-frontend-documentation.md` around lines 311 - 313, Adicione um comentário curto explicando o uso do hook use() dentro de ProviderPublicPage para deixar claro que params é assíncrono no Next.js 15/React 19; por exemplo, junto às variáveis params, resolvedParams e slug explique que use(params) desempacota a Promise de parâmetros para melhorar a legibilidade para leitores menos familiarizados com essa API.
280-309: Tipos e enums importados não estão documentados.O código na página pública do prestador (linhas 287-293) importa tipos como
EVerificationStatus,EProviderType,verificationStatusLabelseproviderTypeLabelsde@/lib/types, mas a documentação não mostra a estrutura desses tipos/enums.Para melhorar a compreensão do leitor, considere adicionar uma seção documentando os tipos principais e enums utilizados no projeto, ou pelo menos uma nota referenciando onde essas definições podem ser encontradas.
💡 Sugestão de seção adicional
Adicionar seção após "# 2. PAGES (ROTAS)":
## 2.1. Tipos e Enums Compartilhados ### `lib/types.ts` Enums para status e tipos de prestadores: - EVerificationStatus: Pending (1), UnderReview (2), Approved (3), Rejected (4), Suspended (5) - EProviderType: valores e labels em português - verificationStatusLabels: mapeamento de status para labels localizados - providerTypeLabels: mapeamento de tipos para labels localizados🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-frontend-documentation.md` around lines 280 - 309, The docs are missing definitions for the enums and label maps used by the provider page; add a new section after "# 2. PAGES (ROTAS)" documenting lib/types.ts that describes EVerificationStatus, EProviderType, verificationStatusLabels and providerTypeLabels (list enum numeric values and their Portuguese labels, and show the mapping shape), and reference how they are used by getVerificationLabel and getTypeLabel in the provider frontend so readers can link the types to runtime behavior.
1107-1336: Documentação de componentes incompleta.A seção "3. COMPONENTS - UI" não documenta todos os componentes utilizados nos exemplos de código. Os componentes
Avatar,AvatarFallbackeBadgesão referenciados em múltiplos lugares (linhas 1349, 1372, 1450, 1561, 1575, 1625) mas não possuem documentação própria nesta seção.Para tornar a documentação verdadeiramente completa, adicione as definições desses componentes.
📋 Componentes faltantes para documentar
Componentes a adicionar na seção 3:
components/ui/avatar.tsx(Avatar, AvatarFallback, AvatarImage)components/ui/badge.tsx(Badge com variantes)Estes componentes são usados em:
- Header (linha 1349, 1372)
- Verification Card (linha 1450)
- Profile Components (linhas 1561, 1575, 1625)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-frontend-documentation.md` around lines 1107 - 1336, A seção "3. COMPONENTS - UI" está incompleta: crie e documente os componentes faltantes Avatar (Avatar, AvatarFallback, AvatarImage) e Badge (com variantes) — adicionar entradas explicando props, default exports e exemplos de uso onde são referenciados (Header, Verification Card, Profile Components); implementações/arquivos devem corresponder aos símbolos components/ui/avatar.tsx (exportando Avatar, AvatarFallback, AvatarImage) e components/ui/badge.tsx (exportando Badge com variantes e tipos de props), e a documentação deve incluir exemplos de import, props list (size, src, alt, fallback, variant) e snippets mostrando uso nos locais citados.src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts (1)
49-50: Uso excessivo deanyreduz segurança de tipos.As funções de mutação usam
anypara os dados de entrada, perdendo os benefícios do TypeScript e ocultando possíveis incompatibilidades de tipo com a API. Por exemplo, emuseUpdateCategory, quando a página passa{ id, name, description, isActive }, o hook normaliza parabody: data.body ?? data, mas o tipoApiCategoriesPutData["body"]esperaUpdateServiceCategoryRequestcom propriedades específicas (name,description,displayOrder), nãoidouisActive.Considere tipar adequadamente os parâmetros das mutations para garantir compatibilidade com os tipos gerados da API.
💡 Exemplo de tipagem para useUpdateCategory
+type UpdateCategoryInput = { + id: string; + name: string; + description?: string; + displayOrder?: number; +}; export function useUpdateCategory() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (data: any) => - apiCategoriesPut(data.path ? data : { - path: { id: data.id }, - body: data.body ?? data, - }), + mutationFn: (data: UpdateCategoryInput) => + apiCategoriesPut({ + path: { id: data.id }, + body: { name: data.name, description: data.description, displayOrder: data.displayOrder }, + }),Also applies to: 61-65, 78-79
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts` around lines 49 - 50, The mutation handlers (e.g., mutationFn in useUpdateCategory and similar hooks at the other ranges) currently use broad any types causing unsafe payloads; replace any with the precise API-generated types (e.g., ApiCategoriesPostData, ApiCategoriesPutData["body"], UpdateServiceCategoryRequest) by typing the mutation parameter and the useMutation generics, and adjust the normalization logic (the body: data.body ?? data pattern) to produce the exact expected shape (remove id/isActive and map to name/description/displayOrder when calling apiCategoriesPut or apiCategoriesPost) so the hook signatures (useUpdateCategory, useCreateCategory, etc.) and their calls to apiCategoriesPost/apiCategoriesPut are type-safe and aligned with the API request types.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsx (1)
44-44: Uso excessivo deanyna normalização de dados.A tipagem
anyem múltiplos pontos dificulta a manutenção e desabilita verificações do TypeScript. Considere criar um tipo union para as possíveis estruturas de resposta.♻️ Sugestão de melhoria
+type ProvidersResponse = + | { items: ProviderDto[] } + | { value: ProviderDto[] } + | ProviderDto[]; +const normalizeProviders = (data: ProvidersResponse | undefined): ProviderDto[] => { + if (!data) return []; + if (Array.isArray(data)) return data; + if ('items' in data) return data.items; + if ('value' in data) return data.value; + return []; +}; - const providers: any[] = (data as any)?.items ?? (data as any)?.value ?? (data as any) ?? []; + const providers = normalizeProviders(data as ProvidersResponse | undefined);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx at line 44, The code uses repeated any when normalizing the API response into the providers variable (const providers: any[] = (data as any)?.items ?? (data as any)?.value ?? (data as any) ?? []), which disables TypeScript checks; define a union type (e.g., ApiListResponse = { items?: Provider[] } | { value?: Provider[] } | Provider[]) and a small type guard/normalizer function (referencing data and providers) to safely coerce data into Provider[] without any casts, then replace the current expression with a call to that normalizer so providers is strongly typed as Provider[].
🤖 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.Admin-React/src/app/`(admin)/allowed-cities/page.tsx:
- Around line 111-127: No payload field names are matching the API contract in
handleSubmitCreate: replace cityName with city and stateSigla with state in the
object you pass to createMutation.mutateAsync so the body matches
CreateAllowedCityRequestDto (keep country, serviceRadiusKm and isActive as-is),
and ensure CityFormData fields used (data.city, data.state) are mapped to those
exact keys.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/[id]/page.tsx:
- Around line 28-36: A função getVerificationBadgeVariant está mapeando o status
5 (suspenso) para o caso default ("secondary"), causando inconsistência com
providers/page.tsx onde 5 retorna "warning"; atualize
getVerificationBadgeVariant para tratar explicitamente case 5 e retornar
"warning" e, para evitar duplicação futura, extraia essa função para um módulo
compartilhado (por exemplo em "@/lib/utils" ou "@/lib/types") e importe-a tanto
em src/app/(admin)/providers/[id]/page.tsx quanto em providers/page.tsx.
---
Duplicate comments:
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/documents/page.tsx:
- Around line 184-188: The current JSX nests Link inside Button (Button > Link >
Eye), producing an anchor inside a button which breaks HTML semantics; fix by
removing the nested interactive element: either make Link the outer element and
place the Button/ Eye inside it (so the clickable element is a single anchor),
or keep Button and remove Link by handling navigation from Button (onClick
navigate to `/providers/${provider.id}`) or render Button as an
anchor/Link-friendly element (use your UI Button's "as" or "asChild" prop if
available) so there is only one interactive control (reference: Button, Link,
Eye, provider.id).
- Around line 97-133: KPIs are hidden when providers.length === 0 causing stale
KPI values during refetch; change the render condition to also show KPIs while
the providers query is loading or fetching and ensure kpis come from the latest
query result (or show a loading skeleton). Concretely, use the loading flags
from your data hook (e.g., isLoading or isFetching from the providers query)
alongside providers (replace the condition providers.length > 0 with
providers.length > 0 || isLoading || isFetching) and ensure the component that
computes/sets kpis (referenced as kpis) is updated from the same query result so
KPIs reflect current fetch state (or render placeholders when loading) to keep
KPIs and the table synchronized.
---
Nitpick comments:
In `@docs/provider-frontend-documentation.md`:
- Around line 311-313: Adicione um comentário curto explicando o uso do hook
use() dentro de ProviderPublicPage para deixar claro que params é assíncrono no
Next.js 15/React 19; por exemplo, junto às variáveis params, resolvedParams e
slug explique que use(params) desempacota a Promise de parâmetros para melhorar
a legibilidade para leitores menos familiarizados com essa API.
- Around line 280-309: The docs are missing definitions for the enums and label
maps used by the provider page; add a new section after "# 2. PAGES (ROTAS)"
documenting lib/types.ts that describes EVerificationStatus, EProviderType,
verificationStatusLabels and providerTypeLabels (list enum numeric values and
their Portuguese labels, and show the mapping shape), and reference how they are
used by getVerificationLabel and getTypeLabel in the provider frontend so
readers can link the types to runtime behavior.
- Around line 1107-1336: A seção "3. COMPONENTS - UI" está incompleta: crie e
documente os componentes faltantes Avatar (Avatar, AvatarFallback, AvatarImage)
e Badge (com variantes) — adicionar entradas explicando props, default exports e
exemplos de uso onde são referenciados (Header, Verification Card, Profile
Components); implementações/arquivos devem corresponder aos símbolos
components/ui/avatar.tsx (exportando Avatar, AvatarFallback, AvatarImage) e
components/ui/badge.tsx (exportando Badge com variantes e tipos de props), e a
documentação deve incluir exemplos de import, props list (size, src, alt,
fallback, variant) e snippets mostrando uso nos locais citados.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx:
- Line 71: A atribuição que usa casting "as any" para construir const cities:
AllowedCityDto[] mascara erros de tipo; substitua-a por tipagem segura criando
um tipo union para o retorno da API (por exemplo AllowedCityResponse |
AllowedCityDto[]) ou implementando um type guard (ex:
isAllowedCityArray(response): response is AllowedCityDto[]) e então atribua
cities com uma checagem: se isAllowedCityArray(citiesResponse) use
citiesResponse, se for o outro formato extraia .value; atualize a declaração
envolvendo AllowedCityDto, citiesResponse e a variável cities em page.tsx para
remover todos os "as any" e garantir tipos explícitos e seguros.
- Around line 304-310: The native <select> used for state selection (registered
via createForm.register("state") and rendering options from brazilianStates) can
render inconsistently; replace it with your UI library's controlled Select
component (e.g., <Select> or equivalent) and wire it to the form by using
createForm.setValue / createForm.register or the library's Controller
integration so validation in createForm.formState.errors.state still works;
ensure you map brazilianStates to Select options and preserve the empty
"Selecione..." option and the existing error message rendering.
- Around line 290-330: Add automatic focus to the first field in the create
dialog: when isCreateOpen flips true focus the city input. Update the Input with
id "city" (the Input component used alongside createForm.register("city")) to
accept an autoFocus prop (or, if the Input wrapper doesn’t forward autoFocus,
add a useEffect that calls focus() on a ref tied to that input when isCreateOpen
becomes true). Ensure the ref or autoFocus is wired to the same input registered
by createForm so the first field is focused whenever the Dialog (isCreateOpen)
opens.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx:
- Line 44: The code uses repeated any when normalizing the API response into the
providers variable (const providers: any[] = (data as any)?.items ?? (data as
any)?.value ?? (data as any) ?? []), which disables TypeScript checks; define a
union type (e.g., ApiListResponse = { items?: Provider[] } | { value?:
Provider[] } | Provider[]) and a small type guard/normalizer function
(referencing data and providers) to safely coerce data into Provider[] without
any casts, then replace the current expression with a call to that normalizer so
providers is strongly typed as Provider[].
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts`:
- Around line 49-50: The mutation handlers (e.g., mutationFn in
useUpdateCategory and similar hooks at the other ranges) currently use broad any
types causing unsafe payloads; replace any with the precise API-generated types
(e.g., ApiCategoriesPostData, ApiCategoriesPutData["body"],
UpdateServiceCategoryRequest) by typing the mutation parameter and the
useMutation generics, and adjust the normalization logic (the body: data.body ??
data pattern) to produce the exact expected shape (remove id/isActive and map to
name/description/displayOrder when calling apiCategoriesPut or
apiCategoriesPost) so the hook signatures (useUpdateCategory, useCreateCategory,
etc.) and their calls to apiCategoriesPost/apiCategoriesPut are type-safe and
aligned with the API request types.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 26d805e2-934c-4a72-b828-60ff8e9e045b
⛔ Files ignored due to path filters (7)
src/Web/MeAjudaAi.Web.Customer/public/file.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Customer/public/globe.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Customer/public/next.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Customer/public/vercel.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Customer/public/window.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Provider/package.jsonis excluded by!**/MeAjudaAi.Web.Provider/**src/Web/package-lock.jsonis excluded by!**/package-lock.json,!**/package-lock.json
📒 Files selected for processing (38)
.github/workflows/pr-validation.ymldocs/provider-frontend-documentation.mdsrc/Web/MeAjudaAi.Web.Admin-React/index.d.tssrc/Web/MeAjudaAi.Web.Admin-React/openapi-ts.config.tssrc/Web/MeAjudaAi.Web.Admin-React/package.jsonsrc/Web/MeAjudaAi.Web.Admin-React/postcss.config.jssrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/dashboard/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/documents/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/layout.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/[id]/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/services/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/api/auth/[...nextauth]/route.tssrc/Web/MeAjudaAi.Web.Admin-React/src/app/api/hello/route.tssrc/Web/MeAjudaAi.Web.Admin-React/src/app/layout.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/login/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/providers/app-providers.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/providers/theme-provider.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/card.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/dialog.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/select.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/theme-toggle.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-allowed-cities.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-providers.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-services.tssrc/Web/MeAjudaAi.Web.Admin-React/src/lib/auth/auth.tssrc/Web/MeAjudaAi.Web.Admin-React/src/lib/types.tssrc/Web/MeAjudaAi.Web.Admin-React/src/middleware.tssrc/Web/MeAjudaAi.Web.Admin-React/tsconfig.jsonsrc/Web/MeAjudaAi.Web.Admin-React/tsconfig.tsbuildinfosrc/Web/package.json
✅ Files skipped from review due to trivial changes (14)
- src/Web/MeAjudaAi.Web.Admin-React/src/app/api/hello/route.ts
- src/Web/MeAjudaAi.Web.Admin-React/postcss.config.js
- src/Web/MeAjudaAi.Web.Admin-React/package.json
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/layout.tsx
- src/Web/MeAjudaAi.Web.Admin-React/index.d.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/theme-toggle.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/components/providers/app-providers.tsx
- src/Web/MeAjudaAi.Web.Admin-React/openapi-ts.config.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/dialog.tsx
- src/Web/MeAjudaAi.Web.Admin-React/tsconfig.json
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/card.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/middleware.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/lib/types.ts
🚧 Files skipped from review as they are similar to previous changes (9)
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/page.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/app/api/auth/[...nextauth]/route.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/app/login/page.tsx
- src/Web/package.json
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/components/providers/theme-provider.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-allowed-cities.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-services.ts
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/[id]/page.tsx
Outdated
Show resolved
Hide resolved
✅ Actions performedFull review triggered. |
…nd allowed cities with supporting hooks and utilities.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (12)
src/Web/MeAjudaAi.Web.Admin-React/src/app/global.css (1)
3-57:⚠️ Potential issue | 🟠 MajorTema escuro não está acoplado ao toggle da aplicação.
O tema está preso ao
prefers-color-schemee aos tokens hardcoded; isso impede que a alternância via classe.dark(vinda do provider) reflita corretamente nos utilitários (bg-background,text-foreground, etc.).🔧 Ajuste sugerido
`@theme` inline { - --color-border: `#e0e0e0`; - --color-input: `#e0e0e0`; + --color-border: var(--border); + --color-input: var(--input); - --color-background: `#ffffff`; - --color-surface: `#ffffff`; - --color-surface-raised: `#f5f5f5`; - --color-foreground: `#2e2e2e`; - --color-foreground-subtle: `#666666`; + --color-background: var(--background); + --color-surface: var(--surface); + --color-surface-raised: var(--surface-raised); + --color-foreground: var(--foreground); + --color-foreground-subtle: var(--foreground-subtle); - --color-card: `#ffffff`; - --color-card-foreground: `#2e2e2e`; + --color-card: var(--surface); + --color-card-foreground: var(--foreground); - --color-ring: `#395873`; + --color-ring: var(--ring, `#395873`); } -@media (prefers-color-scheme: dark) { - :root { - --background: `#0a0a0a`; - --foreground: `#ededed`; - --surface: `#1a1a1a`; - --surface-raised: `#262626`; - --foreground-subtle: `#a3a3a3`; - --border: `#404040`; - --input: `#404040`; - } +.dark { + --background: `#0a0a0a`; + --foreground: `#ededed`; + --surface: `#1a1a1a`; + --surface-raised: `#262626`; + --foreground-subtle: `#a3a3a3`; + --border: `#404040`; + --input: `#404040`; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/global.css` around lines 3 - 57, The CSS currently defines theme tokens in an `@theme` inline block and relies on `@media` (prefers-color-scheme: dark), which prevents the app's .dark class toggle from taking effect; move the color token definitions into :root for the light defaults and duplicate them under a .dark selector for dark mode (using the same variable names like --background, --foreground, --surface, --surface-raised, --foreground-subtle, --border, --input, --color-primary, etc.), remove or stop relying on the `@theme` inline block for runtime theming, and keep the `@media` (prefers-color-scheme: dark) as an optional fallback that sets the same variables so both the system preference and the .dark class can work consistently with utilities like bg-background and text-foreground.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/[id]/page.tsx (1)
28-36:⚠️ Potential issue | 🟡 MinorInconsistência no mapeamento de status 5 (Suspenso).
A função
getVerificationBadgeVariantnão trata explicitamente o status 5 (Suspenso), retornando"secondary"pelo caso default. Em outras páginas, o mesmo status pode retornar"warning", causando badges com cores diferentes para o mesmo status.🔧 Correção sugerida
const getVerificationBadgeVariant = (status?: VerificationStatus) => { switch (status) { case 2: return "success" as const; case 0: return "warning" as const; case 3: case 4: return "destructive" as const; + case 5: return "warning" as const; default: return "secondary" as const; } };Considere extrair esta função para um módulo compartilhado (
@/lib/utilsou@/lib/types) para garantir consistência em toda a aplicação.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/[id]/page.tsx around lines 28 - 36, A função getVerificationBadgeVariant está deixando o status 5 (Suspenso) cair no default ("secondary"), causando inconsistência; atualize o switch para tratar explicitamente case 5: return "warning" as const; e depois extraia/centralize essa função (e o tipo VerificationStatus) para um módulo compartilhado (ex.: `@/lib/utils` ou `@/lib/types`) para garantir que todas as páginas usem o mesmo mapeamento de badges.docs/admin-frontend-documentation.md (1)
614-665:⚠️ Potential issue | 🟡 MinorAdicione linguagem no fenced code block.
O bloco de código na linha 614 não especifica linguagem, gerando warning de lint (MD040). Use
textpara resolver.🔧 Ajuste sugerido
-``` +```text src/Web/MeAjudaAi.Web.Admin-React/ ├── src/🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/admin-frontend-documentation.md` around lines 614 - 665, The fenced code block that starts with "src/Web/MeAjudaAi.Web.Admin-React/" in docs/admin-frontend-documentation.md lacks a language tag and triggers MD040; fix it by changing the opening fence from ``` to ```text so the tree block is ```text ... ```, which will satisfy the linter.src/Web/MeAjudaAi.Web.Admin-React/src/components/providers/theme-provider.tsx (1)
43-45:⚠️ Potential issue | 🔴 CriticalBug:
childrenrenderizados fora doThemeContext.Providerantes da montagem.Quando
!mounted, os children são renderizados sem o provider (linha 44), fazendo consumidores que chamamuseTheme()lançarem o erro da linha 57. Isso pode quebrar a UI no primeiro render.🐛 Correção sugerida: sempre envolver children no provider
- if (!mounted) { - return <>{children}</>; - } - return ( <ThemeContext.Provider value={{ theme, setTheme, toggleTheme }}> {children} </ThemeContext.Provider> );Ou, se precisar evitar flash de tema incorreto, use um valor default seguro no provider mesmo antes da montagem.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/providers/theme-provider.tsx` around lines 43 - 45, O retorno antecipado when (!mounted) que renderiza children fora do ThemeContext.Provider causa erros em consumidores que chamam useTheme(); remova esse return e sempre envolver children com <ThemeContext.Provider> (referência: mounted, children, ThemeContext.Provider, useTheme) e, quando ainda não montado, passe um valor seguro/default no value (ex.: fallbackTheme ou um objeto com safe defaults) para evitar flash de tema incorreto até que o estado real esteja pronto.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/documents/page.tsx (1)
184-188:⚠️ Potential issue | 🟠 MajorCorrigir aninhamento de elementos interativos.
O
Button(que renderiza<button>) contém umLink(que renderiza<a>), criando elementos interativos aninhados. Isso viola semântica HTML e quebra navegação por teclado.🔧 Correção sugerida
- <Button variant="ghost" size="icon" aria-label="Visualizar documentos"> - <Link href={`/providers/${provider.id}`}> - <Eye className="h-4 w-4" /> - </Link> - </Button> + <Button asChild variant="ghost" size="icon" aria-label="Visualizar documentos"> + <Link href={`/providers/${provider.id}`}> + <Eye className="h-4 w-4" /> + </Link> + </Button>Isso requer que o componente
Buttonsuporte a propasChildusandoSlotdo Radix UI.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/documents/page.tsx around lines 184 - 188, The Button in page.tsx currently nests a Link (an <a>) causing interactive elements to be nested; update the Button usage to render the anchor as its child by enabling asChild on the Button and passing the Link as its child (i.e., use Button with prop asChild and wrap Link so the Link becomes the rendered element), or update the Button component to accept an asChild prop implemented via Radix UI Slot; adjust the Button invocation around <Link href={`/providers/${provider.id}`}> and keep the Eye icon inside the Link so no <button> contains an <a>.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsx (2)
194-199:⚠️ Potential issue | 🟠 MajorOs botões de ação precisam de rótulos acessíveis.
Editar/excluir só com ícone tende a ser anunciado apenas como “button”. Inclua
aria-labeldescritivo nos dois controles.💡 Ajuste sugerido
- <Button variant="ghost" size="icon" onClick={() => handleOpenEdit(category)}> + <Button + variant="ghost" + size="icon" + aria-label={`Editar ${category.name ?? "categoria"}`} + title={`Editar ${category.name ?? "categoria"}`} + onClick={() => handleOpenEdit(category)} + > <Pencil className="h-4 w-4" /> </Button> - <Button variant="ghost" size="icon" onClick={() => handleOpenDelete(category)}> + <Button + variant="ghost" + size="icon" + aria-label={`Excluir ${category.name ?? "categoria"}`} + title={`Excluir ${category.name ?? "categoria"}`} + onClick={() => handleOpenDelete(category)} + > <Trash2 className="h-4 w-4 text-red-500" /> </Button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/categories/page.tsx around lines 194 - 199, Add descriptive aria-labels to the action icon buttons so screen readers announce their purpose: update the two Button components that call handleOpenEdit(category) and handleOpenDelete(category) (the ones rendering <Pencil /> and <Trash2 />) to include aria-label attributes like "Editar categoria" and "Excluir categoria" (or similar localized text) while keeping the existing onClick handlers and icon children unchanged.
142-147:⚠️ Potential issue | 🟠 MajorCampo de busca sem nome acessível.
Placeholder não substitui label; para leitor de tela esse input fica sem contexto. Adicione
aria-labelou um<label>associado.💡 Ajuste sugerido
<Input + aria-label="Buscar categoria por nome" placeholder="Buscar por nome..." className="pl-10" value={search}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/categories/page.tsx around lines 142 - 147, O campo de busca Input (props: placeholder, className, value={search}, onChange={(e) => setSearch(e.target.value)}) está sem label acessível; adicione um atributo acessível (ex.: aria-label="Buscar categorias por nome") ou associe um <label> ao input para fornecer contexto a leitores de tela, garantindo que o texto descreva claramente a finalidade do campo de busca e mantenha o placeholder existente para UX visual.src/Web/MeAjudaAi.Web.Admin-React/src/lib/types.ts (1)
18-18:⚠️ Potential issue | 🟠 Major
ProviderStatusainda não cobre o estado0.O tipo,
EProviderStatuseproviderStatusLabelscomeçam em1, então qualquer payload comstatus = 0continua fora do contrato local e sem label no UI. IncluaNone/0aqui — idealmente derivando esses valores do contrato compartilhado/gerado para evitar novo drift.Based on learnings, prioritize reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code.💡 Ajuste sugerido
-export type ProviderStatus = 1 | 2 | 3 | 4 | 5; +export type ProviderStatus = 0 | 1 | 2 | 3 | 4 | 5; @@ export const EProviderStatus = { + None: 0, PendingBasicInfo: 1, PendingDocumentVerification: 2, Active: 3, Suspended: 4, Rejected: 5, } as const; @@ export const providerStatusLabels: Record<ProviderStatus, string> = { + 0: "Não definido", };Also applies to: 30-36, 62-68
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/lib/types.ts` at line 18, ProviderStatus currently omits the 0 state; update the union type export ProviderStatus to include 0 (e.g., 0 | 1 | 2 | 3 | 4 | 5) and ensure EProviderStatus and providerStatusLabels are aligned to include a None/0 entry; preferably replace the hard-coded enum/labels by importing the generated/shared contract enum/constants (Shared.Contracts) and derive providerStatusLabels from that import so the Web layer stays in sync with backend/shared values.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsx (2)
144-147:⚠️ Potential issue | 🟠 MajorEvite
<button>dentro de<a>.O
Linkjá é interativo; envolver umButtonaqui cria marcação inválida e pode quebrar foco/navegação por teclado. Renderize só um elemento clicável nessa célula.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx around lines 144 - 147, O trecho JSX envolve um <Button> dentro de um <Link>, o que cria marcação interativa aninhada; remova o <Button> e torne apenas o <Link> o elemento clicável quando provider.id existir (ou substitua o <Link> por um <button> que chama router.push), preservando atributos de acessibilidade e aparência: renderize <Link href={`/providers/${provider.id}`} aria-label="Visualizar" title="Visualizar" className="..."> com o ícone <Eye className="h-4 w-4" /> dentro (ou aplique as classes do Button ao Link) em vez de aninhar <Button>, referenciando os símbolos Link, Button, Eye e provider.id no ajuste.
20-28:⚠️ Potential issue | 🟠 MajorOs badges de verificação ficaram invertidos com o enum atual.
Hoje
InProgressacaba em"success"eVerifiedem"destructive", então a cor mostrada na listagem não bate com o status real. Troque os números mágicos porEVerificationStatuse remapeie os variants.💡 Ajuste sugerido
import { + EVerificationStatus, providerTypeLabels, verificationStatusLabels, type ProviderDto, type VerificationStatus, } from "@/lib/types"; @@ const getVerificationBadgeVariant = (status?: VerificationStatus) => { switch (status) { - case 2: return "success" as const; - case 0: return "warning" as const; - case 3: - case 4: return "destructive" as const; - case 5: return "warning" as const; + case EVerificationStatus.Pending: + case EVerificationStatus.InProgress: + return "warning" as const; + case EVerificationStatus.Verified: + return "success" as const; + case EVerificationStatus.Rejected: + case EVerificationStatus.Suspended: + return "destructive" as const; default: return "secondary" as const; } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx around lines 20 - 28, Substitua os números mágicos em getVerificationBadgeVariant pelo enum EVerificationStatus e corrija o mapeamento para que os estados correspondam visualmente (por exemplo mapear EVerificationStatus.Verified para "success", EVerificationStatus.InProgress para "warning", os estados de rejeição/erro do enum para "destructive", etc.); localizar a função getVerificationBadgeVariant e atualizar cada case para usar EVerificationStatus.<enum> members em vez de literais numéricos e ajustar os variants para refletir corretamente Verified → "success", InProgress → "warning" e os estados de falha/rejeição → "destructive".src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsx (1)
111-127:⚠️ Potential issue | 🔴 CriticalNomes de campos incorretos no payload de criação.
O contrato
CreateAllowedCityRequestDtoda API espera os camposcityestate, mas o código enviacityNameestateSigla. Isso causará falha ou campos vazios no backend.🐛 Correção proposta
const handleSubmitCreate = async (data: CityFormData) => { try { await createMutation.mutateAsync({ body: { - cityName: data.city, - stateSigla: data.state, + city: data.city, + state: data.state, country: "Brasil", serviceRadiusKm: data.serviceRadiusKm, isActive: data.isActive, }, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx around lines 111 - 127, handleSubmitCreate is sending the wrong payload keys to the API — replace cityName and stateSigla with the expected CreateAllowedCityRequestDto keys city and state when calling createMutation.mutateAsync (keep other fields: country, serviceRadiusKm, isActive); update the object in handleSubmitCreate so it maps data.city -> city and data.state -> state before invoking createMutation.src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-providers.ts (1)
118-142:⚠️ Potential issue | 🔴 CriticalEndpoints de ativação/desativação são para auto-serviço, não administração.
apiActivatePosteapiDeactivatePostapontam para/api/v1/providers/me/activatee/api/v1/providers/me/deactivate— endpoints de auto-ativação do prestador autenticado, não do admin. O parâmetro{ path: { id } }é ignorado pelo endpoint real.Quando um admin tentar ativar/desativar um prestador, a requisição irá operar sobre o próprio admin (se for prestador) ou falhar, não sobre o prestador-alvo.
É necessário expor endpoints administrativos no backend (ex:
PUT /api/v1/admin/providers/{id}/activate) antes de implementar essa funcionalidade no frontend.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-providers.ts` around lines 118 - 142, The hooks useActivateProvider and useDeactivateProvider call apiActivatePost/apiDeactivatePost which hit the "me" self-service endpoints (ignoring the provided id) — replace these with the administrative endpoints (or API client functions) that accept a provider id (e.g., admin activate/deactivate functions such as apiAdminProvidersActivate or apiAdminProvidersDeactivate or a PUT to /api/v1/admin/providers/{id}/activate) so the mutationFn sends the id in the request path/body correctly; if those admin endpoints/clients do not yet exist, add the backend routes and corresponding API client functions, then update useActivateProvider and useDeactivateProvider to call them and keep the same onSuccess cache invalidations (providerKeys.detail(id) and providerKeys.lists()).
🧹 Nitpick comments (13)
src/Web/MeAjudaAi.Web.Admin-React/index.d.ts (1)
1-1: Diretiva ESLint desnecessária.A diretiva
eslint-disable@typescript-eslint/no-explicit-any`` não é necessária, pois não há tiposanyexplícitos neste arquivo. Remover esta linha tornará o código mais limpo.♻️ Refatoração sugerida
-/* eslint-disable `@typescript-eslint/no-explicit-any` */ import * as React from 'react';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/index.d.ts` at line 1, Remove the unnecessary ESLint directive "eslint-disable `@typescript-eslint/no-explicit-any`" from the top of the declaration file; since there are no explicit any types in this file, delete that comment line (the directive) so the file no longer disables the rule for nothing and keeps linting strict for any future edits.src/Web/package.json (1)
58-58:autoprefixeré redundante com Tailwind CSS v4.O plugin
@tailwindcss/postcss(linha 48) já inclui Lightning CSS, que cuida automaticamente de vendor prefixes, nesting e minificação. A dependênciaautoprefixerpode ser removida para simplificar a configuração.♻️ Remoção sugerida
- "autoprefixer": "10.4.13",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/package.json` at line 58, Remova a dependência redundante "autoprefixer" do package.json e quaisquer referências a ela na configuração do PostCSS; especificamente, delete the "autoprefixer" entry and ensure PostCSS/tailwind setup continues to use the `@tailwindcss/postcss` plugin (que já fornece Lightning CSS para prefixes), atualizando scripts/install steps se necessário para refletir a remoção.src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/dialog.tsx (1)
40-55: Considere usartwMergepara composição de classes.O
DialogContentusa concatenação de strings para combinar classes, mas outros componentes UI (input.tsx,card.tsx,button.tsx) usamtwMerge. Isso pode causar conflitos de classes Tailwind não resolvidos quando o usuário passaclassNamecustomizado.♻️ Refatoração sugerida
+import { twMerge } from "tailwind-merge"; + export function DialogContent({ children, className = "" }: DialogContentProps) { return ( <DialogPrimitive.Portal> <DialogPrimitive.Overlay className="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" /> <DialogPrimitive.Content - className={`fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-lg ${className}`} + className={twMerge( + "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-lg", + className + )} >Aplique o mesmo padrão para
DialogHeader,DialogFooter,DialogTitleeDialogDescription.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/dialog.tsx` around lines 40 - 55, Replace the string-concatenation approach in DialogContent with tailwind-merge: import the twMerge function (used elsewhere) and call twMerge(baseClassString, className) to combine the fixed Tailwind classes with the incoming className to prevent conflicting utility resolution; apply the same change/pattern to the sibling components DialogHeader, DialogFooter, DialogTitle and DialogDescription so all dialog subcomponents consistently use twMerge for class composition.src/Web/MeAjudaAi.Web.Admin-React/postcss.config.js (1)
1-1: Importação não utilizada.A variável
joinimportada depathnão é utilizada no arquivo. Pode ser removida para manter o código limpo.🧹 Correção proposta
-const { join } = require('path'); - // Note: If you use library-specific PostCSS/Tailwind configuration then you should remove the `postcssConfig` build🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/postcss.config.js` at line 1, Remova a importação não utilizada "join" do módulo "path" no arquivo postcss.config.js: localize a linha que declara const { join } = require('path'); e delete apenas o identificador "join" (ou a linha inteira) para eliminar a variável não usada, garantindo que nenhuma outra referência a join exista no arquivo.src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/index.ts (1)
1-6: Considere exports explícitos no barrel.
export *aqui funciona, mas exports nomeados deixam a API pública mais previsível e reduzem risco de colisão futura.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/index.ts` around lines 1 - 6, Replace the wildcard re-exports with explicit named exports to make the public API predictable: instead of export * from "./use-providers" etc., export the specific hook names (e.g., export { useProviders } from "./use-providers"; export { useAllowedCities } from "./use-allowed-cities"; export { useCategories } from "./use-categories"; export { useUsers } from "./use-users"; export { useDashboard } from "./use-dashboard"; export { useServices } from "./use-services";), and confirm each exported identifier matches the actual named export in its source file (adjust names if the source files export different identifiers or defaults).src/Web/MeAjudaAi.Web.Admin-React/tsconfig.json (1)
39-40: Caminhos de include redundantes ou incorretos.Os paths nas linhas 39-40 parecem incorretos ou desnecessários:
../MeAjudaAi.Web.Admin-React/.next/types/**/*.ts— sobe um nível e volta ao mesmo diretório, caminho circular.../dist/MeAjudaAi.Web.Admin-React/.next/types/**/*.ts— referencia pastadistque pode não existir.A linha 42 (
.next/types/**/*.ts) já cobre os tipos gerados pelo Next.js corretamente.🔧 Correção sugerida
"include": [ "**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx", - "../MeAjudaAi.Web.Admin-React/.next/types/**/*.ts", - "../dist/MeAjudaAi.Web.Admin-React/.next/types/**/*.ts", "next-env.d.ts", ".next/types/**/*.ts" ],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/tsconfig.json` around lines 39 - 40, Remova as entradas redundantes no campo "include" do tsconfig.json: delete as strings "../MeAjudaAi.Web.Admin-React/.next/types/**/*.ts" e "../dist/MeAjudaAi.Web.Admin-React/.next/types/**/*.ts" e mantenha apenas ".next/types/**/*.ts" para cobrir os tipos gerados pelo Next.js; garanta que nenhuma outra referência circular ou a pasta dist inexistente permaneça nas entradas de include.src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/badge.tsx (1)
14-15: LGTM com observação menor.Implementação correta do componente Badge. As variantes
successewarningusam cores Tailwind hardcoded (green-100,yellow-100), enquanto outras variantes usam tokens CSS do tema. Isso pode resultar em aparência inconsistente no dark mode.Se necessário suportar dark mode para badges de status, considere adicionar variantes com
dark::- success: "border-transparent bg-green-100 text-green-800", - warning: "border-transparent bg-yellow-100 text-yellow-800", + success: "border-transparent bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100", + warning: "border-transparent bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-100",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/badge.tsx` around lines 14 - 15, The Badge component’s "success" and "warning" variant strings (variants in badge.tsx) use hardcoded Tailwind colors ("bg-green-100", "bg-yellow-100") which can break dark mode consistency; update those variant definitions in the Badge variants map to use the theme CSS tokens (the same pattern used by other variants) or add corresponding dark: classes (e.g., include dark:bg-... and dark:text-... alongside the existing classes) so the Badge component renders correctly in dark mode while keeping names "success" and "warning" intact.src/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsx (1)
69-72: Correção do avatar aplicada. Sugestão menor para consistência.O fallback da inicial do avatar foi corrigido corretamente na linha 69. No entanto, a linha 72 ainda usa
??para o nome completo, o que pode exibir uma string vazia sesession?.user?.namefor"".💡 Sugestão opcional para consistência
- <p className="truncate text-sm font-medium">{session?.user?.name ?? "Admin"}</p> + <p className="truncate text-sm font-medium">{session?.user?.name?.trim() || "Admin"}</p>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsx` around lines 69 - 72, The displayed full name uses nullish coalescing and can show an empty string when session?.user?.name is "", so change the fallback to mirror the avatar initial check: replace the expression using session?.user?.name ?? "Admin" with a trimmed-empty check such as session?.user?.name?.trim() ? session.user.name.trim() : "Admin" so both the avatar initial and the full name consistently fall back when name is empty; update the JSX around the <p> that renders the name accordingly.src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-users.ts (1)
37-37: Remova os casts desnecessários ou corrija a assinatura de tipos.Os casts
as ApiUsersGet2Data(linha 37) eas ApiUsersDeleteData(linha 60) indicam que o objeto passado não corresponde ao tipo esperado pela função. Embora funcionem em tempo de execução, esses casts mascaram incompatibilidades de tipo que podem se tornar erros silenciosos quando a API for regenerada. Considere ajustar a tipagem das chamadas para que sejam compatíveis nativamente, eliminando a necessidade dos casts.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-users.ts` at line 37, O uso de casts "as ApiUsersGet2Data" e "as ApiUsersDeleteData" indica incompatibilidade de tipos; remova esses casts e corrija a chamada para passar o objeto exatamente no formato esperado por apiUsersGet2 e apiUsersDelete (ou ajuste as assinaturas geradas dessas funções) — por exemplo, adapte o shape do argumento (nome da propriedade, estrutura { path: { id } } vs { id } ou outro formato exigido) ou atualize a tipagem/overloads de apiUsersGet2/apiUsersDelete para aceitar o formato atual; garanta que os tipos ApiUsersGet2Data e ApiUsersDeleteData correspondam ao payload real para eliminar a necessidade dos casts.src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.ts (1)
25-40: Essas métricas vão escalar mal com o crescimento da base.O hook faz uma chamada por página, concatena toda a listagem e só então calcula os KPIs. Em produção isso aumenta linearmente a latência do dashboard e a carga no backend. Vale mover esses contadores para um endpoint agregado/cacheado no servidor.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.ts` around lines 25 - 40, The hook use-dashboard.ts currently pages through all providers client-side using apiProvidersGet2 (variables currentPage, totalPages, allProviders) and concatenates everything, which will not scale; replace this by calling a new server-side aggregated endpoint (e.g., /providers/metrics) that returns the KPIs (or a cached aggregate) and use that API in place of the paging loop; if a server change is not possible immediately, at minimum stop collecting allProviders and compute the metrics incrementally per page (aggregate counts from each apiProvidersGet2 response without storing items) to avoid building the full list in memory.src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts (1)
49-50: Considere padronizar a interface de entrada das mutations.A lógica condicional
data.body ? data : { body: data }permite múltiplas formas de chamar o hook, mas dificulta o entendimento e perde type safety. Considere definir tipos explícitos para cada mutation ou padronizar um único formato de entrada.💡 Exemplo de interface padronizada
// Opção 1: Sempre esperar o formato do SDK export function useCreateCategory() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: ApiCategoriesPostData) => apiCategoriesPost(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: categoryKeys.lists() }); }, }); } // Opção 2: Definir tipo próprio do hook type CreateCategoryInput = { name: string; description?: string; }; export function useCreateCategory() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: CreateCategoryInput) => apiCategoriesPost({ body: data }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: categoryKeys.lists() }); }, }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts` around lines 49 - 50, The mutation currently accepts ambiguous input via mutationFn: (data: any) => apiCategoriesPost(data.body ? data : { body: data }), which loses type-safety and clarity; update the hook (e.g., useCreateCategory) to standardize the input by either (A) declaring the SDK type ApiCategoriesPostData and calling apiCategoriesPost(data) directly, or (B) defining a clear hook-specific type (e.g., CreateCategoryInput) and wrapping it as apiCategoriesPost({ body: data }), then update mutationFn, its parameter types, and any callers to use the chosen single input shape and keep onSuccess invalidation (categoryKeys.lists) intact.src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-providers.ts (1)
40-79: Uso excessivo deanycompromete segurança de tipos.Todos os hooks usam
as anypara contornar tipagem do SDK gerado. Isso mascara erros de contrato e dificulta refatorações futuras. Considere ajustar os tipos do SDK ou criar tipos wrapper que reflitam a estrutura real da API.💡 Exemplo de tipagem mais segura
-export function useProviders(filters?: any) { +export function useProviders(filters?: ApiProvidersGet2Data["query"]) { return useQuery({ queryKey: providerKeys.list(filters), - queryFn: () => apiProvidersGet2({ query: filters } as any) as any, - select: (data: any) => data.data ?? data, + queryFn: () => apiProvidersGet2({ query: filters }), + select: (data) => data.data ?? data, }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-providers.ts` around lines 40 - 79, All hooks (useProviders, useProviderById, useProvidersByStatus, useProvidersByType) currently cast SDK calls (apiProvidersGet2, apiProvidersGet3) to any which hides type errors; replace those any casts by defining and using proper request/response types from the SDK (or create local wrapper interfaces) and annotate queryFn return types and select parameter types accordingly so the queryKey helpers (providerKeys.list/detail/byStatus/byType) and consumers get correct typings; ensure enabled flags keep boolean but typed (e.g., id?: string), and update select to map data: ApiResponse<T> -> T without using any.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsx (1)
304-310: Considere usar componente Select acessível.O elemento
<select>nativo não recebe os estilos e comportamentos dos componentes UI customizados. Para consistência visual e melhor acessibilidade (incluindo suporte a teclado e leitores de tela), considere usar um componente Select do design system (ex: Radix UI Select).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx around lines 304 - 310, Substitua o elemento <select> nativo pelo componente Select do design system (ex.: Radix UI Select) mantendo integração com createForm (react-hook-form): importe os componentes Select/Trigger/Content/Item usados no projeto, renderize os estados usando brazilianStates.map para criar Select.Item para cada state, e em vez de {...createForm.register("state")} ligue a seleção ao formulário chamando createForm.setValue("state", value) no onValueChange do Select (e opcionalmente createForm.trigger("state") para validação imediata); preserve a opção vazia ("Selecione...") como Select.Item e mantenha a exibição de erro usando createForm.formState.errors.state.message.
🤖 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.Admin-React/index.d.ts`:
- Around line 4-8: O arquivo index.d.ts declara module '*.svg' e exporta
ReactComponent, mas o projeto não está configurado para isso; corrija removendo
essa declaração se não for usada, ou instale a dependência `@svgr/webpack` no
package.json e atualize next.config.js para suportar SVGR (adicionar regra
webpack para arquivos /\.svg$/ que use `@svgr/webpack` e file-loader/fallback),
garantindo que a importação ReactComponent funcione com a configuração atual;
referencie a declaração module '*.svg' e o símbolo ReactComponent ao aplicar a
alteração.
In
`@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/settings/settings-client.tsx:
- Around line 27-31: Resumo: handleThemeChange atualmente atualiza apenas estado
e localStorage, mas não altera a classe no elemento root, então a UI não muda.
Instruções: inside handleThemeChange, after setTheme and localStorage.setItem,
update document.documentElement.classList by removing any existing theme classes
(e.g., "light" and "dark") and then add the appropriate class: add "dark" when
newTheme === "dark", add "light" when newTheme === "light", and when newTheme
=== "system" detect window.matchMedia("(prefers-color-scheme: dark)").matches to
decide which class to add; keep using the same localStorage key
"meajudaai-theme" and ensure this behavior is mirrored on initial load (e.g.,
reuse this logic or extract to an applyTheme function and call it from
handleThemeChange and initialization code).
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/global.css`:
- Line 3: The Stylelint parser flags the Tailwind v4 at-rule `@theme` (seen in
global.css) as unknown due to the `at-rule-no-unknown` rule; update the
Stylelint config (`.stylelintrc.json`) to either add `"ignoreAtRules":
["theme"]` under rules/at-rule-no-unknown or install/use a Tailwind-aware preset
(e.g., `stylelint-config-tailwindcss`) and extend it so `@theme` is recognized
and the CI/lint pipeline no longer fails on the `@theme` at-rule.
---
Duplicate comments:
In `@docs/admin-frontend-documentation.md`:
- Around line 614-665: The fenced code block that starts with
"src/Web/MeAjudaAi.Web.Admin-React/" in docs/admin-frontend-documentation.md
lacks a language tag and triggers MD040; fix it by changing the opening fence
from ``` to ```text so the tree block is ```text ... ```, which will satisfy the
linter.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx:
- Around line 111-127: handleSubmitCreate is sending the wrong payload keys to
the API — replace cityName and stateSigla with the expected
CreateAllowedCityRequestDto keys city and state when calling
createMutation.mutateAsync (keep other fields: country, serviceRadiusKm,
isActive); update the object in handleSubmitCreate so it maps data.city -> city
and data.state -> state before invoking createMutation.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/categories/page.tsx:
- Around line 194-199: Add descriptive aria-labels to the action icon buttons so
screen readers announce their purpose: update the two Button components that
call handleOpenEdit(category) and handleOpenDelete(category) (the ones rendering
<Pencil /> and <Trash2 />) to include aria-label attributes like "Editar
categoria" and "Excluir categoria" (or similar localized text) while keeping the
existing onClick handlers and icon children unchanged.
- Around line 142-147: O campo de busca Input (props: placeholder, className,
value={search}, onChange={(e) => setSearch(e.target.value)}) está sem label
acessível; adicione um atributo acessível (ex.: aria-label="Buscar categorias
por nome") ou associe um <label> ao input para fornecer contexto a leitores de
tela, garantindo que o texto descreva claramente a finalidade do campo de busca
e mantenha o placeholder existente para UX visual.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/documents/page.tsx:
- Around line 184-188: The Button in page.tsx currently nests a Link (an <a>)
causing interactive elements to be nested; update the Button usage to render the
anchor as its child by enabling asChild on the Button and passing the Link as
its child (i.e., use Button with prop asChild and wrap Link so the Link becomes
the rendered element), or update the Button component to accept an asChild prop
implemented via Radix UI Slot; adjust the Button invocation around <Link
href={`/providers/${provider.id}`}> and keep the Eye icon inside the Link so no
<button> contains an <a>.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/[id]/page.tsx:
- Around line 28-36: A função getVerificationBadgeVariant está deixando o status
5 (Suspenso) cair no default ("secondary"), causando inconsistência; atualize o
switch para tratar explicitamente case 5: return "warning" as const; e depois
extraia/centralize essa função (e o tipo VerificationStatus) para um módulo
compartilhado (ex.: `@/lib/utils` ou `@/lib/types`) para garantir que todas as
páginas usem o mesmo mapeamento de badges.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/page.tsx:
- Around line 144-147: O trecho JSX envolve um <Button> dentro de um <Link>, o
que cria marcação interativa aninhada; remova o <Button> e torne apenas o <Link>
o elemento clicável quando provider.id existir (ou substitua o <Link> por um
<button> que chama router.push), preservando atributos de acessibilidade e
aparência: renderize <Link href={`/providers/${provider.id}`}
aria-label="Visualizar" title="Visualizar" className="..."> com o ícone <Eye
className="h-4 w-4" /> dentro (ou aplique as classes do Button ao Link) em vez
de aninhar <Button>, referenciando os símbolos Link, Button, Eye e provider.id
no ajuste.
- Around line 20-28: Substitua os números mágicos em getVerificationBadgeVariant
pelo enum EVerificationStatus e corrija o mapeamento para que os estados
correspondam visualmente (por exemplo mapear EVerificationStatus.Verified para
"success", EVerificationStatus.InProgress para "warning", os estados de
rejeição/erro do enum para "destructive", etc.); localizar a função
getVerificationBadgeVariant e atualizar cada case para usar
EVerificationStatus.<enum> members em vez de literais numéricos e ajustar os
variants para refletir corretamente Verified → "success", InProgress → "warning"
e os estados de falha/rejeição → "destructive".
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/global.css`:
- Around line 3-57: The CSS currently defines theme tokens in an `@theme` inline
block and relies on `@media` (prefers-color-scheme: dark), which prevents the
app's .dark class toggle from taking effect; move the color token definitions
into :root for the light defaults and duplicate them under a .dark selector for
dark mode (using the same variable names like --background, --foreground,
--surface, --surface-raised, --foreground-subtle, --border, --input,
--color-primary, etc.), remove or stop relying on the `@theme` inline block for
runtime theming, and keep the `@media` (prefers-color-scheme: dark) as an optional
fallback that sets the same variables so both the system preference and the
.dark class can work consistently with utilities like bg-background and
text-foreground.
In
`@src/Web/MeAjudaAi.Web.Admin-React/src/components/providers/theme-provider.tsx`:
- Around line 43-45: O retorno antecipado when (!mounted) que renderiza children
fora do ThemeContext.Provider causa erros em consumidores que chamam useTheme();
remova esse return e sempre envolver children com <ThemeContext.Provider>
(referência: mounted, children, ThemeContext.Provider, useTheme) e, quando ainda
não montado, passe um valor seguro/default no value (ex.: fallbackTheme ou um
objeto com safe defaults) para evitar flash de tema incorreto até que o estado
real esteja pronto.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-providers.ts`:
- Around line 118-142: The hooks useActivateProvider and useDeactivateProvider
call apiActivatePost/apiDeactivatePost which hit the "me" self-service endpoints
(ignoring the provided id) — replace these with the administrative endpoints (or
API client functions) that accept a provider id (e.g., admin activate/deactivate
functions such as apiAdminProvidersActivate or apiAdminProvidersDeactivate or a
PUT to /api/v1/admin/providers/{id}/activate) so the mutationFn sends the id in
the request path/body correctly; if those admin endpoints/clients do not yet
exist, add the backend routes and corresponding API client functions, then
update useActivateProvider and useDeactivateProvider to call them and keep the
same onSuccess cache invalidations (providerKeys.detail(id) and
providerKeys.lists()).
In `@src/Web/MeAjudaAi.Web.Admin-React/src/lib/types.ts`:
- Line 18: ProviderStatus currently omits the 0 state; update the union type
export ProviderStatus to include 0 (e.g., 0 | 1 | 2 | 3 | 4 | 5) and ensure
EProviderStatus and providerStatusLabels are aligned to include a None/0 entry;
preferably replace the hard-coded enum/labels by importing the generated/shared
contract enum/constants (Shared.Contracts) and derive providerStatusLabels from
that import so the Web layer stays in sync with backend/shared values.
---
Nitpick comments:
In `@src/Web/MeAjudaAi.Web.Admin-React/index.d.ts`:
- Line 1: Remove the unnecessary ESLint directive "eslint-disable
`@typescript-eslint/no-explicit-any`" from the top of the declaration file; since
there are no explicit any types in this file, delete that comment line (the
directive) so the file no longer disables the rule for nothing and keeps linting
strict for any future edits.
In `@src/Web/MeAjudaAi.Web.Admin-React/postcss.config.js`:
- Line 1: Remova a importação não utilizada "join" do módulo "path" no arquivo
postcss.config.js: localize a linha que declara const { join } =
require('path'); e delete apenas o identificador "join" (ou a linha inteira)
para eliminar a variável não usada, garantindo que nenhuma outra referência a
join exista no arquivo.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx:
- Around line 304-310: Substitua o elemento <select> nativo pelo componente
Select do design system (ex.: Radix UI Select) mantendo integração com
createForm (react-hook-form): importe os componentes Select/Trigger/Content/Item
usados no projeto, renderize os estados usando brazilianStates.map para criar
Select.Item para cada state, e em vez de {...createForm.register("state")} ligue
a seleção ao formulário chamando createForm.setValue("state", value) no
onValueChange do Select (e opcionalmente createForm.trigger("state") para
validação imediata); preserve a opção vazia ("Selecione...") como Select.Item e
mantenha a exibição de erro usando createForm.formState.errors.state.message.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsx`:
- Around line 69-72: The displayed full name uses nullish coalescing and can
show an empty string when session?.user?.name is "", so change the fallback to
mirror the avatar initial check: replace the expression using
session?.user?.name ?? "Admin" with a trimmed-empty check such as
session?.user?.name?.trim() ? session.user.name.trim() : "Admin" so both the
avatar initial and the full name consistently fall back when name is empty;
update the JSX around the <p> that renders the name accordingly.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/badge.tsx`:
- Around line 14-15: The Badge component’s "success" and "warning" variant
strings (variants in badge.tsx) use hardcoded Tailwind colors ("bg-green-100",
"bg-yellow-100") which can break dark mode consistency; update those variant
definitions in the Badge variants map to use the theme CSS tokens (the same
pattern used by other variants) or add corresponding dark: classes (e.g.,
include dark:bg-... and dark:text-... alongside the existing classes) so the
Badge component renders correctly in dark mode while keeping names "success" and
"warning" intact.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/dialog.tsx`:
- Around line 40-55: Replace the string-concatenation approach in DialogContent
with tailwind-merge: import the twMerge function (used elsewhere) and call
twMerge(baseClassString, className) to combine the fixed Tailwind classes with
the incoming className to prevent conflicting utility resolution; apply the same
change/pattern to the sibling components DialogHeader, DialogFooter, DialogTitle
and DialogDescription so all dialog subcomponents consistently use twMerge for
class composition.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/index.ts`:
- Around line 1-6: Replace the wildcard re-exports with explicit named exports
to make the public API predictable: instead of export * from "./use-providers"
etc., export the specific hook names (e.g., export { useProviders } from
"./use-providers"; export { useAllowedCities } from "./use-allowed-cities";
export { useCategories } from "./use-categories"; export { useUsers } from
"./use-users"; export { useDashboard } from "./use-dashboard"; export {
useServices } from "./use-services";), and confirm each exported identifier
matches the actual named export in its source file (adjust names if the source
files export different identifiers or defaults).
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts`:
- Around line 49-50: The mutation currently accepts ambiguous input via
mutationFn: (data: any) => apiCategoriesPost(data.body ? data : { body: data }),
which loses type-safety and clarity; update the hook (e.g., useCreateCategory)
to standardize the input by either (A) declaring the SDK type
ApiCategoriesPostData and calling apiCategoriesPost(data) directly, or (B)
defining a clear hook-specific type (e.g., CreateCategoryInput) and wrapping it
as apiCategoriesPost({ body: data }), then update mutationFn, its parameter
types, and any callers to use the chosen single input shape and keep onSuccess
invalidation (categoryKeys.lists) intact.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.ts`:
- Around line 25-40: The hook use-dashboard.ts currently pages through all
providers client-side using apiProvidersGet2 (variables currentPage, totalPages,
allProviders) and concatenates everything, which will not scale; replace this by
calling a new server-side aggregated endpoint (e.g., /providers/metrics) that
returns the KPIs (or a cached aggregate) and use that API in place of the paging
loop; if a server change is not possible immediately, at minimum stop collecting
allProviders and compute the metrics incrementally per page (aggregate counts
from each apiProvidersGet2 response without storing items) to avoid building the
full list in memory.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-providers.ts`:
- Around line 40-79: All hooks (useProviders, useProviderById,
useProvidersByStatus, useProvidersByType) currently cast SDK calls
(apiProvidersGet2, apiProvidersGet3) to any which hides type errors; replace
those any casts by defining and using proper request/response types from the SDK
(or create local wrapper interfaces) and annotate queryFn return types and
select parameter types accordingly so the queryKey helpers
(providerKeys.list/detail/byStatus/byType) and consumers get correct typings;
ensure enabled flags keep boolean but typed (e.g., id?: string), and update
select to map data: ApiResponse<T> -> T without using any.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-users.ts`:
- Line 37: O uso de casts "as ApiUsersGet2Data" e "as ApiUsersDeleteData" indica
incompatibilidade de tipos; remova esses casts e corrija a chamada para passar o
objeto exatamente no formato esperado por apiUsersGet2 e apiUsersDelete (ou
ajuste as assinaturas geradas dessas funções) — por exemplo, adapte o shape do
argumento (nome da propriedade, estrutura { path: { id } } vs { id } ou outro
formato exigido) ou atualize a tipagem/overloads de apiUsersGet2/apiUsersDelete
para aceitar o formato atual; garanta que os tipos ApiUsersGet2Data e
ApiUsersDeleteData correspondam ao payload real para eliminar a necessidade dos
casts.
In `@src/Web/MeAjudaAi.Web.Admin-React/tsconfig.json`:
- Around line 39-40: Remova as entradas redundantes no campo "include" do
tsconfig.json: delete as strings
"../MeAjudaAi.Web.Admin-React/.next/types/**/*.ts" e
"../dist/MeAjudaAi.Web.Admin-React/.next/types/**/*.ts" e mantenha apenas
".next/types/**/*.ts" para cobrir os tipos gerados pelo Next.js; garanta que
nenhuma outra referência circular ou a pasta dist inexistente permaneça nas
entradas de include.
In `@src/Web/package.json`:
- Line 58: Remova a dependência redundante "autoprefixer" do package.json e
quaisquer referências a ela na configuração do PostCSS; especificamente, delete
the "autoprefixer" entry and ensure PostCSS/tailwind setup continues to use the
`@tailwindcss/postcss` plugin (que já fornece Lightning CSS para prefixes),
atualizando scripts/install steps se necessário para refletir a remoção.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 710c24a1-1827-4812-9100-bf02d5955f81
⛔ Files ignored due to path filters (26)
src/Web/MeAjudaAi.Web.Admin-React/public/favicon.icois excluded by!**/*.icosrc/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/@tanstack/react-query.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/client.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/client/client.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/client/index.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/client/types.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/client/utils.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/auth.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/bodySerializer.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/params.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/pathSerializer.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/queryKeySerializer.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/serverSentEvents.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/types.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/core/utils.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/index.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/sdk.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/types.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Admin-React/src/lib/api/generated/zod.gen.tsis excluded by!**/generated/**src/Web/MeAjudaAi.Web.Customer/public/file.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Customer/public/globe.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Customer/public/next.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Customer/public/vercel.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Customer/public/window.svgis excluded by!**/*.svgsrc/Web/MeAjudaAi.Web.Provider/package.jsonis excluded by!**/MeAjudaAi.Web.Provider/**src/Web/package-lock.jsonis excluded by!**/package-lock.json,!**/package-lock.json
📒 Files selected for processing (54)
.github/workflows/pr-validation.ymldocs/admin-frontend-documentation.mddocs/customer-frontend-documentation.mddocs/provider-frontend-documentation.mdsrc/Web/MeAjudaAi.Web.Admin-React/.gitignoresrc/Web/MeAjudaAi.Web.Admin-React/.swcrcsrc/Web/MeAjudaAi.Web.Admin-React/index.d.tssrc/Web/MeAjudaAi.Web.Admin-React/next-env.d.tssrc/Web/MeAjudaAi.Web.Admin-React/next.config.jssrc/Web/MeAjudaAi.Web.Admin-React/openapi-ts.config.tssrc/Web/MeAjudaAi.Web.Admin-React/package.jsonsrc/Web/MeAjudaAi.Web.Admin-React/postcss.config.jssrc/Web/MeAjudaAi.Web.Admin-React/project.jsonsrc/Web/MeAjudaAi.Web.Admin-React/public/.gitkeepsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/dashboard/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/documents/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/layout.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/[id]/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/services/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/api/auth/[...nextauth]/route.tssrc/Web/MeAjudaAi.Web.Admin-React/src/app/api/hello/route.tssrc/Web/MeAjudaAi.Web.Admin-React/src/app/global.csssrc/Web/MeAjudaAi.Web.Admin-React/src/app/layout.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/login/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/providers/app-providers.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/providers/theme-provider.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/providers/toast-provider.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/badge.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/button.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/card.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/dialog.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/input.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/select.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/theme-toggle.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/index.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-allowed-cities.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-dashboard.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-providers.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-services.tssrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-users.tssrc/Web/MeAjudaAi.Web.Admin-React/src/lib/auth/auth.tssrc/Web/MeAjudaAi.Web.Admin-React/src/lib/types.tssrc/Web/MeAjudaAi.Web.Admin-React/src/middleware.tssrc/Web/MeAjudaAi.Web.Admin-React/tsconfig.jsonsrc/Web/MeAjudaAi.Web.Admin-React/tsconfig.tsbuildinfosrc/Web/package.json
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsx
Show resolved
Hide resolved
…onal types, enums, and UI components.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (9)
src/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsx (3)
69-74: Evite recalculartrim()e fallback de nome em múltiplos pontos.Você já corrigiu bem o fallback; só falta consolidar em variáveis locais para reduzir duplicação e facilitar manutenção.
🔧 Refactor sugerido
export function Sidebar() { const pathname = usePathname(); const { data: session } = useSession(); + const rawName = session?.user?.name ?? ""; + const displayName = rawName.trim() || "Admin"; + const avatarInitial = displayName.charAt(0).toUpperCase(); return ( @@ <div className="flex h-8 w-8 items-center justify-center rounded-full bg-primary text-primary-foreground text-sm font-medium"> - {session?.user?.name?.trim() ? session.user.name.trim().charAt(0).toUpperCase() : "A"} + {avatarInitial || "A"} </div> <div className="flex-1 overflow-hidden"> - <p className="truncate text-sm font-medium">{session?.user?.name?.trim() ? session.user.name.trim() : "Admin"}</p> + <p className="truncate text-sm font-medium">{displayName}</p> <p className="truncate text-xs text-muted-foreground"> {session?.user?.roles?.includes("admin") ? "Administrador" : "Usuário"} </p> </div>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsx` around lines 69 - 74, The JSX recomputes session.user.name.trim() and the fallback logic in multiple places; extract a local const (e.g., name = session?.user?.name || "Admin"; nameTrimmed = name.trim(); firstInitial = nameTrimmed.charAt(0).toUpperCase()) at the top of the sidebar component and replace the repeated expressions in the avatar, the display <p> and the roles check (session?.user?.roles?.includes("admin")) to use these locals so trimming and fallback are performed once and maintenance is simplified.
20-27: Centralize papéis/labels em constantes compartilhadas.Hoje o papel
"admin"e labels de navegação estão hardcoded no componente. Vale puxar isso deShared.Contracts(ou constantes compartilhadas no domínio admin) para reduzir drift entre Web e backend.Based on learnings: prioritize reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code.
Also applies to: 74-74
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsx` around lines 20 - 27, The sidebar component currently hardcodes navigation labels and the "admin" role; replace these literals by importing the shared constants/enums from Shared.Contracts (or the admin shared constants) and reference them in the nav items array (the list containing items like { href: "/dashboard", label: "Dashboard", icon: LayoutDashboard }) and anywhere "admin" is used (including the occurrence around line 74). Update the nav definition to use the imported label constants and role enum values so labels and roles are sourced from Shared.Contracts, leaving icons/hrefs intact.
34-34: Considere comportamento responsivo explícito para a sidebar fixa.Com
fixed+w-64emLine 34, em telas pequenas a sidebar tende a sobrepor conteúdo principal. Se o admin também precisar funcionar em mobile/tablet, vale adicionar breakpoints (ex.:hidden md:flex) + drawer.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsx` at line 34, The aside element with className "fixed left-0 top-0 z-40 flex h-screen w-64 flex-col border-r border-border bg-surface" will overlap content on small screens; make the sidebar responsive by hiding it on small viewports and showing a drawer/toggle instead: replace the always-fixed behavior with responsive classes (e.g., use "hidden md:flex" on the aside) and implement a mobile drawer/toggle controlled by a boolean state (e.g., isOpen) in the Sidebar component so small screens show a hamburger button that opens a slide-in panel; ensure the desktop layout keeps the fixed w-64 sidebar for md+ breakpoints and that the drawer reuses the same sidebar markup for consistency.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/[id]/page.tsx (1)
181-193: Considere tipardoceservicepara evitarany.O uso de
anyemdoc(linha 183) eservice(linha 208) perde a segurança de tipos e o autocomplete do IDE. Se os tipos da API gerada estiverem disponíveis, considere importá-los.♻️ Sugestão de tipagem
- {provider.documents.map((doc: any, index: number) => ( + {provider.documents.map((doc: ProviderDocument, index: number) => (- {provider.services.map((service: any, index: number) => ( + {provider.services.map((service: ProviderService, index: number) => (Importe os tipos correspondentes do cliente da API ou defina interfaces locais baseadas na resposta esperada.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/[id]/page.tsx around lines 181 - 193, The map callback currently types `doc` as any and `service` elsewhere uses any—replace those anys with concrete types by importing the API-generated types (or defining local interfaces) that describe a ProviderDocument (fields: documentType, fileName, verificationStatus) and the Service shape; update the map signature in the provider.documents.map to use that ProviderDocument type and change the `service` variable declaration to the imported/defined Service type so you regain type-safety and IDE autocomplete (adjust usages of getVerificationBadgeVariant and verificationStatusLabels if their typings need the concrete enum/type).src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsx (2)
32-36: Considere reutilizar a lista de estados deShared.Contracts.A lista
brazilianStatesestá hardcoded aqui. Se o backend já expõe essa constante emShared.Contracts, considere importá-la ou obtê-la via API para evitar divergências futuras e manter o frontend alinhado com o backend.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.Admin-React/src/app/`(admin)/allowed-cities/page.tsx around lines 32 - 36, A array hardcoded brazilianStates deve ser substituída pelo valor único definido em Shared.Contracts; remova a constante local brazilianStates e importe (ou recupere via API) a lista/export enum presente em Shared.Contracts (por exemplo export const/enum de estados) e use esse símbolo importado no componente/page.tsx para garantir consistência com o backend; mantenha um fallback mínimo somente se a importação falhar, mas priorize reutilizar o símbolo Shared.Contracts.
240-242: Desabilite o botão de toggle durante a mutation pendente.O botão de toggle ativo/inativo não é desabilitado enquanto
patchMutation.isPending, permitindo cliques rápidos consecutivos que podem inverter o estado múltiplas vezes.♻️ Correção sugerida
<Button variant="ghost" size="icon" onClick={() => handleToggleActive(city)} aria-label={city.isActive ? "Desativar cidade" : "Ativar cidade"} + disabled={patchMutation.isPending} > <MapPin className={`h-4 w-4 ${city.isActive ? "text-green-500" : "text-gray-400"}`} /> </Button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx around lines 240 - 242, The toggle Button should be disabled while the patch mutation is pending to prevent rapid double clicks; update the Button (the one rendering MapPin) to include a disabled prop checking patchMutation.isPending (or patchMutation.isPending && patchMutation.targetId === city.id if you track per-city mutations) and also add an early guard in handleToggleActive to return immediately when patchMutation.isPending to avoid triggering another mutation while one is in-flight.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsx (2)
58-64: EviteisSavingtravado comtry/catch/finally.Quando a persistência real for conectada, uma exceção no
awaitpode deixar a UI presa em loading. Estruturar agora comfinallyjá evita regressão.🔧 Ajuste sugerido
const handleSave = async () => { if (activeTab === "security") { if (passwords.new && passwords.new !== passwords.confirm) { toast.error("A nova senha e a confirmação não coincidem"); return; } } setIsSaving(true); - // TODO: Implement actual backend API connection using server actions or React Query mutations - await new Promise((resolve) => setTimeout(resolve, 1000)); - toast.success("Configurações salvas com sucesso"); - if (activeTab === "security") setPasswords({ current: "", new: "", confirm: "" }); - setIsSaving(false); + try { + // TODO: Implement actual backend API connection using server actions or React Query mutations + await new Promise((resolve) => setTimeout(resolve, 1000)); + toast.success("Configurações salvas com sucesso"); + if (activeTab === "security") { + setPasswords({ current: "", new: "", confirm: "" }); + } + } catch { + toast.error("Erro ao salvar configurações"); + } finally { + setIsSaving(false); + } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/settings/settings-client.tsx around lines 58 - 64, The current save flow sets setIsSaving(true) then awaits the mock persistence and sets setIsSaving(false) afterward, which can leak if the await throws; wrap the async persistence block in a try/finally so setIsSaving(false) always runs: in the save handler that calls setIsSaving(true) and awaits the fake backend, wrap the await/new Promise and subsequent toast and password reset logic in a try (or try/catch if you want to handle errors) and place setIsSaving(false) in the finally block; reference the save handler where setIsSaving, activeTab, and setPasswords are used to locate the change.
36-42: Aplicar tema também no fallback sem valor salvo.Hoje, se não existir
meajudaai-themenolocalStorage, o estado fica"system"mas o tema não é aplicado por este fluxo. Vale padronizar o comportamento aplicando sempre o tema resolvido.🔧 Ajuste sugerido
useEffect(() => { const saved = localStorage.getItem("meajudaai-theme"); - if (saved === "light" || saved === "dark" || saved === "system") { - setTheme(saved); - applyTheme(saved); - } + const resolvedTheme = + saved === "light" || saved === "dark" || saved === "system" + ? saved + : "system"; + + setTheme(resolvedTheme); + applyTheme(resolvedTheme); }, []);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/settings/settings-client.tsx around lines 36 - 42, The current useEffect only calls applyTheme when a saved localStorage value exists, so when no "meajudaai-theme" is present the state becomes "system" but applyTheme is never invoked; update the useEffect in settings-client.tsx to always call applyTheme with the resolved theme (use the saved value if it is "light"|"dark"|"system", otherwise default to "system"), and ensure you still call setTheme(resolved) and then applyTheme(resolved) so the theme is applied even on the fallback path; reference the useEffect, setTheme and applyTheme symbols and the "meajudaai-theme" localStorage key.src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsx (1)
187-189: Centralize labels de status em constantes/contratos compartilhados“Ativa/Inativa” hardcoded aqui aumenta risco de divergência entre frontends e backend ao longo do tempo.
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.Admin-React/src/app/`(admin)/categories/page.tsx around lines 187 - 189, Replace the hardcoded Portuguese status strings inside the Badge (currently using category.isActive ? "Ativa" : "Inativa") with centralized constants from your shared contracts; import the appropriate enum/object from Shared.Contracts (or the shared module that exposes category status labels) and use it like Badge variant={category.isActive ? STATUS_LABELS.ACTIVE.variant : STATUS_LABELS.INACTIVE.variant} and {category.isActive ? STATUS_LABELS.ACTIVE.label : STATUS_LABELS.INACTIVE.label}, ensuring you reference the shared symbol names instead of literals and update any types/props if needed.
🤖 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.Admin-React/src/app/`(admin)/allowed-cities/page.tsx:
- Around line 81-90: When totalPages can shrink, currentPage must be clamped to
avoid stale state: add a useEffect that watches totalPages and currentPage and,
when currentPage > totalPages, calls setCurrentPage(totalPages) to keep the
state in sync with the derived safePage; update the component that computes
totalPages, safePage, startIndex, paginatedCities (referencing totalPages,
safePage, currentPage, setCurrentPage) so navigation buttons and displayed page
are consistent.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/categories/page.tsx:
- Around line 85-90: The create and edit handlers (e.g., handleSubmitCreate
calling createMutation.mutateAsync and the edit handler calling
editMutation.mutateAsync) currently omit the isActive field from the payload so
the "Categoria Ativa" toggle isn't persisted; update those mutation payloads to
include isActive from the form data (pass data.isActive or a sensible default)
for both create and update paths (also adjust the corresponding functions that
assemble payloads where referenced: createMutation and editMutation invocations
such as in handleSubmitCreate and handleSubmitEdit).
- Around line 59-63: O array de categorias está sendo tratado como any e
tentando acessar `.value`, o que é incorreto — `useCategories()` já retorna
ServiceCategoryDto[] via seu select; remova os casts `any` e normalize
`categoriesResponse` para um array padrão (por exemplo usando
`categoriesResponse ?? []`) em vez de `(... as any)?.value`, e use esse array
tipado quando construir `filteredCategories` (filtrando por `c.name`). Altere
também o cast `any` na ocorrência mencionada na linha 177 para usar o tipo
ServiceCategoryDto em vez de any para manter tipagem segura.
In
`@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/settings/settings-client.tsx:
- Around line 134-136: The static "Função" header in settings-client.tsx is
using a <label> but isn't associated with a form control; replace the <label
className="text-sm font-medium">Função</label> with a semantic non-form element
(e.g., <p> or <span> with the same classes, or a heading element if appropriate)
so it no longer implies a form association while keeping the styling and
accessibility; ensure Badge rendering (Badge variant="success") remains
unchanged and remove any unused htmlFor/for attributes related to this label.
---
Nitpick comments:
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/allowed-cities/page.tsx:
- Around line 32-36: A array hardcoded brazilianStates deve ser substituída pelo
valor único definido em Shared.Contracts; remova a constante local
brazilianStates e importe (ou recupere via API) a lista/export enum presente em
Shared.Contracts (por exemplo export const/enum de estados) e use esse símbolo
importado no componente/page.tsx para garantir consistência com o backend;
mantenha um fallback mínimo somente se a importação falhar, mas priorize
reutilizar o símbolo Shared.Contracts.
- Around line 240-242: The toggle Button should be disabled while the patch
mutation is pending to prevent rapid double clicks; update the Button (the one
rendering MapPin) to include a disabled prop checking patchMutation.isPending
(or patchMutation.isPending && patchMutation.targetId === city.id if you track
per-city mutations) and also add an early guard in handleToggleActive to return
immediately when patchMutation.isPending to avoid triggering another mutation
while one is in-flight.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/categories/page.tsx:
- Around line 187-189: Replace the hardcoded Portuguese status strings inside
the Badge (currently using category.isActive ? "Ativa" : "Inativa") with
centralized constants from your shared contracts; import the appropriate
enum/object from Shared.Contracts (or the shared module that exposes category
status labels) and use it like Badge variant={category.isActive ?
STATUS_LABELS.ACTIVE.variant : STATUS_LABELS.INACTIVE.variant} and
{category.isActive ? STATUS_LABELS.ACTIVE.label : STATUS_LABELS.INACTIVE.label},
ensuring you reference the shared symbol names instead of literals and update
any types/props if needed.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/providers/[id]/page.tsx:
- Around line 181-193: The map callback currently types `doc` as any and
`service` elsewhere uses any—replace those anys with concrete types by importing
the API-generated types (or defining local interfaces) that describe a
ProviderDocument (fields: documentType, fileName, verificationStatus) and the
Service shape; update the map signature in the provider.documents.map to use
that ProviderDocument type and change the `service` variable declaration to the
imported/defined Service type so you regain type-safety and IDE autocomplete
(adjust usages of getVerificationBadgeVariant and verificationStatusLabels if
their typings need the concrete enum/type).
In
`@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/settings/settings-client.tsx:
- Around line 58-64: The current save flow sets setIsSaving(true) then awaits
the mock persistence and sets setIsSaving(false) afterward, which can leak if
the await throws; wrap the async persistence block in a try/finally so
setIsSaving(false) always runs: in the save handler that calls setIsSaving(true)
and awaits the fake backend, wrap the await/new Promise and subsequent toast and
password reset logic in a try (or try/catch if you want to handle errors) and
place setIsSaving(false) in the finally block; reference the save handler where
setIsSaving, activeTab, and setPasswords are used to locate the change.
- Around line 36-42: The current useEffect only calls applyTheme when a saved
localStorage value exists, so when no "meajudaai-theme" is present the state
becomes "system" but applyTheme is never invoked; update the useEffect in
settings-client.tsx to always call applyTheme with the resolved theme (use the
saved value if it is "light"|"dark"|"system", otherwise default to "system"),
and ensure you still call setTheme(resolved) and then applyTheme(resolved) so
the theme is applied even on the fallback path; reference the useEffect,
setTheme and applyTheme symbols and the "meajudaai-theme" localStorage key.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsx`:
- Around line 69-74: The JSX recomputes session.user.name.trim() and the
fallback logic in multiple places; extract a local const (e.g., name =
session?.user?.name || "Admin"; nameTrimmed = name.trim(); firstInitial =
nameTrimmed.charAt(0).toUpperCase()) at the top of the sidebar component and
replace the repeated expressions in the avatar, the display <p> and the roles
check (session?.user?.roles?.includes("admin")) to use these locals so trimming
and fallback are performed once and maintenance is simplified.
- Around line 20-27: The sidebar component currently hardcodes navigation labels
and the "admin" role; replace these literals by importing the shared
constants/enums from Shared.Contracts (or the admin shared constants) and
reference them in the nav items array (the list containing items like { href:
"/dashboard", label: "Dashboard", icon: LayoutDashboard }) and anywhere "admin"
is used (including the occurrence around line 74). Update the nav definition to
use the imported label constants and role enum values so labels and roles are
sourced from Shared.Contracts, leaving icons/hrefs intact.
- Line 34: The aside element with className "fixed left-0 top-0 z-40 flex
h-screen w-64 flex-col border-r border-border bg-surface" will overlap content
on small screens; make the sidebar responsive by hiding it on small viewports
and showing a drawer/toggle instead: replace the always-fixed behavior with
responsive classes (e.g., use "hidden md:flex" on the aside) and implement a
mobile drawer/toggle controlled by a boolean state (e.g., isOpen) in the Sidebar
component so small screens show a hamburger button that opens a slide-in panel;
ensure the desktop layout keeps the fixed w-64 sidebar for md+ breakpoints and
that the drawer reuses the same sidebar markup for consistency.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 02328213-836e-4cc9-85c5-20ab2b6bf489
📒 Files selected for processing (18)
src/Web/MeAjudaAi.Web.Admin-React/.stylelintrc.jsonsrc/Web/MeAjudaAi.Web.Admin-React/index.d.tssrc/Web/MeAjudaAi.Web.Admin-React/postcss.config.jssrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/documents/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/[id]/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/global.csssrc/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/providers/theme-provider.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/badge.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/ui/dialog.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.tssrc/Web/MeAjudaAi.Web.Admin-React/src/lib/types.tssrc/Web/MeAjudaAi.Web.Admin-React/src/lib/utils.tssrc/Web/MeAjudaAi.Web.Admin-React/tsconfig.tsbuildinfo
✅ Files skipped from review due to trivial changes (6)
- src/Web/MeAjudaAi.Web.Admin-React/.stylelintrc.json
- src/Web/MeAjudaAi.Web.Admin-React/postcss.config.js
- src/Web/MeAjudaAi.Web.Admin-React/index.d.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/app/global.css
- src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/badge.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/lib/types.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- src/Web/MeAjudaAi.Web.Admin-React/src/components/providers/theme-provider.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/documents/page.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/components/ui/dialog.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/page.tsx
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsx
Outdated
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsx
Outdated
Show resolved
Hide resolved
…d management pages for categories, providers, cities, and settings.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts (1)
44-52: Considere usar type guard para melhorar a segurança de tipos.A função
normalizeCategoriesResponseusa"data" in (data as object)que pode ser melhorada com um type guard mais explícito.♻️ Sugestão de refatoração
+function isWrappedResponse(data: unknown): data is { data?: ServiceCategoryDto[] } { + return typeof data === "object" && data !== null && "data" in data; +} + function normalizeCategoriesResponse(data: unknown): ServiceCategoryDto[] { if (!data) return []; if (Array.isArray(data)) return data as ServiceCategoryDto[]; - if ("data" in (data as object)) { - const d = data as { data?: ServiceCategoryDto[] }; - return d.data ?? []; - } + if (isWrappedResponse(data)) { + return data.data ?? []; + } return []; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts` around lines 44 - 52, Replace the loose `"data" in (data as object)` check in normalizeCategoriesResponse with a proper type guard: create a function like isDataPayload(obj: unknown): obj is { data?: ServiceCategoryDto[] } that checks obj is non-null, typeof obj === "object" and "data" in obj, then use that guard in normalizeCategoriesResponse to safely narrow the type before accessing d.data and remove the unchecked casts.src/Web/MeAjudaAi.Web.Admin-React/src/lib/types.ts (1)
17-20: Considere derivar tipos numéricos do schema gerado para evitar divergência.Os tipos
ProviderType,ProviderStatus,VerificationStatuseProviderTiersão definidos manualmente e podem divergir do schema Zod gerado (zod.gen.ts) caso o backend adicione novos valores de enum.Uma alternativa seria extrair os tipos do schema gerado usando
z.infer:♻️ Sugestão de refatoração
import { z } from "zod"; import { zMeAjudaAiModulesProvidersApplicationDtosProviderDto } from "./api/generated/zod.gen"; // Derivar tipos do schema gerado export type ProviderType = z.infer<typeof zMeAjudaAiModulesProvidersApplicationDtosProviderDto>["type"]; export type ProviderStatus = z.infer<typeof zMeAjudaAiModulesProvidersApplicationDtosProviderDto>["status"]; // ...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.Admin-React/src/lib/types.ts` around lines 17 - 20, The numeric union types ProviderType, ProviderStatus, VerificationStatus and ProviderTier are manually declared and can drift from the generated Zod schema; replace them by deriving from the generated schema using z.infer on zMeAjudaAiModulesProvidersApplicationDtosProviderDto (and/or the appropriate generated DTO) so each exported type (ProviderType, ProviderStatus, VerificationStatus, ProviderTier) references the corresponding property from z.infer<typeof zMeAjudaAiModulesProvidersApplicationDtosProviderDto>[...]; import z from "zod" and the generated schema symbol (zMeAjudaAiModulesProvidersApplicationDtosProviderDto) and export the inferred property types instead of the hardcoded unions to keep them in sync with backend enums.
🤖 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.Admin-React/src/app/`(admin)/settings/settings-client.tsx:
- Around line 244-250: The theme selector buttons (the button that calls
handleThemeChange("light") and the other two "Escuro"/"Sistema" buttons) aren't
accessible: add type="button" to each <button>, set aria-pressed={theme ===
"<name>"} (e.g., aria-pressed={theme === "light"} on the light button) so screen
readers know which is active, and replace the presentational <div> children with
<span aria-hidden="true"> swatches so they are ignored by assistive tech; apply
the same changes to the other two buttons and keep the existing
className/onclick logic (identify via handleThemeChange and theme).
- Around line 153-157: The label for the checkbox with id "notifNewProviders"
contains block-level <p> elements which is invalid HTML; change the label's
contents to only phrasing content (for example replace the <p> elements with
<span className="block"> for the title and description, or move the descriptive
<p> outside the <label> and connect it via aria-describedby pointing to the
input), ensure the label still references the checkbox by
htmlFor="notifNewProviders", and apply the same fix to the other two checkbox
controls in this component so no label nests block elements.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts`:
- Around line 87-89: O campo isActive está sendo enviado no payload (veja
use-categories.ts onde monta o objeto com isActive: input.isActive ?? true), mas
os DTOs de request do backend (CreateServiceCatalogCategoryRequestDto,
UpdateServiceCatalogCategoryRequestDto) não o aceitam; remova isActive do objeto
enviado nas funções de criação/atualização em use-categories.ts ou,
alternativamente, adicione suporte no backend incluindo isActive nesses DTOs e
aplicando a lógica de persistência correspondente no controller/service;
localize todas referências a input.isActive e elimine-as do payloads de request
se optar por remover do frontend.
---
Nitpick comments:
In `@src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts`:
- Around line 44-52: Replace the loose `"data" in (data as object)` check in
normalizeCategoriesResponse with a proper type guard: create a function like
isDataPayload(obj: unknown): obj is { data?: ServiceCategoryDto[] } that checks
obj is non-null, typeof obj === "object" and "data" in obj, then use that guard
in normalizeCategoriesResponse to safely narrow the type before accessing d.data
and remove the unchecked casts.
In `@src/Web/MeAjudaAi.Web.Admin-React/src/lib/types.ts`:
- Around line 17-20: The numeric union types ProviderType, ProviderStatus,
VerificationStatus and ProviderTier are manually declared and can drift from the
generated Zod schema; replace them by deriving from the generated schema using
z.infer on zMeAjudaAiModulesProvidersApplicationDtosProviderDto (and/or the
appropriate generated DTO) so each exported type (ProviderType, ProviderStatus,
VerificationStatus, ProviderTier) references the corresponding property from
z.infer<typeof zMeAjudaAiModulesProvidersApplicationDtosProviderDto>[...];
import z from "zod" and the generated schema symbol
(zMeAjudaAiModulesProvidersApplicationDtosProviderDto) and export the inferred
property types instead of the hardcoded unions to keep them in sync with backend
enums.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6d41e401-ad00-493a-9cd9-92c563728694
📒 Files selected for processing (7)
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/providers/[id]/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/components/layout/sidebar.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.tssrc/Web/MeAjudaAi.Web.Admin-React/src/lib/types.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsx
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/allowed-cities/page.tsx
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsx
Show resolved
Hide resolved
src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsx (1)
33-34:⚠️ Potential issue | 🟠 MajorO toggle “Categoria Ativa” não tem efeito persistido.
O formulário captura
isActive, mas esse valor não é enviado nas mutations de criar/editar. Hoje o usuário altera o checkbox e nada muda no backend.💡 Ajuste sugerido (opção frontend imediata)
const categorySchema = z.object({ name: z.string().min(2, "Nome deve ter pelo menos 2 caracteres").max(100, "Nome deve ter no máximo 100 caracteres"), description: z.string().max(500, "Descrição deve ter no máximo 500 caracteres").optional(), - isActive: z.boolean(), }); @@ const createForm = useForm<CategoryFormData>({ resolver: zodResolver(categorySchema), - defaultValues: { name: "", description: "", isActive: true }, + defaultValues: { name: "", description: "" }, }); @@ const editForm = useForm<CategoryFormData>({ resolver: zodResolver(categorySchema), - defaultValues: { name: "", description: "", isActive: true }, + defaultValues: { name: "", description: "" }, }); @@ - createForm.reset({ name: "", description: "", isActive: true }); + createForm.reset({ name: "", description: "" }); @@ editForm.reset({ name: category.name ?? "", description: category.description ?? "", - isActive: category.isActive ?? true, });Also applies to: 86-91, 102-106, 243-251, 290-298
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/categories/page.tsx around lines 33 - 34, The form schema includes isActive but that value is never sent to the backend; update the create/edit mutation payloads to include the isActive field from the form state. Locate the form submit handlers (e.g., the functions that call createCategoryMutation and updateCategoryMutation / handlers like handleCreate or handleEdit) and add isActive to the data object sent to those mutations (and ensure defaultValues/controlled input for isActive are wired to the form if missing). Verify the checkbox input bound to isActive is reading/updating the form (e.g., via register or controller) so the submitted payload carries the current isActive boolean to the API. Ensure any optimistic UI or cache updates also reflect the isActive value.
🤖 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.Admin-React/src/hooks/admin/use-categories.ts`:
- Around line 103-107: The update payload in use-categories.ts currently forces
displayOrder to 0 when input.displayOrder is missing, which can unintentionally
reset ordering; change the body construction so displayOrder is only included
when provided (e.g., build body = { name: input.name, description:
input.description ?? null } and conditionally assign body.displayOrder =
input.displayOrder when input.displayOrder is not undefined or when
'displayOrder' in input) so partial updates don't overwrite existing display
order; locate the body object creation inside the update function in
use-categories.ts and apply the conditional inclusion for displayOrder.
---
Duplicate comments:
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/categories/page.tsx:
- Around line 33-34: The form schema includes isActive but that value is never
sent to the backend; update the create/edit mutation payloads to include the
isActive field from the form state. Locate the form submit handlers (e.g., the
functions that call createCategoryMutation and updateCategoryMutation / handlers
like handleCreate or handleEdit) and add isActive to the data object sent to
those mutations (and ensure defaultValues/controlled input for isActive are
wired to the form if missing). Verify the checkbox input bound to isActive is
reading/updating the form (e.g., via register or controller) so the submitted
payload carries the current isActive boolean to the API. Ensure any optimistic
UI or cache updates also reflect the isActive value.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5b24a078-257e-4767-9501-042811af572c
📒 Files selected for processing (4)
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.tssrc/Web/MeAjudaAi.Web.Admin-React/src/lib/types.ts
✅ Files skipped from review due to trivial changes (2)
- src/Web/MeAjudaAi.Web.Admin-React/src/lib/types.ts
- src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/settings/settings-client.tsx
src/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts
Outdated
Show resolved
Hide resolved
…d, update, and delete operations.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsx (2)
90-116: CampodisplayOrdernão exposto na UI.A API suporta
displayOrderpara ordenação de categorias, mas o formulário não oferece um campo para definir esse valor. Considere adicionar um campo numérico nos dialogs de criar/editar se a ordenação manual for um requisito.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/categories/page.tsx around lines 90 - 116, O formulário não expõe o campo displayOrder, então atualize o tipo CategoryFormData para incluir displayOrder: number | undefined, adicione um input numérico ao dialog de criação/edição vinculando-o ao estado/formik/react-hook-form utilizado, e inclua displayOrder nas chamadas de createMutation.mutateAsync (em handleSubmitCreate) e updateMutation.mutateAsync (em handleSubmitEdit) enviando o valor numérico (ou um default como 0/undefined se não preenchido); também adicione validação simples do valor (inteiro >= 0) antes de submeter para evitar envio inválido.
191-212: Adicione estado de loading e previna cliques duplicados no toggle de status.O handler inline do Badge não exibe estado de carregamento nem previne cliques múltiplos durante a mutação. Isso pode causar requisições duplicadas se o usuário clicar rapidamente.
♻️ Sugestão de melhoria
<Badge variant={category.isActive ? CATEGORY_STATUS_LABELS.ACTIVE.variant : CATEGORY_STATUS_LABELS.INACTIVE.variant} - className="cursor-pointer hover:opacity-80" + className={`cursor-pointer hover:opacity-80 ${ + (activateMutation.isPending || deactivateMutation.isPending) ? "pointer-events-none opacity-50" : "" + }`} onClick={async () => { + if (activateMutation.isPending || deactivateMutation.isPending) return; if (!category.id) return; try { if (category.isActive) { await deactivateMutation.mutateAsync(category.id); toast.success("Categoria desativada"); } else { await activateMutation.mutateAsync(category.id); toast.success("Categoria ativada"); } } catch { toast.error("Erro ao alterar status"); } }} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/categories/page.tsx around lines 191 - 212, The inline onClick handler on the Badge (rendering category.isActive) allows duplicate clicks and has no loading state; add a per-row loading guard (e.g., local state like isToggling or derive from per-category mutation status) and check it at the start of the handler to short-circuit duplicate clicks, set it true before calling deactivateMutation.mutateAsync/activateMutation.mutateAsync and reset it in finally, and pass the loading/disabled state to the Badge (and optionally render a spinner or change className) so the button is disabled/visually indicates loading while the mutation for that category id is in-flight.
🤖 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.Admin-React/src/app/`(admin)/categories/page.tsx:
- Around line 264-272: Remova o checkbox "isActive" dos formulários de
criar/editar (remover o JSX que usa createForm.register("isActive") nos dois
lugares) e limpe o schema/defaults que referenciam isActive: elimine a
propriedade isActive do Zod schema de categoria (ex.: categorySchema) e remova
qualquer isActive em defaultValues/initialValues ao instanciar createForm;
alternativa mínima: em vez de remover, torne o input disabled e adicione um
tooltip explicando que o status é alterado pelo toggle na listagem, mas a
solução recomendada é a remoção completa para evitar valores coletados que não
são enviados à API.
---
Nitpick comments:
In `@src/Web/MeAjudaAi.Web.Admin-React/src/app/`(admin)/categories/page.tsx:
- Around line 90-116: O formulário não expõe o campo displayOrder, então
atualize o tipo CategoryFormData para incluir displayOrder: number | undefined,
adicione um input numérico ao dialog de criação/edição vinculando-o ao
estado/formik/react-hook-form utilizado, e inclua displayOrder nas chamadas de
createMutation.mutateAsync (em handleSubmitCreate) e updateMutation.mutateAsync
(em handleSubmitEdit) enviando o valor numérico (ou um default como 0/undefined
se não preenchido); também adicione validação simples do valor (inteiro >= 0)
antes de submeter para evitar envio inválido.
- Around line 191-212: The inline onClick handler on the Badge (rendering
category.isActive) allows duplicate clicks and has no loading state; add a
per-row loading guard (e.g., local state like isToggling or derive from
per-category mutation status) and check it at the start of the handler to
short-circuit duplicate clicks, set it true before calling
deactivateMutation.mutateAsync/activateMutation.mutateAsync and reset it in
finally, and pass the loading/disabled state to the Badge (and optionally render
a spinner or change className) so the button is disabled/visually indicates
loading while the mutation for that category id is in-flight.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: efe0433c-5e09-47ee-90c4-40e8ea7cde41
📒 Files selected for processing (2)
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsxsrc/Web/MeAjudaAi.Web.Admin-React/src/hooks/admin/use-categories.ts
src/Web/MeAjudaAi.Web.Admin-React/src/app/(admin)/categories/page.tsx
Outdated
Show resolved
Hide resolved
…ions and status toggling.
Summary
MeAjudaAi.Web.Admin-React/)Goal
Migrate Admin Portal from Blazor WASM to React/Next.js while keeping the original Blazor project intact until the React version is complete and validated.
Tech Stack
Notes
MeAjudaAi.Web.Admin/(Blazor) is preservedMeAjudaAi.Web.Admin-React/Summary by CodeRabbit
New Features
Documentation
Chores