Skip to content

Commit 5c60ceb

Browse files
Vayun Godaraclaude
authored andcommitted
fix: address team review findings
Security: - Add user_id filter to focus_sessions query (defense in depth, not just RLS) Performance: - Clean up XPBar flash setTimeout on unmount via ref Architecture: - Change /login redirect from permanent (301) to temporary (302) - Remove dead metadata export from not-found.js (Next.js ignores it) - Move CI e2e env vars to job level so Playwright dev server inherits them Accessibility: - Add forced-colors media query so 404 heading is visible in High Contrast Mode - Add :focus-visible style on 404 CTA link for keyboard users - Use <main> landmark on 404 page for screen readers Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bdadcde commit 5c60ceb

6 files changed

Lines changed: 30 additions & 10 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ jobs:
2929
e2e:
3030
runs-on: ubuntu-latest
3131
needs: build-and-lint
32+
env:
33+
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
34+
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
3235
steps:
3336
- uses: actions/checkout@v4
3437

@@ -42,6 +45,3 @@ jobs:
4245
- run: npx playwright install --with-deps chromium
4346

4447
- run: npm run test:e2e
45-
env:
46-
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
47-
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}

app/dashboard/focus/FocusPageClient.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export default function FocusPageClient({ user }) {
3838
const { data, error } = await supabase
3939
.from('focus_sessions')
4040
.select('*')
41+
.eq('user_id', user.id)
4142
.order('started_at', { ascending: false })
4243
.limit(10);
4344

app/not-found.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
import Link from 'next/link';
22
import styles from './not-found.module.css';
33

4-
export const metadata = { title: '404 | LockIn' };
5-
64
export default function NotFound() {
75
return (
86
<div className={styles.container}>
9-
<div className={styles.content}>
7+
<main className={styles.content}>
108
<h1 className={styles.heading}>404</h1>
119
<p className={styles.message}>This page could not be found.</p>
1210
<Link href="/" className={styles.link}>
1311
Go home
1412
</Link>
15-
</div>
13+
</main>
1614
</div>
1715
);
1816
}

app/not-found.module.css

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,20 @@
4242
transition: transform var(--transition-base), box-shadow var(--transition-base);
4343
}
4444

45-
.link:hover {
45+
.link:hover,
46+
.link:focus-visible {
4647
transform: translateY(-2px);
4748
box-shadow: var(--shadow-lg);
4849
}
50+
51+
.link:focus-visible {
52+
outline: 2px solid var(--text-primary, #fff);
53+
outline-offset: 2px;
54+
}
55+
56+
@media (forced-colors: active) {
57+
.heading {
58+
-webkit-text-fill-color: currentColor;
59+
background: none;
60+
}
61+
}

components/XPBar.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export default function XPBar({ userId, refreshKey }) {
1212
const [level, setLevel] = useState(1);
1313
const [xpChanged, setXpChanged] = useState(false);
1414
const prevXpRef = useRef(null);
15+
const flashTimerRef = useRef(null);
1516
const supabase = useMemo(() => createClient(), []);
1617

1718
useEffect(() => {
@@ -25,8 +26,9 @@ export default function XPBar({ userId, refreshKey }) {
2526
if (data) {
2627
const newXP = data.total_xp || 0;
2728
if (prevXpRef.current !== null && newXP > 0 && newXP !== prevXpRef.current) {
29+
if (flashTimerRef.current) clearTimeout(flashTimerRef.current);
2830
setXpChanged(true);
29-
setTimeout(() => setXpChanged(false), 600);
31+
flashTimerRef.current = setTimeout(() => setXpChanged(false), 600);
3032
}
3133
prevXpRef.current = newXP;
3234
setXP(newXP);
@@ -36,6 +38,12 @@ export default function XPBar({ userId, refreshKey }) {
3638
if (userId) fetchXP();
3739
}, [userId, supabase, refreshKey]);
3840

41+
useEffect(() => {
42+
return () => {
43+
if (flashTimerRef.current) clearTimeout(flashTimerRef.current);
44+
};
45+
}, []);
46+
3947
const progress = getProgressToNextLevel(xp);
4048

4149
return (

next.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const nextConfig = {
1717
{
1818
source: '/login',
1919
destination: '/',
20-
permanent: true,
20+
permanent: false,
2121
},
2222
];
2323
},

0 commit comments

Comments
 (0)