Fix/study timer completion feedback#998
Conversation
…gress bar and red flash
|
@OmanshiRaj is attempting to deploy a commit to the durdana3105's projects Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughFour unrelated UI improvements: ChangesUI Feature Additions
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Suggested labels
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 Biome (2.4.16)src/components/studyroom/StudyTimer.tsxFile contains syntax errors that prevent linting: Line 77: expected Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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/components/PeerCard.tsx`:
- Line 183: The onClick handler in the PeerCard component uses
navigate(`/profile/${peer.id}`) which attempts to navigate to a route that does
not exist in the current router configuration. The router only recognizes the
/profile path (without a dynamic ID segment), and Profile.tsx currently renders
the authenticated session user's profile rather than accepting a user ID from
the URL. Change the navigate call from the dynamic route pattern to simply
navigate to /profile, or if peer-specific profile viewing is needed, coordinate
with the router and Profile.tsx page to support the dynamic route pattern by
updating both the route definition and the Profile component to accept and use
the URL parameter.
In `@src/components/studyroom/StudyTimer.tsx`:
- Line 77: The StudyTimer component is missing its closing brace at the end of
the file. After the final `);` statement at line 77, add a closing brace `}` to
properly close the StudyTimer function definition. This will resolve the parse
error that prevents the component from compiling.
In `@src/pages/AnonymousDoubts.tsx`:
- Around line 96-105: The character counter display is present but the actual
textarea or input field that captures user input is missing from this div
element with className="relative". Add a textarea control inside this div
(before the character counter span) that accepts user input, has an onChange
handler to update the `text` state variable, includes appropriate placeholder
text and styling to be visible and usable, and respects the MAX_CHARS limit to
prevent exceeding the character boundary. This will allow users to actually
enter doubt content and make the "Post Doubt" button reachable. The same
textarea control needs to be added at the other affected location as well.
- Around line 75-80: Replace the non-atomic read-modify-write pattern in the
upvote handler with an atomic database operation. Instead of reading the current
upvotes value from client state with doubts.find((d) => d.id === id)!.upvotes +
1 and sending that absolute value to the database, create a Supabase RPC
function (or use the rpc() interface) that performs the increment atomically at
the database level using SQL like update doubts set upvotes = upvotes + 1 where
id = id. This ensures concurrent upvotes do not overwrite each other and
eliminates the brittle non-null assertion. Follow the same pattern already
implemented for resource votes in your codebase.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 6eee2936-9e78-4bff-b115-7fc5ed6993cc
📒 Files selected for processing (4)
src/components/PeerCard.tsxsrc/components/studyroom/LiveCodeRunner.tsxsrc/components/studyroom/StudyTimer.tsxsrc/pages/AnonymousDoubts.tsx
| <Button | ||
| size="sm" | ||
| variant="outline" | ||
| onClick={() => navigate(`/profile/${peer.id}`)} |
There was a problem hiding this comment.
View Profile navigates to a path that is not routable today.
navigate(\/profile/${peer.id}`) does not match the current router contract (/profile), and src/pages/Profile.tsx` currently reads the authenticated session user, not a URL id. This makes the button behavior incorrect at runtime.
🤖 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/components/PeerCard.tsx` at line 183, The onClick handler in the PeerCard
component uses navigate(`/profile/${peer.id}`) which attempts to navigate to a
route that does not exist in the current router configuration. The router only
recognizes the /profile path (without a dynamic ID segment), and Profile.tsx
currently renders the authenticated session user's profile rather than accepting
a user ID from the URL. Change the navigate call from the dynamic route pattern
to simply navigate to /profile, or if peer-specific profile viewing is needed,
coordinate with the router and Profile.tsx page to support the dynamic route
pattern by updating both the route definition and the Profile component to
accept and use the URL parameter.
| </div> | ||
| ); | ||
| } No newline at end of file | ||
| ); No newline at end of file |
There was a problem hiding this comment.
Missing closing brace breaks component parsing.
The component ends after ); without a final } for StudyTimer, which causes a compile-time parse failure.
Proposed fix
return (
<div className={`bg-slate-900 border rounded-xl p-4 transition-colors ${isWarning ? "border-red-500" : "border-slate-700"}`}>
...
</div>
);
+}📝 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.
| ); | |
| ); | |
| } |
🧰 Tools
🪛 Biome (2.4.16)
[error] 77-77: expected } but instead the file ends
(parse)
🤖 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/components/studyroom/StudyTimer.tsx` at line 77, The StudyTimer component
is missing its closing brace at the end of the file. After the final `);`
statement at line 77, add a closing brace `}` to properly close the StudyTimer
function definition. This will resolve the parse error that prevents the
component from compiling.
Source: Linters/SAST tools
| setDoubts((prev) => prev.map((d) => (d.id === id ? { ...d, upvotes: d.upvotes + 1 } : d))); | ||
|
|
||
| const { error } = await (supabase as any) | ||
| .from("doubts") | ||
| .update({ upvotes: doubts.find((d) => d.id === id)!.upvotes + 1 }) | ||
| .eq("id", id); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Look for Supabase SQL functions/migrations that implement atomic upvote increment.
fd -HI -t f --extension sql
rg -n -i "create\\s+function|rpc|upvote|increment|doubts"
# Look for existing client-side RPC usage patterns that could be reused.
rg -n --type ts --type tsx "\\.rpc\\("
# Locate all doubts upvote writes for consistency review.
rg -n --type ts --type tsx "from\\(\"doubts\"\\).*update\\(|upvotes\\s*:"Repository: durdana3105/peer-learning
Length of output: 31858
🏁 Script executed:
cat -n supabase/migrations/20260608000001_anonymous_doubts.sqlRepository: durdana3105/peer-learning
Length of output: 1176
🏁 Script executed:
sed -n '71,90p' src/pages/AnonymousDoubts.tsxRepository: durdana3105/peer-learning
Length of output: 850
🏁 Script executed:
rg -n "upvote.*doubts|doubts.*upvote" --type ts --type tsx -A 5 -B 5Repository: durdana3105/peer-learning
Length of output: 97
🏁 Script executed:
wc -l supabase/migrations/20260608000001_anonymous_doubts.sqlRepository: durdana3105/peer-learning
Length of output: 128
🏁 Script executed:
cat supabase/migrations/20260608000001_anonymous_doubts.sqlRepository: durdana3105/peer-learning
Length of output: 973
🏁 Script executed:
rg -n "upvote.*rpc|rpc.*upvote" --type ts --type tsx --type sqlRepository: durdana3105/peer-learning
Length of output: 97
🏁 Script executed:
rg -n "doubts.*update|update.*doubts" supabase/migrations/ --type sql -A 3 -B 1Repository: durdana3105/peer-learning
Length of output: 51
🏁 Script executed:
rg -n "update.*doubt|doubt.*update" supabase/migrations/ -A 2 -B 1Repository: durdana3105/peer-learning
Length of output: 51
🏁 Script executed:
rg -n "create.*function.*upvote|CREATE FUNCTION.*upvote" supabase/migrations/ -iRepository: durdana3105/peer-learning
Length of output: 51
🏁 Script executed:
rg -n "policy.*update.*doubt|policy.*doubt.*update" supabase/migrations/ -i -A 3Repository: durdana3105/peer-learning
Length of output: 51
🏁 Script executed:
# Check if there are other doubt-related migrations after the initial one
fd -t f --extension sql supabase/migrations/ | grep -i doubtRepository: durdana3105/peer-learning
Length of output: 455
🏁 Script executed:
rg -n "UPDATE|policy" supabase/migrations/20260608000001_anonymous_doubts.sqlRepository: durdana3105/peer-learning
Length of output: 168
🏁 Script executed:
cat -n src/pages/AnonymousDoubts.tsx | head -100Repository: durdana3105/peer-learning
Length of output: 3853
🏁 Script executed:
# Check if there's a bypass or special RLS that allows updates
rg -n "on doubts for update" supabase/migrations/ --type sql -iRepository: durdana3105/peer-learning
Length of output: 51
🏁 Script executed:
# Compare with resource voting which was mentioned as using atomic SQL
cat -n supabase/migrations/20260601000000_resource_votes_and_saves.sql | grep -A 10 -B 5 "upvotes"Repository: durdana3105/peer-learning
Length of output: 1906
Use an atomic database operation to increment upvotes, not client-derived absolute values.
The current upvote implementation reads the upvotes value from client state (doubts.find(...).upvotes + 1) and writes it back to the database. This read-modify-write pattern is non-atomic and vulnerable to concurrent vote loss: when multiple users upvote simultaneously, later writes overwrite earlier increments with stale values. Additionally, the non-null assertion is brittle if the item disappears from local state.
Implement atomic upvote increments using SQL-level operations, following the pattern used by resource votes: create an RPC function with a trigger that performs update doubts set upvotes = upvotes + 1 where id = ? as a single atomic database operation, or use Supabase's rpc() interface to delegate the increment to the database.
🤖 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/pages/AnonymousDoubts.tsx` around lines 75 - 80, Replace the non-atomic
read-modify-write pattern in the upvote handler with an atomic database
operation. Instead of reading the current upvotes value from client state with
doubts.find((d) => d.id === id)!.upvotes + 1 and sending that absolute value to
the database, create a Supabase RPC function (or use the rpc() interface) that
performs the increment atomically at the database level using SQL like update
doubts set upvotes = upvotes + 1 where id = id. This ensures concurrent upvotes
do not overwrite each other and eliminates the brittle non-null assertion.
Follow the same pattern already implemented for resource votes in your codebase.
| <div className="relative"> | ||
|
|
||
| <span className={`absolute bottom-2 right-2 text-xs select-none ${ | ||
| text.length > MAX_CHARS ? "text-red-500 font-semibold" | ||
| : text.length >= MAX_CHARS * 0.9 ? "text-amber-500" | ||
| : "text-slate-400"}`} | ||
| > | ||
| {text.length}/{MAX_CHARS} | ||
| </span> | ||
| </div> |
There was a problem hiding this comment.
Doubt content input appears removed, which blocks posting.
The form now shows a character counter but no visible control updates text; with disabled={... || !text.trim() ...} the “Post Doubt” action becomes effectively unreachable.
Also applies to: 122-122
🤖 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/pages/AnonymousDoubts.tsx` around lines 96 - 105, The character counter
display is present but the actual textarea or input field that captures user
input is missing from this div element with className="relative". Add a textarea
control inside this div (before the character counter span) that accepts user
input, has an onChange handler to update the `text` state variable, includes
appropriate placeholder text and styling to be visible and usable, and respects
the MAX_CHARS limit to prevent exceeding the character boundary. This will allow
users to actually enter doubt content and make the "Post Doubt" button
reachable. The same textarea control needs to be added at the other affected
location as well.
Closes #924
Added else if (isRunning && seconds === 0) branch to stop the timer and fire a toast.success when countdown completes
Merged separate Start/Pause buttons into a single toggle button (handleToggle) — prevents duplicate setInterval calls if Start was clicked while already running
Added a progress bar that fills as time elapses (blue → red in last 60s → green on completion)
Timer display and border flash red during the last 60 seconds
Toggle button is disabled at seconds === 0 to prevent interaction after completion
handleReset now also clears hasCompleted
Summary by CodeRabbit
Release Notes