Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve chatbot integration + css fixes #2872

Merged
merged 7 commits into from
Dec 4, 2024
1 change: 1 addition & 0 deletions apps/backoffice-v2/src/common/env/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ export const EnvSchema = z.object({
return new RegExp(value);
}, z.custom<RegExp>(value => value instanceof RegExp).optional()),
VITE_SAOLA_API_KEY: z.string().optional(),
VITE_BOTPRESS_CLIENT_ID: z.string().default('8f29c89d-ec0e-494d-b18d-6c3590b28be6'),
tomer-shvadron marked this conversation as resolved.
Show resolved Hide resolved
});
75 changes: 65 additions & 10 deletions apps/backoffice-v2/src/domains/chat/chatbot-opengpt.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,93 @@
import { getClient, Webchat, WebchatProvider } from '@botpress/webchat';
import { getClient, Webchat, WebchatProvider, WebchatClient } from '@botpress/webchat';
import { buildTheme } from '@botpress/webchat-generator';
import { useEffect } from 'react';
import { useCallback, useEffect } from 'react';
import { useAuthenticatedUserQuery } from '../../domains/auth/hooks/queries/useAuthenticatedUserQuery/useAuthenticatedUserQuery';
import { useCurrentCaseQuery } from '../../pages/Entity/hooks/useCurrentCaseQuery/useCurrentCaseQuery';
import { useLocation } from 'react-router-dom';

// declare const themeNames: readonly ["prism", "galaxy", "dusk", "eggplant", "dawn", "midnight"];
const { theme, style } = buildTheme({
themeName: 'galaxy',
themeColor: 'blue',
});

const clientId = '8f29c89d-ec0e-494d-b18d-6c3590b28be6';

const Chatbot = ({
isWebchatOpen,
toggleIsWebchatOpen,
client,
shanegrouber marked this conversation as resolved.
Show resolved Hide resolved
setClient,
chatbotClientId,
}: {
isWebchatOpen: boolean;
toggleIsWebchatOpen: () => void;
client: WebchatClient | null;
setClient: (client: WebchatClient) => void;
chatbotClientId: string;
}) => {
const client = getClient({ clientId });
const { data: session } = useAuthenticatedUserQuery();
const { data: currentCase } = useCurrentCaseQuery();
const { pathname } = useLocation();
shanegrouber marked this conversation as resolved.
Show resolved Hide resolved

const sendCaseData = useCallback(
async (caseId: string, newClient?: WebchatClient) => {
if (!currentCase) return;

const clientToUse = newClient || client;
shanegrouber marked this conversation as resolved.
Show resolved Hide resolved

try {
const currentCaseData = {
...currentCase.context,
caseId,
};
await clientToUse?.sendEvent({
type: 'case-data',
data: currentCaseData,
shanegrouber marked this conversation as resolved.
Show resolved Hide resolved
});
} catch (error) {
console.error('Failed to send case data:', error);
}
},
[currentCase, client],
);

useEffect(() => {
if (session?.user) {
const { firstName, lastName, email } = session.user;
void client.updateUser({
if (client || !chatbotClientId || !session?.user) return;

const { firstName, lastName, email } = session.user;
const newClient = getClient({ clientId: chatbotClientId });
setClient(newClient);

// newClient.on('*', (ev: any) => {
shanegrouber marked this conversation as resolved.
Show resolved Hide resolved
// console.log('Event: ', ev);
// });

newClient.on('conversation', (ev: any) => {
// new conversation created
void newClient.updateUser({
data: {
firstName,
lastName,
email,
},
});
}
}, [session, client]);
setTimeout(() => {
const caseId = pathname.split('/')[pathname.split('/').length - 1];
void sendCaseData(caseId || '', newClient);
}, 500);
shanegrouber marked this conversation as resolved.
Show resolved Hide resolved
});
}, [session, client, setClient, sendCaseData, pathname, chatbotClientId]);

useEffect(() => {
console.log('pathname changed to: ', pathname);
shanegrouber marked this conversation as resolved.
Show resolved Hide resolved

const caseId = pathname.split('/')[pathname.split('/').length - 1];

if (caseId) void sendCaseData(caseId);
shanegrouber marked this conversation as resolved.
Show resolved Hide resolved
}, [pathname, sendCaseData]);

if (!client) {
return null;
}

return (
<div>
Expand Down
2 changes: 1 addition & 1 deletion apps/backoffice-v2/src/domains/customer/fetchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const CustomerSchema = z.object({
language: z.union([z.string(), z.null()]).optional(),
features: z
.object({
chatbot: z.object({ isEnabled: z.boolean().default(false), clientId: z.string().optional() }),
tomer-shvadron marked this conversation as resolved.
Show resolved Hide resolved
createBusinessReport: z
.object({ enabled: z.boolean().default(false), options: createBusinessReportOptions })
.optional(),
Expand All @@ -35,7 +36,6 @@ const CustomerSchema = z.object({
isMerchantMonitoringEnabled: z.boolean().default(false),
isExample: z.boolean().default(false),
isDemo: z.boolean().default(false),
isChatbotEnabled: z.boolean().default(false),
})
.nullable()
.default({
Expand Down
26 changes: 24 additions & 2 deletions apps/backoffice-v2/src/pages/Root/Root.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { ServerDownLayout } from './ServerDown.layout';
import { useCustomerQuery } from '@/domains/customer/hooks/queries/useCustomerQuery/useCustomerQuery';
import { FullScreenLoader } from '@/common/components/molecules/FullScreenLoader/FullScreenLoader';
import Chatbot from '@/domains/chat/chatbot-opengpt';
import { WebchatClient } from '@botpress/webchat';
import { RenderChildrenInIFrame } from '@/common/components/organisms/RenderChildrenInIFrame/RenderChildrenInIFrame';
import { ctw } from '@/common/utils/ctw/ctw';
import { env } from '@/common/env/env';

const ReactQueryDevtools = lazy(() =>
process.env.NODE_ENV !== 'production'
Expand All @@ -16,6 +20,7 @@ const ReactQueryDevtools = lazy(() =>

const ChatbotLayout: FunctionComponent = () => {
const { data: customer, isLoading: isLoadingCustomer } = useCustomerQuery();
const [client, setClient] = useState<WebchatClient | null>(null);
const [isWebchatOpen, setIsWebchatOpen] = useState(false);
const toggleIsWebchatOpen = () => {
setIsWebchatOpen(prevState => !prevState);
Expand All @@ -25,11 +30,28 @@ const ChatbotLayout: FunctionComponent = () => {
return <FullScreenLoader />;
}

if (!customer?.config?.isChatbotEnabled) {
if (!customer?.features?.chatbot?.isEnabled) {
tomer-shvadron marked this conversation as resolved.
Show resolved Hide resolved
return null;
}

return <Chatbot isWebchatOpen={isWebchatOpen} toggleIsWebchatOpen={toggleIsWebchatOpen} />;
const chatbotClientId = customer?.features?.chatbot?.clientId || env.VITE_BOTPRESS_CLIENT_ID;

return (
<RenderChildrenInIFrame
className={ctw('fixed bottom-right-0', {
'h-[700px] w-[400px]': isWebchatOpen,
'd-[80px]': !isWebchatOpen,
})}
tomer-shvadron marked this conversation as resolved.
Show resolved Hide resolved
>
<Chatbot
isWebchatOpen={isWebchatOpen}
toggleIsWebchatOpen={toggleIsWebchatOpen}
client={client}
setClient={setClient}
chatbotClientId={chatbotClientId}
/>
</RenderChildrenInIFrame>
);
};

export const Root: FunctionComponent = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ export const WorkflowConfigSchema = Type.Object({
hasUboOngoingMonitoring: Type.Optional(Type.Boolean()),
maxBusinessReports: Type.Optional(Type.Number()),
isMerchantMonitoringEnabled: Type.Optional(Type.Boolean()),
isChatbotEnabled: Type.Optional(Type.Boolean()),
});

export type TWorkflowConfig = Static<typeof WorkflowConfigSchema>;
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export const ConfigSchema = z
hasUboOngoingMonitoring: z.boolean().optional(),
maxBusinessReports: z.number().nonnegative().optional(),
isMerchantMonitoringEnabled: z.boolean().optional(),
isChatbotEnabled: z.boolean().optional(),
uiOptions: z
.object({
redirectUrls: z
Expand Down