Skip to content

Commit bdadcde

Browse files
Vayun Godaraclaude
authored andcommitted
fix: address code review feedback + fix pre-existing lint errors
Review fixes: - Sidebar: use marginLeft: 80 instead of paddingLeft override (was clobbering CSS module's 32px) - 404 page: extract to CSS module, use 100dvh, link to "/" not "/dashboard" - CI: add env vars to build job so Next.js build succeeds Pre-existing lint fixes (exposed by new CI): - ActivityComments: move fetchComments to click handler instead of useEffect - DailySummaryCard: rename useStreakFreeze → applyStreakFreeze (not a React hook) - XPBar: move xpChanged logic into fetch callback to avoid sync setState in effect Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2cab5b0 commit bdadcde

8 files changed

Lines changed: 79 additions & 72 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ jobs:
2020
- run: npm ci
2121

2222
- run: npx next build
23+
env:
24+
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
25+
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
2326

2427
- run: npm run lint
2528

app/dashboard/DashboardLayout.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export default function DashboardLayout({ user, children }) {
4444
<main
4545
id="main-content"
4646
className={styles.main}
47-
style={{ marginLeft: sidebarExpanded ? 260 : 72, paddingLeft: 16 }}
47+
style={{ marginLeft: sidebarExpanded ? 260 : 80 }}
4848
>
4949
<ErrorBoundary message="Something went wrong loading this page.">
5050
<motion.div

app/not-found.js

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

34
export const metadata = { title: '404 | LockIn' };
45

56
export default function NotFound() {
67
return (
7-
<div style={{
8-
minHeight: '100vh',
9-
display: 'flex',
10-
alignItems: 'center',
11-
justifyContent: 'center',
12-
background: 'var(--bg-primary)',
13-
color: 'var(--text-primary)',
14-
fontFamily: 'var(--font-sans)',
15-
}}>
16-
<div style={{ textAlign: 'center' }}>
17-
<h1 style={{
18-
fontSize: 'var(--text-6xl)',
19-
fontWeight: 'var(--font-extrabold)',
20-
marginBottom: '16px',
21-
background: 'var(--gradient-primary)',
22-
WebkitBackgroundClip: 'text',
23-
WebkitTextFillColor: 'transparent',
24-
backgroundClip: 'text',
25-
}}>
26-
404
27-
</h1>
28-
<p style={{
29-
fontSize: 'var(--text-xl)',
30-
color: 'var(--text-secondary)',
31-
marginBottom: '32px',
32-
}}>
33-
This page could not be found.
34-
</p>
35-
<Link
36-
href="/dashboard"
37-
style={{
38-
display: 'inline-block',
39-
padding: '12px 24px',
40-
background: 'var(--gradient-primary)',
41-
color: 'white',
42-
borderRadius: 'var(--radius-full)',
43-
fontWeight: 'var(--font-semibold)',
44-
fontSize: 'var(--text-base)',
45-
textDecoration: 'none',
46-
boxShadow: 'var(--shadow-md)',
47-
transition: 'transform 200ms, box-shadow 200ms',
48-
}}
49-
>
50-
Back to Dashboard
8+
<div className={styles.container}>
9+
<div className={styles.content}>
10+
<h1 className={styles.heading}>404</h1>
11+
<p className={styles.message}>This page could not be found.</p>
12+
<Link href="/" className={styles.link}>
13+
Go home
5114
</Link>
5215
</div>
5316
</div>

app/not-found.module.css

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
.container {
2+
min-height: 100vh;
3+
min-height: 100dvh;
4+
display: flex;
5+
align-items: center;
6+
justify-content: center;
7+
background: var(--bg-primary);
8+
color: var(--text-primary);
9+
font-family: var(--font-sans);
10+
}
11+
12+
.content {
13+
text-align: center;
14+
}
15+
16+
.heading {
17+
font-size: var(--text-6xl);
18+
font-weight: var(--font-extrabold);
19+
margin-bottom: 16px;
20+
background: var(--gradient-primary);
21+
-webkit-background-clip: text;
22+
-webkit-text-fill-color: transparent;
23+
background-clip: text;
24+
}
25+
26+
.message {
27+
font-size: var(--text-xl);
28+
color: var(--text-secondary);
29+
margin-bottom: 32px;
30+
}
31+
32+
.link {
33+
display: inline-block;
34+
padding: 12px 24px;
35+
background: var(--gradient-primary);
36+
color: white;
37+
border-radius: var(--radius-full);
38+
font-weight: var(--font-semibold);
39+
font-size: var(--text-base);
40+
text-decoration: none;
41+
box-shadow: var(--shadow-md);
42+
transition: transform var(--transition-base), box-shadow var(--transition-base);
43+
}
44+
45+
.link:hover {
46+
transform: translateY(-2px);
47+
box-shadow: var(--shadow-lg);
48+
}

components/ActivityComments.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useState, useEffect, useMemo } from 'react';
3+
import { useState, useCallback, useMemo } from 'react';
44
import { motion, AnimatePresence } from 'framer-motion';
55
import Image from 'next/image';
66
import { createClient } from '@/lib/supabase/client';
@@ -17,18 +17,18 @@ export default function ActivityComments({ activityId, initialCount = 0 }) {
1717
const [commentCount, setCommentCount] = useState(initialCount);
1818
const supabase = useMemo(() => createClient(), []);
1919

20-
useEffect(() => {
21-
if (showComments) {
22-
fetchComments();
23-
}
24-
}, [showComments]);
25-
26-
const fetchComments = async () => {
20+
const fetchComments = useCallback(async () => {
2721
setIsLoading(true);
2822
const result = await getComments(supabase, activityId);
2923
setComments(result.data);
3024
setCommentCount(result.data.length);
3125
setIsLoading(false);
26+
}, [supabase, activityId]);
27+
28+
const toggleComments = () => {
29+
const next = !showComments;
30+
setShowComments(next);
31+
if (next) fetchComments();
3232
};
3333

3434
const handleSubmit = async (e) => {
@@ -56,7 +56,7 @@ export default function ActivityComments({ activityId, initialCount = 0 }) {
5656
<div className={styles.container}>
5757
<button
5858
className={styles.toggleButton}
59-
onClick={() => setShowComments(!showComments)}
59+
onClick={toggleComments}
6060
>
6161
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
6262
<path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>

components/DailySummaryCard.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useState, useEffect, useMemo } from 'react';
44
import { motion } from 'framer-motion';
55
import { createClient } from '@/lib/supabase/client';
66
import { fadeInUp, streakCelebration } from '@/lib/animations';
7-
import { checkStreakAtRisk, useStreakFreeze, getStreakFreezeStatus } from '@/lib/streaks-advanced';
7+
import { checkStreakAtRisk, applyStreakFreeze, getStreakFreezeStatus } from '@/lib/streaks-advanced';
88
import { useToast } from '@/components/Toast';
99
import styles from './DailySummaryCard.module.css';
1010

@@ -111,7 +111,7 @@ export default function DailySummaryCard({ userId, refreshKey }) {
111111
}, [userId, supabase, refreshKey]);
112112

113113
const handleUseFreeze = async () => {
114-
const result = await useStreakFreeze(supabase);
114+
const result = await applyStreakFreeze(supabase);
115115
if (result.success) {
116116
toast.success('Streak freeze activated!');
117117
setStreakRisk({ atRisk: false, streak: streakRisk.streak });

components/XPBar.js

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,19 @@ export default function XPBar({ userId, refreshKey }) {
2323
.single();
2424

2525
if (data) {
26-
setXP(data.total_xp || 0);
27-
setLevel(data.level || getLevelFromXP(data.total_xp || 0));
26+
const newXP = data.total_xp || 0;
27+
if (prevXpRef.current !== null && newXP > 0 && newXP !== prevXpRef.current) {
28+
setXpChanged(true);
29+
setTimeout(() => setXpChanged(false), 600);
30+
}
31+
prevXpRef.current = newXP;
32+
setXP(newXP);
33+
setLevel(data.level || getLevelFromXP(newXP));
2834
}
2935
}
3036
if (userId) fetchXP();
3137
}, [userId, supabase, refreshKey]);
3238

33-
useEffect(() => {
34-
if (prevXpRef.current === null) {
35-
prevXpRef.current = xp;
36-
return;
37-
}
38-
if (xp > 0 && xp !== prevXpRef.current) {
39-
setXpChanged(true);
40-
const timer = setTimeout(() => setXpChanged(false), 600);
41-
prevXpRef.current = xp;
42-
return () => clearTimeout(timer);
43-
}
44-
}, [xp]);
45-
4639
const progress = getProgressToNextLevel(xp);
4740

4841
return (

lib/streaks-advanced.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export async function checkStreakAtRisk(supabase, userId) {
5353
/**
5454
* Use a streak freeze (1 free per week)
5555
*/
56-
export async function useStreakFreeze(supabase) {
56+
export async function applyStreakFreeze(supabase) {
5757
try {
5858
const { data: authData } = await supabase.auth.getUser();
5959
const userId = authData?.user?.id;

0 commit comments

Comments
 (0)