diff --git a/.DS_Store b/.DS_Store index 50d86b88..0882062e 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/backend/Dockerfile b/backend/Dockerfile index 7b528ee6..ed2ea280 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,6 @@ -FROM node:22-alpine3.20 +FROM node:22-alpine3.21 + +RUN apk update && apk add bash && rm -rf /var/cache/apk/* RUN apk update && apk add bash && rm -rf /var/cache/apk/* @@ -6,7 +8,7 @@ WORKDIR /app COPY package*.json ./ -RUN npm install +RUN rm -rf package-lock.json && npm install COPY . . diff --git a/backend/package-lock.json b/backend/package-lock.json index 355e842d..891ec2ab 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -19,7 +19,7 @@ "helmet": "^7.1.0", "jsonwebtoken": "^9.0.2", "nodemailer": "^6.9.15", - "pg": "^8.11.5", + "pg": "^8.13.1", "sequelize": "^6.37.3" }, "devDependencies": { @@ -1488,9 +1488,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -1906,9 +1906,9 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -1930,7 +1930,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -1945,6 +1945,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express-validator": { @@ -4124,9 +4128,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, "node_modules/pathval": { @@ -4140,14 +4144,14 @@ } }, "node_modules/pg": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", - "integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==", + "version": "8.13.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.1.tgz", + "integrity": "sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==", "license": "MIT", "dependencies": { - "pg-connection-string": "^2.6.4", - "pg-pool": "^3.6.2", - "pg-protocol": "^1.6.1", + "pg-connection-string": "^2.7.0", + "pg-pool": "^3.7.0", + "pg-protocol": "^1.7.0", "pg-types": "^2.1.0", "pgpass": "1.x" }, @@ -4174,9 +4178,9 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", - "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", + "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", "license": "MIT" }, "node_modules/pg-int8": { @@ -4189,18 +4193,18 @@ } }, "node_modules/pg-pool": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", - "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.0.tgz", + "integrity": "sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==", "license": "MIT", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", - "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz", + "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==", "license": "MIT" }, "node_modules/pg-types": { diff --git a/backend/package.json b/backend/package.json index 21211d1b..9c83998d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -28,7 +28,7 @@ "helmet": "^7.1.0", "jsonwebtoken": "^9.0.2", "nodemailer": "^6.9.15", - "pg": "^8.11.5", + "pg": "^8.13.1", "sequelize": "^6.37.3" }, "devDependencies": { diff --git a/backend/public/scripts/bannerRender.js b/backend/public/scripts/bannerRender.js new file mode 100644 index 00000000..e69de29b diff --git a/backend/public/scripts/popupRender.js b/backend/public/scripts/popupRender.js new file mode 100644 index 00000000..6c8cb366 --- /dev/null +++ b/backend/public/scripts/popupRender.js @@ -0,0 +1,52 @@ +const showPopup = (popupData) => { + if (!popupData || popupData.length === 0) { + console.warn('No popup data available'); + return; + } + + popupData.forEach(popup => { + // Create popup container + const popupContainer = document.createElement('div'); + Object.assign(popupContainer.style, { + position: 'fixed', + bottom: '20px', + right: '20px', + backgroundColor: popup.headerBg || '#FFFFFF', + padding: '20px', + border: '1px solid #000', + zIndex: 1000, + }); + + // Create header + const header = document.createElement('h2'); + header.textContent = popup.headerText; + header.style.color = popup.headerTextColor || '#000'; + popupContainer.appendChild(header); + + // Create content + const content = document.createElement('div'); + content.innerHTML = popup.contentHtml; + Object.assign(content.style, { + color: popup.fontColor || '#000', + fontSize: popup.font || '14px', + }); + popupContainer.appendChild(content); + + // Create action button + const actionButton = document.createElement('button'); + actionButton.textContent = popup.actionButtonText || 'Close'; + Object.assign(actionButton.style, { + backgroundColor: popup.actionButtonColor || '#CCC', + }); + actionButton.addEventListener('click', () => { + document.body.removeChild(popupContainer); // Remove popup when button is clicked + }); + popupContainer.appendChild(actionButton); + + // Append the popup to the document body + document.body.appendChild(popupContainer); + }); + }; + + export default showPopup; + \ No newline at end of file diff --git a/backend/public/snippets/popupSnippet.html b/backend/public/snippets/popupSnippet.html new file mode 100644 index 00000000..609f3217 --- /dev/null +++ b/backend/public/snippets/popupSnippet.html @@ -0,0 +1,30 @@ + + + \ No newline at end of file diff --git a/backend/src/controllers/onboard/bannerData.controller.js b/backend/src/controllers/onboard/bannerData.controller.js new file mode 100644 index 00000000..394d0a4f --- /dev/null +++ b/backend/src/controllers/onboard/bannerData.controller.js @@ -0,0 +1,12 @@ +const bannerService = require("../service/banner.service.js"); +const getBannerData = async (req, res) => { + try { + const { userId } = req.body; + const bannerData = await bannerService.getBannerData(userId); + res.status(200).json(bannerData); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +module.exports = { getBannerData }; diff --git a/backend/src/controllers/onboard/index.js b/backend/src/controllers/onboard/index.js new file mode 100644 index 00000000..db465d6d --- /dev/null +++ b/backend/src/controllers/onboard/index.js @@ -0,0 +1,7 @@ +module.exports = { + // bannerData: require('./bannerData.controller'), + popupData: require('./popupData.controller'), + // tourData: require('./tourData.controller'), + // hintData: require('./hintData.controller') + }; + \ No newline at end of file diff --git a/backend/src/controllers/onboard/popupData.controller.js b/backend/src/controllers/onboard/popupData.controller.js new file mode 100644 index 00000000..89420a83 --- /dev/null +++ b/backend/src/controllers/onboard/popupData.controller.js @@ -0,0 +1,13 @@ +const popupService = require('../../service/popup.service'); + +const getPopupData = async (req, res) => { + try { + const { userId } = req.body; + const popupData = await popupService.getPopups(userId); + res.status(200).json(popupData); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +module.exports = { getPopupData }; diff --git a/backend/src/controllers/onboard/tourData.controller.js b/backend/src/controllers/onboard/tourData.controller.js new file mode 100644 index 00000000..3a2ce68f --- /dev/null +++ b/backend/src/controllers/onboard/tourData.controller.js @@ -0,0 +1,13 @@ +const tourService = require('../service/tour.service'); + +const getTourData = async (req, res) => { + try { + const { userId } = req.body; + const tourData = await tourService.getTourData(userId); + res.status(200).json(tourData); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +module.exports = { getTourData }; diff --git a/backend/src/middleware/onboard.middleware.js b/backend/src/middleware/onboard.middleware.js new file mode 100644 index 00000000..fd5a79e7 --- /dev/null +++ b/backend/src/middleware/onboard.middleware.js @@ -0,0 +1,35 @@ +const db = require('../models'); +const { v4: uuidv4 } = require('uuid'); + +// Middleware to validate API ID +const validateApiId = async (req, res, next) => { + const { apiId } = req.query; // Assume API ID is sent in query params + if (!apiId) { + return res.status(400).json({ error: "API ID is required." }); + } + + try { + const user = await db.User.findOne({ where: { apiId } }); // API ID must be in User model + if (!user) { + return res.status(403).json({ error: "Invalid API ID." }); + } + + req.user = user; // Attach the user to the request for future use + next(); + } catch (error) { + return res.status(500).json({ error: "API ID validation failed." }); + } +}; + +// Middleware to generate client ID for each session +const generateClientId = (req, res, next) => { + if (!req.session.clientId) { + req.session.clientId = uuidv4(); // Generate new client ID and store in session + } + next(); +}; + +module.exports = { + validateApiId, + generateClientId +}; diff --git a/backend/src/routes/guide.routes.js b/backend/src/routes/guide.routes.js index 4904d726..adf32244 100644 --- a/backend/src/routes/guide.routes.js +++ b/backend/src/routes/guide.routes.js @@ -3,7 +3,7 @@ const guideController = require("../controllers/guide.controller.js"); const router = express.Router(); -router.get("/get_guides_by_url", guideController.getGuidesByUrl); +router.post("/get_guides_by_url", guideController.getGuidesByUrl); // router.get("/get_incomplete_guides_by_url", guideController.getIncompleteGuidesByUrl); module.exports = router; diff --git a/backend/src/routes/onboard.routes.js b/backend/src/routes/onboard.routes.js new file mode 100644 index 00000000..de26f64f --- /dev/null +++ b/backend/src/routes/onboard.routes.js @@ -0,0 +1,15 @@ +const express = require("express"); +const { validateApiId, generateClientId } = require("../middleware/onboard.middleware"); +const onboardControllers = require("../controllers/onboard"); + +const router = express.Router(); + +router.use(validateApiId); +router.use(generateClientId); + +// router.get("/banner", onboardControllers.bannerData.getBannerData); +router.get("/popup", onboardControllers.popupData.getPopupData); +// router.get("/tour", onboardControllers.tourData.getTourData); +// router.get("/hint", onboardControllers.hintData.getHintData); + +module.exports = router; diff --git a/backend/src/routes/script.routes.js b/backend/src/routes/script.routes.js new file mode 100644 index 00000000..e69de29b diff --git a/backend/src/scripts/popup.script.js b/backend/src/scripts/popup.script.js new file mode 100644 index 00000000..e69de29b diff --git a/backend/src/service/popup.service.js b/backend/src/service/popup.service.js index dbc475e7..21824d2c 100644 --- a/backend/src/service/popup.service.js +++ b/backend/src/service/popup.service.js @@ -55,6 +55,24 @@ class PopupService { } } + async getPopupByApiAndClientId(apiId, clientId) { + try { + return await Popup.findAll({ + include: [ + { + model: db.User, + as: "creator", + where: { apiId } + }, + ], + where: { clientId }, + order: [['createdAt', 'DESC']], + }); + } catch (error) { + throw new Error("Error retrieving popups for the given API and Client ID"); + } + } + async getPopupByUrl(url) { try { return await Popup.findAll({ where: { url } }); diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 27ca54d8..57fa8611 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,4 +1,6 @@ -FROM node:22-alpine3.20 +FROM node:22-alpine3.21 + +RUN apk update && apk add bash && rm -rf /var/cache/apk/* RUN apk update && apk add bash && rm -rf /var/cache/apk/* @@ -7,7 +9,6 @@ WORKDIR /app COPY package*.json ./ RUN rm -rf package-lock.json && npm install -RUN npm install formik COPY . . diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ea88f237..a4ff140d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -34,12 +34,8 @@ "react-dom": "^18.2.0", "react-dropzone": "^14.2.9", "react-icons": "^5.3.0", - "react-redux": "^9.1.2", "react-router": "^6.27.0", "react-router-dom": "^6.27.0", - "redux": "^5.0.1", - "vite-plugin-svgr": "^4.2.0", - "web-vitals": "^2.1.4", "yup": "^1.4.0" }, "devDependencies": { @@ -60,6 +56,7 @@ }, "node_modules/@ampproject/remapping": { "version": "2.3.0", + "dev": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -82,6 +79,7 @@ }, "node_modules/@babel/compat-data": { "version": "7.25.8", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -89,6 +87,7 @@ }, "node_modules/@babel/core": { "version": "7.25.8", + "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", @@ -117,10 +116,12 @@ }, "node_modules/@babel/core/node_modules/convert-source-map": { "version": "2.0.0", + "dev": true, "license": "MIT" }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -141,6 +142,7 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.25.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.25.7", @@ -155,6 +157,7 @@ }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -173,6 +176,7 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.25.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.7", @@ -189,6 +193,7 @@ }, "node_modules/@babel/helper-simple-access": { "version": "7.25.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.7", @@ -214,6 +219,7 @@ }, "node_modules/@babel/helper-validator-option": { "version": "7.25.7", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -221,6 +227,7 @@ }, "node_modules/@babel/helpers": { "version": "7.25.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.25.7", @@ -1166,6 +1173,7 @@ }, "node_modules/@rollup/pluginutils": { "version": "5.1.2", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", @@ -1434,219 +1442,6 @@ "storybook": "^8.3.6" } }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "8.0.0", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "8.0.0", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "8.0.0", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "8.0.0", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "8.0.0", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "8.0.0", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "8.1.0", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "8.0.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-preset": { - "version": "8.1.0", - "license": "MIT", - "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", - "@svgr/babel-plugin-transform-svg-component": "8.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/core": { - "version": "8.1.0", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3", - "snake-case": "^3.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/core/node_modules/cosmiconfig": { - "version": "8.3.6", - "license": "MIT", - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "8.0.0", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.21.3", - "entities": "^4.4.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/plugin-jsx": { - "version": "8.1.0", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "@svgr/hast-util-to-babel-ast": "8.0.0", - "svg-parser": "^2.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@svgr/core": "*" - } - }, "node_modules/@swc/core": { "version": "1.7.36", "hasInstallScript": true, @@ -2279,7 +2074,9 @@ } }, "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.5", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", + "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", "license": "MIT", "dependencies": { "@types/react": "*", @@ -3211,6 +3008,7 @@ }, "node_modules/browserslist": { "version": "4.24.0", + "dev": true, "funding": [ { "type": "opencollective", @@ -3290,18 +3088,9 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "6.3.0", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001669", + "dev": true, "funding": [ { "type": "opencollective", @@ -3485,7 +3274,9 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -3743,14 +3534,6 @@ "version": "3.1.7", "license": "(MPL-2.0 OR Apache-2.0)" }, - "node_modules/dot-case": { - "version": "3.0.4", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, "node_modules/ee-first": { "version": "1.1.1", "dev": true, @@ -3758,6 +3541,7 @@ }, "node_modules/electron-to-chromium": { "version": "1.5.41", + "dev": true, "license": "ISC" }, "node_modules/encodeurl": { @@ -4005,6 +3789,7 @@ }, "node_modules/escalade": { "version": "3.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4388,6 +4173,7 @@ }, "node_modules/estree-walker": { "version": "2.0.2", + "dev": true, "license": "MIT" }, "node_modules/esutils": { @@ -4437,7 +4223,9 @@ } }, "node_modules/express": { - "version": "4.21.1", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dev": true, "license": "MIT", "dependencies": { @@ -4460,7 +4248,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -4475,6 +4263,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/debug": { @@ -4782,6 +4574,7 @@ }, "node_modules/gensync": { "version": "1.0.0-beta.2", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -5654,6 +5447,7 @@ }, "node_modules/js-yaml": { "version": "4.1.0", + "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -5741,6 +5535,7 @@ }, "node_modules/json5": { "version": "2.2.3", + "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -5877,15 +5672,9 @@ "get-func-name": "^2.0.1" } }, - "node_modules/lower-case": { - "version": "2.0.2", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, "node_modules/lru-cache": { "version": "5.1.1", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" @@ -6111,7 +5900,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", @@ -6144,16 +5935,9 @@ "dev": true, "license": "MIT" }, - "node_modules/no-case": { - "version": "3.0.4", - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, "node_modules/node-releases": { "version": "2.0.18", + "dev": true, "license": "MIT" }, "node_modules/npm-run-path": { @@ -6455,7 +6239,9 @@ "license": "MIT" }, "node_modules/path-to-regexp": { - "version": "0.1.10", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "dev": true, "license": "MIT" }, @@ -6485,6 +6271,7 @@ }, "node_modules/picomatch": { "version": "2.3.1", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -7020,31 +6807,6 @@ "version": "18.3.1", "license": "MIT" }, - "node_modules/react-redux": { - "version": "9.1.2", - "license": "MIT", - "dependencies": { - "@types/use-sync-external-store": "^0.0.3", - "use-sync-external-store": "^1.0.0" - }, - "peerDependencies": { - "@types/react": "^18.2.25", - "react": "^18.0", - "redux": "^5.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, - "node_modules/react-redux/node_modules/@types/use-sync-external-store": { - "version": "0.0.3", - "license": "MIT" - }, "node_modules/react-router": { "version": "6.27.0", "license": "MIT", @@ -7112,10 +6874,6 @@ "node": ">=0.10.0" } }, - "node_modules/redux": { - "version": "5.0.1", - "license": "MIT" - }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "dev": true, @@ -7615,14 +7373,6 @@ "node": ">=8" } }, - "node_modules/snake-case": { - "version": "3.0.4", - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, "node_modules/source-map": { "version": "0.5.7", "license": "BSD-3-Clause", @@ -7865,10 +7615,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/svg-parser": { - "version": "2.0.4", - "license": "MIT" - }, "node_modules/symbol-tree": { "version": "3.2.4", "dev": true, @@ -8209,7 +7955,7 @@ }, "node_modules/typescript": { "version": "5.6.3", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "peer": true, "bin": { @@ -8297,6 +8043,7 @@ }, "node_modules/update-browserslist-db": { "version": "1.1.1", + "dev": true, "funding": [ { "type": "opencollective", @@ -8464,18 +8211,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/vite-plugin-svgr": { - "version": "4.2.0", - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.5", - "@svgr/core": "^8.1.0", - "@svgr/plugin-jsx": "^8.1.0" - }, - "peerDependencies": { - "vite": "^2.6.0 || 3 || 4 || 5" - } - }, "node_modules/vite/node_modules/esbuild": { "version": "0.21.5", "hasInstallScript": true, @@ -8625,10 +8360,6 @@ "node": ">=10.13.0" } }, - "node_modules/web-vitals": { - "version": "2.1.4", - "license": "Apache-2.0" - }, "node_modules/webidl-conversions": { "version": "7.0.0", "dev": true, @@ -8928,6 +8659,7 @@ }, "node_modules/yallist": { "version": "3.1.1", + "dev": true, "license": "ISC" }, "node_modules/yaml": { @@ -8960,4 +8692,3 @@ } } } - diff --git a/frontend/src/scenes/settings/CodeTab/CodeTab.jsx b/frontend/src/scenes/settings/CodeTab/CodeTab.jsx index 4a80dcd5..aad20339 100644 --- a/frontend/src/scenes/settings/CodeTab/CodeTab.jsx +++ b/frontend/src/scenes/settings/CodeTab/CodeTab.jsx @@ -62,34 +62,20 @@ const CodeTab = () => {

                 {`
-`}
+                            var s=document.createElement("script");
+                            s.type="text/javascript";
+                            s.async=false;
+                            s.onerror=()=>{console.log("onboard not loaded");};
+                            s.src = 'http://localhost:8082/main.js=${apiKey}';
+                            (document.getElementsByTagName("head")[0] || document.getElementsByTagName("body")[0]).appendChild(script);
+                        })();
+                    
+                `}
             
) diff --git a/jsAgent/banner.js b/jsAgent/banner.js new file mode 100644 index 00000000..80ce9d76 --- /dev/null +++ b/jsAgent/banner.js @@ -0,0 +1 @@ +console.log('banner.js is here!'); \ No newline at end of file diff --git a/jsAgent/links.js b/jsAgent/links.js new file mode 100644 index 00000000..ffcd3320 --- /dev/null +++ b/jsAgent/links.js @@ -0,0 +1,49 @@ +console.log('link.js is loaded'); +const linksDefaultOptions = { + "url": "https://www.google.com", + "order": 1, + "target": true +}; + + + + +bw.links={ + init:function(){ + bw.links.putIcon(); + bw.links.putPlaceHolder(); + bw.links.bindClick(); + }, + putIcon:function(){ + let temp_html = ` + + + `; + document.body.insertAdjacentHTML('beforeend', temp_html); + }, + putPlaceHolder: function(){ + let temp_html = ``; + document.getElementById('bw-links').insertAdjacentHTML('beforebegin', temp_html); + }, + bindClick : function(){ + bw.util.bindLive("#bw-links", "click", function(){ + bw.links.toggle(); + }) + }, + show : function(){ + document.getElementById('bw-links-placeholder').style.display = 'block'; + }, + hide : function(){ + document.getElementById('bw-links-placeholder').style.display = 'none'; + }, + toggle: function(){ + document.getElementById('bw-links-placeholder').style.display = document.getElementById('bw-links-placeholder').style.display=='none'? 'block':'none'; + } + +}; + +(async function () { + bw.links.init(); +})(); + + diff --git a/jsAgent/main.js b/jsAgent/main.js new file mode 100644 index 00000000..fbc192d6 --- /dev/null +++ b/jsAgent/main.js @@ -0,0 +1,178 @@ +//CONSTANTS +const BW_SERVER_ENDPOINT_BASE = + "http://localhost:3000/api/guide/get_guides_by_url"; +const BW_JS_BASE_URL = "http://localhost:8082/"; +const BW_POPUP_JS_URL = `${BW_JS_BASE_URL}popup.js`; +const BW_LINKS_JS_URL = `${BW_JS_BASE_URL}links.js`; +const BW_BANNER_JS_URL = `${BW_JS_BASE_URL}banner.js`; +const BW_TOUR_JS_URL = `${BW_JS_BASE_URL}tour.js`; + +const BW_USER_KEY = "BW_USER_KEY"; + +//GLOBALS +window.BW_USER = ""; +if (window.bw === undefined) { + window.bw = {}; +} +if (bw.util === undefined) { + bw.util = {}; +} + +bw.util = { + isScriptLoaded: function (src) { + var scripts = document.getElementsByTagName("script"); + for (var i = 0; i < scripts.length; i++) + if (scripts[i].getAttribute("src") == src) return true; + return false; + }, + loadScriptAsync: function (url, cb, errcb) { + try { + if (bw.util.isScriptLoaded(url)) { + cb && cb(); + } else { + var script = document.createElement("script"); + script.type = "text/javascript"; + script.async = false; + if (script.readyState) { + script.onreadystatechange = function () { + if ( + script.readyState == "loaded" || + script.readyState == "complete" + ) { + script.onreadystatechange = null; + cb && cb(); + } + }; + } else { + script.onload = function () { + cb && cb(); + }; + } + script.onerror = function () { + errcb && errcb(); + }; + script.src = url; + ( + document.getElementsByTagName("head")[0] || + document.getElementsByTagName("body")[0] + ).appendChild(script); + } + } catch (e) { + console.log(e); + } + }, + bindLive: function (selector, event, cb, cnx) { + bw.util.addEvent(cnx || document, event, function (e) { + var qs = (cnx || document).querySelectorAll(selector); + if (qs) { + var el = e.target || e.srcElement, + index = -1; + while (el && (index = Array.prototype.indexOf.call(qs, el)) === -1) + el = el.parentElement; + if (index > -1) cb.call(el, e); + } + }); + }, + addEvent: function (el, type, fn) { + if (el.attachEvent) el.attachEvent("on" + type, fn); + else el.addEventListener(type, fn); + }, + generateGUID: function () { + var guid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace( + /[xy]/g, + function (o) { + var n = (Math.random() * 16) | 0, + g = o == "x" ? n : (n & 3) | 8; + return g.toString(16); + } + ); + return guid; + }, +}; + +bw.data = { + getData: async function (userId) { + const myHeaders = new Headers(); + myHeaders.append("Content-Type", "application/json"); + + const requestOptions = { + method: "POST", + headers: myHeaders, + body: JSON.stringify({ + userId, + url: location.origin, + }), + redirect: "follow", + }; + + const response = await fetch(BW_SERVER_ENDPOINT_BASE, requestOptions); + const data = await response.json(); + return data; + }, +}; + +bw.store = { + insert: function (key, value) { + localStorage.setItem(key, value); + }, + remove: function (key) { + localStorage.removeItem(key); + }, + get: function (key) { + return localStorage.getItem(key); + }, +}; + +bw.user = { + createUser: function () { + const generated_userId = bw.util.generateGUID(); + bw.store.insert(BW_USER_KEY, generated_userId); + window.BW_USER = generated_userId; + }, + checkIfBwUserCreated: function () { + let result = false; + let userID = bw.store.get(BW_USER_KEY); + if (userID != null) { + result = true; + } + return result; + }, + getUserID: function () { + return bw.store.get(BW_USER_KEY); + }, + removeUser: function () { + bw.store.remove(BW_USER_KEY); + }, +}; + +bw.init = (cb) => { + if (!bw.user.checkIfBwUserCreated()) { + bw.user.createUser(); + } + window.BW_USER = bw.user.getUserID(); + cb && cb(); +}; + +(function () { + bw.init(async function () { + try { + const onBoardConfig = await bw.data.getData(window.BW_USER); + console.log("data loaded:", onBoardConfig); + window.bwonboarddata = onBoardConfig; + if (onBoardConfig.popup.length > 0) { + bw.util.loadScriptAsync(BW_POPUP_JS_URL); + } + if (onBoardConfig.tour?.length > 0) { + bw.util.loadScriptAsync(BW_TOUR_JS_URL); + } + if (onBoardConfig.banner?.length > 0) { + bw.util.loadScriptAsync(BW_BANNER_JS_URL); + } + if (onBoardConfig.link?.length > 0) { + bw.util.loadScriptAsync(BW_LINKS_JS_URL); + } + } catch (error) { + console.log("error :", error); + } + }); +})(); diff --git a/jsAgent/popup.js b/jsAgent/popup.js new file mode 100644 index 00000000..1ca7aa73 --- /dev/null +++ b/jsAgent/popup.js @@ -0,0 +1,117 @@ +const popupDefaultOptions = { + padding: 20, + overlayOpacity:"0", + overlayBlur:0, + closeButtonAction: "no action", + popupSize: "small", + url: "https://", + actionButtonText: "Take me to subscription page", + headerBackgroundColor: "#F8F9F8", + headerColor: "#101828", + textColor: "#344054", + buttonBackgroundColor: "#7F56D9", + buttonTextColor: "#FFFFFF", + header: "default", + content: "" +} + +const PopUpSize = Object.freeze({ + small:{ + width: 400, + height: 250 + }, + medium:{ + width: 500, + height: 300 + }, + large:{ + width: 700, + height: 350 + } +}); + +bw.popup = { + init: function () { + bw.popup.addOverlay(); + bw.popup.addModal(()=>{ + bw.popup.bindEvents(); + }); + }, + + addOverlay: function () { + document.body.insertAdjacentHTML('afterbegin', `
`); + }, + addModal: function (cb) { + const options = window.bwonboarddata.popup[0]; + let overlay = document.getElementById('bw-overlay'); + + let option = { + ...popupDefaultOptions, + ...options + }; + const size = PopUpSize[option.popupSize]; + + let temp_html = ` +
+ +
` + overlay.insertAdjacentHTML('afterbegin', temp_html); + cb && cb(); + }, + addHeader: function(headerTitle, bgColor, textColor, padding){ + let headerHtml = ``; + return headerHtml; + }, + addButton: function(text, bgColor, textColor, padding, btnId, btnEvent, btnlink){ + let buttonHtml = ``; + + bw.util.bindLive(`#${btnId}`, 'click', function(){ + if(btnEvent == 'no action'){ + bw.popup.hideModal(); + } + else if(btnEvent == 'open url'){ + location.href = btnlink; + } + else if(btnEvent == 'open url in a new tab'){ + window.open(btnlink); + } + }); + + return buttonHtml; + }, + bindEvents: function(){ + bw.util.bindLive(`#bw-modal-close`, 'click', function(){ + bw.popup.hideModal(); + }); + + bw.util.bindLive("#bw-overlay", "click",function(){ + bw.popup.hideModal(); + }); + }, + showModal: function () { + document.getElementById('bw-overlay').style.display = 'block'; + }, + hideModal: function(){ + document.getElementById('bw-overlay').style.display = 'none'; + } +}; + + +(async function () { + bw.popup.init(); +})(); \ No newline at end of file diff --git a/jsAgent/popupRender.js b/jsAgent/popupRender.js new file mode 100644 index 00000000..6c8cb366 --- /dev/null +++ b/jsAgent/popupRender.js @@ -0,0 +1,52 @@ +const showPopup = (popupData) => { + if (!popupData || popupData.length === 0) { + console.warn('No popup data available'); + return; + } + + popupData.forEach(popup => { + // Create popup container + const popupContainer = document.createElement('div'); + Object.assign(popupContainer.style, { + position: 'fixed', + bottom: '20px', + right: '20px', + backgroundColor: popup.headerBg || '#FFFFFF', + padding: '20px', + border: '1px solid #000', + zIndex: 1000, + }); + + // Create header + const header = document.createElement('h2'); + header.textContent = popup.headerText; + header.style.color = popup.headerTextColor || '#000'; + popupContainer.appendChild(header); + + // Create content + const content = document.createElement('div'); + content.innerHTML = popup.contentHtml; + Object.assign(content.style, { + color: popup.fontColor || '#000', + fontSize: popup.font || '14px', + }); + popupContainer.appendChild(content); + + // Create action button + const actionButton = document.createElement('button'); + actionButton.textContent = popup.actionButtonText || 'Close'; + Object.assign(actionButton.style, { + backgroundColor: popup.actionButtonColor || '#CCC', + }); + actionButton.addEventListener('click', () => { + document.body.removeChild(popupContainer); // Remove popup when button is clicked + }); + popupContainer.appendChild(actionButton); + + // Append the popup to the document body + document.body.appendChild(popupContainer); + }); + }; + + export default showPopup; + \ No newline at end of file diff --git a/jsAgent/tour.js b/jsAgent/tour.js new file mode 100644 index 00000000..3e96f605 --- /dev/null +++ b/jsAgent/tour.js @@ -0,0 +1 @@ +console.log('tour.js is here'); \ No newline at end of file