Skip to content

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

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open

Agent tour #598

wants to merge 8 commits into from

Conversation

swoopertr
Copy link
Contributor

Describe your changes

Implementing tour agent
issue number 541

@erenfn erenfn changed the base branch from master to develop April 29, 2025 22:56
Copy link
Contributor

coderabbitai bot commented May 6, 2025

Walkthrough

The 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

File(s) Change Summary
backend/.env Changed the database host from "db" to "localhost" in the backend environment configuration.
backend/src/routes/tour.routes.js Moved the GET /get_tour/:id route above the JWT authentication middleware, making this endpoint publicly accessible without authentication.
jsAgent/a.html Added a new standalone HTML file that dynamically creates a styled popup for "Projects" with a header, description, pagination, and navigation buttons, all managed and styled via JavaScript.
jsAgent/main.js Introduced a constant for the tour detail endpoint URL and added the bw.data.getTourById async method to fetch tour details by ID. Minor formatting adjustment in the initialization function.
jsAgent/tour.js Completely rewrote the script to implement a multi-step guided tour feature. Added the bw.tour object with state management, UI dialog generation, navigation logic, asynchronous dependency loading, and dynamic positioning.

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
Loading
sequenceDiagram
    participant User
    participant Backend

    User->>Backend: GET /get_tour/:id (no JWT required)
    Backend-->>User: Tour data JSON
Loading
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
Loading
✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 line

This 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 line

This 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 properties

There are some issues with the SVG styling:

  1. display: block and display: inline-block are both specified
  2. !important is used excessively which makes styles hard to override
  3. Some properties like float: right alongside position: absolute are redundant

Consider 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 function

The 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 function

The 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 in createCloseButton

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 global bw.tour.currentStep and bw.tour.tourData, rendering its dialogItem argument superfluous. Either use the parameter for clarity or remove it to keep the API tidy.


301-306: Optional chaining can simplify defensive code

Biome flags recommend options?.tour?.[0] to avoid a hard crash when bwonboarddata 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6b54bb1 and 86e30d3.

📒 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 endpoint

Adding 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 recomputing disabled inside setActiveIndicator.

@@ -3,7 +3,7 @@
DB_USERNAME=user123
DB_PASSWORD=password123
DB_NAME=onboarding_db
DB_HOST=db
DB_HOST=localhost
Copy link
Contributor

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);
Copy link
Contributor

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.

Comment on lines +141 to +154
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;
},
Copy link
Contributor

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.

Suggested change
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;
}
},

Comment on lines +1 to +209
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>
Copy link
Contributor

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:

  1. Moving the CSS into a separate stylesheet instead of inline styles
  2. Using template literals or a templating engine for cleaner HTML generation
  3. Implementing actual content updates when navigation occurs (currently just console logs)
  4. Adding accessibility attributes (aria-*)

The current implementation is a good start but should be better integrated with the tour functionality.

Comment on lines +254 to +260
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%)`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
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%)`;
}

Comment on lines +82 to +86
generateContent: function (content, textColor) {
const p = document.createElement('p');
p.id = 'bw-tour-description';
p.innerHTML = content;
p.style.cssText = `
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
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 = `

Comment on lines +295 to +315
* @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);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
* 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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant