feat: Add fresh chat to list of support integration (#7448)
* chore: add fresh chat configs to env.example * feat: add fresh chat menu item and render * refactor: remove some event listeners * chore: make popover closed after the click * refactor the code and fix some bugs * feat: auto open chat --------- Co-authored-by: Peer Richelsen <peeroke@gmail.com>pull/6940/head^2
parent
d28c914d3c
commit
29ceaee8d2
|
@ -78,6 +78,10 @@ NEXT_PUBLIC_ZENDESK_KEY=
|
||||||
# Help Scout Config
|
# Help Scout Config
|
||||||
NEXT_PUBLIC_HELPSCOUT_KEY=
|
NEXT_PUBLIC_HELPSCOUT_KEY=
|
||||||
|
|
||||||
|
# Fresh Chat Config
|
||||||
|
NEXT_PUBLIC_FRESHCHAT_TOKEN=
|
||||||
|
NEXT_PUBLIC_FRESHCHAT_HOST=
|
||||||
|
|
||||||
# Inbox to send user feedback
|
# Inbox to send user feedback
|
||||||
SEND_FEEDBACK_EMAIL=
|
SEND_FEEDBACK_EMAIL=
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
|
import FreshChatMenuItem from "../lib/freshchat/FreshChatMenuItem";
|
||||||
import HelpscoutMenuItem from "../lib/helpscout/HelpscoutMenuItem";
|
import HelpscoutMenuItem from "../lib/helpscout/HelpscoutMenuItem";
|
||||||
import IntercomMenuItem from "../lib/intercom/IntercomMenuItem";
|
import IntercomMenuItem from "../lib/intercom/IntercomMenuItem";
|
||||||
import ZendeskMenuItem from "../lib/zendesk/ZendeskMenuItem";
|
import ZendeskMenuItem from "../lib/zendesk/ZendeskMenuItem";
|
||||||
|
|
||||||
export default function HelpMenuItem() {
|
interface ContactMenuItem {
|
||||||
|
onHelpItemSelect: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ContactMenuItem(props: ContactMenuItem) {
|
||||||
|
const { onHelpItemSelect } = props;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<IntercomMenuItem />
|
<IntercomMenuItem onHelpItemSelect={onHelpItemSelect} />
|
||||||
<ZendeskMenuItem />
|
<ZendeskMenuItem onHelpItemSelect={onHelpItemSelect} />
|
||||||
<HelpscoutMenuItem />
|
<HelpscoutMenuItem onHelpItemSelect={onHelpItemSelect} />
|
||||||
|
<FreshChatMenuItem onHelpItemSelect={onHelpItemSelect} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { trpc } from "@calcom/trpc/react";
|
||||||
import { Button, showToast } from "@calcom/ui";
|
import { Button, showToast } from "@calcom/ui";
|
||||||
import { FiExternalLink, FiAlertTriangle } from "@calcom/ui/components/icon";
|
import { FiExternalLink, FiAlertTriangle } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
|
import { useFreshChat } from "../lib/freshchat/FreshChatProvider";
|
||||||
|
import { isFreshChatEnabled } from "../lib/freshchat/FreshChatScript";
|
||||||
import ContactMenuItem from "./ContactMenuItem";
|
import ContactMenuItem from "./ContactMenuItem";
|
||||||
|
|
||||||
interface HelpMenuItemProps {
|
interface HelpMenuItemProps {
|
||||||
|
@ -21,6 +23,8 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
|
||||||
const [, loadChat] = useChat();
|
const [, loadChat] = useChat();
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|
||||||
|
const { setActive: setFreshChat } = useFreshChat();
|
||||||
|
|
||||||
const mutation = trpc.viewer.submitFeedback.useMutation({
|
const mutation = trpc.viewer.submitFeedback.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setDisableSubmit(true);
|
setDisableSubmit(true);
|
||||||
|
@ -43,7 +47,6 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
|
||||||
<div className="w-full py-5">
|
<div className="w-full py-5">
|
||||||
<p className="mb-1 px-5 text-gray-500">{t("resources").toUpperCase()}</p>
|
<p className="mb-1 px-5 text-gray-500">{t("resources").toUpperCase()}</p>
|
||||||
<a
|
<a
|
||||||
onClick={() => onHelpItemSelect()}
|
|
||||||
href="https://docs.cal.com/"
|
href="https://docs.cal.com/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="flex w-full px-5 py-2 pr-4 text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
className="flex w-full px-5 py-2 pr-4 text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
||||||
|
@ -57,7 +60,6 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
onClick={() => onHelpItemSelect()}
|
|
||||||
href="https://developer.cal.com/"
|
href="https://developer.cal.com/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="flex w-full px-5 py-2 pr-4 text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
className="flex w-full px-5 py-2 pr-4 text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
||||||
|
@ -70,8 +72,8 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
<div onClick={() => onHelpItemSelect()}>
|
<div>
|
||||||
<ContactMenuItem />
|
<ContactMenuItem onHelpItemSelect={onHelpItemSelect} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -202,7 +204,11 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
|
||||||
className="font-medium underline hover:text-gray-700"
|
className="font-medium underline hover:text-gray-700"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActive(true);
|
setActive(true);
|
||||||
|
if (isFreshChatEnabled) {
|
||||||
|
setFreshChat(true);
|
||||||
|
} else {
|
||||||
loadChat({ open: true });
|
loadChat({ open: true });
|
||||||
|
}
|
||||||
onHelpItemSelect();
|
onHelpItemSelect();
|
||||||
}}>
|
}}>
|
||||||
{t("contact_support")}
|
{t("contact_support")}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|
||||||
|
import { useFreshChat } from "./FreshChatProvider";
|
||||||
|
import { isFreshChatEnabled } from "./FreshChatScript";
|
||||||
|
|
||||||
|
interface FreshChatMenuItemProps {
|
||||||
|
onHelpItemSelect: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FreshChatMenuItem(props: FreshChatMenuItemProps) {
|
||||||
|
const { onHelpItemSelect } = props;
|
||||||
|
const { t } = useLocale();
|
||||||
|
|
||||||
|
const { setActive } = useFreshChat();
|
||||||
|
|
||||||
|
if (!isFreshChatEnabled) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setActive(true);
|
||||||
|
onHelpItemSelect();
|
||||||
|
}}
|
||||||
|
className="flex w-full px-5 py-2 pr-4 text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900">
|
||||||
|
{t("contact_support")}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import type { ReactNode, Dispatch, SetStateAction } from "react";
|
||||||
|
import { createContext, useState, useContext } from "react";
|
||||||
|
|
||||||
|
import FreshChatScript from "./FreshChatScript";
|
||||||
|
|
||||||
|
type FreshChatContextType = { active: boolean; setActive: Dispatch<SetStateAction<boolean>> };
|
||||||
|
|
||||||
|
const FreshChatContext = createContext<FreshChatContextType>({ active: false, setActive: () => undefined });
|
||||||
|
|
||||||
|
interface FreshChatProviderProps {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useFreshChat = () => useContext(FreshChatContext);
|
||||||
|
|
||||||
|
export default function FreshChatProvider(props: FreshChatProviderProps) {
|
||||||
|
const [active, setActive] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FreshChatContext.Provider value={{ active, setActive }}>
|
||||||
|
{props.children}
|
||||||
|
{active && <FreshChatScript />}
|
||||||
|
</FreshChatContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import Script from "next/script";
|
||||||
|
|
||||||
|
import { trpc } from "@calcom/trpc/react";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
fcWidget: any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||||
|
const host = process.env.NEXT_PUBLIC_FRESHCHAT_HOST;
|
||||||
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||||
|
const token = process.env.NEXT_PUBLIC_FRESHCHAT_TOKEN;
|
||||||
|
|
||||||
|
export const isFreshChatEnabled = host !== "undefined" && token !== "undefined";
|
||||||
|
|
||||||
|
export default function FreshChatScript() {
|
||||||
|
const { data } = trpc.viewer.me.useQuery();
|
||||||
|
return (
|
||||||
|
<Script
|
||||||
|
id="fresh-chat-sdk"
|
||||||
|
src="https://wchat.freshchat.com/js/widget.js"
|
||||||
|
onLoad={() => {
|
||||||
|
window.fcWidget.init({
|
||||||
|
token,
|
||||||
|
host,
|
||||||
|
externalId: data?.id,
|
||||||
|
lastName: data?.name,
|
||||||
|
email: data?.email,
|
||||||
|
meta: {
|
||||||
|
username: data?.username,
|
||||||
|
},
|
||||||
|
open: true,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -3,7 +3,12 @@ import { HelpScout, useChat } from "react-live-chat-loader";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|
||||||
export default function HelpscoutMenuItem() {
|
interface HelpscoutMenuItemProps {
|
||||||
|
onHelpItemSelect: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function HelpscoutMenuItem(props: HelpscoutMenuItemProps) {
|
||||||
|
const { onHelpItemSelect } = props;
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const [active, setActive] = useState(false);
|
const [active, setActive] = useState(false);
|
||||||
|
|
||||||
|
@ -12,10 +17,12 @@ export default function HelpscoutMenuItem() {
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
setActive(true);
|
setActive(true);
|
||||||
loadChat({ open: true });
|
loadChat({ open: true });
|
||||||
|
onHelpItemSelect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||||
if (!process.env.NEXT_PUBLIC_HELPSCOUT_KEY) return null;
|
if (!process.env.NEXT_PUBLIC_HELPSCOUT_KEY) return null;
|
||||||
else
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -2,16 +2,23 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|
||||||
import { useIntercom } from "./useIntercom";
|
import { useIntercom } from "./useIntercom";
|
||||||
|
|
||||||
export default function IntercomMenuItem() {
|
interface IntercomMenuItemProps {
|
||||||
|
onHelpItemSelect: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function IntercomMenuItem(props: IntercomMenuItemProps) {
|
||||||
|
const { onHelpItemSelect } = props;
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const { boot, show } = useIntercom();
|
const { boot, show } = useIntercom();
|
||||||
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||||
if (!process.env.NEXT_PUBLIC_INTERCOM_APP_ID) return null;
|
if (!process.env.NEXT_PUBLIC_INTERCOM_APP_ID) return null;
|
||||||
else
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
boot();
|
boot();
|
||||||
show();
|
show();
|
||||||
|
onHelpItemSelect();
|
||||||
}}
|
}}
|
||||||
className="flex w-full px-5 py-2 pr-4 text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900">
|
className="flex w-full px-5 py-2 pr-4 text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900">
|
||||||
{t("contact_support")}
|
{t("contact_support")}
|
||||||
|
|
|
@ -3,18 +3,27 @@ import { useState } from "react";
|
||||||
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|
||||||
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||||
const ZENDESK_KEY = process.env.NEXT_PUBLIC_ZENDESK_KEY;
|
const ZENDESK_KEY = process.env.NEXT_PUBLIC_ZENDESK_KEY;
|
||||||
|
|
||||||
export default function ZendeskMenuItem() {
|
interface ZendeskMenuItemProps {
|
||||||
|
onHelpItemSelect: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ZendeskMenuItem(props: ZendeskMenuItemProps) {
|
||||||
|
const { onHelpItemSelect } = props;
|
||||||
const [active, setActive] = useState(false);
|
const [active, setActive] = useState(false);
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|
||||||
if (!process.env.NEXT_PUBLIC_ZENDESK_KEY) return null;
|
if (!ZENDESK_KEY) return null;
|
||||||
else
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActive(true)}
|
onClick={() => {
|
||||||
|
setActive(true);
|
||||||
|
onHelpItemSelect();
|
||||||
|
}}
|
||||||
className="flex w-full px-5 py-2 pr-4 text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900">
|
className="flex w-full px-5 py-2 pr-4 text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900">
|
||||||
{t("contact_support")}
|
{t("contact_support")}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -63,6 +63,7 @@ import {
|
||||||
FiArrowLeft,
|
FiArrowLeft,
|
||||||
} from "@calcom/ui/components/icon";
|
} from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
|
import FreshChatProvider from "../ee/support/lib/freshchat/FreshChatProvider";
|
||||||
import { TeamInviteBadge } from "./TeamInviteBadge";
|
import { TeamInviteBadge } from "./TeamInviteBadge";
|
||||||
|
|
||||||
/* TODO: Migate this */
|
/* TODO: Migate this */
|
||||||
|
@ -316,6 +317,7 @@ function UserDropdown({ small }: { small?: boolean }) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DropdownMenuPortal>
|
<DropdownMenuPortal>
|
||||||
|
<FreshChatProvider>
|
||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
onInteractOutside={() => {
|
onInteractOutside={() => {
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
|
@ -399,7 +401,11 @@ function UserDropdown({ small }: { small?: boolean }) {
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem className="desktop-hidden hidden lg:flex">
|
<DropdownMenuItem className="desktop-hidden hidden lg:flex">
|
||||||
<DropdownItem StartIcon={FiDownload} target="_blank" rel="noreferrer" href={DESKTOP_APP_LINK}>
|
<DropdownItem
|
||||||
|
StartIcon={FiDownload}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
href={DESKTOP_APP_LINK}>
|
||||||
{t("download_desktop_app")}
|
{t("download_desktop_app")}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
@ -416,6 +422,7 @@ function UserDropdown({ small }: { small?: boolean }) {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
</FreshChatProvider>
|
||||||
</DropdownMenuPortal>
|
</DropdownMenuPortal>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue