Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 68 additions & 1 deletion src/app/aluno/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,74 @@ export default async function AlunoDashboardPage() {
},
});

const aluno = await alunoPromise;
let aluno = await alunoPromise;

// Auto-provision: OAuth users (Google/GitHub/Apple) who have no aluno row get
// one created lazily on first dashboard visit. Email+name come from Supabase
// auth.user_metadata; CPF is required @unique but unknown from Google, so we
// mint a deterministic placeholder from the auth user id (uniqueness from the
// already-unique auth uid). Admin can fix the real CPF later. Throws on the
// rare race where two concurrent requests both miss findUnique — caught below
// surfaces as the original empty-state so the next request finds the row.
if (!aluno) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Custom agent: Enforce Pragmatic Test Coverage

The auto-provision logic is core business logic, but its success and failure paths are not exercised by the current tests. page.test.tsx only mocks prisma.aluno.findUnique and does not cover the new prisma.aluno.create call or the race-condition refetch in the catch block. Consider adding tests that mock a successful creation and simulate the unique-violation race case so both branches are validated.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/aluno/dashboard/page.tsx, line 46:

<comment>The auto-provision logic is core business logic, but its success and failure paths are not exercised by the current tests. `page.test.tsx` only mocks `prisma.aluno.findUnique` and does not cover the new `prisma.aluno.create` call or the race-condition refetch in the catch block. Consider adding tests that mock a successful creation and simulate the unique-violation race case so both branches are validated.</comment>

<file context>
@@ -34,7 +34,74 @@ export default async function AlunoDashboardPage() {
+  // already-unique auth uid). Admin can fix the real CPF later. Throws on the
+  // rare race where two concurrent requests both miss findUnique — caught below
+  // surfaces as the original empty-state so the next request finds the row.
+  if (!aluno) {
+    const meta = user.user_metadata ?? {};
+    const nomeCompleto =
</file context>

const meta = user.user_metadata ?? {};
const nomeCompleto =
(meta.full_name as string | undefined) ??
(meta.name as string | undefined) ??
user.email ??
'Aluno Google';
Comment on lines +48 to +52

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: OAuth users with an empty full_name/name metadata value can be auto-provisioned with a blank display name because ?? does not fall back on empty strings. Treating non-empty trimmed strings as valid before falling back to email keeps the dashboard identity usable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/aluno/dashboard/page.tsx, line 48:

<comment>OAuth users with an empty `full_name`/`name` metadata value can be auto-provisioned with a blank display name because `??` does not fall back on empty strings. Treating non-empty trimmed strings as valid before falling back to email keeps the dashboard identity usable.</comment>

<file context>
@@ -34,7 +34,74 @@ export default async function AlunoDashboardPage() {
+  // surfaces as the original empty-state so the next request finds the row.
+  if (!aluno) {
+    const meta = user.user_metadata ?? {};
+    const nomeCompleto =
+      (meta.full_name as string | undefined) ??
+      (meta.name as string | undefined) ??
</file context>
Suggested change
const nomeCompleto =
(meta.full_name as string | undefined) ??
(meta.name as string | undefined) ??
user.email ??
'Aluno Google';
const nomeCompleto =
(typeof meta.full_name === 'string' && meta.full_name.trim()) ||
(typeof meta.name === 'string' && meta.name.trim()) ||
user.email ||
'Aluno Google';

const fotoUrl = (meta.avatar_url as string | undefined) ?? null;
const cpfPlaceholder = `OAUTH-${user.id.slice(0, 18)}`;
try {
aluno = await prisma.aluno.create({
data: {
email: user.email!,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect getUser return type and the User.email typing to confirm nullability.
fd -t f 'server.(ts|tsx)' src/utils/supabase --exec cat -n {}
rg -nP '\buser\.email\b' src/app/aluno/dashboard/page.tsx

Repository: EmiyaKiritsugu3/PWeb_Project

Length of output: 2170


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the dashboard page around the cited lines and the getUser helper.
file="src/app/aluno/dashboard/page.tsx"

echo "== file size =="
wc -l "$file"

echo "== relevant slices =="
sed -n '1,180p' "$file"

echo "== search for guards / redirects related to email =="
rg -n "email|redirect|notFound|throw|return null|optional|undefined" "$file"

Repository: EmiyaKiritsugu3/PWeb_Project

Length of output: 5913


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="src/app/aluno/dashboard/page.tsx"

# Show tighter context around the reported lines.
sed -n '1,140p' "$file" | nl -ba | sed -n '1,140p'

# Also inspect the Supabase auth user type if it is vendored in the repo lock/types.
rg -n "type User|interface User|email\?:|email:.*undefined" . -g '!node_modules' -g '!dist' -g '!build'

Repository: EmiyaKiritsugu3/PWeb_Project

Length of output: 206


Handle user.email once up front. If the email can be missing, guard or redirect before the Prisma queries and reuse a local email value; mixing user.email and user.email! leaves the no-email OAuth path inconsistent.

🤖 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/page.tsx` at line 58, The dashboard flow currently
mixes `user.email` and `user.email!`, which leaves the no-email OAuth path
inconsistent. In `page.tsx`, handle the email once at the start of the component
or handler by reading `user.email` into a local `email` variable, then guard or
redirect early if it is missing before any Prisma queries. After that, reuse the
same `email` variable throughout the rest of the logic instead of accessing
`user.email` again.

nomeCompleto,
fotoUrl,
cpf: cpfPlaceholder,
},
select: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The select projection block (15 lines) is duplicated verbatim between the aluno.create() call and the catch block's findUnique() refetch. If the projection changes — adding a new field, renaming a relation — both copies must be updated in sync. Consider extracting the shared select object into a variable or function to keep them consistent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/aluno/dashboard/page.tsx, line 63:

<comment>The `select` projection block (15 lines) is duplicated verbatim between the `aluno.create()` call and the catch block's `findUnique()` refetch. If the projection changes — adding a new field, renaming a relation — both copies must be updated in sync. Consider extracting the shared `select` object into a variable or function to keep them consistent.</comment>

<file context>
@@ -34,7 +34,74 @@ export default async function AlunoDashboardPage() {
+          fotoUrl,
+          cpf: cpfPlaceholder,
+        },
+        select: {
+          id: true,
+          nomeCompleto: true,
</file context>

id: true,
nomeCompleto: true,
fotoUrl: true,
nivel: true,
exp: true,
statusMatricula: true,
streakDiasSeguidos: true,
treinosNoMes: true,
ultimoTreinoData: true,
Matriculas: {
where: { status: 'ATIVA' },
orderBy: { dataVencimento: 'desc' },
take: 1,
select: { id: true, status: true, dataVencimento: true, planoId: true },
},
},
});
} catch {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: First-time OAuth users can still land on the “perfil de aluno não foi encontrado” fallback for real create failures because this catches every prisma.aluno.create error as if it were only a race. Narrowing this to Prisma P2002 unique-constraint errors and rethrowing/logging other errors would preserve the intended auto-provisioning behavior and make failures visible.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/aluno/dashboard/page.tsx, line 81:

<comment>First-time OAuth users can still land on the “perfil de aluno não foi encontrado” fallback for real create failures because this catches every `prisma.aluno.create` error as if it were only a race. Narrowing this to Prisma `P2002` unique-constraint errors and rethrowing/logging other errors would preserve the intended auto-provisioning behavior and make failures visible.</comment>

<file context>
@@ -34,7 +34,74 @@ export default async function AlunoDashboardPage() {
+          },
+        },
+      });
+    } catch {
+      // Race: another request created the row. Refetch instead of crashing.
+      aluno = await prisma.aluno.findUnique({
</file context>

// Race: another request created the row. Refetch instead of crashing.
aluno = await prisma.aluno.findUnique({
where: { email: user.email },
select: {
id: true,
nomeCompleto: true,
fotoUrl: true,
nivel: true,
exp: true,
statusMatricula: true,
streakDiasSeguidos: true,
treinosNoMes: true,
ultimoTreinoData: true,
Matriculas: {
where: { status: 'ATIVA' },
orderBy: { dataVencimento: 'desc' },
take: 1,
select: { id: true, status: true, dataVencimento: true, planoId: true },
},
},
});
}
Comment on lines +81 to +103

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== file outline ==\n'
ast-grep outline src/app/aluno/dashboard/page.tsx --view expanded

printf '\n== relevant lines 1-170 ==\n'
cat -n src/app/aluno/dashboard/page.tsx | sed -n '1,170p'

printf '\n== prisma/aluno references in file ==\n'
rg -n "prisma\.aluno|Prisma|Sentry|catch" src/app/aluno/dashboard/page.tsx

printf '\n== imports at top ==\n'
sed -n '1,35p' src/app/aluno/dashboard/page.tsx

Repository: EmiyaKiritsugu3/PWeb_Project

Length of output: 7943


Narrow the catch to the unique-violation race. A bare catch {} treats every create failure as a concurrent insert, so transient DB/validation errors get swallowed and the user falls back to the empty state. Handle only P2002 here and log/rethrow the rest.

🤖 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/page.tsx` around lines 81 - 103, The `catch` in
`dashboard/page.tsx` is too broad and currently treats every
`prisma.aluno.create` failure as a concurrent insert. Narrow the handler around
the `aluno` creation path to only recover from the unique-constraint race (check
for Prisma `P2002`), then keep the existing `findUnique` refetch; for any other
error, log it and rethrow so real DB/validation failures are not swallowed. Use
the `prisma.aluno.create` and fallback `prisma.aluno.findUnique` block as the
fix point.

Source: Coding guidelines

}

if (!aluno) {
return (
Expand Down
7 changes: 5 additions & 2 deletions supabase/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,11 @@ max_indexes = 5
[auth]
enabled = true
# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used
# in emails.
site_url = "http://localhost:3000"
# in emails. Must be the production URL — Supabase falls back to this when a `redirectTo`
# fails allowlist validation, so leaving it as localhost in a linked cloud project sends
# users to localhost on every OAuth redirect mismatch. Local dev resolves via
# additional_redirect_urls (localhost entry) instead.
site_url = "https://smartmanagementsystem.vercel.app"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Local password signup/email confirmation will now send developers to the production app when using the local Supabase stack. The signUp flow does not provide emailRedirectTo, so this production site_url becomes the fallback for confirmation links; consider adding an explicit local/prod callback URL in that signup call or separating local/cloud auth config.

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

<comment>Local password signup/email confirmation will now send developers to the production app when using the local Supabase stack. The `signUp` flow does not provide `emailRedirectTo`, so this production `site_url` becomes the fallback for confirmation links; consider adding an explicit local/prod callback URL in that signup call or separating local/cloud auth config.</comment>

<file context>
@@ -150,8 +150,11 @@ max_indexes = 5
+# fails allowlist validation, so leaving it as localhost in a linked cloud project sends
+# users to localhost on every OAuth redirect mismatch. Local dev resolves via
+# additional_redirect_urls (localhost entry) instead.
+site_url = "https://smartmanagementsystem.vercel.app"
 # A list of *exact* URLs that auth providers are permitted to redirect to post authentication.
 additional_redirect_urls = [
</file context>

# A list of *exact* URLs that auth providers are permitted to redirect to post authentication.
additional_redirect_urls = [
"http://localhost:3000/auth/callback",
Expand Down
Loading