feat: add interactive onboarding tour with settings and compose page support - #125
Conversation
|
@yashyrs027 is attempting to deploy a commit to the itzzavdhesh's projects Team on Vercel. A member of the Team first needs to authorize it. |
✍️ DCO Sign-off NeededHey @yashyrs027! 👋 One or more commits in this PR are missing a Warning
How to fix: For the latest commit: git commit --amend --signoff
git push --force-with-leaseFor multiple commits, replace git rebase --signoff HEAD~N
git push --force-with-leaseThis comment will update automatically after you push. 🤖 VoiceForge Automation · Updates automatically on edits |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughAdds a React Joyride onboarding tour with persistent state, cross-tab navigation, annotated UI targets, App integration, Settings restart support, and Joyride-specific styling. ChangesInteractive Onboarding Tour Feature
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant App
participant OnboardingTour
participant Joyride
participant useOnboarding
User->>App: Open application
App->>OnboardingTour: Render activeTab and onSelectTab
OnboardingTour->>useOnboarding: Read tour state
OnboardingTour->>Joyride: Render controlled step
User->>Joyride: Navigate or skip
Joyride->>OnboardingTour: Send lifecycle callback
OnboardingTour->>App: Select target tab
OnboardingTour->>useOnboarding: Stop tour on completion
useOnboarding->>useOnboarding: Persist completion state
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 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 💡 1🛠️ Fix failing CI checks 💡
🧪 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.5.6)client/src/components/TextToSpeech.jsxFile contains syntax errors that prevent linting: Line 238: Expected corresponding JSX closing tag for 'button'. client/src/App.jsxFile contains syntax errors that prevent linting: Line 158: Expected corresponding JSX closing tag for 'main'. client/src/pages/Settings.jsxFile contains syntax errors that prevent linting: Line 370: expected 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. Comment |
🎉 PR Ready for Mentor ReviewHey @yashyrs027! 👋 Your PR passed all checks and is now in the SSOC review queue. Note 🔗 Closing: #109 · 📐 1535 lines across 11 file(s) · 📬 Already requested or no eligible reviewer found @joyprakashk @rushi-k12, this PR is ready for your review — please confirm scope, check behavior and tests, then approve or request changes. Important This is not an approval. Please wait for mentor feedback before expecting a merge. If changes are requested, push them to this same branch and keep the PR focused on the linked issue. 🤖 VoiceForge Automation · Updates automatically on edits |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
client/src/hooks/useOnboarding.js (1)
6-12: ⚡ Quick winDocument the localStorage error fallback behavior.
Returning
truewhen localStorage access fails (line 10) prevents the tour from auto-starting in private browsing or restricted contexts. While this is reasonable defensive behavior, it's not immediately obvious and could confuse future maintainers.📝 Suggested clarification
Add a brief comment explaining the rationale:
function hasCompletedTour() { try { return localStorage.getItem(ONBOARDING_STORAGE_KEY) === "true"; } catch { + // Treat as completed in restricted contexts to prevent auto-start errors return true; } }🤖 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 `@client/src/hooks/useOnboarding.js` around lines 6 - 12, Add a brief inline comment inside the hasCompletedTour function (near the try/catch and ONBOARDING_STORAGE_KEY usage) explaining that when localStorage access throws (e.g., private browsing or restricted contexts) the function intentionally returns true to prevent auto-starting the onboarding tour; note that this is a defensive choice to avoid repeatedly showing the tour when persistence is unavailable.
🤖 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 `@client/src/components/OnboardingTour.jsx`:
- Around line 166-169: The onboarding is forcing restarts because useOnboarding
is called with ignoreCompletion: true; remove the ignoreCompletion option from
the useOnboarding call in the OnboardingTour component (leave autoStart: true
and the destructured runTour/stopTour as-is) so the hook respects stored
completion state and the tour only auto-starts for first-time users; keep
resetTour behavior unchanged via settings event sync.
- Around line 1-8: The OnboardingTour component imports react-joyride (ACTIONS,
EVENTS, Joyride, STATUS) but the package is missing from project manifests; add
react-joyride to the client's dependencies by running in the client folder
(e.g., cd client && npm install react-joyride --save) or add "react-joyride":
"<appropriate-version>" to client/package.json and run npm install to update
package-lock.json so the import in client/src/components/OnboardingTour.jsx
resolves.
---
Nitpick comments:
In `@client/src/hooks/useOnboarding.js`:
- Around line 6-12: Add a brief inline comment inside the hasCompletedTour
function (near the try/catch and ONBOARDING_STORAGE_KEY usage) explaining that
when localStorage access throws (e.g., private browsing or restricted contexts)
the function intentionally returns true to prevent auto-starting the onboarding
tour; note that this is a defensive choice to avoid repeatedly showing the tour
when persistence is unavailable.
🪄 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: 32f667d6-59e1-44fa-9ec2-db5e4fb56606
📒 Files selected for processing (11)
client/src/App.jsxclient/src/components/OnboardingTour.jsxclient/src/components/TextToSpeech.jsxclient/src/components/VideoPreview.jsxclient/src/components/VirtualCamera.jsxclient/src/components/VoiceForge.jsxclient/src/components/VoiceRecorder.jsxclient/src/hooks/useOnboarding.jsclient/src/pages/Onboarding.jsxclient/src/pages/Settings.jsxclient/src/styles.css
| import React from "react"; | ||
| import { | ||
| ACTIONS, | ||
| EVENTS, | ||
| Joyride, | ||
| STATUS, | ||
| } from "react-joyride"; | ||
| import useOnboarding from "../hooks/useOnboarding.js"; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify react-joyride is missing from package.json and show installation command
echo "=== Checking package.json for react-joyride ==="
if grep -q "react-joyride" package.json; then
echo "✓ Found in package.json"
jq '.dependencies["react-joyride"], .devDependencies["react-joyride"]' package.json
else
echo "✗ Not found in package.json"
fi
echo ""
echo "=== Installation command ==="
echo "npm install react-joyride"
echo ""
echo "=== Or with specific version ==="
echo "npm install react-joyride@^2.7.0"Repository: itzzavdhesh/VoiceForge
Length of output: 265
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Find all package.json files ==="
find . -name package.json -print
echo
echo "=== Check react-joyride presence in all package.json ==="
while IFS= read -r pj; do
echo "--- $pj ---"
node - <<'NODE' "$pj"
const fs = require('fs');
const path = process.argv[1];
const file = process.argv[2] || path; // fallback
const p = file;
const data = JSON.parse(fs.readFileSync(p,'utf8'));
const deps = data.dependencies || {};
const dev = data.devDependencies || {};
const v = deps["react-joyride"] ?? dev["react-joyride"] ?? null;
console.log(v ? `react-joyride: ${v}` : "react-joyride: not present");
NODE "$pj"
done < <(find . -name package.json -print)
echo
echo "=== Search lockfiles for react-joyride ==="
rg -n --hidden --no-ignore-vcs "react-joyride" package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null || trueRepository: itzzavdhesh/VoiceForge
Length of output: 393
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Find all package.json files ==="
mapfile -t pjs < <(find . -name package.json -print)
printf '%s\n' "${pjs[@]}"
echo
echo "=== Check react-joyride presence in all package.json ==="
for pj in "${pjs[@]}"; do
echo "--- $pj ---"
node - "$pj" <<'NODE'
const fs = require('fs');
const pj = process.argv[2];
const data = JSON.parse(fs.readFileSync(pj,'utf8'));
const deps = data.dependencies || {};
const dev = data.devDependencies || {};
const v = deps["react-joyride"] ?? dev["react-joyride"] ?? null;
console.log(v ? `react-joyride: ${v}` : "react-joyride: not present");
NODE
done
echo
echo "=== Search lockfiles for react-joyride ==="
for f in package-lock.json yarn.lock pnpm-lock.yaml; do
if [ -f "$f" ]; then
echo "--- $f ---"
rg -n "react-joyride" "$f" || true
fi
doneRepository: itzzavdhesh/VoiceForge
Length of output: 177
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Find all package.json files ==="
mapfile -t pjs < <(find . -name package.json -print)
printf '%s\n' "${pjs[@]}"
echo
echo "=== Check react-joyride presence in all package.json ==="
for pj in "${pjs[@]}"; do
echo "--- $pj ---"
node - "$pj" <<'NODE'
const fs = require('fs');
const pj = process.argv[2];
const data = JSON.parse(fs.readFileSync(pj,'utf8'));
const deps = data.dependencies || {};
const dev = data.devDependencies || {};
const v = deps["react-joyride"] ?? dev["react-joyride"] ?? null;
console.log(v ? `react-joyride: ${v}` : "react-joyride: not present");
NODE
done
echo
echo "=== Search lockfiles for react-joyride ==="
for f in package-lock.json yarn.lock pnpm-lock.yaml; do
if [ -f "$f" ]; then
echo "--- $f ---"
rg -n "react-joyride" "$f" || true
fi
doneRepository: itzzavdhesh/VoiceForge
Length of output: 177
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Find all package.json files ==="
mapfile -t pjs < <(find . -name package.json -print)
printf '%s\n' "${pjs[@]}"
echo
echo "=== Check react-joyride presence in all package.json ==="
for pj in "${pjs[@]}"; do
echo "--- $pj ---"
node - "$pj" <<'NODE'
const fs = require('fs');
const pj = process.argv[2];
const data = JSON.parse(fs.readFileSync(pj,'utf8'));
const deps = data.dependencies || {};
const dev = data.devDependencies || {};
const v = deps["react-joyride"] ?? dev["react-joyride"] ?? null;
console.log(v ? `react-joyride: ${v}` : "react-joyride: not present");
NODE
done
echo
echo "=== Search lockfiles for react-joyride ==="
for f in package-lock.json yarn.lock pnpm-lock.yaml; do
if [ -f "$f" ]; then
echo "--- $f ---"
rg -n "react-joyride" "$f" || true
fi
doneRepository: itzzavdhesh/VoiceForge
Length of output: 177
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Find all package.json files ==="
mapfile -t pjs < <(find . -name package.json -print)
printf '%s\n' "${pjs[@]}"
echo
echo "=== Check react-joyride presence in all package.json files ==="
node <<'NODE'
const fs = require('fs');
const { execSync } = require('child_process');
const pjs = execSync('find . -name package.json -print', { encoding: 'utf8' })
.trim().split('\n').filter(Boolean);
for (const pj of pjs) {
const data = JSON.parse(fs.readFileSync(pj, 'utf8'));
const deps = data.dependencies || {};
const dev = data.devDependencies || {};
const v = deps["react-joyride"] ?? dev["react-joyride"] ?? null;
console.log(`${pj}: ${v ? `react-joyride: ${v}` : 'react-joyride: not present'}`);
}
NODE
echo
echo "=== Search lockfiles for react-joyride ==="
for f in package-lock.json yarn.lock pnpm-lock.yaml; do
if [ -f "$f" ]; then
echo "--- $f ---"
rg -n "react-joyride" "$f" || true
fi
done
# also search recursively for lockfiles in case they are nested
echo
echo "=== Search nested lockfiles for react-joyride ==="
for lf in $(find . -name package-lock.json -o -name yarn.lock -o -name pnpm-lock.yaml 2>/dev/null); do
echo "--- $lf ---"
rg -n "react-joyride" "$lf" || true
doneRepository: itzzavdhesh/VoiceForge
Length of output: 177
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Find all package.json files ==="
mapfile -t pjs < <(find . -name package.json -print 2>/dev/null || true)
printf '%s\n' "${pjs[@]}" | cat
echo
echo "=== react-joyride presence in all package.json files ==="
for pj in "${pjs[@]}"; do
echo "--- $pj ---"
if rg -n '"react-joyride"\s*:' "$pj" >/dev/null 2>&1; then
val="$(jq -r '.dependencies["react-joyride"] // .devDependencies["react-joyride"] // empty' "$pj" 2>/dev/null || true)"
if [ -n "$val" ]; then
echo "react-joyride: $val"
else
echo "react-joyride: present (value not readable)"
fi
else
echo "react-joyride: not present"
fi
done
echo
echo "=== Search lockfiles at repo root (if present) ==="
for f in package-lock.json yarn.lock pnpm-lock.yaml; do
if [ -f "$f" ]; then
echo "--- $f ---"
rg -n "react-joyride" "$f" || true
fi
done
echo
echo "=== Search nested lockfiles ==="
mapfile -t lfs < <(find . -name package-lock.json -o -name yarn.lock -o -name pnpm-lock.yaml 2>/dev/null || true)
if [ "${`#lfs`[@]}" -eq 0 ]; then
echo "No nested lockfiles found."
else
for lf in "${lfs[@]}"; do
echo "--- $lf ---"
rg -n "react-joyride" "$lf" || true
done
fiRepository: itzzavdhesh/VoiceForge
Length of output: 177
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== package.json files ==="
ls -1 package.json client/package.json server/package.json 2>/dev/null || true
echo
echo "=== react-joyride in package.json files (deps/devDeps) ==="
for pj in package.json client/package.json server/package.json; do
if [ -f "$pj" ]; then
echo "--- $pj ---"
rg -n '"react-joyride"\s*:' "$pj" || echo "not present"
fi
done
echo
echo "=== react-joyride in top-level lockfiles (if present) ==="
for lf in package-lock.json yarn.lock pnpm-lock.yaml; do
if [ -f "$lf" ]; then
echo "--- $lf ---"
rg -n "react-joyride" "$lf" || echo "no matches"
fi
done
echo
echo "=== react-joyride in nested lockfiles (limited to common names, if any) ==="
for lf in $(find . -name package-lock.json -o -name yarn.lock -o -name pnpm-lock.yaml 2>/dev/null); do
echo "--- $lf ---"
rg -n "react-joyride" "$lf" || echo "no matches"
doneRepository: itzzavdhesh/VoiceForge
Length of output: 531
Add missing react-joyride dependency
react-joyride is imported in client/src/components/OnboardingTour.jsx but is not listed in dependencies/devDependencies in package.json, client/package.json, or server/package.json, and it doesn’t appear in package-lock.json. Add it (e.g., cd client && npm i react-joyride, or add to client/package.json dependencies).
🧰 Tools
🪛 GitHub Actions: CI / 0_build.txt
[error] 1-1: Vite/Rollup failed to resolve import "react-joyride" from "client/src/components/OnboardingTour.jsx". This is most likely unintended and can break the app at runtime. Fix by installing/adding the dependency or configuring Rollup externalize options (build.rollupOptions.external).
🪛 GitHub Actions: CI / build
[error] 1-1: Vite/Rollup failed to resolve import "react-joyride" from "client/src/components/OnboardingTour.jsx". Ensure the dependency is installed and resolvable (or add it to build.rollupOptions.external if intended).
🤖 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 `@client/src/components/OnboardingTour.jsx` around lines 1 - 8, The
OnboardingTour component imports react-joyride (ACTIONS, EVENTS, Joyride,
STATUS) but the package is missing from project manifests; add react-joyride to
the client's dependencies by running in the client folder (e.g., cd client &&
npm install react-joyride --save) or add "react-joyride":
"<appropriate-version>" to client/package.json and run npm install to update
package-lock.json so the import in client/src/components/OnboardingTour.jsx
resolves.
There was a problem hiding this comment.
4 issues found across 11 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="client/src/pages/Settings.jsx">
<violation number="1" location="client/src/pages/Settings.jsx:126">
P2: Restart button does not restart from beginning of onboarding flow. It restarts at Settings because current-tab-based initial step is used.</violation>
</file>
<file name="client/src/components/OnboardingTour.jsx">
<violation number="1" location="client/src/components/OnboardingTour.jsx:7">
P0: The `react-joyride` package is imported but not declared in any `package.json`. CI confirms the build fails with an unresolved import error. Add `react-joyride` to `client/package.json` dependencies (e.g. `npm install react-joyride`).</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
| EVENTS, | ||
| Joyride, | ||
| STATUS, | ||
| } from "react-joyride"; |
There was a problem hiding this comment.
P0: The react-joyride package is imported but not declared in any package.json. CI confirms the build fails with an unresolved import error. Add react-joyride to client/package.json dependencies (e.g. npm install react-joyride).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At client/src/components/OnboardingTour.jsx, line 7:
<comment>The `react-joyride` package is imported but not declared in any `package.json`. CI confirms the build fails with an unresolved import error. Add `react-joyride` to `client/package.json` dependencies (e.g. `npm install react-joyride`).</comment>
<file context>
@@ -0,0 +1,263 @@
+ EVENTS,
+ Joyride,
+ STATUS,
+} from "react-joyride";
+import useOnboarding from "../hooks/useOnboarding.js";
+
</file context>
| </div> | ||
| <button | ||
| type="button" | ||
| onClick={resetTour} |
There was a problem hiding this comment.
P2: Restart button does not restart from beginning of onboarding flow. It restarts at Settings because current-tab-based initial step is used.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At client/src/pages/Settings.jsx, line 126:
<comment>Restart button does not restart from beginning of onboarding flow. It restarts at Settings because current-tab-based initial step is used.</comment>
<file context>
@@ -106,7 +111,31 @@ export default function Settings() {
+ </div>
+ <button
+ type="button"
+ onClick={resetTour}
+ aria-label="Restart onboarding tour"
+ className="inline-flex min-h-11 items-center justify-center gap-2 rounded-md bg-moss px-4 font-bold text-white transition hover:bg-moss/90 dark:bg-glow dark:text-black dark:hover:bg-glow/90"
</file context>
|
Hello @yashyrs027 , please resolve these 3 issue and you are good to go 🔴 Blocking —
|
🎊 PR Merged SuccessfullyHey @yashyrs027! 👋 Congratulations and thank you for your contribution to VoiceForge! Note 🔗 Linked issue(s): #109 · ✅ Marked as merged and complete Maintainers may still handle final cleanup, release notes, or follow-up tracking after the merge. 🤖 VoiceForge Automation · Updates automatically on edits |
🚀 Program
SSOC
📝 Description
This PR implements a comprehensive Interactive Onboarding Tour for VoiceForge using React Joyride, helping first-time users understand the complete workflow of the application.
✨ Features Added
Added a guided onboarding tour covering:
Created a reusable
OnboardingTourcomponent for managing guided walkthroughs.Added a custom
useOnboardinghook to:Integrated onboarding targets into existing components using
data-tourattributes:🎨 UI/UX Improvements
Added onboarding/instruction dialogs to additional pages:
Added a "Restart Onboarding Tour" option in Settings.
Styled onboarding tooltips to match the VoiceForge design system.
♿ Accessibility Improvements
⚙️ State Management
Automatically launches onboarding for first-time users.
Supports restarting the onboarding experience from Settings.
🛡️ Error Handling
📁 Files Added
client/src/components/OnboardingTour.jsxclient/src/hooks/useOnboarding.js📁 Files Updated
client/src/App.jsxclient/src/pages/Settings.jsxclient/src/components/VoiceRecorder.jsxclient/src/components/VoiceForge.jsxclient/src/components/VirtualCamera.jsxclient/src/components/TextToSpeech.jsxclient/src/components/VideoPreview.jsxThis implementation improves user onboarding, accessibility, discoverability of features, and overall first-time user experience while maintaining compatibility with the existing VoiceForge architecture.
🔗 Related Issue
Closes #109
🔄 Type of Change
🧪 How to Test
Clone the repository.
Install dependencies:
Install React Joyride:
Start the development server:
Open:
Verify onboarding automatically appears for first-time users.
Navigate through all onboarding steps.
Verify highlighted elements correspond to:
Verify onboarding is available on:
Open Settings and click "Restart Onboarding Tour".
Verify the onboarding restarts correctly.
Test on mobile viewport and confirm tooltip responsiveness.
Verify onboarding completion is stored in LocalStorage.
📸 Screenshots
Before
After
✅ Checklist
Summary by cubic
Adds an interactive onboarding tour with
react-joyride, now global inAppand active across Onboarding, Call, Compose, and Settings. The tour auto-starts for new users, switches tabs as needed, and can be restarted from Settings.New Features
OnboardingTourinAppusinguseOnboardingwith auto-start, tab-aware navigation, and completion memory.data-tourtargets added for record/clone, virtual camera, TTS input/generate, video preview, compose workspace/message/speak, settings overview/API key, and restart control.styles.css.Refactors
pages/Onboarding.jsxto a Joyride-based flow with unified callbacks.hooks/useOnboarding.js; integratedOnboardingTourinApp.VoiceRecorder,VirtualCamera,VideoPreview,TextToSpeech, andVoiceForgeto expose tour targets and align with the tour.Written for commit 422cf42. Summary will update on new commits.
Summary by CodeRabbit
New Features
Improvements