Skip to content

fix(aluno-ui): 10 UI fixes — a11y, UX, brand, design sync#194

Merged
EmiyaKiritsugu3 merged 22 commits into
mainfrom
feat/aluno-ui-10-fixes
Jul 8, 2026
Merged

fix(aluno-ui): 10 UI fixes — a11y, UX, brand, design sync#194
EmiyaKiritsugu3 merged 22 commits into
mainfrom
feat/aluno-ui-10-fixes

Conversation

@EmiyaKiritsugu3

@EmiyaKiritsugu3 EmiyaKiritsugu3 commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Changes

10 fixes from audited PRD-UI-Aluno-Aprimoramento (25→10 after code verification)

Phase 0 — Quick wins

  • Fix aria-label="Perfil do usuario""Menu do aluno" (typo)
  • Dashboard achievements justify-aroundjustify-center
  • Landing page brand: "Academia Five Star""Five Star Gym"

Phase 1a — A11y fundamentals

  • CardTitle <div><h3> semantic heading (affects all pages)
  • Login inputs: autoComplete, type="email", inputMode="email"
  • Auth errors inline role="alert" (replaces disappearing toast)
  • CircularProgress SVG: viewBox + aria-hidden

Phase 1b — UX interactions

  • Card-treino: active:scale-[.98] + focus-visible:ring-2 press feedback
  • Meus-treinos: exercise names truncated below count
  • Enable Perfil dropdown → /aluno/perfil stub route

Phase 2 — Design sync

  • DESIGN.md: document Inter (body) + Outfit (headline) split
  • DESIGN.md: buttons rounded-fullrounded-md (match code)
  • DESIGN.md: remove stale SF Pro reference

Cleanup

  • Remove .claude/workflows/aluno-ui-premium-exec-wf.js (stale)
  • Remove old audit plan doc
  • New execution plan: docs/superpowers/plans/2026-07-07-aluno-ui-10-fixes.md

QA

  • Typecheck: 0 errors
  • Lint: 0 errors
  • No new dependencies

Summary by cubic

Fixes 10 aluno UI issues across a11y, UX, and brand, adds Google/GitHub/Apple and magic‑link sign‑in with a secure callback, and completes the workout flow with history saving and an immediate dashboard refresh. Also hardens auth redirects with a tiered callback URL fallback and Supabase redirect allowlists so login works in local, preview, and production.

  • New Features

    • Auth: /auth/callback built with @supabase/ssr; server actions for magic link + Google/GitHub/Apple; social buttons on /aluno/login and /login; OAuth actions return { url | error }.
    • Workouts: “Iniciar Treino” opens WorkoutSession; finishing saves history and calls router.refresh to update XP/nível/streak on the dashboard.
  • Bug Fixes

    • A11y/UX/brand: CardTitle is <h3>; login inputs add type/autoComplete; inline auth errors use role="alert"; CircularProgress adds viewBox/aria-hidden; treino cards add press/focus feedback; exercise names show under counts; achievements centered; header aria-label is "Menu do aluno"; landing uses "Five Star Gym"; Perfil menu routes to /aluno/perfil.
    • Auth reliability: shared validateNext blocks open redirects (path traversal, protocol‑relative, sibling‑prefix spoofing) with safe fallbacks; callback URL resolves via NEXT_PUBLIC_APP_URLNEXT_PUBLIC_VERCEL_URLhttp://localhost:3000; Supabase allowlist updated (site_url=localhost:3000, https://**.vercel.app/auth/callback, prod URL); /auth/callback bypassed by auth middleware; admin OAuth returns to /dashboard; masks “user already registered”; magic‑link loading no longer sticks; Sentry strips OAuth codes; .env.example documents preview/prod guidance; tests expanded for server actions, callback validation, and dashboard refresh.
    • E2E stability: anchored login button regex, robust password input selector, and h1‑scoped heading queries to avoid Playwright strict‑mode conflicts.

Written for commit f4fda4f. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added an “Aluno Perfil” page placeholder.
    • Dashboard now supports “Iniciar Treino” with a dedicated workout-session flow.
    • Login now includes Google, GitHub, Apple, and magic-link options.
  • Bug Fixes
    • Password login errors now show inline, accessible alerts; added password show/hide toggle.
    • Improved safe handling for authentication callback redirects and refined dashboard layout.
    • “Meus Treinos” now shows a concise exercise preview (or “0 exercícios” when empty).
  • Documentation
    • Added a Five Star Gym design-system guide (colors, typography, motion, components).
  • Tests
    • Expanded unit and e2e coverage for auth flows and the workout-session UI.

Q01: aluno-header aria-label 'Perfil do usuario' → 'Menu do aluno'
Q02: dashboard achievements justify-around → justify-center
Q03: landing page 'Academia Five Star' → 'Five Star Gym'
cleanup: remove stale .claude/workflows/ and old audit plan
A01: CardTitle <div> → <h3> semantic heading
A02: login email/password autocomplete + type attributes
A03: auth errors inline with role=alert (replaces toast)
A04: circular-progress SVG viewBox + aria-hidden
U01: card-treino press feedback (active:scale + focus-visible ring)
U02: meus-treinos exercise names truncated below count
U03: enable Perfil dropdown + /aluno/perfil stub page
…-md buttons

P01: align button spec (rounded-full → rounded-md matches code)
P02: document Inter/Outfit font split, remove stale SF Pro reference
- --font-headline: 'Outfit', 'Inter', sans-serif
- Typography table: add dedicated Headlines row (Outfit)
@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
smartmanagementsystem Ready Ready Preview, Comment Jul 8, 2026 11:56am

@ecc-tools

ecc-tools Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR adds shared auth callback and redirect safety, expands aluno login and dashboard flows, updates related UI copy and components, and adds design and planning documentation.

Changes

Aluno auth, login, dashboard, and design updates

Layer / File(s) Summary
Auth callback and sign-in actions
src/lib/constants.ts, src/lib/auth-redirect.ts, src/lib/actions/auth.ts, src/app/auth/callback/route.ts, src/utils/supabase/middleware.ts, src/app/auth/callback/route.test.ts, src/lib/actions/auth.test.ts
Adds the shared callback path and redirect validator, the Supabase OAuth and magic-link actions, the auth callback route, middleware bypass, and tests for the auth flow.
Aluno login page and tests
src/app/aluno/login/page.tsx, src/app/aluno/login/page.test.tsx, src/app/login/page.tsx
Adds inline auth errors, password visibility, OAuth and magic-link entry points, and updates the aluno login tests and public login page for the new flows.
Dashboard workout and profile navigation
src/app/aluno/dashboard/dashboard-client.tsx, src/components/dashboard/aluno/card-treino.tsx, src/app/aluno/aluno-header.tsx, src/app/aluno/perfil/page.tsx, src/app/aluno/aluno-header.test.tsx, src/app/aluno/dashboard/dashboard-client.test.tsx
Adds workout session start and finish handling, profile navigation, a perfil page, and the header and dashboard test router mocks.
Workout list, landing copy, and component polish
src/app/aluno/meus-treinos/meus-treinos-client.tsx, src/app/page.tsx, src/app/page.test.tsx, src/components/ui/card.tsx, src/components/ui/circular-progress.tsx
Shows exercise-name previews in workout cards, updates landing copy, and adjusts shared UI component markup and formatting.
Design and plan docs
DESIGN.md, docs/superpowers/plans/*, docs/CURRENT-STATE.md, .env.example
Adds the design system document, adds the Aluno UI fixes plan, updates the current-state log, and reorders the example app URL value.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is substantive but does not follow the repository template and is missing required sections like Type of Change and Checklist. Reformat the PR body to match the template and add the missing Type of Change, Related Documents, and Checklist sections.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the main aluno UI fix scope.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/aluno-ui-10-fixes

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 issues found across 12 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/app/aluno/meus-treinos/meus-treinos-client.tsx">

<violation number="1" location="src/app/aluno/meus-treinos/meus-treinos-client.tsx:184">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The newly added exercise-name list below the exercise count is user-visible behavior that lacks test coverage. `meus-treinos-client.test.tsx` already tests this component, so adding a test for the conditional exercise-name display (e.g., verifying names render, only up to 3 are shown, and the element is absent when there are no exercises) would be consistent with the existing shallow test style and would ensure the fix does not regress.</violation>
</file>

<file name="src/app/aluno/login/page.tsx">

<violation number="1" location="src/app/aluno/login/page.tsx:90">
P2: Error handling no longer calls `notify.error` — the inline `role="alert"` div replaces the toast on the error path. However, `notify.success` still fires on the success path. If there are other consuming code paths that depend on error notifications (e.g., global error tracking), the switch from `notify.error` to `setAuthError` drops that side effect. Make sure that no downstream analytics, logging, or Sentry capture relied on the previous `notify.error` call.</violation>
</file>

<file name="src/components/dashboard/aluno/card-treino.tsx">

<violation number="1" location="src/components/dashboard/aluno/card-treino.tsx:85">
P3: The intended keyboard focus feedback for exercise rows will not show because the new `focus-visible:*` classes are on a non-focusable `div`; keyboard focus lands on the nested `Checkbox` or info `Button` instead. Using `focus-within:*` on the row would make the row highlight when any child control receives keyboard focus.</violation>
</file>

<file name="src/app/aluno/aluno-header.tsx">

<violation number="1" location="src/app/aluno/aluno-header.tsx:91">
P2: The existing `aluno-header` unit tests now render `UserMenu` without a Next router context, because this menu item adds `useRouter()` to a component whose tests do not mock `next/navigation`. Using a regular `Link`/`asChild` menu item for `/aluno/perfil`, or adding the missing router mock in the test, would keep the profile navigation while avoiding a test-suite regression.</violation>
</file>

<file name="docs/superpowers/plans/2026-07-07-aluno-ui-10-fixes.md">

<violation number="1" location="docs/superpowers/plans/2026-07-07-aluno-ui-10-fixes.md:39">
P3: The execution plan points the button radius change in the opposite direction from this PR’s stated design sync and the new `DESIGN.md` table. As written, someone following the plan would change `button.tsx` from `rounded-md` to `rounded-full` even though the design doc now standardizes buttons on `rounded-md`; consider updating P01 to describe the doc/design alignment instead.</violation>

<violation number="2" location="docs/superpowers/plans/2026-07-07-aluno-ui-10-fixes.md:46">
P2: The plan’s gate command omits required repository gates, notably `npm run e2e` (and the README also lists `format:check`). Since this document is an execution plan for the UI fixes, the shortened command can let the change appear complete without running the documented merge checks; consider aligning it with the repo’s quality-gate list.</violation>
</file>

<file name="DESIGN.md">

<violation number="1" location="DESIGN.md:1">
P3: The new design-system document introduces `Aegis Fitness OS` as the product identity, which conflicts with the Five Star Gym branding this PR is standardizing. This looks like a stale/template name and could keep future UI copy and design guidance inconsistent; consider renaming the document heading/introduction to the project’s actual brand.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/app/aluno/meus-treinos/meus-treinos-client.tsx
Comment thread src/app/aluno/login/page.tsx
Comment thread src/app/aluno/aluno-header.tsx
Comment thread docs/superpowers/plans/2026-07-07-aluno-ui-10-fixes.md Outdated
Comment thread src/components/dashboard/aluno/card-treino.tsx Outdated
Comment thread docs/superpowers/plans/2026-07-07-aluno-ui-10-fixes.md Outdated
Comment thread DESIGN.md Outdated
…b/magic-link, middleware bypass

Wave 1: AUTH_CALLBACK_PATH constant, middleware excludes /auth/callback
Wave 2: signInWithMagicLink, signInWithGoogle, signInWithGitHub Server Actions
+ WorkoutSession integration on aluno dashboard
@ecc-tools

ecc-tools Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

/aluno/login: social buttons (inline SVGs) + magic link form + separators
/login: social buttons between form and footer
@ecc-tools

ecc-tools Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@ecc-tools

ecc-tools Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

Student + admin login pages now offer Google, GitHub, and Apple
All OAuth providers share the same architecture (Server Action → callback)
@ecc-tools

ecc-tools Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (5)
src/app/auth/callback/route.ts (2)

15-21: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate next-path validation vs. validateNext in src/lib/actions/auth.ts.

Same open-redirect allowlist logic (/dashboard, /aluno) is reimplemented inline here without the //-prefix guard present in auth.ts. Not currently exploitable (single-leading-slash paths stay same-origin via URL.pathname), but the divergence is a maintenance risk since this route is the actual public security boundary (reachable directly, not only via the server actions).

🤖 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 `@src/app/auth/callback/route.ts` around lines 15 - 21, The next-path allowlist
logic in the callback route is duplicated and has diverged from the shared
`validateNext` behavior in `auth.ts`. Update the callback handler to use the
same validation helper or extract a shared utility so the `/dashboard` and
`/aluno` checks, including the `//`-prefix guard, stay consistent across
`route.ts` and `validateNext`.

56-61: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Auth exchange errors are discarded without logging.

The Supabase error from exchangeCodeForSession is dropped after redirecting to /login; no observability into why the exchange failed (expired code, PKCE mismatch, etc.).

As per coding guidelines, "Use @sentry/nextjs (v10) for error tracking and observability."

🤖 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 `@src/app/auth/callback/route.ts` around lines 56 - 61, The auth callback
handler in the `route.ts` path redirects on `exchangeCodeForSession` failure but
drops the Supabase `error`, so add error tracking before the redirect. In the
callback logic that checks `error`, capture the failure with `@sentry/nextjs`
v10 (for example, the error returned by `exchangeCodeForSession`) and include
relevant context such as the request URL or callback state, then keep the
existing redirect to `/login`. Make the change in the callback route handler
using the existing `error` branch so failures are observable without changing
the user flow.

Source: Coding guidelines

src/lib/actions/auth.ts (2)

8-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate open-redirect validation logic across files; no Zod schema for input.

validateNext here duplicates the inline check in src/app/auth/callback/route.ts (Lines 20-21) — the two implementations already differ slightly (this one also blocks //), risking drift. Consider extracting a single shared validateNext/isSafeNextPath helper used by both files.

Separately, signInWithMagicLink's email input is only checked with !email rather than validated with a Zod schema, per project convention.

As per coding guidelines, "Use Zod 3 for schema validation" for src/**/*.{ts,tsx}.

Also applies to: 21-23

🤖 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 `@src/lib/actions/auth.ts` around lines 8 - 14, The redirect and email
validation in sign-in flow is duplicated and inconsistent, and the email input
is not using the project’s Zod-based validation convention. Extract the
`validateNext`/`isSafeNextPath` logic from `validateNext` in
`src/lib/actions/auth.ts` and reuse the same helper from
`src/app/auth/callback/route.ts` so both paths share one implementation, then
update `signInWithMagicLink` to validate its input with a Zod schema instead of
only checking `!email`.

Source: Coding guidelines


34-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No error tracking on auth failures.

Supabase errors (error.message) are swallowed and only surfaced to the caller as plain strings; nothing is reported to Sentry, making auth failures invisible in production monitoring.

As per coding guidelines, "Use @sentry/nextjs (v10) for error tracking and observability."

Also applies to: 47-49, 61-63

🤖 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 `@src/lib/actions/auth.ts` at line 34, The auth action error paths are only
returning error.message and not reporting failures to Sentry, so auth issues are
invisible in monitoring. Update the affected auth handlers in auth.ts (the
branches around the error checks, including the other matching error returns
mentioned in the review) to call `@sentry/nextjs` captureException before
returning the plain error message to the caller. Keep the existing response
shape, but ensure every Supabase auth failure path is tracked with Sentry.

Source: Coding guidelines

src/app/aluno/meus-treinos/meus-treinos-client.tsx (1)

184-191: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider a title tooltip for the truncated exercise list.

With truncate + max-w-[200px], longer exercise names or more than 3 items get cut off with no way to see the full list without opening the editor. A native title attribute with the full comma-joined list would be a cheap accessibility/UX win.

💡 Optional enhancement
                       {treino.exercicios.length > 0 && (
-                        <span className="block text-xs text-muted-foreground/70 truncate max-w-[200px]">
+                        <span
+                          className="block text-xs text-muted-foreground/70 truncate max-w-[200px]"
+                          title={treino.exercicios.map((e) => e.nomeExercicio).join(', ')}
+                        >
                           {treino.exercicios
                             .slice(0, 3)
                             .map((e) => e.nomeExercicio)
                             .join(', ')}
                         </span>
                       )}
🤖 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 `@src/app/aluno/meus-treinos/meus-treinos-client.tsx` around lines 184 - 191,
Add a native title tooltip to the truncated exercise list so the full
comma-joined names remain visible on hover. In the meus-treinos-client.tsx
rendering block that maps treino.exercicios and joins the first 3 names, compute
the full list once and apply it to the span’s title attribute while keeping the
existing truncate/max-w-[200px] styling. Keep the change localized to this
exercise list display logic.
🤖 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 `@DESIGN.md`:
- Around line 1-3: The document title and intro in DESIGN.md use a brand name
that conflicts with the app’s Five Star Gym branding. Update the heading and
opening sentence to match the same brand string used elsewhere in the app and
footer, keeping the naming consistent throughout this design doc.

In `@docs/superpowers/plans/2026-07-07-aluno-ui-10-fixes.md`:
- Line 39: The planned button radius change is inconsistent with the documented
design spec because button.tsx is expected to remain rounded-md. Update the plan
entry that references button.tsx so it matches DESIGN.md, or revise the spec
first if rounded-full is the intended standard, and keep the plan/spec aligned
using the button.tsx and DESIGN.md references.

In `@src/app/aluno/aluno-header.tsx`:
- Line 4: The new useRouter import in aluno-header introduces a test failure
because UserMenu now expects an App Router context. Update the affected test
setup in aluno-header.test.tsx to mock next/navigation’s useRouter with vi.mock
(or provide an AppRouterContext wrapper) so the component can render without the
real router. Keep the fix focused on the UserMenu/aluno-header test path and
ensure the mocked router exposes the minimal methods used by the component.

In `@src/app/aluno/dashboard/dashboard-client.tsx`:
- Around line 83-105: The dashboard state in handleFinishWorkout is not being
refreshed after a successful registrarHistoricoTreinoAction save, so the mounted
aluno stats stay stale. Update the success path in dashboard-client.tsx to
trigger a client refresh after res.success, using the router instance in this
component so the latest server data is pulled in. Keep the existing
notify.success flow, and ensure the refresh happens after the save completes so
the XP, level, streak, and monthly workouts display updates immediately.

In `@src/app/aluno/login/page.tsx`:
- Around line 88-90: The catch block in the login flow currently exposes raw
auth error messages and stops reporting failures. Update the `catch` in
`src/app/aluno/login/page.tsx` to map any thrown auth error to a generic,
user-safe message for `setAuthError`, and send the original `error` through
`@sentry/nextjs` for observability. Keep the change local to the login page
logic so the `signIn`/`signUp` flow no longer leaks account-specific details
like “User already registered.”

In `@src/lib/actions/auth.ts`:
- Line 6: The appUrl fallback in auth.ts is silently pointing production flows
to localhost, which can break magic-link and OAuth redirects; update the appUrl
initialization used by the auth actions to require NEXT_PUBLIC_APP_URL instead
of defaulting to http://localhost:3001. If the environment variable is missing,
fail fast with a clear error during auth setup so the issue is caught before
sign-in links are generated.

---

Nitpick comments:
In `@src/app/aluno/meus-treinos/meus-treinos-client.tsx`:
- Around line 184-191: Add a native title tooltip to the truncated exercise list
so the full comma-joined names remain visible on hover. In the
meus-treinos-client.tsx rendering block that maps treino.exercicios and joins
the first 3 names, compute the full list once and apply it to the span’s title
attribute while keeping the existing truncate/max-w-[200px] styling. Keep the
change localized to this exercise list display logic.

In `@src/app/auth/callback/route.ts`:
- Around line 15-21: The next-path allowlist logic in the callback route is
duplicated and has diverged from the shared `validateNext` behavior in
`auth.ts`. Update the callback handler to use the same validation helper or
extract a shared utility so the `/dashboard` and `/aluno` checks, including the
`//`-prefix guard, stay consistent across `route.ts` and `validateNext`.
- Around line 56-61: The auth callback handler in the `route.ts` path redirects
on `exchangeCodeForSession` failure but drops the Supabase `error`, so add error
tracking before the redirect. In the callback logic that checks `error`, capture
the failure with `@sentry/nextjs` v10 (for example, the error returned by
`exchangeCodeForSession`) and include relevant context such as the request URL
or callback state, then keep the existing redirect to `/login`. Make the change
in the callback route handler using the existing `error` branch so failures are
observable without changing the user flow.

In `@src/lib/actions/auth.ts`:
- Around line 8-14: The redirect and email validation in sign-in flow is
duplicated and inconsistent, and the email input is not using the project’s
Zod-based validation convention. Extract the `validateNext`/`isSafeNextPath`
logic from `validateNext` in `src/lib/actions/auth.ts` and reuse the same helper
from `src/app/auth/callback/route.ts` so both paths share one implementation,
then update `signInWithMagicLink` to validate its input with a Zod schema
instead of only checking `!email`.
- Line 34: The auth action error paths are only returning error.message and not
reporting failures to Sentry, so auth issues are invisible in monitoring. Update
the affected auth handlers in auth.ts (the branches around the error checks,
including the other matching error returns mentioned in the review) to call
`@sentry/nextjs` captureException before returning the plain error message to the
caller. Keep the existing response shape, but ensure every Supabase auth failure
path is tracked with Sentry.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 25611da0-590b-4ba5-b30f-f3bbf208cafb

📥 Commits

Reviewing files that changed from the base of the PR and between c85342b and 62c9221.

📒 Files selected for processing (16)
  • DESIGN.md
  • docs/superpowers/plans/2026-05-14-fix-npm-audit-vulnerabilities.md
  • docs/superpowers/plans/2026-07-07-aluno-ui-10-fixes.md
  • src/app/aluno/aluno-header.tsx
  • src/app/aluno/dashboard/dashboard-client.tsx
  • src/app/aluno/login/page.tsx
  • src/app/aluno/meus-treinos/meus-treinos-client.tsx
  • src/app/aluno/perfil/page.tsx
  • src/app/auth/callback/route.ts
  • src/app/page.tsx
  • src/components/dashboard/aluno/card-treino.tsx
  • src/components/ui/card.tsx
  • src/components/ui/circular-progress.tsx
  • src/lib/actions/auth.ts
  • src/lib/constants.ts
  • src/utils/supabase/middleware.ts
💤 Files with no reviewable changes (1)
  • docs/superpowers/plans/2026-05-14-fix-npm-audit-vulnerabilities.md

Comment thread DESIGN.md Outdated
Comment thread docs/superpowers/plans/2026-07-07-aluno-ui-10-fixes.md Outdated
Comment thread src/app/aluno/aluno-header.tsx
Comment thread src/app/aluno/dashboard/dashboard-client.tsx
Comment thread src/app/aluno/login/page.tsx
Comment thread src/lib/actions/auth.ts Outdated
- .env.example: fix NEXT_PUBLIC_APP_URL port 3000→3001 (prevents auth break)
- admin login: replace Globe/Code with brand SVGs (matches student page)
- admin login: add setError() in OAuth handlers (was silently swallowing errors)
- route.test.ts: 5 tests (code exchange, redirect validation, open-redirect)
- auth.test.ts: 9 tests (magic link, Google/GitHub/Apple OAuth)
@ecc-tools

ecc-tools Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

13 issues found across 12 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/app/aluno/login/page.tsx">

<violation number="1" location="src/app/aluno/login/page.tsx:90">
P2: Error handling no longer calls `notify.error` — the inline `role="alert"` div replaces the toast on the error path. However, `notify.success` still fires on the success path. If there are other consuming code paths that depend on error notifications (e.g., global error tracking), the switch from `notify.error` to `setAuthError` drops that side effect. Make sure that no downstream analytics, logging, or Sentry capture relied on the previous `notify.error` call.</violation>

<violation number="2" location="src/app/aluno/login/page.tsx:114">
P2: The OAuth client handlers conflate error messages and redirect URLs in the same `result.error` field, relying on `startsWith('http')` to decide whether to navigate or display an error. This is brittle: if a Supabase error message ever begins with `http`, it would be treated as a redirect target. A clearer contract—e.g. returning `{ url?: string; error?: string }` from the server actions—would make the flow explicit and safer.</violation>
</file>

<file name="src/app/aluno/meus-treinos/meus-treinos-client.tsx">

<violation number="1" location="src/app/aluno/meus-treinos/meus-treinos-client.tsx:184">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

The newly added exercise-name list below the exercise count is user-visible behavior that lacks test coverage. `meus-treinos-client.test.tsx` already tests this component, so adding a test for the conditional exercise-name display (e.g., verifying names render, only up to 3 are shown, and the element is absent when there are no exercises) would be consistent with the existing shallow test style and would ensure the fix does not regress.</violation>
</file>

<file name="src/components/dashboard/aluno/card-treino.tsx">

<violation number="1" location="src/components/dashboard/aluno/card-treino.tsx:85">
P3: The intended keyboard focus feedback for exercise rows will not show because the new `focus-visible:*` classes are on a non-focusable `div`; keyboard focus lands on the nested `Checkbox` or info `Button` instead. Using `focus-within:*` on the row would make the row highlight when any child control receives keyboard focus.</violation>
</file>

<file name="src/app/aluno/dashboard/dashboard-client.tsx">

<violation number="1" location="src/app/aluno/dashboard/dashboard-client.tsx:37">
P2: Custom agent: **Enforce Pragmatic Test Coverage**

New workout session UI behavior and state management are untested. The `treinoEmSessao` state drives conditional rendering of `WorkoutSession`, and `handleFinishWorkout` adds success and error notification paths around `registrarHistoricoTreinoAction`. The component's test file (`dashboard-client.test.tsx`) does not cover the state toggle, the conditional render path, or the `handleFinishWorkout` success/error paths. Consider adding tests that verify: (1) triggering `onStartWorkout` renders `WorkoutSession`, (2) `handleFinishWorkout` calls `notify.success` on a successful server action response, and (3) `handleFinishWorkout` calls `notify.error` when the server action returns an error or throws.</violation>

<violation number="2" location="src/app/aluno/dashboard/dashboard-client.tsx:83">
P1: The new workout save flow passes a client-built `HistoricoTreino` payload directly to the server action, trusting client-reported duration and exercise completion for gamification rewards. Consider validating or recomputing progression-critical fields (e.g., `duracaoMinutos`, completed series count, and `treinoId` ownership) server-side so that XP, level, and streak calculations cannot be inflated by a manipulated client payload.</violation>
</file>

<file name=".env.example">

<violation number="1" location=".env.example:88">
P2: The app URL is being changed to `localhost:3001` here, but `package.json` still runs `next dev` without a `--port` flag, so the Next.js dev server will continue to bind to port 3000 by default. When a developer copies `.env.example` to `.env.local`, server-side auth redirects and magic links will point to `localhost:3001` while the app is actually running on `localhost:3000`, breaking local OAuth and magic-link flows. Either update `package.json` to explicitly bind port 3001 (e.g., `next dev --port 3001`) or keep the example URL aligned with the Next.js default of 3000 to avoid this mismatch.</violation>
</file>

<file name="src/lib/actions/auth.test.ts">

<violation number="1" location="src/lib/actions/auth.test.ts:61">
P3: The `signInWithGitHub` and `signInWithApple` tests only verify that a URL is returned, but unlike `signInWithGoogle`, they don't assert that the correct provider name is passed to the Supabase client. Since all OAuth tests share the same `mockSignInWithOAuth` mock, a copy-paste error that accidentally uses the wrong provider (e.g., `provider: 'google'` in `signInWithGitHub`) would still make these tests pass. Adding provider-specific assertions — the same `expect.objectContaining({ provider: 'github' })` / `expect.objectContaining({ provider: 'apple' })` pattern used in the Google test — would better guard against misconfiguration.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread src/app/aluno/dashboard/dashboard-client.tsx
Comment thread src/app/auth/callback/route.ts Outdated
Comment thread src/app/login/page.tsx Outdated
Comment thread src/lib/actions/auth.ts Outdated
Comment thread src/app/aluno/dashboard/dashboard-client.tsx
Comment thread src/app/aluno/login/page.tsx Outdated
Comment thread src/app/aluno/login/page.tsx Outdated
Comment thread src/lib/actions/auth.ts Outdated
Comment thread src/lib/actions/auth.ts Outdated
Comment thread src/lib/actions/auth.test.ts Outdated
signInWithGoogle/GitHub/Apple now pass next=/dashboard so callback
redirects correctly after authentication
@ecc-tools

ecc-tools Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

…header

- page.test: 'Academia Five Star' → 'Five Star Gym' (matches Q03 brand rename)
- aluno-header.test: mock useRouter from next/navigation (UserMenu now uses router for Perfil)

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 7 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread src/lib/auth-redirect.ts Outdated
Comment thread src/app/auth/callback/route.ts Outdated
sfU (account enumeration): the signUp fallback in `handleFormSubmit`
surfaces raw Supabase error messages. When an attacker enters a
registered email with a wrong password, signIn fails with "Invalid
login credentials" and the code falls back to signUp, which throws
"User already registered" — leaking account existence to the caller.
Detect that class of message via regex and surface the same generic
credential error as a failed login, so the error reveals nothing.

se1 (stale dashboard after save): `registrarHistoricoTreinoAction` calls
`revalidatePath('/aluno/dashboard')`, which invalidates the cache but
does not update this already-mounted client component's `aluno` props.
Add `router.refresh()` after a successful save so the RSC payload is
re-fetched and XP/level/streak/treinosNoMes reflect the new state
without a manual reload. Mock `next/navigation` in
`dashboard-client.test.tsx` to satisfy `useRouter`.

Tests: 1151 pass, 0 fail. Typecheck + lint clean.

Co-Authored-By: Claude <noreply@anthropic.com>
@ecc-tools

ecc-tools Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/app/aluno/dashboard/dashboard-client.test.tsx (1)

30-32: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting the refresh mock for assertability.

refresh: vi.fn() creates a new mock on every useRouter() call, so tests cannot assert that router.refresh() was actually invoked after handleFinishWorkout succeeds. If any test needs to verify the refresh behavior, extract a shared mock reference.

♻️ Optional refactor: shared mock reference
+const mockRefresh = vi.fn();
 vi.mock('next/navigation', () => ({
-  useRouter: () => ({ refresh: vi.fn() }),
+  useRouter: () => ({ refresh: mockRefresh }),
 }));

+// In tests:
+// expect(mockRefresh).toHaveBeenCalledTimes(1);
🤖 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 `@src/app/aluno/dashboard/dashboard-client.test.tsx` around lines 30 - 32, The
`useRouter` mock in `dashboard-client.test.tsx` creates a new `refresh` spy on
every call, which prevents assertions on `router.refresh()` after
`handleFinishWorkout` succeeds. Update the `next/navigation` mock to use a
shared `refresh` mock reference that can be reused across tests, and keep it
accessible where the `DashboardClient` behavior is exercised so refresh
invocation can be asserted reliably.
🤖 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 `@src/app/auth/callback/route.ts`:
- Around line 60-64: The Sentry capture in the callback route is sending the
full request URL, which includes the OAuth code and raw next value. Update the
Sentry.captureException call in the callback handler to use only the pathname
(or another redacted callback identifier) instead of request.url, and keep the
extra metadata free of credentials while preserving enough context to debug the
auth failure.

---

Nitpick comments:
In `@src/app/aluno/dashboard/dashboard-client.test.tsx`:
- Around line 30-32: The `useRouter` mock in `dashboard-client.test.tsx` creates
a new `refresh` spy on every call, which prevents assertions on
`router.refresh()` after `handleFinishWorkout` succeeds. Update the
`next/navigation` mock to use a shared `refresh` mock reference that can be
reused across tests, and keep it accessible where the `DashboardClient` behavior
is exercised so refresh invocation can be asserted reliably.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 746e44cd-79dd-481f-89c1-2759b87252fa

📥 Commits

Reviewing files that changed from the base of the PR and between fcb1081 and 48d6dd7.

📒 Files selected for processing (9)
  • DESIGN.md
  • docs/superpowers/plans/2026-07-07-aluno-ui-10-fixes.md
  • src/app/aluno/dashboard/dashboard-client.test.tsx
  • src/app/aluno/dashboard/dashboard-client.tsx
  • src/app/aluno/login/page.tsx
  • src/app/auth/callback/route.ts
  • src/app/login/page.tsx
  • src/lib/actions/auth.ts
  • src/lib/auth-redirect.ts
✅ Files skipped from review due to trivial changes (2)
  • src/lib/auth-redirect.ts
  • DESIGN.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/app/login/page.tsx
  • src/lib/actions/auth.ts
  • src/app/aluno/login/page.tsx
  • src/app/aluno/dashboard/dashboard-client.tsx

Comment thread src/app/auth/callback/route.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 3 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread src/app/aluno/dashboard/dashboard-client.tsx
Comment thread src/app/aluno/login/page.tsx
- auth-redirect.ts: wrap new URL() in try/catch; malformed next returns '/'
  instead of 500 (cubic PMD5t P1)
- callback/route.ts: strip query string from Sentry callbackUrl to avoid
  persisting OAuth code in third-party logs (cubic PMD50 P1)
- actions/auth.ts: replace silent localhost:3001 fallback with explicit
  throw when NEXT_PUBLIC_APP_URL unset, read lazily at call time (coderabbit sfi)
- .env.example: move NEXT_PUBLIC_APP_URL above NODE_ENV (coderabbit PLDGo)

Co-Authored-By: Claude <noreply@anthropic.com>
@ecc-tools

ecc-tools Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

… + a11y

Address remaining cubic/coderabbit deferred review threads:

- card-treino: focus-visible→focus-within on exercise row divs (non-focusable
  container; focus-within fires when child button/link receives keyboard focus)
- .env.example: revert NEXT_PUBLIC_APP_URL to :3000 (matches `next dev` default;
  :3001 broke test env + dev parity)
- auth: split OAuth return contract — dedicated `url`/`error` fields replace the
  brittle `startsWith('http')` heuristic; client never disambiguates via error
  string. Both login pages + auth.test.ts updated to the new contract.
- auth: lazy NEXT_PUBLIC_APP_URL read inside callbackUrl() (not module load) so
  test harnesses injecting env after import still work.
- meus-treinos-client.test: assert exercise names render, sliced to 3, joined;
  omit name list when 0 exercises (O8Gaq).
- dashboard-client.test: mock @/lib/actions/treinos (unblocks Prisma DATABASE_URL
  import-time failure) + WorkoutSession gate; cover WorkoutSession state toggle
  (O9FaX), router.refresh() after registrarHistorico success (PMTVI), error path.
- aluno/login.test: account-enumeration guard — "User already registered" and
  "User has already been registered" both masked to generic credential error,
  no leak, no push (PMTVN).

All 112 test files / 1159 tests pass. Typecheck clean.

Co-Authored-By: Claude <noreply@anthropic.com>
@ecc-tools

ecc-tools Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

Document final commit 641831d: P2/P3 test coverage (O8Gaq, O9FaX, PMTVI,
PMTVN) + a11y (O8GbK) + .env parity (O9Faj) + OAuth url/error contract
(O9Fap, O9Fa4). 8 cubic threads resolved, 0 unresolved. 112 files / 1159 tests.

Co-Authored-By: Claude <noreply@anthropic.com>
@ecc-tools

ecc-tools Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

20/21 E2E failures stemmed from selectors resolving to multiple elements
under Playwright strict mode:

- helpers/auth.ts + auth.spec.ts: getByRole('button', {name: /entrar|login/i})
  matched 3 buttons (submit + Google + GitHub). Anchored to
  /^entrar( no sistema)?$/i to hit only the staff "Entrar no Sistema" and
  aluno "Entrar" submit buttons.
- helpers/auth.ts: getByLabel(/senha|password/i) on /aluno/login resolved to
  the visibility-toggle button aria-label "Mostrar senha" instead of the
  RHF-controlled input (FormLabel has no htmlFor binding). Switched to
  input[type="password"] locator, which works on both /login (classic
  label) and /aluno/login (RHF).
- student-portal.spec.ts: getByRole('heading', {name:'Meus Treinos'}) hit 2
  elements — PageHeader h1 plus a nav/sidebar mirror reusing i18n
  myWorkouts. Pinned level:1.

Result: 21/21 pass (1.8m).

Co-Authored-By: Claude <noreply@anthropic.com>
@ecc-tools

ecc-tools Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/app/login/page.tsx (2)

60-68: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Add role="alert" to the error display for OAuth error accessibility.

The new handleOAuthLogin sets errors via setError(result.error), which renders at line 138 in a <p> without role="alert". Screen readers won't announce dynamically inserted OAuth errors. The PR objectives include role="alert" for auth errors (applied on the aluno page); the admin login page should follow suit since OAuth errors now flow through this same display.

♿ Proposed fix
-            {error && <p className="text-sm text-destructive">{error}</p>}
+            {error && <p role="alert" className="text-sm text-destructive">{error}</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 `@src/app/login/page.tsx` around lines 60 - 68, The OAuth error message
rendered by the login page’s error display is missing an accessible alert role,
so screen readers may not announce errors set by handleOAuthLogin. Update the
error-rendering element used by the login page (the same one showing
setError(result.error), near the login form) to include role="alert", matching
the auth error accessibility pattern already used on the aluno page.

169-223: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add aria-hidden="true" to decorative OAuth button SVG icons.

All three OAuth button SVGs (Google, GitHub, Apple) are decorative — each button has a text label like "Entrar com Google". Without aria-hidden, screen readers may attempt to announce the SVG content. The PR already applied aria-hidden to the CircularProgress SVG; these new SVGs should follow the same standard.

♿ Proposed fix
               <svg
                 className="h-4 w-4"
                 viewBox="0 0 24 24"
                 fill="none"
+                aria-hidden="true"
                 xmlns="http://www.w3.org/2000/svg"
               >

Apply the same aria-hidden="true" addition to the GitHub SVG (line 200) and Apple SVG (line 216).

🤖 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 `@src/app/login/page.tsx` around lines 169 - 223, The OAuth button SVG icons in
the login page are decorative and should be hidden from assistive tech; update
the SVGs used in the login buttons so they match the existing accessibility
pattern from the CircularProgress icon. Add aria-hidden="true" to the Google,
GitHub, and Apple SVG elements in the login button rendering code within the
login page component, keeping the text labels as the accessible names.
🤖 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 `@src/app/aluno/meus-treinos/meus-treinos-client.test.tsx`:
- Around line 236-251: Strengthen the MeusTreinosClient test coverage by adding
a negative assertion in the “omits exercise name list when treino has no
exercises” case so it explicitly verifies the exercise-name area is not rendered
when exercicios is empty. Update the assertions in meus-treinos-client.test.tsx
to use more direct Testing Library checks for the existing getByText calls, and
keep the test aligned with the behavior of MeusTreinosClient’s treino card
rendering.

---

Outside diff comments:
In `@src/app/login/page.tsx`:
- Around line 60-68: The OAuth error message rendered by the login page’s error
display is missing an accessible alert role, so screen readers may not announce
errors set by handleOAuthLogin. Update the error-rendering element used by the
login page (the same one showing setError(result.error), near the login form) to
include role="alert", matching the auth error accessibility pattern already used
on the aluno page.
- Around line 169-223: The OAuth button SVG icons in the login page are
decorative and should be hidden from assistive tech; update the SVGs used in the
login buttons so they match the existing accessibility pattern from the
CircularProgress icon. Add aria-hidden="true" to the Google, GitHub, and Apple
SVG elements in the login button rendering code within the login page component,
keeping the text labels as the accessible names.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2cb5a412-8f52-4cf4-a90f-8bd845049d31

📥 Commits

Reviewing files that changed from the base of the PR and between 48d6dd7 and 6ab8af1.

📒 Files selected for processing (15)
  • .env.example
  • docs/CURRENT-STATE.md
  • src/app/aluno/dashboard/dashboard-client.test.tsx
  • src/app/aluno/login/page.test.tsx
  • src/app/aluno/login/page.tsx
  • src/app/aluno/meus-treinos/meus-treinos-client.test.tsx
  • src/app/auth/callback/route.ts
  • src/app/login/page.tsx
  • src/components/dashboard/aluno/card-treino.tsx
  • src/lib/actions/auth.test.ts
  • src/lib/actions/auth.ts
  • src/lib/auth-redirect.ts
  • tests/e2e/helpers/auth.ts
  • tests/e2e/specs/auth.spec.ts
  • tests/e2e/specs/student-portal.spec.ts
✅ Files skipped from review due to trivial changes (2)
  • .env.example
  • docs/CURRENT-STATE.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/lib/auth-redirect.ts
  • src/lib/actions/auth.test.ts
  • src/components/dashboard/aluno/card-treino.tsx
  • src/app/auth/callback/route.ts
  • src/app/aluno/login/page.tsx

Comment on lines +236 to +251

// O8Gaq: exercise names render under treino title, sliced to first 3, joined.
it('renders exercise names on treino cards', () => {
render(<MeusTreinosClient initialTreinos={mockTreinos} userId="user-1" />);
expect(screen.getByText('Supino')).toBeTruthy();
expect(screen.getByText('Corrida')).toBeTruthy();
});

it('omits exercise name list when treino has no exercises', () => {
const emptyExTreino: Treino[] = [
{ id: 't-empty', alunoId: 'user-1', objetivo: 'Vazio', diaSemana: 2, exercicios: [] },
];
render(<MeusTreinosClient initialTreinos={emptyExTreino} userId="user-1" />);
expect(screen.getByText('Vazio')).toBeTruthy();
expect(screen.getByText('0 exercícios')).toBeTruthy();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Strengthen the "omits" test with a negative assertion.

The test named "omits exercise name list when treino has no exercises" only asserts that 'Vazio' and '0 exercícios' are present. It doesn't explicitly verify the exercise-name span is absent. Adding a negative query would make the test name accurate:

🧪 Suggested improvement
   it('omits exercise name list when treino has no exercises', () => {
     const emptyExTreino: Treino[] = [
       { id: 't-empty', alunoId: 'user-1', objetivo: 'Vazio', diaSemana: 2, exercicios: [] },
     ];
     render(<MeusTreinosClient initialTreinos={emptyExTreino} userId="user-1" />);
     expect(screen.getByText('Vazio')).toBeTruthy();
     expect(screen.getByText('0 exercícios')).toBeTruthy();
+    // Verify no exercise-name span is rendered
+    expect(screen.queryByText(/,/)).toBeNull();
   });

Additionally, expect(screen.getByText(...)).toBeTruthy() is redundant — getByText already throws if the element isn't found. Consider using toBeInTheDocument() (if @testing-library/jest-dom is configured) or simply calling screen.getByText(...) without wrapping in expect.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// O8Gaq: exercise names render under treino title, sliced to first 3, joined.
it('renders exercise names on treino cards', () => {
render(<MeusTreinosClient initialTreinos={mockTreinos} userId="user-1" />);
expect(screen.getByText('Supino')).toBeTruthy();
expect(screen.getByText('Corrida')).toBeTruthy();
});
it('omits exercise name list when treino has no exercises', () => {
const emptyExTreino: Treino[] = [
{ id: 't-empty', alunoId: 'user-1', objetivo: 'Vazio', diaSemana: 2, exercicios: [] },
];
render(<MeusTreinosClient initialTreinos={emptyExTreino} userId="user-1" />);
expect(screen.getByText('Vazio')).toBeTruthy();
expect(screen.getByText('0 exercícios')).toBeTruthy();
});
// O8Gaq: exercise names render under treino title, sliced to first 3, joined.
it('renders exercise names on treino cards', () => {
render(<MeusTreinosClient initialTreinos={mockTreinos} userId="user-1" />);
expect(screen.getByText('Supino')).toBeTruthy();
expect(screen.getByText('Corrida')).toBeTruthy();
});
it('omits exercise name list when treino has no exercises', () => {
const emptyExTreino: Treino[] = [
{ id: 't-empty', alunoId: 'user-1', objetivo: 'Vazio', diaSemana: 2, exercicios: [] },
];
render(<MeusTreinosClient initialTreinos={emptyExTreino} userId="user-1" />);
expect(screen.getByText('Vazio')).toBeTruthy();
expect(screen.getByText('0 exercícios')).toBeTruthy();
// Verify no exercise-name span is rendered
expect(screen.queryByText(/,/)).toBeNull();
});
🤖 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 `@src/app/aluno/meus-treinos/meus-treinos-client.test.tsx` around lines 236 -
251, Strengthen the MeusTreinosClient test coverage by adding a negative
assertion in the “omits exercise name list when treino has no exercises” case so
it explicitly verifies the exercise-name area is not rendered when exercicios is
empty. Update the assertions in meus-treinos-client.test.tsx to use more direct
Testing Library checks for the existing getByText calls, and keep the test
aligned with the behavior of MeusTreinosClient’s treino card rendering.

EmiyaKiritsugu3 and others added 5 commits July 8, 2026 08:13
…w/prod)

Design for fixing silent magic-link + OAuth auth failures in Vercel
Preview/Production. Root causes: NEXT_PUBLIC_APP_URL set to empty string in
Vercel Production (empty-string defeats ?? fallback), missing VERCEL_URL
tier, supabase/config.toml allowlist mismatches (127.0.0.1 vs localhost,
https-on-local, no localhost/vercel.app glob/prod), stale cloud Supabase
allowlist. callbackUrl() rewritten to tiered resolution (explicit → VERCEL_URL
→ localhost), config.toml allowlist fixed, Vercel prod env set. Password login
unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
Context7 audit found Fix B glob https://*-*.vercel.app/auth/callback wrong:

- Supabase * matches non-separator chars (separators . and /); - is not a

  separator so * technically spans it, but ambiguous for multi-hyphen

  preview hostnames (smartmanagementsystem-6rfj2hapr-emiyakiritsugu3s-projects

  .vercel.app).

- ** matches any sequence of chars — documented Supabase-recommended pattern

  for Netlify/Vercel preview URLs (supabase.com/docs/guides/auth/redirect-urls).

- Supabase docs recommend exact paths for production; explicit prod URL retained.

Fix E references 'mirror Fix B entries' so cloud allowlist propagates automatically.

Co-Authored-By: Claude <noreply@anthropic.com>
…fallback

Fix A — callbackUrl() tiered resolution (src/lib/actions/auth.ts):

- Tier 1: NEXT_PUBLIC_APP_URL?.trim() — empty string converted to null

  via explicit || null so ?? falls through to VERCEL_URL.

- Tier 2: VERCEL_URL — Vercel auto-injected deploy hostname, prefix https://.

- Tier 3: http://localhost:3000 — zero-config local dev default.

- Removed throw-on-unset; local dev works without any env vars.

Fix B — supabase/config.toml allowlist: site_url → localhost:3000,

additional_redirect_urls with https://**.vercel.app/auth/callback glob

(** matches any chars per Context7 Supabase docs) + explicit prod URL.

Fix F — .env.example: annotated NEXT_PUBLIC_APP_URL with preview/prod

guidance (leave unset for VERCEL_URL fallback in preview).

Fix G — auth.test.ts: 5 new tier-resolution tests (14 total, all pass)

through public signInWithMagicLink API; afterEach cleans VERCEL_URL only.

Quality: tsc strict clean, 1164/1164 tests green.

Co-Authored-By: Claude <noreply@anthropic.com>
Matched local [auth.email] and [auth.mfa.totp] to cloud production values

so supabase config push only changes redirect URLs:

- auth.email: enable_confirmations=true, max_frequency=1m0s, otp_length=8

- auth.mfa.totp: enroll_enabled=true, verify_enabled=true

Co-Authored-By: Claude <noreply@anthropic.com>
Context7 audit of Supabase official docs (supabase.com/docs/guides/auth/redirect-urls)

shows the recommended pattern:

  process?.env?.NEXT_PUBLIC_VERCEL_URL

VERCEL_URL is Vercel's system env var; NEXT_PUBLIC_VERCEL_URL is the Next.js

framework-specific equivalent that Supabase explicitly recommends.

Changes:

- auth.ts: process.env.VERCEL_URL → process.env.NEXT_PUBLIC_VERCEL_URL

- auth.test.ts: all VERCEL_URL refs → NEXT_PUBLIC_VERCEL_URL (tests + afterEach)

- .env.example: comment updated to NEXT_PUBLIC_VERCEL_URL

- spec: code snippet + tier descriptions + Fix D all updated

Quality: tsc strict clean, 14/14 auth tests green.

Co-Authored-By: Claude <noreply@anthropic.com>
@ecc-tools

ecc-tools Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@sonarqubecloud

sonarqubecloud Bot commented Jul 8, 2026

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="supabase/config.toml">

<violation number="1" location="supabase/config.toml:158">
P0: The `additional_redirect_urls` include `https://**.vercel.app/auth/callback`, which uses a `**` globstar that matches every Vercel subdomain worldwide. Supabase's own security documentation classifies overly broad wildcard redirects as dangerous because they allow any attacker-controlled domain to receive authentication codes and tokens. If any auth flow (OAuth, magic link, email confirmation) passes a user-influenced `redirectTo` value, an attacker could redirect the victim's session token to their own Vercel deployment. Consider restricting the wildcard to your project's Vercel team or removing it and relying only on the exact production URL.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread supabase/config.toml
additional_redirect_urls = ["https://127.0.0.1:3000"]
additional_redirect_urls = [
"http://localhost:3000/auth/callback",
"https://**.vercel.app/auth/callback",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: The additional_redirect_urls include https://**.vercel.app/auth/callback, which uses a ** globstar that matches every Vercel subdomain worldwide. Supabase's own security documentation classifies overly broad wildcard redirects as dangerous because they allow any attacker-controlled domain to receive authentication codes and tokens. If any auth flow (OAuth, magic link, email confirmation) passes a user-influenced redirectTo value, an attacker could redirect the victim's session token to their own Vercel deployment. Consider restricting the wildcard to your project's Vercel team or removing it and relying only on the exact production URL.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/config.toml, line 158:

<comment>The `additional_redirect_urls` include `https://**.vercel.app/auth/callback`, which uses a `**` globstar that matches every Vercel subdomain worldwide. Supabase's own security documentation classifies overly broad wildcard redirects as dangerous because they allow any attacker-controlled domain to receive authentication codes and tokens. If any auth flow (OAuth, magic link, email confirmation) passes a user-influenced `redirectTo` value, an attacker could redirect the victim's session token to their own Vercel deployment. Consider restricting the wildcard to your project's Vercel team or removing it and relying only on the exact production URL.</comment>

<file context>
@@ -151,9 +151,13 @@ max_indexes = 5
-additional_redirect_urls = ["https://127.0.0.1:3000"]
+additional_redirect_urls = [
+  "http://localhost:3000/auth/callback",
+  "https://**.vercel.app/auth/callback",
+  "https://smartmanagementsystem.vercel.app/auth/callback"
+]
</file context>
Suggested change
"https://**.vercel.app/auth/callback",
"https://smartmanagementsystem.vercel.app/auth/callback"

@EmiyaKiritsugu3 EmiyaKiritsugu3 merged commit 6f7dfc5 into main Jul 8, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant