-
-
Notifications
You must be signed in to change notification settings - Fork 7
Implement animated onboarding tour using driver.js #513
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: sync/main-auth-backend-final-13145176703902541894
Are you sure you want to change the base?
Implement animated onboarding tour using driver.js #513
Conversation
Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Manus AI seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing touches🧪 Generate unit tests (beta)
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 |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
qcx | 47e640b | Feb 11 2026, 09:25 AM |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The onboarding implementation is close, but the auto-trigger timer in Chat can fire after the user starts interacting because it isn’t reliably cleaned up, which is likely to interrupt real usage. The tour also references several selectors (e.g., '.mobile-map-section', data-testid="chat-input", data-testid="map-toggle") that are not clearly guaranteed by this diff, risking a broken tour in production. Separately, committing server.log is a must-fix repo hygiene issue, and the next-env.d.ts change to a /.next/dev/... path is likely to be environment-fragile.
Summary of changes
Onboarding tour (driver.js)
- Added a new client hook
useOnboardingTour()incomponents/onboarding-tour.tsxthat configures and launches adriver.jstour with separate desktop and mobile step definitions. - Introduced automatic first-time tour triggering in
components/chat.tsxusinglocalStorage(qcx_onboarding_v1) and a 5s delay whenmessages.length === 0. - Added manual “Help / Tour” triggers:
- Desktop header help button (
data-testid="help-tour") incomponents/header.tsx. - Mobile bottom-bar help button (
data-testid="mobile-help-tour") incomponents/mobile-icons-bar.tsx.
- Desktop header help button (
UI targeting improvements
- Added/adjusted
data-testidattributes to support reliable tour targeting:- Map container:
data-testid="mapbox-container"incomponents/map/mapbox-map.tsx. - Search & usage buttons/portals for desktop and mobile in
components/header.tsx,components/header-search-button.tsx, andcomponents/mobile-icons-bar.tsx.
- Map container:
Dependency
- Added
driver.jstopackage.json.
Misc
- Updated
next-env.d.tsto reference./.next/dev/types/routes.d.ts. server.logchanged substantially (appears to include local dev output).
components/chat.tsx
Outdated
| // Automatic onboarding tour trigger | ||
| useEffect(() => { | ||
| const tourCompleted = localStorage.getItem('qcx_onboarding_v1') | ||
| if (!tourCompleted && messages.length === 0) { | ||
| const timer = setTimeout(() => { | ||
| startTour(isMobile) | ||
| }, 5000) | ||
| return () => clearTimeout(timer) | ||
| } | ||
| }, [isMobile, startTour, messages.length]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The auto-start effect reads localStorage unconditionally. In Next.js, this component is likely client-side, but the effect can still run in environments where localStorage is unavailable (e.g., some test runners) and crash. Also, startTour can be scheduled multiple times if messages.length is 0 while isMobile changes (e.g., resize) within the 5s window.
Consider guarding localStorage access and ensuring the timer is always cleaned up, plus preventing repeated scheduling while the same conditions hold.
Suggestion
Make the effect resilient and avoid duplicate scheduling. For example:
- Wrap storage access in a
tryand checktypeof window !== 'undefined'. - Always set up/clear a timer, and optionally store a ref flag like
hasScheduledTourRef.current.
useEffect(() => {
if (typeof window === 'undefined') return
let timer: ReturnType<typeof setTimeout> | undefined
try {
const tourCompleted = window.localStorage.getItem('qcx_onboarding_v1')
if (!tourCompleted && messages.length === 0) {
timer = setTimeout(() => startTour(isMobile), 5000)
}
} catch {
// ignore storage access failures (e.g., privacy mode/test env)
}
return () => {
if (timer) clearTimeout(timer)
}
}, [isMobile, startTour, messages.length])Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.
components/onboarding-tour.tsx
Outdated
| export const useOnboardingTour = () => { | ||
| const startTour = (isMobile: boolean) => { | ||
| const desktopSteps = [ | ||
| { | ||
| element: '[data-testid="chat-input"]', | ||
| popover: { | ||
| title: 'Chat Explorer', | ||
| description: 'Ask questions about any location, upload images for analysis, or request geospatial data. Try "Show me parks in London".', | ||
| side: "right", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mapbox-container"]', | ||
| popover: { | ||
| title: 'Interactive Map', | ||
| description: 'Visualize your results here. You can pan, zoom, and interact with geocoded markers generated from your chat.', | ||
| side: "left", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="logo-history-toggle"]', | ||
| popover: { | ||
| title: 'History & Navigation', | ||
| description: 'Click the QCX logo to toggle your chat history and quickly jump back to previous explorations.', | ||
| side: "bottom", | ||
| align: 'end' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="map-toggle"]', | ||
| popover: { | ||
| title: 'Map Modes', | ||
| description: 'Switch between Live updates, My Maps, or use Drawing Mode to measure areas and distances directly on the map.', | ||
| side: "bottom", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="calendar-toggle"]', | ||
| popover: { | ||
| title: 'Calendar & Notes', | ||
| description: 'Keep track of your findings with the built-in notepad. Notes can be geocoded and saved to specific dates.', | ||
| side: "bottom", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="resolution-search"]', | ||
| popover: { | ||
| title: 'Resolution Search', | ||
| description: 'Perform deep satellite analysis of your current map view. This captures high-resolution imagery for detailed inspection.', | ||
| side: "bottom", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="usage-toggle"]', | ||
| popover: { | ||
| title: 'Usage & Credits', | ||
| description: 'Monitor your compute usage and manage your credits. You can upgrade your plan here to access advanced features.', | ||
| side: "bottom", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="profile-toggle"]', | ||
| popover: { | ||
| title: 'User Profile', | ||
| description: 'Manage your account settings, appearance preferences, and security options.', | ||
| side: "bottom", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| popover: { | ||
| title: 'All Set!', | ||
| description: 'You are ready to explore the planet with QCX. Click the "?" help icon in the header anytime to restart this tour.' | ||
| } | ||
| } | ||
| ]; | ||
|
|
||
| const mobileSteps = [ | ||
| { | ||
| element: '[data-testid="chat-input"]', | ||
| popover: { | ||
| title: 'Chat', | ||
| description: 'Type your queries here to start exploring.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '.mobile-map-section', | ||
| popover: { | ||
| title: 'Map View', | ||
| description: 'Visual results and data will appear in this section.', | ||
| side: "bottom", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-new-chat-button"]', | ||
| popover: { | ||
| title: 'New Chat', | ||
| description: 'Clear the current session and start a fresh exploration.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="map-toggle"]', | ||
| popover: { | ||
| title: 'Map Controls', | ||
| description: 'Change map layers or enter drawing mode.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-calendar-button"]', | ||
| popover: { | ||
| title: 'Notes', | ||
| description: 'Access your calendar and geocoded notes.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-search-button"]', | ||
| popover: { | ||
| title: 'Resolution Search', | ||
| description: 'Analyze the current map view with high-res satellite data.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-attachment-button"]', | ||
| popover: { | ||
| title: 'Attachments', | ||
| description: 'Upload images or files to analyze.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-submit-button"]', | ||
| popover: { | ||
| title: 'Send', | ||
| description: 'Submit your query to the AI.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-usage-button"]', | ||
| popover: { | ||
| title: 'Usage', | ||
| description: 'Check your credits and usage statistics.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-help-tour"]', | ||
| popover: { | ||
| title: 'Help', | ||
| description: 'Restart this tour anytime by clicking the "?" icon.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| popover: { | ||
| title: 'Welcome to QCX', | ||
| description: 'Enjoy exploring! You can find the tour again in the bottom bar.' | ||
| } | ||
| } | ||
| ]; | ||
|
|
||
| const driverObj = driver({ | ||
| showProgress: true, | ||
| animate: true, | ||
| steps: isMobile ? mobileSteps : desktopSteps, | ||
| onDestroyStarted: () => { | ||
| localStorage.setItem('qcx_onboarding_v1', 'true'); | ||
| driverObj.destroy(); | ||
| } | ||
| }); | ||
|
|
||
| driverObj.drive(); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useOnboardingTour() creates a new driverObj every time startTour() is called, but there’s no guard against starting multiple overlapping tours (e.g., auto-trigger + user clicking help, or double-clicking help). This can lead to duplicated overlays and inconsistent onDestroyStarted behavior.
At minimum, the hook should prevent re-entrancy (track an active instance and no-op or destroy before starting a new one).
Suggestion
Add a module-level or hook-level ref to store the active driver instance and guard against concurrent runs.
Example (module-level singleton):
let activeDriver: ReturnType<typeof driver> | null = null
export const useOnboardingTour = () => {
const startTour = (isMobile: boolean) => {
if (activeDriver) {
activeDriver.destroy()
activeDriver = null
}
const driverObj = driver({
showProgress: true,
animate: true,
steps: isMobile ? mobileSteps : desktopSteps,
onDestroyStarted: () => {
localStorage.setItem('qcx_onboarding_v1', 'true')
driverObj.destroy()
activeDriver = null
},
})
activeDriver = driverObj
driverObj.drive()
}
return { startTour }
}Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.
components/onboarding-tour.tsx
Outdated
| const desktopSteps = [ | ||
| { | ||
| element: '[data-testid="chat-input"]', | ||
| popover: { | ||
| title: 'Chat Explorer', | ||
| description: 'Ask questions about any location, upload images for analysis, or request geospatial data. Try "Show me parks in London".', | ||
| side: "right", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mapbox-container"]', | ||
| popover: { | ||
| title: 'Interactive Map', | ||
| description: 'Visualize your results here. You can pan, zoom, and interact with geocoded markers generated from your chat.', | ||
| side: "left", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="logo-history-toggle"]', | ||
| popover: { | ||
| title: 'History & Navigation', | ||
| description: 'Click the QCX logo to toggle your chat history and quickly jump back to previous explorations.', | ||
| side: "bottom", | ||
| align: 'end' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="map-toggle"]', | ||
| popover: { | ||
| title: 'Map Modes', | ||
| description: 'Switch between Live updates, My Maps, or use Drawing Mode to measure areas and distances directly on the map.', | ||
| side: "bottom", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="calendar-toggle"]', | ||
| popover: { | ||
| title: 'Calendar & Notes', | ||
| description: 'Keep track of your findings with the built-in notepad. Notes can be geocoded and saved to specific dates.', | ||
| side: "bottom", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="resolution-search"]', | ||
| popover: { | ||
| title: 'Resolution Search', | ||
| description: 'Perform deep satellite analysis of your current map view. This captures high-resolution imagery for detailed inspection.', | ||
| side: "bottom", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="usage-toggle"]', | ||
| popover: { | ||
| title: 'Usage & Credits', | ||
| description: 'Monitor your compute usage and manage your credits. You can upgrade your plan here to access advanced features.', | ||
| side: "bottom", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="profile-toggle"]', | ||
| popover: { | ||
| title: 'User Profile', | ||
| description: 'Manage your account settings, appearance preferences, and security options.', | ||
| side: "bottom", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| popover: { | ||
| title: 'All Set!', | ||
| description: 'You are ready to explore the planet with QCX. Click the "?" help icon in the header anytime to restart this tour.' | ||
| } | ||
| } | ||
| ]; | ||
|
|
||
| const mobileSteps = [ | ||
| { | ||
| element: '[data-testid="chat-input"]', | ||
| popover: { | ||
| title: 'Chat', | ||
| description: 'Type your queries here to start exploring.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '.mobile-map-section', | ||
| popover: { | ||
| title: 'Map View', | ||
| description: 'Visual results and data will appear in this section.', | ||
| side: "bottom", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-new-chat-button"]', | ||
| popover: { | ||
| title: 'New Chat', | ||
| description: 'Clear the current session and start a fresh exploration.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="map-toggle"]', | ||
| popover: { | ||
| title: 'Map Controls', | ||
| description: 'Change map layers or enter drawing mode.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-calendar-button"]', | ||
| popover: { | ||
| title: 'Notes', | ||
| description: 'Access your calendar and geocoded notes.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-search-button"]', | ||
| popover: { | ||
| title: 'Resolution Search', | ||
| description: 'Analyze the current map view with high-res satellite data.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-attachment-button"]', | ||
| popover: { | ||
| title: 'Attachments', | ||
| description: 'Upload images or files to analyze.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-submit-button"]', | ||
| popover: { | ||
| title: 'Send', | ||
| description: 'Submit your query to the AI.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-usage-button"]', | ||
| popover: { | ||
| title: 'Usage', | ||
| description: 'Check your credits and usage statistics.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| element: '[data-testid="mobile-help-tour"]', | ||
| popover: { | ||
| title: 'Help', | ||
| description: 'Restart this tour anytime by clicking the "?" icon.', | ||
| side: "top", | ||
| align: 'start' | ||
| } | ||
| }, | ||
| { | ||
| popover: { | ||
| title: 'Welcome to QCX', | ||
| description: 'Enjoy exploring! You can find the tour again in the bottom bar.' | ||
| } | ||
| } | ||
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tour step targets include selectors that are not guaranteed to exist based on the changes in this PR. In particular:
- Mobile tour targets
'.mobile-map-section', but this diff doesn’t add that class anywhere. - Desktop tour targets
data-testid="chat-input",data-testid="map-toggle", anddata-testid="profile-toggle", but this diff only adds/changes some testids (e.g.mapbox-container,usage-toggle, etc.).
If any selector is missing at runtime, driver.js will typically skip/fail steps or behave unpredictably, resulting in a broken onboarding experience.
Suggestion
Ensure every referenced selector is backed by an actual element in the rendered UI (add the missing data-testid/class attributes in the relevant components), or add defensive filtering of steps to only include those whose element exists.
Example filtering approach:
const steps = (isMobile ? mobileSteps : desktopSteps).filter(s => {
if (!('element' in s) || !s.element) return true
return document.querySelector(s.element) != null
})Reply with "@CharlieHelps yes please" if you'd like me to add a commit that adds existence-filtering and/or the missing data-testids.
| const driverObj = driver({ | ||
| showProgress: true, | ||
| animate: true, | ||
| steps: isMobile ? mobileSteps : desktopSteps, | ||
| onDestroyStarted: () => { | ||
| localStorage.setItem('qcx_onboarding_v1', 'true'); | ||
| driverObj.destroy(); | ||
| } | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onDestroyStarted unconditionally marks the tour as completed. That means closing the tour early (or accidentally) will suppress auto-onboarding forever, which is likely not intended.
Also, calling driverObj.destroy() from within onDestroyStarted can be redundant / risky depending on Driver.js lifecycle semantics (some libraries call destroy as part of the callback path).
Suggestion
Only persist completion when the tour actually completes, and avoid double-destroy patterns.
If Driver.js supports an explicit completion hook (e.g., onDestroyed vs. onDestroyStarted, or a "last step" callback), use that to set qcx_onboarding_v1. Otherwise, set completion when advancing past the last step.
Example approach (conceptual):
const steps = isMobile ? mobileSteps : desktopSteps
const driverObj = driver({
showProgress: true,
animate: true,
steps,
onDestroyed: () => {
// consider NOT marking complete here if user exited early
},
onNextClick: () => {
const isLast = driverObj.getActiveIndex?.() === steps.length - 1
if (isLast) localStorage.setItem('qcx_onboarding_v1', 'true')
driverObj.moveNext()
},
})Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.
| 'use client' | ||
|
|
||
| import { driver } from "driver.js" | ||
| import "driver.js/dist/driver.css" | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
components/onboarding-tour.tsx imports driver.js/dist/driver.css inside a component module. In Next.js App Router, global CSS imports are typically restricted to specific entry points (e.g., app/layout.tsx). If this currently works in your setup, fine—but it’s a fragile integration point and can break with Next upgrades/bundling changes.
At minimum, centralizing the CSS import will reduce the chance of duplicate CSS injection and makes styling predictable.
Suggestion
Move driver.css import to a global stylesheet or app/layout.tsx (or app/globals.css via @import). Keep useOnboardingTour focused on behavior only.
For example, in app/globals.css:
@import "driver.js/dist/driver.css";…and remove the CSS import from components/onboarding-tour.tsx.
Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.
server.log
Outdated
|
|
||
| > QCX@0.1.0 dev | ||
| > next dev --turbo | ||
|
|
||
| ▲ Next.js 16.1.6 (Turbopack) | ||
| - Local: http://localhost:3000 | ||
| - Network: http://192.168.0.2:3000 | ||
| - Environments: .env | ||
| - Experiments (use with caution): | ||
| · serverActions | ||
|
|
||
| ✓ Starting... | ||
| Attention: Next.js now collects completely anonymous telemetry regarding usage. | ||
| This information is used to shape Next.js' roadmap and prioritize features. | ||
| You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL: | ||
| https://nextjs.org/telemetry | ||
|
|
||
| ⚠ The "middleware" file convention is deprecated. Please use "proxy" instead. Learn more: https://nextjs.org/docs/messages/middleware-to-proxy | ||
| ✓ Ready in 1489ms | ||
| ○ Compiling / ... | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 34.9s (compile: 32.4s, proxy.ts: 135ms, render: 2.4s) | ||
| ✓ Finished writing to filesystem cache in 14.1s | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 319ms (compile: 45ms, proxy.ts: 12ms, render: 262ms) | ||
| GET / 307 in 169ms (compile: 38ms, proxy.ts: 13ms, render: 118ms) | ||
| ○ Compiling /auth ... | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 6.7s (compile: 6.1s, proxy.ts: 8ms, render: 587ms) | ||
| GET /auth 200 in 6.8s (compile: 6.2s, proxy.ts: 98ms, render: 563ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 429ms (compile: 34ms, proxy.ts: 15ms, render: 381ms) | ||
| GET / 307 in 440ms (compile: 30ms, proxy.ts: 102ms, render: 308ms) | ||
| GET /auth 200 in 319ms (compile: 13ms, proxy.ts: 6ms, render: 300ms) | ||
| GET /auth 200 in 320ms (compile: 18ms, proxy.ts: 30ms, render: 272ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 211ms (compile: 26ms, proxy.ts: 5ms, render: 180ms) | ||
| GET /auth 200 in 85ms (compile: 5ms, proxy.ts: 4ms, render: 76ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 117ms (compile: 24ms, proxy.ts: 6ms, render: 87ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 73ms (compile: 6ms, proxy.ts: 3ms, render: 64ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 153ms (compile: 31ms, proxy.ts: 5ms, render: 117ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 119ms (compile: 11ms, proxy.ts: 9ms, render: 100ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 114ms (compile: 26ms, proxy.ts: 5ms, render: 83ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 74ms (compile: 5ms, proxy.ts: 4ms, render: 65ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 138ms (compile: 33ms, proxy.ts: 5ms, render: 100ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 94ms (compile: 8ms, proxy.ts: 4ms, render: 82ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 117ms (compile: 25ms, proxy.ts: 12ms, render: 80ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 78ms (compile: 6ms, proxy.ts: 4ms, render: 68ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 117ms (compile: 24ms, proxy.ts: 5ms, render: 89ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 83ms (compile: 6ms, proxy.ts: 4ms, render: 73ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 125ms (compile: 20ms, proxy.ts: 4ms, render: 101ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 86ms (compile: 5ms, proxy.ts: 4ms, render: 77ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 102ms (compile: 16ms, proxy.ts: 4ms, render: 82ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 77ms (compile: 8ms, proxy.ts: 4ms, render: 65ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 87ms (compile: 18ms, proxy.ts: 3ms, render: 66ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 81ms (compile: 5ms, proxy.ts: 3ms, render: 73ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 118ms (compile: 24ms, proxy.ts: 5ms, render: 90ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 80ms (compile: 4ms, proxy.ts: 5ms, render: 70ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 159ms (compile: 20ms, proxy.ts: 4ms, render: 136ms) | ||
| GET /auth 200 in 79ms (compile: 5ms, proxy.ts: 3ms, render: 71ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 151ms (compile: 16ms, proxy.ts: 4ms, render: 131ms) | ||
| GET /auth 200 in 75ms (compile: 7ms, proxy.ts: 3ms, render: 65ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 193ms (compile: 18ms, proxy.ts: 3ms, render: 171ms) | ||
| GET /auth 200 in 115ms (compile: 9ms, proxy.ts: 4ms, render: 101ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 219ms (compile: 26ms, proxy.ts: 8ms, render: 185ms) | ||
| GET /auth 200 in 116ms (compile: 8ms, proxy.ts: 4ms, render: 104ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 168ms (compile: 21ms, proxy.ts: 4ms, render: 143ms) | ||
| GET /auth 200 in 88ms (compile: 5ms, proxy.ts: 4ms, render: 78ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 88ms (compile: 18ms, proxy.ts: 4ms, render: 66ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 68ms (compile: 6ms, proxy.ts: 3ms, render: 59ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 82ms (compile: 17ms, proxy.ts: 3ms, render: 61ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 71ms (compile: 4ms, proxy.ts: 4ms, render: 64ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 110ms (compile: 25ms, proxy.ts: 6ms, render: 79ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 78ms (compile: 5ms, proxy.ts: 4ms, render: 68ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 100ms (compile: 18ms, proxy.ts: 5ms, render: 77ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 69ms (compile: 5ms, proxy.ts: 3ms, render: 61ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET / 307 in 88ms (compile: 19ms, proxy.ts: 4ms, render: 66ms) | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| GET /auth 200 in 66ms (compile: 5ms, proxy.ts: 3ms, render: 59ms) | ||
| ✓ Compiled in 205ms | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| [Auth] Supabase URL or Anon Key is not set for server-side auth. | ||
| ⨯ Error: Profile toggle context must be used within a ProfileToggleProvider | ||
| at useProfileToggle (components/profile-toggle-context.tsx:44:11) | ||
| at Chat (components/chat.tsx:35:42) | ||
| 42 | const context = useContext(ProfileToggleContext) | ||
| 43 | if (context === undefined) { | ||
| > 44 | throw new Error('Profile toggle context must be used within a ProfileToggleProvider') | ||
| | ^ | ||
| 45 | } | ||
| 46 | return context | ||
| 47 | } { | ||
| digest: '18451499' | ||
| } | ||
| GET / 500 in 1985ms (compile: 560ms, proxy.ts: 5ms, render: 1420ms) | ||
| ✓ Compiled in 257ms |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
server.log appears to be a local development artifact (and includes environment/config warnings and runtime errors). Checking this into the repo will create noisy diffs and can leak incidental info over time.
Unless this file is intentionally part of the product (e.g., used as a fixture), it should be removed from version control and added to .gitignore.
Suggestion
Remove server.log from the repository and add it to .gitignore (and/or your tooling’s ignore list).
Steps:
git rm --cached server.log- Add
server.log(or*.log) to.gitignore
Reply with "@CharlieHelps yes please" if you'd like me to add a commit doing this.
next-env.d.ts
Outdated
| /// <reference types="next" /> | ||
| /// <reference types="next/image-types/global" /> | ||
| import "./.next/types/routes.d.ts"; | ||
| import "./.next/dev/types/routes.d.ts"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing next-env.d.ts to reference ./.next/dev/types/routes.d.ts is suspicious: next-env.d.ts is auto-managed by Next and generally should not import build-output paths that differ between environments. This risks breaking type resolution on CI or in production builds where that path doesn’t exist.
Unless you have a documented reason for this change, it should be reverted to the standard reference or removed entirely.
Suggestion
Revert this line to the canonical Next.js generated form (or remove the import if it’s not required in your setup). Typically it should remain as generated by Next.
Reply with "@CharlieHelps yes please" if you'd like me to add a commit reverting this change.
- Added `driver.js` dependency and types. - Created `useOnboardingTour` hook in `components/onboarding-tour.tsx` with tailored steps for desktop and mobile. - Integrated automatic tour trigger in `components/chat.tsx` for new users (with 5s delay). - Added manual "Help" buttons (HelpCircle icon) to `Header` and `MobileIconsBar`. - Added `data-testid` attributes to Mapbox container, Resolution Search buttons, and Usage toggle for reliable targeting. - Used versioned localStorage key `qcx_onboarding_v1` to track completion. - Verified build and type safety. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
|
@coderabbitai I don't see any of the onboarding pop ups |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain
🏁 Script executed: Length of output: 5971 🏁 Script executed: Length of output: 11773 🏁 Script executed: Length of output: 6043 🏁 Script executed: Length of output: 3951
Primary Issue: LocalStorage FlagThe tour uses Quick Fix: Open your browser's developer console and run: localStorage.removeItem('qcx_onboarding_v1')Then refresh the page with an empty chat (no messages). Testing Steps:
Additional Debugging:If clearing localStorage doesn't work, check the browser console for errors: // In the browser console, manually test the tour:
const { driver } = await import('driver.js');
const driverObj = driver({
showProgress: true,
animate: true,
steps: [{ popover: { title: 'Test', description: 'Testing tour' } }]
});
driverObj.drive();Verification Checklist:
Let me know if clearing the localStorage flag resolves the issue, or if you see any errors in the console! |
I've investigated this and made several improvements to ensure the tour starts correctly:
Please check again! You can also manually trigger the tour using the new '?' help icon in the header (desktop) or bottom bar (mobile). |
- Moved driver.js CSS import to layout for better reliability. - Added a non-targeted welcome step as the first step of the tour. - Added debug logging for tour initialization. - Improved automatic trigger logic in Chat component. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
This PR implements a comprehensive onboarding tour for the QCX application using driver.js.
Key features:
PR created automatically by Jules for task 7118773695721375913 started by @ngoiyaeric