Merge pull request #206 from Hexastack/feat/widget-changes

Feat/widget changes
This commit is contained in:
Med Marrouchi 2024-10-12 19:58:12 +01:00 committed by GitHub
commit b11761ac2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 925 additions and 259 deletions

View File

@ -63,6 +63,10 @@ export class BlockService extends BaseService<Block, BlockPopulate, BlockFull> {
blocks: BlockFull[], blocks: BlockFull[],
event: EventWrapper<any, any>, event: EventWrapper<any, any>,
): Promise<BlockFull | undefined> { ): Promise<BlockFull | undefined> {
if (!blocks.length) {
return undefined;
}
// Search for block matching a given event // Search for block matching a given event
let block: BlockFull | undefined = undefined; let block: BlockFull | undefined = undefined;
const payload = event.getPayload(); const payload = event.getPayload();

View File

@ -443,7 +443,7 @@ export class BotService {
}); });
if (!blocks.length) { if (!blocks.length) {
return this.logger.debug('No starting message blocks was found'); this.logger.debug('No starting message blocks was found');
} }
// Search for a block match // Search for a block match

View File

@ -67,7 +67,7 @@ export const settingModels: SettingCreateDto[] = [
{ {
group: 'nlp_settings', group: 'nlp_settings',
label: 'threshold', label: 'threshold',
value: 0.9, value: 0.1,
type: SettingType.number, type: SettingType.number,
config: { config: {
min: 0, min: 0,

View File

@ -68,11 +68,8 @@ describe('WebsocketGateway', () => {
it('should connect successfully', async () => { it('should connect successfully', async () => {
ioClient.connect(); ioClient.connect();
await new Promise<void>((resolve) => { await new Promise<void>((resolve) => {
// ioClient.on('connect', () => { ioClient.on('connect', () => {
// console.log('connected'); expect(true).toBe(true);
// });
ioClient.on('message', (data) => {
expect(data.statusCode).toBe(200);
resolve(); resolve();
}); });
}); });

View File

@ -255,22 +255,6 @@ export class WebsocketGateway
this.logger.debug(`Number of connected clients: ${sockets?.size}`); this.logger.debug(`Number of connected clients: ${sockets?.size}`);
this.eventEmitter.emit(`hook:websocket:connection`, client); this.eventEmitter.emit(`hook:websocket:connection`, client);
// @TODO : Revisit once we don't use anymore in frontend
const response = new SocketResponse();
client.send(
response
.setHeaders({
'access-control-allow-origin':
config.security.cors.allowOrigins.join(','),
vary: 'Origin',
'access-control-allow-credentials':
config.security.cors.allowCredentials.toString(),
})
.status(200)
.json({
success: true,
}),
);
} }
async handleDisconnect(client: Socket): Promise<void> { async handleDisconnect(client: Socket): Promise<void> {

View File

@ -36,13 +36,3 @@ services:
pull_policy: build pull_policy: build
volumes: volumes:
- ../frontend/:/app/frontend/ - ../frontend/:/app/frontend/
widget:
build:
context: ../widget
target: development
pull_policy: build
volumes:
- ../widget/src:/app/src
ports:
- ${APP_WIDGET_PORT}:5173

View File

@ -0,0 +1,12 @@
version: "3.9"
services:
widget:
build:
context: ../widget
target: development
pull_policy: build
volumes:
- ../widget/src:/app/src
ports:
- ${APP_WIDGET_PORT}:5173

View File

@ -0,0 +1,13 @@
version: "3.9"
services:
widget:
container_name: widget
image: hexastack/hexabot-widget:latest
networks:
- app-network
depends_on:
api:
condition: service_healthy
ports:
- ${APP_WIDGET_PORT}:5173

View File

@ -52,17 +52,6 @@ services:
volumes: volumes:
- mongo-data:/data/db - mongo-data:/data/db
widget:
container_name: widget
image: hexastack/hexabot-widget:latest
networks:
- app-network
depends_on:
api:
condition: service_healthy
ports:
- ${APP_WIDGET_PORT}:5173
volumes: volumes:
mongo-data: mongo-data:
api-data: api-data:

View File

@ -1,7 +1,7 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
import withTM from "next-transpile-modules"; import withTM from "next-transpile-modules";
const nextConfig = withTM(["hexabot-widget"])({ const nextConfig = withTM(["hexabot-chat-widget"])({
async rewrites() { async rewrites() {
return [ return [
{ {
@ -11,7 +11,7 @@ const nextConfig = withTM(["hexabot-widget"])({
]; ];
}, },
webpack(config) { webpack(config) {
if (process.env.NODE_ENV==="development") { if (process.env.NODE_ENV === "development") {
config.watchOptions = { config.watchOptions = {
poll: 1000, poll: 1000,
aggregateTimeout: 300, aggregateTimeout: 300,

View File

@ -29,7 +29,7 @@
"axios": "^1.7.7", "axios": "^1.7.7",
"eazychart-css": "^0.2.1-alpha.0", "eazychart-css": "^0.2.1-alpha.0",
"eazychart-react": "^0.8.0-alpha.0", "eazychart-react": "^0.8.0-alpha.0",
"hexabot-widget": "*", "hexabot-chat-widget": "*",
"next": "^14.2.13", "next": "^14.2.13",
"next-transpile-modules": "^10.0.1", "next-transpile-modules": "^10.0.1",
"normalizr": "^3.6.2", "normalizr": "^3.6.2",

View File

@ -7,7 +7,7 @@
*/ */
import { Avatar, Box } from "@mui/material"; import { Avatar, Box } from "@mui/material";
import UiChatWidget from "hexabot-widget/src/UiChatWidget"; import UiChatWidget from "hexabot-chat-widget/src/UiChatWidget";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { getAvatarSrc } from "@/components/inbox/helpers/mapMessages"; import { getAvatarSrc } from "@/components/inbox/helpers/mapMessages";

591
package-lock.json generated
View File

@ -63,7 +63,7 @@
"axios": "^1.7.7", "axios": "^1.7.7",
"eazychart-css": "^0.2.1-alpha.0", "eazychart-css": "^0.2.1-alpha.0",
"eazychart-react": "^0.8.0-alpha.0", "eazychart-react": "^0.8.0-alpha.0",
"hexabot-widget": "*", "hexabot-chat-widget": "*",
"next": "^14.2.13", "next": "^14.2.13",
"next-transpile-modules": "^10.0.1", "next-transpile-modules": "^10.0.1",
"normalizr": "^3.6.2", "normalizr": "^3.6.2",
@ -977,6 +977,261 @@
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz",
"integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": { "node_modules/@esbuild/linux-x64": {
"version": "0.21.5", "version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
@ -994,6 +1249,102 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/@esbuild/netbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@eslint-community/eslint-utils": { "node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@ -2034,6 +2385,148 @@
} }
} }
}, },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.5.tgz",
"integrity": "sha512-SU5cvamg0Eyu/F+kLeMXS7GoahL+OoizlclVFX3l5Ql6yNlywJJ0OuqTzUx0v+aHhPHEB/56CT06GQrRrGNYww==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.5.tgz",
"integrity": "sha512-S4pit5BP6E5R5C8S6tgU/drvgjtYW76FBuG6+ibG3tMvlD1h9LHVF9KmlmaUBQ8Obou7hEyS+0w+IR/VtxwNMQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz",
"integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.5.tgz",
"integrity": "sha512-D8brJEFg5D+QxFcW6jYANu+Rr9SlKtTenmsX5hOSzNYVrK5oLAEMTUgKWYJP+wdKyCdeSwnapLsn+OVRFycuQg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.5.tgz",
"integrity": "sha512-PNqXYmdNFyWNg0ma5LdY8wP+eQfdvyaBAojAXgO7/gs0Q/6TQJVXAXe8gwW9URjbS0YAammur0fynYGiWsKlXw==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.5.tgz",
"integrity": "sha512-kSSCZOKz3HqlrEuwKd9TYv7vxPYD77vHSUvM2y0YaTGnFc8AdI5TTQRrM1yIp3tXCKrSL9A7JLoILjtad5t8pQ==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.5.tgz",
"integrity": "sha512-oTXQeJHRbOnwRnRffb6bmqmUugz0glXaPyspp4gbQOPVApdpRrY/j7KP3lr7M8kTfQTyrBUzFjj5EuHAhqH4/w==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.5.tgz",
"integrity": "sha512-qnOTIIs6tIGFKCHdhYitgC2XQ2X25InIbZFor5wh+mALH84qnFHvc+vmWUpyX97B0hNvwNUL4B+MB8vJvH65Fw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.5.tgz",
"integrity": "sha512-TMYu+DUdNlgBXING13rHSfUc3Ky5nLPbWs4bFnT+R6Vu3OvXkTkixvvBKk8uO4MT5Ab6lC3U7x8S8El2q5o56w==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.5.tgz",
"integrity": "sha512-PTQq1Kz22ZRvuhr3uURH+U/Q/a0pbxJoICGSprNLAoBEkyD3Sh9qP5I0Asn0y0wejXQBbsVMRZRxlbGFD9OK4A==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.5.tgz",
"integrity": "sha512-bR5nCojtpuMss6TDEmf/jnBnzlo+6n1UhgwqUvRoe4VIotC7FG1IKkyJbwsT7JDsF2jxR+NTnuOwiGv0hLyDoQ==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": { "node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.22.5", "version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.5.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.5.tgz",
@ -2060,6 +2553,45 @@
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.5.tgz",
"integrity": "sha512-RXT8S1HP8AFN/Kr3tg4fuYrNxZ/pZf1HemC5Tsddc6HzgGnJm0+Lh5rAHJkDuW3StI0ynNXukidROMXYl6ew8w==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.5.tgz",
"integrity": "sha512-ElTYOh50InL8kzyUD6XsnPit7jYCKrphmddKAe1/Ytt74apOxDq5YEcbsiKs0fR3vff3jEneMM+3I7jbqaMyBg==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.5.tgz",
"integrity": "sha512-+lvL/4mQxSV8MukpkKyyvfwhH266COcWlXE/1qxwN08ajovta3459zrjLghYMgDerlzNwLAcFpvU+WWE5y6nAQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rushstack/eslint-patch": { "node_modules/@rushstack/eslint-patch": {
"version": "1.10.4", "version": "1.10.4",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz",
@ -4419,6 +4951,22 @@
"@esbuild/win32-x64": "0.21.5" "@esbuild/win32-x64": "0.21.5"
} }
}, },
"node_modules/esbuild/node_modules/@esbuild/darwin-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/escalade": { "node_modules/escalade": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
@ -5420,6 +5968,20 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@ -5794,6 +6356,10 @@
"resolved": "https://registry.npmjs.org/heap/-/heap-0.2.5.tgz", "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.5.tgz",
"integrity": "sha512-G7HLD+WKcrOyJP5VQwYZNC3Z6FcQ7YYjEFiFoIj8PfEr73mu421o8B1N5DKUcc8K37EsJ2XXWA8DtrDz/2dReg==" "integrity": "sha512-G7HLD+WKcrOyJP5VQwYZNC3Z6FcQ7YYjEFiFoIj8PfEr73mu421o8B1N5DKUcc8K37EsJ2XXWA8DtrDz/2dReg=="
}, },
"node_modules/hexabot-chat-widget": {
"resolved": "widget",
"link": true
},
"node_modules/hexabot-cli": { "node_modules/hexabot-cli": {
"resolved": "cli", "resolved": "cli",
"link": true "link": true
@ -5802,10 +6368,6 @@
"resolved": "frontend", "resolved": "frontend",
"link": true "link": true
}, },
"node_modules/hexabot-widget": {
"resolved": "widget",
"link": true
},
"node_modules/hoist-non-react-statics": { "node_modules/hoist-non-react-statics": {
"version": "3.3.2", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@ -8012,6 +8574,19 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/rollup/node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.22.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.5.tgz",
"integrity": "sha512-250ZGg4ipTL0TGvLlfACkIxS9+KLtIbn7BCZjsZj88zSg2Lvu3Xdw6dhAhfe/FjjXPVNCtcSp+WZjVsD3a/Zlw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/run-parallel": { "node_modules/run-parallel": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@ -9343,7 +9918,7 @@
} }
}, },
"widget": { "widget": {
"name": "hexabot-widget", "name": "hexabot-chat-widget",
"version": "2.0.0", "version": "2.0.0",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"dependencies": { "dependencies": {
@ -9369,6 +9944,10 @@
"typescript": "^5.2.2", "typescript": "^5.2.2",
"vite": "^5.3.4", "vite": "^5.3.4",
"vite-plugin-dts": "^4.0.2" "vite-plugin-dts": "^4.0.2"
},
"optionalDependencies": {
"@esbuild/darwin-arm64": "^0.24.0",
"@rollup/rollup-darwin-arm64": "^4.24.0"
} }
}, },
"widget/node_modules/@types/react": { "widget/node_modules/@types/react": {

17
widget/.npmignore Normal file
View File

@ -0,0 +1,17 @@
src
node_modules
build
public
tests
Dockerfile
.dockerignore
.eslintrc.json
index.html
package.json
tsconfig.app.json
tsconfig.json
tsconfig.node.json
vite.config.ts
dist/index.html
dist/shadow.html
vite.svg

View File

@ -1,6 +1,8 @@
# Hexabot Live Chat Widget # Hexabot Live Chat Widget
The [Hexabot](https://hexabot.ai/) Live Chat Widget is a React-based embeddable widget that allows users to integrate real-time chat functionality into their websites. It connects to the Hexabot API and facilitates seamless interaction between end-users and chatbots across multiple channels. The [Hexabot](https://hexabot.ai/) Live Chat Widget is a React-based embeddable widget that allows users to integrate real-time chat functionality into their websites. It connects to the Hexabot API and facilitates seamless interaction between end-users and chatbots across multiple channels.
[Hexabot](https://hexabot.ai/) is an open-source chatbot / agent solution that allows users to create and manage AI-powered, multi-channel, and multilingual chatbots with ease. If you would like to learn more, please visit the [official github repo](https://github.com/Hexastack/Hexabot/).
## Key Features ## Key Features
- **Real-Time Chat:** Engage in real-time conversations with users directly through your website. - **Real-Time Chat:** Engage in real-time conversations with users directly through your website.
- **Customizable:** Easily customize the widget's appearance and behavior to fit your brand and website. - **Customizable:** Easily customize the widget's appearance and behavior to fit your brand and website.
@ -68,6 +70,51 @@ Once the widget is built, you can easily embed it into any webpage. Here's an ex
``` ```
Replace the values in apiUrl and token with your configuration details. Replace the values in apiUrl and token with your configuration details.
To prevent the website css from conflicting with the chat widget css, we can leverage the shadow dom:
```js
<script crossorigin src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="<<WIDGET URL>>/hexabot-widget.umd.js"></script>
<div id="hb-chat-widget"></div>
<script>
// Create the shadow root and attach it to the widget container
const createElement = (tag, props = {}) => Object.assign(document.createElement(tag), props);
const shadowContainer = createElement("div");
document
.getElementById('hb-chat-widget')
.attachShadow({ mode: 'open' })
.append(
shadowContainer,
createElement("link", {
rel: "stylesheet",
href: "<<WIDGET URL>>/style.css"
});
);
// Render the widget inside the shadow root
ReactDOM.render(
React.createElement(HexabotWidget, {
apiUrl: 'https://api.yourdomain.com',
channel: 'offline',
token: 'token123',
}),
shadowContainer,
);
</script>
```
If you would like to use the official widget and benefit from updates automatically, you can consider using the cdn url:
`https://cdn.jsdelivr.net/npm/hexabot-chat-widget@2.0.4/dist/`
or lastest from major version:
`https://cdn.jsdelivr.net/npm/hexabot-chat-widget@2/dist/`
JsDelivr uses the package published in the NPM registry : https://www.npmjs.com/package/hexabot-widget
## Examples
As a proof of concept we developed a Wordpress plugin to embed the chat widget in a Wordpress website : [https://github.com/hexastack/hexabot-wordpress-live-chat-widget](https://github.com/hexastack/hexabot-wordpress-live-chat-widget)
## Customization ## Customization
You can customize the look and feel of the chat widget by modifying the widgets scss styles or behavior. The widget allows you to: You can customize the look and feel of the chat widget by modifying the widgets scss styles or behavior. The widget allows you to:

View File

@ -1,11 +1,12 @@
{ {
"name": "hexabot-widget", "name": "hexabot-chat-widget",
"private": true,
"version": "2.0.0", "version": "2.0.0",
"description": "Hexabot is a solution for creating and managing chatbots across multiple channels, leveraging AI for advanced conversational capabilities. It provides a user-friendly interface for building, training, and deploying chatbots with integrated support for various messaging platforms.", "description": "Hexabot is a solution for creating and managing chatbots across multiple channels, leveraging AI for advanced conversational capabilities. It provides a user-friendly interface for building, training, and deploying chatbots with integrated support for various messaging platforms.",
"author": "Hexastack", "author": "Hexastack",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"type": "module", "type": "module",
"main": "dist/hexabot-widget.umd.js",
"style": "dist/style.css",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "tsc && vite build", "build": "tsc && vite build",
@ -38,5 +39,9 @@
"typescript": "^5.2.2", "typescript": "^5.2.2",
"vite": "^5.3.4", "vite": "^5.3.4",
"vite-plugin-dts": "^4.0.2" "vite-plugin-dts": "^4.0.2"
},
"optionalDependencies": {
"@rollup/rollup-darwin-arm64": "^4.24.0",
"@esbuild/darwin-arm64": "^0.24.0"
} }
} }

View File

@ -18,15 +18,26 @@
<body> <body>
<div id="hb-chat-widget"></div> <div id="hb-chat-widget"></div>
<script> <script>
const el = React.createElement; // Create the shadow root and attach it to the widget container
const domContainer = document.getElementById('hb-chat-widget'); const createElement = (tag, props = {}) => Object.assign(document.createElement(tag), props);
const shadowContainer = createElement("div");
document
.getElementById('hb-chat-widget')
.attachShadow({ mode: 'open' })
.append(
shadowContainer,
createElement("link", {
rel: "stylesheet",
href: "./style.css"
})
);
ReactDOM.render( ReactDOM.render(
el(HexabotWidget, { React.createElement(HexabotWidget, {
apiUrl: 'http://localhost:4000', apiUrl: 'http://localhost:4000',
channel: 'offline', channel: 'offline',
token: 'token123', token: 'token123',
}), }),
domContainer, shadowContainer,
); );
</script> </script>
</body> </body>

View File

@ -15,7 +15,6 @@ import { useChat } from '../providers/ChatProvider';
import { useColors } from '../providers/ColorProvider'; import { useColors } from '../providers/ColorProvider';
import { useSocketLifecycle } from '../providers/SocketProvider'; import { useSocketLifecycle } from '../providers/SocketProvider';
import { useWidget, WidgetContextType } from '../providers/WidgetProvider'; import { useWidget, WidgetContextType } from '../providers/WidgetProvider';
import './Launcher.scss'; import './Launcher.scss';
type LauncherProps = PropsWithChildren<{ type LauncherProps = PropsWithChildren<{

View File

@ -8,7 +8,7 @@
border-bottom-left-radius: 10px; border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px; border-bottom-right-radius: 10px;
transition: background-color 0.2s ease, box-shadow 0.2s ease; transition: background-color 0.2s ease, box-shadow 0.2s ease;
border-top: 1px solid #eaeaea; border: 1px solid #eaeaea;
} }
.sc-user-input--text { .sc-user-input--text {

View File

@ -137,10 +137,7 @@ const UserInput: React.FC = () => {
const uploading = outgoingMessageState === OutgoingMessageState.uploading; const uploading = outgoingMessageState === OutgoingMessageState.uploading;
return ( return (
<div <div className="sc-user-input-wrapper">
className="sc-user-input-wrapper"
style={{ fill: colors.userInput.text }}
>
{suggestions.length > 0 && <Suggestions />} {suggestions.length > 0 && <Suggestions />}
{(file || uploading) && ( {(file || uploading) && (

View File

@ -6,7 +6,13 @@
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/ */
import React, { SyntheticEvent, useCallback, useEffect, useState } from 'react'; import React, {
SyntheticEvent,
useCallback,
useEffect,
useRef,
useState,
} from 'react';
import { useTranslation } from '../hooks/useTranslation'; import { useTranslation } from '../hooks/useTranslation';
import { useChat } from '../providers/ChatProvider'; import { useChat } from '../providers/ChatProvider';
@ -39,6 +45,7 @@ const UserSubscription: React.FC = () => {
} = useChat(); } = useChat();
const [firstName, setFirstName] = useState<string>(''); const [firstName, setFirstName] = useState<string>('');
const [lastName, setLastName] = useState<string>(''); const [lastName, setLastName] = useState<string>('');
const isInitialized = useRef(false);
const handleSubmit = useCallback( const handleSubmit = useCallback(
async (event?: React.FormEvent<HTMLFormElement>) => { async (event?: React.FormEvent<HTMLFormElement>) => {
event?.preventDefault(); event?.preventDefault();
@ -83,7 +90,7 @@ const UserSubscription: React.FC = () => {
data: { data: {
type: TOutgoingMessageType.postback, type: TOutgoingMessageType.postback,
data: { data: {
text: 'GET_STARTED', //TODO:use translation here? text: t('messages.get_started'),
payload: 'GET_STARTED', payload: 'GET_STARTED',
}, },
author: profile.foreign_id, author: profile.foreign_id,
@ -113,14 +120,18 @@ const UserSubscription: React.FC = () => {
); );
useEffect(() => { useEffect(() => {
const profile = localStorage.getItem('profile'); // User already subscribed ? (example : refreshed the page)
if (!isInitialized.current) {
isInitialized.current = true;
const profile = localStorage.getItem('profile');
if (profile) { if (profile) {
const parsedProfile = JSON.parse(profile); const parsedProfile = JSON.parse(profile);
setFirstName(parsedProfile.first_name); setFirstName(parsedProfile.first_name);
setLastName(parsedProfile.last_name); setLastName(parsedProfile.last_name);
handleSubmit(); handleSubmit();
}
} }
}, [handleSubmit, setScreen]); }, [handleSubmit, setScreen]);

View File

@ -5,6 +5,7 @@
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
outline: none; outline: none;
opacity: .5;
} }
.sc-user-input--emoji-icon-wrapper:focus { .sc-user-input--emoji-icon-wrapper:focus {
@ -12,18 +13,14 @@
} }
.sc-user-input--emoji-icon { .sc-user-input--emoji-icon {
width: 18px; width: 20px;
height: 18px; height: 20px;
cursor: pointer; cursor: pointer;
align-self: center; align-self: center;
vertical-align: middle; vertical-align: middle;
} }
.sc-user-input--emoji-icon-wrapper:focus .sc-user-input--emoji-icon path, .sc-user-input--emoji-icon-wrapper:focus,
.sc-user-input--emoji-icon-wrapper:focus .sc-user-input--emoji-icon circle, .sc-user-input--emoji-icon-wrapper:hover {
.sc-user-input--emoji-icon.active path, opacity: 1;
.sc-user-input--emoji-icon.active circle,
.sc-user-input--emoji-icon:hover path,
.sc-user-input--emoji-icon:hover circle {
filter: contrast(15%);
} }

View File

@ -1,6 +1,12 @@
.sc-user-input--file-wrapper { .sc-user-input--file-wrapper {
background: none;
border: none;
padding: 0px;
margin: 0px;
outline: none;
position: relative; position: relative;
cursor: pointer; cursor: pointer;
opacity: .5;
} }
.sc-user-input--file-icon-wrapper { .sc-user-input--file-icon-wrapper {
background: none; background: none;
@ -8,7 +14,7 @@
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
outline: none; outline: none;
cursor: pointer; cursor: pointer !important;
} }
.sc-user-input--file-icon { .sc-user-input--file-icon {
@ -19,6 +25,6 @@
vertical-align: middle; vertical-align: middle;
} }
.sc-user-input--file-icon:hover path { .sc-user-input--file-wrapper:hover {
filter: contrast(15%); opacity: 1
} }

View File

@ -4,6 +4,7 @@
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
outline: none; outline: none;
opacity: .5;
} }
.sc-user-input--location-icon-wrapper:focus { .sc-user-input--location-icon-wrapper:focus {
@ -11,20 +12,13 @@
} }
.sc-user-input--location-icon { .sc-user-input--location-icon {
width: 16px; width: 20px;
height: 16px; height: 20px;
cursor: pointer; cursor: pointer;
align-self: center; align-self: center;
vertical-align: middle; vertical-align: middle;
} }
.sc-user-input--location-icon-wrapper:focus .sc-user-input--location-icon path, .sc-user-input--location-icon-wrapper:hover {
.sc-user-input--location-icon-wrapper:focus opacity: 1;
.sc-user-input--location-icon
circle,
.sc-user-input--location-icon.active path,
.sc-user-input--location-icon.active circle,
.sc-user-input--location-icon:hover path,
.sc-user-input--location-icon:hover circle {
filter: contrast(15%);
} }

View File

@ -12,7 +12,7 @@ const EmojiIcon: FC<SVGProps<SVGSVGElement>> = ({
x = '0', x = '0',
y = '0', y = '0',
className = 'sc-user-input--emoji-icon', className = 'sc-user-input--emoji-icon',
viewBox = '0 0 37 37', viewBox = '0 0 24 24',
...rest ...rest
}) => { }) => {
return ( return (
@ -24,10 +24,11 @@ const EmojiIcon: FC<SVGProps<SVGSVGElement>> = ({
viewBox={viewBox} viewBox={viewBox}
{...rest} {...rest}
> >
<path d="M18.696 37.393C8.387 37.393 0 29.006 0 18.696 0 8.387 8.387 0 18.696 0c10.31 0 18.696 8.387 18.696 18.696.001 10.31-8.386 18.697-18.696 18.697zm0-35.393C9.49 2 2 9.49 2 18.696c0 9.206 7.49 16.696 16.696 16.696 9.206 0 16.696-7.49 16.696-16.696C35.393 9.49 27.902 2 18.696 2z" /> <circle cx="15.5" cy="9.5" r="1.5" />
<circle cx="12.379" cy="14.359" r="1.938" /> <circle cx="8.5" cy="9.5" r="1.5" />
<circle cx="24.371" cy="14.414" r="1.992" /> <circle cx="15.5" cy="9.5" r="1.5" />
<path d="M18.035 27.453c-5.748 0-8.342-4.18-8.449-4.357a1 1 0 011.71-1.038c.094.151 2.161 3.396 6.74 3.396 4.713 0 7.518-3.462 7.545-3.497a1 1 0 011.566 1.244c-.138.173-3.444 4.252-9.112 4.252z" /> <circle cx="8.5" cy="9.5" r="1.5" />
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2M12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8m0-2.5c2.33 0 4.32-1.45 5.12-3.5h-1.67c-.69 1.19-1.97 2-3.45 2s-2.75-.81-3.45-2H6.88c.8 2.05 2.79 3.5 5.12 3.5" />
</svg> </svg>
); );
}; };

View File

@ -12,7 +12,7 @@ const FileInputIcon: FC<SVGProps<SVGSVGElement>> = ({
x = '0', x = '0',
y = '0', y = '0',
className = 'sc-user-input--file-icon', className = 'sc-user-input--file-icon',
viewBox = '0 0 32 32', viewBox = '0 0 24 24',
...rest ...rest
}) => { }) => {
return ( return (
@ -24,10 +24,7 @@ const FileInputIcon: FC<SVGProps<SVGSVGElement>> = ({
viewBox={viewBox} viewBox={viewBox}
{...rest} {...rest}
> >
<path <path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6z" />{' '}
fill="currentColor"
d="M20.807 10.22l-2.03-2.029-10.15 10.148c-1.682 1.681-1.682 4.408 0 6.089s4.408 1.681 6.09 0l12.18-12.178a7.173 7.173 0 000-10.148 7.176 7.176 0 00-10.149 0L3.96 14.889l-.027.026c-3.909 3.909-3.909 10.245 0 14.153 3.908 3.908 10.246 3.908 14.156 0l.026-.027.001.001 8.729-8.728-2.031-2.029-8.729 8.727-.026.026a7.148 7.148 0 01-10.096 0 7.144 7.144 0 010-10.093l.028-.026-.001-.002L18.78 4.131c1.678-1.679 4.411-1.679 6.09 0s1.678 4.411 0 6.089L12.69 22.398c-.56.56-1.47.56-2.03 0a1.437 1.437 0 010-2.029L20.81 10.22z"
/>
</svg> </svg>
); );
}; };

View File

@ -13,7 +13,7 @@ const LocationIcon: FC<SVGProps<SVGSVGElement>> = ({
y = '0', y = '0',
className = 'sc-user-input--location-icon', className = 'sc-user-input--location-icon',
version = '1.1', version = '1.1',
viewBox = '0 0 32 32', viewBox = '0 0 24 24',
...rest ...rest
}) => { }) => {
return ( return (
@ -26,8 +26,7 @@ const LocationIcon: FC<SVGProps<SVGSVGElement>> = ({
viewBox={viewBox} viewBox={viewBox}
{...rest} {...rest}
> >
<path d="M16.002 17.746c3.309 0 6-2.692 6-6s-2.691-6-6-6-6 2.691-6 6 2.691 6 6 6zm0-11c2.758 0 5 2.242 5 5s-2.242 5-5 5-5-2.242-5-5 2.242-5 5-5z" /> <path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7m0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5" />
<path d="M16 0C9.382 0 4 5.316 4 12.001c0 7 6.001 14.161 10.376 19.194.016.02.718.805 1.586.805h.077c.867 0 1.57-.785 1.586-.805 4.377-5.033 10.377-12.193 10.377-19.194A11.971 11.971 0 0016 0zm.117 29.883c-.021.02-.082.064-.135.098-.01-.027-.084-.086-.129-.133C12.188 25.631 6 18.514 6 12.001 6 6.487 10.487 2 16 2c5.516 0 10.002 4.487 10.002 10.002 0 6.512-6.188 13.629-9.885 17.881z" />
</svg> </svg>
); );
}; };

View File

@ -18,7 +18,7 @@
margin: 2px; margin: 2px;
cursor: pointer; cursor: pointer;
outline: 0; outline: 0;
font-size: 1rem; font-size: .75rem;
} }
} }

View File

@ -11,34 +11,34 @@ import { ColorState } from '../types/colors.types';
const colors: Record<string, ColorState> = { const colors: Record<string, ColorState> = {
orange: { orange: {
header: { header: {
bg: '#E6A23D', bg: '#FF851B',
text: '#fff', text: '#fff',
}, },
launcher: { launcher: {
bg: '#E6A23D', bg: '#FF851B',
}, },
messageList: { messageList: {
bg: '#fff', bg: '#fff',
}, },
sent: { sent: {
bg: '#E6A23D', bg: '#FF851B',
text: '#fff', text: '#fff',
}, },
received: { received: {
bg: '#eaeaea', bg: '#eaeaea',
text: '#222222', text: '#000',
}, },
userInput: { userInput: {
bg: '#fff', bg: '#fbfbfb',
text: '#212121', text: '#000',
}, },
button: { button: {
bg: '#ffffff', bg: '#ffffff',
text: '#E6A23D', text: '#FF851B',
border: '#E6A23D', border: '#FF851B',
}, },
messageStatus: { messageStatus: {
bg: '#E6A23D', bg: '#FF851B',
}, },
messageTime: { messageTime: {
text: '#9C9C9C', text: '#9C9C9C',
@ -46,139 +46,139 @@ const colors: Record<string, ColorState> = {
}, },
red: { red: {
header: { header: {
bg: '#AB1251', bg: '#FF4136',
text: '#fff', text: '#fff',
}, },
launcher: { launcher: {
bg: '#AB1251', bg: '#FF4136',
}, },
messageList: { messageList: {
bg: '#fff', bg: '#fff',
}, },
sent: { sent: {
bg: '#AB1251', bg: '#FF4136',
text: '#fff', text: '#fff',
}, },
received: { received: {
bg: '#eaeaea', bg: '#eaeaea',
text: '#222222', text: '#000',
}, },
userInput: { userInput: {
bg: '#fff', bg: '#fbfbfb',
text: '#212121', text: '#000',
}, },
button: { button: {
bg: '#ffffff', bg: '#ffffff',
text: '#AB1251', text: '#FF4136',
border: '#AB1251', border: '#FF4136',
}, },
messageStatus: { messageStatus: {
bg: '#AB1251', bg: '#FF4136',
}, },
messageTime: { messageTime: {
text: '#9C9C9C', text: '#FF4136',
}, },
}, },
green: { green: {
header: { header: {
bg: '#ABBD49', bg: '#2ECC40',
text: '#fff', text: '#fff',
}, },
launcher: { launcher: {
bg: '#ABBD49', bg: '#2ECC40',
}, },
messageList: { messageList: {
bg: '#fff', bg: '#fff',
}, },
sent: { sent: {
bg: '#4CAF50', bg: '#2ECC40',
text: '#fff', text: '#fff',
}, },
received: { received: {
bg: '#eaeaea', bg: '#eaeaea',
text: '#222222', text: '#000',
}, },
userInput: { userInput: {
bg: '#fff', bg: '#fbfbfb',
text: '#212121', text: '#000',
}, },
button: { button: {
bg: '#ffffff', bg: '#ffffff',
text: '#ABBD49', text: '#2ECC40',
border: '#ABBD49', border: '#2ECC40',
}, },
messageStatus: { messageStatus: {
bg: '#ABBD49', bg: '#2ECC40',
}, },
messageTime: { messageTime: {
text: '#9C9C9C', text: '#2ECC40',
}, },
}, },
blue: { blue: {
header: { header: {
bg: '#108AA8', bg: '#0074D9',
text: '#ffffff', text: '#fff',
}, },
launcher: { launcher: {
bg: '#108AA8', bg: '#0074D9',
}, },
messageList: { messageList: {
bg: '#ffffff', bg: '#fff',
}, },
sent: { sent: {
bg: '#108AA8', bg: '#0074D9',
text: '#ffffff', text: '#fff',
}, },
received: { received: {
bg: '#eaeaea', bg: '#eaeaea',
text: '#222222', text: '#000',
}, },
userInput: { userInput: {
bg: '#f4f7f9', bg: '#fbfbfb',
text: '#565867', text: '#000',
}, },
button: { button: {
bg: '#ffffff', bg: '#ffffff',
text: '#108AA8', text: '#0074D9',
border: '#108AA8', border: '#0074D9',
}, },
messageStatus: { messageStatus: {
bg: '#108AA8', bg: '#0074D9',
}, },
messageTime: { messageTime: {
text: '#9C9C9C', text: '#0074D9',
}, },
}, },
teal: { teal: {
header: { header: {
bg: '#279084', bg: '#1BA089',
text: '#ffffff', text: '#fff',
}, },
launcher: { launcher: {
bg: '#279084', bg: '#1BA089',
}, },
messageList: { messageList: {
bg: '#ffffff', bg: '#fff',
}, },
sent: { sent: {
bg: '#279084', bg: '#1BA089',
text: '#ffffff', text: '#fff',
}, },
received: { received: {
bg: '#eaeaea', bg: '#eaeaea',
text: '#222222', text: '#000',
}, },
userInput: { userInput: {
bg: '#f4f7f9', bg: '#fbfbfb',
text: '#565867', text: '#000',
}, },
button: { button: {
bg: '#ffffff', bg: '#ffffff',
text: '#279084', text: '#1BA089',
border: '#279084', border: '#1BA089',
}, },
messageStatus: { messageStatus: {
bg: '#279084', bg: '#1BA089',
}, },
messageTime: { messageTime: {
text: '#9C9C9C', text: '#9C9C9C',
@ -186,26 +186,26 @@ const colors: Record<string, ColorState> = {
}, },
dark: { dark: {
header: { header: {
bg: '#34495e', bg: '#000',
text: '#ecf0f1', text: '#ecf0f1',
}, },
launcher: { launcher: {
bg: '#34495e', bg: '#000',
}, },
messageList: { messageList: {
bg: '#2c3e50', bg: '#FFF',
}, },
sent: { sent: {
bg: '#7f8c8d', bg: '#000',
text: '#ecf0f1', text: '#FFF',
}, },
received: { received: {
bg: '#95a5a6', bg: '#F0F0F0',
text: '#ecf0f1', text: '#000',
}, },
userInput: { userInput: {
bg: '#34495e', bg: '#FFF',
text: '#ecf0f1', text: '#000',
}, },
button: { button: {
bg: '#2c3e50', bg: '#2c3e50',

View File

@ -241,15 +241,14 @@ const ChatProvider: React.FC<{
) { ) {
if ('author' in newIOMessage) { if ('author' in newIOMessage) {
newIOMessage.direction = newIOMessage.direction =
newIOMessage.author === participants[1].foreign_id || newIOMessage.author === 'chatbot'
newIOMessage.author === participants[1].id ? Direction.received
? Direction.sent : Direction.sent;
: Direction.received;
newIOMessage.read = true; newIOMessage.read = true;
newIOMessage.delivery = true; newIOMessage.delivery = true;
} }
messages.push(newIOMessage as TMessage); setMessages([...messages, newIOMessage as TMessage]);
setScroll(0); setScroll(0);
} }
@ -310,30 +309,29 @@ const ChatProvider: React.FC<{
}>( }>(
`/webhook/${config.channel}/?verification_token=${config.token}&first_name=${firstName}&last_name=${lastName}`, `/webhook/${config.channel}/?verification_token=${config.token}&first_name=${firstName}&last_name=${lastName}`,
); );
const { messages, profile } = body;
localStorage.setItem('profile', JSON.stringify(profile)); localStorage.setItem('profile', JSON.stringify(body.profile));
// @TODO : condition mix on id VS foreign_id setMessages(
messages.forEach((message) => { body.messages.map((message) => {
const direction = return {
message.author === profile.foreign_id || ...message,
message.author === profile.id direction:
? Direction.sent message.author === body.profile.foreign_id ||
: Direction.received; message.author === body.profile.id
? Direction.sent
message.direction = direction; : Direction.received,
if (message.direction === Direction.sent) { read: message.direction === Direction.sent || message.read,
message.read = true; delivery:
message.delivery = false; message.direction === Direction.sent || message.delivery,
} } as TMessage;
}); }),
setMessages(messages); );
setParticipants([ setParticipants([
...participants, ...participants,
{ {
id: profile.foreign_id, id: body.profile.foreign_id,
foreign_id: profile.foreign_id, foreign_id: body.profile.foreign_id,
name: `${profile.first_name} ${profile.last_name}`, name: `${body.profile.first_name} ${body.profile.last_name}`,
}, },
]); ]);
setConnectionState(3); setConnectionState(3);

View File

@ -12,43 +12,42 @@ import {
useContext, useContext,
useEffect, useEffect,
useRef, useRef,
useState,
} from 'react'; } from 'react';
import { useConfig } from './ConfigProvider'; import { useConfig } from './ConfigProvider';
import { builSocketIoClient, SocketIoClient } from '../utils/SocketIoClient'; import { getSocketIoClient, SocketIoClient } from '../utils/SocketIoClient';
interface socketContext { interface socketContext {
socket: SocketIoClient; socket: SocketIoClient;
connected: boolean;
} }
const socketContext = createContext<socketContext>({ const socketContext = createContext<socketContext>({
socket: {} as SocketIoClient, socket: {} as SocketIoClient,
connected: false,
}); });
export const SocketProvider = (props: PropsWithChildren) => { export const SocketProvider = (props: PropsWithChildren) => {
const config = useConfig(); const config = useConfig();
const socketRef = useRef(builSocketIoClient(config)); const socketRef = useRef(
const [connected, setConnected] = useState(false); getSocketIoClient(config, {
useEffect(() => {
socketRef.current.init({
onConnect: () => { onConnect: () => {
setConnected(true); // eslint-disable-next-line no-console
console.info(
'Hexabot Live Chat : Successfully established WS Connection!',
);
}, },
onConnectError: () => { onConnectError: () => {
setConnected(false); // eslint-disable-next-line no-console
console.error('Hexabot Live Chat : Failed to establish WS Connection!');
}, },
onDisconnect: () => { onDisconnect: () => {
setConnected(false); // eslint-disable-next-line no-console
console.info('Hexabot Live Chat : Disconnected WS.');
}, },
}); }),
}, []); );
return ( return (
<socketContext.Provider value={{ socket: socketRef.current, connected }}> <socketContext.Provider value={{ socket: socketRef.current }}>
{props.children} {props.children}
</socketContext.Provider> </socketContext.Provider>
); );
@ -58,12 +57,6 @@ export const useSocket = () => {
return useContext(socketContext); return useContext(socketContext);
}; };
export const useSocketConnected = () => {
const { connected } = useSocket();
return connected;
};
export const useSubscribe = <T,>(event: string, callback: (arg: T) => void) => { export const useSubscribe = <T,>(event: string, callback: (arg: T) => void) => {
const { socket } = useSocket(); const { socket } = useSocket();
@ -83,5 +76,6 @@ export const useSocketLifecycle = () => {
return () => { return () => {
socket.disconnect(); socket.disconnect();
}; };
}, [socket]); // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}; };

View File

@ -11,6 +11,7 @@
"back": "Back" "back": "Back"
}, },
"messages": { "messages": {
"get_started": "Get Started",
"file_message": { "file_message": {
"browser_audio_unsupport": "Browser does not support the audio element.", "browser_audio_unsupport": "Browser does not support the audio element.",
"browser_video_unsupport": "Browser does not support the video element.", "browser_video_unsupport": "Browser does not support the video element.",

View File

@ -11,6 +11,7 @@
"back": "Retour" "back": "Retour"
}, },
"messages": { "messages": {
"get_started": "Démarrer",
"file_message": { "file_message": {
"browser_audio_unsupport": "Le navigateur ne prend pas en charge l'élément audio.", "browser_audio_unsupport": "Le navigateur ne prend pas en charge l'élément audio.",
"browser_video_unsupport": "Le navigateur ne prend pas en charge l'élément vidéo.", "browser_video_unsupport": "Le navigateur ne prend pas en charge l'élément vidéo.",

View File

@ -16,6 +16,12 @@ import {
type SocketIoClientConfig = Partial<ManagerOptions & SocketOptions>; type SocketIoClientConfig = Partial<ManagerOptions & SocketOptions>;
type SocketIoEventHandlers = {
onConnect?: () => void;
onDisconnect?: (reason: string, details: unknown) => void;
onConnectError?: (error: Error) => void;
};
export class SocketIoClient { export class SocketIoClient {
/** /**
* Default configuration for the socket client * Default configuration for the socket client
@ -50,9 +56,11 @@ export class SocketIoClient {
private config: SocketIoClientConfig; private config: SocketIoClientConfig;
private initialized: boolean = false; constructor(
apiUrl: string,
constructor(apiUrl: string, socketConfig?: SocketIoClientConfig) { socketConfig: SocketIoClientConfig,
handlers: SocketIoEventHandlers,
) {
this.config = { this.config = {
...SocketIoClient.defaultConfig, ...SocketIoClient.defaultConfig,
...socketConfig, ...socketConfig,
@ -61,6 +69,7 @@ export class SocketIoClient {
const url = new URL(apiUrl); const url = new URL(apiUrl);
this.socket = io(url.origin, this.config); this.socket = io(url.origin, this.config);
this.init(handlers);
} }
/** /**
@ -71,16 +80,10 @@ export class SocketIoClient {
onConnect, onConnect,
onDisconnect, onDisconnect,
onConnectError, onConnectError,
}: { }: SocketIoEventHandlers) {
onConnect?: () => void;
onDisconnect?: (reason: string, details: unknown) => void;
onConnectError?: (error: Error) => void;
}) {
if (!this.initialized) this.socket.connect();
onConnect && this.uniqueOn('connect', onConnect); onConnect && this.uniqueOn('connect', onConnect);
onDisconnect && this.uniqueOn('disconnect', onDisconnect); onDisconnect && this.uniqueOn('disconnect', onDisconnect);
onConnectError && this.uniqueOn('connect_error', onConnectError); onConnectError && this.uniqueOn('connect_error', onConnectError);
this.initialized = true;
} }
/** /**
@ -100,7 +103,6 @@ export class SocketIoClient {
*/ */
public disconnect() { public disconnect() {
this.socket.disconnect(); this.socket.disconnect();
this.initialized = false;
} }
/** /**
@ -184,10 +186,28 @@ export class SocketIoClient {
} }
} }
export const builSocketIoClient = (config: Config) => let socketIoClient: SocketIoClient;
new SocketIoClient(config.apiUrl, {
query: { /**
channel: config.channel, * Returns a singleton instance of the socket io client
verification_token: config.token, *
}, * @param config The socket connection config
}); * @param handlers Event handlers
* @returns Socket io client instance
*/
export const getSocketIoClient = (config: Config, handlers: SocketIoEventHandlers) => {
if (!socketIoClient) {
socketIoClient = new SocketIoClient(
config.apiUrl,
{
query: {
channel: config.channel,
verification_token: config.token,
},
},
handlers,
);
}
return socketIoClient;
};

View File

@ -5,28 +5,31 @@ import { defineConfig } from "vite";
import dts from "vite-plugin-dts"; import dts from "vite-plugin-dts";
export default defineConfig({ export default defineConfig(({ mode }) => {
plugins: [react(), dts()], return {
server: { plugins: [react(), dts()],
host: "0.0.0.0", server: {
}, host: '0.0.0.0',
define: {
"process.env": process.env,
},
build: {
lib: {
entry: resolve(__dirname, "src/ChatWidget.tsx"),
name: "HexabotWidget",
fileName: (format) => `hexabot-widget.${format}.js`,
}, },
rollupOptions: { define: {
external: ["react", "react-dom"], 'process.env':
output: { mode === 'development' ? { 'process.env': process.env } : {},
globals: { },
react: "React", build: {
"react-dom": "ReactDOM", lib: {
entry: resolve(__dirname, 'src/ChatWidget.tsx'),
name: 'HexabotWidget',
fileName: (format) => `hexabot-widget.${format}.js`,
},
rollupOptions: {
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
}, },
}, },
}, },
}, };
}); });