Migrate admin-frontend to shadcn/ui, add dark mode, and translate UI to pt-BR - #23
Conversation
…to pt-BR Replaces the hand-rolled component layer with shadcn/ui (Radix primitives + Tailwind, CLI-generated, kept close to registry output per project convention) and resets the theme to the stock "Nova"/neutral palette. Adds a full light/dark theme system (OS-aware default, persisted override) and makes AdminLayout responsive (collapsible desktop sidebar, off-canvas mobile drawer). Reworks the Tags vertical's list/form pattern as the reference implementation for every future feature vertical: a Table instead of stacked Cards, and a single Dialog modal for create/edit instead of an inline form. Translates all user-facing text (labels, messages, aria-labels, confirm prompts) to Brazilian Portuguese per explicit project requirement, including reachable domain/error messages. Updates CLAUDE.md, the admin-feature-vertical skill, DECISIONS.md, and adds ADR 005 so the new conventions (shadcn/ui, Table+Dialog, pt-BR text) apply automatically to the next feature verticals. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 20 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (30)
📝 WalkthroughWalkthroughThe admin frontend adds a shadcn/ui-based component system, semantic light/dark theming, responsive navigation, Portuguese localization, shared page components, and a table-based Tags CRUD flow using one create/edit dialog. ChangesAdmin frontend UI refresh
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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 |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (3)
apps/admin-frontend/src/presentation/components/StatusMessage.tsx (1)
10-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winGuard the indexed access on
TONE_CLASSESfornoUncheckedIndexedAccesscompliance.With
noUncheckedIndexedAccess: true,TONE_CLASSES[tone]is typedstring | undefined. The coding guidelines require guarding indexed-access results before use. Add a fallback to prevent a potential"text-sm undefined"class string.As per coding guidelines: "With
noUncheckedIndexedAccess: true, always guard indexed-access results before using them."♻️ Proposed fix
- return <p className={`text-sm ${TONE_CLASSES[tone]}`}>{children}</p> + return <p className={`text-sm ${TONE_CLASSES[tone] ?? ''}`}>{children}</p>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/admin-frontend/src/presentation/components/StatusMessage.tsx` around lines 10 - 16, Guard the indexed lookup in StatusMessage before constructing the className: assign TONE_CLASSES[tone] to a local variable and provide a safe fallback (such as an empty string or muted tone class) when it is undefined, then use that guarded value in the returned paragraph.Source: Coding guidelines
apps/admin-frontend/src/components/ui/spinner.tsx (1)
1-4: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAdd
import * as React from 'react'for consistency with sibling UI components.
React.ComponentProps<'svg'>on line 4 references theReactnamespace without an explicit import. While this resolves via the UMD global from@types/react, every other component incomponents/ui/explicitly imports React. Adding the import keeps the directory consistent and avoids confusion.♻️ Proposed fix
+import * as React from 'react' import { cn } from '`@/lib/utils`' import { Loader2Icon } from 'lucide-react'🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/admin-frontend/src/components/ui/spinner.tsx` around lines 1 - 4, Add an explicit `import * as React from 'react'` alongside the existing imports in the `Spinner` component file so its `React.ComponentProps<'svg'>` type reference follows the convention used by sibling UI components.apps/admin-frontend/src/presentation/pages/CallbackPage/CallbackPage.tsx (1)
38-43: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winUse
<Link>instead of a raw<a>for the login redirect.The raw anchor causes a full page reload. React Router's
<Link to="/login">enables client-side navigation and preserves SPA behavior. The existing test (getByRole('link')+toHaveAttribute('href', '/login')) will still pass since<Link>renders an<a>.♻️ Proposed refactor
-import { useNavigate } from 'react-router' +import { useNavigate, Link } from 'react-router'- <a - href="/login" - className="mt-6 inline-block text-sm font-medium text-primary hover:underline" - > + <Link + to="/login" + className="mt-6 inline-block text-sm font-medium text-primary hover:underline" + > Voltar para o login - </a> + </Link>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/admin-frontend/src/presentation/pages/CallbackPage/CallbackPage.tsx` around lines 38 - 43, Replace the raw anchor in CallbackPage with React Router’s Link component, importing it from the project’s router dependency and changing href="/login" to to="/login" while preserving the existing className and label.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/admin-frontend/package.json`:
- Line 28: Move the shadcn package entry from dependencies to devDependencies in
the package manifest, preserving its version and removing the duplicate
dependency entry.
In `@apps/admin-frontend/src/components/ui/dialog.tsx`:
- Around line 79-104: Remove the non-standard showCloseButton prop and its
conditional DialogPrimitive.Close rendering from DialogFooter, restoring it to
the generated shadcn/ui styled div API. Move any required close-button rendering
into each consuming form or dialog component.
In `@apps/admin-frontend/src/presentation/layouts/AdminLayout.test.tsx`:
- Around line 110-117: The mobile sidebar test in “opens and closes the mobile
sidebar drawer” only checks a link that remains in the DOM regardless of drawer
state. Assert the drawer’s backdrop is present after clicking “abrir menu” and
absent after clicking the “Painel” link, using the drawer/backdrop’s accessible
role or identifying selector.
In `@apps/admin-frontend/src/presentation/layouts/AdminLayout.tsx`:
- Around line 68-176: Replace the hand-rolled mobile drawer in AdminLayout with
the shadcn/ui Sheet component, preserving the existing navigation, collapsed
desktop sidebar, backdrop behavior, Escape handling, and mobile open state. Use
SheetTrigger for the “Abrir menu” control and SheetContent for the mobile
navigation so focus trapping, focus restoration, and dialog ARIA semantics are
handled automatically; keep the desktop aside separate and ensure mobile
navigation closes through the Sheet state and NavLink actions.
In `@apps/admin-frontend/src/presentation/pages/TagsPage/TagsPage.tsx`:
- Around line 84-89: Update handleDelete to wrap deleteTag(id) in try/catch,
including any subsequent refetch failure, and report the error through the
page-level error state used by handleSubmit via messageFrom. Render that error
state below PageHeader so deletion failures provide user feedback and do not
produce unhandled promise rejections.
In `@apps/admin-frontend/src/presentation/providers/ThemeProvider.tsx`:
- Around line 31-45: OS theme changes can override a user-selected theme because
the listener in the mount-only useEffect remains active after setTheme persists
a preference. Update handleChange to re-check localStorage.getItem(STORAGE_KEY)
and return without calling setThemeState when an explicit value exists;
otherwise continue applying the media query preference.
---
Nitpick comments:
In `@apps/admin-frontend/src/components/ui/spinner.tsx`:
- Around line 1-4: Add an explicit `import * as React from 'react'` alongside
the existing imports in the `Spinner` component file so its
`React.ComponentProps<'svg'>` type reference follows the convention used by
sibling UI components.
In `@apps/admin-frontend/src/presentation/components/StatusMessage.tsx`:
- Around line 10-16: Guard the indexed lookup in StatusMessage before
constructing the className: assign TONE_CLASSES[tone] to a local variable and
provide a safe fallback (such as an empty string or muted tone class) when it is
undefined, then use that guarded value in the returned paragraph.
In `@apps/admin-frontend/src/presentation/pages/CallbackPage/CallbackPage.tsx`:
- Around line 38-43: Replace the raw anchor in CallbackPage with React Router’s
Link component, importing it from the project’s router dependency and changing
href="/login" to to="/login" while preserving the existing className and label.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 21285447-6909-41dd-8354-53bf11feaabb
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (57)
.claude/launch.jsonapps/admin-frontend/.skills/admin-feature-vertical/SKILL.mdapps/admin-frontend/CLAUDE.mdapps/admin-frontend/components.jsonapps/admin-frontend/docs/DECISIONS.mdapps/admin-frontend/docs/STATUS.mdapps/admin-frontend/docs/adr/005-shadcn-ui-component-library.mdapps/admin-frontend/package.jsonapps/admin-frontend/src/components/ui/button.tsxapps/admin-frontend/src/components/ui/card.tsxapps/admin-frontend/src/components/ui/dialog.tsxapps/admin-frontend/src/components/ui/input.tsxapps/admin-frontend/src/components/ui/label.tsxapps/admin-frontend/src/components/ui/spinner.tsxapps/admin-frontend/src/components/ui/table.tsxapps/admin-frontend/src/components/ui/textarea.tsxapps/admin-frontend/src/domain/entities/Tag.tsapps/admin-frontend/src/index.cssapps/admin-frontend/src/infrastructure/http/UnauthenticatedError.tsapps/admin-frontend/src/lib/utils.tsapps/admin-frontend/src/presentation/components/CenteredScreen.tsxapps/admin-frontend/src/presentation/components/FullScreenSpinner.tsxapps/admin-frontend/src/presentation/components/PageHeader.tsxapps/admin-frontend/src/presentation/components/PlaceholderPage.test.tsxapps/admin-frontend/src/presentation/components/PlaceholderPage.tsxapps/admin-frontend/src/presentation/components/StatusMessage.tsxapps/admin-frontend/src/presentation/components/TextAreaField.tsxapps/admin-frontend/src/presentation/components/TextField.test.tsxapps/admin-frontend/src/presentation/components/TextField.tsxapps/admin-frontend/src/presentation/components/ThemeToggle.tsxapps/admin-frontend/src/presentation/hooks/useTags.tsapps/admin-frontend/src/presentation/hooks/useTheme.tsapps/admin-frontend/src/presentation/layouts/AdminLayout.test.tsxapps/admin-frontend/src/presentation/layouts/AdminLayout.tsxapps/admin-frontend/src/presentation/pages/AppointmentsPage/AppointmentsPage.tsxapps/admin-frontend/src/presentation/pages/CallbackPage/CallbackPage.test.tsxapps/admin-frontend/src/presentation/pages/CallbackPage/CallbackPage.tsxapps/admin-frontend/src/presentation/pages/ClientsPage/ClientsPage.tsxapps/admin-frontend/src/presentation/pages/DashboardPage/DashboardPage.tsxapps/admin-frontend/src/presentation/pages/InboxPage/InboxPage.tsxapps/admin-frontend/src/presentation/pages/LoginPage/LoginPage.test.tsxapps/admin-frontend/src/presentation/pages/LoginPage/LoginPage.tsxapps/admin-frontend/src/presentation/pages/ServicesPage/ServicesPage.tsxapps/admin-frontend/src/presentation/pages/SettingsPage/SettingsPage.tsxapps/admin-frontend/src/presentation/pages/TagsPage/TagForm.tsxapps/admin-frontend/src/presentation/pages/TagsPage/TagsPage.test.tsxapps/admin-frontend/src/presentation/pages/TagsPage/TagsPage.tsxapps/admin-frontend/src/presentation/providers/AppProviders.tsxapps/admin-frontend/src/presentation/providers/ThemeContext.tsapps/admin-frontend/src/presentation/providers/ThemeProvider.test.tsxapps/admin-frontend/src/presentation/providers/ThemeProvider.tsxapps/admin-frontend/src/presentation/routes/ProtectedRoute.tsxapps/admin-frontend/src/test/setup.tsapps/admin-frontend/tsconfig.app.jsonapps/admin-frontend/tsconfig.jsonapps/admin-frontend/vite.config.tsapps/admin-frontend/vitest.config.ts
… feedback Fixes found via adversarial testing of the Tags screen: duplicate-tag error message was untranslated (backend-owned text), the submit button stayed enabled with a blank name, and a stale "Editar etiqueta" dialog flashed empty after creating a tag (Dialog title/content were derived from state that clears before the close animation finishes). Extends the pt-BR requirement to the backend: every FluentValidation rule now has an explicit Portuguese message (most were silently falling back to FluentValidation's English defaults), plus all Result/domain exception messages reachable through the API (Tags, ServiceOfferings, tenant provisioning, the shared generic-exception-handler fallback). Addresses CodeRabbit review feedback on PR #23: - shadcn moved to devDependencies (dev-only CLI tool) - ThemeProvider's OS-theme listener now re-checks localStorage inside its handler, not just at effect setup, so it can't override an explicit choice made after mount - handleDelete now catches and surfaces errors instead of leaving an unhandled rejection - CallbackPage uses react-router's Link instead of a raw <a> - AdminLayout's mobile drawer now uses shadcn's Sheet (Radix Dialog) instead of a hand-rolled backdrop, gaining focus trap, focus restoration, Escape-to-close, and aria-modal semantics for free Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Swaps the Tags delete flow's native window.confirm for shadcn/ui's AlertDialog: styled consistently with the rest of the app, translatable, and testable with normal RTL queries (window.confirm blocked automated testing of the delete flow entirely). AlertDialogAction closes itself by default on click, so onClick calls event.preventDefault() to keep the dialog open until the delete request actually resolves — a failure now shows inline instead of silently closing. Documents the pattern in the skill/DECISIONS.md so future delete flows (Services, Clients, etc.) follow it by default. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Migrate admin-frontend to shadcn/ui, add dark mode, and translate UI to pt-BR Replaces the hand-rolled component layer with shadcn/ui (Radix primitives + Tailwind, CLI-generated, kept close to registry output per project convention) and resets the theme to the stock "Nova"/neutral palette. Adds a full light/dark theme system (OS-aware default, persisted override) and makes AdminLayout responsive (collapsible desktop sidebar, off-canvas mobile drawer). Reworks the Tags vertical's list/form pattern as the reference implementation for every future feature vertical: a Table instead of stacked Cards, and a single Dialog modal for create/edit instead of an inline form. Translates all user-facing text (labels, messages, aria-labels, confirm prompts) to Brazilian Portuguese per explicit project requirement, including reachable domain/error messages. Updates CLAUDE.md, the admin-feature-vertical skill, DECISIONS.md, and adds ADR 005 so the new conventions (shadcn/ui, Table+Dialog, pt-BR text) apply automatically to the next feature verticals. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Fix Tags UX bugs, translate backend messages to pt-BR, address review feedback Fixes found via adversarial testing of the Tags screen: duplicate-tag error message was untranslated (backend-owned text), the submit button stayed enabled with a blank name, and a stale "Editar etiqueta" dialog flashed empty after creating a tag (Dialog title/content were derived from state that clears before the close animation finishes). Extends the pt-BR requirement to the backend: every FluentValidation rule now has an explicit Portuguese message (most were silently falling back to FluentValidation's English defaults), plus all Result/domain exception messages reachable through the API (Tags, ServiceOfferings, tenant provisioning, the shared generic-exception-handler fallback). Addresses CodeRabbit review feedback on PR #23: - shadcn moved to devDependencies (dev-only CLI tool) - ThemeProvider's OS-theme listener now re-checks localStorage inside its handler, not just at effect setup, so it can't override an explicit choice made after mount - handleDelete now catches and surfaces errors instead of leaving an unhandled rejection - CallbackPage uses react-router's Link instead of a raw <a> - AdminLayout's mobile drawer now uses shadcn's Sheet (Radix Dialog) instead of a hand-rolled backdrop, gaining focus trap, focus restoration, Escape-to-close, and aria-modal semantics for free Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Replace window.confirm with shadcn's AlertDialog for delete confirmation Swaps the Tags delete flow's native window.confirm for shadcn/ui's AlertDialog: styled consistently with the rest of the app, translatable, and testable with normal RTL queries (window.confirm blocked automated testing of the delete flow entirely). AlertDialogAction closes itself by default on click, so onClick calls event.preventDefault() to keep the dialog open until the delete request actually resolves — a failure now shows inline instead of silently closing. Documents the pattern in the skill/DECISIONS.md so future delete flows (Services, Clients, etc.) follow it by default. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Summary
neutralpalette; confirmed via CLI experimentation (Vega preset) that this verbosity is shared across every shadcn preset, not something worth fighting.ThemeProvider/useTheme/ThemeToggle): defaults to OS preference, persists an explicit override.AdminLayoutresponsive: collapsible desktop icon-rail sidebar (persisted) + off-canvas mobile drawer belowmd.Table(not stackedCards), create/edit is a singleDialogmodal (not inline/routed).aria-labels,window.confirmprompts, and reachable domain/error messages — to Brazilian Portuguese (pt-BR), per explicit project requirement. Code, comments, and docs stay in English.CLAUDE.md, theadmin-feature-verticalskill,docs/DECISIONS.md,docs/STATUS.md, and addsdocs/adr/005-shadcn-ui-component-library.mdso these conventions apply automatically when Services/Clients/Appointments/Inbox/Dashboard/Settings get built.Test plan
npm run build— cleannpm run lint— 0 errors (32 pre-existing warnings, all in unmodified shadcn-generated files)npm run test— 116/116 passing🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Tests