-
Notifications
You must be signed in to change notification settings - Fork 45
Agent tour #598
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: develop
Are you sure you want to change the base?
Agent tour #598
Conversation
- Change DB_HOST from 'db' to 'localhost' in .env file. - Add new index.html file for project overview with UI components. - Enhance tour.js with library loading and dialog generation stubs.
…eneration with navigation controls
WalkthroughThe updates include modifying the backend database host configuration, adjusting route middleware to make a tour detail endpoint public, and introducing new or rewritten frontend scripts for a guided tour and project popup UI. The frontend now features dynamic UI creation, multi-step guided tours, and new data-fetching methods for tour details. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant TourScript (jsAgent/tour.js)
participant DataModule (jsAgent/main.js)
participant Backend
User->>TourScript: Page loads (script executes)
TourScript->>DataModule: getTourById(tourId)
DataModule->>Backend: GET /get_tour/:id
Backend-->>DataModule: Tour data JSON
DataModule-->>TourScript: Tour data
TourScript->>TourScript: Initialize tour (load dependencies, set state)
TourScript->>User: Display guided tour dialog (step 1)
User->>TourScript: Clicks "Next" or pagination
TourScript->>TourScript: Update current step, update dialog UI
User->>TourScript: Completes last step
TourScript->>Backend: (Optional) Send completion data
TourScript->>User: Remove tour dialog
sequenceDiagram
participant User
participant Backend
User->>Backend: GET /get_tour/:id (no JWT required)
Backend-->>User: Tour data JSON
sequenceDiagram
participant User
participant ProjectPopup (jsAgent/a.html)
User->>ProjectPopup: Loads HTML file
ProjectPopup->>User: Displays popup with header, description, pagination, navigation
User->>ProjectPopup: Interacts with pagination or navigation buttons
ProjectPopup->>ProjectPopup: Updates UI state and highlights active page
User->>ProjectPopup: Clicks close button
ProjectPopup->>User: Hides popup
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
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.
Actionable comments posted: 7
🧹 Nitpick comments (8)
backend/src/routes/tour.routes.js (1)
18-18
: Unnecessary blank lineThis blank line doesn't serve any purpose and could be removed to maintain consistent spacing in the file.
router.get('/tours', tourController.getToursByUserId); -
jsAgent/main.js (1)
204-204
: Unnecessary blank lineThis blank line affects readability and is inconsistent with the spacing in the rest of the file.
console.log("data loaded:", onBoardConfig); - window.bwonboarddata = onBoardConfig;
jsAgent/a.html (3)
59-72
: SVG styling issues and duplicate propertiesThere are some issues with the SVG styling:
display: block
anddisplay: inline-block
are both specified!important
is used excessively which makes styles hard to override- Some properties like
float: right
alongsideposition: absolute
are redundantConsider cleaning up these styles for better maintainability.
closeButton.style.cssText = ` - fill: rgb(152, 162, 179) !important; - font-size: 20px !important; - display: block !important; - position: absolute !important; - float: right !important; - right: 23px !important; - cursor: pointer !important; - width: 20px !important; - height: 20px !important; - display: inline-block !important; - margin: auto !important; + fill: rgb(152, 162, 179); + font-size: 20px; + position: absolute; + right: 23px; + cursor: pointer; + width: 20px; + height: 20px; + display: inline-block; + margin: auto; `;
175-178
: Improve the setActiveIndicator functionThe current function resets all indicators and then sets the active one. This could be more efficient by directly targeting the previously active indicator.
Additionally, consider using CSS classes for active state rather than inline styles for better separation of concerns.
function setActiveIndicator(index) { - indicators.forEach(indicator => indicator.style.backgroundColor = '#ddd'); - indicators[index].style.backgroundColor = '#673ab7'; + // Find current active indicator and deactivate it + const activeIndicator = document.querySelector('.indicator.active'); + if (activeIndicator) { + activeIndicator.classList.remove('active'); + activeIndicator.style.backgroundColor = '#ddd'; + } + // Activate the new indicator + indicators[index].classList.add('active'); + indicators[index].style.backgroundColor = '#673ab7'; }
203-205
: Improve the closeContainer functionThe current function only hides the container but doesn't clean up event listeners, which could lead to memory leaks if the container is shown and hidden multiple times.
Consider fully removing the container or implementing a complete cleanup function.
function closeContainer() { - container.style.display = 'none'; // Or container.remove(); + // Remove the container completely to clean up memory + container.remove(); + // Alternatively, if you need to show it again later: + // container.style.display = 'none'; + // Then implement a way to clear event listeners when appropriate }jsAgent/tour.js (3)
74-81
: Unused parameter increateCloseButton
container
is passed in but never referenced, which is confusing for readers and may hint at incomplete logic.-createCloseButton: function (container) { +createCloseButton: function () {If you later need the container inside the SVG handler, you can capture it via closure when wiring the click listener rather than passing it here.
135-146
:dialogItem
parameter is accepted but ignored
generateDialog
relies entirely on the globalbw.tour.currentStep
andbw.tour.tourData
, rendering itsdialogItem
argument superfluous. Either use the parameter for clarity or remove it to keep the API tidy.
301-306
: Optional chaining can simplify defensive codeBiome flags recommend
options?.tour?.[0]
to avoid a hard crash whenbwonboarddata
is missing. Worth adopting once the callback signature is corrected.🧰 Tools
🪛 Biome (1.9.4)
[error] 306-306: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
backend/.env
(1 hunks)backend/src/routes/tour.routes.js
(1 hunks)jsAgent/a.html
(1 hunks)jsAgent/main.js
(3 hunks)jsAgent/tour.js
(1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
jsAgent/tour.js
[error] 19-19: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 21-21: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 306-306: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 309-309: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 313-313: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build (22.x)
🔇 Additional comments (2)
jsAgent/main.js (1)
5-5
: Good addition of constant for API endpointAdding a constant for the tour detail endpoint URL is a good practice. This makes the code more maintainable and follows the pattern used for other endpoints.
jsAgent/tour.js (1)
117-118
:Next
button disabling logic could block tour completion
nextButton.disabled
is computed once at creation (currentStep === lastStep
). When the user eventually reaches the last step via navigation, the button is still enabled, but if the tour ever starts at the last step (edge-case or resumed session) it will be disabled and stay that way, preventing completion. Consider recomputingdisabled
insidesetActiveIndicator
.
@@ -3,7 +3,7 @@ | |||
DB_USERNAME=user123 | |||
DB_PASSWORD=password123 | |||
DB_NAME=onboarding_db | |||
DB_HOST=db | |||
DB_HOST=localhost |
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.
💡 Verification agent
🧩 Analysis chain
Database host change might affect deployment environments
Changing the DB_HOST from "db" to "localhost" looks intentional for local development, but this could cause issues if the same .env file is used in container-based deployments where "db" is the expected host name.
Have you considered using environment-specific configuration or a conditional override to handle different environments?
🏁 Script executed:
#!/bin/bash
# Check if Docker Compose is being used in the project
if [ -f "docker-compose.yml" ]; then
echo "Docker Compose file found, checking for database service configuration"
grep -A 10 "db:" docker-compose.yml
echo "-------"
echo "The .env file now uses 'localhost' which might not work properly with Docker Compose"
fi
# Check if there are any deployment scripts or configurations that might be affected
find . -type f -name "*.yml" -o -name "*.yaml" | xargs grep -l "db" | head -n 5
Length of output: 1199
Let’s avoid a deployment “mom’s spaghetti” moment and verify how DB_HOST is wired up across Compose and your backend:
🏁 Script executed:
#!/bin/bash
# Check if docker-compose loads .env via env_file
grep -n "env_file" docker-compose.yml || echo "No env_file references in docker-compose.yml"
# Check if DB_HOST is explicitly passed into any service
grep -n "DB_HOST" docker-compose.yml || echo "No DB_HOST environment key in docker-compose.yml"
# Search for DB_HOST usage in backend code
grep -R "DB_HOST" -n backend || echo "No DB_HOST references in backend folder"
# Look for dotenv initialization in backend
grep -R "require('dotenv')" -n backend && echo "dotenv is initialized" || \
grep -R "import .*dotenv" -n backend && echo "dotenv is initialized" || \
echo "No dotenv initialization found in backend folder"
Length of output: 932
Let’s confirm the exact env_file target, available .env variants, and how config.js picks hosts:
🏁 Script executed:
#!/bin/bash
# Show which file docker-compose loads
echo "⎯⎯⎯ docker-compose.yml env_file block ⎯⎯⎯"
grep -n "env_file" -A2 docker-compose.yml || echo "No env_file section found"
# List all .env files in backend/
echo
echo "⎯⎯⎯ .env files in backend/ ⎯⎯⎯"
find backend -maxdepth 1 -type f -name ".env*" -print
# Show development/test/production sections in config.js
echo
echo "⎯⎯⎯ backend/config/config.js env sections ⎯⎯⎯"
grep -nE "^( (development|test|production):)" -A5 backend/config/config.js || echo "No env sections matched"
Length of output: 1036
DB_HOST mismatch will break Docker networking
docker-compose.yml loads backend/.env
(lines 12–14), which now sets
DB_HOST=localhost # backend/.env:6
Inside Docker, your Postgres service is reachable at host db
, not localhost
, so your backend container will try to connect to itself.
To fix this, consider:
• Splitting env files:
– .env.development
for local (localhost)
– .env.docker
or .env.production
for containers (db)
Then update docker-compose.yml’s env_file
to point to the correct file per environment.
• Overriding in docker-compose.yml:
services:
backend:
environment:
- DB_HOST=db
• Adding a conditional in your config.js:
host: process.env.NODE_ENV === 'development'
? 'localhost'
: 'db'
This ensures local dev uses localhost
while container deployments use the Docker service name.
@@ -10,12 +10,12 @@ const router = express.Router(); | |||
const teamPermissions = settings.team.permissions.tours; | |||
|
|||
router.get('/get_tour_by_url', bodyUrlValidator, handleValidationErrors, tourController.getTourByUrl); | |||
|
|||
router.get('/get_tour/:id', tourController.getTourById); |
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.
💡 Verification agent
🧩 Analysis chain
Security consideration: Exposing tour data publicly
Moving the getTourById endpoint above the JWT authentication middleware makes this endpoint publicly accessible. Ensure that this endpoint doesn't expose sensitive data and implement appropriate rate limiting to prevent abuse.
Consider adding request validation to prevent potential injection attacks through the ID parameter.
🏁 Script executed:
#!/bin/bash
# Check how the tour controller handles the ID parameter
echo "Examining tour controller implementation for getTourById:"
find backend/src -name "tour.controller.js" -exec cat {} \; | grep -A 15 "getTourById"
Length of output: 737
Secure and Validate getTourById
Endpoint
-
The route at backend/src/routes/tour.routes.js:13 is currently public (no JWT auth). Confirm that it’s acceptable to expose tour data without authentication and that no sensitive fields (internal notes, admin-only flags, etc.) are returned.
-
Implement rate limiting (e.g., with express-rate-limit) on this endpoint to guard against enumeration or DoS attacks.
-
Validate the
:id
parameter (e.g., using express-validator or Joi) to ensure it’s a well-formed identifier (UUID or MongoDB ObjectId) and to prevent injection attacks. -
If this endpoint should require authentication, move it below your JWT middleware:
- router.get('/get_tour/:id', tourController.getTourById); + router.get( + '/get_tour/:id', + rateLimiter, + param('id').isMongoId(), // or .isUUID() + handleValidationErrors, + jwtMiddleware, + tourController.getTourById + );
Committable suggestion skipped: line range outside the PR's diff.
getTourById: async function (tourId) { | ||
const myHeaders = new Headers(); | ||
myHeaders.append("Content-Type", "application/json"); | ||
|
||
const requestOptions = { | ||
method: "GET", | ||
headers: myHeaders, | ||
redirect: "follow", | ||
}; | ||
|
||
const response = await fetch(`${BW_TOUR_DETAIL_JS_URL}${tourId}`, requestOptions); | ||
const data = await response.json(); | ||
return data; | ||
}, |
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.
🛠️ Refactor suggestion
Implement error handling for the getTourById method
The new getTourById method doesn't have any error handling for failed requests. If the fetch fails or returns an error status, it will throw an unhandled exception.
Consider adding try/catch blocks and checking the response status before parsing JSON, similar to other methods in this file.
getTourById: async function (tourId) {
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
const requestOptions = {
method: "GET",
headers: myHeaders,
redirect: "follow",
};
+ try {
const response = await fetch(`${BW_TOUR_DETAIL_JS_URL}${tourId}`, requestOptions);
+ if (!response.ok) {
+ console.log("Error fetching tour data:", response.status);
+ return null;
+ }
const data = await response.json();
return data;
+ } catch (error) {
+ console.log("Error fetching tour data:", error);
+ return null;
+ }
},
📝 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.
getTourById: async function (tourId) { | |
const myHeaders = new Headers(); | |
myHeaders.append("Content-Type", "application/json"); | |
const requestOptions = { | |
method: "GET", | |
headers: myHeaders, | |
redirect: "follow", | |
}; | |
const response = await fetch(`${BW_TOUR_DETAIL_JS_URL}${tourId}`, requestOptions); | |
const data = await response.json(); | |
return data; | |
}, | |
getTourById: async function (tourId) { | |
const myHeaders = new Headers(); | |
myHeaders.append("Content-Type", "application/json"); | |
const requestOptions = { | |
method: "GET", | |
headers: myHeaders, | |
redirect: "follow", | |
}; | |
try { | |
const response = await fetch(`${BW_TOUR_DETAIL_JS_URL}${tourId}`, requestOptions); | |
if (!response.ok) { | |
console.log("Error fetching tour data:", response.status); | |
return null; | |
} | |
const data = await response.json(); | |
return data; | |
} catch (error) { | |
console.log("Error fetching tour data:", error); | |
return null; | |
} | |
}, |
indicator.style.cssText = ` | ||
width: 23px; | ||
height: 5px; | ||
margin: 0 5px; | ||
border-radius: 2px; | ||
background-color: #ddd; | ||
cursor: pointer; | ||
`; | ||
if (i === 0) { | ||
indicator.classList.add('active'); | ||
indicator.style.cssText += `background-color: #673ab7;`; | ||
} | ||
pagination.appendChild(indicator); | ||
indicators.push(indicator); | ||
} | ||
|
||
// Create footer (buttons container) | ||
const footer = document.createElement('div'); | ||
footer.className = 'footer'; | ||
footer.style.cssText = ` | ||
display: flex; | ||
justify-content: flex-end; | ||
background-color: #f8f8f8; | ||
border-top: 1px solid #ddd; | ||
padding: 15px 20px 15px 15px; | ||
width: 100%; | ||
box-sizing: border-box; | ||
`; | ||
container.appendChild(footer); | ||
|
||
// Create back button | ||
const backButton = document.createElement('button'); | ||
backButton.className = 'back-button'; | ||
backButton.textContent = 'Back'; | ||
backButton.style.cssText = ` | ||
background-color: #eee; | ||
color: #333; | ||
margin-right: 10px; | ||
padding: 8px 16px; | ||
border: none; | ||
border-radius: 8px; | ||
cursor: pointer; | ||
min-width: 128px; | ||
`; | ||
backButton.addEventListener('click', handleBack); | ||
footer.appendChild(backButton); | ||
|
||
// Create next button | ||
const nextButton = document.createElement('button'); | ||
nextButton.className = 'next-button'; | ||
nextButton.textContent = 'Next'; | ||
nextButton.style.cssText = ` | ||
background-color: #673ab7; | ||
color: white; | ||
padding: 8px 16px; | ||
border: none; | ||
border-radius: 8px; | ||
cursor: pointer; | ||
min-width: 128px; | ||
`; | ||
nextButton.addEventListener('click', handleNext); | ||
footer.appendChild(nextButton); | ||
|
||
let currentPage = 0; | ||
|
||
function setActiveIndicator(index) { | ||
indicators.forEach(indicator => indicator.style.backgroundColor = '#ddd'); | ||
indicators[index].style.backgroundColor = '#673ab7'; | ||
} | ||
|
||
indicators.forEach((indicator, index) => { | ||
indicator.addEventListener('click', () => { | ||
setActiveIndicator(index); | ||
currentPage = index; | ||
console.log(`Navigated to page ${currentPage + 1}`); | ||
// Add page content update logic here | ||
}); | ||
}); | ||
|
||
function handleBack() { | ||
currentPage = Math.max(0, currentPage - 1); | ||
setActiveIndicator(currentPage); | ||
console.log("Back button clicked, now on page " + (currentPage + 1)); | ||
// Add page content update logic here | ||
} | ||
|
||
function handleNext() { | ||
currentPage = Math.min(indicators.length - 1, currentPage + 1); | ||
setActiveIndicator(currentPage); | ||
console.log("Next button clicked, now on page " + (currentPage + 1)); | ||
// Add page content update logic here | ||
} | ||
|
||
function closeContainer() { | ||
container.style.display = 'none'; // Or container.remove(); | ||
} | ||
</script> | ||
|
||
</body> | ||
</html> |
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.
🛠️ Refactor suggestion
Consider integrating this HTML prototype into the main application
This standalone HTML file appears to be a prototype for a Projects popup UI. For production use, consider:
- Moving the CSS into a separate stylesheet instead of inline styles
- Using template literals or a templating engine for cleaner HTML generation
- Implementing actual content updates when navigation occurs (currently just console logs)
- Adding accessibility attributes (aria-*)
The current implementation is a good start but should be better integrated with the tour functionality.
function updatePosition() { | ||
const targetElement = document.querySelector(bw.tour.tourData.steps[bw.tour.currentStep].targetElement); | ||
//update container position according to target element with smooth animation transition | ||
const rect = targetElement.getBoundingClientRect(); | ||
container.style.left = `${rect.left + window.scrollX}px`; | ||
container.style.top = `${rect.top + window.scrollY}px`; | ||
container.style.transform = `translate(-50%, 0%)`; |
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.
updatePosition
crashes when the target selector is missing
document.querySelector(...)
can return null
; calling getBoundingClientRect()
on it will throw. Guard against missing or transient elements and provide a graceful fallback.
- const targetElement = document.querySelector(bw.tour.tourData.steps[bw.tour.currentStep].targetElement);
- //update container position according to target element with smooth animation transition
- const rect = targetElement.getBoundingClientRect();
+ const targetElement = document.querySelector(
+ bw.tour.tourData.steps[bw.tour.currentStep].targetElement
+ );
+ if (!targetElement) {
+ console.warn("Target element not found for step", bw.tour.currentStep);
+ return;
+ }
+ // Update container position according to target element with smooth animation transition
+ const rect = targetElement.getBoundingClientRect();
This avoids a runtime exception that would abruptly end the tour.
📝 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.
function updatePosition() { | |
const targetElement = document.querySelector(bw.tour.tourData.steps[bw.tour.currentStep].targetElement); | |
//update container position according to target element with smooth animation transition | |
const rect = targetElement.getBoundingClientRect(); | |
container.style.left = `${rect.left + window.scrollX}px`; | |
container.style.top = `${rect.top + window.scrollY}px`; | |
container.style.transform = `translate(-50%, 0%)`; | |
function updatePosition() { | |
const targetElement = document.querySelector( | |
bw.tour.tourData.steps[bw.tour.currentStep].targetElement | |
); | |
if (!targetElement) { | |
console.warn("Target element not found for step", bw.tour.currentStep); | |
return; | |
} | |
// Update container position according to target element with smooth animation transition | |
const rect = targetElement.getBoundingClientRect(); | |
container.style.left = `${rect.left + window.scrollX}px`; | |
container.style.top = `${rect.top + window.scrollY}px`; | |
container.style.transform = `translate(-50%, 0%)`; | |
} |
generateContent: function (content, textColor) { | ||
const p = document.createElement('p'); | ||
p.id = 'bw-tour-description'; | ||
p.innerHTML = content; | ||
p.style.cssText = ` |
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.
Un-sanitised HTML opens the door to XSS
p.innerHTML = content;
assumes the backend always delivers safe markup. If a malicious script sneaks into tourData.description
, it runs in your users’ browsers with full privileges. Prefer textContent
, or run the HTML through a trusted sanitiser (DOMPurify, etc.).
- p.innerHTML = content;
+ // If rich HTML is required, run `content` through a sanitizer first.
+ p.textContent = content;
Better safe than sorry when mom’s spaghetti is on the keyboard.
📝 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.
generateContent: function (content, textColor) { | |
const p = document.createElement('p'); | |
p.id = 'bw-tour-description'; | |
p.innerHTML = content; | |
p.style.cssText = ` | |
generateContent: function (content, textColor) { | |
const p = document.createElement('p'); | |
p.id = 'bw-tour-description'; | |
// If rich HTML is required, run `content` through a sanitizer first. | |
p.textContent = content; | |
p.style.cssText = ` |
* @param {function(object): void} cb - Callback function, called with the tour options object. | ||
* @param {function(): void} errCb - Callback function, called on error. | ||
* @returns {void} | ||
*/ | ||
loadFloatingUILibraries: function (cb) { | ||
bw.util.loadScriptAsync(FLOATING_UI_CORE_URL, () => { | ||
console.log("@floating-ui/core loaded successfully"); | ||
bw.util.loadScriptAsync(FLOATING_UI_DOM_URL, () => { | ||
console.log("@floating-ui/dom loaded successfully"); | ||
const options = window.bwonboarddata.tour[0]; | ||
cb && cb(options); | ||
}, (err) => { | ||
console.error("Failed to load @floating-ui/dom:", err); | ||
cb && cb(false); | ||
}); | ||
}, (err) => { | ||
console.error("Failed to load @floating-ui/core:", err); | ||
cb && cb(false); | ||
}); | ||
} |
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.
Mismatch between declared docs, call-site and implementation silently drops the error-callback
loadFloatingUILibraries
is documented (295-299) – and invoked in init
(15-22) – with two callbacks, yet the function signature only accepts one.
As a result, the failure path you intend to handle is swallowed, and init
proceeds to dereference result.id
, which will blow up if the scripts failed to load.
- loadFloatingUILibraries: function (cb) {
+ // Accept both success & error callbacks
+ loadFloatingUILibraries: function (cb, errCb) {
...
- cb && cb(options);
+ cb && cb(options); // success
...
- console.error("Failed to load @floating-ui/dom:", err);
- cb && cb(false);
+ console.error("Failed to load @floating-ui/dom:", err);
+ errCb ? errCb(err) : cb && cb(false);
...
- console.error("Failed to load @floating-ui/core:", err);
- cb && cb(false);
+ console.error("Failed to load @floating-ui/core:", err);
+ errCb ? errCb(err) : cb && cb(false);
And at the call-site:
- this.loadFloatingUILibraries(async (result) => {
- const tourId = result.id;
+ this.loadFloatingUILibraries(async (result) => {
+ if (!result || !result.id) { // guard against failures
+ cb && cb(false);
+ return;
+ }
+ const tourId = result.id;
...
- }, () => {
- cb && cb(false);
- });
+ }, (err) => {
+ console.error("Tour initialisation failed:", err);
+ cb && cb(false);
+ });
Without this fix, a network hiccup will crash the tour and leave the user staring at a blank page—knees weak, arms heavy.
📝 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.
* Loads floating-ui libraries asynchronously. | |
* @param {function(object): void} cb - Callback function, called with the tour options object. | |
* @param {function(): void} errCb - Callback function, called on error. | |
* @returns {void} | |
*/ | |
loadFloatingUILibraries: function (cb) { | |
bw.util.loadScriptAsync(FLOATING_UI_CORE_URL, () => { | |
console.log("@floating-ui/core loaded successfully"); | |
bw.util.loadScriptAsync(FLOATING_UI_DOM_URL, () => { | |
console.log("@floating-ui/dom loaded successfully"); | |
const options = window.bwonboarddata.tour[0]; | |
cb && cb(options); | |
}, (err) => { | |
console.error("Failed to load @floating-ui/dom:", err); | |
cb && cb(false); | |
}); | |
}, (err) => { | |
console.error("Failed to load @floating-ui/core:", err); | |
cb && cb(false); | |
}); | |
} | |
/** | |
* Loads floating-ui libraries asynchronously. | |
* @param {function(object): void} cb ‑ Callback function, called with the tour options object. | |
* @param {function(): void} errCb ‑ Callback function, called on error. | |
* @returns {void} | |
*/ | |
// Accept both success & error callbacks | |
loadFloatingUILibraries: function (cb, errCb) { | |
bw.util.loadScriptAsync(FLOATING_UI_CORE_URL, () => { | |
console.log("@floating-ui/core loaded successfully"); | |
bw.util.loadScriptAsync(FLOATING_UI_DOM_URL, () => { | |
console.log("@floating-ui/dom loaded successfully"); | |
const options = window.bwonboarddata.tour[0]; | |
cb && cb(options); // success | |
}, (err) => { | |
console.error("Failed to load @floating-ui/dom:", err); | |
errCb ? errCb(err) : cb && cb(false); | |
}); | |
}, (err) => { | |
console.error("Failed to load @floating-ui/core:", err); | |
errCb ? errCb(err) : cb && cb(false); | |
}); | |
} |
🧰 Tools
🪛 Biome (1.9.4)
[error] 306-306: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 309-309: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 313-313: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
Describe your changes
Implementing tour agent
issue number 541