+7








225313e391
* plain custom desin * No display on card * dynamic plain chat component and hmac hash * re-route users to plain.com chat instead of intercom chat when going to /support * provider errors * yarn lock fix * plain chat removed unneeded hmac * fix ts error * error handling improved * remove intercome provider from app-dir * Create getting-started.mdx (#18342) * Delete help directory (#18343) * chore: moved docs/help to /help (#18345) * chore: Delete unused guides directory (#18346) * feat: booking filters (#18303) Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> * chore: Remove `HeadSeo` components where no longer needed + improve app router metadata logic (#18348) * remove HeadSeo for already migrated pages and refactor prepareMetadata * create _generateMetadataWithoutImage and refactor _generateMetadata * chore: app router - /bookings status page (#18183) * chore: app router - /bookings page * remove env vars * fix * Update middleware.ts * revert unneeded change * refactor for the better * fix * cache i18n instances (#18309) * feat: virtual queues tab in insights (#18260) Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: sean-brydon <sean@cal.com> Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: Omar López <zomars@me.com> * feat: update translations via @replexica (#18361) Co-authored-by: Replexica <support@replexica.com> * update OOO e2e tests to remove flakiness (#18367) * fix: metadata is overwritten for child managed eventType when updating parent (#18059) * fix: metadata is overwirten * Update * type error * Update * fix test * fix type error * chore: added routing support link (#18369) * Added routing form support link * small change * Type fix --------- Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com> * chore: refactor handling logic for embeds in app router (#18362) * refactor handling logic for embeds in app router * fix type checks * add test for withEmbedSsrAppDir * fix * review changes * yarn lock --------- Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: Calcom Bot <109866826+calcom-bot@users.noreply.github.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> Co-authored-by: Benny Joo <sldisek783@gmail.com> Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: sean-brydon <sean@cal.com> Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Co-authored-by: Omar López <zomars@me.com> Co-authored-by: Replexica <support@replexica.com> Co-authored-by: Vijay <vijayraghav22@gmail.com> Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com> Co-authored-by: Alex van Andel <me@alexvanandel.com>
248 lines
6.5 KiB
TypeScript
248 lines
6.5 KiB
TypeScript
"use client";
|
|
|
|
import { useSession } from "next-auth/react";
|
|
import { usePathname, useSearchParams } from "next/navigation";
|
|
import Script from "next/script";
|
|
import { useEffect, useState } from "react";
|
|
|
|
declare global {
|
|
interface Window {
|
|
Plain?: {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
init: (config: any) => void;
|
|
open: () => void;
|
|
};
|
|
plainScriptLoaded?: () => void;
|
|
__PLAIN_CONFIG__?: PlainChatConfig;
|
|
}
|
|
}
|
|
|
|
interface PlainChatConfig {
|
|
appId: string;
|
|
customerDetails: {
|
|
email: string;
|
|
emailHash: string;
|
|
fullName: string;
|
|
shortName: string;
|
|
chatAvatarUrl: string;
|
|
};
|
|
links: Array<{
|
|
icon: string;
|
|
text: string;
|
|
url: string;
|
|
}>;
|
|
chatButtons: Array<{
|
|
icon: string;
|
|
text: string;
|
|
type: string;
|
|
form?: {
|
|
fields: Array<{
|
|
type: string;
|
|
placeholder: string;
|
|
options: Array<{
|
|
icon: string;
|
|
text: string;
|
|
threadDetails: {
|
|
severity: string;
|
|
labelTypeIds: Array<string>;
|
|
issueType: string;
|
|
priority: string;
|
|
};
|
|
}>;
|
|
}>;
|
|
};
|
|
}>;
|
|
entryPoint: {
|
|
type: string;
|
|
};
|
|
hideBranding: boolean;
|
|
theme: string;
|
|
style: {
|
|
brandColor: string;
|
|
launcherBackgroundColor: string;
|
|
launcherIconColor: string;
|
|
};
|
|
position: {
|
|
bottom: string;
|
|
right: string;
|
|
};
|
|
}
|
|
|
|
const PlainChat = () => {
|
|
const [config, setConfig] = useState<PlainChatConfig | null>(null);
|
|
const { data: session } = useSession();
|
|
const pathname = usePathname();
|
|
const searchParams = useSearchParams();
|
|
|
|
useEffect(() => {
|
|
const initConfig = async () => {
|
|
if (!session?.user?.email) return;
|
|
|
|
try {
|
|
const response = await fetch("/api/plain-hash", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
throw new Error(`Failed to generate hash: ${errorText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
if (!data.hash || !data.email || !data.appId) {
|
|
throw new Error("Missing required fields in API response");
|
|
}
|
|
|
|
const plainChatConfig: PlainChatConfig = {
|
|
appId: data.appId,
|
|
customerDetails: {
|
|
email: data.email,
|
|
shortName: data.shortName,
|
|
fullName: data.fullName,
|
|
emailHash: data.hash,
|
|
chatAvatarUrl: data.chatAvatarUrl,
|
|
},
|
|
links: [
|
|
{
|
|
icon: "book",
|
|
text: "Documentation",
|
|
url: "https://cal.com/docs",
|
|
},
|
|
{
|
|
icon: "chat",
|
|
text: "Ask the community",
|
|
url: "https://github.com/calcom/cal.com/discussions",
|
|
},
|
|
],
|
|
chatButtons: [
|
|
{
|
|
icon: "chat",
|
|
text: "Ask a question",
|
|
type: "primary",
|
|
},
|
|
{
|
|
icon: "bulb",
|
|
text: "Send feedback",
|
|
type: "default",
|
|
},
|
|
{
|
|
icon: "error",
|
|
text: "Report an issue",
|
|
type: "default",
|
|
form: {
|
|
fields: [
|
|
{
|
|
type: "dropdown",
|
|
placeholder: "Select severity...",
|
|
options: [
|
|
{
|
|
icon: "support",
|
|
text: "I'm unable to use the app",
|
|
threadDetails: {
|
|
severity: "critical",
|
|
issueType: "critical",
|
|
labelTypeIds: ["lt_01JFJWNWAC464N8DZ6YE71YJRF"],
|
|
priority: "u",
|
|
},
|
|
},
|
|
{
|
|
icon: "error",
|
|
text: "Major functionality degraded",
|
|
threadDetails: {
|
|
severity: "major",
|
|
issueType: "major",
|
|
labelTypeIds: ["lt_01JFJWP3KECF1YQES6XF212RFW"],
|
|
priority: "h",
|
|
},
|
|
},
|
|
{
|
|
icon: "bug",
|
|
text: "Minor annoyance",
|
|
threadDetails: {
|
|
severity: "minor",
|
|
issueType: "minor",
|
|
labelTypeIds: ["lt_01JFJWPC8ADW0PK28JHMJR6NSS"],
|
|
priority: "l",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
entryPoint: {
|
|
type: "chat",
|
|
},
|
|
hideBranding: true,
|
|
theme: "auto",
|
|
style: {
|
|
brandColor: "#FFFFFF",
|
|
launcherBackgroundColor: "#262626",
|
|
launcherIconColor: "#FFFFFF",
|
|
},
|
|
position: {
|
|
bottom: "20px",
|
|
right: "20px",
|
|
},
|
|
};
|
|
|
|
if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") {
|
|
window.__PLAIN_CONFIG__ = plainChatConfig;
|
|
}
|
|
|
|
setConfig(plainChatConfig);
|
|
|
|
if (pathname === "/event-types" && searchParams?.has("openPlain")) {
|
|
const timer = setTimeout(() => {
|
|
if (window.Plain) {
|
|
window.Plain.open();
|
|
}
|
|
}, 100);
|
|
return () => clearTimeout(timer);
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to initialize Plain Chat:", error);
|
|
}
|
|
};
|
|
|
|
initConfig();
|
|
}, [session, pathname, searchParams]);
|
|
|
|
const plainChatScript = `
|
|
window.plainScriptLoaded = function() {
|
|
if (window.Plain && ${Boolean(config)}) {
|
|
try {
|
|
Plain.init(${config ? JSON.stringify(config) : null});
|
|
} catch (error) {
|
|
console.error("Failed to initialize Plain:", error);
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
if (!config) return null;
|
|
|
|
return (
|
|
<>
|
|
<Script
|
|
id="plain-chat"
|
|
src="https://chat.cdn-plain.com/index.js"
|
|
strategy="afterInteractive"
|
|
onLoad={() => window.plainScriptLoaded?.()}
|
|
/>
|
|
<Script
|
|
id="plain-chat-init"
|
|
strategy="afterInteractive"
|
|
dangerouslySetInnerHTML={{ __html: plainChatScript }}
|
|
/>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default PlainChat;
|